...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_configmap_test.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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 TestCreateConfigMap(t *testing.T) {
    31  	tests := map[string]struct {
    32  		configMapName string
    33  		configMapType string
    34  		appendHash    bool
    35  		fromLiteral   []string
    36  		fromFile      []string
    37  		fromEnvFile   []string
    38  		setup         func(t *testing.T, configMapOptions *ConfigMapOptions) func()
    39  
    40  		expected  *corev1.ConfigMap
    41  		expectErr string
    42  	}{
    43  		"create_foo_configmap": {
    44  			configMapName: "foo",
    45  			expected: &corev1.ConfigMap{
    46  				TypeMeta: metav1.TypeMeta{
    47  					APIVersion: corev1.SchemeGroupVersion.String(),
    48  					Kind:       "ConfigMap",
    49  				},
    50  				ObjectMeta: metav1.ObjectMeta{
    51  					Name: "foo",
    52  				},
    53  				Data:       map[string]string{},
    54  				BinaryData: map[string][]byte{},
    55  			},
    56  		},
    57  		"create_foo_hash_configmap": {
    58  			configMapName: "foo",
    59  			appendHash:    true,
    60  			expected: &corev1.ConfigMap{
    61  				TypeMeta: metav1.TypeMeta{
    62  					APIVersion: corev1.SchemeGroupVersion.String(),
    63  					Kind:       "ConfigMap",
    64  				},
    65  				ObjectMeta: metav1.ObjectMeta{
    66  					Name: "foo-867km9574f",
    67  				},
    68  				Data:       map[string]string{},
    69  				BinaryData: map[string][]byte{},
    70  			},
    71  		},
    72  		"create_foo_type_configmap": {
    73  			configMapName: "foo",
    74  			configMapType: "my-type",
    75  			expected: &corev1.ConfigMap{
    76  				TypeMeta: metav1.TypeMeta{
    77  					APIVersion: corev1.SchemeGroupVersion.String(),
    78  					Kind:       "ConfigMap",
    79  				},
    80  				ObjectMeta: metav1.ObjectMeta{
    81  					Name: "foo",
    82  				},
    83  				Data:       map[string]string{},
    84  				BinaryData: map[string][]byte{},
    85  			},
    86  		},
    87  		"create_foo_type_hash_configmap": {
    88  			configMapName: "foo",
    89  			configMapType: "my-type",
    90  			appendHash:    true,
    91  			expected: &corev1.ConfigMap{
    92  				TypeMeta: metav1.TypeMeta{
    93  					APIVersion: corev1.SchemeGroupVersion.String(),
    94  					Kind:       "ConfigMap",
    95  				},
    96  				ObjectMeta: metav1.ObjectMeta{
    97  					Name: "foo-867km9574f",
    98  				},
    99  				Data:       map[string]string{},
   100  				BinaryData: map[string][]byte{},
   101  			},
   102  		},
   103  		"create_foo_two_literal_configmap": {
   104  			configMapName: "foo",
   105  			fromLiteral:   []string{"key1=value1", "key2=value2"},
   106  			expected: &corev1.ConfigMap{
   107  				TypeMeta: metav1.TypeMeta{
   108  					APIVersion: corev1.SchemeGroupVersion.String(),
   109  					Kind:       "ConfigMap",
   110  				},
   111  				ObjectMeta: metav1.ObjectMeta{
   112  					Name: "foo",
   113  				},
   114  				Data: map[string]string{
   115  					"key1": "value1",
   116  					"key2": "value2",
   117  				},
   118  				BinaryData: map[string][]byte{},
   119  			},
   120  		},
   121  		"create_foo_two_literal_hash_configmap": {
   122  			configMapName: "foo",
   123  			fromLiteral:   []string{"key1=value1", "key2=value2"},
   124  			appendHash:    true,
   125  			expected: &corev1.ConfigMap{
   126  				TypeMeta: metav1.TypeMeta{
   127  					APIVersion: corev1.SchemeGroupVersion.String(),
   128  					Kind:       "ConfigMap",
   129  				},
   130  				ObjectMeta: metav1.ObjectMeta{
   131  					Name: "foo-gcb75dd9gb",
   132  				},
   133  				Data: map[string]string{
   134  					"key1": "value1",
   135  					"key2": "value2",
   136  				},
   137  				BinaryData: map[string][]byte{},
   138  			},
   139  		},
   140  		"create_foo_key1_=value1_configmap": {
   141  			configMapName: "foo",
   142  			fromLiteral:   []string{"key1==value1"},
   143  			expected: &corev1.ConfigMap{
   144  				TypeMeta: metav1.TypeMeta{
   145  					APIVersion: corev1.SchemeGroupVersion.String(),
   146  					Kind:       "ConfigMap",
   147  				},
   148  				ObjectMeta: metav1.ObjectMeta{
   149  					Name: "foo",
   150  				},
   151  				Data: map[string]string{
   152  					"key1": "=value1",
   153  				},
   154  				BinaryData: map[string][]byte{},
   155  			},
   156  		},
   157  		"create_foo_key1_=value1_hash_configmap": {
   158  			configMapName: "foo",
   159  			fromLiteral:   []string{"key1==value1"},
   160  			appendHash:    true,
   161  			expected: &corev1.ConfigMap{
   162  				TypeMeta: metav1.TypeMeta{
   163  					APIVersion: corev1.SchemeGroupVersion.String(),
   164  					Kind:       "ConfigMap",
   165  				},
   166  				ObjectMeta: metav1.ObjectMeta{
   167  					Name: "foo-bdgk9ttt7m",
   168  				},
   169  				Data: map[string]string{
   170  					"key1": "=value1",
   171  				},
   172  				BinaryData: map[string][]byte{},
   173  			},
   174  		},
   175  		"create_foo_from_file_foo1_foo2_configmap": {
   176  			configMapName: "foo",
   177  			setup:         setupBinaryFile([]byte{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}),
   178  			fromFile:      []string{"foo1", "foo2"},
   179  			expected: &corev1.ConfigMap{
   180  				TypeMeta: metav1.TypeMeta{
   181  					APIVersion: corev1.SchemeGroupVersion.String(),
   182  					Kind:       "ConfigMap",
   183  				},
   184  				ObjectMeta: metav1.ObjectMeta{
   185  					Name: "foo",
   186  				},
   187  				Data: map[string]string{
   188  					"foo1": "hello world",
   189  					"foo2": "hello world",
   190  				},
   191  				BinaryData: map[string][]byte{},
   192  			},
   193  		},
   194  		"create_foo_from_file_foo1_foo2_and_configmap": {
   195  			configMapName: "foo",
   196  			setup:         setupBinaryFile([]byte{0xff, 0xfd}),
   197  			fromFile:      []string{"foo1", "foo2"},
   198  			expected: &corev1.ConfigMap{
   199  				TypeMeta: metav1.TypeMeta{
   200  					APIVersion: corev1.SchemeGroupVersion.String(),
   201  					Kind:       "ConfigMap",
   202  				},
   203  				ObjectMeta: metav1.ObjectMeta{
   204  					Name: "foo",
   205  				},
   206  				Data: map[string]string{},
   207  				BinaryData: map[string][]byte{
   208  					"foo1": {0xff, 0xfd},
   209  					"foo2": {0xff, 0xfd},
   210  				},
   211  			},
   212  		},
   213  		"create_valid_env_from_env_file_configmap": {
   214  			configMapName: "valid_env",
   215  			setup:         setupEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}}),
   216  			fromEnvFile:   []string{"file.env"},
   217  			expected: &corev1.ConfigMap{
   218  				TypeMeta: metav1.TypeMeta{
   219  					APIVersion: corev1.SchemeGroupVersion.String(),
   220  					Kind:       "ConfigMap",
   221  				},
   222  				ObjectMeta: metav1.ObjectMeta{
   223  					Name: "valid_env",
   224  				},
   225  				Data: map[string]string{
   226  					"key1": "value1",
   227  					"key2": "value2",
   228  				},
   229  				BinaryData: map[string][]byte{},
   230  			},
   231  		},
   232  		"create_two_valid_env_from_env_file_configmap": {
   233  			configMapName: "two_valid_env",
   234  			setup:         setupEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}, {"key3=value3"}}),
   235  			fromEnvFile:   []string{"file1.env", "file2.env"},
   236  			expected: &corev1.ConfigMap{
   237  				TypeMeta: metav1.TypeMeta{
   238  					APIVersion: corev1.SchemeGroupVersion.String(),
   239  					Kind:       "ConfigMap",
   240  				},
   241  				ObjectMeta: metav1.ObjectMeta{
   242  					Name: "two_valid_env",
   243  				},
   244  				Data: map[string]string{
   245  					"key1": "value1",
   246  					"key2": "value2",
   247  					"key3": "value3",
   248  				},
   249  				BinaryData: map[string][]byte{},
   250  			},
   251  		},
   252  		"create_valid_env_from_env_file_hash_configmap": {
   253  			configMapName: "valid_env",
   254  			setup:         setupEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}}),
   255  			fromEnvFile:   []string{"file.env"},
   256  			appendHash:    true,
   257  			expected: &corev1.ConfigMap{
   258  				TypeMeta: metav1.TypeMeta{
   259  					APIVersion: corev1.SchemeGroupVersion.String(),
   260  					Kind:       "ConfigMap",
   261  				},
   262  				ObjectMeta: metav1.ObjectMeta{
   263  					Name: "valid_env-2cgh8552ch",
   264  				},
   265  				Data: map[string]string{
   266  					"key1": "value1",
   267  					"key2": "value2",
   268  				},
   269  				BinaryData: map[string][]byte{},
   270  			},
   271  		},
   272  		"create_two_valid_env_from_env_file_hash_configmap": {
   273  			configMapName: "two_valid_env",
   274  			setup:         setupEnvFile([][]string{{"key1=value1", "#", "", "key2=value2"}, {"key3=value3"}}),
   275  			fromEnvFile:   []string{"file1.env", "file2.env"},
   276  			appendHash:    true,
   277  			expected: &corev1.ConfigMap{
   278  				TypeMeta: metav1.TypeMeta{
   279  					APIVersion: corev1.SchemeGroupVersion.String(),
   280  					Kind:       "ConfigMap",
   281  				},
   282  				ObjectMeta: metav1.ObjectMeta{
   283  					Name: "two_valid_env-2m5tm82522",
   284  				},
   285  				Data: map[string]string{
   286  					"key1": "value1",
   287  					"key2": "value2",
   288  					"key3": "value3",
   289  				},
   290  				BinaryData: map[string][]byte{},
   291  			},
   292  		},
   293  		"create_get_env_from_env_file_configmap": {
   294  			configMapName: "get_env",
   295  			setup: func() func(t *testing.T, configMapOptions *ConfigMapOptions) func() {
   296  				t.Setenv("g_key1", "1")
   297  				t.Setenv("g_key2", "2")
   298  				return setupEnvFile([][]string{{"g_key1", "g_key2="}})
   299  			}(),
   300  			fromEnvFile: []string{"file.env"},
   301  			expected: &corev1.ConfigMap{
   302  				TypeMeta: metav1.TypeMeta{
   303  					APIVersion: corev1.SchemeGroupVersion.String(),
   304  					Kind:       "ConfigMap",
   305  				},
   306  				ObjectMeta: metav1.ObjectMeta{
   307  					Name: "get_env",
   308  				},
   309  				Data: map[string]string{
   310  					"g_key1": "1",
   311  					"g_key2": "",
   312  				},
   313  				BinaryData: map[string][]byte{},
   314  			},
   315  		},
   316  		"create_get_env_from_env_file_hash_configmap": {
   317  			configMapName: "get_env",
   318  			setup: func() func(t *testing.T, configMapOptions *ConfigMapOptions) func() {
   319  				t.Setenv("g_key1", "1")
   320  				t.Setenv("g_key2", "2")
   321  				return setupEnvFile([][]string{{"g_key1", "g_key2="}})
   322  			}(),
   323  			fromEnvFile: []string{"file.env"},
   324  			appendHash:  true,
   325  			expected: &corev1.ConfigMap{
   326  				TypeMeta: metav1.TypeMeta{
   327  					APIVersion: corev1.SchemeGroupVersion.String(),
   328  					Kind:       "ConfigMap",
   329  				},
   330  				ObjectMeta: metav1.ObjectMeta{
   331  					Name: "get_env-54k882kkd2",
   332  				},
   333  				Data: map[string]string{
   334  					"g_key1": "1",
   335  					"g_key2": "",
   336  				},
   337  				BinaryData: map[string][]byte{},
   338  			},
   339  		},
   340  		"create_value_with_space_from_env_file_configmap": {
   341  			configMapName: "value_with_space",
   342  			setup:         setupEnvFile([][]string{{"key1=  value1"}}),
   343  			fromEnvFile:   []string{"file.env"},
   344  			expected: &corev1.ConfigMap{
   345  				TypeMeta: metav1.TypeMeta{
   346  					APIVersion: corev1.SchemeGroupVersion.String(),
   347  					Kind:       "ConfigMap",
   348  				},
   349  				ObjectMeta: metav1.ObjectMeta{
   350  					Name: "value_with_space",
   351  				},
   352  				Data: map[string]string{
   353  					"key1": "  value1",
   354  				},
   355  				BinaryData: map[string][]byte{},
   356  			},
   357  		},
   358  		"create_value_with_space_from_env_file_hash_configmap": {
   359  			configMapName: "valid_with_space",
   360  			setup:         setupEnvFile([][]string{{"key1=  value1"}}),
   361  			fromEnvFile:   []string{"file.env"},
   362  			appendHash:    true,
   363  			expected: &corev1.ConfigMap{
   364  				TypeMeta: metav1.TypeMeta{
   365  					APIVersion: corev1.SchemeGroupVersion.String(),
   366  					Kind:       "ConfigMap",
   367  				},
   368  				ObjectMeta: metav1.ObjectMeta{
   369  					Name: "valid_with_space-b4448m7gdm",
   370  				},
   371  				Data: map[string]string{
   372  					"key1": "  value1",
   373  				},
   374  				BinaryData: map[string][]byte{},
   375  			},
   376  		},
   377  		"create_invalid_configmap_filepath_contains_=": {
   378  			configMapName: "foo",
   379  			fromFile:      []string{"key1=/file=2"},
   380  			expectErr:     `key names or file paths cannot contain '='`,
   381  		},
   382  		"create_invalid_configmap_filepath_key_contains_=": {
   383  			configMapName: "foo",
   384  			fromFile:      []string{"=key=/file1"},
   385  			expectErr:     `key names or file paths cannot contain '='`,
   386  		},
   387  		"create_invalid_configmap_literal_key_contains_=": {
   388  			configMapName: "foo",
   389  			fromFile:      []string{"=key=value1"},
   390  			expectErr:     `key names or file paths cannot contain '='`,
   391  		},
   392  		"create_invalid_configmap_duplicate_key1": {
   393  			configMapName: "foo",
   394  			fromLiteral:   []string{"key1=value1", "key1=value2"},
   395  			expectErr:     `cannot add key "key1", another key by that name already exists in Data for ConfigMap "foo"`,
   396  		},
   397  		"create_invalid_configmap_no_file": {
   398  			configMapName: "foo",
   399  			fromFile:      []string{"key1=/file1"},
   400  			expectErr:     `error reading /file1: no such file or directory`,
   401  		},
   402  		"create_invalid_configmap_invalid_literal": {
   403  			configMapName: "foo",
   404  			fromLiteral:   []string{"key1value1"},
   405  			expectErr:     `invalid literal source key1value1, expected key=value`,
   406  		},
   407  		"create_invalid_configmap_invalid_filepath": {
   408  			configMapName: "foo",
   409  			fromFile:      []string{"key1==file1"},
   410  			expectErr:     `key names or file paths cannot contain '='`,
   411  		},
   412  		"create_invalid_configmap_too_many_args": {
   413  			configMapName: "too_many_args",
   414  			fromFile:      []string{"key1=/file1"},
   415  			fromEnvFile:   []string{"file.env"},
   416  			expectErr:     `from-env-file cannot be combined with from-file or from-literal`,
   417  		},
   418  		"create_invalid_configmap_too_many_args_1": {
   419  			configMapName: "too_many_args_1",
   420  			fromLiteral:   []string{"key1=value1"},
   421  			fromEnvFile:   []string{"file.env"},
   422  			expectErr:     `from-env-file cannot be combined with from-file or from-literal`,
   423  		},
   424  	}
   425  
   426  	// run all the tests
   427  	for name, test := range tests {
   428  		t.Run(name, func(t *testing.T) {
   429  			var configMap *corev1.ConfigMap = nil
   430  			configMapOptions := ConfigMapOptions{
   431  				Name:           test.configMapName,
   432  				Type:           test.configMapType,
   433  				AppendHash:     test.appendHash,
   434  				FileSources:    test.fromFile,
   435  				LiteralSources: test.fromLiteral,
   436  				EnvFileSources: test.fromEnvFile,
   437  			}
   438  
   439  			if test.setup != nil {
   440  				if teardown := test.setup(t, &configMapOptions); teardown != nil {
   441  					defer teardown()
   442  				}
   443  			}
   444  			err := configMapOptions.Validate()
   445  
   446  			if err == nil {
   447  				configMap, err = configMapOptions.createConfigMap()
   448  			}
   449  			if test.expectErr == "" {
   450  				require.NoError(t, err)
   451  				if !apiequality.Semantic.DeepEqual(configMap, test.expected) {
   452  					t.Errorf("\nexpected:\n%#v\ngot:\n%#v", test.expected, configMap)
   453  				}
   454  			} else {
   455  				require.Error(t, err)
   456  				require.EqualError(t, err, test.expectErr)
   457  			}
   458  		})
   459  	}
   460  }
   461  
   462  func setupEnvFile(lines [][]string) func(*testing.T, *ConfigMapOptions) func() {
   463  	return func(t *testing.T, configMapOptions *ConfigMapOptions) func() {
   464  		files := []*os.File{}
   465  		filenames := configMapOptions.EnvFileSources
   466  		for _, filename := range filenames {
   467  			file, err := os.CreateTemp("", filename)
   468  			if err != nil {
   469  				t.Errorf("unexpected error: %v", err)
   470  			}
   471  			files = append(files, file)
   472  		}
   473  		for i, f := range files {
   474  			for _, l := range lines[i] {
   475  				f.WriteString(l)
   476  				f.WriteString("\r\n")
   477  			}
   478  			f.Close()
   479  			configMapOptions.EnvFileSources[i] = f.Name()
   480  		}
   481  		return func() {
   482  			for _, f := range files {
   483  				os.Remove(f.Name())
   484  			}
   485  		}
   486  	}
   487  }
   488  
   489  func setupBinaryFile(data []byte) func(*testing.T, *ConfigMapOptions) func() {
   490  	return func(t *testing.T, configMapOptions *ConfigMapOptions) func() {
   491  		tmp, _ := os.MkdirTemp("", "")
   492  		files := configMapOptions.FileSources
   493  		for i, file := range files {
   494  			f := tmp + "/" + file
   495  			os.WriteFile(f, data, 0644)
   496  			configMapOptions.FileSources[i] = f
   497  		}
   498  		return func() {
   499  			for _, file := range files {
   500  				f := tmp + "/" + file
   501  				os.Remove(f)
   502  			}
   503  		}
   504  	}
   505  }
   506  

View as plain text