pods.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. Copyright 2016 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 framework
  14. import (
  15. "fmt"
  16. "regexp"
  17. "sync"
  18. "time"
  19. v1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/labels"
  23. "k8s.io/apimachinery/pkg/util/sets"
  24. "k8s.io/apimachinery/pkg/util/wait"
  25. "k8s.io/client-go/kubernetes/scheme"
  26. v1core "k8s.io/client-go/kubernetes/typed/core/v1"
  27. podutil "k8s.io/kubernetes/pkg/api/v1/pod"
  28. "k8s.io/kubernetes/pkg/kubelet/events"
  29. "k8s.io/kubernetes/pkg/kubelet/sysctl"
  30. e2elog "k8s.io/kubernetes/test/e2e/framework/log"
  31. "github.com/onsi/ginkgo"
  32. "github.com/onsi/gomega"
  33. )
  34. // DefaultPodDeletionTimeout is the default timeout for deleting pod
  35. const DefaultPodDeletionTimeout = 3 * time.Minute
  36. // ImageWhiteList is the images used in the current test suite. It should be initialized in test suite and
  37. // the images in the white list should be pre-pulled in the test suite. Currently, this is only used by
  38. // node e2e test.
  39. var ImageWhiteList sets.String
  40. // PodClient is a convenience method for getting a pod client interface in the framework's namespace,
  41. // possibly applying test-suite specific transformations to the pod spec, e.g. for
  42. // node e2e pod scheduling.
  43. func (f *Framework) PodClient() *PodClient {
  44. return &PodClient{
  45. f: f,
  46. PodInterface: f.ClientSet.CoreV1().Pods(f.Namespace.Name),
  47. }
  48. }
  49. // PodClientNS is a convenience method for getting a pod client interface in an alternative namespace,
  50. // possibly applying test-suite specific transformations to the pod spec, e.g. for
  51. // node e2e pod scheduling.
  52. func (f *Framework) PodClientNS(namespace string) *PodClient {
  53. return &PodClient{
  54. f: f,
  55. PodInterface: f.ClientSet.CoreV1().Pods(namespace),
  56. }
  57. }
  58. // PodClient is a struct for pod client.
  59. type PodClient struct {
  60. f *Framework
  61. v1core.PodInterface
  62. }
  63. // Create creates a new pod according to the framework specifications (don't wait for it to start).
  64. func (c *PodClient) Create(pod *v1.Pod) *v1.Pod {
  65. c.mungeSpec(pod)
  66. p, err := c.PodInterface.Create(pod)
  67. ExpectNoError(err, "Error creating Pod")
  68. return p
  69. }
  70. // CreateEventually retries pod creation for a while before failing
  71. // the test with the most recent error. This mimicks the behavior
  72. // of a controller (like the one for DaemonSet) and is necessary
  73. // because pod creation can fail while its service account is still
  74. // getting provisioned
  75. // (https://github.com/kubernetes/kubernetes/issues/68776).
  76. //
  77. // Both the timeout and polling interval are configurable as optional
  78. // arguments:
  79. // - The first optional argument is the timeout.
  80. // - The second optional argument is the polling interval.
  81. //
  82. // Both intervals can either be specified as time.Duration, parsable
  83. // duration strings or as floats/integers. In the last case they are
  84. // interpreted as seconds.
  85. func (c *PodClient) CreateEventually(pod *v1.Pod, opts ...interface{}) *v1.Pod {
  86. c.mungeSpec(pod)
  87. var ret *v1.Pod
  88. gomega.Eventually(func() error {
  89. p, err := c.PodInterface.Create(pod)
  90. ret = p
  91. return err
  92. }, opts...).ShouldNot(gomega.HaveOccurred(), "Failed to create %q pod", pod.GetName())
  93. return ret
  94. }
  95. // CreateSyncInNamespace creates a new pod according to the framework specifications in the given namespace, and waits for it to start.
  96. func (c *PodClient) CreateSyncInNamespace(pod *v1.Pod, namespace string) *v1.Pod {
  97. p := c.Create(pod)
  98. ExpectNoError(WaitForPodNameRunningInNamespace(c.f.ClientSet, p.Name, namespace))
  99. // Get the newest pod after it becomes running, some status may change after pod created, such as pod ip.
  100. p, err := c.Get(p.Name, metav1.GetOptions{})
  101. ExpectNoError(err)
  102. return p
  103. }
  104. // CreateSync creates a new pod according to the framework specifications, and wait for it to start.
  105. func (c *PodClient) CreateSync(pod *v1.Pod) *v1.Pod {
  106. return c.CreateSyncInNamespace(pod, c.f.Namespace.Name)
  107. }
  108. // CreateBatch create a batch of pods. All pods are created before waiting.
  109. func (c *PodClient) CreateBatch(pods []*v1.Pod) []*v1.Pod {
  110. ps := make([]*v1.Pod, len(pods))
  111. var wg sync.WaitGroup
  112. for i, pod := range pods {
  113. wg.Add(1)
  114. go func(i int, pod *v1.Pod) {
  115. defer wg.Done()
  116. defer ginkgo.GinkgoRecover()
  117. ps[i] = c.CreateSync(pod)
  118. }(i, pod)
  119. }
  120. wg.Wait()
  121. return ps
  122. }
  123. // Update updates the pod object. It retries if there is a conflict, throw out error if
  124. // there is any other errors. name is the pod name, updateFn is the function updating the
  125. // pod object.
  126. func (c *PodClient) Update(name string, updateFn func(pod *v1.Pod)) {
  127. ExpectNoError(wait.Poll(time.Millisecond*500, time.Second*30, func() (bool, error) {
  128. pod, err := c.PodInterface.Get(name, metav1.GetOptions{})
  129. if err != nil {
  130. return false, fmt.Errorf("failed to get pod %q: %v", name, err)
  131. }
  132. updateFn(pod)
  133. _, err = c.PodInterface.Update(pod)
  134. if err == nil {
  135. e2elog.Logf("Successfully updated pod %q", name)
  136. return true, nil
  137. }
  138. if errors.IsConflict(err) {
  139. e2elog.Logf("Conflicting update to pod %q, re-get and re-update: %v", name, err)
  140. return false, nil
  141. }
  142. return false, fmt.Errorf("failed to update pod %q: %v", name, err)
  143. }))
  144. }
  145. // DeleteSync deletes the pod and wait for the pod to disappear for `timeout`. If the pod doesn't
  146. // disappear before the timeout, it will fail the test.
  147. func (c *PodClient) DeleteSync(name string, options *metav1.DeleteOptions, timeout time.Duration) {
  148. c.DeleteSyncInNamespace(name, c.f.Namespace.Name, options, timeout)
  149. }
  150. // DeleteSyncInNamespace deletes the pod from the namespace and wait for the pod to disappear for `timeout`. If the pod doesn't
  151. // disappear before the timeout, it will fail the test.
  152. func (c *PodClient) DeleteSyncInNamespace(name string, namespace string, options *metav1.DeleteOptions, timeout time.Duration) {
  153. err := c.Delete(name, options)
  154. if err != nil && !errors.IsNotFound(err) {
  155. Failf("Failed to delete pod %q: %v", name, err)
  156. }
  157. gomega.Expect(WaitForPodToDisappear(c.f.ClientSet, namespace, name, labels.Everything(),
  158. 2*time.Second, timeout)).To(gomega.Succeed(), "wait for pod %q to disappear", name)
  159. }
  160. // mungeSpec apply test-suite specific transformations to the pod spec.
  161. func (c *PodClient) mungeSpec(pod *v1.Pod) {
  162. if !TestContext.NodeE2E {
  163. return
  164. }
  165. gomega.Expect(pod.Spec.NodeName).To(gomega.Or(gomega.BeZero(), gomega.Equal(TestContext.NodeName)), "Test misconfigured")
  166. pod.Spec.NodeName = TestContext.NodeName
  167. // Node e2e does not support the default DNSClusterFirst policy. Set
  168. // the policy to DNSDefault, which is configured per node.
  169. pod.Spec.DNSPolicy = v1.DNSDefault
  170. // PrepullImages only works for node e2e now. For cluster e2e, image prepull is not enforced,
  171. // we should not munge ImagePullPolicy for cluster e2e pods.
  172. if !TestContext.PrepullImages {
  173. return
  174. }
  175. // If prepull is enabled, munge the container spec to make sure the images are not pulled
  176. // during the test.
  177. for i := range pod.Spec.Containers {
  178. c := &pod.Spec.Containers[i]
  179. if c.ImagePullPolicy == v1.PullAlways {
  180. // If the image pull policy is PullAlways, the image doesn't need to be in
  181. // the white list or pre-pulled, because the image is expected to be pulled
  182. // in the test anyway.
  183. continue
  184. }
  185. // If the image policy is not PullAlways, the image must be in the white list and
  186. // pre-pulled.
  187. gomega.Expect(ImageWhiteList.Has(c.Image)).To(gomega.BeTrue(), "Image %q is not in the white list, consider adding it to CommonImageWhiteList in test/e2e/common/util.go or NodeImageWhiteList in test/e2e_node/image_list.go", c.Image)
  188. // Do not pull images during the tests because the images in white list should have
  189. // been prepulled.
  190. c.ImagePullPolicy = v1.PullNever
  191. }
  192. }
  193. // WaitForSuccess waits for pod to succeed.
  194. // TODO(random-liu): Move pod wait function into this file
  195. func (c *PodClient) WaitForSuccess(name string, timeout time.Duration) {
  196. f := c.f
  197. gomega.Expect(WaitForPodCondition(f.ClientSet, f.Namespace.Name, name, "success or failure", timeout,
  198. func(pod *v1.Pod) (bool, error) {
  199. switch pod.Status.Phase {
  200. case v1.PodFailed:
  201. return true, fmt.Errorf("pod %q failed with reason: %q, message: %q", name, pod.Status.Reason, pod.Status.Message)
  202. case v1.PodSucceeded:
  203. return true, nil
  204. default:
  205. return false, nil
  206. }
  207. },
  208. )).To(gomega.Succeed(), "wait for pod %q to success", name)
  209. }
  210. // WaitForFailure waits for pod to fail.
  211. func (c *PodClient) WaitForFailure(name string, timeout time.Duration) {
  212. f := c.f
  213. gomega.Expect(WaitForPodCondition(f.ClientSet, f.Namespace.Name, name, "success or failure", timeout,
  214. func(pod *v1.Pod) (bool, error) {
  215. switch pod.Status.Phase {
  216. case v1.PodFailed:
  217. return true, nil
  218. case v1.PodSucceeded:
  219. return true, fmt.Errorf("pod %q successed with reason: %q, message: %q", name, pod.Status.Reason, pod.Status.Message)
  220. default:
  221. return false, nil
  222. }
  223. },
  224. )).To(gomega.Succeed(), "wait for pod %q to fail", name)
  225. }
  226. // WaitForFinish waits for pod to finish running, regardless of success or failure.
  227. func (c *PodClient) WaitForFinish(name string, timeout time.Duration) {
  228. f := c.f
  229. gomega.Expect(WaitForPodCondition(f.ClientSet, f.Namespace.Name, name, "success or failure", timeout,
  230. func(pod *v1.Pod) (bool, error) {
  231. switch pod.Status.Phase {
  232. case v1.PodFailed:
  233. return true, nil
  234. case v1.PodSucceeded:
  235. return true, nil
  236. default:
  237. return false, nil
  238. }
  239. },
  240. )).To(gomega.Succeed(), "wait for pod %q to finish running", name)
  241. }
  242. // WaitForErrorEventOrSuccess waits for pod to succeed or an error event for that pod.
  243. func (c *PodClient) WaitForErrorEventOrSuccess(pod *v1.Pod) (*v1.Event, error) {
  244. var ev *v1.Event
  245. err := wait.Poll(Poll, PodStartTimeout, func() (bool, error) {
  246. evnts, err := c.f.ClientSet.CoreV1().Events(pod.Namespace).Search(scheme.Scheme, pod)
  247. if err != nil {
  248. return false, fmt.Errorf("error in listing events: %s", err)
  249. }
  250. for _, e := range evnts.Items {
  251. switch e.Reason {
  252. case events.KillingContainer, events.FailedToCreateContainer, sysctl.UnsupportedReason, sysctl.ForbiddenReason:
  253. ev = &e
  254. return true, nil
  255. case events.StartedContainer:
  256. return true, nil
  257. default:
  258. // ignore all other errors
  259. }
  260. }
  261. return false, nil
  262. })
  263. return ev, err
  264. }
  265. // MatchContainerOutput gets output of a container and match expected regexp in the output.
  266. func (c *PodClient) MatchContainerOutput(name string, containerName string, expectedRegexp string) error {
  267. f := c.f
  268. output, err := GetPodLogs(f.ClientSet, f.Namespace.Name, name, containerName)
  269. if err != nil {
  270. return fmt.Errorf("failed to get output for container %q of pod %q", containerName, name)
  271. }
  272. regex, err := regexp.Compile(expectedRegexp)
  273. if err != nil {
  274. return fmt.Errorf("failed to compile regexp %q: %v", expectedRegexp, err)
  275. }
  276. if !regex.MatchString(output) {
  277. return fmt.Errorf("failed to match regexp %q in output %q", expectedRegexp, output)
  278. }
  279. return nil
  280. }
  281. // PodIsReady returns true if the specified pod is ready. Otherwise false.
  282. func (c *PodClient) PodIsReady(name string) bool {
  283. pod, err := c.Get(name, metav1.GetOptions{})
  284. ExpectNoError(err)
  285. return podutil.IsPodReady(pod)
  286. }