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