Skip to content

Commit 5c892f5

Browse files
authored
Make Ingress validating webhook ignore ingresses not managed by AWS LBC (#3272)
* Don't manage ingress classes with non-matching controller even when managing ingresses without classes * Make Ingress validating webhook ignore ingresses not managed by AWS LBC
1 parent 9e8f685 commit 5c892f5

File tree

10 files changed

+355
-29
lines changed

10 files changed

+355
-29
lines changed

controllers/ingress/group_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func NewGroupReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder
6565
stackMarshaller := deploy.NewDefaultStackMarshaller()
6666
stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler,
6767
controllerConfig, ingressTagPrefix, logger)
68-
classLoader := ingress.NewDefaultClassLoader(k8sClient)
68+
classLoader := ingress.NewDefaultClassLoader(k8sClient, true)
6969
classAnnotationMatcher := ingress.NewDefaultClassAnnotationMatcher(controllerConfig.IngressConfig.IngressClass)
7070
manageIngressesWithoutIngressClass := controllerConfig.IngressConfig.IngressClass == ""
7171
groupLoader := ingress.NewDefaultGroupLoader(k8sClient, eventRecorder, annotationParser, classLoader, classAnnotationMatcher, manageIngressesWithoutIngressClass)

pkg/config/ingress_config.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ const (
1515

1616
// IngressConfig contains the configurations for the Ingress controller
1717
type IngressConfig struct {
18-
// Name of the Ingress class this controller satisfies
19-
// If empty, all Ingresses without ingress.class annotation, or ingress.class==alb get considered
18+
// Name of the Ingress class this controller satisfies.
19+
// If empty, all with a `kubernetes.io/ingress.class`
20+
// annotation of `alb` get considered.
21+
// Also, if empty, all ingresses without either a `kubernetes.io/ingress.class` annotation or
22+
// an IngressClassName get considered.
2023
IngressClass string
2124

22-
// DisableIngressClassAnnotation specifies whether to disable new usage of kubernetes.io/ingress.class annotation.
25+
// DisableIngressClassAnnotation specifies whether to disable new use of the `kubernetes.io/ingress.class` annotation.
2326
DisableIngressClassAnnotation bool
2427

25-
// DisableIngressGroupNameAnnotation specifies whether to disable new usage of alb.ingress.kubernetes.io/group.name annotation.
28+
// DisableIngressGroupNameAnnotation specifies whether to disable new use of the `alb.ingress.kubernetes.io/group.name` annotation.
2629
DisableIngressGroupNameAnnotation bool
2730

2831
// Max concurrent reconcile loops for Ingress objects

pkg/ingress/class_annotation_matcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type ClassAnnotationMatcher interface {
1010
}
1111

1212
// NewDefaultClassAnnotationMatcher constructs new defaultClassAnnotationMatcher.
13-
func NewDefaultClassAnnotationMatcher(ingressClass string) *defaultClassAnnotationMatcher {
13+
func NewDefaultClassAnnotationMatcher(ingressClass string) ClassAnnotationMatcher {
1414
return &defaultClassAnnotationMatcher{
1515
ingressClass: ingressClass,
1616
}

pkg/ingress/class_loader.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
const (
2020
// the controller name used in IngressClass for ALB.
21-
ingressClassControllerALB = "ingress.k8s.aws/alb"
21+
IngressClassControllerALB = "ingress.k8s.aws/alb"
2222
// the Kind for IngressClassParams CRD.
2323
ingressClassParamsKind = "IngressClassParams"
2424
// default class from ingressClass
@@ -35,15 +35,17 @@ type ClassLoader interface {
3535
}
3636

3737
// NewDefaultClassLoader constructs new defaultClassLoader instance.
38-
func NewDefaultClassLoader(client client.Client) *defaultClassLoader {
38+
func NewDefaultClassLoader(client client.Client, loadParams bool) ClassLoader {
3939
return &defaultClassLoader{
40-
client: client,
40+
client: client,
41+
loadParams: loadParams,
4142
}
4243
}
4344

4445
// default implementation for ClassLoader
4546
type defaultClassLoader struct {
46-
client client.Client
47+
client client.Client
48+
loadParams bool
4749
}
4850

4951
// GetDefaultIngressClass returns the default IngressClass from the list of IngressClasses.
@@ -91,7 +93,7 @@ func (l *defaultClassLoader) Load(ctx context.Context, ing *networking.Ingress)
9193
}
9294
return ClassConfiguration{}, err
9395
}
94-
if ingClass.Spec.Controller != ingressClassControllerALB || ingClass.Spec.Parameters == nil {
96+
if ingClass.Spec.Controller != IngressClassControllerALB || ingClass.Spec.Parameters == nil || !l.loadParams {
9597
return ClassConfiguration{
9698
IngClass: ingClass,
9799
}, nil

pkg/ingress/class_loader_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,8 @@ func Test_defaultClassLoader_Load(t *testing.T) {
660660
}
661661

662662
l := &defaultClassLoader{
663-
client: k8sClient,
663+
client: k8sClient,
664+
loadParams: true,
664665
}
665666
got, err := l.Load(ctx, tt.args.ing)
666667
if tt.wantErr != nil {
@@ -852,7 +853,8 @@ func Test_defaultClassLoader_validateIngressClassParamsNamespaceRestriction(t *t
852853
}
853854

854855
l := &defaultClassLoader{
855-
client: k8sClient,
856+
client: k8sClient,
857+
loadParams: true,
856858
}
857859
if tt.args.admissionReq != nil {
858860
ctx = webhook.ContextWithAdmissionRequest(ctx, *tt.args.admissionReq)

pkg/ingress/group_loader.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,11 @@ func (m *defaultGroupLoader) classifyIngress(ctx context.Context, ing *networkin
215215
}, false, err
216216
}
217217

218-
if matchesIngressClass := ingClassConfig.IngClass != nil && ingClassConfig.IngClass.Spec.Controller == ingressClassControllerALB; matchesIngressClass {
218+
if ingClassConfig.IngClass != nil {
219219
return ClassifiedIngress{
220220
Ing: ing,
221221
IngClassConfig: ingClassConfig,
222-
}, true, nil
222+
}, ingClassConfig.IngClass.Spec.Controller == IngressClassControllerALB, nil
223223
}
224224

225225
return ClassifiedIngress{

pkg/ingress/group_loader_test.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ func Test_defaultGroupLoader_Load(t *testing.T) {
621621
}
622622

623623
annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
624-
classLoader := NewDefaultClassLoader(k8sClient)
624+
classLoader := NewDefaultClassLoader(k8sClient, true)
625625
classAnnotationMatcher := NewDefaultClassAnnotationMatcher("alb")
626626
m := &defaultGroupLoader{
627627
client: k8sClient,
@@ -1485,7 +1485,7 @@ func Test_defaultGroupLoader_checkGroupMembershipType(t *testing.T) {
14851485
}
14861486

14871487
annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
1488-
classLoader := NewDefaultClassLoader(k8sClient)
1488+
classLoader := NewDefaultClassLoader(k8sClient, true)
14891489
classAnnotationMatcher := NewDefaultClassAnnotationMatcher("alb")
14901490
m := &defaultGroupLoader{
14911491
client: k8sClient,
@@ -1759,7 +1759,7 @@ func Test_defaultGroupLoader_loadGroupIDIfAnyHelper(t *testing.T) {
17591759
}
17601760

17611761
annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
1762-
classLoader := NewDefaultClassLoader(k8sClient)
1762+
classLoader := NewDefaultClassLoader(k8sClient, true)
17631763
classAnnotationMatcher := NewDefaultClassAnnotationMatcher("alb")
17641764
m := &defaultGroupLoader{
17651765
client: k8sClient,
@@ -2107,6 +2107,60 @@ func Test_defaultGroupLoader_classifyIngress(t *testing.T) {
21072107
},
21082108
wantIngressClassMatches: false,
21092109
},
2110+
{
2111+
name: "class specified via ingressClassName - mismatches - manageIngressesWithoutIngressClass is set",
2112+
env: env{
2113+
ingClassList: []*networking.IngressClass{
2114+
{
2115+
ObjectMeta: metav1.ObjectMeta{
2116+
Name: "ing-class",
2117+
},
2118+
Spec: networking.IngressClassSpec{
2119+
Controller: "some.other/nginx",
2120+
},
2121+
},
2122+
},
2123+
},
2124+
fields: fields{
2125+
ingressClass: "",
2126+
manageIngressesWithoutIngressClass: true,
2127+
},
2128+
args: args{
2129+
ing: &networking.Ingress{
2130+
ObjectMeta: metav1.ObjectMeta{
2131+
Namespace: "ing-ns",
2132+
Name: "ing-name",
2133+
Annotations: map[string]string{},
2134+
},
2135+
Spec: networking.IngressSpec{
2136+
IngressClassName: awssdk.String("ing-class"),
2137+
},
2138+
},
2139+
},
2140+
wantClassifiedIng: ClassifiedIngress{
2141+
Ing: &networking.Ingress{
2142+
ObjectMeta: metav1.ObjectMeta{
2143+
Namespace: "ing-ns",
2144+
Name: "ing-name",
2145+
Annotations: map[string]string{},
2146+
},
2147+
Spec: networking.IngressSpec{
2148+
IngressClassName: awssdk.String("ing-class"),
2149+
},
2150+
},
2151+
IngClassConfig: ClassConfiguration{
2152+
IngClass: &networking.IngressClass{
2153+
ObjectMeta: metav1.ObjectMeta{
2154+
Name: "ing-class",
2155+
},
2156+
Spec: networking.IngressClassSpec{
2157+
Controller: "some.other/nginx",
2158+
},
2159+
},
2160+
},
2161+
},
2162+
wantIngressClassMatches: false,
2163+
},
21102164
}
21112165
for _, tt := range tests {
21122166
t.Run(tt.name, func(t *testing.T) {
@@ -2122,7 +2176,7 @@ func Test_defaultGroupLoader_classifyIngress(t *testing.T) {
21222176
}
21232177

21242178
annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
2125-
classLoader := NewDefaultClassLoader(k8sClient)
2179+
classLoader := NewDefaultClassLoader(k8sClient, true)
21262180
classAnnotationMatcher := NewDefaultClassAnnotationMatcher(tt.fields.ingressClass)
21272181
m := &defaultGroupLoader{
21282182
client: k8sClient,

pkg/ingress/reference_indexer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (i *defaultReferenceIndexer) BuildIngressClassRefIndexes(_ context.Context,
103103
}
104104

105105
func (i *defaultReferenceIndexer) BuildIngressClassParamsRefIndexes(_ context.Context, ingClass *networking.IngressClass) []string {
106-
if ingClass.Spec.Controller != ingressClassControllerALB || ingClass.Spec.Parameters == nil {
106+
if ingClass.Spec.Controller != IngressClassControllerALB || ingClass.Spec.Parameters == nil {
107107
return nil
108108
}
109109
if ingClass.Spec.Parameters.APIGroup == nil ||

webhooks/networking/ingress_validator.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ const (
2525
// NewIngressValidator returns a validator for Ingress API.
2626
func NewIngressValidator(client client.Client, ingConfig config.IngressConfig, logger logr.Logger) *ingressValidator {
2727
return &ingressValidator{
28-
annotationParser: annotations.NewSuffixAnnotationParser(annotations.AnnotationPrefixIngress),
29-
classAnnotationMatcher: ingress.NewDefaultClassAnnotationMatcher(ingConfig.IngressClass),
30-
classLoader: ingress.NewDefaultClassLoader(client),
31-
disableIngressClassAnnotation: ingConfig.DisableIngressClassAnnotation,
32-
disableIngressGroupAnnotation: ingConfig.DisableIngressGroupNameAnnotation,
33-
logger: logger,
28+
annotationParser: annotations.NewSuffixAnnotationParser(annotations.AnnotationPrefixIngress),
29+
classAnnotationMatcher: ingress.NewDefaultClassAnnotationMatcher(ingConfig.IngressClass),
30+
classLoader: ingress.NewDefaultClassLoader(client, false),
31+
disableIngressClassAnnotation: ingConfig.DisableIngressClassAnnotation,
32+
disableIngressGroupAnnotation: ingConfig.DisableIngressGroupNameAnnotation,
33+
manageIngressesWithoutIngressClass: ingConfig.IngressClass == "",
34+
logger: logger,
3435
}
3536
}
3637

@@ -42,7 +43,10 @@ type ingressValidator struct {
4243
classLoader ingress.ClassLoader
4344
disableIngressClassAnnotation bool
4445
disableIngressGroupAnnotation bool
45-
logger logr.Logger
46+
// manageIngressesWithoutIngressClass specifies whether ingresses without "kubernetes.io/ingress.class" annotation
47+
// and "spec.ingressClassName" should be managed or not.
48+
manageIngressesWithoutIngressClass bool
49+
logger logr.Logger
4650
}
4751

4852
func (v *ingressValidator) Prototype(req admission.Request) (runtime.Object, error) {
@@ -51,6 +55,9 @@ func (v *ingressValidator) Prototype(req admission.Request) (runtime.Object, err
5155

5256
func (v *ingressValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error {
5357
ing := obj.(*networking.Ingress)
58+
if skip, err := v.checkIngressClass(ctx, ing); skip || err != nil {
59+
return err
60+
}
5461
if err := v.checkIngressClassAnnotationUsage(ing, nil); err != nil {
5562
return err
5663
}
@@ -69,6 +76,9 @@ func (v *ingressValidator) ValidateCreate(ctx context.Context, obj runtime.Objec
6976
func (v *ingressValidator) ValidateUpdate(ctx context.Context, obj runtime.Object, oldObj runtime.Object) error {
7077
ing := obj.(*networking.Ingress)
7178
oldIng := oldObj.(*networking.Ingress)
79+
if skip, err := v.checkIngressClass(ctx, ing); skip || err != nil {
80+
return err
81+
}
7282
if err := v.checkIngressClassAnnotationUsage(ing, oldIng); err != nil {
7383
return err
7484
}
@@ -88,6 +98,21 @@ func (v *ingressValidator) ValidateDelete(ctx context.Context, obj runtime.Objec
8898
return nil
8999
}
90100

101+
// checkIngressClass checks to see if this ingress is handled by this controller.
102+
func (v *ingressValidator) checkIngressClass(ctx context.Context, ing *networking.Ingress) (bool, error) {
103+
if ingClassAnnotation, exists := ing.Annotations[annotations.IngressClass]; exists {
104+
return !v.classAnnotationMatcher.Matches(ingClassAnnotation), nil
105+
}
106+
classConfiguration, err := v.classLoader.Load(ctx, ing)
107+
if err != nil {
108+
return false, err
109+
}
110+
if classConfiguration.IngClass != nil {
111+
return classConfiguration.IngClass.Spec.Controller != ingress.IngressClassControllerALB, nil
112+
}
113+
return !v.manageIngressesWithoutIngressClass, nil
114+
}
115+
91116
// checkIngressClassAnnotationUsage checks the usage of kubernetes.io/ingress.class annotation.
92117
// kubernetes.io/ingress.class annotation cannot be set to the ingress class for this controller once disabled,
93118
// so that we enforce users to use spec.ingressClassName in Ingress and IngressClass resource instead.

0 commit comments

Comments
 (0)