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  })