k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/network/dns.go (about) 1 /* 2 Copyright 2015 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 network 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "time" 24 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "k8s.io/kubernetes/test/e2e/framework" 29 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 30 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 31 e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 32 e2eservice "k8s.io/kubernetes/test/e2e/framework/service" 33 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 34 "k8s.io/kubernetes/test/e2e/network/common" 35 admissionapi "k8s.io/pod-security-admission/api" 36 37 "github.com/onsi/ginkgo/v2" 38 ) 39 40 const dnsTestPodHostName = "dns-querier-1" 41 const dnsTestServiceName = "dns-test-service" 42 43 var _ = common.SIGDescribe("DNS", func() { 44 f := framework.NewDefaultFramework("dns") 45 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 46 47 /* 48 Release: v1.9 49 Testname: DNS, cluster 50 Description: When a Pod is created, the pod MUST be able to resolve cluster dns entries such as kubernetes.default via DNS. 51 */ 52 framework.ConformanceIt("should provide DNS for the cluster", func(ctx context.Context) { 53 // All the names we need to be able to resolve. 54 // TODO: Spin up a separate test service and test that dns works for that service. 55 // NOTE: This only contains the FQDN and the Host name, for testing partial name, see the test below 56 namesToResolve := []string{ 57 fmt.Sprintf("kubernetes.default.svc.%s", framework.TestContext.ClusterDNSDomain), 58 } 59 // TODO: Validate both IPv4 and IPv6 families for dual-stack 60 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 61 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 62 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 63 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 64 65 // Run a pod which probes DNS and exposes the results by HTTP. 66 ginkgo.By("creating a pod to probe DNS") 67 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 68 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 69 }) 70 71 // Added due to #8512. This is critical for GCE and GKE deployments. 72 ginkgo.It("should provide DNS for the cluster [Provider:GCE]", func(ctx context.Context) { 73 e2eskipper.SkipUnlessProviderIs("gce", "gke") 74 75 namesToResolve := []string{"google.com"} 76 // Windows containers do not have a route to the GCE metadata server by default. 77 if !framework.NodeOSDistroIs("windows") { 78 namesToResolve = append(namesToResolve, "metadata") 79 } 80 81 // TODO: Validate both IPv4 and IPv6 families for dual-stack 82 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 83 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 84 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 85 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 86 87 // Run a pod which probes DNS and exposes the results by HTTP. 88 ginkgo.By("creating a pod to probe DNS") 89 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 90 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 91 }) 92 93 // [LinuxOnly]: As Windows currently does not support resolving PQDNs. 94 ginkgo.It("should resolve DNS of partial qualified names for the cluster [LinuxOnly]", func(ctx context.Context) { 95 // All the names we need to be able to resolve. 96 namesToResolve := []string{ 97 "kubernetes.default", 98 "kubernetes.default.svc", 99 } 100 hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", dnsTestPodHostName, dnsTestServiceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 101 hostEntries := []string{hostFQDN, dnsTestPodHostName} 102 // TODO: Validate both IPv4 and IPv6 families for dual-stack 103 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostEntries, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 104 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostEntries, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 105 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 106 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 107 108 // Run a pod which probes DNS and exposes the results by HTTP. 109 ginkgo.By("creating a pod to probe DNS") 110 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 111 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 112 }) 113 114 /* 115 Release: v1.14 116 Testname: DNS, cluster 117 Description: When a Pod is created, the pod MUST be able to resolve cluster dns entries such as kubernetes.default via /etc/hosts. 118 */ 119 framework.ConformanceIt("should provide /etc/hosts entries for the cluster", func(ctx context.Context) { 120 hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", dnsTestPodHostName, dnsTestServiceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 121 hostEntries := []string{hostFQDN, dnsTestPodHostName} 122 // TODO: Validate both IPv4 and IPv6 families for dual-stack 123 wheezyProbeCmd, wheezyFileNames := createProbeCommand(nil, hostEntries, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 124 jessieProbeCmd, jessieFileNames := createProbeCommand(nil, hostEntries, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 125 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 126 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 127 128 // Run a pod which probes /etc/hosts and exposes the results by HTTP. 129 ginkgo.By("creating a pod to probe /etc/hosts") 130 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 131 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 132 }) 133 134 /* 135 Release: v1.9 136 Testname: DNS, services 137 Description: When a headless service is created, the service MUST be able to resolve all the required service endpoints. When the service is created, any pod in the same namespace must be able to resolve the service by all of the expected DNS names. 138 */ 139 framework.ConformanceIt("should provide DNS for services", func(ctx context.Context) { 140 // NOTE: This only contains the FQDN and the Host name, for testing partial name, see the test below 141 // Create a test headless service. 142 ginkgo.By("Creating a test headless service") 143 testServiceSelector := map[string]string{ 144 "dns-test": "true", 145 } 146 headlessService := e2eservice.CreateServiceSpec(dnsTestServiceName, "", true, testServiceSelector) 147 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, headlessService, metav1.CreateOptions{}) 148 framework.ExpectNoError(err, "failed to create headless service: %s", dnsTestServiceName) 149 ginkgo.DeferCleanup(func(ctx context.Context) error { 150 ginkgo.By("deleting the test headless service") 151 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, headlessService.Name, metav1.DeleteOptions{}) 152 }) 153 154 regularServiceName := "test-service-2" 155 regularService := e2eservice.CreateServiceSpec(regularServiceName, "", false, testServiceSelector) 156 regularService, err = f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, regularService, metav1.CreateOptions{}) 157 framework.ExpectNoError(err, "failed to create regular service: %s", regularServiceName) 158 159 ginkgo.DeferCleanup(func(ctx context.Context) error { 160 ginkgo.By("deleting the test service") 161 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, regularService.Name, metav1.DeleteOptions{}) 162 }) 163 164 // All the names we need to be able to resolve. 165 // TODO: Create more endpoints and ensure that multiple A records are returned 166 // for headless service. 167 namesToResolve := []string{ 168 fmt.Sprintf("%s.%s.svc.%s", headlessService.Name, f.Namespace.Name, framework.TestContext.ClusterDNSDomain), 169 fmt.Sprintf("_http._tcp.%s.%s.svc.%s", headlessService.Name, f.Namespace.Name, framework.TestContext.ClusterDNSDomain), 170 fmt.Sprintf("_http._tcp.%s.%s.svc.%s", regularService.Name, f.Namespace.Name, framework.TestContext.ClusterDNSDomain), 171 } 172 173 // TODO: Validate both IPv4 and IPv6 families for dual-stack 174 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 175 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 176 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 177 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 178 179 // Run a pod which probes DNS and exposes the results by HTTP. 180 ginkgo.By("creating a pod to probe DNS") 181 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 182 pod.ObjectMeta.Labels = testServiceSelector 183 184 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 185 }) 186 187 /* 188 Release: v1.17 189 Testname: DNS, PQDN for services 190 Description: Create a headless service and normal service. Both the services MUST be able to resolve partial qualified DNS entries of their service endpoints by serving A records and SRV records. 191 [LinuxOnly]: As Windows currently does not support resolving PQDNs. 192 */ 193 framework.ConformanceIt("should resolve DNS of partial qualified names for services [LinuxOnly]", func(ctx context.Context) { 194 // Create a test headless service. 195 ginkgo.By("Creating a test headless service") 196 testServiceSelector := map[string]string{ 197 "dns-test": "true", 198 } 199 headlessService := e2eservice.CreateServiceSpec(dnsTestServiceName, "", true, testServiceSelector) 200 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, headlessService, metav1.CreateOptions{}) 201 framework.ExpectNoError(err, "failed to create headless service: %s", dnsTestServiceName) 202 ginkgo.DeferCleanup(func(ctx context.Context) error { 203 ginkgo.By("deleting the test headless service") 204 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, headlessService.Name, metav1.DeleteOptions{}) 205 }) 206 207 regularServiceName := "test-service-2" 208 regularService := e2eservice.CreateServiceSpec(regularServiceName, "", false, testServiceSelector) 209 regularService, err = f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, regularService, metav1.CreateOptions{}) 210 framework.ExpectNoError(err, "failed to create regular service: %s", regularServiceName) 211 ginkgo.DeferCleanup(func(ctx context.Context) error { 212 ginkgo.By("deleting the test service") 213 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, regularService.Name, metav1.DeleteOptions{}) 214 }) 215 216 // All the names we need to be able to resolve. 217 // for headless service. 218 namesToResolve := []string{ 219 headlessService.Name, 220 fmt.Sprintf("%s.%s", headlessService.Name, f.Namespace.Name), 221 fmt.Sprintf("%s.%s.svc", headlessService.Name, f.Namespace.Name), 222 fmt.Sprintf("_http._tcp.%s.%s.svc", headlessService.Name, f.Namespace.Name), 223 fmt.Sprintf("_http._tcp.%s.%s.svc", regularService.Name, f.Namespace.Name), 224 } 225 226 // TODO: Validate both IPv4 and IPv6 families for dual-stack 227 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 228 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 229 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 230 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 231 232 // Run a pod which probes DNS and exposes the results by HTTP. 233 ginkgo.By("creating a pod to probe DNS") 234 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 235 pod.ObjectMeta.Labels = testServiceSelector 236 237 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 238 }) 239 240 /* 241 Release: v1.15 242 Testname: DNS, resolve the hostname 243 Description: Create a headless service with label. Create a Pod with label to match service's label, with hostname and a subdomain same as service name. 244 Pod MUST be able to resolve its fully qualified domain name as well as hostname by serving an A record at that name. 245 */ 246 framework.ConformanceIt("should provide DNS for pods for Hostname", func(ctx context.Context) { 247 // Create a test headless service. 248 ginkgo.By("Creating a test headless service") 249 testServiceSelector := map[string]string{ 250 "dns-test-hostname-attribute": "true", 251 } 252 serviceName := "dns-test-service-2" 253 podHostname := "dns-querier-2" 254 headlessService := e2eservice.CreateServiceSpec(serviceName, "", true, testServiceSelector) 255 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, headlessService, metav1.CreateOptions{}) 256 framework.ExpectNoError(err, "failed to create headless service: %s", serviceName) 257 258 ginkgo.DeferCleanup(func(ctx context.Context) error { 259 ginkgo.By("deleting the test headless service") 260 defer ginkgo.GinkgoRecover() 261 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, headlessService.Name, metav1.DeleteOptions{}) 262 }) 263 264 hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", podHostname, serviceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 265 hostNames := []string{hostFQDN, podHostname} 266 // TODO: Validate both IPv4 and IPv6 families for dual-stack 267 wheezyProbeCmd, wheezyFileNames := createProbeCommand(nil, hostNames, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 268 jessieProbeCmd, jessieFileNames := createProbeCommand(nil, hostNames, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 269 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 270 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 271 272 // Run a pod which probes DNS and exposes the results by HTTP. 273 ginkgo.By("creating a pod to probe DNS") 274 pod1 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 275 pod1.ObjectMeta.Labels = testServiceSelector 276 pod1.Spec.Hostname = podHostname 277 pod1.Spec.Subdomain = serviceName 278 279 validateDNSResults(ctx, f, pod1, append(wheezyFileNames, jessieFileNames...)) 280 }) 281 282 /* 283 Release: v1.15 284 Testname: DNS, resolve the subdomain 285 Description: Create a headless service with label. Create a Pod with label to match service's label, with hostname and a subdomain same as service name. 286 Pod MUST be able to resolve its fully qualified domain name as well as subdomain by serving an A record at that name. 287 */ 288 framework.ConformanceIt("should provide DNS for pods for Subdomain", func(ctx context.Context) { 289 // Create a test headless service. 290 ginkgo.By("Creating a test headless service") 291 testServiceSelector := map[string]string{ 292 "dns-test-hostname-attribute": "true", 293 } 294 serviceName := "dns-test-service-2" 295 podHostname := "dns-querier-2" 296 headlessService := e2eservice.CreateServiceSpec(serviceName, "", true, testServiceSelector) 297 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, headlessService, metav1.CreateOptions{}) 298 framework.ExpectNoError(err, "failed to create headless service: %s", serviceName) 299 300 ginkgo.DeferCleanup(func(ctx context.Context) error { 301 ginkgo.By("deleting the test headless service") 302 defer ginkgo.GinkgoRecover() 303 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, headlessService.Name, metav1.DeleteOptions{}) 304 }) 305 306 hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", podHostname, serviceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 307 subdomain := fmt.Sprintf("%s.%s.svc.%s", serviceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 308 namesToResolve := []string{hostFQDN, subdomain} 309 // TODO: Validate both IPv4 and IPv6 families for dual-stack 310 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 311 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 312 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 313 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 314 315 // Run a pod which probes DNS and exposes the results by HTTP. 316 ginkgo.By("creating a pod to probe DNS") 317 pod1 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 318 pod1.ObjectMeta.Labels = testServiceSelector 319 pod1.Spec.Hostname = podHostname 320 pod1.Spec.Subdomain = serviceName 321 322 validateDNSResults(ctx, f, pod1, append(wheezyFileNames, jessieFileNames...)) 323 }) 324 325 /* 326 Release: v1.15 327 Testname: DNS, for ExternalName Services 328 Description: Create a service with externalName. Pod MUST be able to resolve the address for this service via CNAME. When externalName of this service is changed, Pod MUST resolve to new DNS entry for the service. 329 Change the service type from externalName to ClusterIP, Pod MUST resolve DNS to the service by serving A records. 330 */ 331 framework.ConformanceIt("should provide DNS for ExternalName services", func(ctx context.Context) { 332 // Create a test ExternalName service. 333 ginkgo.By("Creating a test externalName service") 334 serviceName := "dns-test-service-3" 335 externalNameService := e2eservice.CreateServiceSpec(serviceName, "foo.example.com", false, nil) 336 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, externalNameService, metav1.CreateOptions{}) 337 framework.ExpectNoError(err, "failed to create ExternalName service: %s", serviceName) 338 339 ginkgo.DeferCleanup(func(ctx context.Context) error { 340 ginkgo.By("deleting the test externalName service") 341 defer ginkgo.GinkgoRecover() 342 return f.ClientSet.CoreV1().Services(f.Namespace.Name).Delete(ctx, externalNameService.Name, metav1.DeleteOptions{}) 343 }) 344 hostFQDN := fmt.Sprintf("%s.%s.svc.%s", serviceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 345 wheezyProbeCmd, wheezyFileName := createTargetedProbeCommand(hostFQDN, "CNAME", "wheezy") 346 jessieProbeCmd, jessieFileName := createTargetedProbeCommand(hostFQDN, "CNAME", "jessie") 347 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 348 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 349 350 // Run a pod which probes DNS and exposes the results by HTTP. 351 ginkgo.By("creating a pod to probe DNS") 352 pod1 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 353 354 validateTargetedProbeOutput(ctx, f, pod1, []string{wheezyFileName, jessieFileName}, "foo.example.com.") 355 356 // Test changing the externalName field 357 ginkgo.By("changing the externalName to bar.example.com") 358 _, err = e2eservice.UpdateService(ctx, f.ClientSet, f.Namespace.Name, serviceName, func(s *v1.Service) { 359 s.Spec.ExternalName = "bar.example.com" 360 }) 361 framework.ExpectNoError(err, "failed to change externalName of service: %s", serviceName) 362 wheezyProbeCmd, wheezyFileName = createTargetedProbeCommand(hostFQDN, "CNAME", "wheezy") 363 jessieProbeCmd, jessieFileName = createTargetedProbeCommand(hostFQDN, "CNAME", "jessie") 364 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 365 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 366 367 // Run a pod which probes DNS and exposes the results by HTTP. 368 ginkgo.By("creating a second pod to probe DNS") 369 pod2 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 370 371 validateTargetedProbeOutput(ctx, f, pod2, []string{wheezyFileName, jessieFileName}, "bar.example.com.") 372 373 // Test changing type from ExternalName to ClusterIP 374 ginkgo.By("changing the service to type=ClusterIP") 375 _, err = e2eservice.UpdateService(ctx, f.ClientSet, f.Namespace.Name, serviceName, func(s *v1.Service) { 376 s.Spec.Type = v1.ServiceTypeClusterIP 377 s.Spec.Ports = []v1.ServicePort{ 378 {Port: 80, Name: "http", Protocol: v1.ProtocolTCP}, 379 } 380 }) 381 framework.ExpectNoError(err, "failed to change service type to ClusterIP for service: %s", serviceName) 382 targetRecord := "A" 383 if framework.TestContext.ClusterIsIPv6() { 384 targetRecord = "AAAA" 385 } 386 // TODO: For dual stack we can run from here two createTargetedProbeCommand() 387 // one looking for an A record and another one for an AAAA record 388 wheezyProbeCmd, wheezyFileName = createTargetedProbeCommand(hostFQDN, targetRecord, "wheezy") 389 jessieProbeCmd, jessieFileName = createTargetedProbeCommand(hostFQDN, targetRecord, "jessie") 390 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 391 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 392 393 // Run a pod which probes DNS and exposes the results by HTTP. 394 ginkgo.By("creating a third pod to probe DNS") 395 pod3 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 396 397 svc, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Get(ctx, externalNameService.Name, metav1.GetOptions{}) 398 framework.ExpectNoError(err, "failed to get service: %s", externalNameService.Name) 399 400 validateTargetedProbeOutput(ctx, f, pod3, []string{wheezyFileName, jessieFileName}, svc.Spec.ClusterIP) 401 }) 402 403 /* 404 Release: v1.17 405 Testname: DNS, custom dnsConfig 406 Description: Create a Pod with DNSPolicy as None and custom DNS configuration, specifying nameservers and search path entries. 407 Pod creation MUST be successful and provided DNS configuration MUST be configured in the Pod. 408 */ 409 framework.ConformanceIt("should support configurable pod DNS nameservers", func(ctx context.Context) { 410 ginkgo.By("Creating a pod with dnsPolicy=None and customized dnsConfig...") 411 testServerIP := "1.1.1.1" 412 testSearchPath := "resolv.conf.local" 413 testAgnhostPod := e2epod.NewAgnhostPod(f.Namespace.Name, "test-dns-nameservers", nil, nil, nil) 414 testAgnhostPod.Spec.DNSPolicy = v1.DNSNone 415 testAgnhostPod.Spec.DNSConfig = &v1.PodDNSConfig{ 416 Nameservers: []string{testServerIP}, 417 Searches: []string{testSearchPath}, 418 } 419 testAgnhostPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, testAgnhostPod, metav1.CreateOptions{}) 420 framework.ExpectNoError(err, "failed to create pod: %s", testAgnhostPod.Name) 421 framework.Logf("Created pod %v", testAgnhostPod) 422 ginkgo.DeferCleanup(func(ctx context.Context) error { 423 framework.Logf("Deleting pod %s...", testAgnhostPod.Name) 424 return f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(ctx, testAgnhostPod.Name, *metav1.NewDeleteOptions(0)) 425 }) 426 err = e2epod.WaitTimeoutForPodReadyInNamespace(ctx, f.ClientSet, testAgnhostPod.Name, f.Namespace.Name, framework.PodStartTimeout) 427 framework.ExpectNoError(err, "failed to wait for pod %s to be running", testAgnhostPod.Name) 428 429 runCommand := func(arg string) string { 430 cmd := []string{"/agnhost", arg} 431 stdout, stderr, err := e2epod.ExecWithOptions(f, e2epod.ExecOptions{ 432 Command: cmd, 433 Namespace: f.Namespace.Name, 434 PodName: testAgnhostPod.Name, 435 ContainerName: testAgnhostPod.Spec.Containers[0].Name, 436 CaptureStdout: true, 437 CaptureStderr: true, 438 }) 439 framework.ExpectNoError(err, "failed to run command '/agnhost %s' on pod, stdout: %v, stderr: %v, err: %v", arg, stdout, stderr, err) 440 return stdout 441 } 442 443 ginkgo.By("Verifying customized DNS suffix list is configured on pod...") 444 stdout := runCommand("dns-suffix") 445 if !strings.Contains(stdout, testSearchPath) { 446 framework.Failf("customized DNS suffix list not found configured in pod, expected to contain: %s, got: %s", testSearchPath, stdout) 447 } 448 449 ginkgo.By("Verifying customized DNS server is configured on pod...") 450 stdout = runCommand("dns-server-list") 451 if !strings.Contains(stdout, testServerIP) { 452 framework.Failf("customized DNS server not found in configured in pod, expected to contain: %s, got: %s", testServerIP, stdout) 453 } 454 }) 455 456 ginkgo.It("should support configurable pod resolv.conf", func(ctx context.Context) { 457 ginkgo.By("Preparing a test DNS service with injected DNS names...") 458 testInjectedIP := "1.1.1.1" 459 testDNSNameShort := "notexistname" 460 testSearchPath := "resolv.conf.local" 461 testDNSNameFull := fmt.Sprintf("%s.%s", testDNSNameShort, testSearchPath) 462 463 corednsConfig := generateCoreDNSConfigmap(f.Namespace.Name, map[string]string{ 464 testDNSNameFull: testInjectedIP, 465 }) 466 corednsConfig, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, corednsConfig, metav1.CreateOptions{}) 467 framework.ExpectNoError(err, "unable to create test configMap %s", corednsConfig.Name) 468 469 ginkgo.DeferCleanup(func(ctx context.Context) error { 470 framework.Logf("Deleting configmap %s...", corednsConfig.Name) 471 return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Delete(ctx, corednsConfig.Name, metav1.DeleteOptions{}) 472 }) 473 474 testServerPod := generateCoreDNSServerPod(corednsConfig) 475 testServerPod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, testServerPod, metav1.CreateOptions{}) 476 framework.ExpectNoError(err, "failed to create pod: %s", testServerPod.Name) 477 framework.Logf("Created pod %v", testServerPod) 478 ginkgo.DeferCleanup(func(ctx context.Context) error { 479 framework.Logf("Deleting pod %s...", testServerPod.Name) 480 return f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(ctx, testServerPod.Name, *metav1.NewDeleteOptions(0)) 481 }) 482 err = e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, testServerPod.Name, f.Namespace.Name) 483 framework.ExpectNoError(err, "failed to wait for pod %s to be running", testServerPod.Name) 484 485 // Retrieve server pod IP. 486 testServerPod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(ctx, testServerPod.Name, metav1.GetOptions{}) 487 framework.ExpectNoError(err, "failed to get pod %v", testServerPod.Name) 488 testServerIP := testServerPod.Status.PodIP 489 framework.Logf("testServerIP is %s", testServerIP) 490 491 ginkgo.By("Creating a pod with dnsPolicy=None and customized dnsConfig...") 492 testUtilsPod := e2epod.NewAgnhostPod(f.Namespace.Name, "e2e-dns-utils", nil, nil, nil) 493 testUtilsPod.Spec.DNSPolicy = v1.DNSNone 494 testNdotsValue := "2" 495 testUtilsPod.Spec.DNSConfig = &v1.PodDNSConfig{ 496 Nameservers: []string{testServerIP}, 497 Searches: []string{testSearchPath}, 498 Options: []v1.PodDNSConfigOption{ 499 { 500 Name: "ndots", 501 Value: &testNdotsValue, 502 }, 503 }, 504 } 505 testUtilsPod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, testUtilsPod, metav1.CreateOptions{}) 506 framework.ExpectNoError(err, "failed to create pod: %s", testUtilsPod.Name) 507 framework.Logf("Created pod %v", testUtilsPod) 508 ginkgo.DeferCleanup(func(ctx context.Context) error { 509 framework.Logf("Deleting pod %s...", testUtilsPod.Name) 510 return f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(ctx, testUtilsPod.Name, *metav1.NewDeleteOptions(0)) 511 }) 512 err = e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, testUtilsPod.Name, f.Namespace.Name) 513 framework.ExpectNoError(err, "failed to wait for pod %s to be running", testUtilsPod.Name) 514 515 ginkgo.By("Verifying customized DNS option is configured on pod...") 516 // TODO: Figure out a better way other than checking the actual resolv,conf file. 517 cmd := []string{"cat", "/etc/resolv.conf"} 518 stdout, stderr, err := e2epod.ExecWithOptions(f, e2epod.ExecOptions{ 519 Command: cmd, 520 Namespace: f.Namespace.Name, 521 PodName: testUtilsPod.Name, 522 ContainerName: testUtilsPod.Spec.Containers[0].Name, 523 CaptureStdout: true, 524 CaptureStderr: true, 525 }) 526 framework.ExpectNoError(err, "failed to examine resolv,conf file on pod, stdout: %v, stderr: %v, err: %v", stdout, stderr, err) 527 if !strings.Contains(stdout, "ndots:2") { 528 framework.Failf("customized DNS options not found in resolv.conf, got: %s", stdout) 529 } 530 531 ginkgo.By("Verifying customized name server and search path are working...") 532 // Do dig on not-exist-dns-name and see if the injected DNS record is returned. 533 // This verifies both: 534 // - Custom search path is appended. 535 // - DNS query is sent to the specified server. 536 cmd = []string{"dig", "+short", "+search", testDNSNameShort} 537 digFunc := func() (bool, error) { 538 stdout, stderr, err := e2epod.ExecWithOptions(f, e2epod.ExecOptions{ 539 Command: cmd, 540 Namespace: f.Namespace.Name, 541 PodName: testUtilsPod.Name, 542 ContainerName: testUtilsPod.Spec.Containers[0].Name, 543 CaptureStdout: true, 544 CaptureStderr: true, 545 }) 546 if err != nil { 547 framework.Logf("ginkgo.Failed to execute dig command, stdout:%v, stderr: %v, err: %v", stdout, stderr, err) 548 return false, nil 549 } 550 res := strings.Split(stdout, "\n") 551 if len(res) != 1 || res[0] != testInjectedIP { 552 framework.Logf("Expect command `%v` to return %s, got: %v", cmd, testInjectedIP, res) 553 return false, nil 554 } 555 return true, nil 556 } 557 err = wait.PollImmediate(5*time.Second, 3*time.Minute, digFunc) 558 framework.ExpectNoError(err, "failed to verify customized name server and search path") 559 560 // TODO: Add more test cases for other DNSPolicies. 561 }) 562 563 ginkgo.It("should work with the pod containing more than 6 DNS search paths and longer than 256 search list characters", func(ctx context.Context) { 564 // All the names we need to be able to resolve. 565 namesToResolve := []string{ 566 "kubernetes.default", 567 "kubernetes.default.svc", 568 } 569 hostFQDN := fmt.Sprintf("%s.%s.%s.svc.%s", dnsTestPodHostName, dnsTestServiceName, f.Namespace.Name, framework.TestContext.ClusterDNSDomain) 570 hostEntries := []string{hostFQDN, dnsTestPodHostName} 571 // TODO: Validate both IPv4 and IPv6 families for dual-stack 572 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostEntries, "", "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 573 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostEntries, "", "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 574 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 575 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 576 577 ginkgo.By("Creating a pod with expanded DNS configuration to probe DNS") 578 testNdotsValue := "5" 579 testSearchPaths := []string{ 580 fmt.Sprintf("%038d.k8s.io", 1), 581 fmt.Sprintf("%038d.k8s.io", 2), 582 fmt.Sprintf("%038d.k8s.io", 3), 583 fmt.Sprintf("%038d.k8s.io", 4), 584 fmt.Sprintf("%038d.k8s.io", 5), 585 fmt.Sprintf("%038d.k8s.io", 6), // 260 characters 586 } 587 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 588 pod.Spec.DNSPolicy = v1.DNSClusterFirst 589 pod.Spec.DNSConfig = &v1.PodDNSConfig{ 590 Searches: testSearchPaths, 591 Options: []v1.PodDNSConfigOption{ 592 { 593 Name: "ndots", 594 Value: &testNdotsValue, 595 }, 596 }, 597 } 598 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 599 }) 600 601 }) 602 603 var _ = common.SIGDescribe("DNS HostNetwork", func() { 604 f := framework.NewDefaultFramework("hostnetworkdns") 605 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 606 607 ginkgo.It("should resolve DNS of partial qualified names for services on hostNetwork pods with dnsPolicy: ClusterFirstWithHostNet [LinuxOnly]", func(ctx context.Context) { 608 // Create a test headless service. 609 ginkgo.By("Creating a test headless service") 610 testServiceSelector := map[string]string{ 611 "dns-test": "true", 612 } 613 headlessService := e2eservice.CreateServiceSpec(dnsTestServiceName, "", true, testServiceSelector) 614 _, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, headlessService, metav1.CreateOptions{}) 615 framework.ExpectNoError(err, "failed to create headless service: %s", dnsTestServiceName) 616 617 regularServiceName := "test-service-2" 618 regularService := e2eservice.CreateServiceSpec(regularServiceName, "", false, testServiceSelector) 619 regularService, err = f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(ctx, regularService, metav1.CreateOptions{}) 620 framework.ExpectNoError(err, "failed to create regular service: %s", regularServiceName) 621 622 // All the names we need to be able to resolve. 623 // for headless service. 624 namesToResolve := []string{ 625 headlessService.Name, 626 fmt.Sprintf("%s.%s", headlessService.Name, f.Namespace.Name), 627 fmt.Sprintf("%s.%s.svc", headlessService.Name, f.Namespace.Name), 628 fmt.Sprintf("_http._tcp.%s.%s.svc", headlessService.Name, f.Namespace.Name), 629 fmt.Sprintf("_http._tcp.%s.%s.svc", regularService.Name, f.Namespace.Name), 630 } 631 632 // TODO: Validate both IPv4 and IPv6 families for dual-stack 633 wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 634 jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "jessie", f.Namespace.Name, framework.TestContext.ClusterDNSDomain, framework.TestContext.ClusterIsIPv6()) 635 ginkgo.By("Running these commands on wheezy: " + wheezyProbeCmd + "\n") 636 ginkgo.By("Running these commands on jessie: " + jessieProbeCmd + "\n") 637 638 // Run a pod which probes DNS and exposes the results by HTTP. 639 ginkgo.By("creating a pod to probe DNS") 640 pod := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, dnsTestPodHostName, dnsTestServiceName) 641 pod.ObjectMeta.Labels = testServiceSelector 642 pod.Spec.HostNetwork = true 643 pod.Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet 644 validateDNSResults(ctx, f, pod, append(wheezyFileNames, jessieFileNames...)) 645 }) 646 647 // https://issues.k8s.io/67019 648 ginkgo.It("spec.Hostname field is not silently ignored and is used for hostname for a Pod", func(ctx context.Context) { 649 ginkgo.By("Creating a pod by setting a hostname") 650 651 testAgnhostPod := e2epod.NewAgnhostPod(f.Namespace.Name, "test-dns-hostname", nil, nil, nil) 652 testAgnhostPod.Spec.Hostname = dnsTestPodHostName 653 testAgnhostPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, testAgnhostPod, metav1.CreateOptions{}) 654 framework.ExpectNoError(err, "failed to created pod: %s", testAgnhostPod.Name) 655 656 err = e2epod.WaitTimeoutForPodReadyInNamespace(ctx, f.ClientSet, testAgnhostPod.Name, f.Namespace.Name, framework.PodStartTimeout) 657 framework.ExpectNoError(err, "failed to wait for pod %s to be running", testAgnhostPod.Name) 658 659 stdout, err := e2eoutput.RunHostCmd(testAgnhostPod.Namespace, testAgnhostPod.Name, "hostname") 660 framework.ExpectNoError(err, "failed to run command hostname: %s", stdout) 661 hostname := strings.TrimSpace(stdout) 662 if testAgnhostPod.Spec.Hostname != hostname { 663 framework.Failf("expected hostname: %s, got: %s", testAgnhostPod.Spec.Hostname, hostname) 664 } 665 }) 666 667 // https://issues.k8s.io/67019 668 ginkgo.It("spec.Hostname field is silently ignored and the node hostname is used when hostNetwork is set to true for a Pod", func(ctx context.Context) { 669 ginkgo.By("Creating a pod by setting a hostNetwork to true") 670 671 testAgnhostPod := e2epod.NewAgnhostPod(f.Namespace.Name, "test-dns-hostnetwork", nil, nil, nil) 672 testAgnhostPod.Spec.Hostname = dnsTestPodHostName 673 testAgnhostPod.Spec.HostNetwork = true 674 node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) 675 framework.ExpectNoError(err) 676 nodeSelection := e2epod.NodeSelection{} 677 e2epod.SetAffinity(&nodeSelection, node.Name) 678 e2epod.SetNodeSelection(&testAgnhostPod.Spec, nodeSelection) 679 680 testAgnhostPod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, testAgnhostPod, metav1.CreateOptions{}) 681 framework.ExpectNoError(err, "failed to created pod: %s", testAgnhostPod.Name) 682 683 err = e2epod.WaitTimeoutForPodReadyInNamespace(ctx, f.ClientSet, testAgnhostPod.Name, f.Namespace.Name, framework.PodStartTimeout) 684 framework.ExpectNoError(err, "failed to wait for pod %s to be running", testAgnhostPod.Name) 685 686 stdout, err := e2eoutput.RunHostCmd(testAgnhostPod.Namespace, testAgnhostPod.Name, "hostname") 687 framework.ExpectNoError(err, "failed to run command hostname: %s", stdout) 688 hostname := strings.TrimSpace(stdout) 689 if dnsTestPodHostName == hostname { 690 framework.Failf("https://issues.k8s.io/67019 expected spec.Hostname %s to be ignored", hostname) 691 } 692 }) 693 694 })