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