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