1
16
17 package create
18
19 import (
20 "os"
21 "testing"
22
23 "github.com/stretchr/testify/require"
24
25 corev1 "k8s.io/api/core/v1"
26 apiequality "k8s.io/apimachinery/pkg/api/equality"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 )
29
30 func TestCreateSecretObject(t *testing.T) {
31 secretObject := newSecretObj("foo", "foo-namespace", corev1.SecretTypeDockerConfigJson)
32 expectedSecretObject := &corev1.Secret{
33 TypeMeta: metav1.TypeMeta{
34 APIVersion: corev1.SchemeGroupVersion.String(),
35 Kind: "Secret",
36 },
37 ObjectMeta: metav1.ObjectMeta{
38 Name: "foo",
39 Namespace: "foo-namespace",
40 },
41 Type: corev1.SecretTypeDockerConfigJson,
42 Data: map[string][]byte{},
43 }
44 t.Run("Creating a Secret Object", func(t *testing.T) {
45 if !apiequality.Semantic.DeepEqual(secretObject, expectedSecretObject) {
46 t.Errorf("expected:\n%#v\ngot:\n%#v", secretObject, expectedSecretObject)
47 }
48 })
49 }
50
51 func TestCreateSecretGeneric(t *testing.T) {
52 tests := map[string]struct {
53 secretName string
54 secretType string
55 fromLiteral []string
56 fromFile []string
57 fromEnvFile []string
58 appendHash bool
59 setup func(t *testing.T, secretGenericOptions *CreateSecretOptions) func()
60
61 expected *corev1.Secret
62 expectErr string
63 }{
64 "create_secret_foo": {
65 secretName: "foo",
66 expected: &corev1.Secret{
67 TypeMeta: metav1.TypeMeta{
68 APIVersion: corev1.SchemeGroupVersion.String(),
69 Kind: "Secret",
70 },
71 ObjectMeta: metav1.ObjectMeta{
72 Name: "foo",
73 },
74 Data: map[string][]byte{},
75 },
76 },
77 "create_secret_foo_hash": {
78 secretName: "foo",
79 appendHash: true,
80 expected: &corev1.Secret{
81 TypeMeta: metav1.TypeMeta{
82 APIVersion: corev1.SchemeGroupVersion.String(),
83 Kind: "Secret",
84 },
85 ObjectMeta: metav1.ObjectMeta{
86 Name: "foo-949tdgdkgg",
87 },
88 Data: map[string][]byte{},
89 },
90 },
91 "create_secret_foo_type": {
92 secretName: "foo",
93 secretType: "my-type",
94 expected: &corev1.Secret{
95 TypeMeta: metav1.TypeMeta{
96 APIVersion: corev1.SchemeGroupVersion.String(),
97 Kind: "Secret",
98 },
99 ObjectMeta: metav1.ObjectMeta{
100 Name: "foo",
101 },
102 Data: map[string][]byte{},
103 Type: "my-type",
104 },
105 },
106 "create_secret_foo_type_hash": {
107 secretName: "foo",
108 secretType: "my-type",
109 appendHash: true,
110 expected: &corev1.Secret{
111 TypeMeta: metav1.TypeMeta{
112 APIVersion: corev1.SchemeGroupVersion.String(),
113 Kind: "Secret",
114 },
115 ObjectMeta: metav1.ObjectMeta{
116 Name: "foo-dg474f9t76",
117 },
118 Data: map[string][]byte{},
119 Type: "my-type",
120 },
121 },
122 "create_secret_foo_two_literal": {
123 secretName: "foo",
124 fromLiteral: []string{"key1=value1", "key2=value2"},
125 expected: &corev1.Secret{
126 TypeMeta: metav1.TypeMeta{
127 APIVersion: corev1.SchemeGroupVersion.String(),
128 Kind: "Secret",
129 },
130 ObjectMeta: metav1.ObjectMeta{
131 Name: "foo",
132 },
133 Data: map[string][]byte{
134 "key1": []byte("value1"),
135 "key2": []byte("value2"),
136 },
137 },
138 },
139 "create_secret_foo_two_literal_hash": {
140 secretName: "foo",
141 fromLiteral: []string{"key1=value1", "key2=value2"},
142 appendHash: true,
143 expected: &corev1.Secret{
144 TypeMeta: metav1.TypeMeta{
145 APIVersion: corev1.SchemeGroupVersion.String(),
146 Kind: "Secret",
147 },
148 ObjectMeta: metav1.ObjectMeta{
149 Name: "foo-tf72c228m4",
150 },
151 Data: map[string][]byte{
152 "key1": []byte("value1"),
153 "key2": []byte("value2"),
154 },
155 },
156 },
157 "create_secret_foo_key1_=value1": {
158 secretName: "foo",
159 fromLiteral: []string{"key1==value1"},
160 expected: &corev1.Secret{
161 TypeMeta: metav1.TypeMeta{
162 APIVersion: corev1.SchemeGroupVersion.String(),
163 Kind: "Secret",
164 },
165 ObjectMeta: metav1.ObjectMeta{
166 Name: "foo",
167 },
168 Data: map[string][]byte{
169 "key1": []byte("=value1"),
170 },
171 },
172 },
173 "create_secret_foo_key1_=value1_hash": {
174 secretName: "foo",
175 fromLiteral: []string{"key1==value1"},
176 appendHash: true,
177 expected: &corev1.Secret{
178 TypeMeta: metav1.TypeMeta{
179 APIVersion: corev1.SchemeGroupVersion.String(),
180 Kind: "Secret",
181 },
182 ObjectMeta: metav1.ObjectMeta{
183 Name: "foo-fdcc8tkhh5",
184 },
185 Data: map[string][]byte{
186 "key1": []byte("=value1"),
187 },
188 },
189 },
190 "create_secret_foo_from_file_foo1_foo2_secret": {
191 secretName: "foo",
192 setup: setupSecretBinaryFile([]byte{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}),
193 fromFile: []string{"foo1", "foo2"},
194 expected: &corev1.Secret{
195 TypeMeta: metav1.TypeMeta{
196 APIVersion: corev1.SchemeGroupVersion.String(),
197 Kind: "Secret",
198 },
199 ObjectMeta: metav1.ObjectMeta{
200 Name: "foo",
201 },
202 Data: map[string][]byte{
203 "foo1": []byte("hello world"),
204 "foo2": []byte("hello world"),
205 },
206 },
207 },
208 "create_secret_foo_from_file_foo1_foo2_hash": {
209 secretName: "foo",
210 setup: setupSecretBinaryFile([]byte{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}),
211 fromFile: []string{"foo1", "foo2"},
212 appendHash: true,
213 expected: &corev1.Secret{
214 TypeMeta: metav1.TypeMeta{
215 APIVersion: corev1.SchemeGroupVersion.String(),
216 Kind: "Secret",
217 },
218 ObjectMeta: metav1.ObjectMeta{
219 Name: "foo-hbkh2cdb57",
220 },
221 Data: map[string][]byte{
222 "foo1": []byte("hello world"),
223 "foo2": []byte("hello world"),
224 },
225 },
226 },
227 "create_secret_foo_from_file_foo1_foo2_and": {
228 secretName: "foo",
229 setup: setupSecretBinaryFile([]byte{0xff, 0xfd}),
230 fromFile: []string{"foo1", "foo2"},
231 expected: &corev1.Secret{
232 TypeMeta: metav1.TypeMeta{
233 APIVersion: corev1.SchemeGroupVersion.String(),
234 Kind: "Secret",
235 },
236 ObjectMeta: metav1.ObjectMeta{
237 Name: "foo",
238 },
239 Data: map[string][]byte{
240 "foo1": {0xff, 0xfd},
241 "foo2": {0xff, 0xfd},
242 },
243 },
244 },
245 "create_secret_foo_from_file_foo1_foo2_and_hash": {
246 secretName: "foo",
247 setup: setupSecretBinaryFile([]byte{0xff, 0xfd}),
248 fromFile: []string{"foo1", "foo2"},
249 appendHash: true,
250 expected: &corev1.Secret{
251 TypeMeta: metav1.TypeMeta{
252 APIVersion: corev1.SchemeGroupVersion.String(),
253 Kind: "Secret",
254 },
255 ObjectMeta: metav1.ObjectMeta{
256 Name: "foo-mkhg4ktk4d",
257 },
258 Data: map[string][]byte{
259 "foo1": {0xff, 0xfd},
260 "foo2": {0xff, 0xfd},
261 },
262 },
263 },
264 "create_secret_valid_env_from_env_file": {
265 secretName: "valid_env",
266 setup: setupSecretEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}}),
267 fromEnvFile: []string{"file.env"},
268 expected: &corev1.Secret{
269 TypeMeta: metav1.TypeMeta{
270 APIVersion: corev1.SchemeGroupVersion.String(),
271 Kind: "Secret",
272 },
273 ObjectMeta: metav1.ObjectMeta{
274 Name: "valid_env",
275 },
276 Data: map[string][]byte{
277 "key1": []byte("value1"),
278 "key2": []byte("value2"),
279 },
280 },
281 },
282 "create_secret_valid_env_from_env_file_hash": {
283 secretName: "valid_env",
284 setup: setupSecretEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}}),
285 fromEnvFile: []string{"file.env"},
286 appendHash: true,
287 expected: &corev1.Secret{
288 TypeMeta: metav1.TypeMeta{
289 APIVersion: corev1.SchemeGroupVersion.String(),
290 Kind: "Secret",
291 },
292 ObjectMeta: metav1.ObjectMeta{
293 Name: "valid_env-bkb2m2965h",
294 },
295 Data: map[string][]byte{
296 "key1": []byte("value1"),
297 "key2": []byte("value2"),
298 },
299 },
300 },
301 "create_two_secret_valid_env_from_env_file": {
302 secretName: "two_valid_env",
303 setup: setupSecretEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}, {"key3=value3"}}),
304 fromEnvFile: []string{"file1.env", "file2.env"},
305 expected: &corev1.Secret{
306 TypeMeta: metav1.TypeMeta{
307 APIVersion: corev1.SchemeGroupVersion.String(),
308 Kind: "Secret",
309 },
310 ObjectMeta: metav1.ObjectMeta{
311 Name: "two_valid_env",
312 },
313 Data: map[string][]byte{
314 "key1": []byte("value1"),
315 "key2": []byte("value2"),
316 "key3": []byte("value3"),
317 },
318 },
319 },
320 "create_two_secret_valid_env_from_env_file_hash": {
321 secretName: "two_valid_env",
322 setup: setupSecretEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}, {"key3=value3"}}),
323 fromEnvFile: []string{"file1.env", "file2.env"},
324 appendHash: true,
325 expected: &corev1.Secret{
326 TypeMeta: metav1.TypeMeta{
327 APIVersion: corev1.SchemeGroupVersion.String(),
328 Kind: "Secret",
329 },
330 ObjectMeta: metav1.ObjectMeta{
331 Name: "two_valid_env-gd56gct5cf",
332 },
333 Data: map[string][]byte{
334 "key1": []byte("value1"),
335 "key2": []byte("value2"),
336 "key3": []byte("value3"),
337 },
338 },
339 },
340 "create_secret_get_env_from_env_file": {
341 secretName: "get_env",
342 setup: func() func(t *testing.T, secretGenericOptions *CreateSecretOptions) func() {
343 t.Setenv("g_key1", "1")
344 t.Setenv("g_key2", "2")
345 return setupSecretEnvFile([][]string{{"g_key1", "g_key2="}})
346 }(),
347 fromEnvFile: []string{"file.env"},
348 expected: &corev1.Secret{
349 TypeMeta: metav1.TypeMeta{
350 APIVersion: corev1.SchemeGroupVersion.String(),
351 Kind: "Secret",
352 },
353 ObjectMeta: metav1.ObjectMeta{
354 Name: "get_env",
355 },
356 Data: map[string][]byte{
357 "g_key1": []byte("1"),
358 "g_key2": []byte(""),
359 },
360 },
361 },
362 "create_secret_get_env_from_env_file_hash": {
363 secretName: "get_env",
364 setup: func() func(t *testing.T, secretGenericOptions *CreateSecretOptions) func() {
365 t.Setenv("g_key1", "1")
366 t.Setenv("g_key2", "2")
367 return setupSecretEnvFile([][]string{{"g_key1", "g_key2="}})
368 }(),
369 fromEnvFile: []string{"file.env"},
370 appendHash: true,
371 expected: &corev1.Secret{
372 TypeMeta: metav1.TypeMeta{
373 APIVersion: corev1.SchemeGroupVersion.String(),
374 Kind: "Secret",
375 },
376 ObjectMeta: metav1.ObjectMeta{
377 Name: "get_env-68mt8f2kkt",
378 },
379 Data: map[string][]byte{
380 "g_key1": []byte("1"),
381 "g_key2": []byte(""),
382 },
383 },
384 },
385 "create_secret_value_with_space_from_env_file": {
386 secretName: "value_with_space",
387 setup: setupSecretEnvFile([][]string{{" key1= value1"}}),
388 fromEnvFile: []string{"file.env"},
389 expected: &corev1.Secret{
390 TypeMeta: metav1.TypeMeta{
391 APIVersion: corev1.SchemeGroupVersion.String(),
392 Kind: "Secret",
393 },
394 ObjectMeta: metav1.ObjectMeta{
395 Name: "value_with_space",
396 },
397 Data: map[string][]byte{
398 "key1": []byte(" value1"),
399 },
400 },
401 },
402 "create_secret_value_with_space_from_env_file_hash": {
403 secretName: "valid_with_space",
404 setup: setupSecretEnvFile([][]string{{" key1= value1"}}),
405 fromEnvFile: []string{"file.env"},
406 appendHash: true,
407 expected: &corev1.Secret{
408 TypeMeta: metav1.TypeMeta{
409 APIVersion: corev1.SchemeGroupVersion.String(),
410 Kind: "Secret",
411 },
412 ObjectMeta: metav1.ObjectMeta{
413 Name: "valid_with_space-bhkb4gfck6",
414 },
415 Data: map[string][]byte{
416 "key1": []byte(" value1"),
417 },
418 },
419 },
420 "create_invalid_secret_filepath_contains_=": {
421 secretName: "foo",
422 fromFile: []string{"key1=/file=2"},
423 expectErr: `key names or file paths cannot contain '='`,
424 },
425 "create_invalid_secret_filepath_key_contains_=": {
426 secretName: "foo",
427 fromFile: []string{"=key=/file1"},
428 expectErr: `key names or file paths cannot contain '='`,
429 },
430 "create_invalid_secret_literal_key_contains_=": {
431 secretName: "foo",
432 fromLiteral: []string{"=key=value1"},
433 expectErr: `invalid literal source =key=value1, expected key=value`,
434 },
435 "create_invalid_secret_literal_key_with_invalid_character": {
436 secretName: "foo",
437 fromLiteral: []string{"key#1=value1"},
438 expectErr: `"key#1" is not valid key name for a Secret a valid config key must consist of alphanumeric characters, '-', '_' or '.' (e.g. 'key.name', or 'KEY_NAME', or 'key-name', regex used for validation is '[-._a-zA-Z0-9]+')`,
439 },
440 "create_invalid_secret_env_key_contains_#": {
441 secretName: "invalid_key",
442 setup: setupSecretEnvFile([][]string{{"key#1=value1"}}),
443 fromEnvFile: []string{"file.env"},
444 expectErr: `"key#1" is not a valid key name: a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1', regex used for validation is '[-._a-zA-Z][-._a-zA-Z0-9]*')`,
445 },
446 "create_invalid_secret_env_key_start_with_digit": {
447 secretName: "invalid_key",
448 setup: setupSecretEnvFile([][]string{{"1key=value1"}}),
449 fromEnvFile: []string{"file.env"},
450 expectErr: `"1key" is not a valid key name: a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1', regex used for validation is '[-._a-zA-Z][-._a-zA-Z0-9]*')`,
451 },
452 "create_invalid_secret_env_key_with_invalid_character": {
453 secretName: "invalid_key",
454 setup: setupSecretEnvFile([][]string{{"key@=value1"}}),
455 fromEnvFile: []string{"file.env"},
456 expectErr: `"key@" is not a valid key name: a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1', regex used for validation is '[-._a-zA-Z][-._a-zA-Z0-9]*')`,
457 },
458 "create_invalid_secret_duplicate_key1": {
459 secretName: "foo",
460 fromLiteral: []string{"key1=value1", "key1=value2"},
461 expectErr: `cannot add key key1, another key by that name already exists`,
462 },
463 "create_invalid_secret_no_file": {
464 secretName: "foo",
465 fromFile: []string{"key1=/file1"},
466 expectErr: `error reading /file1: no such file or directory`,
467 },
468 "create_invalid_secret_invalid_literal": {
469 secretName: "foo",
470 fromLiteral: []string{"key1value1"},
471 expectErr: `invalid literal source key1value1, expected key=value`,
472 },
473 "create_invalid_secret_invalid_filepath": {
474 secretName: "foo",
475 fromFile: []string{"key1==file1"},
476 expectErr: `key names or file paths cannot contain '='`,
477 },
478 "create_invalid_secret_no_name": {
479 expectErr: `name must be specified`,
480 },
481 "create_invalid_secret_too_many_args": {
482 secretName: "too_many_args",
483 fromFile: []string{"key1=/file1"},
484 fromEnvFile: []string{"foo"},
485 expectErr: `from-env-file cannot be combined with from-file or from-literal`,
486 },
487 "create_invalid_secret_too_many_args_1": {
488 secretName: "too_many_args_1",
489 fromLiteral: []string{"key1=value1"},
490 fromEnvFile: []string{"foo"},
491 expectErr: `from-env-file cannot be combined with from-file or from-literal`,
492 },
493 "create_invalid_secret_too_many_args_2": {
494 secretName: "too_many_args_2",
495 fromFile: []string{"key1=/file1"},
496 fromLiteral: []string{"key1=value1"},
497 fromEnvFile: []string{"foo"},
498 expectErr: `from-env-file cannot be combined with from-file or from-literal`,
499 },
500 }
501
502
503 for name, test := range tests {
504 t.Run(name, func(t *testing.T) {
505 var secret *corev1.Secret = nil
506 secretOptions := CreateSecretOptions{
507 Name: test.secretName,
508 Type: test.secretType,
509 AppendHash: test.appendHash,
510 FileSources: test.fromFile,
511 LiteralSources: test.fromLiteral,
512 EnvFileSources: test.fromEnvFile,
513 }
514 if test.setup != nil {
515 if teardown := test.setup(t, &secretOptions); teardown != nil {
516 defer teardown()
517 }
518 }
519 err := secretOptions.Validate()
520 if err == nil {
521 secret, err = secretOptions.createSecret()
522 }
523
524 if test.expectErr == "" {
525 require.NoError(t, err)
526 if !apiequality.Semantic.DeepEqual(secret, test.expected) {
527 t.Errorf("\nexpected:\n%#v\ngot:\n%#v", test.expected, secret)
528 }
529 } else {
530 require.Error(t, err)
531 require.EqualError(t, err, test.expectErr)
532 }
533 })
534 }
535 }
536
537 func setupSecretEnvFile(lines [][]string) func(*testing.T, *CreateSecretOptions) func() {
538 return func(t *testing.T, secretOptions *CreateSecretOptions) func() {
539 files := []*os.File{}
540 filenames := secretOptions.EnvFileSources
541 for _, filename := range filenames {
542 file, err := os.CreateTemp("", filename)
543 if err != nil {
544 t.Errorf("unexpected error: %v", err)
545 }
546 files = append(files, file)
547 }
548 for i, f := range files {
549 for _, l := range lines[i] {
550 f.WriteString(l)
551 f.WriteString("\r\n")
552 }
553 f.Close()
554 secretOptions.EnvFileSources[i] = f.Name()
555 }
556 return func() {
557 for _, f := range files {
558 os.Remove(f.Name())
559 }
560 }
561 }
562 }
563
564 func setupSecretBinaryFile(data []byte) func(*testing.T, *CreateSecretOptions) func() {
565 return func(t *testing.T, secretOptions *CreateSecretOptions) func() {
566 tmp, _ := os.MkdirTemp("", "")
567 files := secretOptions.FileSources
568 for i, file := range files {
569 f := tmp + "/" + file
570 os.WriteFile(f, data, 0644)
571 secretOptions.FileSources[i] = f
572 }
573 return func() {
574 for _, file := range files {
575 f := tmp + "/" + file
576 os.RemoveAll(f)
577 }
578 }
579 }
580 }
581
View as plain text