example_cluster_dns.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package network
  14. import (
  15. "context"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. "time"
  22. "github.com/onsi/ginkgo"
  23. v1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/labels"
  26. "k8s.io/apimachinery/pkg/util/wait"
  27. clientset "k8s.io/client-go/kubernetes"
  28. api "k8s.io/kubernetes/pkg/apis/core"
  29. "k8s.io/kubernetes/test/e2e/framework"
  30. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  31. e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
  32. )
  33. const (
  34. dnsReadyTimeout = time.Minute
  35. // RespondingTimeout is how long to wait for a service to be responding.
  36. RespondingTimeout = 2 * time.Minute
  37. )
  38. const queryDNSPythonTemplate string = `
  39. import socket
  40. try:
  41. socket.gethostbyname('%s')
  42. print('ok')
  43. except:
  44. print('err')`
  45. var _ = SIGDescribe("ClusterDns [Feature:Example]", func() {
  46. f := framework.NewDefaultFramework("cluster-dns")
  47. var c clientset.Interface
  48. ginkgo.BeforeEach(func() {
  49. c = f.ClientSet
  50. })
  51. ginkgo.It("should create pod that uses dns", func() {
  52. mkpath := func(file string) string {
  53. return filepath.Join(os.Getenv("GOPATH"), "src/k8s.io/examples/staging/cluster-dns", file)
  54. }
  55. // contrary to the example, this test does not use contexts, for simplicity
  56. // namespaces are passed directly.
  57. // Also, for simplicity, we don't use yamls with namespaces, but we
  58. // create testing namespaces instead.
  59. backendRcYaml := mkpath("dns-backend-rc.yaml")
  60. backendRcName := "dns-backend"
  61. backendSvcYaml := mkpath("dns-backend-service.yaml")
  62. backendSvcName := "dns-backend"
  63. backendPodName := "dns-backend"
  64. frontendPodYaml := mkpath("dns-frontend-pod.yaml")
  65. frontendPodName := "dns-frontend"
  66. frontendPodContainerName := "dns-frontend"
  67. podOutput := "Hello World!"
  68. // we need two namespaces anyway, so let's forget about
  69. // the one created in BeforeEach and create two new ones.
  70. namespaces := []*v1.Namespace{nil, nil}
  71. for i := range namespaces {
  72. var err error
  73. namespaceName := fmt.Sprintf("dnsexample%d", i)
  74. namespaces[i], err = f.CreateNamespace(namespaceName, nil)
  75. framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName)
  76. }
  77. for _, ns := range namespaces {
  78. framework.RunKubectlOrDie(ns.Name, "create", "-f", backendRcYaml, getNsCmdFlag(ns))
  79. }
  80. for _, ns := range namespaces {
  81. framework.RunKubectlOrDie(ns.Name, "create", "-f", backendSvcYaml, getNsCmdFlag(ns))
  82. }
  83. // wait for objects
  84. for _, ns := range namespaces {
  85. e2epod.WaitForControlledPodsRunning(c, ns.Name, backendRcName, api.Kind("ReplicationController"))
  86. framework.WaitForService(c, ns.Name, backendSvcName, true, framework.Poll, framework.ServiceStartTimeout)
  87. }
  88. // it is not enough that pods are running because they may be set to running, but
  89. // the application itself may have not been initialized. Just query the application.
  90. for _, ns := range namespaces {
  91. label := labels.SelectorFromSet(labels.Set(map[string]string{"name": backendRcName}))
  92. options := metav1.ListOptions{LabelSelector: label.String()}
  93. pods, err := c.CoreV1().Pods(ns.Name).List(context.TODO(), options)
  94. framework.ExpectNoError(err, "failed to list pods in namespace: %s", ns.Name)
  95. err = e2epod.PodsResponding(c, ns.Name, backendPodName, false, pods)
  96. framework.ExpectNoError(err, "waiting for all pods to respond")
  97. framework.Logf("found %d backend pods responding in namespace %s", len(pods.Items), ns.Name)
  98. err = waitForServiceResponding(c, ns.Name, backendSvcName)
  99. framework.ExpectNoError(err, "waiting for the service to respond")
  100. }
  101. // Now another tricky part:
  102. // It may happen that the service name is not yet in DNS.
  103. // So if we start our pod, it will fail. We must make sure
  104. // the name is already resolvable. So let's try to query DNS from
  105. // the pod we have, until we find our service name.
  106. // This complicated code may be removed if the pod itself retried after
  107. // dns error or timeout.
  108. // This code is probably unnecessary, but let's stay on the safe side.
  109. label := labels.SelectorFromSet(labels.Set(map[string]string{"name": backendPodName}))
  110. options := metav1.ListOptions{LabelSelector: label.String()}
  111. pods, err := c.CoreV1().Pods(namespaces[0].Name).List(context.TODO(), options)
  112. if err != nil || pods == nil || len(pods.Items) == 0 {
  113. framework.Failf("no running pods found")
  114. }
  115. podName := pods.Items[0].Name
  116. queryDNS := fmt.Sprintf(queryDNSPythonTemplate, backendSvcName+"."+namespaces[0].Name)
  117. _, err = framework.LookForStringInPodExec(namespaces[0].Name, podName, []string{"python", "-c", queryDNS}, "ok", dnsReadyTimeout)
  118. framework.ExpectNoError(err, "waiting for output from pod exec")
  119. updatedPodYaml := prepareResourceWithReplacedString(frontendPodYaml, fmt.Sprintf("dns-backend.development.svc.%s", framework.TestContext.ClusterDNSDomain), fmt.Sprintf("dns-backend.%s.svc.%s", namespaces[0].Name, framework.TestContext.ClusterDNSDomain))
  120. // create a pod in each namespace
  121. for _, ns := range namespaces {
  122. framework.NewKubectlCommand(ns.Name, "create", "-f", "-", getNsCmdFlag(ns)).WithStdinData(updatedPodYaml).ExecOrDie(ns.Name)
  123. }
  124. // wait until the pods have been scheduler, i.e. are not Pending anymore. Remember
  125. // that we cannot wait for the pods to be running because our pods terminate by themselves.
  126. for _, ns := range namespaces {
  127. err := e2epod.WaitForPodNotPending(c, ns.Name, frontendPodName)
  128. framework.ExpectNoError(err)
  129. }
  130. // wait for pods to print their result
  131. for _, ns := range namespaces {
  132. _, err := framework.LookForStringInLog(ns.Name, frontendPodName, frontendPodContainerName, podOutput, framework.PodStartTimeout)
  133. framework.ExpectNoError(err, "pod %s failed to print result in logs", frontendPodName)
  134. }
  135. })
  136. })
  137. func getNsCmdFlag(ns *v1.Namespace) string {
  138. return fmt.Sprintf("--namespace=%v", ns.Name)
  139. }
  140. // pass enough context with the 'old' parameter so that it replaces what your really intended.
  141. func prepareResourceWithReplacedString(inputFile, old, new string) string {
  142. f, err := os.Open(inputFile)
  143. framework.ExpectNoError(err, "failed to open file: %s", inputFile)
  144. defer f.Close()
  145. data, err := ioutil.ReadAll(f)
  146. framework.ExpectNoError(err, "failed to read from file: %s", inputFile)
  147. podYaml := strings.Replace(string(data), old, new, 1)
  148. return podYaml
  149. }
  150. // waitForServiceResponding waits for the service to be responding.
  151. func waitForServiceResponding(c clientset.Interface, ns, name string) error {
  152. ginkgo.By(fmt.Sprintf("trying to dial the service %s.%s via the proxy", ns, name))
  153. return wait.PollImmediate(framework.Poll, RespondingTimeout, func() (done bool, err error) {
  154. proxyRequest, errProxy := e2eservice.GetServicesProxyRequest(c, c.CoreV1().RESTClient().Get())
  155. if errProxy != nil {
  156. framework.Logf("Failed to get services proxy request: %v:", errProxy)
  157. return false, nil
  158. }
  159. ctx, cancel := context.WithTimeout(context.Background(), framework.SingleCallTimeout)
  160. defer cancel()
  161. body, err := proxyRequest.Namespace(ns).
  162. Name(name).
  163. Do(ctx).
  164. Raw()
  165. if err != nil {
  166. if ctx.Err() != nil {
  167. framework.Failf("Failed to GET from service %s: %v", name, err)
  168. return true, err
  169. }
  170. framework.Logf("Failed to GET from service %s: %v:", name, err)
  171. return false, nil
  172. }
  173. got := string(body)
  174. if len(got) == 0 {
  175. framework.Logf("Service %s: expected non-empty response", name)
  176. return false, err // stop polling
  177. }
  178. framework.Logf("Service %s: found nonempty answer: %s", name, got)
  179. return true, nil
  180. })
  181. }