k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/common/node/configmap.go (about) 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 node 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/uuid" 28 "k8s.io/kubernetes/test/e2e/feature" 29 "k8s.io/kubernetes/test/e2e/framework" 30 e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 31 imageutils "k8s.io/kubernetes/test/utils/image" 32 admissionapi "k8s.io/pod-security-admission/api" 33 34 "github.com/onsi/ginkgo/v2" 35 "github.com/onsi/gomega" 36 ) 37 38 var _ = SIGDescribe("ConfigMap", func() { 39 f := framework.NewDefaultFramework("configmap") 40 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 41 42 /* 43 Release: v1.9 44 Testname: ConfigMap, from environment field 45 Description: Create a Pod with an environment variable value set using a value from ConfigMap. A ConfigMap value MUST be accessible in the container environment. 46 */ 47 framework.ConformanceIt("should be consumable via environment variable", f.WithNodeConformance(), func(ctx context.Context) { 48 name := "configmap-test-" + string(uuid.NewUUID()) 49 configMap := newConfigMap(f, name) 50 ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name)) 51 var err error 52 if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil { 53 framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) 54 } 55 56 pod := &v1.Pod{ 57 ObjectMeta: metav1.ObjectMeta{ 58 Name: "pod-configmaps-" + string(uuid.NewUUID()), 59 }, 60 Spec: v1.PodSpec{ 61 Containers: []v1.Container{ 62 { 63 Name: "env-test", 64 Image: imageutils.GetE2EImage(imageutils.BusyBox), 65 Command: []string{"sh", "-c", "env"}, 66 Env: []v1.EnvVar{ 67 { 68 Name: "CONFIG_DATA_1", 69 ValueFrom: &v1.EnvVarSource{ 70 ConfigMapKeyRef: &v1.ConfigMapKeySelector{ 71 LocalObjectReference: v1.LocalObjectReference{ 72 Name: name, 73 }, 74 Key: "data-1", 75 }, 76 }, 77 }, 78 }, 79 }, 80 }, 81 RestartPolicy: v1.RestartPolicyNever, 82 }, 83 } 84 85 e2epodoutput.TestContainerOutput(ctx, f, "consume configMaps", pod, 0, []string{ 86 "CONFIG_DATA_1=value-1", 87 }) 88 }) 89 90 /* 91 Release: v1.9 92 Testname: ConfigMap, from environment variables 93 Description: Create a Pod with a environment source from ConfigMap. All ConfigMap values MUST be available as environment variables in the container. 94 */ 95 framework.ConformanceIt("should be consumable via the environment", f.WithNodeConformance(), func(ctx context.Context) { 96 name := "configmap-test-" + string(uuid.NewUUID()) 97 configMap := newConfigMap(f, name) 98 ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name)) 99 var err error 100 if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil { 101 framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) 102 } 103 104 pod := &v1.Pod{ 105 ObjectMeta: metav1.ObjectMeta{ 106 Name: "pod-configmaps-" + string(uuid.NewUUID()), 107 }, 108 Spec: v1.PodSpec{ 109 Containers: []v1.Container{ 110 { 111 Name: "env-test", 112 Image: imageutils.GetE2EImage(imageutils.BusyBox), 113 Command: []string{"sh", "-c", "env"}, 114 EnvFrom: []v1.EnvFromSource{ 115 { 116 ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 117 }, 118 { 119 Prefix: "p-", 120 ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 121 }, 122 }, 123 }, 124 }, 125 RestartPolicy: v1.RestartPolicyNever, 126 }, 127 } 128 129 e2epodoutput.TestContainerOutput(ctx, f, "consume configMaps", pod, 0, []string{ 130 "data-1=value-1", "data-2=value-2", "data-3=value-3", 131 "p-data-1=value-1", "p-data-2=value-2", "p-data-3=value-3", 132 }) 133 }) 134 135 /* 136 Release: v1.14 137 Testname: ConfigMap, with empty-key 138 Description: Attempt to create a ConfigMap with an empty key. The creation MUST fail. 139 */ 140 framework.ConformanceIt("should fail to create ConfigMap with empty key", func(ctx context.Context) { 141 configMap, err := newConfigMapWithEmptyKey(ctx, f) 142 gomega.Expect(err).To(gomega.HaveOccurred(), "created configMap %q with empty key in namespace %q", configMap.Name, f.Namespace.Name) 143 }) 144 145 ginkgo.It("should update ConfigMap successfully", func(ctx context.Context) { 146 name := "configmap-test-" + string(uuid.NewUUID()) 147 configMap := newConfigMap(f, name) 148 ginkgo.By(fmt.Sprintf("Creating ConfigMap %v/%v", f.Namespace.Name, configMap.Name)) 149 _, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}) 150 framework.ExpectNoError(err, "failed to create ConfigMap") 151 152 configMap.Data = map[string]string{ 153 "data": "value", 154 } 155 ginkgo.By(fmt.Sprintf("Updating configMap %v/%v", f.Namespace.Name, configMap.Name)) 156 _, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(ctx, configMap, metav1.UpdateOptions{}) 157 framework.ExpectNoError(err, "failed to update ConfigMap") 158 159 configMapFromUpdate, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(ctx, name, metav1.GetOptions{}) 160 framework.ExpectNoError(err, "failed to get ConfigMap") 161 ginkgo.By(fmt.Sprintf("Verifying update of ConfigMap %v/%v", f.Namespace.Name, configMap.Name)) 162 gomega.Expect(configMapFromUpdate.Data).To(gomega.Equal(configMap.Data)) 163 }) 164 165 /* 166 Release: v1.19 167 Testname: ConfigMap lifecycle 168 Description: Attempt to create a ConfigMap. Patch the created ConfigMap. Fetching the ConfigMap MUST reflect changes. 169 By fetching all the ConfigMaps via a Label selector it MUST find the ConfigMap by it's static label and updated value. The ConfigMap must be deleted by Collection. 170 */ 171 framework.ConformanceIt("should run through a ConfigMap lifecycle", func(ctx context.Context) { 172 testNamespaceName := f.Namespace.Name 173 testConfigMapName := "test-configmap" + string(uuid.NewUUID()) 174 175 testConfigMap := v1.ConfigMap{ 176 ObjectMeta: metav1.ObjectMeta{ 177 Name: testConfigMapName, 178 Labels: map[string]string{ 179 "test-configmap-static": "true", 180 }, 181 }, 182 Data: map[string]string{ 183 "valueName": "value", 184 }, 185 } 186 187 ginkgo.By("creating a ConfigMap") 188 _, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Create(ctx, &testConfigMap, metav1.CreateOptions{}) 189 framework.ExpectNoError(err, "failed to create ConfigMap") 190 191 ginkgo.By("fetching the ConfigMap") 192 configMap, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Get(ctx, testConfigMapName, metav1.GetOptions{}) 193 framework.ExpectNoError(err, "failed to get ConfigMap") 194 gomega.Expect(configMap.Data["valueName"]).To(gomega.Equal(testConfigMap.Data["valueName"])) 195 gomega.Expect(configMap.Labels["test-configmap-static"]).To(gomega.Equal(testConfigMap.Labels["test-configmap-static"])) 196 197 configMapPatchPayload, err := json.Marshal(v1.ConfigMap{ 198 ObjectMeta: metav1.ObjectMeta{ 199 Labels: map[string]string{ 200 "test-configmap": "patched", 201 }, 202 }, 203 Data: map[string]string{ 204 "valueName": "value1", 205 }, 206 }) 207 framework.ExpectNoError(err, "failed to marshal patch data") 208 209 ginkgo.By("patching the ConfigMap") 210 _, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Patch(ctx, testConfigMapName, types.StrategicMergePatchType, []byte(configMapPatchPayload), metav1.PatchOptions{}) 211 framework.ExpectNoError(err, "failed to patch ConfigMap") 212 213 ginkgo.By("listing all ConfigMaps in all namespaces with a label selector") 214 configMapList, err := f.ClientSet.CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{ 215 LabelSelector: "test-configmap=patched", 216 }) 217 framework.ExpectNoError(err, "failed to list ConfigMaps with LabelSelector") 218 testConfigMapFound := false 219 for _, cm := range configMapList.Items { 220 if cm.ObjectMeta.Name == testConfigMap.ObjectMeta.Name && 221 cm.ObjectMeta.Namespace == testNamespaceName && 222 cm.ObjectMeta.Labels["test-configmap-static"] == testConfigMap.ObjectMeta.Labels["test-configmap-static"] && 223 cm.ObjectMeta.Labels["test-configmap"] == "patched" && 224 cm.Data["valueName"] == "value1" { 225 testConfigMapFound = true 226 break 227 } 228 } 229 if !testConfigMapFound { 230 framework.Failf("failed to find ConfigMap %s/%s by label selector", testNamespaceName, testConfigMap.ObjectMeta.Name) 231 } 232 233 ginkgo.By("deleting the ConfigMap by collection with a label selector") 234 err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ 235 LabelSelector: "test-configmap-static=true", 236 }) 237 framework.ExpectNoError(err, "failed to delete ConfigMap collection with LabelSelector") 238 239 ginkgo.By("listing all ConfigMaps in test namespace") 240 configMapList, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).List(ctx, metav1.ListOptions{ 241 LabelSelector: "test-configmap-static=true", 242 }) 243 framework.ExpectNoError(err, "failed to list ConfigMap by LabelSelector") 244 gomega.Expect(configMapList.Items).To(gomega.BeEmpty(), "ConfigMap is still present after being deleted by collection") 245 }) 246 247 /* 248 Release: v1.30 249 Testname: ConfigMap, from environment field 250 Description: Create a Pod with an environment variable value set using a value from ConfigMap. 251 Allows users to use envFrom to set prefix starting with a digit as environment variable names. 252 */ 253 framework.It("should be consumable as environment variable names when configmap keys start with a digit", 254 feature.RelaxedEnvironmentVariableValidation, func(ctx context.Context) { 255 name := "configmap-test-" + string(uuid.NewUUID()) 256 configMap := newConfigMap(f, name) 257 ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name)) 258 var err error 259 if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil { 260 framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) 261 } 262 263 pod := &v1.Pod{ 264 ObjectMeta: metav1.ObjectMeta{ 265 Name: "pod-configmaps-" + string(uuid.NewUUID()), 266 }, 267 Spec: v1.PodSpec{ 268 Containers: []v1.Container{ 269 { 270 Name: "env-test", 271 Image: imageutils.GetE2EImage(imageutils.BusyBox), 272 Command: []string{"sh", "-c", "env"}, 273 EnvFrom: []v1.EnvFromSource{ 274 { 275 ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 276 }, 277 { 278 // prefix start with a digit can be consumed as environment variables. 279 Prefix: "1-", 280 ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 281 }, 282 }, 283 }, 284 }, 285 RestartPolicy: v1.RestartPolicyNever, 286 }, 287 } 288 289 e2epodoutput.TestContainerOutput(ctx, f, "consume configMaps", pod, 0, []string{ 290 "data-1=value-1", "data-2=value-2", "data-3=value-3", 291 "1-data-1=value-1", "1-data-2=value-2", "1-data-3=value-3", 292 }) 293 }) 294 }) 295 296 func newConfigMap(f *framework.Framework, name string) *v1.ConfigMap { 297 return &v1.ConfigMap{ 298 ObjectMeta: metav1.ObjectMeta{ 299 Namespace: f.Namespace.Name, 300 Name: name, 301 }, 302 Data: map[string]string{ 303 "data-1": "value-1", 304 "data-2": "value-2", 305 "data-3": "value-3", 306 }, 307 } 308 } 309 310 func newConfigMapWithEmptyKey(ctx context.Context, f *framework.Framework) (*v1.ConfigMap, error) { 311 name := "configmap-test-emptyKey-" + string(uuid.NewUUID()) 312 configMap := &v1.ConfigMap{ 313 ObjectMeta: metav1.ObjectMeta{ 314 Namespace: f.Namespace.Name, 315 Name: name, 316 }, 317 Data: map[string]string{ 318 "": "value-1", 319 }, 320 } 321 322 ginkgo.By(fmt.Sprintf("Creating configMap that has name %s", configMap.Name)) 323 return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}) 324 }