load.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  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 scalability
  14. import (
  15. "context"
  16. "fmt"
  17. "math"
  18. "math/rand"
  19. "net"
  20. "net/http"
  21. "os"
  22. "strconv"
  23. "sync"
  24. "time"
  25. v1 "k8s.io/api/core/v1"
  26. "k8s.io/apimachinery/pkg/api/resource"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/labels"
  29. "k8s.io/apimachinery/pkg/runtime/schema"
  30. "k8s.io/apimachinery/pkg/util/intstr"
  31. utilnet "k8s.io/apimachinery/pkg/util/net"
  32. "k8s.io/apimachinery/pkg/util/wait"
  33. "k8s.io/client-go/discovery"
  34. cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
  35. "k8s.io/client-go/dynamic"
  36. clientset "k8s.io/client-go/kubernetes"
  37. "k8s.io/client-go/kubernetes/scheme"
  38. restclient "k8s.io/client-go/rest"
  39. "k8s.io/client-go/restmapper"
  40. scaleclient "k8s.io/client-go/scale"
  41. "k8s.io/client-go/transport"
  42. "k8s.io/client-go/util/workqueue"
  43. "k8s.io/kubernetes/pkg/apis/batch"
  44. api "k8s.io/kubernetes/pkg/apis/core"
  45. "k8s.io/kubernetes/pkg/apis/extensions"
  46. "k8s.io/kubernetes/test/e2e/framework"
  47. e2elog "k8s.io/kubernetes/test/e2e/framework/log"
  48. "k8s.io/kubernetes/test/e2e/framework/timer"
  49. testutils "k8s.io/kubernetes/test/utils"
  50. . "github.com/onsi/ginkgo"
  51. . "github.com/onsi/gomega"
  52. )
  53. const (
  54. smallGroupSize = 5
  55. mediumGroupSize = 30
  56. bigGroupSize = 250
  57. smallGroupName = "load-small"
  58. mediumGroupName = "load-medium"
  59. bigGroupName = "load-big"
  60. // We start RCs/Services/pods/... in different namespace in this test.
  61. // nodeCountPerNamespace determines how many namespaces we will be using
  62. // depending on the number of nodes in the underlying cluster.
  63. nodeCountPerNamespace = 100
  64. // How many threads will be used to create/delete services during this test.
  65. serviceOperationsParallelism = 1
  66. svcLabelKey = "svc-label"
  67. )
  68. var randomKind = schema.GroupKind{Kind: "Random"}
  69. var knownKinds = []schema.GroupKind{
  70. api.Kind("ReplicationController"),
  71. extensions.Kind("Deployment"),
  72. // TODO: uncomment when Jobs are fixed: #38497
  73. //batch.Kind("Job"),
  74. extensions.Kind("ReplicaSet"),
  75. }
  76. // This test suite can take a long time to run, so by default it is added to
  77. // the ginkgo.skip list (see driver.go).
  78. // To run this suite you must explicitly ask for it by setting the
  79. // -t/--test flag or ginkgo.focus flag.
  80. var _ = SIGDescribe("Load capacity", func() {
  81. var clientset clientset.Interface
  82. var nodeCount int
  83. var ns string
  84. var configs []testutils.RunObjectConfig
  85. var secretConfigs []*testutils.SecretConfig
  86. var configMapConfigs []*testutils.ConfigMapConfig
  87. testCaseBaseName := "load"
  88. var testPhaseDurations *timer.TestPhaseTimer
  89. var profileGathererStopCh chan struct{}
  90. // Gathers metrics before teardown
  91. // TODO add flag that allows to skip cleanup on failure
  92. AfterEach(func() {
  93. // Stop apiserver CPU profile gatherer and gather memory allocations profile.
  94. close(profileGathererStopCh)
  95. wg := sync.WaitGroup{}
  96. wg.Add(1)
  97. framework.GatherMemoryProfile("kube-apiserver", "load", &wg)
  98. wg.Wait()
  99. // Verify latency metrics
  100. highLatencyRequests, metrics, err := framework.HighLatencyRequests(clientset, nodeCount)
  101. framework.ExpectNoError(err)
  102. if err == nil {
  103. summaries := make([]framework.TestDataSummary, 0, 2)
  104. summaries = append(summaries, metrics)
  105. summaries = append(summaries, testPhaseDurations)
  106. framework.PrintSummaries(summaries, testCaseBaseName)
  107. Expect(highLatencyRequests).NotTo(BeNumerically(">", 0), "There should be no high-latency requests")
  108. }
  109. })
  110. // We assume a default throughput of 10 pods/second throughput.
  111. // We may want to revisit it in the future.
  112. // However, this can be overridden by LOAD_TEST_THROUGHPUT env var.
  113. throughput := 10
  114. if throughputEnv := os.Getenv("LOAD_TEST_THROUGHPUT"); throughputEnv != "" {
  115. if newThroughput, err := strconv.Atoi(throughputEnv); err == nil {
  116. throughput = newThroughput
  117. }
  118. }
  119. // Explicitly put here, to delete namespace at the end of the test
  120. // (after measuring latency metrics, etc.).
  121. options := framework.Options{
  122. ClientQPS: float32(math.Max(50.0, float64(2*throughput))),
  123. ClientBurst: int(math.Max(100.0, float64(4*throughput))),
  124. }
  125. f := framework.NewFramework(testCaseBaseName, options, nil)
  126. f.NamespaceDeletionTimeout = time.Hour
  127. BeforeEach(func() {
  128. testPhaseDurations = timer.NewTestPhaseTimer()
  129. clientset = f.ClientSet
  130. ns = f.Namespace.Name
  131. nodes := framework.GetReadySchedulableNodesOrDie(clientset)
  132. nodeCount = len(nodes.Items)
  133. Expect(nodeCount).NotTo(BeZero())
  134. // Terminating a namespace (deleting the remaining objects from it - which
  135. // generally means events) can affect the current run. Thus we wait for all
  136. // terminating namespace to be finally deleted before starting this test.
  137. err := framework.CheckTestingNSDeletedExcept(clientset, ns)
  138. framework.ExpectNoError(err)
  139. framework.ExpectNoError(framework.ResetMetrics(clientset))
  140. // Start apiserver CPU profile gatherer with frequency based on cluster size.
  141. profileGatheringDelay := time.Duration(5+nodeCount/100) * time.Minute
  142. profileGathererStopCh = framework.StartCPUProfileGatherer("kube-apiserver", "load", profileGatheringDelay)
  143. })
  144. type Load struct {
  145. podsPerNode int
  146. image string
  147. command []string
  148. // What kind of resource we want to create
  149. kind schema.GroupKind
  150. services bool
  151. secretsPerPod int
  152. configMapsPerPod int
  153. daemonsPerNode int
  154. quotas bool
  155. }
  156. loadTests := []Load{
  157. // The container will consume 1 cpu and 512mb of memory.
  158. {podsPerNode: 3, image: "jess/stress", command: []string{"stress", "-c", "1", "-m", "2"}, kind: api.Kind("ReplicationController")},
  159. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: api.Kind("ReplicationController")},
  160. // Tests for other resource types
  161. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: extensions.Kind("Deployment")},
  162. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: batch.Kind("Job")},
  163. // Test scheduling when daemons are preset
  164. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: api.Kind("ReplicationController"), daemonsPerNode: 2},
  165. // Test with secrets
  166. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: extensions.Kind("Deployment"), secretsPerPod: 2},
  167. // Test with configmaps
  168. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: extensions.Kind("Deployment"), configMapsPerPod: 2},
  169. // Special test case which randomizes created resources
  170. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: randomKind},
  171. // Test with quotas
  172. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: api.Kind("ReplicationController"), quotas: true},
  173. {podsPerNode: 30, image: framework.ServeHostnameImage, kind: randomKind, quotas: true},
  174. }
  175. isCanonical := func(test *Load) bool {
  176. return test.podsPerNode == 30 && test.kind == api.Kind("ReplicationController") && test.daemonsPerNode == 0 && test.secretsPerPod == 0 && test.configMapsPerPod == 0 && !test.quotas
  177. }
  178. for _, testArg := range loadTests {
  179. feature := "ManualPerformance"
  180. if isCanonical(&testArg) {
  181. feature = "Performance"
  182. }
  183. name := fmt.Sprintf("[Feature:%s] should be able to handle %v pods per node %v with %v secrets, %v configmaps and %v daemons",
  184. feature,
  185. testArg.podsPerNode,
  186. testArg.kind,
  187. testArg.secretsPerPod,
  188. testArg.configMapsPerPod,
  189. testArg.daemonsPerNode,
  190. )
  191. if testArg.quotas {
  192. name += " with quotas"
  193. }
  194. itArg := testArg
  195. itArg.services = os.Getenv("CREATE_SERVICES") != "false"
  196. It(name, func() {
  197. // Create a number of namespaces.
  198. namespaceCount := (nodeCount + nodeCountPerNamespace - 1) / nodeCountPerNamespace
  199. namespaces, err := CreateNamespaces(f, namespaceCount, fmt.Sprintf("load-%v-nodepods", itArg.podsPerNode), testPhaseDurations.StartPhase(110, "namespace creation"))
  200. framework.ExpectNoError(err)
  201. totalPods := (itArg.podsPerNode - itArg.daemonsPerNode) * nodeCount
  202. configs, secretConfigs, configMapConfigs = generateConfigs(totalPods, itArg.image, itArg.command, namespaces, itArg.kind, itArg.secretsPerPod, itArg.configMapsPerPod)
  203. if itArg.quotas {
  204. framework.ExpectNoError(CreateQuotas(f, namespaces, 2*totalPods, testPhaseDurations.StartPhase(115, "quota creation")))
  205. }
  206. f.AddonResourceConstraints = loadResourceConstraints()
  207. serviceCreationPhase := testPhaseDurations.StartPhase(120, "services creation")
  208. defer serviceCreationPhase.End()
  209. if itArg.services {
  210. e2elog.Logf("Creating services")
  211. services := generateServicesForConfigs(configs)
  212. createService := func(i int) {
  213. defer GinkgoRecover()
  214. framework.ExpectNoError(testutils.CreateServiceWithRetries(clientset, services[i].Namespace, services[i]))
  215. }
  216. workqueue.ParallelizeUntil(context.TODO(), serviceOperationsParallelism, len(services), createService)
  217. e2elog.Logf("%v Services created.", len(services))
  218. defer func(services []*v1.Service) {
  219. serviceCleanupPhase := testPhaseDurations.StartPhase(800, "services deletion")
  220. defer serviceCleanupPhase.End()
  221. e2elog.Logf("Starting to delete services...")
  222. deleteService := func(i int) {
  223. defer GinkgoRecover()
  224. framework.ExpectNoError(testutils.DeleteResourceWithRetries(clientset, api.Kind("Service"), services[i].Namespace, services[i].Name, nil))
  225. }
  226. workqueue.ParallelizeUntil(context.TODO(), serviceOperationsParallelism, len(services), deleteService)
  227. e2elog.Logf("Services deleted")
  228. }(services)
  229. } else {
  230. e2elog.Logf("Skipping service creation")
  231. }
  232. serviceCreationPhase.End()
  233. // Create all secrets.
  234. secretsCreationPhase := testPhaseDurations.StartPhase(130, "secrets creation")
  235. defer secretsCreationPhase.End()
  236. for i := range secretConfigs {
  237. secretConfigs[i].Run()
  238. defer secretConfigs[i].Stop()
  239. }
  240. secretsCreationPhase.End()
  241. // Create all configmaps.
  242. configMapsCreationPhase := testPhaseDurations.StartPhase(140, "configmaps creation")
  243. defer configMapsCreationPhase.End()
  244. for i := range configMapConfigs {
  245. configMapConfigs[i].Run()
  246. defer configMapConfigs[i].Stop()
  247. }
  248. configMapsCreationPhase.End()
  249. // StartDaemon if needed
  250. daemonSetCreationPhase := testPhaseDurations.StartPhase(150, "daemonsets creation")
  251. defer daemonSetCreationPhase.End()
  252. for i := 0; i < itArg.daemonsPerNode; i++ {
  253. daemonName := fmt.Sprintf("load-daemon-%v", i)
  254. daemonConfig := &testutils.DaemonConfig{
  255. Client: f.ClientSet,
  256. Name: daemonName,
  257. Namespace: f.Namespace.Name,
  258. LogFunc: e2elog.Logf,
  259. }
  260. daemonConfig.Run()
  261. defer func(config *testutils.DaemonConfig) {
  262. framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(
  263. f.ClientSet,
  264. extensions.Kind("DaemonSet"),
  265. config.Namespace,
  266. config.Name,
  267. ))
  268. }(daemonConfig)
  269. }
  270. daemonSetCreationPhase.End()
  271. // Simulate lifetime of RC:
  272. // * create with initial size
  273. // * scale RC to a random size and list all pods
  274. // * scale RC to a random size and list all pods
  275. // * delete it
  276. //
  277. // This will generate ~5 creations/deletions per second assuming:
  278. // - X small RCs each 5 pods [ 5 * X = totalPods / 2 ]
  279. // - Y medium RCs each 30 pods [ 30 * Y = totalPods / 4 ]
  280. // - Z big RCs each 250 pods [ 250 * Z = totalPods / 4]
  281. // We would like to spread creating replication controllers over time
  282. // to make it possible to create/schedule them in the meantime.
  283. // Currently we assume <throughput> pods/second average throughput.
  284. // We may want to revisit it in the future.
  285. e2elog.Logf("Starting to create %v objects...", itArg.kind)
  286. creatingTime := time.Duration(totalPods/throughput) * time.Second
  287. createAllResources(configs, creatingTime, testPhaseDurations.StartPhase(200, "load pods creation"))
  288. By("============================================================================")
  289. // We would like to spread scaling replication controllers over time
  290. // to make it possible to create/schedule & delete them in the meantime.
  291. // Currently we assume that <throughput> pods/second average throughput.
  292. // The expected number of created/deleted pods is totalPods/4 when scaling,
  293. // as each RC changes its size from X to a uniform random value in [X/2, 3X/2].
  294. scalingTime := time.Duration(totalPods/(4*throughput)) * time.Second
  295. e2elog.Logf("Starting to scale %v objects first time...", itArg.kind)
  296. scaleAllResources(configs, scalingTime, testPhaseDurations.StartPhase(300, "scaling first time"))
  297. By("============================================================================")
  298. // Cleanup all created replication controllers.
  299. // Currently we assume <throughput> pods/second average deletion throughput.
  300. // We may want to revisit it in the future.
  301. deletingTime := time.Duration(totalPods/throughput) * time.Second
  302. e2elog.Logf("Starting to delete %v objects...", itArg.kind)
  303. deleteAllResources(configs, deletingTime, testPhaseDurations.StartPhase(500, "load pods deletion"))
  304. })
  305. }
  306. })
  307. func createClients(numberOfClients int) ([]clientset.Interface, []scaleclient.ScalesGetter, error) {
  308. clients := make([]clientset.Interface, numberOfClients)
  309. scalesClients := make([]scaleclient.ScalesGetter, numberOfClients)
  310. for i := 0; i < numberOfClients; i++ {
  311. config, err := framework.LoadConfig()
  312. framework.ExpectNoError(err)
  313. config.QPS = 100
  314. config.Burst = 200
  315. if framework.TestContext.KubeAPIContentType != "" {
  316. config.ContentType = framework.TestContext.KubeAPIContentType
  317. }
  318. // For the purpose of this test, we want to force that clients
  319. // do not share underlying transport (which is a default behavior
  320. // in Kubernetes). Thus, we are explicitly creating transport for
  321. // each client here.
  322. transportConfig, err := config.TransportConfig()
  323. if err != nil {
  324. return nil, nil, err
  325. }
  326. tlsConfig, err := transport.TLSConfigFor(transportConfig)
  327. if err != nil {
  328. return nil, nil, err
  329. }
  330. config.Transport = utilnet.SetTransportDefaults(&http.Transport{
  331. Proxy: http.ProxyFromEnvironment,
  332. TLSHandshakeTimeout: 10 * time.Second,
  333. TLSClientConfig: tlsConfig,
  334. MaxIdleConnsPerHost: 100,
  335. DialContext: (&net.Dialer{
  336. Timeout: 30 * time.Second,
  337. KeepAlive: 30 * time.Second,
  338. }).DialContext,
  339. })
  340. config.WrapTransport = transportConfig.WrapTransport
  341. config.Dial = transportConfig.Dial
  342. // Overwrite TLS-related fields from config to avoid collision with
  343. // Transport field.
  344. config.TLSClientConfig = restclient.TLSClientConfig{}
  345. config.AuthProvider = nil
  346. config.ExecProvider = nil
  347. c, err := clientset.NewForConfig(config)
  348. if err != nil {
  349. return nil, nil, err
  350. }
  351. clients[i] = c
  352. // create scale client, if GroupVersion or NegotiatedSerializer are not set
  353. // assign default values - these fields are mandatory (required by RESTClientFor).
  354. if config.GroupVersion == nil {
  355. config.GroupVersion = &schema.GroupVersion{}
  356. }
  357. if config.NegotiatedSerializer == nil {
  358. config.NegotiatedSerializer = scheme.Codecs
  359. }
  360. restClient, err := restclient.RESTClientFor(config)
  361. if err != nil {
  362. return nil, nil, err
  363. }
  364. discoClient, err := discovery.NewDiscoveryClientForConfig(config)
  365. if err != nil {
  366. return nil, nil, err
  367. }
  368. cachedDiscoClient := cacheddiscovery.NewMemCacheClient(discoClient)
  369. restMapper := restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoClient)
  370. restMapper.Reset()
  371. resolver := scaleclient.NewDiscoveryScaleKindResolver(cachedDiscoClient)
  372. scalesClients[i] = scaleclient.New(restClient, restMapper, dynamic.LegacyAPIPathResolverFunc, resolver)
  373. }
  374. return clients, scalesClients, nil
  375. }
  376. func computePodCounts(total int) (int, int, int) {
  377. // Small RCs owns ~0.5 of total number of pods, medium and big RCs ~0.25 each.
  378. // For example for 3000 pods (100 nodes, 30 pods per node) there are:
  379. // - 300 small RCs each 5 pods
  380. // - 25 medium RCs each 30 pods
  381. // - 3 big RCs each 250 pods
  382. bigGroupCount := total / 4 / bigGroupSize
  383. total -= bigGroupCount * bigGroupSize
  384. mediumGroupCount := total / 3 / mediumGroupSize
  385. total -= mediumGroupCount * mediumGroupSize
  386. smallGroupCount := total / smallGroupSize
  387. return smallGroupCount, mediumGroupCount, bigGroupCount
  388. }
  389. func loadResourceConstraints() map[string]framework.ResourceConstraint {
  390. constraints := make(map[string]framework.ResourceConstraint)
  391. constraints["coredns"] = framework.ResourceConstraint{
  392. CPUConstraint: framework.NoCPUConstraint,
  393. MemoryConstraint: 170 * (1024 * 1024),
  394. }
  395. constraints["kubedns"] = framework.ResourceConstraint{
  396. CPUConstraint: framework.NoCPUConstraint,
  397. MemoryConstraint: 170 * (1024 * 1024),
  398. }
  399. return constraints
  400. }
  401. func generateConfigs(
  402. totalPods int,
  403. image string,
  404. command []string,
  405. nss []*v1.Namespace,
  406. kind schema.GroupKind,
  407. secretsPerPod int,
  408. configMapsPerPod int,
  409. ) ([]testutils.RunObjectConfig, []*testutils.SecretConfig, []*testutils.ConfigMapConfig) {
  410. configs := make([]testutils.RunObjectConfig, 0)
  411. secretConfigs := make([]*testutils.SecretConfig, 0)
  412. configMapConfigs := make([]*testutils.ConfigMapConfig, 0)
  413. smallGroupCount, mediumGroupCount, bigGroupCount := computePodCounts(totalPods)
  414. newConfigs, newSecretConfigs, newConfigMapConfigs := GenerateConfigsForGroup(nss, smallGroupName, smallGroupSize, smallGroupCount, image, command, kind, secretsPerPod, configMapsPerPod)
  415. configs = append(configs, newConfigs...)
  416. secretConfigs = append(secretConfigs, newSecretConfigs...)
  417. configMapConfigs = append(configMapConfigs, newConfigMapConfigs...)
  418. newConfigs, newSecretConfigs, newConfigMapConfigs = GenerateConfigsForGroup(nss, mediumGroupName, mediumGroupSize, mediumGroupCount, image, command, kind, secretsPerPod, configMapsPerPod)
  419. configs = append(configs, newConfigs...)
  420. secretConfigs = append(secretConfigs, newSecretConfigs...)
  421. configMapConfigs = append(configMapConfigs, newConfigMapConfigs...)
  422. newConfigs, newSecretConfigs, newConfigMapConfigs = GenerateConfigsForGroup(nss, bigGroupName, bigGroupSize, bigGroupCount, image, command, kind, secretsPerPod, configMapsPerPod)
  423. configs = append(configs, newConfigs...)
  424. secretConfigs = append(secretConfigs, newSecretConfigs...)
  425. configMapConfigs = append(configMapConfigs, newConfigMapConfigs...)
  426. // Create a number of clients to better simulate real usecase
  427. // where not everyone is using exactly the same client.
  428. rcsPerClient := 20
  429. clients, scalesClients, err := createClients((len(configs) + rcsPerClient - 1) / rcsPerClient)
  430. framework.ExpectNoError(err)
  431. for i := 0; i < len(configs); i++ {
  432. configs[i].SetClient(clients[i%len(clients)])
  433. configs[i].SetScalesClient(scalesClients[i%len(clients)])
  434. }
  435. for i := 0; i < len(secretConfigs); i++ {
  436. secretConfigs[i].Client = clients[i%len(clients)]
  437. }
  438. for i := 0; i < len(configMapConfigs); i++ {
  439. configMapConfigs[i].Client = clients[i%len(clients)]
  440. }
  441. return configs, secretConfigs, configMapConfigs
  442. }
  443. func GenerateConfigsForGroup(
  444. nss []*v1.Namespace,
  445. groupName string,
  446. size, count int,
  447. image string,
  448. command []string,
  449. kind schema.GroupKind,
  450. secretsPerPod int,
  451. configMapsPerPod int,
  452. ) ([]testutils.RunObjectConfig, []*testutils.SecretConfig, []*testutils.ConfigMapConfig) {
  453. configs := make([]testutils.RunObjectConfig, 0, count)
  454. secretConfigs := make([]*testutils.SecretConfig, 0, count*secretsPerPod)
  455. configMapConfigs := make([]*testutils.ConfigMapConfig, 0, count*configMapsPerPod)
  456. savedKind := kind
  457. for i := 1; i <= count; i++ {
  458. kind = savedKind
  459. namespace := nss[i%len(nss)].Name
  460. secretNames := make([]string, 0, secretsPerPod)
  461. configMapNames := make([]string, 0, configMapsPerPod)
  462. for j := 0; j < secretsPerPod; j++ {
  463. secretName := fmt.Sprintf("%v-%v-secret-%v", groupName, i, j)
  464. secretConfigs = append(secretConfigs, &testutils.SecretConfig{
  465. Content: map[string]string{"foo": "bar"},
  466. Client: nil, // this will be overwritten later
  467. Name: secretName,
  468. Namespace: namespace,
  469. LogFunc: e2elog.Logf,
  470. })
  471. secretNames = append(secretNames, secretName)
  472. }
  473. for j := 0; j < configMapsPerPod; j++ {
  474. configMapName := fmt.Sprintf("%v-%v-configmap-%v", groupName, i, j)
  475. configMapConfigs = append(configMapConfigs, &testutils.ConfigMapConfig{
  476. Content: map[string]string{"foo": "bar"},
  477. Client: nil, // this will be overwritten later
  478. Name: configMapName,
  479. Namespace: namespace,
  480. LogFunc: e2elog.Logf,
  481. })
  482. configMapNames = append(configMapNames, configMapName)
  483. }
  484. baseConfig := &testutils.RCConfig{
  485. Client: nil, // this will be overwritten later
  486. Name: groupName + "-" + strconv.Itoa(i),
  487. Namespace: namespace,
  488. Timeout: UnreadyNodeToleration,
  489. Image: image,
  490. Command: command,
  491. Replicas: size,
  492. CpuRequest: 10, // 0.01 core
  493. MemRequest: 26214400, // 25MB
  494. SecretNames: secretNames,
  495. ConfigMapNames: configMapNames,
  496. // Define a label to group every 2 RCs into one service.
  497. Labels: map[string]string{svcLabelKey: groupName + "-" + strconv.Itoa((i+1)/2)},
  498. Tolerations: []v1.Toleration{
  499. {
  500. Key: "node.kubernetes.io/not-ready",
  501. Operator: v1.TolerationOpExists,
  502. Effect: v1.TaintEffectNoExecute,
  503. TolerationSeconds: func(i int64) *int64 { return &i }(int64(UnreadyNodeToleration / time.Second)),
  504. }, {
  505. Key: "node.kubernetes.io/unreachable",
  506. Operator: v1.TolerationOpExists,
  507. Effect: v1.TaintEffectNoExecute,
  508. TolerationSeconds: func(i int64) *int64 { return &i }(int64(UnreadyNodeToleration / time.Second)),
  509. },
  510. },
  511. }
  512. if kind == randomKind {
  513. kind = knownKinds[rand.Int()%len(knownKinds)]
  514. }
  515. var config testutils.RunObjectConfig
  516. switch kind {
  517. case api.Kind("ReplicationController"):
  518. config = baseConfig
  519. case extensions.Kind("ReplicaSet"):
  520. config = &testutils.ReplicaSetConfig{RCConfig: *baseConfig}
  521. case extensions.Kind("Deployment"):
  522. config = &testutils.DeploymentConfig{RCConfig: *baseConfig}
  523. case batch.Kind("Job"):
  524. config = &testutils.JobConfig{RCConfig: *baseConfig}
  525. default:
  526. framework.Failf("Unsupported kind for config creation: %v", kind)
  527. }
  528. configs = append(configs, config)
  529. }
  530. return configs, secretConfigs, configMapConfigs
  531. }
  532. func generateServicesForConfigs(configs []testutils.RunObjectConfig) []*v1.Service {
  533. services := make([]*v1.Service, 0)
  534. currentSvcLabel := ""
  535. for _, config := range configs {
  536. svcLabel, found := config.GetLabelValue(svcLabelKey)
  537. if !found || svcLabel == currentSvcLabel {
  538. continue
  539. }
  540. currentSvcLabel = svcLabel
  541. serviceName := config.GetName() + "-svc"
  542. labels := map[string]string{
  543. "name": config.GetName(),
  544. svcLabelKey: currentSvcLabel,
  545. }
  546. service := &v1.Service{
  547. ObjectMeta: metav1.ObjectMeta{
  548. Name: serviceName,
  549. Namespace: config.GetNamespace(),
  550. },
  551. Spec: v1.ServiceSpec{
  552. Selector: labels,
  553. Ports: []v1.ServicePort{{
  554. Port: 80,
  555. TargetPort: intstr.FromInt(80),
  556. }},
  557. },
  558. }
  559. services = append(services, service)
  560. }
  561. return services
  562. }
  563. func sleepUpTo(d time.Duration) {
  564. if d.Nanoseconds() > 0 {
  565. time.Sleep(time.Duration(rand.Int63n(d.Nanoseconds())))
  566. }
  567. }
  568. func retryWithExponentialBackOff(initialDuration time.Duration, fn wait.ConditionFunc) error {
  569. backoff := wait.Backoff{
  570. Duration: initialDuration,
  571. Factor: 3,
  572. Jitter: 0,
  573. Steps: 6,
  574. }
  575. return wait.ExponentialBackoff(backoff, fn)
  576. }
  577. func createAllResources(configs []testutils.RunObjectConfig, creatingTime time.Duration, testPhase *timer.Phase) {
  578. defer testPhase.End()
  579. var wg sync.WaitGroup
  580. wg.Add(len(configs))
  581. for _, config := range configs {
  582. go createResource(&wg, config, creatingTime)
  583. }
  584. wg.Wait()
  585. }
  586. func createResource(wg *sync.WaitGroup, config testutils.RunObjectConfig, creatingTime time.Duration) {
  587. defer GinkgoRecover()
  588. defer wg.Done()
  589. sleepUpTo(creatingTime)
  590. framework.ExpectNoError(config.Run(), fmt.Sprintf("creating %v %s", config.GetKind(), config.GetName()))
  591. }
  592. func scaleAllResources(configs []testutils.RunObjectConfig, scalingTime time.Duration, testPhase *timer.Phase) {
  593. defer testPhase.End()
  594. var wg sync.WaitGroup
  595. wg.Add(len(configs))
  596. for _, config := range configs {
  597. go scaleResource(&wg, config, scalingTime)
  598. }
  599. wg.Wait()
  600. }
  601. // Scales RC to a random size within [0.5*size, 1.5*size] and lists all the pods afterwards.
  602. // Scaling happens always based on original size, not the current size.
  603. func scaleResource(wg *sync.WaitGroup, config testutils.RunObjectConfig, scalingTime time.Duration) {
  604. defer GinkgoRecover()
  605. defer wg.Done()
  606. sleepUpTo(scalingTime)
  607. newSize := uint(rand.Intn(config.GetReplicas()) + config.GetReplicas()/2)
  608. framework.ExpectNoError(framework.ScaleResource(
  609. config.GetClient(),
  610. config.GetScalesGetter(),
  611. config.GetNamespace(),
  612. config.GetName(),
  613. newSize,
  614. true,
  615. config.GetKind(),
  616. config.GetGroupResource(),
  617. ),
  618. fmt.Sprintf("scaling %v %v", config.GetKind(), config.GetName()))
  619. selector := labels.SelectorFromSet(labels.Set(map[string]string{"name": config.GetName()}))
  620. options := metav1.ListOptions{
  621. LabelSelector: selector.String(),
  622. ResourceVersion: "0",
  623. }
  624. listResourcePodsFunc := func() (bool, error) {
  625. _, err := config.GetClient().CoreV1().Pods(config.GetNamespace()).List(options)
  626. if err == nil {
  627. return true, nil
  628. }
  629. e2elog.Logf("Failed to list pods from %v %v due to: %v", config.GetKind(), config.GetName(), err)
  630. if testutils.IsRetryableAPIError(err) {
  631. return false, nil
  632. }
  633. return false, fmt.Errorf("Failed to list pods from %v %v with non-retriable error: %v", config.GetKind(), config.GetName(), err)
  634. }
  635. err := retryWithExponentialBackOff(100*time.Millisecond, listResourcePodsFunc)
  636. framework.ExpectNoError(err)
  637. }
  638. func deleteAllResources(configs []testutils.RunObjectConfig, deletingTime time.Duration, testPhase *timer.Phase) {
  639. defer testPhase.End()
  640. var wg sync.WaitGroup
  641. wg.Add(len(configs))
  642. for _, config := range configs {
  643. go deleteResource(&wg, config, deletingTime)
  644. }
  645. wg.Wait()
  646. }
  647. func deleteResource(wg *sync.WaitGroup, config testutils.RunObjectConfig, deletingTime time.Duration) {
  648. defer GinkgoRecover()
  649. defer wg.Done()
  650. sleepUpTo(deletingTime)
  651. framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(
  652. config.GetClient(), config.GetKind(), config.GetNamespace(), config.GetName()),
  653. fmt.Sprintf("deleting %v %s", config.GetKind(), config.GetName()))
  654. }
  655. func CreateNamespaces(f *framework.Framework, namespaceCount int, namePrefix string, testPhase *timer.Phase) ([]*v1.Namespace, error) {
  656. defer testPhase.End()
  657. namespaces := []*v1.Namespace{}
  658. for i := 1; i <= namespaceCount; i++ {
  659. namespace, err := f.CreateNamespace(fmt.Sprintf("%v-%d", namePrefix, i), nil)
  660. if err != nil {
  661. return []*v1.Namespace{}, err
  662. }
  663. namespaces = append(namespaces, namespace)
  664. }
  665. return namespaces, nil
  666. }
  667. func CreateQuotas(f *framework.Framework, namespaces []*v1.Namespace, podCount int, testPhase *timer.Phase) error {
  668. defer testPhase.End()
  669. quotaTemplate := &v1.ResourceQuota{
  670. Spec: v1.ResourceQuotaSpec{
  671. Hard: v1.ResourceList{"pods": *resource.NewQuantity(int64(podCount), resource.DecimalSI)},
  672. },
  673. }
  674. for _, ns := range namespaces {
  675. quotaTemplate.Name = ns.Name + "-quota"
  676. if err := testutils.CreateResourceQuotaWithRetries(f.ClientSet, ns.Name, quotaTemplate); err != nil {
  677. return fmt.Errorf("Error creating quota: %v", err)
  678. }
  679. }
  680. return nil
  681. }