Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoupling of Parser #822

Merged
merged 13 commits into from
Sep 3, 2020
Merged
5 changes: 1 addition & 4 deletions internal/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (n *KongController) syncIngress(interface{}) error {
})

n.Logger.Infof("syncing configuration")
state, err := n.parser.Build()
state, err := parser.Build(n.Logger.WithField("component", "store"), n.store)
if err != nil {
return fmt.Errorf("error building kong state: %w", err)
}
Expand Down Expand Up @@ -160,7 +160,6 @@ func NewKongController(ctx context.Context,
}

n.store = store
n.parser = parser.New(n.store, n.Logger.WithField("component", "store"))
n.syncQueue = task.NewTaskQueue(n.syncIngress,
config.Logger.WithField("component", "sync-queue"))

Expand Down Expand Up @@ -240,8 +239,6 @@ type KongController struct {

store store.Storer

parser parser.Parser

PluginSchemaStore PluginSchemaStore

Logger logrus.FieldLogger
Expand Down
6 changes: 3 additions & 3 deletions internal/ingress/controller/kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ import (
"github.com/kong/deck/state"
deckutils "github.com/kong/deck/utils"
"github.com/kong/go-kong/kong"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/controller/parser"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/controller/parser/kongstate"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/utils"
)

// OnUpdate is called periodically by syncQueue to keep the configuration in sync.
// returning nil implies the synchronization finished correctly.
// Returning an error means requeue the update.
func (n *KongController) OnUpdate(ctx context.Context, state *parser.KongState) error {
func (n *KongController) OnUpdate(ctx context.Context, state *kongstate.KongState) error {
targetContent := n.toDeckContent(ctx, state)

var customEntities []byte
Expand Down Expand Up @@ -295,7 +295,7 @@ func (n *KongController) getIngressControllerTags() []string {

func (n *KongController) toDeckContent(
ctx context.Context,
k8sState *parser.KongState) *file.Content {
k8sState *kongstate.KongState) *file.Content {
var content file.Content
content.FormatVersion = "1.1"
var err error
Expand Down
114 changes: 114 additions & 0 deletions internal/ingress/controller/parser/ingressrules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package parser

import (
"github.com/kong/go-kong/kong"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/annotations"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/controller/parser/kongstate"
"github.com/kong/kubernetes-ingress-controller/internal/ingress/store"
"github.com/sirupsen/logrus"
networking "k8s.io/api/networking/v1beta1"
)

type ingressRules struct {
SecretNameToSNIs SecretNameToSNIs
ServiceNameToServices map[string]kongstate.Service
}

func newIngressRules() ingressRules {
return ingressRules{
SecretNameToSNIs: newSecretNameToSNIs(),
ServiceNameToServices: make(map[string]kongstate.Service),
}
}

func mergeIngressRules(objs ...ingressRules) ingressRules {
result := newIngressRules()

for _, obj := range objs {
for k, v := range obj.SecretNameToSNIs {
result.SecretNameToSNIs[k] = append(result.SecretNameToSNIs[k], v...)
}
for k, v := range obj.ServiceNameToServices {
result.ServiceNameToServices[k] = v
}
}
return result
}

func (ir *ingressRules) populateServices(log logrus.FieldLogger, s store.Storer) {
// populate Kubernetes Service
for key, service := range ir.ServiceNameToServices {
k8sSvc, err := s.GetService(service.Namespace, service.Backend.Name)
if err != nil {
log.WithFields(logrus.Fields{
"service_name": service.Backend.Name,
"service_namespace": service.Namespace,
}).Errorf("failed to fetch service: %v", err)
}
if k8sSvc != nil {
service.K8sService = *k8sSvc
}
secretName := annotations.ExtractClientCertificate(
service.K8sService.GetAnnotations())
if secretName != "" {
secret, err := s.GetSecret(service.K8sService.Namespace,
secretName)
secretKey := service.K8sService.Namespace + "/" + secretName
// ensure that the cert is loaded into Kong
if _, ok := ir.SecretNameToSNIs[secretKey]; !ok {
ir.SecretNameToSNIs[secretKey] = []string{}
}
if err == nil {
service.ClientCertificate = &kong.Certificate{
ID: kong.String(string(secret.UID)),
}
} else {
log.WithFields(logrus.Fields{
"secret_name": secretName,
"secret_namespace": service.K8sService.Namespace,
}).Errorf("failed to fetch secret: %v", err)
}
}
ir.ServiceNameToServices[key] = service
}
}

type SecretNameToSNIs map[string][]string

func newSecretNameToSNIs() SecretNameToSNIs {
return SecretNameToSNIs(map[string][]string{})
}

func (m SecretNameToSNIs) addFromIngressTLS(tlsSections []networking.IngressTLS, namespace string) {
for _, tls := range tlsSections {
if len(tls.Hosts) == 0 {
continue
}
if tls.SecretName == "" {
continue
}
hosts := tls.Hosts
secretName := namespace + "/" + tls.SecretName
hosts = m.filterHosts(hosts)
if m[secretName] != nil {
hosts = append(hosts, m[secretName]...)
}
m[secretName] = hosts
}
}

func (m SecretNameToSNIs) filterHosts(hosts []string) []string {
hostsToAdd := []string{}
seenHosts := map[string]bool{}
for _, hosts := range m {
for _, host := range hosts {
seenHosts[host] = true
}
}
for _, host := range hosts {
if !seenHosts[host] {
hostsToAdd = append(hostsToAdd, host)
}
}
return hostsToAdd
}
172 changes: 172 additions & 0 deletions internal/ingress/controller/parser/ingressrules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package parser

import (
mflendrich marked this conversation as resolved.
Show resolved Hide resolved
"testing"

"github.com/kong/kubernetes-ingress-controller/internal/ingress/controller/parser/kongstate"
"github.com/stretchr/testify/assert"
networking "k8s.io/api/networking/v1beta1"
)

func TestMergeIngressRules(t *testing.T) {
for _, tt := range []struct {
name string
inputs []ingressRules
wantOutput *ingressRules
}{
{
name: "empty list",
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{},
ServiceNameToServices: map[string]kongstate.Service{},
},
},
{
name: "nil maps",
inputs: []ingressRules{
{}, {}, {},
},
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{},
ServiceNameToServices: map[string]kongstate.Service{},
},
},
{
name: "one input",
inputs: []ingressRules{
{
SecretNameToSNIs: map[string][]string{"a": {"b", "c"}, "d": {"e", "f"}},
ServiceNameToServices: map[string]kongstate.Service{"1": {Namespace: "potato"}},
},
},
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{"a": {"b", "c"}, "d": {"e", "f"}},
ServiceNameToServices: map[string]kongstate.Service{"1": {Namespace: "potato"}},
},
},
{
name: "three inputs",
inputs: []ingressRules{
{
SecretNameToSNIs: map[string][]string{"a": {"b", "c"}, "d": {"e", "f"}},
ServiceNameToServices: map[string]kongstate.Service{"1": {Namespace: "potato"}},
},
{
SecretNameToSNIs: map[string][]string{"g": {"h"}},
},
{
ServiceNameToServices: map[string]kongstate.Service{"2": {Namespace: "carrot"}},
},
},
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{"a": {"b", "c"}, "d": {"e", "f"}, "g": {"h"}},
ServiceNameToServices: map[string]kongstate.Service{"1": {Namespace: "potato"}, "2": {Namespace: "carrot"}},
},
},
mflendrich marked this conversation as resolved.
Show resolved Hide resolved
{
name: "can merge SNI arrays",
inputs: []ingressRules{
{
SecretNameToSNIs: map[string][]string{"a": {"b", "c"}},
},
{
SecretNameToSNIs: map[string][]string{"a": {"d", "e"}},
},
},
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{"a": {"b", "c", "d", "e"}},
ServiceNameToServices: map[string]kongstate.Service{},
},
},
{
name: "overwrites services",
inputs: []ingressRules{
{
ServiceNameToServices: map[string]kongstate.Service{"svc-name": {Namespace: "old"}},
},
{
ServiceNameToServices: map[string]kongstate.Service{"svc-name": {Namespace: "new"}},
},
},
wantOutput: &ingressRules{
SecretNameToSNIs: map[string][]string{},
ServiceNameToServices: map[string]kongstate.Service{"svc-name": {Namespace: "new"}},
},
},
} {
t.Run(tt.name, func(t *testing.T) {
gotOutput := mergeIngressRules(tt.inputs...)
assert.Equal(t, &gotOutput, tt.wantOutput)
})
}
}

