framework.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  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 framework contains provider-independent helper code for
  14. // building and running E2E tests with Ginkgo. The actual Ginkgo test
  15. // suites gets assembled by combining this framework, the optional
  16. // provider support code and specific tests via a separate .go file
  17. // like Kubernetes' test/e2e.go.
  18. package framework
  19. import (
  20. "context"
  21. "fmt"
  22. "io/ioutil"
  23. "math/rand"
  24. "path"
  25. "strings"
  26. "sync"
  27. "time"
  28. "k8s.io/apimachinery/pkg/runtime"
  29. v1 "k8s.io/api/core/v1"
  30. apierrors "k8s.io/apimachinery/pkg/api/errors"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apimachinery/pkg/labels"
  33. "k8s.io/apimachinery/pkg/runtime/schema"
  34. "k8s.io/apimachinery/pkg/util/intstr"
  35. "k8s.io/apimachinery/pkg/util/wait"
  36. "k8s.io/client-go/discovery"
  37. cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
  38. "k8s.io/client-go/dynamic"
  39. clientset "k8s.io/client-go/kubernetes"
  40. "k8s.io/client-go/kubernetes/scheme"
  41. "k8s.io/client-go/rest"
  42. "k8s.io/client-go/restmapper"
  43. scaleclient "k8s.io/client-go/scale"
  44. testutils "k8s.io/kubernetes/test/utils"
  45. "github.com/onsi/ginkgo"
  46. "github.com/onsi/gomega"
  47. // TODO: Remove the following imports (ref: https://github.com/kubernetes/kubernetes/issues/81245)
  48. e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
  49. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  50. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  51. )
  52. const (
  53. // DefaultNamespaceDeletionTimeout is timeout duration for waiting for a namespace deletion.
  54. DefaultNamespaceDeletionTimeout = 5 * time.Minute
  55. )
  56. // Framework supports common operations used by e2e tests; it will keep a client & a namespace for you.
  57. // Eventual goal is to merge this with integration test framework.
  58. type Framework struct {
  59. BaseName string
  60. // Set together with creating the ClientSet and the namespace.
  61. // Guaranteed to be unique in the cluster even when running the same
  62. // test multiple times in parallel.
  63. UniqueName string
  64. clientConfig *rest.Config
  65. ClientSet clientset.Interface
  66. KubemarkExternalClusterClientSet clientset.Interface
  67. DynamicClient dynamic.Interface
  68. ScalesGetter scaleclient.ScalesGetter
  69. SkipNamespaceCreation bool // Whether to skip creating a namespace
  70. Namespace *v1.Namespace // Every test has at least one namespace unless creation is skipped
  71. namespacesToDelete []*v1.Namespace // Some tests have more than one.
  72. NamespaceDeletionTimeout time.Duration
  73. SkipPrivilegedPSPBinding bool // Whether to skip creating a binding to the privileged PSP in the test namespace
  74. gatherer *ContainerResourceGatherer
  75. // Constraints that passed to a check which is executed after data is gathered to
  76. // see if 99% of results are within acceptable bounds. It has to be injected in the test,
  77. // as expectations vary greatly. Constraints are grouped by the container names.
  78. AddonResourceConstraints map[string]ResourceConstraint
  79. logsSizeWaitGroup sync.WaitGroup
  80. logsSizeCloseChannel chan bool
  81. logsSizeVerifier *LogsSizeVerifier
  82. // Flaky operation failures in an e2e test can be captured through this.
  83. flakeReport *FlakeReport
  84. // To make sure that this framework cleans up after itself, no matter what,
  85. // we install a Cleanup action before each test and clear it after. If we
  86. // should abort, the AfterSuite hook should run all Cleanup actions.
  87. cleanupHandle CleanupActionHandle
  88. // afterEaches is a map of name to function to be called after each test. These are not
  89. // cleared. The call order is randomized so that no dependencies can grow between
  90. // the various afterEaches
  91. afterEaches map[string]AfterEachActionFunc
  92. // beforeEachStarted indicates that BeforeEach has started
  93. beforeEachStarted bool
  94. // configuration for framework's client
  95. Options Options
  96. // Place where various additional data is stored during test run to be printed to ReportDir,
  97. // or stdout if ReportDir is not set once test ends.
  98. TestSummaries []TestDataSummary
  99. // Place to keep ClusterAutoscaler metrics from before test in order to compute delta.
  100. clusterAutoscalerMetricsBeforeTest e2emetrics.Collection
  101. }
  102. // AfterEachActionFunc is a function that can be called after each test
  103. type AfterEachActionFunc func(f *Framework, failed bool)
  104. // TestDataSummary is an interface for managing test data.
  105. type TestDataSummary interface {
  106. SummaryKind() string
  107. PrintHumanReadable() string
  108. PrintJSON() string
  109. }
  110. // Options is a struct for managing test framework options.
  111. type Options struct {
  112. ClientQPS float32
  113. ClientBurst int
  114. GroupVersion *schema.GroupVersion
  115. }
  116. // NewDefaultFramework makes a new framework and sets up a BeforeEach/AfterEach for
  117. // you (you can write additional before/after each functions).
  118. func NewDefaultFramework(baseName string) *Framework {
  119. options := Options{
  120. ClientQPS: 20,
  121. ClientBurst: 50,
  122. }
  123. return NewFramework(baseName, options, nil)
  124. }
  125. // NewFramework creates a test framework.
  126. func NewFramework(baseName string, options Options, client clientset.Interface) *Framework {
  127. f := &Framework{
  128. BaseName: baseName,
  129. AddonResourceConstraints: make(map[string]ResourceConstraint),
  130. Options: options,
  131. ClientSet: client,
  132. }
  133. f.AddAfterEach("dumpNamespaceInfo", func(f *Framework, failed bool) {
  134. if !failed {
  135. return
  136. }
  137. if !TestContext.DumpLogsOnFailure {
  138. return
  139. }
  140. if !f.SkipNamespaceCreation {
  141. for _, ns := range f.namespacesToDelete {
  142. DumpAllNamespaceInfo(f.ClientSet, ns.Name)
  143. }
  144. }
  145. })
  146. ginkgo.BeforeEach(f.BeforeEach)
  147. ginkgo.AfterEach(f.AfterEach)
  148. return f
  149. }
  150. // BeforeEach gets a client and makes a namespace.
  151. func (f *Framework) BeforeEach() {
  152. f.beforeEachStarted = true
  153. // The fact that we need this feels like a bug in ginkgo.
  154. // https://github.com/onsi/ginkgo/issues/222
  155. f.cleanupHandle = AddCleanupAction(f.AfterEach)
  156. if f.ClientSet == nil {
  157. ginkgo.By("Creating a kubernetes client")
  158. config, err := LoadConfig()
  159. ExpectNoError(err)
  160. config.QPS = f.Options.ClientQPS
  161. config.Burst = f.Options.ClientBurst
  162. if f.Options.GroupVersion != nil {
  163. config.GroupVersion = f.Options.GroupVersion
  164. }
  165. if TestContext.KubeAPIContentType != "" {
  166. config.ContentType = TestContext.KubeAPIContentType
  167. }
  168. f.clientConfig = rest.CopyConfig(config)
  169. f.ClientSet, err = clientset.NewForConfig(config)
  170. ExpectNoError(err)
  171. f.DynamicClient, err = dynamic.NewForConfig(config)
  172. ExpectNoError(err)
  173. // node.k8s.io is based on CRD, which is served only as JSON
  174. jsonConfig := config
  175. jsonConfig.ContentType = "application/json"
  176. ExpectNoError(err)
  177. // create scales getter, set GroupVersion and NegotiatedSerializer to default values
  178. // as they are required when creating a REST client.
  179. if config.GroupVersion == nil {
  180. config.GroupVersion = &schema.GroupVersion{}
  181. }
  182. if config.NegotiatedSerializer == nil {
  183. config.NegotiatedSerializer = scheme.Codecs
  184. }
  185. restClient, err := rest.RESTClientFor(config)
  186. ExpectNoError(err)
  187. discoClient, err := discovery.NewDiscoveryClientForConfig(config)
  188. ExpectNoError(err)
  189. cachedDiscoClient := cacheddiscovery.NewMemCacheClient(discoClient)
  190. restMapper := restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoClient)
  191. restMapper.Reset()
  192. resolver := scaleclient.NewDiscoveryScaleKindResolver(cachedDiscoClient)
  193. f.ScalesGetter = scaleclient.New(restClient, restMapper, dynamic.LegacyAPIPathResolverFunc, resolver)
  194. TestContext.CloudConfig.Provider.FrameworkBeforeEach(f)
  195. }
  196. if !f.SkipNamespaceCreation {
  197. ginkgo.By(fmt.Sprintf("Building a namespace api object, basename %s", f.BaseName))
  198. namespace, err := f.CreateNamespace(f.BaseName, map[string]string{
  199. "e2e-framework": f.BaseName,
  200. })
  201. ExpectNoError(err)
  202. f.Namespace = namespace
  203. if TestContext.VerifyServiceAccount {
  204. ginkgo.By("Waiting for a default service account to be provisioned in namespace")
  205. err = WaitForDefaultServiceAccountInNamespace(f.ClientSet, namespace.Name)
  206. ExpectNoError(err)
  207. } else {
  208. Logf("Skipping waiting for service account")
  209. }
  210. f.UniqueName = f.Namespace.GetName()
  211. } else {
  212. // not guaranteed to be unique, but very likely
  213. f.UniqueName = fmt.Sprintf("%s-%08x", f.BaseName, rand.Int31())
  214. }
  215. if TestContext.GatherKubeSystemResourceUsageData != "false" && TestContext.GatherKubeSystemResourceUsageData != "none" {
  216. var err error
  217. var nodeMode NodesSet
  218. switch TestContext.GatherKubeSystemResourceUsageData {
  219. case "master":
  220. nodeMode = MasterNodes
  221. case "masteranddns":
  222. nodeMode = MasterAndDNSNodes
  223. default:
  224. nodeMode = AllNodes
  225. }
  226. f.gatherer, err = NewResourceUsageGatherer(f.ClientSet, ResourceGathererOptions{
  227. InKubemark: ProviderIs("kubemark"),
  228. Nodes: nodeMode,
  229. ResourceDataGatheringPeriod: 60 * time.Second,
  230. ProbeDuration: 15 * time.Second,
  231. PrintVerboseLogs: false,
  232. }, nil)
  233. if err != nil {
  234. Logf("Error while creating NewResourceUsageGatherer: %v", err)
  235. } else {
  236. go f.gatherer.StartGatheringData()
  237. }
  238. }
  239. if TestContext.GatherLogsSizes {
  240. f.logsSizeWaitGroup = sync.WaitGroup{}
  241. f.logsSizeWaitGroup.Add(1)
  242. f.logsSizeCloseChannel = make(chan bool)
  243. f.logsSizeVerifier = NewLogsVerifier(f.ClientSet, f.logsSizeCloseChannel)
  244. go func() {
  245. f.logsSizeVerifier.Run()
  246. f.logsSizeWaitGroup.Done()
  247. }()
  248. }
  249. gatherMetricsAfterTest := TestContext.GatherMetricsAfterTest == "true" || TestContext.GatherMetricsAfterTest == "master"
  250. if gatherMetricsAfterTest && TestContext.IncludeClusterAutoscalerMetrics {
  251. grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, !ProviderIs("kubemark"), false, false, false, TestContext.IncludeClusterAutoscalerMetrics)
  252. if err != nil {
  253. Logf("Failed to create MetricsGrabber (skipping ClusterAutoscaler metrics gathering before test): %v", err)
  254. } else {
  255. f.clusterAutoscalerMetricsBeforeTest, err = grabber.Grab()
  256. if err != nil {
  257. Logf("MetricsGrabber failed to grab CA metrics before test (skipping metrics gathering): %v", err)
  258. } else {
  259. Logf("Gathered ClusterAutoscaler metrics before test")
  260. }
  261. }
  262. }
  263. f.flakeReport = NewFlakeReport()
  264. }
  265. // printSummaries prints summaries of tests.
  266. func printSummaries(summaries []TestDataSummary, testBaseName string) {
  267. now := time.Now()
  268. for i := range summaries {
  269. Logf("Printing summary: %v", summaries[i].SummaryKind())
  270. switch TestContext.OutputPrintType {
  271. case "hr":
  272. if TestContext.ReportDir == "" {
  273. Logf(summaries[i].PrintHumanReadable())
  274. } else {
  275. // TODO: learn to extract test name and append it to the kind instead of timestamp.
  276. filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".txt")
  277. if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintHumanReadable()), 0644); err != nil {
  278. Logf("Failed to write file %v with test performance data: %v", filePath, err)
  279. }
  280. }
  281. case "json":
  282. fallthrough
  283. default:
  284. if TestContext.OutputPrintType != "json" {
  285. Logf("Unknown output type: %v. Printing JSON", TestContext.OutputPrintType)
  286. }
  287. if TestContext.ReportDir == "" {
  288. Logf("%v JSON\n%v", summaries[i].SummaryKind(), summaries[i].PrintJSON())
  289. Logf("Finished")
  290. } else {
  291. // TODO: learn to extract test name and append it to the kind instead of timestamp.
  292. filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".json")
  293. Logf("Writing to %s", filePath)
  294. if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintJSON()), 0644); err != nil {
  295. Logf("Failed to write file %v with test performance data: %v", filePath, err)
  296. }
  297. }
  298. }
  299. }
  300. }
  301. // AddAfterEach is a way to add a function to be called after every test. The execution order is intentionally random
  302. // to avoid growing dependencies. If you register the same name twice, it is a coding error and will panic.
  303. func (f *Framework) AddAfterEach(name string, fn AfterEachActionFunc) {
  304. if _, ok := f.afterEaches[name]; ok {
  305. panic(fmt.Sprintf("%q is already registered", name))
  306. }
  307. if f.afterEaches == nil {
  308. f.afterEaches = map[string]AfterEachActionFunc{}
  309. }
  310. f.afterEaches[name] = fn
  311. }
  312. // AfterEach deletes the namespace, after reading its events.
  313. func (f *Framework) AfterEach() {
  314. // If BeforeEach never started AfterEach should be skipped.
  315. // Currently some tests under e2e/storage have this condition.
  316. if !f.beforeEachStarted {
  317. return
  318. }
  319. RemoveCleanupAction(f.cleanupHandle)
  320. // This should not happen. Given ClientSet is a public field a test must have updated it!
  321. // Error out early before any API calls during cleanup.
  322. if f.ClientSet == nil {
  323. Failf("The framework ClientSet must not be nil at this point")
  324. }
  325. // DeleteNamespace at the very end in defer, to avoid any
  326. // expectation failures preventing deleting the namespace.
  327. defer func() {
  328. nsDeletionErrors := map[string]error{}
  329. // Whether to delete namespace is determined by 3 factors: delete-namespace flag, delete-namespace-on-failure flag and the test result
  330. // if delete-namespace set to false, namespace will always be preserved.
  331. // if delete-namespace is true and delete-namespace-on-failure is false, namespace will be preserved if test failed.
  332. if TestContext.DeleteNamespace && (TestContext.DeleteNamespaceOnFailure || !ginkgo.CurrentGinkgoTestDescription().Failed) {
  333. for _, ns := range f.namespacesToDelete {
  334. ginkgo.By(fmt.Sprintf("Destroying namespace %q for this suite.", ns.Name))
  335. if err := f.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), ns.Name, nil); err != nil {
  336. if !apierrors.IsNotFound(err) {
  337. nsDeletionErrors[ns.Name] = err
  338. // Dump namespace if we are unable to delete the namespace and the dump was not already performed.
  339. if !ginkgo.CurrentGinkgoTestDescription().Failed && TestContext.DumpLogsOnFailure {
  340. DumpAllNamespaceInfo(f.ClientSet, ns.Name)
  341. }
  342. } else {
  343. Logf("Namespace %v was already deleted", ns.Name)
  344. }
  345. }
  346. }
  347. } else {
  348. if !TestContext.DeleteNamespace {
  349. Logf("Found DeleteNamespace=false, skipping namespace deletion!")
  350. } else {
  351. Logf("Found DeleteNamespaceOnFailure=false and current test failed, skipping namespace deletion!")
  352. }
  353. }
  354. // Paranoia-- prevent reuse!
  355. f.Namespace = nil
  356. f.clientConfig = nil
  357. f.ClientSet = nil
  358. f.namespacesToDelete = nil
  359. // if we had errors deleting, report them now.
  360. if len(nsDeletionErrors) != 0 {
  361. messages := []string{}
  362. for namespaceKey, namespaceErr := range nsDeletionErrors {
  363. messages = append(messages, fmt.Sprintf("Couldn't delete ns: %q: %s (%#v)", namespaceKey, namespaceErr, namespaceErr))
  364. }
  365. Failf(strings.Join(messages, ","))
  366. }
  367. }()
  368. // run all aftereach functions in random order to ensure no dependencies grow
  369. for _, afterEachFn := range f.afterEaches {
  370. afterEachFn(f, ginkgo.CurrentGinkgoTestDescription().Failed)
  371. }
  372. if TestContext.GatherKubeSystemResourceUsageData != "false" && TestContext.GatherKubeSystemResourceUsageData != "none" && f.gatherer != nil {
  373. ginkgo.By("Collecting resource usage data")
  374. summary, resourceViolationError := f.gatherer.StopAndSummarize([]int{90, 99, 100}, f.AddonResourceConstraints)
  375. defer ExpectNoError(resourceViolationError)
  376. f.TestSummaries = append(f.TestSummaries, summary)
  377. }
  378. if TestContext.GatherLogsSizes {
  379. ginkgo.By("Gathering log sizes data")
  380. close(f.logsSizeCloseChannel)
  381. f.logsSizeWaitGroup.Wait()
  382. f.TestSummaries = append(f.TestSummaries, f.logsSizeVerifier.GetSummary())
  383. }
  384. if TestContext.GatherMetricsAfterTest != "false" {
  385. ginkgo.By("Gathering metrics")
  386. // Grab apiserver, scheduler, controller-manager metrics and (optionally) nodes' kubelet metrics.
  387. grabMetricsFromKubelets := TestContext.GatherMetricsAfterTest != "master" && !ProviderIs("kubemark")
  388. grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, grabMetricsFromKubelets, true, true, true, TestContext.IncludeClusterAutoscalerMetrics)
  389. if err != nil {
  390. Logf("Failed to create MetricsGrabber (skipping metrics gathering): %v", err)
  391. } else {
  392. received, err := grabber.Grab()
  393. if err != nil {
  394. Logf("MetricsGrabber failed to grab some of the metrics: %v", err)
  395. }
  396. (*e2emetrics.ComponentCollection)(&received).ComputeClusterAutoscalerMetricsDelta(f.clusterAutoscalerMetricsBeforeTest)
  397. f.TestSummaries = append(f.TestSummaries, (*e2emetrics.ComponentCollection)(&received))
  398. }
  399. }
  400. TestContext.CloudConfig.Provider.FrameworkAfterEach(f)
  401. // Report any flakes that were observed in the e2e test and reset.
  402. if f.flakeReport != nil && f.flakeReport.GetFlakeCount() > 0 {
  403. f.TestSummaries = append(f.TestSummaries, f.flakeReport)
  404. f.flakeReport = nil
  405. }
  406. printSummaries(f.TestSummaries, f.BaseName)
  407. // Check whether all nodes are ready after the test.
  408. // This is explicitly done at the very end of the test, to avoid
  409. // e.g. not removing namespace in case of this failure.
  410. if err := AllNodesReady(f.ClientSet, 3*time.Minute); err != nil {
  411. Failf("All nodes should be ready after test, %v", err)
  412. }
  413. }
  414. // CreateNamespace creates a namespace for e2e testing.
  415. func (f *Framework) CreateNamespace(baseName string, labels map[string]string) (*v1.Namespace, error) {
  416. createTestingNS := TestContext.CreateTestingNS
  417. if createTestingNS == nil {
  418. createTestingNS = CreateTestingNS
  419. }
  420. ns, err := createTestingNS(baseName, f.ClientSet, labels)
  421. // check ns instead of err to see if it's nil as we may
  422. // fail to create serviceAccount in it.
  423. f.AddNamespacesToDelete(ns)
  424. if err == nil && !f.SkipPrivilegedPSPBinding {
  425. CreatePrivilegedPSPBinding(f.ClientSet, ns.Name)
  426. }
  427. return ns, err
  428. }
  429. // RecordFlakeIfError records flakeness info if error happens.
  430. // NOTE: This function is not used at any places yet, but we are in progress for https://github.com/kubernetes/kubernetes/issues/66239 which requires this. Please don't remove this.
  431. func (f *Framework) RecordFlakeIfError(err error, optionalDescription ...interface{}) {
  432. f.flakeReport.RecordFlakeIfError(err, optionalDescription)
  433. }
  434. // AddNamespacesToDelete adds one or more namespaces to be deleted when the test
  435. // completes.
  436. func (f *Framework) AddNamespacesToDelete(namespaces ...*v1.Namespace) {
  437. for _, ns := range namespaces {
  438. if ns == nil {
  439. continue
  440. }
  441. f.namespacesToDelete = append(f.namespacesToDelete, ns)
  442. }
  443. }
  444. // WaitForPodTerminated waits for the pod to be terminated with the given reason.
  445. func (f *Framework) WaitForPodTerminated(podName, reason string) error {
  446. return e2epod.WaitForPodTerminatedInNamespace(f.ClientSet, podName, reason, f.Namespace.Name)
  447. }
  448. // WaitForPodNotFound waits for the pod to be completely terminated (not "Get-able").
  449. func (f *Framework) WaitForPodNotFound(podName string, timeout time.Duration) error {
  450. return e2epod.WaitForPodNotFoundInNamespace(f.ClientSet, podName, f.Namespace.Name, timeout)
  451. }
  452. // WaitForPodRunning waits for the pod to run in the namespace.
  453. func (f *Framework) WaitForPodRunning(podName string) error {
  454. return e2epod.WaitForPodNameRunningInNamespace(f.ClientSet, podName, f.Namespace.Name)
  455. }
  456. // WaitForPodReady waits for the pod to flip to ready in the namespace.
  457. func (f *Framework) WaitForPodReady(podName string) error {
  458. return e2epod.WaitTimeoutForPodReadyInNamespace(f.ClientSet, podName, f.Namespace.Name, PodStartTimeout)
  459. }
  460. // WaitForPodRunningSlow waits for the pod to run in the namespace.
  461. // It has a longer timeout then WaitForPodRunning (util.slowPodStartTimeout).
  462. func (f *Framework) WaitForPodRunningSlow(podName string) error {
  463. return e2epod.WaitForPodRunningInNamespaceSlow(f.ClientSet, podName, f.Namespace.Name)
  464. }
  465. // WaitForPodNoLongerRunning waits for the pod to no longer be running in the namespace, for either
  466. // success or failure.
  467. func (f *Framework) WaitForPodNoLongerRunning(podName string) error {
  468. return e2epod.WaitForPodNoLongerRunningInNamespace(f.ClientSet, podName, f.Namespace.Name)
  469. }
  470. // ClientConfig an externally accessible method for reading the kube client config.
  471. func (f *Framework) ClientConfig() *rest.Config {
  472. ret := rest.CopyConfig(f.clientConfig)
  473. // json is least common denominator
  474. ret.ContentType = runtime.ContentTypeJSON
  475. ret.AcceptContentTypes = runtime.ContentTypeJSON
  476. return ret
  477. }
  478. // TestContainerOutput runs the given pod in the given namespace and waits
  479. // for all of the containers in the podSpec to move into the 'Success' status, and tests
  480. // the specified container log against the given expected output using a substring matcher.
  481. func (f *Framework) TestContainerOutput(scenarioName string, pod *v1.Pod, containerIndex int, expectedOutput []string) {
  482. f.testContainerOutputMatcher(scenarioName, pod, containerIndex, expectedOutput, gomega.ContainSubstring)
  483. }
  484. // TestContainerOutputRegexp runs the given pod in the given namespace and waits
  485. // for all of the containers in the podSpec to move into the 'Success' status, and tests
  486. // the specified container log against the given expected output using a regexp matcher.
  487. func (f *Framework) TestContainerOutputRegexp(scenarioName string, pod *v1.Pod, containerIndex int, expectedOutput []string) {
  488. f.testContainerOutputMatcher(scenarioName, pod, containerIndex, expectedOutput, gomega.MatchRegexp)
  489. }
  490. // CreateServiceForSimpleAppWithPods is a convenience wrapper to create a service and its matching pods all at once.
  491. func (f *Framework) CreateServiceForSimpleAppWithPods(contPort int, svcPort int, appName string, podSpec func(n v1.Node) v1.PodSpec, count int, block bool) (*v1.Service, error) {
  492. var err error
  493. theService := f.CreateServiceForSimpleApp(contPort, svcPort, appName)
  494. f.CreatePodsPerNodeForSimpleApp(appName, podSpec, count)
  495. if block {
  496. err = testutils.WaitForPodsWithLabelRunning(f.ClientSet, f.Namespace.Name, labels.SelectorFromSet(labels.Set(theService.Spec.Selector)))
  497. }
  498. return theService, err
  499. }
  500. // CreateServiceForSimpleApp returns a service that selects/exposes pods (send -1 ports if no exposure needed) with an app label.
  501. func (f *Framework) CreateServiceForSimpleApp(contPort, svcPort int, appName string) *v1.Service {
  502. if appName == "" {
  503. panic(fmt.Sprintf("no app name provided"))
  504. }
  505. serviceSelector := map[string]string{
  506. "app": appName + "-pod",
  507. }
  508. // For convenience, user sending ports are optional.
  509. portsFunc := func() []v1.ServicePort {
  510. if contPort < 1 || svcPort < 1 {
  511. return nil
  512. }
  513. return []v1.ServicePort{{
  514. Protocol: v1.ProtocolTCP,
  515. Port: int32(svcPort),
  516. TargetPort: intstr.FromInt(contPort),
  517. }}
  518. }
  519. Logf("Creating a service-for-%v for selecting app=%v-pod", appName, appName)
  520. service, err := f.ClientSet.CoreV1().Services(f.Namespace.Name).Create(context.TODO(), &v1.Service{
  521. ObjectMeta: metav1.ObjectMeta{
  522. Name: "service-for-" + appName,
  523. Labels: map[string]string{
  524. "app": appName + "-service",
  525. },
  526. },
  527. Spec: v1.ServiceSpec{
  528. Ports: portsFunc(),
  529. Selector: serviceSelector,
  530. },
  531. }, metav1.CreateOptions{})
  532. ExpectNoError(err)
  533. return service
  534. }
  535. // CreatePodsPerNodeForSimpleApp creates pods w/ labels. Useful for tests which make a bunch of pods w/o any networking.
  536. func (f *Framework) CreatePodsPerNodeForSimpleApp(appName string, podSpec func(n v1.Node) v1.PodSpec, maxCount int) map[string]string {
  537. nodes, err := e2enode.GetBoundedReadySchedulableNodes(f.ClientSet, maxCount)
  538. ExpectNoError(err)
  539. podLabels := map[string]string{
  540. "app": appName + "-pod",
  541. }
  542. for i, node := range nodes.Items {
  543. Logf("%v/%v : Creating container with label app=%v-pod", i, maxCount, appName)
  544. _, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), &v1.Pod{
  545. ObjectMeta: metav1.ObjectMeta{
  546. Name: fmt.Sprintf(appName+"-pod-%v", i),
  547. Labels: podLabels,
  548. },
  549. Spec: podSpec(node),
  550. }, metav1.CreateOptions{})
  551. ExpectNoError(err)
  552. }
  553. return podLabels
  554. }
  555. // KubeUser is a struct for managing kubernetes user info.
  556. type KubeUser struct {
  557. Name string `yaml:"name"`
  558. User struct {
  559. Username string `yaml:"username"`
  560. Password string `yaml:"password"`
  561. Token string `yaml:"token"`
  562. } `yaml:"user"`
  563. }
  564. // KubeCluster is a struct for managing kubernetes cluster info.
  565. type KubeCluster struct {
  566. Name string `yaml:"name"`
  567. Cluster struct {
  568. CertificateAuthorityData string `yaml:"certificate-authority-data"`
  569. Server string `yaml:"server"`
  570. } `yaml:"cluster"`
  571. }
  572. // KubeConfig is a struct for managing kubernetes config.
  573. type KubeConfig struct {
  574. Contexts []struct {
  575. Name string `yaml:"name"`
  576. Context struct {
  577. Cluster string `yaml:"cluster"`
  578. User string
  579. } `yaml:"context"`
  580. } `yaml:"contexts"`
  581. Clusters []KubeCluster `yaml:"clusters"`
  582. Users []KubeUser `yaml:"users"`
  583. }
  584. // FindUser returns user info which is the specified user name.
  585. func (kc *KubeConfig) FindUser(name string) *KubeUser {
  586. for _, user := range kc.Users {
  587. if user.Name == name {
  588. return &user
  589. }
  590. }
  591. return nil
  592. }
  593. // FindCluster returns cluster info which is the specified cluster name.
  594. func (kc *KubeConfig) FindCluster(name string) *KubeCluster {
  595. for _, cluster := range kc.Clusters {
  596. if cluster.Name == name {
  597. return &cluster
  598. }
  599. }
  600. return nil
  601. }
  602. // KubeDescribe is wrapper function for ginkgo describe. Adds namespacing.
  603. // TODO: Support type safe tagging as well https://github.com/kubernetes/kubernetes/pull/22401.
  604. func KubeDescribe(text string, body func()) bool {
  605. return ginkgo.Describe("[k8s.io] "+text, body)
  606. }
  607. // ConformanceIt is wrapper function for ginkgo It. Adds "[Conformance]" tag and makes static analysis easier.
  608. func ConformanceIt(text string, body interface{}, timeout ...float64) bool {
  609. return ginkgo.It(text+" [Conformance]", body, timeout...)
  610. }
  611. // PodStateVerification represents a verification of pod state.
  612. // Any time you have a set of pods that you want to operate against or query,
  613. // this struct can be used to declaratively identify those pods.
  614. type PodStateVerification struct {
  615. // Optional: only pods that have k=v labels will pass this filter.
  616. Selectors map[string]string
  617. // Required: The phases which are valid for your pod.
  618. ValidPhases []v1.PodPhase
  619. // Optional: only pods passing this function will pass the filter
  620. // Verify a pod.
  621. // As an optimization, in addition to specifying filter (boolean),
  622. // this function allows specifying an error as well.
  623. // The error indicates that the polling of the pod spectrum should stop.
  624. Verify func(v1.Pod) (bool, error)
  625. // Optional: only pods with this name will pass the filter.
  626. PodName string
  627. }
  628. // ClusterVerification is a struct for a verification of cluster state.
  629. type ClusterVerification struct {
  630. client clientset.Interface
  631. namespace *v1.Namespace // pointer rather than string, since ns isn't created until before each.
  632. podState PodStateVerification
  633. }
  634. // NewClusterVerification creates a new cluster verification.
  635. func (f *Framework) NewClusterVerification(namespace *v1.Namespace, filter PodStateVerification) *ClusterVerification {
  636. return &ClusterVerification{
  637. f.ClientSet,
  638. namespace,
  639. filter,
  640. }
  641. }
  642. func passesPodNameFilter(pod v1.Pod, name string) bool {
  643. return name == "" || strings.Contains(pod.Name, name)
  644. }
  645. func passesVerifyFilter(pod v1.Pod, verify func(p v1.Pod) (bool, error)) (bool, error) {
  646. if verify == nil {
  647. return true, nil
  648. }
  649. verified, err := verify(pod)
  650. // If an error is returned, by definition, pod verification fails
  651. if err != nil {
  652. return false, err
  653. }
  654. return verified, nil
  655. }
  656. func passesPhasesFilter(pod v1.Pod, validPhases []v1.PodPhase) bool {
  657. passesPhaseFilter := false
  658. for _, phase := range validPhases {
  659. if pod.Status.Phase == phase {
  660. passesPhaseFilter = true
  661. }
  662. }
  663. return passesPhaseFilter
  664. }
  665. // filterLabels returns a list of pods which have labels.
  666. func filterLabels(selectors map[string]string, cli clientset.Interface, ns string) (*v1.PodList, error) {
  667. var err error
  668. var selector labels.Selector
  669. var pl *v1.PodList
  670. // List pods based on selectors. This might be a tiny optimization rather then filtering
  671. // everything manually.
  672. if len(selectors) > 0 {
  673. selector = labels.SelectorFromSet(labels.Set(selectors))
  674. options := metav1.ListOptions{LabelSelector: selector.String()}
  675. pl, err = cli.CoreV1().Pods(ns).List(context.TODO(), options)
  676. } else {
  677. pl, err = cli.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
  678. }
  679. return pl, err
  680. }
  681. // filter filters pods which pass a filter. It can be used to compose
  682. // the more useful abstractions like ForEach, WaitFor, and so on, which
  683. // can be used directly by tests.
  684. func (p *PodStateVerification) filter(c clientset.Interface, namespace *v1.Namespace) ([]v1.Pod, error) {
  685. if len(p.ValidPhases) == 0 || namespace == nil {
  686. panic(fmt.Errorf("Need to specify a valid pod phases (%v) and namespace (%v). ", p.ValidPhases, namespace))
  687. }
  688. ns := namespace.Name
  689. pl, err := filterLabels(p.Selectors, c, ns) // Build an v1.PodList to operate against.
  690. Logf("Selector matched %v pods for %v", len(pl.Items), p.Selectors)
  691. if len(pl.Items) == 0 || err != nil {
  692. return pl.Items, err
  693. }
  694. unfilteredPods := pl.Items
  695. filteredPods := []v1.Pod{}
  696. ReturnPodsSoFar:
  697. // Next: Pod must match at least one of the states that the user specified
  698. for _, pod := range unfilteredPods {
  699. if !(passesPhasesFilter(pod, p.ValidPhases) && passesPodNameFilter(pod, p.PodName)) {
  700. continue
  701. }
  702. passesVerify, err := passesVerifyFilter(pod, p.Verify)
  703. if err != nil {
  704. Logf("Error detected on %v : %v !", pod.Name, err)
  705. break ReturnPodsSoFar
  706. }
  707. if passesVerify {
  708. filteredPods = append(filteredPods, pod)
  709. }
  710. }
  711. return filteredPods, err
  712. }
  713. // WaitFor waits for some minimum number of pods to be verified, according to the PodStateVerification
  714. // definition.
  715. func (cl *ClusterVerification) WaitFor(atLeast int, timeout time.Duration) ([]v1.Pod, error) {
  716. pods := []v1.Pod{}
  717. var returnedErr error
  718. err := wait.Poll(1*time.Second, timeout, func() (bool, error) {
  719. pods, returnedErr = cl.podState.filter(cl.client, cl.namespace)
  720. // Failure
  721. if returnedErr != nil {
  722. Logf("Cutting polling short: We got an error from the pod filtering layer.")
  723. // stop polling if the pod filtering returns an error. that should never happen.
  724. // it indicates, for example, that the client is broken or something non-pod related.
  725. return false, returnedErr
  726. }
  727. Logf("Found %v / %v", len(pods), atLeast)
  728. // Success
  729. if len(pods) >= atLeast {
  730. return true, nil
  731. }
  732. // Keep trying...
  733. return false, nil
  734. })
  735. Logf("WaitFor completed with timeout %v. Pods found = %v out of %v", timeout, len(pods), atLeast)
  736. return pods, err
  737. }
  738. // WaitForOrFail provides a shorthand WaitFor with failure as an option if anything goes wrong.
  739. func (cl *ClusterVerification) WaitForOrFail(atLeast int, timeout time.Duration) {
  740. pods, err := cl.WaitFor(atLeast, timeout)
  741. if err != nil || len(pods) < atLeast {
  742. Failf("Verified %v of %v pods , error : %v", len(pods), atLeast, err)
  743. }
  744. }
  745. // ForEach runs a function against every verifiable pod. Be warned that this doesn't wait for "n" pods to verify,
  746. // so it may return very quickly if you have strict pod state requirements.
  747. //
  748. // For example, if you require at least 5 pods to be running before your test will pass,
  749. // its smart to first call "clusterVerification.WaitFor(5)" before you call clusterVerification.ForEach.
  750. func (cl *ClusterVerification) ForEach(podFunc func(v1.Pod)) error {
  751. pods, err := cl.podState.filter(cl.client, cl.namespace)
  752. if err == nil {
  753. if len(pods) == 0 {
  754. Failf("No pods matched the filter.")
  755. }
  756. Logf("ForEach: Found %v pods from the filter. Now looping through them.", len(pods))
  757. for _, p := range pods {
  758. podFunc(p)
  759. }
  760. } else {
  761. Logf("ForEach: Something went wrong when filtering pods to execute against: %v", err)
  762. }
  763. return err
  764. }