k8s.io/kubernetes@v1.29.3/test/e2e/common/node/downwardapi.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 "fmt" 22 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/api/resource" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/util/uuid" 27 "k8s.io/kubernetes/test/e2e/framework" 28 e2enetwork "k8s.io/kubernetes/test/e2e/framework/network" 29 e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 30 "k8s.io/kubernetes/test/e2e/nodefeature" 31 imageutils "k8s.io/kubernetes/test/utils/image" 32 admissionapi "k8s.io/pod-security-admission/api" 33 34 "github.com/onsi/ginkgo/v2" 35 ) 36 37 var _ = SIGDescribe("Downward API", func() { 38 f := framework.NewDefaultFramework("downward-api") 39 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 40 41 /* 42 Release: v1.9 43 Testname: DownwardAPI, environment for name, namespace and ip 44 Description: Downward API MUST expose Pod and Container fields as environment variables. Specify Pod Name, namespace and IP as environment variable in the Pod Spec are visible at runtime in the container. 45 */ 46 framework.ConformanceIt("should provide pod name, namespace and IP address as env vars", f.WithNodeConformance(), func(ctx context.Context) { 47 podName := "downward-api-" + string(uuid.NewUUID()) 48 env := []v1.EnvVar{ 49 { 50 Name: "POD_NAME", 51 ValueFrom: &v1.EnvVarSource{ 52 FieldRef: &v1.ObjectFieldSelector{ 53 APIVersion: "v1", 54 FieldPath: "metadata.name", 55 }, 56 }, 57 }, 58 { 59 Name: "POD_NAMESPACE", 60 ValueFrom: &v1.EnvVarSource{ 61 FieldRef: &v1.ObjectFieldSelector{ 62 APIVersion: "v1", 63 FieldPath: "metadata.namespace", 64 }, 65 }, 66 }, 67 { 68 Name: "POD_IP", 69 ValueFrom: &v1.EnvVarSource{ 70 FieldRef: &v1.ObjectFieldSelector{ 71 APIVersion: "v1", 72 FieldPath: "status.podIP", 73 }, 74 }, 75 }, 76 } 77 78 expectations := []string{ 79 fmt.Sprintf("POD_NAME=%v", podName), 80 fmt.Sprintf("POD_NAMESPACE=%v", f.Namespace.Name), 81 fmt.Sprintf("POD_IP=%v|%v", e2enetwork.RegexIPv4, e2enetwork.RegexIPv6), 82 } 83 84 testDownwardAPI(ctx, f, podName, env, expectations) 85 }) 86 87 /* 88 Release: v1.9 89 Testname: DownwardAPI, environment for host ip 90 Description: Downward API MUST expose Pod and Container fields as environment variables. Specify host IP as environment variable in the Pod Spec are visible at runtime in the container. 91 */ 92 framework.ConformanceIt("should provide host IP as an env var", f.WithNodeConformance(), func(ctx context.Context) { 93 podName := "downward-api-" + string(uuid.NewUUID()) 94 env := []v1.EnvVar{ 95 { 96 Name: "HOST_IP", 97 ValueFrom: &v1.EnvVarSource{ 98 FieldRef: &v1.ObjectFieldSelector{ 99 APIVersion: "v1", 100 FieldPath: "status.hostIP", 101 }, 102 }, 103 }, 104 } 105 106 expectations := []string{ 107 fmt.Sprintf("HOST_IP=%v|%v", e2enetwork.RegexIPv4, e2enetwork.RegexIPv6), 108 } 109 110 testDownwardAPI(ctx, f, podName, env, expectations) 111 }) 112 113 ginkgo.It("should provide host IP and pod IP as an env var if pod uses host network [LinuxOnly]", func(ctx context.Context) { 114 podName := "downward-api-" + string(uuid.NewUUID()) 115 env := []v1.EnvVar{ 116 { 117 Name: "HOST_IP", 118 ValueFrom: &v1.EnvVarSource{ 119 FieldRef: &v1.ObjectFieldSelector{ 120 APIVersion: "v1", 121 FieldPath: "status.hostIP", 122 }, 123 }, 124 }, 125 { 126 Name: "POD_IP", 127 ValueFrom: &v1.EnvVarSource{ 128 FieldRef: &v1.ObjectFieldSelector{ 129 APIVersion: "v1", 130 FieldPath: "status.podIP", 131 }, 132 }, 133 }, 134 } 135 136 expectations := []string{ 137 fmt.Sprintf("OK"), 138 } 139 140 pod := &v1.Pod{ 141 ObjectMeta: metav1.ObjectMeta{ 142 Name: podName, 143 Labels: map[string]string{"name": podName}, 144 }, 145 Spec: v1.PodSpec{ 146 Containers: []v1.Container{ 147 { 148 Name: "dapi-container", 149 Image: imageutils.GetE2EImage(imageutils.BusyBox), 150 Command: []string{"sh", "-c", `[[ "${HOST_IP:?}" == "${POD_IP:?}" ]] && echo 'OK' || echo "HOST_IP: '${HOST_IP}' != POD_IP: '${POD_IP}'"`}, 151 Env: env, 152 }, 153 }, 154 HostNetwork: true, 155 RestartPolicy: v1.RestartPolicyNever, 156 }, 157 } 158 159 testDownwardAPIUsingPod(ctx, f, pod, env, expectations) 160 161 }) 162 163 /* 164 Release: v1.9 165 Testname: DownwardAPI, environment for CPU and memory limits and requests 166 Description: Downward API MUST expose CPU request and Memory request set through environment variables at runtime in the container. 167 */ 168 framework.ConformanceIt("should provide container's limits.cpu/memory and requests.cpu/memory as env vars", f.WithNodeConformance(), func(ctx context.Context) { 169 podName := "downward-api-" + string(uuid.NewUUID()) 170 env := []v1.EnvVar{ 171 { 172 Name: "CPU_LIMIT", 173 ValueFrom: &v1.EnvVarSource{ 174 ResourceFieldRef: &v1.ResourceFieldSelector{ 175 Resource: "limits.cpu", 176 }, 177 }, 178 }, 179 { 180 Name: "MEMORY_LIMIT", 181 ValueFrom: &v1.EnvVarSource{ 182 ResourceFieldRef: &v1.ResourceFieldSelector{ 183 Resource: "limits.memory", 184 }, 185 }, 186 }, 187 { 188 Name: "CPU_REQUEST", 189 ValueFrom: &v1.EnvVarSource{ 190 ResourceFieldRef: &v1.ResourceFieldSelector{ 191 Resource: "requests.cpu", 192 }, 193 }, 194 }, 195 { 196 Name: "MEMORY_REQUEST", 197 ValueFrom: &v1.EnvVarSource{ 198 ResourceFieldRef: &v1.ResourceFieldSelector{ 199 Resource: "requests.memory", 200 }, 201 }, 202 }, 203 } 204 expectations := []string{ 205 "CPU_LIMIT=2", 206 "MEMORY_LIMIT=67108864", 207 "CPU_REQUEST=1", 208 "MEMORY_REQUEST=33554432", 209 } 210 211 testDownwardAPI(ctx, f, podName, env, expectations) 212 }) 213 214 /* 215 Release: v1.9 216 Testname: DownwardAPI, environment for default CPU and memory limits and requests 217 Description: Downward API MUST expose CPU request and Memory limits set through environment variables at runtime in the container. 218 */ 219 framework.ConformanceIt("should provide default limits.cpu/memory from node allocatable", f.WithNodeConformance(), func(ctx context.Context) { 220 podName := "downward-api-" + string(uuid.NewUUID()) 221 env := []v1.EnvVar{ 222 { 223 Name: "CPU_LIMIT", 224 ValueFrom: &v1.EnvVarSource{ 225 ResourceFieldRef: &v1.ResourceFieldSelector{ 226 Resource: "limits.cpu", 227 }, 228 }, 229 }, 230 { 231 Name: "MEMORY_LIMIT", 232 ValueFrom: &v1.EnvVarSource{ 233 ResourceFieldRef: &v1.ResourceFieldSelector{ 234 Resource: "limits.memory", 235 }, 236 }, 237 }, 238 } 239 expectations := []string{ 240 "CPU_LIMIT=[1-9]", 241 "MEMORY_LIMIT=[1-9]", 242 } 243 pod := &v1.Pod{ 244 ObjectMeta: metav1.ObjectMeta{ 245 Name: podName, 246 Labels: map[string]string{"name": podName}, 247 }, 248 Spec: v1.PodSpec{ 249 Containers: []v1.Container{ 250 { 251 Name: "dapi-container", 252 Image: imageutils.GetE2EImage(imageutils.BusyBox), 253 Command: []string{"sh", "-c", "env"}, 254 Env: env, 255 }, 256 }, 257 RestartPolicy: v1.RestartPolicyNever, 258 }, 259 } 260 261 testDownwardAPIUsingPod(ctx, f, pod, env, expectations) 262 }) 263 264 /* 265 Release: v1.9 266 Testname: DownwardAPI, environment for Pod UID 267 Description: Downward API MUST expose Pod UID set through environment variables at runtime in the container. 268 */ 269 framework.ConformanceIt("should provide pod UID as env vars", f.WithNodeConformance(), func(ctx context.Context) { 270 podName := "downward-api-" + string(uuid.NewUUID()) 271 env := []v1.EnvVar{ 272 { 273 Name: "POD_UID", 274 ValueFrom: &v1.EnvVarSource{ 275 FieldRef: &v1.ObjectFieldSelector{ 276 APIVersion: "v1", 277 FieldPath: "metadata.uid", 278 }, 279 }, 280 }, 281 } 282 283 expectations := []string{ 284 "POD_UID=[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}", 285 } 286 287 testDownwardAPI(ctx, f, podName, env, expectations) 288 }) 289 }) 290 291 var _ = SIGDescribe("Downward API", framework.WithSerial(), framework.WithDisruptive(), nodefeature.DownwardAPIHugePages, func() { 292 f := framework.NewDefaultFramework("downward-api") 293 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 294 295 ginkgo.Context("Downward API tests for hugepages", func() { 296 ginkgo.It("should provide container's limits.hugepages-<pagesize> and requests.hugepages-<pagesize> as env vars", func(ctx context.Context) { 297 podName := "downward-api-" + string(uuid.NewUUID()) 298 env := []v1.EnvVar{ 299 { 300 Name: "HUGEPAGES_LIMIT", 301 ValueFrom: &v1.EnvVarSource{ 302 ResourceFieldRef: &v1.ResourceFieldSelector{ 303 Resource: "limits.hugepages-2Mi", 304 }, 305 }, 306 }, 307 { 308 Name: "HUGEPAGES_REQUEST", 309 ValueFrom: &v1.EnvVarSource{ 310 ResourceFieldRef: &v1.ResourceFieldSelector{ 311 Resource: "requests.hugepages-2Mi", 312 }, 313 }, 314 }, 315 } 316 317 // Important: we explicitly request no hugepages so the test can run where none are present. 318 expectations := []string{ 319 fmt.Sprintf("HUGEPAGES_LIMIT=%d", 0), 320 fmt.Sprintf("HUGEPAGES_REQUEST=%d", 0), 321 } 322 pod := &v1.Pod{ 323 ObjectMeta: metav1.ObjectMeta{ 324 Name: podName, 325 Labels: map[string]string{"name": podName}, 326 }, 327 Spec: v1.PodSpec{ 328 Containers: []v1.Container{ 329 { 330 Name: "dapi-container", 331 Image: imageutils.GetE2EImage(imageutils.BusyBox), 332 Command: []string{"sh", "-c", "env"}, 333 Resources: v1.ResourceRequirements{ 334 Requests: v1.ResourceList{ 335 "cpu": resource.MustParse("10m"), 336 "hugepages-2Mi": resource.MustParse("0Mi"), 337 }, 338 Limits: v1.ResourceList{ 339 "hugepages-2Mi": resource.MustParse("0Mi"), 340 }, 341 }, 342 Env: env, 343 }, 344 }, 345 RestartPolicy: v1.RestartPolicyNever, 346 }, 347 } 348 testDownwardAPIUsingPod(ctx, f, pod, env, expectations) 349 }) 350 351 ginkgo.It("should provide default limits.hugepages-<pagesize> from node allocatable", func(ctx context.Context) { 352 podName := "downward-api-" + string(uuid.NewUUID()) 353 env := []v1.EnvVar{ 354 { 355 Name: "HUGEPAGES_LIMIT", 356 ValueFrom: &v1.EnvVarSource{ 357 ResourceFieldRef: &v1.ResourceFieldSelector{ 358 Resource: "limits.hugepages-2Mi", 359 }, 360 }, 361 }, 362 } 363 // Important: we allow for 0 so the test passes in environments where no hugepages are allocated. 364 expectations := []string{ 365 "HUGEPAGES_LIMIT=((0)|([1-9][0-9]*))\n", 366 } 367 pod := &v1.Pod{ 368 ObjectMeta: metav1.ObjectMeta{ 369 Name: podName, 370 Labels: map[string]string{"name": podName}, 371 }, 372 Spec: v1.PodSpec{ 373 Containers: []v1.Container{ 374 { 375 Name: "dapi-container", 376 Image: imageutils.GetE2EImage(imageutils.BusyBox), 377 Command: []string{"sh", "-c", "env"}, 378 Env: env, 379 }, 380 }, 381 RestartPolicy: v1.RestartPolicyNever, 382 }, 383 } 384 385 testDownwardAPIUsingPod(ctx, f, pod, env, expectations) 386 }) 387 }) 388 389 }) 390 391 func testDownwardAPI(ctx context.Context, f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) { 392 pod := &v1.Pod{ 393 ObjectMeta: metav1.ObjectMeta{ 394 Name: podName, 395 Labels: map[string]string{"name": podName}, 396 }, 397 Spec: v1.PodSpec{ 398 Containers: []v1.Container{ 399 { 400 Name: "dapi-container", 401 Image: imageutils.GetE2EImage(imageutils.BusyBox), 402 Command: []string{"sh", "-c", "env"}, 403 Resources: v1.ResourceRequirements{ 404 Requests: v1.ResourceList{ 405 v1.ResourceCPU: resource.MustParse("250m"), 406 v1.ResourceMemory: resource.MustParse("32Mi"), 407 }, 408 Limits: v1.ResourceList{ 409 v1.ResourceCPU: resource.MustParse("1250m"), 410 v1.ResourceMemory: resource.MustParse("64Mi"), 411 }, 412 }, 413 Env: env, 414 }, 415 }, 416 RestartPolicy: v1.RestartPolicyNever, 417 }, 418 } 419 420 testDownwardAPIUsingPod(ctx, f, pod, env, expectations) 421 } 422 423 func testDownwardAPIUsingPod(ctx context.Context, f *framework.Framework, pod *v1.Pod, env []v1.EnvVar, expectations []string) { 424 e2epodoutput.TestContainerOutputRegexp(ctx, f, "downward api env vars", pod, 0, expectations) 425 }