1 package inject
2
3 import (
4 "fmt"
5 "testing"
6
7 "github.com/linkerd/linkerd2/pkg/k8s"
8 corev1 "k8s.io/api/core/v1"
9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 )
11
12 func TestInjectable(t *testing.T) {
13 var testCases = []struct {
14 podSpec *corev1.PodSpec
15 podMeta *metav1.ObjectMeta
16 nsAnnotations map[string]string
17 unsupportedResource bool
18 injectable bool
19 reasons []string
20 }{
21 {
22 podSpec: &corev1.PodSpec{
23 HostNetwork: false,
24 Containers: []corev1.Container{
25 {
26 VolumeMounts: []corev1.VolumeMount{
27 {
28 MountPath: k8s.MountPathServiceAccount,
29 },
30 },
31 },
32 },
33 },
34 podMeta: &metav1.ObjectMeta{
35 Annotations: map[string]string{
36 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
37 },
38 },
39 injectable: true,
40 },
41 {
42 podSpec: &corev1.PodSpec{
43 HostNetwork: true,
44 Containers: []corev1.Container{
45 {
46 VolumeMounts: []corev1.VolumeMount{
47 {
48 MountPath: k8s.MountPathServiceAccount,
49 },
50 },
51 },
52 },
53 },
54 podMeta: &metav1.ObjectMeta{
55 Annotations: map[string]string{
56 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
57 },
58 },
59 injectable: false,
60 reasons: []string{hostNetworkEnabled},
61 },
62 {
63 podSpec: &corev1.PodSpec{
64 Containers: []corev1.Container{
65 {
66 Name: k8s.ProxyContainerName,
67 Image: "cr.l5d.io/linkerd/proxy:",
68 VolumeMounts: []corev1.VolumeMount{
69 {
70 MountPath: k8s.MountPathServiceAccount,
71 },
72 },
73 },
74 },
75 },
76 podMeta: &metav1.ObjectMeta{
77 Annotations: map[string]string{
78 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
79 },
80 },
81 injectable: false,
82 reasons: []string{sidecarExists},
83 },
84 {
85 podSpec: &corev1.PodSpec{
86 InitContainers: []corev1.Container{
87 {
88 Name: k8s.InitContainerName,
89 Image: "cr.l5d.io/linkerd/proxy-init:",
90 },
91 },
92 Containers: []corev1.Container{
93 {
94 VolumeMounts: []corev1.VolumeMount{
95 {
96 MountPath: k8s.MountPathServiceAccount,
97 },
98 },
99 },
100 },
101 },
102 podMeta: &metav1.ObjectMeta{
103 Annotations: map[string]string{
104 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
105 },
106 },
107 injectable: false,
108 reasons: []string{sidecarExists},
109 },
110 {
111 unsupportedResource: true,
112 podSpec: &corev1.PodSpec{
113 Containers: []corev1.Container{
114 {
115 VolumeMounts: []corev1.VolumeMount{
116 {
117 MountPath: k8s.MountPathServiceAccount,
118 },
119 },
120 },
121 },
122 },
123 podMeta: &metav1.ObjectMeta{
124 Annotations: map[string]string{
125 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
126 },
127 },
128 injectable: false,
129 reasons: []string{unsupportedResource},
130 },
131 {
132 unsupportedResource: true,
133 podSpec: &corev1.PodSpec{
134 HostNetwork: true,
135 Containers: []corev1.Container{
136 {
137 VolumeMounts: []corev1.VolumeMount{
138 {
139 MountPath: k8s.MountPathServiceAccount,
140 },
141 },
142 },
143 },
144 },
145 podMeta: &metav1.ObjectMeta{
146 Annotations: map[string]string{
147 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
148 },
149 },
150
151 injectable: false,
152 reasons: []string{hostNetworkEnabled, unsupportedResource},
153 },
154 {
155 nsAnnotations: map[string]string{
156 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
157 },
158 podSpec: &corev1.PodSpec{
159 HostNetwork: true,
160 Containers: []corev1.Container{
161 {
162 VolumeMounts: []corev1.VolumeMount{
163 {
164 MountPath: k8s.MountPathServiceAccount,
165 },
166 },
167 },
168 },
169 },
170 podMeta: &metav1.ObjectMeta{
171 Annotations: map[string]string{
172 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
173 },
174 },
175
176 injectable: false,
177 reasons: []string{hostNetworkEnabled, injectDisableAnnotationPresent},
178 },
179 {
180 nsAnnotations: map[string]string{
181 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
182 },
183 unsupportedResource: true,
184 podSpec: &corev1.PodSpec{
185 HostNetwork: true,
186 Containers: []corev1.Container{
187 {
188 VolumeMounts: []corev1.VolumeMount{
189 {
190 MountPath: k8s.MountPathServiceAccount,
191 },
192 },
193 },
194 },
195 },
196 podMeta: &metav1.ObjectMeta{
197 Annotations: map[string]string{
198 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
199 },
200 },
201
202 injectable: false,
203 reasons: []string{hostNetworkEnabled, unsupportedResource, injectDisableAnnotationPresent},
204 },
205 {
206 unsupportedResource: true,
207 podSpec: &corev1.PodSpec{
208 HostNetwork: true,
209 Containers: []corev1.Container{
210 {
211 VolumeMounts: []corev1.VolumeMount{
212 {
213 MountPath: k8s.MountPathServiceAccount,
214 },
215 },
216 },
217 },
218 },
219 podMeta: &metav1.ObjectMeta{
220 Annotations: map[string]string{},
221 },
222
223 injectable: false,
224 reasons: []string{hostNetworkEnabled, unsupportedResource, injectEnableAnnotationAbsent},
225 },
226 {
227 podSpec: &corev1.PodSpec{HostNetwork: true,
228 Containers: []corev1.Container{
229 {
230 Name: k8s.ProxyContainerName,
231 Image: "cr.l5d.io/linkerd/proxy:",
232 VolumeMounts: []corev1.VolumeMount{
233 {
234 MountPath: k8s.MountPathServiceAccount,
235 },
236 },
237 },
238 }},
239 podMeta: &metav1.ObjectMeta{
240 Annotations: map[string]string{},
241 },
242
243 injectable: false,
244 reasons: []string{hostNetworkEnabled, sidecarExists, injectEnableAnnotationAbsent},
245 },
246 }
247
248 for i, testCase := range testCases {
249 testCase := testCase
250 t.Run(fmt.Sprintf("test case #%d", i), func(t *testing.T) {
251 resourceConfig := &ResourceConfig{}
252 resourceConfig.WithNsAnnotations(testCase.nsAnnotations)
253 resourceConfig.pod.spec = testCase.podSpec
254 resourceConfig.origin = OriginWebhook
255 resourceConfig.pod.meta = testCase.podMeta
256
257 report := newReport(resourceConfig)
258 report.UnsupportedResource = testCase.unsupportedResource
259
260 actual, reasons := report.Injectable()
261 if testCase.injectable != actual {
262 t.Errorf("Expected %t. Actual %t", testCase.injectable, actual)
263 }
264
265 if len(reasons) != len(testCase.reasons) {
266 t.Errorf("Expected %d number of reasons. Actual %d", len(testCase.reasons), len(reasons))
267 }
268
269 for i := range reasons {
270 if testCase.reasons[i] != reasons[i] {
271 t.Errorf("Expected reason '%s'. Actual reason '%s'", testCase.reasons[i], reasons[i])
272 }
273 }
274
275 })
276 }
277 }
278
279 func TestDisableByAnnotation(t *testing.T) {
280 t.Run("webhook origin", func(t *testing.T) {
281 var testCases = []struct {
282 podMeta *metav1.ObjectMeta
283 nsAnnotations map[string]string
284 expected bool
285 }{
286 {
287 podMeta: &metav1.ObjectMeta{
288 Annotations: map[string]string{
289 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
290 },
291 },
292 expected: false,
293 },
294 {
295 podMeta: &metav1.ObjectMeta{
296 Annotations: map[string]string{
297 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
298 },
299 },
300 nsAnnotations: map[string]string{
301 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
302 },
303 expected: false,
304 },
305 {
306 podMeta: &metav1.ObjectMeta{
307 Annotations: map[string]string{
308 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
309 },
310 },
311 nsAnnotations: map[string]string{
312 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
313 },
314 expected: false,
315 },
316 {
317 podMeta: &metav1.ObjectMeta{},
318 nsAnnotations: map[string]string{
319 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
320 },
321 expected: false,
322 },
323 {
324 podMeta: &metav1.ObjectMeta{
325 Annotations: map[string]string{
326 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
327 },
328 },
329 nsAnnotations: map[string]string{
330 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
331 },
332 expected: true,
333 },
334 {
335 podMeta: &metav1.ObjectMeta{
336 Annotations: map[string]string{
337 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
338 },
339 },
340 nsAnnotations: map[string]string{
341 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
342 },
343 expected: true,
344 },
345 {
346 podMeta: &metav1.ObjectMeta{
347 Annotations: map[string]string{
348 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
349 },
350 },
351 nsAnnotations: map[string]string{},
352 expected: true,
353 },
354 {
355 podMeta: &metav1.ObjectMeta{},
356 nsAnnotations: map[string]string{
357 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
358 },
359 expected: true,
360 },
361 {
362 podMeta: &metav1.ObjectMeta{},
363 nsAnnotations: map[string]string{},
364 expected: true,
365 },
366 }
367
368 for i, testCase := range testCases {
369 testCase := testCase
370 t.Run(fmt.Sprintf("test case #%d", i), func(t *testing.T) {
371 resourceConfig := &ResourceConfig{origin: OriginWebhook}
372 resourceConfig.WithNsAnnotations(testCase.nsAnnotations)
373 resourceConfig.pod.meta = testCase.podMeta
374 resourceConfig.pod.spec = &corev1.PodSpec{}
375
376 report := newReport(resourceConfig)
377 if actual, _, _ := report.disabledByAnnotation(resourceConfig); testCase.expected != actual {
378 t.Errorf("Expected %t. Actual %t", testCase.expected, actual)
379 }
380 })
381 }
382 })
383
384 t.Run("CLI origin", func(t *testing.T) {
385 var testCases = []struct {
386 podMeta *metav1.ObjectMeta
387 expected bool
388 }{
389 {
390 podMeta: &metav1.ObjectMeta{},
391 expected: false,
392 },
393 {
394 podMeta: &metav1.ObjectMeta{
395 Annotations: map[string]string{
396 k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled,
397 },
398 },
399 expected: false,
400 },
401 {
402 podMeta: &metav1.ObjectMeta{
403 Annotations: map[string]string{
404 k8s.ProxyInjectAnnotation: k8s.ProxyInjectDisabled,
405 },
406 },
407 expected: true,
408 },
409 }
410
411 for i, testCase := range testCases {
412 testCase := testCase
413 t.Run(fmt.Sprintf("test case #%d", i), func(t *testing.T) {
414 resourceConfig := &ResourceConfig{origin: OriginCLI}
415 resourceConfig.pod.meta = testCase.podMeta
416 resourceConfig.pod.spec = &corev1.PodSpec{}
417
418 report := newReport(resourceConfig)
419 if actual, _, _ := report.disabledByAnnotation(resourceConfig); testCase.expected != actual {
420 t.Errorf("Expected %t. Actual %t", testCase.expected, actual)
421 }
422 })
423 }
424 })
425 }
426
View as plain text