1
16
17 package apimachinery
18
19 import (
20 "context"
21 "encoding/json"
22 "fmt"
23 "sync/atomic"
24 "time"
25
26 appsv1 "k8s.io/api/apps/v1"
27 batchv1 "k8s.io/api/batch/v1"
28 v1 "k8s.io/api/core/v1"
29 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
30 apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
31 apiextensionstestserver "k8s.io/apiextensions-apiserver/test/integration/fixtures"
32 apierrors "k8s.io/apimachinery/pkg/api/errors"
33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
35 "k8s.io/apimachinery/pkg/runtime/schema"
36 "k8s.io/apimachinery/pkg/types"
37 utilerrors "k8s.io/apimachinery/pkg/util/errors"
38 "k8s.io/apimachinery/pkg/util/wait"
39 "k8s.io/apiserver/pkg/storage/names"
40 clientset "k8s.io/client-go/kubernetes"
41 clientv1 "k8s.io/client-go/kubernetes/typed/core/v1"
42 "k8s.io/kubernetes/test/e2e/framework"
43 e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment"
44 e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
45 e2enode "k8s.io/kubernetes/test/e2e/framework/node"
46 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
47 imageutils "k8s.io/kubernetes/test/utils/image"
48 admissionapi "k8s.io/pod-security-admission/api"
49
50 "github.com/onsi/ginkgo/v2"
51 "github.com/onsi/gomega"
52 )
53
54
55
56
57 func estimateMaximumPods(ctx context.Context, c clientset.Interface, min, max int32) int32 {
58 nodes, err := e2enode.GetReadySchedulableNodes(ctx, c)
59 framework.ExpectNoError(err)
60
61 availablePods := int32(0)
62 for _, node := range nodes.Items {
63 if q, ok := node.Status.Allocatable["pods"]; ok {
64 if num, ok := q.AsInt64(); ok {
65 availablePods += int32(num)
66 continue
67 }
68 }
69
70
71 availablePods += 10
72 }
73
74 availablePods = int32(float32(availablePods) * 0.5)
75
76 if availablePods > max {
77 availablePods = max
78 }
79 if availablePods < min {
80 availablePods = min
81 }
82 return availablePods
83 }
84
85 func getForegroundOptions() metav1.DeleteOptions {
86 policy := metav1.DeletePropagationForeground
87 return metav1.DeleteOptions{PropagationPolicy: &policy}
88 }
89
90 func getBackgroundOptions() metav1.DeleteOptions {
91 policy := metav1.DeletePropagationBackground
92 return metav1.DeleteOptions{PropagationPolicy: &policy}
93 }
94
95 func getOrphanOptions() metav1.DeleteOptions {
96 policy := metav1.DeletePropagationOrphan
97 return metav1.DeleteOptions{PropagationPolicy: &policy}
98 }
99
100 var (
101 zero = int64(0)
102 lablecount = int64(0)
103 )
104
105 const (
106
107
108
109
110
111
112
113
114 gcInformerResyncRetryTimeout = time.Minute
115
116
117
118
119
120 replicaSyncTimeout = 2 * time.Minute
121 )
122
123 func getPodTemplateSpec(labels map[string]string) v1.PodTemplateSpec {
124 return v1.PodTemplateSpec{
125 ObjectMeta: metav1.ObjectMeta{
126 Labels: labels,
127 },
128 Spec: v1.PodSpec{
129 TerminationGracePeriodSeconds: &zero,
130 Containers: []v1.Container{
131 {
132 Name: "nginx",
133 Image: imageutils.GetE2EImage(imageutils.Nginx),
134 },
135 },
136 },
137 }
138 }
139
140 func newOwnerDeployment(f *framework.Framework, deploymentName string, labels map[string]string) *appsv1.Deployment {
141 return e2edeployment.NewDeployment(deploymentName, 2, labels, "nginx", imageutils.GetE2EImage(imageutils.Nginx), appsv1.RollingUpdateDeploymentStrategyType)
142 }
143
144 func newOwnerRC(f *framework.Framework, name string, replicas int32, labels map[string]string) *v1.ReplicationController {
145 template := getPodTemplateSpec(labels)
146 return &v1.ReplicationController{
147 TypeMeta: metav1.TypeMeta{
148 Kind: "ReplicationController",
149 APIVersion: "v1",
150 },
151 ObjectMeta: metav1.ObjectMeta{
152 Namespace: f.Namespace.Name,
153 Name: name,
154 },
155 Spec: v1.ReplicationControllerSpec{
156 Replicas: &replicas,
157 Selector: labels,
158 Template: &template,
159 },
160 }
161 }
162
163 func newGCPod(name string) *v1.Pod {
164 return &v1.Pod{
165 TypeMeta: metav1.TypeMeta{
166 Kind: "Pod",
167 APIVersion: "v1",
168 },
169 ObjectMeta: metav1.ObjectMeta{
170 Name: name,
171 },
172 Spec: v1.PodSpec{
173 TerminationGracePeriodSeconds: new(int64),
174 Containers: []v1.Container{
175 {
176 Name: "nginx",
177 Image: imageutils.GetE2EImage(imageutils.Nginx),
178 },
179 },
180 },
181 }
182 }
183
184
185
186 func verifyRemainingObjects(ctx context.Context, f *framework.Framework, objects map[string]int) (bool, error) {
187 var ret = true
188
189 for object, num := range objects {
190 switch object {
191 case "Pods":
192 pods, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).List(ctx, metav1.ListOptions{})
193 if err != nil {
194 return false, fmt.Errorf("failed to list pods: %w", err)
195 }
196 if len(pods.Items) != num {
197 ret = false
198 ginkgo.By(fmt.Sprintf("expected %d pods, got %d pods", num, len(pods.Items)))
199 }
200 case "Deployments":
201 deployments, err := f.ClientSet.AppsV1().Deployments(f.Namespace.Name).List(ctx, metav1.ListOptions{})
202 if err != nil {
203 return false, fmt.Errorf("failed to list deployments: %w", err)
204 }
205 if len(deployments.Items) != num {
206 ret = false
207 ginkgo.By(fmt.Sprintf("expected %d Deployments, got %d Deployments", num, len(deployments.Items)))
208 }
209 case "ReplicaSets":
210 rs, err := f.ClientSet.AppsV1().ReplicaSets(f.Namespace.Name).List(ctx, metav1.ListOptions{})
211 if err != nil {
212 return false, fmt.Errorf("failed to list rs: %w", err)
213 }
214 if len(rs.Items) != num {
215 ret = false
216 ginkgo.By(fmt.Sprintf("expected %d rs, got %d rs", num, len(rs.Items)))
217 }
218 case "ReplicationControllers":
219 rcs, err := f.ClientSet.CoreV1().ReplicationControllers(f.Namespace.Name).List(ctx, metav1.ListOptions{})
220 if err != nil {
221 return false, fmt.Errorf("failed to list replication controllers: %w", err)
222 }
223 if len(rcs.Items) != num {
224 ret = false
225 ginkgo.By(fmt.Sprintf("expected %d RCs, got %d RCs", num, len(rcs.Items)))
226 }
227 case "CronJobs":
228 cronJobs, err := f.ClientSet.BatchV1().CronJobs(f.Namespace.Name).List(ctx, metav1.ListOptions{})
229 if err != nil {
230 return false, fmt.Errorf("failed to list cronjobs: %w", err)
231 }
232 if len(cronJobs.Items) != num {
233 ret = false
234 ginkgo.By(fmt.Sprintf("expected %d cronjobs, got %d cronjobs", num, len(cronJobs.Items)))
235 }
236 case "Jobs":
237 jobs, err := f.ClientSet.BatchV1().Jobs(f.Namespace.Name).List(ctx, metav1.ListOptions{})
238 if err != nil {
239 return false, fmt.Errorf("failed to list jobs: %w", err)
240 }
241 if len(jobs.Items) != num {
242 ret = false
243 ginkgo.By(fmt.Sprintf("expected %d jobs, got %d jobs", num, len(jobs.Items)))
244 }
245 default:
246 return false, fmt.Errorf("object %s is not supported", object)
247 }
248 }
249
250 return ret, nil
251 }
252
253 func gatherMetrics(ctx context.Context, f *framework.Framework) {
254 ginkgo.By("Gathering metrics")
255 var summary framework.TestDataSummary
256 grabber, err := e2emetrics.NewMetricsGrabber(ctx, f.ClientSet, f.KubemarkExternalClusterClientSet, f.ClientConfig(), false, false, true, false, false, false)
257 if err != nil {
258 framework.Logf("Failed to create MetricsGrabber. Skipping metrics gathering.")
259 } else {
260 received, err := grabber.Grab(ctx)
261 if err != nil {
262 framework.Logf("MetricsGrabber failed grab metrics. Skipping metrics gathering.")
263 } else {
264 summary = (*e2emetrics.ComponentCollection)(&received)
265 framework.Logf(summary.PrintHumanReadable())
266 }
267 }
268 }
269
270 func newCronJob(name, schedule string) *batchv1.CronJob {
271 parallelism := int32(1)
272 completions := int32(1)
273 return &batchv1.CronJob{
274 ObjectMeta: metav1.ObjectMeta{
275 Name: name,
276 },
277 TypeMeta: metav1.TypeMeta{
278 Kind: "CronJob",
279 },
280 Spec: batchv1.CronJobSpec{
281 Schedule: schedule,
282 JobTemplate: batchv1.JobTemplateSpec{
283 Spec: batchv1.JobSpec{
284 Parallelism: ¶llelism,
285 Completions: &completions,
286 Template: v1.PodTemplateSpec{
287 Spec: v1.PodSpec{
288 RestartPolicy: v1.RestartPolicyOnFailure,
289 TerminationGracePeriodSeconds: &zero,
290 Containers: []v1.Container{
291 {
292 Name: "c",
293 Image: imageutils.GetE2EImage(imageutils.BusyBox),
294 Command: []string{"sleep", "300"},
295 },
296 },
297 },
298 },
299 },
300 },
301 },
302 }
303 }
304
305
306 func getUniqLabel(labelkey, labelvalue string) map[string]string {
307 count := atomic.AddInt64(&lablecount, 1)
308 uniqlabelkey := fmt.Sprintf("%s-%05d", labelkey, count)
309 uniqlabelvalue := fmt.Sprintf("%s-%05d", labelvalue, count)
310 return map[string]string{uniqlabelkey: uniqlabelvalue}
311 }
312
313 var _ = SIGDescribe("Garbage collector", func() {
314 f := framework.NewDefaultFramework("gc")
315 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
316
317
322 framework.ConformanceIt("should delete pods created by rc when not orphaning", func(ctx context.Context) {
323 clientSet := f.ClientSet
324 rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
325 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
326 rcName := "simpletest.rc"
327 uniqLabels := getUniqLabel("gctest", "delete_pods")
328 rc := newOwnerRC(f, rcName, 2, uniqLabels)
329 ginkgo.By("create the rc")
330 rc, err := rcClient.Create(ctx, rc, metav1.CreateOptions{})
331 if err != nil {
332 framework.Failf("Failed to create replication controller: %v", err)
333 }
334
335 if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
336 pods, err := podClient.List(ctx, metav1.ListOptions{})
337 if err != nil {
338 return false, fmt.Errorf("failed to list pods: %w", err)
339 }
340
341
342
343
344 if len(pods.Items) > 0 {
345 return true, nil
346 }
347 return false, nil
348
349 }); err != nil {
350 framework.Failf("failed to wait for the rc to create some pods: %v", err)
351 }
352 ginkgo.By("delete the rc")
353 deleteOptions := getBackgroundOptions()
354 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
355 if err := rcClient.Delete(ctx, rc.ObjectMeta.Name, deleteOptions); err != nil {
356 framework.Failf("failed to delete the rc: %v", err)
357 }
358 ginkgo.By("wait for all pods to be garbage collected")
359
360 if err := wait.PollWithContext(ctx, 5*time.Second, (60*time.Second)+gcInformerResyncRetryTimeout, func(ctx context.Context) (bool, error) {
361 objects := map[string]int{"ReplicationControllers": 0, "Pods": 0}
362 return verifyRemainingObjects(ctx, f, objects)
363 }); err != nil {
364 framework.Failf("failed to wait for all pods to be deleted: %v", err)
365 remainingPods, err := podClient.List(ctx, metav1.ListOptions{})
366 if err != nil {
367 framework.Failf("failed to list pods post mortem: %v", err)
368 } else {
369 framework.Failf("remaining pods are: %#v", remainingPods)
370 }
371 }
372 gatherMetrics(ctx, f)
373 })
374
375
380 framework.ConformanceIt("should orphan pods created by rc if delete options say so", func(ctx context.Context) {
381 clientSet := f.ClientSet
382 rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
383 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
384 rcName := "simpletest.rc"
385 uniqLabels := getUniqLabel("gctest", "orphan_pods")
386 rc := newOwnerRC(f, rcName, estimateMaximumPods(ctx, clientSet, 10, 100), uniqLabels)
387 ginkgo.By("create the rc")
388 rc, err := rcClient.Create(ctx, rc, metav1.CreateOptions{})
389 if err != nil {
390 framework.Failf("Failed to create replication controller: %v", err)
391 }
392
393 waitForReplicas(ctx, rc, rcClient)
394
395 ginkgo.By("delete the rc")
396 deleteOptions := getOrphanOptions()
397 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
398 if err := rcClient.Delete(ctx, rc.ObjectMeta.Name, deleteOptions); err != nil {
399 framework.Failf("failed to delete the rc: %v", err)
400 }
401 ginkgo.By("wait for the rc to be deleted")
402
403
404
405
406
407
408
409 if err := wait.PollWithContext(ctx, 5*time.Second, 120*time.Second+gcInformerResyncRetryTimeout, func(ctx context.Context) (bool, error) {
410 rcs, err := rcClient.List(ctx, metav1.ListOptions{})
411 if err != nil {
412 return false, fmt.Errorf("failed to list rcs: %w", err)
413 }
414 if len(rcs.Items) != 0 {
415 return false, nil
416 }
417 return true, nil
418 }); err != nil {
419 framework.Failf("%v", err)
420 }
421 ginkgo.By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods")
422 time.Sleep(30 * time.Second)
423 pods, err := podClient.List(ctx, metav1.ListOptions{})
424 if err != nil {
425 framework.Failf("Failed to list pods: %v", err)
426 }
427 if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a {
428 framework.Failf("expect %d pods, got %d pods", e, a)
429 }
430 gatherMetrics(ctx, f)
431 if err = e2epod.DeletePodsWithGracePeriod(ctx, clientSet, pods.Items, 0); err != nil {
432 framework.Logf("WARNING: failed to delete pods: %v", err)
433 }
434 })
435
436
437
438 ginkgo.It("should orphan pods created by rc if deleteOptions.OrphanDependents is nil", func(ctx context.Context) {
439 clientSet := f.ClientSet
440 rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
441 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
442 rcName := "simpletest.rc"
443 uniqLabels := getUniqLabel("gctest", "orphan_pods_nil_option")
444 rc := newOwnerRC(f, rcName, 2, uniqLabels)
445 ginkgo.By("create the rc")
446 rc, err := rcClient.Create(ctx, rc, metav1.CreateOptions{})
447 if err != nil {
448 framework.Failf("Failed to create replication controller: %v", err)
449 }
450
451 waitForReplicas(ctx, rc, rcClient)
452
453 ginkgo.By("delete the rc")
454 deleteOptions := metav1.DeleteOptions{
455 Preconditions: metav1.NewUIDPreconditions(string(rc.UID)),
456 }
457 if err := rcClient.Delete(ctx, rc.ObjectMeta.Name, deleteOptions); err != nil {
458 framework.Failf("failed to delete the rc: %v", err)
459 }
460 ginkgo.By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods")
461 time.Sleep(30 * time.Second)
462 pods, err := podClient.List(ctx, metav1.ListOptions{})
463 if err != nil {
464 framework.Failf("Failed to list pods: %v", err)
465 }
466 if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a {
467 framework.Failf("expect %d pods, got %d pods", e, a)
468 }
469 gatherMetrics(ctx, f)
470 if err = e2epod.DeletePodsWithGracePeriod(ctx, clientSet, pods.Items, 0); err != nil {
471 framework.Logf("WARNING: failed to delete pods: %v", err)
472 }
473 })
474
475
480 framework.ConformanceIt("should delete RS created by deployment when not orphaning", func(ctx context.Context) {
481 clientSet := f.ClientSet
482 deployClient := clientSet.AppsV1().Deployments(f.Namespace.Name)
483 rsClient := clientSet.AppsV1().ReplicaSets(f.Namespace.Name)
484 deploymentName := "simpletest.deployment"
485 uniqLabels := getUniqLabel("gctest", "delete_rs")
486 deployment := newOwnerDeployment(f, deploymentName, uniqLabels)
487 ginkgo.By("create the deployment")
488 createdDeployment, err := deployClient.Create(ctx, deployment, metav1.CreateOptions{})
489 if err != nil {
490 framework.Failf("Failed to create deployment: %v", err)
491 }
492
493 ginkgo.By("Wait for the Deployment to create new ReplicaSet")
494 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
495 rsList, err := rsClient.List(ctx, metav1.ListOptions{})
496 if err != nil {
497 return false, fmt.Errorf("failed to list rs: %w", err)
498 }
499 return len(rsList.Items) > 0, nil
500
501 })
502 if err != nil {
503 framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err)
504 }
505
506 ginkgo.By("delete the deployment")
507 deleteOptions := getBackgroundOptions()
508 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(createdDeployment.UID))
509 if err := deployClient.Delete(ctx, deployment.ObjectMeta.Name, deleteOptions); err != nil {
510 framework.Failf("failed to delete the deployment: %v", err)
511 }
512 ginkgo.By("wait for all rs to be garbage collected")
513 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 1*time.Minute+gcInformerResyncRetryTimeout, true, func(ctx context.Context) (bool, error) {
514 objects := map[string]int{"Deployments": 0, "ReplicaSets": 0, "Pods": 0}
515 return verifyRemainingObjects(ctx, f, objects)
516 })
517 if err != nil {
518 errList := make([]error, 0)
519 errList = append(errList, err)
520 remainingRSs, err := rsClient.List(ctx, metav1.ListOptions{})
521 if err != nil {
522 errList = append(errList, fmt.Errorf("failed to list RSs post mortem: %w", err))
523 } else {
524 errList = append(errList, fmt.Errorf("remaining rs are: %#v", remainingRSs))
525 }
526 aggregatedError := utilerrors.NewAggregate(errList)
527 framework.Failf("Failed to wait for all rs to be garbage collected: %v", aggregatedError)
528
529 }
530
531 gatherMetrics(ctx, f)
532 })
533
534
539 framework.ConformanceIt("should orphan RS created by deployment when deleteOptions.PropagationPolicy is Orphan", func(ctx context.Context) {
540 clientSet := f.ClientSet
541 deployClient := clientSet.AppsV1().Deployments(f.Namespace.Name)
542 rsClient := clientSet.AppsV1().ReplicaSets(f.Namespace.Name)
543 deploymentName := "simpletest.deployment"
544 uniqLabels := getUniqLabel("gctest", "orphan_rs")
545 deployment := newOwnerDeployment(f, deploymentName, uniqLabels)
546 ginkgo.By("create the deployment")
547 createdDeployment, err := deployClient.Create(ctx, deployment, metav1.CreateOptions{})
548 if err != nil {
549 framework.Failf("Failed to create deployment: %v", err)
550 }
551
552 ginkgo.By("Wait for the Deployment to create new ReplicaSet")
553 var replicaset appsv1.ReplicaSet
554 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
555 rsList, err := rsClient.List(ctx, metav1.ListOptions{})
556 if err != nil {
557 return false, fmt.Errorf("failed to list rs: %w", err)
558 }
559 if len(rsList.Items) > 0 {
560 replicaset = rsList.Items[0]
561 return true, nil
562 }
563 return false, nil
564
565 })
566 if err != nil {
567 framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err)
568 }
569
570 desiredGeneration := replicaset.Generation
571 if err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 60*time.Second, true, func(ctx context.Context) (bool, error) {
572 newRS, err := clientSet.AppsV1().ReplicaSets(replicaset.Namespace).Get(ctx, replicaset.Name, metav1.GetOptions{})
573 if err != nil {
574 return false, err
575 }
576 return newRS.Status.ObservedGeneration >= desiredGeneration && newRS.Status.Replicas == *replicaset.Spec.Replicas, nil
577 }); err != nil {
578 framework.Failf("failed to verify .Status.Replicas is equal to .Spec.Replicas for replicaset %q: %v", replicaset.Name, err)
579 }
580
581 ginkgo.By("delete the deployment")
582 deleteOptions := getOrphanOptions()
583 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(createdDeployment.UID))
584 if err := deployClient.Delete(ctx, deployment.ObjectMeta.Name, deleteOptions); err != nil {
585 framework.Failf("failed to delete the deployment: %v", err)
586 }
587 ginkgo.By("wait for deployment deletion to see if the garbage collector mistakenly deletes the rs")
588 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 1*time.Minute+gcInformerResyncRetryTimeout, true, func(ctx context.Context) (bool, error) {
589 dList, err := deployClient.List(ctx, metav1.ListOptions{})
590 if err != nil {
591 return false, fmt.Errorf("failed to list deployments: %w", err)
592 }
593 return len(dList.Items) == 0, nil
594 })
595 if err != nil {
596 framework.Failf("Failed to wait for the Deployment to be deleted: %v", err)
597 }
598
599 objects := map[string]int{"Deployments": 0, "ReplicaSets": 1, "Pods": 2}
600 ok, err := verifyRemainingObjects(ctx, f, objects)
601 if err != nil {
602 framework.Failf("Unexpected error while verifying remaining deployments, rs, and pods: %v", err)
603 }
604 if !ok {
605 errList := make([]error, 0)
606 remainingRSs, err := rsClient.List(ctx, metav1.ListOptions{})
607 if err != nil {
608 errList = append(errList, fmt.Errorf("failed to list RSs post mortem: %w", err))
609 } else {
610 errList = append(errList, fmt.Errorf("remaining rs post mortem: %#v", remainingRSs))
611 }
612 remainingDSs, err := deployClient.List(ctx, metav1.ListOptions{})
613 if err != nil {
614 errList = append(errList, fmt.Errorf("failed to list Deployments post mortem: %w", err))
615 } else {
616 errList = append(errList, fmt.Errorf("remaining deployment's post mortem: %#v", remainingDSs))
617 }
618 aggregatedError := utilerrors.NewAggregate(errList)
619 framework.Failf("Failed to verify remaining deployments, rs, and pods: %v", aggregatedError)
620 }
621 rs, err := clientSet.AppsV1().ReplicaSets(f.Namespace.Name).List(ctx, metav1.ListOptions{})
622 if err != nil {
623 framework.Failf("Failed to list ReplicaSet %v", err)
624 }
625 for _, replicaSet := range rs.Items {
626 if metav1.GetControllerOf(&replicaSet.ObjectMeta) != nil {
627 framework.Failf("Found ReplicaSet with non nil ownerRef %v", replicaSet)
628 }
629 }
630
631 gatherMetrics(ctx, f)
632 })
633
634
639 framework.ConformanceIt("should keep the rc around until all its pods are deleted if the deleteOptions says so", func(ctx context.Context) {
640 clientSet := f.ClientSet
641 rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
642 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
643 rcName := "simpletest.rc"
644 uniqLabels := getUniqLabel("gctest", "delete_pods_foreground")
645 rc := newOwnerRC(f, rcName, estimateMaximumPods(ctx, clientSet, 10, 100), uniqLabels)
646 ginkgo.By("create the rc")
647 rc, err := rcClient.Create(ctx, rc, metav1.CreateOptions{})
648 if err != nil {
649 framework.Failf("Failed to create replication controller: %v", err)
650 }
651
652 waitForReplicas(ctx, rc, rcClient)
653
654 ginkgo.By("delete the rc")
655 deleteOptions := getForegroundOptions()
656 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
657 if err := rcClient.Delete(ctx, rc.ObjectMeta.Name, deleteOptions); err != nil {
658 framework.Failf("failed to delete the rc: %v", err)
659 }
660 ginkgo.By("wait for the rc to be deleted")
661
662
663
664
665
666 if err := wait.PollWithContext(ctx, 1*time.Second, 30*time.Second+gcInformerResyncRetryTimeout, func(ctx context.Context) (bool, error) {
667 _, err := rcClient.Get(ctx, rc.Name, metav1.GetOptions{})
668 if err == nil {
669 pods, _ := podClient.List(ctx, metav1.ListOptions{})
670 framework.Logf("%d pods remaining", len(pods.Items))
671 count := 0
672 for _, pod := range pods.Items {
673 if pod.ObjectMeta.DeletionTimestamp == nil {
674 count++
675 }
676 }
677 framework.Logf("%d pods has nil DeletionTimestamp", count)
678 framework.Logf("")
679 return false, nil
680 }
681 if apierrors.IsNotFound(err) {
682 return true, nil
683 }
684 return false, err
685 }); err != nil {
686 pods, err2 := podClient.List(ctx, metav1.ListOptions{})
687 if err2 != nil {
688 framework.Failf("%v", err2)
689 }
690 framework.Logf("%d remaining pods are:", len(pods.Items))
691 framework.Logf("The ObjectMeta of the remaining pods are:")
692 for _, pod := range pods.Items {
693 framework.Logf("%#v", pod.ObjectMeta)
694 }
695 framework.Failf("failed to delete the rc: %v", err)
696 }
697
698 pods, err := podClient.List(ctx, metav1.ListOptions{})
699 if err != nil {
700 framework.Failf("%v", err)
701 }
702 if len(pods.Items) != 0 {
703 framework.Failf("expected no pods, got %#v", pods)
704 }
705 gatherMetrics(ctx, f)
706 })
707
708
709
714 framework.ConformanceIt("should not delete dependents that have both valid owner and owner that's waiting for dependents to be deleted", func(ctx context.Context) {
715 clientSet := f.ClientSet
716 rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
717 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
718 rc1Name := "simpletest-rc-to-be-deleted"
719 replicas := estimateMaximumPods(ctx, clientSet, 10, 100)
720 halfReplicas := int(replicas / 2)
721 uniqLabelsDeleted := getUniqLabel("gctest_d", "valid_and_pending_owners_d")
722 rc1 := newOwnerRC(f, rc1Name, replicas, uniqLabelsDeleted)
723 ginkgo.By("create the rc1")
724 rc1, err := rcClient.Create(ctx, rc1, metav1.CreateOptions{})
725 if err != nil {
726 framework.Failf("Failed to create replication controller: %v", err)
727 }
728 rc2Name := "simpletest-rc-to-stay"
729 uniqLabelsStay := getUniqLabel("gctest_s", "valid_and_pending_owners_s")
730 rc2 := newOwnerRC(f, rc2Name, 0, uniqLabelsStay)
731 ginkgo.By("create the rc2")
732 rc2, err = rcClient.Create(ctx, rc2, metav1.CreateOptions{})
733 if err != nil {
734 framework.Failf("Failed to create replication controller: %v", err)
735 }
736
737 waitForReplicas(ctx, rc1, rcClient)
738
739 ginkgo.By(fmt.Sprintf("set half of pods created by rc %s to have rc %s as owner as well", rc1Name, rc2Name))
740 pods, err := podClient.List(ctx, metav1.ListOptions{})
741 framework.ExpectNoError(err, "failed to list pods in namespace: %s", f.Namespace.Name)
742 patch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"apiVersion":"v1","kind":"ReplicationController","name":"%s","uid":"%s"}]}}`, rc2.ObjectMeta.Name, rc2.ObjectMeta.UID)
743 for i := 0; i < halfReplicas; i++ {
744 pod := pods.Items[i]
745 _, err := podClient.Patch(ctx, pod.Name, types.StrategicMergePatchType, []byte(patch), metav1.PatchOptions{})
746 framework.ExpectNoError(err, "failed to apply to pod %s in namespace %s, a strategic merge patch: %s", pod.Name, f.Namespace.Name, patch)
747 }
748
749 ginkgo.By(fmt.Sprintf("delete the rc %s", rc1Name))
750 deleteOptions := getForegroundOptions()
751 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc1.UID))
752 if err := rcClient.Delete(ctx, rc1.ObjectMeta.Name, deleteOptions); err != nil {
753 framework.Failf("failed to delete the rc: %v", err)
754 }
755 ginkgo.By("wait for the rc to be deleted")
756
757
758 if err := wait.PollWithContext(ctx, 5*time.Second, 90*time.Second, func(ctx context.Context) (bool, error) {
759 _, err := rcClient.Get(ctx, rc1.Name, metav1.GetOptions{})
760 if err == nil {
761 pods, _ := podClient.List(ctx, metav1.ListOptions{})
762 framework.Logf("%d pods remaining", len(pods.Items))
763 count := 0
764 for _, pod := range pods.Items {
765 if pod.ObjectMeta.DeletionTimestamp == nil {
766 count++
767 }
768 }
769 framework.Logf("%d pods has nil DeletionTimestamp", count)
770 framework.Logf("")
771 return false, nil
772 }
773 if apierrors.IsNotFound(err) {
774 return true, nil
775 }
776 return false, err
777 }); err != nil {
778 pods, err2 := podClient.List(ctx, metav1.ListOptions{})
779 if err2 != nil {
780 framework.Failf("%v", err2)
781 }
782 framework.Logf("%d remaining pods are:", len(pods.Items))
783 framework.Logf("ObjectMeta of remaining pods are:")
784 for _, pod := range pods.Items {
785 framework.Logf("%#v", pod.ObjectMeta)
786 }
787 framework.Failf("failed to delete rc %s, err: %v", rc1Name, err)
788 }
789
790 pods, err = podClient.List(ctx, metav1.ListOptions{})
791 if err != nil {
792 framework.Failf("%v", err)
793 }
794 if len(pods.Items) != halfReplicas {
795 framework.Failf("expected %d pods, got %d", halfReplicas, len(pods.Items))
796 }
797 for _, pod := range pods.Items {
798 if pod.ObjectMeta.DeletionTimestamp != nil {
799 framework.Failf("expected pod DeletionTimestamp to be nil, got %#v", pod.ObjectMeta)
800 }
801
802 if len(pod.ObjectMeta.OwnerReferences) != 1 {
803 framework.Failf("expected pod to only have 1 owner, got %#v", pod.ObjectMeta.OwnerReferences)
804 }
805 }
806 gatherMetrics(ctx, f)
807 if err = e2epod.DeletePodsWithGracePeriod(ctx, clientSet, pods.Items, 0); err != nil {
808 framework.Logf("WARNING: failed to delete pods: %v", err)
809 }
810 })
811
812
813
818 framework.ConformanceIt("should not be blocked by dependency circle", func(ctx context.Context) {
819 clientSet := f.ClientSet
820 podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
821 pod1Name := "pod1"
822 pod1 := newGCPod(pod1Name)
823 pod1, err := podClient.Create(ctx, pod1, metav1.CreateOptions{})
824 framework.ExpectNoError(err, "failed to create pod %s in namespace: %s", pod1Name, f.Namespace.Name)
825 pod2Name := "pod2"
826 pod2 := newGCPod(pod2Name)
827 pod2, err = podClient.Create(ctx, pod2, metav1.CreateOptions{})
828 framework.ExpectNoError(err, "failed to create pod %s in namespace: %s", pod2Name, f.Namespace.Name)
829 pod3Name := "pod3"
830 pod3 := newGCPod(pod3Name)
831 pod3, err = podClient.Create(ctx, pod3, metav1.CreateOptions{})
832 framework.ExpectNoError(err, "failed to create pod %s in namespace: %s", pod3Name, f.Namespace.Name)
833
834 addRefPatch := func(name string, uid types.UID) []byte {
835 return []byte(fmt.Sprintf(`{"metadata":{"ownerReferences":[{"apiVersion":"v1","kind":"Pod","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}]}}`, name, uid))
836 }
837 patch1 := addRefPatch(pod3.Name, pod3.UID)
838 pod1, err = podClient.Patch(ctx, pod1.Name, types.StrategicMergePatchType, patch1, metav1.PatchOptions{})
839 framework.ExpectNoError(err, "failed to apply to pod %s in namespace %s, a strategic merge patch: %s", pod1.Name, f.Namespace.Name, patch1)
840 framework.Logf("pod1.ObjectMeta.OwnerReferences=%#v", pod1.ObjectMeta.OwnerReferences)
841 patch2 := addRefPatch(pod1.Name, pod1.UID)
842 pod2, err = podClient.Patch(ctx, pod2.Name, types.StrategicMergePatchType, patch2, metav1.PatchOptions{})
843 framework.ExpectNoError(err, "failed to apply to pod %s in namespace %s, a strategic merge patch: %s", pod2.Name, f.Namespace.Name, patch2)
844 framework.Logf("pod2.ObjectMeta.OwnerReferences=%#v", pod2.ObjectMeta.OwnerReferences)
845 patch3 := addRefPatch(pod2.Name, pod2.UID)
846 pod3, err = podClient.Patch(ctx, pod3.Name, types.StrategicMergePatchType, patch3, metav1.PatchOptions{})
847 framework.ExpectNoError(err, "failed to apply to pod %s in namespace %s, a strategic merge patch: %s", pod3.Name, f.Namespace.Name, patch3)
848 framework.Logf("pod3.ObjectMeta.OwnerReferences=%#v", pod3.ObjectMeta.OwnerReferences)
849
850 deleteOptions := getForegroundOptions()
851 deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(pod1.UID))
852 err = podClient.Delete(ctx, pod1.ObjectMeta.Name, deleteOptions)
853 framework.ExpectNoError(err, "failed to delete pod %s in namespace: %s", pod1.Name, f.Namespace.Name)
854 var pods *v1.PodList
855 var err2 error
856
857
858 if err := wait.PollWithContext(ctx, 5*time.Second, 90*time.Second+gcInformerResyncRetryTimeout, func(ctx context.Context) (bool, error) {
859 pods, err2 = podClient.List(ctx, metav1.ListOptions{})
860 if err2 != nil {
861 return false, fmt.Errorf("failed to list pods: %w", err)
862 }
863 if len(pods.Items) == 0 {
864 return true, nil
865 }
866 return false, nil
867 }); err != nil {
868 data, _ := json.Marshal(pods.Items)
869 framework.Logf("pods are %s", string(data))
870 framework.Failf("failed to wait for all pods to be deleted: %v", err)
871 }
872 })
873
874 ginkgo.It("should support cascading deletion of custom resources", func(ctx context.Context) {
875 config, err := framework.LoadConfig()
876 if err != nil {
877 framework.Failf("failed to load config: %v", err)
878 }
879
880 apiExtensionClient, err := apiextensionsclientset.NewForConfig(config)
881 if err != nil {
882 framework.Failf("failed to initialize apiExtensionClient: %v", err)
883 }
884
885
886
887 definition := apiextensionstestserver.NewRandomNameV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
888 defer func() {
889 err = apiextensionstestserver.DeleteV1CustomResourceDefinition(definition, apiExtensionClient)
890 if err != nil && !apierrors.IsNotFound(err) {
891 framework.Failf("failed to delete CustomResourceDefinition: %v", err)
892 }
893 }()
894 definition, err = apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, f.DynamicClient)
895 if err != nil {
896 framework.Failf("failed to create CustomResourceDefinition: %v", err)
897 }
898 gomega.Expect(definition.Spec.Versions).To(gomega.HaveLen(1), "custom resource definition should have one version")
899 version := definition.Spec.Versions[0]
900
901
902 gvr := schema.GroupVersionResource{Group: definition.Spec.Group, Version: version.Name, Resource: definition.Spec.Names.Plural}
903 resourceClient := f.DynamicClient.Resource(gvr)
904
905 apiVersion := definition.Spec.Group + "/" + version.Name
906
907
908 ownerName := names.SimpleNameGenerator.GenerateName("owner")
909 owner := &unstructured.Unstructured{
910 Object: map[string]interface{}{
911 "apiVersion": apiVersion,
912 "kind": definition.Spec.Names.Kind,
913 "metadata": map[string]interface{}{
914 "name": ownerName,
915 },
916 },
917 }
918 persistedOwner, err := resourceClient.Create(ctx, owner, metav1.CreateOptions{})
919 if err != nil {
920 framework.Failf("failed to create owner resource %q: %v", ownerName, err)
921 }
922 framework.Logf("created owner resource %q", ownerName)
923
924
925 dependentName := names.SimpleNameGenerator.GenerateName("dependent")
926 dependent := &unstructured.Unstructured{
927 Object: map[string]interface{}{
928 "apiVersion": apiVersion,
929 "kind": definition.Spec.Names.Kind,
930 "metadata": map[string]interface{}{
931 "name": dependentName,
932 "ownerReferences": []interface{}{
933 map[string]interface{}{
934 "uid": string(persistedOwner.GetUID()),
935 "apiVersion": apiVersion,
936 "kind": definition.Spec.Names.Kind,
937 "name": ownerName,
938 },
939 },
940 },
941 },
942 }
943 persistedDependent, err := resourceClient.Create(ctx, dependent, metav1.CreateOptions{})
944 if err != nil {
945 framework.Failf("failed to create dependent resource %q: %v", dependentName, err)
946 }
947 framework.Logf("created dependent resource %q", dependentName)
948
949
950 background := metav1.DeletePropagationBackground
951 err = resourceClient.Delete(ctx, ownerName, metav1.DeleteOptions{PropagationPolicy: &background})
952 if err != nil {
953 framework.Failf("failed to delete owner resource %q: %v", ownerName, err)
954 }
955
956
957
958 canaryName := names.SimpleNameGenerator.GenerateName("canary")
959 canary := &unstructured.Unstructured{
960 Object: map[string]interface{}{
961 "apiVersion": apiVersion,
962 "kind": definition.Spec.Names.Kind,
963 "metadata": map[string]interface{}{"name": canaryName}},
964 }
965 _, err = resourceClient.Create(ctx, canary, metav1.CreateOptions{})
966 if err != nil {
967 framework.Failf("failed to create canary resource %q: %v", canaryName, err)
968 }
969 framework.Logf("created canary resource %q", canaryName)
970 foreground := metav1.DeletePropagationForeground
971 err = resourceClient.Delete(ctx, canaryName, metav1.DeleteOptions{PropagationPolicy: &foreground})
972 if err != nil {
973 framework.Failf("failed to delete canary resource %q: %v", canaryName, err)
974 }
975
976 var lastCanary *unstructured.Unstructured
977 if err := wait.PollUntilContextTimeout(ctx, 5*time.Second, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
978 lastCanary, err = resourceClient.Get(ctx, dependentName, metav1.GetOptions{})
979 return apierrors.IsNotFound(err), nil
980 }); err != nil {
981 framework.Logf("canary last state: %#v", lastCanary)
982 framework.Failf("failed waiting for canary resource %q to be deleted", canaryName)
983 }
984
985
986 var lastDependent *unstructured.Unstructured
987 var err2 error
988 if err := wait.PollWithContext(ctx, 5*time.Second, 60*time.Second, func(ctx context.Context) (bool, error) {
989 lastDependent, err2 = resourceClient.Get(ctx, dependentName, metav1.GetOptions{})
990 return apierrors.IsNotFound(err2), nil
991 }); err != nil {
992 framework.Logf("owner: %#v", persistedOwner)
993 framework.Logf("dependent: %#v", persistedDependent)
994 framework.Logf("dependent last state: %#v", lastDependent)
995 framework.Failf("failed waiting for dependent resource %q to be deleted", dependentName)
996 }
997
998
999 _, err = resourceClient.Get(ctx, ownerName, metav1.GetOptions{})
1000 if err == nil {
1001 framework.Failf("expected owner resource %q to be deleted", ownerName)
1002 } else {
1003 if !apierrors.IsNotFound(err) {
1004 framework.Failf("unexpected error getting owner resource %q: %v", ownerName, err)
1005 }
1006 }
1007 })
1008
1009 ginkgo.It("should support orphan deletion of custom resources", func(ctx context.Context) {
1010 config, err := framework.LoadConfig()
1011 if err != nil {
1012 framework.Failf("failed to load config: %v", err)
1013 }
1014
1015 apiExtensionClient, err := apiextensionsclientset.NewForConfig(config)
1016 if err != nil {
1017 framework.Failf("failed to initialize apiExtensionClient: %v", err)
1018 }
1019
1020
1021
1022 definition := apiextensionstestserver.NewRandomNameV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
1023 defer func() {
1024 err = apiextensionstestserver.DeleteV1CustomResourceDefinition(definition, apiExtensionClient)
1025 if err != nil && !apierrors.IsNotFound(err) {
1026 framework.Failf("failed to delete CustomResourceDefinition: %v", err)
1027 }
1028 }()
1029 definition, err = apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, f.DynamicClient)
1030 if err != nil {
1031 framework.Failf("failed to create CustomResourceDefinition: %v", err)
1032 }
1033 gomega.Expect(definition.Spec.Versions).To(gomega.HaveLen(1), "custom resource definition should have one version")
1034 version := definition.Spec.Versions[0]
1035
1036
1037 gvr := schema.GroupVersionResource{Group: definition.Spec.Group, Version: version.Name, Resource: definition.Spec.Names.Plural}
1038 resourceClient := f.DynamicClient.Resource(gvr)
1039
1040 apiVersion := definition.Spec.Group + "/" + version.Name
1041
1042
1043 ownerName := names.SimpleNameGenerator.GenerateName("owner")
1044 owner := &unstructured.Unstructured{
1045 Object: map[string]interface{}{
1046 "apiVersion": apiVersion,
1047 "kind": definition.Spec.Names.Kind,
1048 "metadata": map[string]interface{}{
1049 "name": ownerName,
1050 },
1051 },
1052 }
1053 persistedOwner, err := resourceClient.Create(ctx, owner, metav1.CreateOptions{})
1054 if err != nil {
1055 framework.Failf("failed to create owner resource %q: %v", ownerName, err)
1056 }
1057 framework.Logf("created owner resource %q", ownerName)
1058
1059
1060 dependentName := names.SimpleNameGenerator.GenerateName("dependent")
1061 dependent := &unstructured.Unstructured{
1062 Object: map[string]interface{}{
1063 "apiVersion": apiVersion,
1064 "kind": definition.Spec.Names.Kind,
1065 "metadata": map[string]interface{}{
1066 "name": dependentName,
1067 "ownerReferences": []map[string]string{
1068 {
1069 "uid": string(persistedOwner.GetUID()),
1070 "apiVersion": apiVersion,
1071 "kind": definition.Spec.Names.Kind,
1072 "name": ownerName,
1073 },
1074 },
1075 },
1076 },
1077 }
1078 _, err = resourceClient.Create(ctx, dependent, metav1.CreateOptions{})
1079 if err != nil {
1080 framework.Failf("failed to create dependent resource %q: %v", dependentName, err)
1081 }
1082 framework.Logf("created dependent resource %q", dependentName)
1083
1084
1085 err = resourceClient.Delete(ctx, ownerName, getOrphanOptions())
1086 if err != nil {
1087 framework.Failf("failed to delete owner resource %q: %v", ownerName, err)
1088 }
1089
1090 ginkgo.By("wait for the owner to be deleted")
1091 if err := wait.PollWithContext(ctx, 5*time.Second, 120*time.Second, func(ctx context.Context) (bool, error) {
1092 _, err = resourceClient.Get(ctx, ownerName, metav1.GetOptions{})
1093 if err == nil {
1094 return false, nil
1095 }
1096 if err != nil && !apierrors.IsNotFound(err) {
1097 return false, fmt.Errorf("failed to get owner: %w", err)
1098 }
1099 return true, nil
1100 }); err != nil {
1101 framework.Failf("timeout in waiting for the owner to be deleted: %v", err)
1102 }
1103
1104 timeout := 30*time.Second + gcInformerResyncRetryTimeout
1105 ginkgo.By(fmt.Sprintf("wait for %s to see if the garbage collector mistakenly deletes the dependent crd\n", timeout))
1106 gomega.Consistently(ctx, framework.HandleRetry(func(ctx context.Context) (*unstructured.Unstructured, error) {
1107 return resourceClient.Get(ctx, dependentName, metav1.GetOptions{})
1108 })).WithTimeout(timeout).WithPolling(5 * time.Second).ShouldNot(gomega.BeNil())
1109 })
1110
1111 ginkgo.It("should delete jobs and pods created by cronjob", func(ctx context.Context) {
1112
1113 ginkgo.By("Create the cronjob")
1114 cronJob := newCronJob("simple", "*/1 * * * ?")
1115 cronJob, err := f.ClientSet.BatchV1().CronJobs(f.Namespace.Name).Create(ctx, cronJob, metav1.CreateOptions{})
1116 framework.ExpectNoError(err, "failed to create cronjob: %+v, in namespace: %s", cronJob, f.Namespace.Name)
1117
1118 ginkgo.By("Wait for the CronJob to create new Job")
1119 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 2*time.Minute, true, func(ctx context.Context) (bool, error) {
1120 jobs, err := f.ClientSet.BatchV1().Jobs(f.Namespace.Name).List(ctx, metav1.ListOptions{})
1121 if err != nil {
1122 return false, fmt.Errorf("failed to list jobs: %w", err)
1123 }
1124 return len(jobs.Items) > 0, nil
1125 })
1126 if err != nil {
1127 framework.Failf("Failed to wait for the CronJob to create some Jobs: %v", err)
1128 }
1129
1130 ginkgo.By("Delete the cronjob")
1131 if err := f.ClientSet.BatchV1().CronJobs(f.Namespace.Name).Delete(ctx, cronJob.Name, getBackgroundOptions()); err != nil {
1132 framework.Failf("Failed to delete the CronJob: %v", err)
1133 }
1134 ginkgo.By("Verify if cronjob does not leave jobs nor pods behind")
1135 err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
1136 objects := map[string]int{"CronJobs": 0, "Jobs": 0, "Pods": 0}
1137 return verifyRemainingObjects(ctx, f, objects)
1138 })
1139 if err != nil {
1140 framework.Failf("Failed to wait for all jobs and pods to be deleted: %v", err)
1141 }
1142
1143 gatherMetrics(ctx, f)
1144 })
1145 })
1146
1147
1148 func waitForReplicas(ctx context.Context, rc *v1.ReplicationController, rcClient clientv1.ReplicationControllerInterface) {
1149 var (
1150 lastObservedRC *v1.ReplicationController
1151 err error
1152 )
1153 if err := wait.PollWithContext(ctx, framework.Poll, replicaSyncTimeout, func(ctx context.Context) (bool, error) {
1154 lastObservedRC, err = rcClient.Get(ctx, rc.Name, metav1.GetOptions{})
1155 if err != nil {
1156 return false, err
1157 }
1158 if lastObservedRC.Status.Replicas == *rc.Spec.Replicas {
1159 return true, nil
1160 }
1161 return false, nil
1162 }); err != nil {
1163 if lastObservedRC == nil {
1164 framework.Failf("Failed to get ReplicationController %q: %v", rc.Name, err)
1165 } else {
1166 framework.Failf("failed to wait for the rc.Status.Replicas (%d) to reach rc.Spec.Replicas (%d): %v",
1167 lastObservedRC.Status.Replicas, *lastObservedRC.Spec.Replicas, err)
1168 }
1169 }
1170 }
1171
View as plain text