forked from gruntwork-io/helm-kubernetes-services
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathk8s_service_config_injection_template_test.go
561 lines (498 loc) · 19.2 KB
/
k8s_service_config_injection_template_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
// +build all tpl
// NOTE: We use build flags to differentiate between template tests and integration tests so that you can conveniently
// run just the template tests. See the test README for more information.
package test
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/gruntwork-io/terratest/modules/helm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
)
// Test that setting the `envVars` input value to empty object leaves env vars out of the pod.
func TestK8SServiceEnvVarConfigMapsSecretsEmptyDoesNotAddEnvVarsAndVolumesToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(t, map[string]string{})
// Verify that there is only one container
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
// ... and that there are no environments
environments := appContainer.Env
assert.Equal(t, len(environments), 0)
// ... or volumes configured
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
assert.Equal(t, len(renderedPodVolumes), 0)
}
// Test that setting the `envVars` input value will include those environment vars
// We test by injecting to the envVars:
// DB_HOST: "mysql.default.svc.cluster.local"
// DB_PORT: 3306
func TestK8SServiceEnvVarAddsEnvVarsToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"envVars.DB_HOST": "mysql.default.svc.cluster.local",
"envVars.DB_PORT": "3306",
},
)
// Verify that there is only one container and that the environments section is populated.
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
environments := appContainer.Env
assert.Equal(t, len(environments), 2)
renderedEnvVar := map[string]string{}
for _, env := range environments {
renderedEnvVar[env.Name] = env.Value
}
assert.Equal(t, renderedEnvVar["DB_HOST"], "mysql.default.svc.cluster.local")
assert.Equal(t, renderedEnvVar["DB_PORT"], "3306")
}
// Test that setting the `additionalContainerEnv` input value will include those environment vars
// We test by injecting:
// additionalContainerEnv:
// - name: DD_AGENT_HOST
// valueFrom:
// fieldRef:
// fieldPath: status.hostIP
// - name: DD_ENTITY_ID
// valueFrom:
// fieldRef:
// fieldPath: metadata.uid
func TestK8SServiceAdditionalEnvVarAddsEnvVarsToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"additionalContainerEnv[0].name": "DD_AGENT_HOST",
"additionalContainerEnv[0].valueFrom.fieldRef.fieldPath": "status.hostIP",
"additionalContainerEnv[1].name": "DD_ENTITY_ID",
"additionalContainerEnv[1].valueFrom.fieldRef.fieldPath": "metadata.uid",
},
)
// Verify that there is only one container and that the environments section is populated.
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
environments := appContainer.Env
assert.Equal(t, len(environments), 2)
renderedEnvVar := map[string]string{}
for _, env := range environments {
renderedEnvVar[env.Name] = env.ValueFrom.FieldRef.FieldPath
}
assert.Equal(t, renderedEnvVar["DD_AGENT_HOST"], "status.hostIP")
assert.Equal(t, renderedEnvVar["DD_ENTITY_ID"], "metadata.uid")
}
// Test that setting the `configMaps` input value with environment include those environment vars
// We test by injecting to configMaps:
// configMaps:
// dbsettings:
// as: environment
// items:
// host:
// envVarName: DB_HOST
// port:
// envVarName: DB_PORT
func TestK8SServiceEnvironmentConfigMapAddsEnvVarsToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"configMaps.dbsettings.as": "environment",
"configMaps.dbsettings.items.host.envVarName": "DB_HOST",
"configMaps.dbsettings.items.port.envVarName": "DB_PORT",
},
)
// Verify that there is only one container and that the environments section is empty.
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
environments := appContainer.Env
assert.Equal(t, len(environments), 2)
// Read in the configured env vars for convenient mapping of env var name
renderedEnvVar := map[string]corev1.EnvVar{}
for _, env := range environments {
renderedEnvVar[env.Name] = env
}
// Verify the DB_HOST env var comes from config map host key of dbsettings
assert.Equal(t, renderedEnvVar["DB_HOST"].Value, "")
require.NotNil(t, renderedEnvVar["DB_HOST"].ValueFrom)
require.NotNil(t, renderedEnvVar["DB_HOST"].ValueFrom.ConfigMapKeyRef)
assert.Equal(t, renderedEnvVar["DB_HOST"].ValueFrom.ConfigMapKeyRef.Key, "host")
assert.Equal(t, renderedEnvVar["DB_HOST"].ValueFrom.ConfigMapKeyRef.Name, "dbsettings")
// Verify the DB_PORT env var comes from config map port key of dbsettings
assert.Equal(t, renderedEnvVar["DB_PORT"].Value, "")
require.NotNil(t, renderedEnvVar["DB_PORT"].ValueFrom)
require.NotNil(t, renderedEnvVar["DB_PORT"].ValueFrom.ConfigMapKeyRef)
assert.Equal(t, renderedEnvVar["DB_PORT"].ValueFrom.ConfigMapKeyRef.Key, "port")
assert.Equal(t, renderedEnvVar["DB_PORT"].ValueFrom.ConfigMapKeyRef.Name, "dbsettings")
}
// Test that setting the `configMaps` input value with volume include the volume mount for the config map
// We test by injecting to configMaps:
// configMaps:
// dbsettings:
// as: volume
// mountPath: /etc/db
func TestK8SServiceVolumeConfigMapAddsVolumeAndVolumeMountToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"configMaps.dbsettings.as": "volume",
"configMaps.dbsettings.mountPath": "/etc/db",
},
)
// Verify that there is only one container and only one volume
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 1)
podVolume := renderedPodVolumes[0]
// Check that the pod volume is a configmap volume
assert.Equal(t, podVolume.Name, "dbsettings-volume")
require.NotNil(t, podVolume.ConfigMap)
assert.Equal(t, podVolume.ConfigMap.Name, "dbsettings")
// Check that the pod volume will be mounted
require.Equal(t, len(appContainer.VolumeMounts), 1)
volumeMount := appContainer.VolumeMounts[0]
assert.Equal(t, volumeMount.Name, "dbsettings-volume")
assert.Equal(t, volumeMount.MountPath, "/etc/db")
}
// Test that setting the `configMaps` input value with volume and individual file mount paths will set the appropriate
// settings
// We test by injecting to configMaps:
// configMaps:
// dbsettings:
// as: volume
// mountPath: /etc/db
// items:
// host:
// filePath: host.txt
func TestK8SServiceVolumeConfigMapWithKeyFilePathAddsVolumeWithKeyFilePathToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"configMaps.dbsettings.as": "volume",
"configMaps.dbsettings.mountPath": "/etc/db",
"configMaps.dbsettings.items.host.filePath": "host.txt",
},
)
// Verify that there is only one volume
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 1)
podVolume := renderedPodVolumes[0]
// Check that the pod volume is a configmap volume and has a file path instruction for host key
assert.Equal(t, podVolume.Name, "dbsettings-volume")
require.NotNil(t, podVolume.ConfigMap)
assert.Equal(t, podVolume.ConfigMap.Name, "dbsettings")
require.Equal(t, len(podVolume.ConfigMap.Items), 1)
keyToPath := podVolume.ConfigMap.Items[0]
assert.Equal(t, keyToPath.Key, "host")
assert.Equal(t, keyToPath.Path, "host.txt")
}
// Test the file mode calculation. We test by injecting to configMaps the following with different file mode octals and
// validating the decimal:
// configMaps:
// dbsettings:
// as: volume
// mountPath: /etc/db
// items:
// host:
// filePath: host.txt
// fileMode: 644
func TestK8SServiceVolumeConfigMapWithKeyFileModeConvertsOctalToDecimal(t *testing.T) {
t.Parallel()
testCases := []struct {
octal string
decimal int32
}{
{"644", 420},
{"700", 448},
{"755", 493},
{"777", 511},
}
for _, testCase := range testCases {
// Capture range variable to force scope
testCase := testCase
t.Run(testCase.octal, func(t *testing.T) {
t.Parallel()
checkFileMode(t, "configMaps", testCase.octal, testCase.decimal)
})
}
}
// Test that setting the `secrets` input value with environment include those environment vars
// We test by injecting to secrets:
// secrets:
// dbsettings:
// as: environment
// items:
// host:
// envVarName: DB_HOST
// port:
// envVarName: DB_PORT
func TestK8SServiceEnvironmentSecretAddsEnvVarsToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"secrets.dbsettings.as": "environment",
"secrets.dbsettings.items.host.envVarName": "DB_HOST",
"secrets.dbsettings.items.port.envVarName": "DB_PORT",
},
)
// Verify that there is only one container and that the environments section is empty.
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
environments := appContainer.Env
assert.Equal(t, len(environments), 2)
// Read in the configured env vars for convenient mapping of env var name
renderedEnvVar := map[string]corev1.EnvVar{}
for _, env := range environments {
renderedEnvVar[env.Name] = env
}
// Verify the DB_HOST env var comes from secret host key of dbsettings
assert.Equal(t, renderedEnvVar["DB_HOST"].Value, "")
require.NotNil(t, renderedEnvVar["DB_HOST"].ValueFrom)
require.NotNil(t, renderedEnvVar["DB_HOST"].ValueFrom.SecretKeyRef)
assert.Equal(t, renderedEnvVar["DB_HOST"].ValueFrom.SecretKeyRef.Key, "host")
assert.Equal(t, renderedEnvVar["DB_HOST"].ValueFrom.SecretKeyRef.Name, "dbsettings")
// Verify the DB_PORT env var comes from secret port key of dbsettings
assert.Equal(t, renderedEnvVar["DB_PORT"].Value, "")
require.NotNil(t, renderedEnvVar["DB_PORT"].ValueFrom)
require.NotNil(t, renderedEnvVar["DB_PORT"].ValueFrom.SecretKeyRef)
assert.Equal(t, renderedEnvVar["DB_PORT"].ValueFrom.SecretKeyRef.Key, "port")
assert.Equal(t, renderedEnvVar["DB_PORT"].ValueFrom.SecretKeyRef.Name, "dbsettings")
}
// Test that setting the `secrets` input value with volume include the volume mount for the secret
// We test by injecting to secrets:
// secrets:
// dbsettings:
// as: volume
// mountPath: /etc/db
func TestK8SServiceVolumeSecretAddsVolumeAndVolumeMountToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"secrets.dbsettings.as": "volume",
"secrets.dbsettings.mountPath": "/etc/db",
},
)
// Verify that there is only one container and only one volume
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 1)
podVolume := renderedPodVolumes[0]
// Check that the pod volume is a secret volume
assert.Equal(t, podVolume.Name, "dbsettings-volume")
require.NotNil(t, podVolume.Secret)
assert.Equal(t, podVolume.Secret.SecretName, "dbsettings")
// Check that the pod volume will be mounted
require.Equal(t, len(appContainer.VolumeMounts), 1)
volumeMount := appContainer.VolumeMounts[0]
assert.Equal(t, volumeMount.Name, "dbsettings-volume")
assert.Equal(t, volumeMount.MountPath, "/etc/db")
}
// Test that setting the `secrets` input value with volume and individual file mount paths will set the appropriate
// settings
// We test by injecting to secrets:
// secrets:
// dbsettings:
// as: volume
// mountPath: /etc/db
// items:
// host:
// filePath: host.txt
func TestK8SServiceVolumeSecretWithKeyFilePathAddsVolumeWithKeyFilePathToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"secrets.dbsettings.as": "volume",
"secrets.dbsettings.mountPath": "/etc/db",
"secrets.dbsettings.items.host.filePath": "host.txt",
},
)
// Verify that there is only one volume
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 1)
podVolume := renderedPodVolumes[0]
// Check that the pod volume is a secret volume and has a file path instruction for host key
assert.Equal(t, podVolume.Name, "dbsettings-volume")
require.NotNil(t, podVolume.Secret)
assert.Equal(t, podVolume.Secret.SecretName, "dbsettings")
require.Equal(t, len(podVolume.Secret.Items), 1)
keyToPath := podVolume.Secret.Items[0]
assert.Equal(t, keyToPath.Key, "host")
assert.Equal(t, keyToPath.Path, "host.txt")
}
// Test the file mode calculation. We test by injecting to secrets the following with different file mode octals and
// validating the decimal:
// secrets:
// dbsettings:
// as: volume
// mountPath: /etc/db
// items:
// host:
// filePath: host.txt
// fileMode: 644
func TestK8SServiceVolumeSecretWithKeyFileModeConvertsOctalToDecimal(t *testing.T) {
t.Parallel()
testCases := []struct {
octal string
decimal int32
}{
{"644", 420},
{"700", 448},
{"755", 493},
{"777", 511},
}
for _, testCase := range testCases {
// Capture range variable to force scope
testCase := testCase
t.Run(testCase.octal, func(t *testing.T) {
t.Parallel()
checkFileMode(t, "secrets", testCase.octal, testCase.decimal)
})
}
}
// Test the file mode calculation assertions. We test by injecting to secrets the following with different file mode
// octals and checking that it fails
// secrets:
// dbsettings:
// as: volume
// mountPath: /etc/db
// items:
// host:
// filePath: host.txt
// fileMode: 644
func TestK8SServiceFileModeOctalToDecimalAssertions(t *testing.T) {
t.Parallel()
helmChartPath, err := filepath.Abs(filepath.Join("..", "charts", "k8s-service"))
require.NoError(t, err)
testCases := []string{
"800", // First digit greater than max (7)
"080", // Second digit greater than max (7)
"008", // Third digit greater than max (7)
"nan", // Not a number
"75n", // Not a number
"0644", // Not three digits
"44", // Not three digits
}
for _, testCase := range testCases {
// Capture range variable to force scope
testCase := testCase
t.Run(testCase, func(t *testing.T) {
t.Parallel()
// We make sure to pass in the linter_values.yaml values file, which we assume has all the required values
// defined.
options := &helm.Options{
ValuesFiles: []string{filepath.Join("..", "charts", "k8s-service", "linter_values.yaml")},
SetValues: map[string]string{
"secrets.dbsettings.as": "volume",
"secrets.dbsettings.mountPath": "/etc/db",
"secrets.dbsettings.items.host.filePath": "host.txt",
"secrets.dbsettings.items.host.fileMode": testCase,
},
}
// Render just the deployment resource
_, err := helm.RenderTemplateE(t, options, helmChartPath, strings.ToLower(t.Name()), []string{"templates/deployment.yaml"})
assert.Error(t, err)
})
}
}
// Test that setting the `secrets` and `configMaps` input value with volume include the volume mount for both.
// We test by injecting to secrets and configMaps:
// configMaps:
// dbsettings:
// as: volume
// mountPath: /etc/db
// secrets:
// dbpassword:
// as: volume
// mountPath: /etc/dbpass
func TestK8SServiceVolumeSecretAndConfigMapAddsBothVolumesAndVolumeMountsToPod(t *testing.T) {
t.Parallel()
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
"configMaps.dbsettings.as": "volume",
"configMaps.dbsettings.mountPath": "/etc/db",
"secrets.dbpassword.as": "volume",
"secrets.dbpassword.mountPath": "/etc/dbpass",
},
)
// Verify that there is only one container and only one volume
renderedPodContainers := deployment.Spec.Template.Spec.Containers
require.Equal(t, len(renderedPodContainers), 1)
appContainer := renderedPodContainers[0]
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 2)
// Map volumes to a map for easy lookup
volumes := map[string]corev1.Volume{}
for _, volume := range renderedPodVolumes {
volumes[volume.Name] = volume
}
// Check configMap pod volume exists
volume := volumes["dbsettings-volume"]
require.NotNil(t, volume.ConfigMap)
assert.Equal(t, volume.ConfigMap.Name, "dbsettings")
// Check secret pod volume exists
volume = volumes["dbpassword-volume"]
require.NotNil(t, volume.Secret)
assert.Equal(t, volume.Secret.SecretName, "dbpassword")
// Check that both volumes will be mounted on the pod in the specified paths
volumeMounts := map[string]corev1.VolumeMount{}
for _, mount := range appContainer.VolumeMounts {
volumeMounts[mount.Name] = mount
}
assert.Equal(t, volumeMounts["dbsettings-volume"].MountPath, "/etc/db")
assert.Equal(t, volumeMounts["dbpassword-volume"].MountPath, "/etc/dbpass")
}
func checkFileMode(t *testing.T, configMapsOrSecrets string, fileModeOctal string, fileModeDecimal int32) {
deployment := renderK8SServiceDeploymentWithSetValues(
t,
map[string]string{
fmt.Sprintf("%s.dbsettings.as", configMapsOrSecrets): "volume",
fmt.Sprintf("%s.dbsettings.mountPath", configMapsOrSecrets): "/etc/db",
fmt.Sprintf("%s.dbsettings.items.host.filePath", configMapsOrSecrets): "host.txt",
fmt.Sprintf("%s.dbsettings.items.host.fileMode", configMapsOrSecrets): fileModeOctal,
},
)
// Verify that there is only one volume
renderedPodVolumes := deployment.Spec.Template.Spec.Volumes
require.Equal(t, len(renderedPodVolumes), 1)
podVolume := renderedPodVolumes[0]
assert.Equal(t, podVolume.Name, "dbsettings-volume")
// Check that the pod volume is a configmap/secret volume and has a file mode instruction with decimal value of octal
switch configMapsOrSecrets {
case "configMaps":
require.NotNil(t, podVolume.ConfigMap)
assert.Equal(t, podVolume.ConfigMap.Name, "dbsettings")
require.Equal(t, len(podVolume.ConfigMap.Items), 1)
keyToPath := podVolume.ConfigMap.Items[0]
assert.Equal(t, keyToPath.Key, "host")
require.NotNil(t, keyToPath.Mode)
assert.Equal(t, *keyToPath.Mode, fileModeDecimal)
case "secrets":
require.NotNil(t, podVolume.Secret)
assert.Equal(t, podVolume.Secret.SecretName, "dbsettings")
require.Equal(t, len(podVolume.Secret.Items), 1)
keyToPath := podVolume.Secret.Items[0]
assert.Equal(t, keyToPath.Key, "host")
require.NotNil(t, keyToPath.Mode)
assert.Equal(t, *keyToPath.Mode, fileModeDecimal)
default:
t.Fatalf("Unexpected attribute name: %s", configMapsOrSecrets)
}
}