1
16
17 package defaultingressclass
18
19 import (
20 "context"
21 "reflect"
22 "testing"
23 "time"
24
25 networkingv1 "k8s.io/api/networking/v1"
26 networkingv1beta1 "k8s.io/api/networking/v1beta1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apiserver/pkg/admission"
29 admissiontesting "k8s.io/apiserver/pkg/admission/testing"
30 "k8s.io/client-go/informers"
31 api "k8s.io/kubernetes/pkg/apis/core"
32 "k8s.io/kubernetes/pkg/apis/networking"
33 "k8s.io/kubernetes/pkg/controller"
34 utilpointer "k8s.io/utils/pointer"
35 )
36
37 func TestAdmission(t *testing.T) {
38 defaultClass1 := &networkingv1.IngressClass{
39 TypeMeta: metav1.TypeMeta{
40 Kind: "IngressClass",
41 },
42 ObjectMeta: metav1.ObjectMeta{
43 Name: "default1",
44 Annotations: map[string]string{
45 networkingv1.AnnotationIsDefaultIngressClass: "true",
46 },
47 },
48 }
49 defaultClass2 := &networkingv1.IngressClass{
50 ObjectMeta: metav1.ObjectMeta{
51 Name: "default2",
52 Annotations: map[string]string{
53 networkingv1.AnnotationIsDefaultIngressClass: "true",
54 },
55 },
56 }
57
58 classWithFalseDefault := &networkingv1.IngressClass{
59 TypeMeta: metav1.TypeMeta{
60 Kind: "IngressClass",
61 },
62 ObjectMeta: metav1.ObjectMeta{
63 Name: "nondefault1",
64 Annotations: map[string]string{
65 networkingv1.AnnotationIsDefaultIngressClass: "false",
66 },
67 },
68 }
69
70 classWithNoDefault := &networkingv1.IngressClass{
71 TypeMeta: metav1.TypeMeta{
72 Kind: "IngressClass",
73 },
74 ObjectMeta: metav1.ObjectMeta{
75 Name: "nondefault2",
76 },
77 }
78
79 classWithEmptyDefault := &networkingv1.IngressClass{
80 TypeMeta: metav1.TypeMeta{
81 Kind: "IngressClass",
82 },
83 ObjectMeta: metav1.ObjectMeta{
84 Name: "nondefault2",
85 Annotations: map[string]string{
86 networkingv1.AnnotationIsDefaultIngressClass: "",
87 },
88 },
89 }
90
91 defaultClassWithCreateTime1 := &networkingv1.IngressClass{
92 TypeMeta: metav1.TypeMeta{
93 Kind: "IngressClass",
94 },
95 ObjectMeta: metav1.ObjectMeta{
96 Name: "default1",
97 CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 1, time.UTC)),
98 Annotations: map[string]string{
99 networkingv1.AnnotationIsDefaultIngressClass: "true",
100 },
101 },
102 }
103 defaultClassWithCreateTime2 := &networkingv1.IngressClass{
104 TypeMeta: metav1.TypeMeta{
105 Kind: "IngressClass",
106 },
107 ObjectMeta: metav1.ObjectMeta{
108 Name: "default2",
109 CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
110 Annotations: map[string]string{
111 networkingv1.AnnotationIsDefaultIngressClass: "true",
112 },
113 },
114 }
115
116 testCases := []struct {
117 name string
118 classes []*networkingv1.IngressClass
119 classField *string
120 classAnnotation *string
121 expectedClass *string
122 expectedError error
123 }{
124 {
125 name: "no default, no modification of Ingress",
126 classes: []*networkingv1.IngressClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
127 classField: nil,
128 classAnnotation: nil,
129 expectedClass: nil,
130 expectedError: nil,
131 },
132 {
133 name: "one default, modify Ingress with class=nil",
134 classes: []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
135 classField: nil,
136 classAnnotation: nil,
137 expectedClass: utilpointer.String(defaultClass1.Name),
138 expectedError: nil,
139 },
140 {
141 name: "one default, no modification of Ingress with class field=''",
142 classes: []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
143 classField: utilpointer.String(""),
144 classAnnotation: nil,
145 expectedClass: utilpointer.String(""),
146 expectedError: nil,
147 },
148 {
149 name: "one default, no modification of Ingress with class field='foo'",
150 classes: []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
151 classField: utilpointer.String("foo"),
152 classAnnotation: nil,
153 expectedClass: utilpointer.String("foo"),
154 expectedError: nil,
155 },
156 {
157 name: "one default, no modification of Ingress with class annotation='foo'",
158 classes: []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
159 classField: nil,
160 classAnnotation: utilpointer.String("foo"),
161 expectedClass: nil,
162 expectedError: nil,
163 },
164 {
165 name: "two defaults with the same creation time, choose the one with the lower name",
166 classes: []*networkingv1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
167 classField: nil,
168 classAnnotation: nil,
169 expectedClass: utilpointer.String(defaultClass1.Name),
170 expectedError: nil,
171 },
172 {
173 name: "two defaults, no modification with Ingress with class field=''",
174 classes: []*networkingv1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
175 classField: utilpointer.String(""),
176 classAnnotation: nil,
177 expectedClass: utilpointer.String(""),
178 expectedError: nil,
179 },
180 {
181 name: "two defaults, choose the one with the newer creation time",
182 classes: []*networkingv1.IngressClass{defaultClassWithCreateTime1, defaultClassWithCreateTime2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
183 classField: nil,
184 classAnnotation: nil,
185 expectedClass: utilpointer.String(defaultClassWithCreateTime1.Name),
186 expectedError: nil,
187 },
188 }
189
190 for _, testCase := range testCases {
191 t.Run(testCase.name, func(t *testing.T) {
192 ctrl := newPlugin()
193 informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
194 ctrl.SetExternalKubeInformerFactory(informerFactory)
195 for _, c := range testCase.classes {
196 informerFactory.Networking().V1().IngressClasses().Informer().GetStore().Add(c)
197 }
198
199 ingress := &networking.Ingress{ObjectMeta: metav1.ObjectMeta{Name: "testing", Namespace: "testing"}}
200 if testCase.classField != nil {
201 ingress.Spec.IngressClassName = testCase.classField
202 }
203 if testCase.classAnnotation != nil {
204 ingress.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: *testCase.classAnnotation}
205 }
206
207 attrs := admission.NewAttributesRecord(
208 ingress,
209 nil,
210 api.Kind("Ingress").WithVersion("version"),
211 ingress.Namespace,
212 ingress.Name,
213 networkingv1.Resource("ingresses").WithVersion("version"),
214 "",
215 admission.Create,
216 &metav1.CreateOptions{},
217 false,
218 nil,
219 )
220
221 err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(context.TODO(), attrs, nil)
222 if !reflect.DeepEqual(err, testCase.expectedError) {
223 t.Errorf("Expected error: %v, got %v", testCase.expectedError, err)
224 }
225 if !reflect.DeepEqual(testCase.expectedClass, ingress.Spec.IngressClassName) {
226 t.Errorf("Expected class name %+v, got %+v", *testCase.expectedClass, ingress.Spec.IngressClassName)
227 }
228 })
229 }
230 }
231
View as plain text