func Test_addFromIngressTLS(t *testing.T) {
type args struct {
tlsSections []networking.IngressTLS
namespace string
}
tests := []struct {
name string
args args
want SecretNameToSNIs
}{
{
args: args{
tlsSections: []networking.IngressTLS{
{
Hosts: []string{
"1.example.com",
"2.example.com",
},
SecretName: "sooper-secret",
},
{
Hosts: []string{
"3.example.com",
"4.example.com",
},
SecretName: "sooper-secret2",
},
},
namespace: "foo",
},
want: SecretNameToSNIs{
"foo/sooper-secret": {"1.example.com", "2.example.com"},
"foo/sooper-secret2": {"3.example.com", "4.example.com"},
},
},
{
args: args{
tlsSections: []networking.IngressTLS{
{
Hosts: []string{
"1.example.com",
},
SecretName: "sooper-secret",
},
{
Hosts: []string{
"3.example.com",
"1.example.com",
"4.example.com",
},
SecretName: "sooper-secret2",
},
},
namespace: "foo",
},
want: SecretNameToSNIs{
"foo/sooper-secret": {"1.example.com"},
"foo/sooper-secret2": {"3.example.com", "4.example.com"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := newSecretNameToSNIs()
m.addFromIngressTLS(tt.args.tlsSections, tt.args.namespace)
assert.Equal(t, m, tt.want)
})
}
}
Loading