k8s.io/kubernetes@v1.29.3/test/e2e/network/example_cluster_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 "path/filepath" 23 "strings" 24 "time" 25 26 "github.com/onsi/ginkgo/v2" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/util/wait" 32 clientset "k8s.io/client-go/kubernetes" 33 api "k8s.io/kubernetes/pkg/apis/core" 34 "k8s.io/kubernetes/test/e2e/feature" 35 "k8s.io/kubernetes/test/e2e/framework" 36 e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl" 37 e2enetwork "k8s.io/kubernetes/test/e2e/framework/network" 38 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 39 e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 40 e2eresource "k8s.io/kubernetes/test/e2e/framework/resource" 41 e2eservice "k8s.io/kubernetes/test/e2e/framework/service" 42 e2etestfiles "k8s.io/kubernetes/test/e2e/framework/testfiles" 43 "k8s.io/kubernetes/test/e2e/network/common" 44 admissionapi "k8s.io/pod-security-admission/api" 45 ) 46 47 const ( 48 dnsReadyTimeout = time.Minute 49 50 // RespondingTimeout is how long to wait for a service to be responding. 51 RespondingTimeout = 2 * time.Minute 52 ) 53 54 const queryDNSPythonTemplate string = ` 55 import socket 56 try: 57 socket.gethostbyname('%s') 58 print('ok') 59 except: 60 print('err')` 61 62 var _ = common.SIGDescribe("ClusterDns", feature.Example, func() { 63 f := framework.NewDefaultFramework("cluster-dns") 64 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 65 66 var c clientset.Interface 67 ginkgo.BeforeEach(func() { 68 c = f.ClientSet 69 }) 70 71 read := func(file string) string { 72 data, err := e2etestfiles.Read(file) 73 if err != nil { 74 framework.Fail(err.Error()) 75 } 76 return string(data) 77 } 78 79 ginkgo.It("should create pod that uses dns", func(ctx context.Context) { 80 // contrary to the example, this test does not use contexts, for simplicity 81 // namespaces are passed directly. 82 // Also, for simplicity, we don't use yamls with namespaces, but we 83 // create testing namespaces instead. 84 85 backendName := "dns-backend" 86 frontendName := "dns-frontend" 87 clusterDnsPath := "test/e2e/testing-manifests/cluster-dns" 88 podOutput := "Hello World!" 89 90 // we need two namespaces anyway, so let's forget about 91 // the one created in BeforeEach and create two new ones. 92 namespaces := []*v1.Namespace{nil, nil} 93 for i := range namespaces { 94 var err error 95 namespaceName := fmt.Sprintf("dnsexample%d", i) 96 namespaces[i], err = f.CreateNamespace(ctx, namespaceName, nil) 97 framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName) 98 } 99 100 for _, ns := range namespaces { 101 e2ekubectl.RunKubectlOrDieInput(ns.Name, read(filepath.Join(clusterDnsPath, "dns-backend-rc.yaml")), "create", "-f", "-") 102 } 103 104 for _, ns := range namespaces { 105 e2ekubectl.RunKubectlOrDieInput(ns.Name, read(filepath.Join(clusterDnsPath, "dns-backend-service.yaml")), "create", "-f", "-") 106 } 107 108 // wait for objects 109 for _, ns := range namespaces { 110 e2eresource.WaitForControlledPodsRunning(ctx, c, ns.Name, backendName, api.Kind("ReplicationController")) 111 framework.ExpectNoError(e2enetwork.WaitForService(ctx, c, ns.Name, backendName, true, framework.Poll, framework.ServiceStartTimeout)) 112 } 113 // it is not enough that pods are running because they may be set to running, but 114 // the application itself may have not been initialized. Just query the application. 115 for _, ns := range namespaces { 116 label := labels.SelectorFromSet(labels.Set(map[string]string{"name": backendName})) 117 options := metav1.ListOptions{LabelSelector: label.String()} 118 pods, err := c.CoreV1().Pods(ns.Name).List(ctx, options) 119 framework.ExpectNoError(err, "failed to list pods in namespace: %s", ns.Name) 120 err = e2epod.WaitForPodsResponding(ctx, c, ns.Name, backendName, false, 0, pods) 121 framework.ExpectNoError(err, "waiting for all pods to respond") 122 framework.Logf("found %d backend pods responding in namespace %s", len(pods.Items), ns.Name) 123 124 err = waitForServiceResponding(ctx, c, ns.Name, backendName) 125 framework.ExpectNoError(err, "waiting for the service to respond") 126 } 127 128 // Now another tricky part: 129 // It may happen that the service name is not yet in DNS. 130 // So if we start our pod, it will fail. We must make sure 131 // the name is already resolvable. So let's try to query DNS from 132 // the pod we have, until we find our service name. 133 // This complicated code may be removed if the pod itself retried after 134 // dns error or timeout. 135 // This code is probably unnecessary, but let's stay on the safe side. 136 label := labels.SelectorFromSet(labels.Set(map[string]string{"name": backendName})) 137 options := metav1.ListOptions{LabelSelector: label.String()} 138 pods, err := c.CoreV1().Pods(namespaces[0].Name).List(ctx, options) 139 140 if err != nil || pods == nil || len(pods.Items) == 0 { 141 framework.Failf("no running pods found") 142 } 143 podName := pods.Items[0].Name 144 145 queryDNS := fmt.Sprintf(queryDNSPythonTemplate, backendName+"."+namespaces[0].Name) 146 _, err = e2eoutput.LookForStringInPodExec(namespaces[0].Name, podName, []string{"python", "-c", queryDNS}, "ok", dnsReadyTimeout) 147 framework.ExpectNoError(err, "waiting for output from pod exec") 148 149 updatedPodYaml := strings.Replace(read(filepath.Join(clusterDnsPath, "dns-frontend-pod.yaml")), fmt.Sprintf("dns-backend.development.svc.%s", framework.TestContext.ClusterDNSDomain), fmt.Sprintf("dns-backend.%s.svc.%s", namespaces[0].Name, framework.TestContext.ClusterDNSDomain), 1) 150 151 // create a pod in each namespace 152 for _, ns := range namespaces { 153 e2ekubectl.RunKubectlOrDieInput(ns.Name, updatedPodYaml, "create", "-f", "-") 154 } 155 156 // wait until the pods have been scheduler, i.e. are not Pending anymore. Remember 157 // that we cannot wait for the pods to be running because our pods terminate by themselves. 158 for _, ns := range namespaces { 159 err := e2epod.WaitForPodNotPending(ctx, c, ns.Name, frontendName) 160 framework.ExpectNoError(err) 161 } 162 163 // wait for pods to print their result 164 for _, ns := range namespaces { 165 _, err := e2eoutput.LookForStringInLog(ns.Name, frontendName, frontendName, podOutput, framework.PodStartTimeout) 166 framework.ExpectNoError(err, "pod %s failed to print result in logs", frontendName) 167 } 168 }) 169 }) 170 171 // waitForServiceResponding waits for the service to be responding. 172 func waitForServiceResponding(ctx context.Context, c clientset.Interface, ns, name string) error { 173 ginkgo.By(fmt.Sprintf("trying to dial the service %s.%s via the proxy", ns, name)) 174 175 return wait.PollUntilContextTimeout(ctx, framework.Poll, RespondingTimeout, true, func(ctx context.Context) (done bool, err error) { 176 proxyRequest, errProxy := e2eservice.GetServicesProxyRequest(c, c.CoreV1().RESTClient().Get()) 177 if errProxy != nil { 178 framework.Logf("Failed to get services proxy request: %v:", errProxy) 179 return false, nil 180 } 181 182 ctx, cancel := context.WithTimeout(ctx, framework.SingleCallTimeout) 183 defer cancel() 184 185 body, err := proxyRequest.Namespace(ns). 186 Name(name). 187 Do(ctx). 188 Raw() 189 if err != nil { 190 if ctx.Err() != nil { 191 framework.Failf("Failed to GET from service %s: %v", name, err) 192 return true, err 193 } 194 framework.Logf("Failed to GET from service %s: %v:", name, err) 195 return false, nil 196 } 197 got := string(body) 198 if len(got) == 0 { 199 framework.Logf("Service %s: expected non-empty response", name) 200 return false, err // stop polling 201 } 202 framework.Logf("Service %s: found nonempty answer: %s", name, got) 203 return true, nil 204 }) 205 }