e2e_node_suite_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // +build linux
  2. /*
  3. Copyright 2016 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. // To run tests in this suite
  15. // NOTE: This test suite requires password-less sudo capabilities to run the kubelet and kube-apiserver.
  16. package e2e_node
  17. import (
  18. "bytes"
  19. "encoding/json"
  20. "flag"
  21. "fmt"
  22. "io/ioutil"
  23. "math/rand"
  24. "os"
  25. "os/exec"
  26. "path"
  27. "syscall"
  28. "testing"
  29. "time"
  30. "k8s.io/api/core/v1"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. utilyaml "k8s.io/apimachinery/pkg/util/yaml"
  33. clientset "k8s.io/client-go/kubernetes"
  34. "k8s.io/kubernetes/cmd/kubeadm/app/util/system"
  35. commontest "k8s.io/kubernetes/test/e2e/common"
  36. "k8s.io/kubernetes/test/e2e/framework"
  37. "k8s.io/kubernetes/test/e2e_node/services"
  38. "github.com/kardianos/osext"
  39. . "github.com/onsi/ginkgo"
  40. "github.com/onsi/ginkgo/config"
  41. morereporters "github.com/onsi/ginkgo/reporters"
  42. . "github.com/onsi/gomega"
  43. "github.com/spf13/pflag"
  44. "k8s.io/klog"
  45. )
  46. var e2es *services.E2EServices
  47. // TODO(random-liu): Change the following modes to sub-command.
  48. var runServicesMode = flag.Bool("run-services-mode", false, "If true, only run services (etcd, apiserver) in current process, and not run test.")
  49. var runKubeletMode = flag.Bool("run-kubelet-mode", false, "If true, only start kubelet, and not run test.")
  50. var systemValidateMode = flag.Bool("system-validate-mode", false, "If true, only run system validation in current process, and not run test.")
  51. var systemSpecFile = flag.String("system-spec-file", "", "The name of the system spec file that will be used for node conformance test. If it's unspecified or empty, the default system spec (system.DefaultSysSpec) will be used.")
  52. func init() {
  53. framework.RegisterCommonFlags()
  54. framework.RegisterNodeFlags()
  55. pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
  56. // Mark the run-services-mode flag as hidden to prevent user from using it.
  57. pflag.CommandLine.MarkHidden("run-services-mode")
  58. // It's weird that if I directly use pflag in TestContext, it will report error.
  59. // It seems that someone is using flag.Parse() after init() and TestMain().
  60. // TODO(random-liu): Find who is using flag.Parse() and cause errors and move the following logic
  61. // into TestContext.
  62. // TODO(pohly): remove RegisterNodeFlags from test_context.go enable Viper config support here?
  63. }
  64. func TestMain(m *testing.M) {
  65. rand.Seed(time.Now().UnixNano())
  66. pflag.Parse()
  67. framework.AfterReadingAllFlags(&framework.TestContext)
  68. setExtraEnvs()
  69. os.Exit(m.Run())
  70. }
  71. // When running the containerized conformance test, we'll mount the
  72. // host root filesystem as readonly to /rootfs.
  73. const rootfs = "/rootfs"
  74. func TestE2eNode(t *testing.T) {
  75. if *runServicesMode {
  76. // If run-services-mode is specified, only run services in current process.
  77. services.RunE2EServices(t)
  78. return
  79. }
  80. if *runKubeletMode {
  81. // If run-kubelet-mode is specified, only start kubelet.
  82. services.RunKubelet()
  83. return
  84. }
  85. if *systemValidateMode {
  86. // If system-validate-mode is specified, only run system validation in current process.
  87. spec := &system.DefaultSysSpec
  88. if *systemSpecFile != "" {
  89. var err error
  90. spec, err = loadSystemSpecFromFile(*systemSpecFile)
  91. if err != nil {
  92. klog.Exitf("Failed to load system spec: %v", err)
  93. }
  94. }
  95. if framework.TestContext.NodeConformance {
  96. // Chroot to /rootfs to make system validation can check system
  97. // as in the root filesystem.
  98. // TODO(random-liu): Consider to chroot the whole test process to make writing
  99. // test easier.
  100. if err := syscall.Chroot(rootfs); err != nil {
  101. klog.Exitf("chroot %q failed: %v", rootfs, err)
  102. }
  103. }
  104. if _, err := system.ValidateSpec(*spec, framework.TestContext.ContainerRuntime); err != nil {
  105. klog.Exitf("system validation failed: %v", err)
  106. }
  107. return
  108. }
  109. // If run-services-mode is not specified, run test.
  110. RegisterFailHandler(Fail)
  111. reporters := []Reporter{}
  112. reportDir := framework.TestContext.ReportDir
  113. if reportDir != "" {
  114. // Create the directory if it doesn't already exists
  115. if err := os.MkdirAll(reportDir, 0755); err != nil {
  116. klog.Errorf("Failed creating report directory: %v", err)
  117. } else {
  118. // Configure a junit reporter to write to the directory
  119. junitFile := fmt.Sprintf("junit_%s_%02d.xml", framework.TestContext.ReportPrefix, config.GinkgoConfig.ParallelNode)
  120. junitPath := path.Join(reportDir, junitFile)
  121. reporters = append(reporters, morereporters.NewJUnitReporter(junitPath))
  122. }
  123. }
  124. RunSpecsWithDefaultAndCustomReporters(t, "E2eNode Suite", reporters)
  125. }
  126. // Setup the kubelet on the node
  127. var _ = SynchronizedBeforeSuite(func() []byte {
  128. // Run system validation test.
  129. Expect(validateSystem()).To(Succeed(), "system validation")
  130. // Pre-pull the images tests depend on so we can fail immediately if there is an image pull issue
  131. // This helps with debugging test flakes since it is hard to tell when a test failure is due to image pulling.
  132. if framework.TestContext.PrepullImages {
  133. klog.Infof("Pre-pulling images so that they are cached for the tests.")
  134. updateImageWhiteList()
  135. err := PrePullAllImages()
  136. Expect(err).ShouldNot(HaveOccurred())
  137. }
  138. // TODO(yifan): Temporary workaround to disable coreos from auto restart
  139. // by masking the locksmithd.
  140. // We should mask locksmithd when provisioning the machine.
  141. maskLocksmithdOnCoreos()
  142. if *startServices {
  143. // If the services are expected to stop after test, they should monitor the test process.
  144. // If the services are expected to keep running after test, they should not monitor the test process.
  145. e2es = services.NewE2EServices(*stopServices)
  146. Expect(e2es.Start()).To(Succeed(), "should be able to start node services.")
  147. klog.Infof("Node services started. Running tests...")
  148. } else {
  149. klog.Infof("Running tests without starting services.")
  150. }
  151. klog.Infof("Wait for the node to be ready")
  152. waitForNodeReady()
  153. // Reference common test to make the import valid.
  154. commontest.CurrentSuite = commontest.NodeE2E
  155. return nil
  156. }, func([]byte) {
  157. // update test context with node configuration.
  158. Expect(updateTestContext()).To(Succeed(), "update test context with node config.")
  159. })
  160. // Tear down the kubelet on the node
  161. var _ = SynchronizedAfterSuite(func() {}, func() {
  162. if e2es != nil {
  163. if *startServices && *stopServices {
  164. klog.Infof("Stopping node services...")
  165. e2es.Stop()
  166. }
  167. }
  168. klog.Infof("Tests Finished")
  169. })
  170. // validateSystem runs system validation in a separate process and returns error if validation fails.
  171. func validateSystem() error {
  172. testBin, err := osext.Executable()
  173. if err != nil {
  174. return fmt.Errorf("can't get current binary: %v", err)
  175. }
  176. // Pass all flags into the child process, so that it will see the same flag set.
  177. output, err := exec.Command(testBin, append([]string{"--system-validate-mode"}, os.Args[1:]...)...).CombinedOutput()
  178. // The output of system validation should have been formatted, directly print here.
  179. fmt.Print(string(output))
  180. if err != nil {
  181. return fmt.Errorf("system validation failed: %v", err)
  182. }
  183. return nil
  184. }
  185. func maskLocksmithdOnCoreos() {
  186. data, err := ioutil.ReadFile("/etc/os-release")
  187. if err != nil {
  188. // Not all distros contain this file.
  189. klog.Infof("Could not read /etc/os-release: %v", err)
  190. return
  191. }
  192. if bytes.Contains(data, []byte("ID=coreos")) {
  193. output, err := exec.Command("systemctl", "mask", "--now", "locksmithd").CombinedOutput()
  194. Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("should be able to mask locksmithd - output: %q", string(output)))
  195. klog.Infof("Locksmithd is masked successfully")
  196. }
  197. }
  198. func waitForNodeReady() {
  199. const (
  200. // nodeReadyTimeout is the time to wait for node to become ready.
  201. nodeReadyTimeout = 2 * time.Minute
  202. // nodeReadyPollInterval is the interval to check node ready.
  203. nodeReadyPollInterval = 1 * time.Second
  204. )
  205. client, err := getAPIServerClient()
  206. Expect(err).NotTo(HaveOccurred(), "should be able to get apiserver client.")
  207. Eventually(func() error {
  208. node, err := getNode(client)
  209. if err != nil {
  210. return fmt.Errorf("failed to get node: %v", err)
  211. }
  212. if !isNodeReady(node) {
  213. return fmt.Errorf("node is not ready: %+v", node)
  214. }
  215. return nil
  216. }, nodeReadyTimeout, nodeReadyPollInterval).Should(Succeed())
  217. }
  218. // updateTestContext updates the test context with the node name.
  219. // TODO(random-liu): Using dynamic kubelet configuration feature to
  220. // update test context with node configuration.
  221. func updateTestContext() error {
  222. setExtraEnvs()
  223. updateImageWhiteList()
  224. client, err := getAPIServerClient()
  225. if err != nil {
  226. return fmt.Errorf("failed to get apiserver client: %v", err)
  227. }
  228. // Update test context with current node object.
  229. node, err := getNode(client)
  230. if err != nil {
  231. return fmt.Errorf("failed to get node: %v", err)
  232. }
  233. framework.TestContext.NodeName = node.Name // Set node name.
  234. // Update test context with current kubelet configuration.
  235. // This assumes all tests which dynamically change kubelet configuration
  236. // must: 1) run in serial; 2) restore kubelet configuration after test.
  237. kubeletCfg, err := getCurrentKubeletConfig()
  238. if err != nil {
  239. return fmt.Errorf("failed to get kubelet configuration: %v", err)
  240. }
  241. framework.TestContext.KubeletConfig = *kubeletCfg // Set kubelet config
  242. return nil
  243. }
  244. // getNode gets node object from the apiserver.
  245. func getNode(c *clientset.Clientset) (*v1.Node, error) {
  246. nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
  247. Expect(err).NotTo(HaveOccurred(), "should be able to list nodes.")
  248. if nodes == nil {
  249. return nil, fmt.Errorf("the node list is nil.")
  250. }
  251. Expect(len(nodes.Items) > 1).NotTo(BeTrue(), "the number of nodes is more than 1.")
  252. if len(nodes.Items) == 0 {
  253. return nil, fmt.Errorf("empty node list: %+v", nodes)
  254. }
  255. return &nodes.Items[0], nil
  256. }
  257. // getAPIServerClient gets a apiserver client.
  258. func getAPIServerClient() (*clientset.Clientset, error) {
  259. config, err := framework.LoadConfig()
  260. if err != nil {
  261. return nil, fmt.Errorf("failed to load config: %v", err)
  262. }
  263. client, err := clientset.NewForConfig(config)
  264. if err != nil {
  265. return nil, fmt.Errorf("failed to create client: %v", err)
  266. }
  267. return client, nil
  268. }
  269. // loadSystemSpecFromFile returns the system spec from the file with the
  270. // filename.
  271. func loadSystemSpecFromFile(filename string) (*system.SysSpec, error) {
  272. b, err := ioutil.ReadFile(filename)
  273. if err != nil {
  274. return nil, err
  275. }
  276. data, err := utilyaml.ToJSON(b)
  277. if err != nil {
  278. return nil, err
  279. }
  280. spec := new(system.SysSpec)
  281. if err := json.Unmarshal(data, spec); err != nil {
  282. return nil, err
  283. }
  284. return spec, nil
  285. }
  286. // isNodeReady returns true if a node is ready; false otherwise.
  287. func isNodeReady(node *v1.Node) bool {
  288. for _, c := range node.Status.Conditions {
  289. if c.Type == v1.NodeReady {
  290. return c.Status == v1.ConditionTrue
  291. }
  292. }
  293. return false
  294. }
  295. func setExtraEnvs() {
  296. for name, value := range framework.TestContext.ExtraEnvs {
  297. os.Setenv(name, value)
  298. }
  299. }