Skip to content

Commit

Permalink
feat: instantiate global plugins in Kong via labelling
Browse files Browse the repository at this point in the history
There are cases where a plugin(s) should be executed for all proxied
requests. In order to do this with the existing model, it becomes
cumbersome to configure it for 100s of different services and would make
sense to configure globally for all services. This feature is already
supported by Kong.

With this change, all KongPlugin resources that have a label of
`"global" : "true"` will be setup as global plugins in Kong
by the Ingress controller.
  • Loading branch information
hbagdi committed Sep 10, 2018
1 parent eb888b4 commit 2c99413
Showing 1 changed file with 95 additions and 5 deletions.
100 changes: 95 additions & 5 deletions internal/ingress/controller/kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ import (

"github.com/fatih/structs"
"github.com/golang/glog"
configurationv1 "github.com/kong/kubernetes-ingress-controller/internal/apis/configuration/v1"
"github.com/pkg/errors"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/util/sets"

kong "github.com/kong/kubernetes-ingress-controller/internal/apis/admin"
kongadminv1 "github.com/kong/kubernetes-ingress-controller/internal/apis/admin/v1"
configurationv1 "github.com/kong/kubernetes-ingress-controller/internal/apis/configuration/v1"
pluginv1 "github.com/kong/kubernetes-ingress-controller/internal/apis/plugin/v1"
"github.com/kong/kubernetes-ingress-controller/internal/ingress"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/annotations"
"github.com/kong/kubernetes-ingress-controller/internal/net/ssl"
"github.com/pkg/errors"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/util/sets"
)

// OnUpdate is called periodically by syncQueue to keep the configuration in sync.
Expand Down Expand Up @@ -92,6 +92,11 @@ func (n *NGINXController) OnUpdate(ingressCfg *ingress.Configuration) error {
return err
}

err = n.syncGlobalPlugins()
if err != nil {
return err
}

checkServices, err := n.syncServices(ingressCfg)
if err != nil {
return err
Expand All @@ -114,6 +119,91 @@ func (n *NGINXController) OnUpdate(ingressCfg *ingress.Configuration) error {
return nil
}

func (n *NGINXController) syncGlobalPlugins() error {
glog.Infof("syncing global plugins")

targetGlobalPlugins, err := n.store.ListGlobalKongPlugins()
if err != nil {
return err
}
targetPluginMap := make(map[string]*pluginv1.KongPlugin)
var duplicates []string // keep track of duplicate

for i := 0; i < len(targetGlobalPlugins); i++ {
name := targetGlobalPlugins[i].Name
// empty name skip it
if name == "" {
continue
}
if _, ok := targetPluginMap[name]; ok {
glog.Error("Multiple KongPlugin definitions found with 'global' annotation for :", name,
", the plugin will not be applied")
duplicates = append(duplicates, name)
continue
}
targetPluginMap[name] = targetGlobalPlugins[i]
}

// remove duplicates
for _, plugin := range duplicates {
delete(targetPluginMap, plugin)
}

client := n.cfg.Kong.Client
plugins, err := client.Plugins().List(nil)
if err != nil {
return err
}

// plugins in Kong
currentGlobalPlugins := make(map[string]kongadminv1.Plugin)
for _, plugin := range plugins.Items {
if plugin.Route == "" && plugin.Service == "" && plugin.Consumer == "" {
currentGlobalPlugins[plugin.Name] = plugin
}
}

// sync plugins to Kong
for pluginName, kongPlugin := range targetPluginMap {
// plugin exists?
if pluginInKong, ok := currentGlobalPlugins[pluginName]; !ok {
// no, create it
p := &kongadminv1.Plugin{
Name: pluginName,
Config: kongadminv1.Configuration(kongPlugin.Config),
}
_, res := client.Plugins().CreateGlobal(p)
if res.StatusCode != http.StatusCreated {
return errors.Wrap(res.Error(), fmt.Sprintf("creating a global Kong plugin %v", p))
}
} else {
// plugin exists, is the configuration up to date
if !pluginDeepEqual(kongPlugin.Config, &pluginInKong) {
// no, update it
p := &kongadminv1.Plugin{
Name: pluginName,
Config: kongadminv1.Configuration(kongPlugin.Config),
}
_, res := client.Plugins().Patch(pluginInKong.ID, p)
if res.StatusCode != http.StatusOK {
return errors.Wrap(res.Error(), fmt.Sprintf("updating a global Kong plugin %v", p))
}
}
}
// remove from the current list, all that remain in the current list will be deleted
delete(currentGlobalPlugins, pluginName)
}

// delete the ones not configured in k8s
for _, plugin := range currentGlobalPlugins {
err := client.Plugins().Delete(plugin.ID)
if err != nil {
return err
}
}
return nil
}

// syncTargets reconciles the state between the ingress controller and
// kong comparing the endpoints in Kubernetes and the targets in a
// particular kong upstream. To avoid downtimes we create the new targets
Expand Down

0 comments on commit 2c99413

Please sign in to comment.