networking.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. Copyright 2014 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 e2e
  14. import (
  15. "fmt"
  16. "net/http"
  17. "strings"
  18. "time"
  19. "k8s.io/kubernetes/pkg/api"
  20. "k8s.io/kubernetes/pkg/util/intstr"
  21. "k8s.io/kubernetes/test/e2e/framework"
  22. . "github.com/onsi/ginkgo"
  23. . "github.com/onsi/gomega"
  24. )
  25. var _ = framework.KubeDescribe("Networking", func() {
  26. f := framework.NewDefaultFramework("nettest")
  27. var svcname = "nettest"
  28. BeforeEach(func() {
  29. //Assert basic external connectivity.
  30. //Since this is not really a test of kubernetes in any way, we
  31. //leave it as a pre-test assertion, rather than a Ginko test.
  32. By("Executing a successful http request from the external internet")
  33. resp, err := http.Get("http://google.com")
  34. if err != nil {
  35. framework.Failf("Unable to connect/talk to the internet: %v", err)
  36. }
  37. if resp.StatusCode != http.StatusOK {
  38. framework.Failf("Unexpected error code, expected 200, got, %v (%v)", resp.StatusCode, resp)
  39. }
  40. })
  41. It("should provide Internet connection for containers [Conformance]", func() {
  42. By("Running container which tries to wget google.com")
  43. framework.ExpectNoError(framework.CheckConnectivityToHost(f, "", "wget-test", "google.com", 30))
  44. })
  45. // First test because it has no dependencies on variables created later on.
  46. It("should provide unchanging, static URL paths for kubernetes api services [Conformance]", func() {
  47. tests := []struct {
  48. path string
  49. }{
  50. {path: "/healthz"},
  51. {path: "/api"},
  52. {path: "/apis"},
  53. {path: "/logs"},
  54. {path: "/metrics"},
  55. {path: "/swaggerapi"},
  56. {path: "/version"},
  57. // TODO: test proxy links here
  58. }
  59. for _, test := range tests {
  60. By(fmt.Sprintf("testing: %s", test.path))
  61. data, err := f.Client.RESTClient.Get().
  62. AbsPath(test.path).
  63. DoRaw()
  64. if err != nil {
  65. framework.Failf("Failed: %v\nBody: %s", err, string(data))
  66. }
  67. }
  68. })
  69. //Now we can proceed with the test.
  70. It("should function for intra-pod communication [Conformance]", func() {
  71. By(fmt.Sprintf("Creating a service named %q in namespace %q", svcname, f.Namespace.Name))
  72. svc, err := f.Client.Services(f.Namespace.Name).Create(&api.Service{
  73. ObjectMeta: api.ObjectMeta{
  74. Name: svcname,
  75. Labels: map[string]string{
  76. "name": svcname,
  77. },
  78. },
  79. Spec: api.ServiceSpec{
  80. Ports: []api.ServicePort{{
  81. Protocol: "TCP",
  82. Port: 8080,
  83. TargetPort: intstr.FromInt(8080),
  84. }},
  85. Selector: map[string]string{
  86. "name": svcname,
  87. },
  88. },
  89. })
  90. if err != nil {
  91. framework.Failf("unable to create test service named [%s] %v", svc.Name, err)
  92. }
  93. // Clean up service
  94. defer func() {
  95. By("Cleaning up the service")
  96. if err = f.Client.Services(f.Namespace.Name).Delete(svc.Name); err != nil {
  97. framework.Failf("unable to delete svc %v: %v", svc.Name, err)
  98. }
  99. }()
  100. By("Creating a webserver (pending) pod on each node")
  101. framework.ExpectNoError(framework.WaitForAllNodesSchedulable(f.Client))
  102. nodes := framework.GetReadySchedulableNodesOrDie(f.Client)
  103. // This test is super expensive in terms of network usage - large services
  104. // result in huge "Endpoint" objects and all underlying pods read them
  105. // periodically. Moreover, all KubeProxies watch all of them.
  106. // Thus we limit the maximum number of pods under a service.
  107. //
  108. // TODO: Remove this limitation once services, endpoints and data flows
  109. // between nodes and master are better optimized.
  110. maxNodeCount := 250
  111. if len(nodes.Items) > maxNodeCount {
  112. nodes.Items = nodes.Items[:maxNodeCount]
  113. }
  114. if len(nodes.Items) == 1 {
  115. // in general, the test requires two nodes. But for local development, often a one node cluster
  116. // is created, for simplicity and speed. (see issue #10012). We permit one-node test
  117. // only in some cases
  118. if !framework.ProviderIs("local") {
  119. framework.Failf(fmt.Sprintf("The test requires two Ready nodes on %s, but found just one.", framework.TestContext.Provider))
  120. }
  121. framework.Logf("Only one ready node is detected. The test has limited scope in such setting. " +
  122. "Rerun it with at least two nodes to get complete coverage.")
  123. }
  124. podNames := LaunchNetTestPodPerNode(f, nodes, svcname)
  125. // Clean up the pods
  126. defer func() {
  127. By("Cleaning up the webserver pods")
  128. for _, podName := range podNames {
  129. if err = f.Client.Pods(f.Namespace.Name).Delete(podName, nil); err != nil {
  130. framework.Logf("Failed to delete pod %s: %v", podName, err)
  131. }
  132. }
  133. }()
  134. By("Waiting for the webserver pods to transition to Running state")
  135. for _, podName := range podNames {
  136. err = f.WaitForPodRunning(podName)
  137. Expect(err).NotTo(HaveOccurred())
  138. }
  139. By("Waiting for connectivity to be verified")
  140. passed := false
  141. //once response OK, evaluate response body for pass/fail.
  142. var body []byte
  143. getDetails := func() ([]byte, error) {
  144. proxyRequest, errProxy := framework.GetServicesProxyRequest(f.Client, f.Client.Get())
  145. if errProxy != nil {
  146. return nil, errProxy
  147. }
  148. return proxyRequest.Namespace(f.Namespace.Name).
  149. Name(svc.Name).
  150. Suffix("read").
  151. DoRaw()
  152. }
  153. getStatus := func() ([]byte, error) {
  154. proxyRequest, errProxy := framework.GetServicesProxyRequest(f.Client, f.Client.Get())
  155. if errProxy != nil {
  156. return nil, errProxy
  157. }
  158. return proxyRequest.Namespace(f.Namespace.Name).
  159. Name(svc.Name).
  160. Suffix("status").
  161. DoRaw()
  162. }
  163. // nettest containers will wait for all service endpoints to come up for 2 minutes
  164. // apply a 3 minutes observation period here to avoid this test to time out before the nettest starts to contact peers
  165. timeout := time.Now().Add(3 * time.Minute)
  166. for i := 0; !passed && timeout.After(time.Now()); i++ {
  167. time.Sleep(2 * time.Second)
  168. framework.Logf("About to make a proxy status call")
  169. start := time.Now()
  170. body, err = getStatus()
  171. framework.Logf("Proxy status call returned in %v", time.Since(start))
  172. if err != nil {
  173. framework.Logf("Attempt %v: service/pod still starting. (error: '%v')", i, err)
  174. continue
  175. }
  176. // Finally, we pass/fail the test based on if the container's response body, as to whether or not it was able to find peers.
  177. switch {
  178. case string(body) == "pass":
  179. framework.Logf("Passed on attempt %v. Cleaning up.", i)
  180. passed = true
  181. case string(body) == "running":
  182. framework.Logf("Attempt %v: test still running", i)
  183. case string(body) == "fail":
  184. if body, err = getDetails(); err != nil {
  185. framework.Failf("Failed on attempt %v. Cleaning up. Error reading details: %v", i, err)
  186. } else {
  187. framework.Failf("Failed on attempt %v. Cleaning up. Details:\n%s", i, string(body))
  188. }
  189. case strings.Contains(string(body), "no endpoints available"):
  190. framework.Logf("Attempt %v: waiting on service/endpoints", i)
  191. default:
  192. framework.Logf("Unexpected response:\n%s", body)
  193. }
  194. }
  195. if !passed {
  196. if body, err = getDetails(); err != nil {
  197. framework.Failf("Timed out. Cleaning up. Error reading details: %v", err)
  198. } else {
  199. framework.Failf("Timed out. Cleaning up. Details:\n%s", string(body))
  200. }
  201. }
  202. Expect(string(body)).To(Equal("pass"))
  203. })
  204. framework.KubeDescribe("Granular Checks", func() {
  205. connectivityTimeout := 10
  206. It("should function for pod communication on a single node", func() {
  207. By("Picking a node")
  208. nodes := framework.GetReadySchedulableNodesOrDie(f.Client)
  209. node := nodes.Items[0]
  210. By("Creating a webserver pod")
  211. podName := "same-node-webserver"
  212. defer f.Client.Pods(f.Namespace.Name).Delete(podName, nil)
  213. ip := framework.LaunchWebserverPod(f, podName, node.Name)
  214. By("Checking that the webserver is accessible from a pod on the same node")
  215. framework.ExpectNoError(framework.CheckConnectivityToHost(f, node.Name, "same-node-wget", ip, connectivityTimeout))
  216. })
  217. It("should function for pod communication between nodes", func() {
  218. podClient := f.Client.Pods(f.Namespace.Name)
  219. By("Picking multiple nodes")
  220. nodes := framework.GetReadySchedulableNodesOrDie(f.Client)
  221. if len(nodes.Items) == 1 {
  222. framework.Skipf("The test requires two Ready nodes on %s, but found just one.", framework.TestContext.Provider)
  223. }
  224. node1 := nodes.Items[0]
  225. node2 := nodes.Items[1]
  226. By("Creating a webserver pod")
  227. podName := "different-node-webserver"
  228. defer podClient.Delete(podName, nil)
  229. ip := framework.LaunchWebserverPod(f, podName, node1.Name)
  230. By("Checking that the webserver is accessible from a pod on a different node")
  231. framework.ExpectNoError(framework.CheckConnectivityToHost(f, node2.Name, "different-node-wget", ip, connectivityTimeout))
  232. })
  233. })
  234. })
  235. func LaunchNetTestPodPerNode(f *framework.Framework, nodes *api.NodeList, name string) []string {
  236. podNames := []string{}
  237. totalPods := len(nodes.Items)
  238. Expect(totalPods).NotTo(Equal(0))
  239. for _, node := range nodes.Items {
  240. pod, err := f.Client.Pods(f.Namespace.Name).Create(&api.Pod{
  241. ObjectMeta: api.ObjectMeta{
  242. GenerateName: name + "-",
  243. Labels: map[string]string{
  244. "name": name,
  245. },
  246. },
  247. Spec: api.PodSpec{
  248. Containers: []api.Container{
  249. {
  250. Name: "webserver",
  251. Image: "gcr.io/google_containers/nettest:1.9",
  252. Args: []string{
  253. "-service=" + name,
  254. //peers >= totalPods should be asserted by the container.
  255. //the nettest container finds peers by looking up list of svc endpoints.
  256. fmt.Sprintf("-peers=%d", totalPods),
  257. "-namespace=" + f.Namespace.Name},
  258. Ports: []api.ContainerPort{{ContainerPort: 8080}},
  259. },
  260. },
  261. NodeName: node.Name,
  262. RestartPolicy: api.RestartPolicyNever,
  263. },
  264. })
  265. Expect(err).NotTo(HaveOccurred())
  266. framework.Logf("Created pod %s on node %s", pod.ObjectMeta.Name, node.Name)
  267. podNames = append(podNames, pod.ObjectMeta.Name)
  268. }
  269. return podNames
  270. }