Skip to content

Commit

Permalink
feat: implement --skip-classless-ingress-v1beta1
Browse files Browse the repository at this point in the history
From #664
  • Loading branch information
nutellinoit committed Jul 7, 2020
1 parent c07cfd4 commit 4bf2405
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 38 deletions.
10 changes: 7 additions & 3 deletions cli/ingress-controller/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ type cliConfig struct {
KongCustomEntitiesSecret string

// Resource filtering
WatchNamespace string
IngressClass string
ElectionID string
WatchNamespace string
SkipClasslessIngressV1beta1 bool
IngressClass string
ElectionID string

// Ingress Status publish resource
PublishService string
Expand Down Expand Up @@ -175,6 +176,8 @@ mode of Kong. Takes the form of namespace/name.`)
// Resource filtering
flags.String("watch-namespace", apiv1.NamespaceAll,
`Namespace to watch for Ingress. Default is to watch all namespaces`)
flags.Bool("skip-classless-ingress-v1beta1", false,
`Skip non annotated Ingresses and Kong CRDs.`)
flags.String("ingress-class", annotations.DefaultIngressClass,
`Name of the ingress class to route through this controller.`)
flags.String("election-id", "ingress-controller-leader",
Expand Down Expand Up @@ -320,6 +323,7 @@ func parseFlags() (cliConfig, error) {

// Resource filtering
config.WatchNamespace = viper.GetString("watch-namespace")
config.SkipClasslessIngressV1beta1 = viper.GetBool("skip-classless-ingress-v1beta1")
config.IngressClass = viper.GetString("ingress-class")
config.ElectionID = viper.GetString("election-id")

Expand Down
8 changes: 5 additions & 3 deletions cli/ingress-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,10 @@ func main() {
var synced []cache.InformerSynced
updateChannel := channels.NewRingChannel(1024)
reh := controller.ResourceEventHandler{
UpdateCh: updateChannel,
IsValidIngresClass: annotations.IngressClassValidatorFunc(cliConfig.IngressClass),
UpdateCh: updateChannel,
IsValidIngresClass: annotations.IngressClassValidatorFunc(
cliConfig.IngressClass,
cliConfig.SkipClasslessIngressV1beta1),
}
var informers []cache.SharedIndexInformer
var cacheStores store.CacheStores
Expand Down Expand Up @@ -366,7 +368,7 @@ func main() {
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
}

store := store.New(cacheStores, cliConfig.IngressClass)
store := store.New(cacheStores, cliConfig.IngressClass, cliConfig.SkipClasslessIngressV1beta1)
kong, err := controller.NewKongController(&controllerConfig, updateChannel,
store)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cli/ingress-controller/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestHandleSigterm(t *testing.T) {
KubeClient: kubeClient,
},
channels.NewRingChannel(1024),
store.New(store.CacheStores{}, conf.IngressClass),
store.New(store.CacheStores{}, conf.IngressClass, conf.SkipClasslessIngressV1beta1),
)

exitCh := make(chan int, 1)
Expand Down
12 changes: 6 additions & 6 deletions internal/ingress/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ const (
DefaultIngressClass = "kong"
)

func validIngress(ingressAnnotationValue, ingressClass string) bool {
func validIngress(ingressAnnotationValue, ingressClass string, skipClasslessIngress bool) bool {
// we have 2 valid combinations
// 1 - ingress with default class | blank annotation on ingress
// 2 - ingress with specific class | same annotation on ingress
//
// and 2 invalid combinations
// 3 - ingress with default class | fixed annotation on ingress
// 4 - ingress with specific class | different annotation on ingress
if ingressAnnotationValue == "" && ingressClass == DefaultIngressClass {
if ingressAnnotationValue == "" && !skipClasslessIngress && ingressClass == DefaultIngressClass {
return true
}
return ingressAnnotationValue == ingressClass
Expand All @@ -66,22 +66,22 @@ func validIngress(ingressAnnotationValue, ingressClass string) bool {
// IngressClassValidatorFunc returns a function which can validate if an Object
// belongs to an the ingressClass or not.
func IngressClassValidatorFunc(
ingressClass string) func(obj metav1.Object) bool {
ingressClass string, skipClasslessIngress bool) func(obj metav1.Object) bool {

return func(obj metav1.Object) bool {
ingress := obj.GetAnnotations()[ingressClassKey]
return validIngress(ingress, ingressClass)
return validIngress(ingress, ingressClass, skipClasslessIngress)
}
}

// IngressClassValidatorFuncFromObjectMeta returns a function which
// can validate if an ObjectMeta belongs to an the ingressClass or not.
func IngressClassValidatorFuncFromObjectMeta(
ingressClass string) func(obj *metav1.ObjectMeta) bool {
ingressClass string, skipClasslessIngress bool) func(obj *metav1.ObjectMeta) bool {

return func(obj *metav1.ObjectMeta) bool {
ingress := obj.GetAnnotations()[ingressClassKey]
return validIngress(ingress, ingressClass)
return validIngress(ingress, ingressClass, skipClasslessIngress)
}
}

Expand Down
25 changes: 15 additions & 10 deletions internal/ingress/annotations/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ import (

func TestIngressClassValidatorFunc(t *testing.T) {
tests := []struct {
ingress string
controller string
isValid bool
ingress string
skipClasslessIngress bool
controller string
isValid bool
}{
{"", "", true},
{"", "kong", true},
{"kong", "kong", true},
{"custom", "custom", true},
{"", "killer", false},
{"custom", "kong", false},
{"", false, "", true},
{"", false, "kong", true},
{"", true, "kong", false},
{"kong", false, "kong", true},
{"kong", true, "kong", true},
{"custom", false, "custom", true},
{"", false, "killer", false},
{"custom", false, "kong", false},
{"custom", true, "kong", false},
{"", true, "custom", false},
}

ing := &extensions.Ingress{
Expand All @@ -50,7 +55,7 @@ func TestIngressClassValidatorFunc(t *testing.T) {
ing.SetAnnotations(data)
for _, test := range tests {
ing.Annotations[ingressClassKey] = test.ingress
f := IngressClassValidatorFunc(test.controller)
f := IngressClassValidatorFunc(test.controller, test.skipClasslessIngress)
b := f(&ing.ObjectMeta)
if b != test.isValid {
t.Errorf("test %v - expected %v but %v was returned", test, test.isValid, b)
Expand Down
110 changes: 110 additions & 0 deletions internal/ingress/controller/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,116 @@ func TestKongRouteAnnotations(t *testing.T) {
})
}

func TestKongSkipClasslessIngress(t *testing.T) {
assert := assert.New(t)
t.Run("Kong classless ingress evaluated (true)", func(t *testing.T) {
ingresses := []*networking.Ingress{
{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
Annotations: map[string]string{},
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Backend: networking.IngressBackend{
ServiceName: "foo-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
},
}
services := []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-svc",
Namespace: "default",
},
},
}

store, err := store.NewFakeStore(store.FakeObjects{
Ingresses: ingresses,
Services: services,
SkipClasslessIngress: false,
})
assert.Nil(err)
parser := New(store)
state, err := parser.Build()
assert.Nil(err)
assert.NotNil(state)

assert.Equal(1, len(state.Services),
"expected one service to be rendered")
})
t.Run("Kong classless ingress evaluated (false)", func(t *testing.T) {
ingresses := []*networking.Ingress{
{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
Annotations: map[string]string{},
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Backend: networking.IngressBackend{
ServiceName: "foo-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
},
}
services := []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-svc",
Namespace: "default",
},
},
}

store, err := store.NewFakeStore(store.FakeObjects{
Ingresses: ingresses,
Services: services,
SkipClasslessIngress: true,
})
assert.Nil(err)
parser := New(store)
state, err := parser.Build()
assert.Nil(err)
assert.NotNil(state)

assert.Equal(0, len(state.Services),
"expected zero service to be rendered")
})
}

func TestKnativeIngressAndPlugins(t *testing.T) {
assert := assert.New(t)
t.Run("knative ingress rule and service-level plugin", func(t *testing.T) {
Expand Down
23 changes: 12 additions & 11 deletions internal/ingress/store/fake_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ func clusterResourceKeyFunc(obj interface{}) (string, error) {

// FakeObjects can be used to populate a fake Store.
type FakeObjects struct {
Ingresses []*networking.Ingress
TCPIngresses []*configurationv1beta1.TCPIngress
Services []*apiv1.Service
Endpoints []*apiv1.Endpoints
Secrets []*apiv1.Secret
KongPlugins []*configurationv1.KongPlugin
KongClusterPlugins []*configurationv1.KongClusterPlugin
KongIngresses []*configurationv1.KongIngress
KongConsumers []*configurationv1.KongConsumer
KongCredentials []*configurationv1.KongCredential
Ingresses []*networking.Ingress
TCPIngresses []*configurationv1beta1.TCPIngress
Services []*apiv1.Service
Endpoints []*apiv1.Endpoints
Secrets []*apiv1.Secret
KongPlugins []*configurationv1.KongPlugin
KongClusterPlugins []*configurationv1.KongClusterPlugin
KongIngresses []*configurationv1.KongIngress
KongConsumers []*configurationv1.KongConsumer
KongCredentials []*configurationv1.KongCredential
SkipClasslessIngress bool

KnativeIngresses []*knative.Ingress
}
Expand Down Expand Up @@ -140,7 +141,7 @@ func NewFakeStore(

KnativeIngress: knativeIngressStore,
},
isValidIngresClass: annotations.IngressClassValidatorFuncFromObjectMeta("kong"),
isValidIngresClass: annotations.IngressClassValidatorFuncFromObjectMeta("kong", objects.SkipClasslessIngress),
}
return s, nil
}
11 changes: 7 additions & 4 deletions internal/ingress/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ type Store struct {

ingressClass string

skipClasslessIngress bool

isValidIngresClass func(objectMeta *metav1.ObjectMeta) bool
}

Expand All @@ -104,11 +106,12 @@ type CacheStores struct {
}

// New creates a new object store to be used in the ingress controller
func New(cs CacheStores, ingressClass string) Storer {
func New(cs CacheStores, ingressClass string, skipClasslessIngress bool) Storer {
return Store{
stores: cs,
ingressClass: ingressClass,
isValidIngresClass: annotations.IngressClassValidatorFuncFromObjectMeta(ingressClass),
stores: cs,
ingressClass: ingressClass,
skipClasslessIngress: skipClasslessIngress,
isValidIngresClass: annotations.IngressClassValidatorFuncFromObjectMeta(ingressClass, skipClasslessIngress),
}
}

Expand Down

0 comments on commit 4bf2405

Please sign in to comment.