k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/apimachinery/garbage_collector.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 // estimateMaximumPods estimates how many pods the cluster can handle 55 // with some wiggle room, to prevent pods being unable to schedule due 56 // to max pod constraints. 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 // best guess per node, since default maxPerCore is 10 and most nodes have at least 70 // one core. 71 availablePods += 10 72 } 73 //avoid creating exactly max pods 74 availablePods = int32(float32(availablePods) * 0.5) 75 // bound the top and bottom 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 // The GC controller periodically rediscovers available APIs and syncs running informers for those resources. 107 // If previously available APIs are removed during that resync process, the sync process can fail and need to be retried. 108 // 109 // During e2e runs, parallel tests add/remove API resources (by creating/deleting CRDs and aggregated APIs), 110 // which makes it likely GC will need to retry informer resync at some point during an e2e run. 111 // 112 // This timeout covers two resync/retry periods, and should be added to wait timeouts to account for delays 113 // to the GC controller caused by API changes in other tests. 114 gcInformerResyncRetryTimeout = time.Minute 115 116 // Many operations in these tests are per-replica and may require 100 mutating requests. The 117 // default client QPS of a controller is 5. If the qps is saturated, it will take 20s complete 118 // 100 requests. The e2e tests are running in parallel, so a controller might be stuck 119 // processing other tests. 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 // verifyRemainingObjects verifies if the number of remaining objects. 185 // It returns error if the communication with the API server fails. 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 // getUniqLabel returns a UniqLabel based on labeLkey and labelvalue. 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 /* 318 Release: v1.9 319 Testname: Garbage Collector, delete replication controller, propagation policy background 320 Description: Create a replication controller with 2 Pods. Once RC is created and the first Pod is created, delete RC with deleteOptions.PropagationPolicy set to Background. Deleting the Replication Controller MUST cause pods created by that RC to be deleted. 321 */ 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 // wait for rc to create some pods 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 // We intentionally don't wait the number of pods to reach 341 // rc.Spec.Replicas. We want to see if the garbage collector and the 342 // rc manager work properly if the rc is deleted before it reaches 343 // stasis. 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 // wait for the RCs and Pods to reach the expected numbers. 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 /* 376 Release: v1.9 377 Testname: Garbage Collector, delete replication controller, propagation policy orphan 378 Description: Create a replication controller with maximum allocatable Pods between 10 and 100 replicas. Once RC is created and the all Pods are created, delete RC with deleteOptions.PropagationPolicy set to Orphan. Deleting the Replication Controller MUST cause pods created by that RC to be orphaned. 379 */ 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 // wait for rc to create pods 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 // Orphaning the 100 pods takes 100 PATCH operations. The default qps of 403 // a client is 5. If the qps is saturated, it will take 20s to orphan 404 // the pods. However, apiserver takes hundreds of ms to finish one 405 // PATCH, and the gc sends the patching in a single thread, so the 406 // actual qps is less than 5. Also, the e2e tests are running in 407 // parallel, the GC controller might get distracted by other tests. 408 // According to the test logs, 120s is enough time. 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 // deleteOptions.OrphanDependents is deprecated in 1.7 and preferred to use the PropagationPolicy. 437 // Discussion is tracked under https://github.com/kubernetes/kubernetes/issues/65427 to promote for conformance in future. 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 // wait for rc to create some pods 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 /* 476 Release: v1.9 477 Testname: Garbage Collector, delete deployment, propagation policy background 478 Description: Create a deployment with a replicaset. Once replicaset is created , delete the deployment with deleteOptions.PropagationPolicy set to Background. Deleting the deployment MUST delete the replicaset created by the deployment and also the Pods that belong to the deployments MUST be deleted. 479 */ 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 // wait for deployment to create some rs 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 /* 535 Release: v1.9 536 Testname: Garbage Collector, delete deployment, propagation policy orphan 537 Description: Create a deployment with a replicaset. Once replicaset is created , delete the deployment with deleteOptions.PropagationPolicy set to Orphan. Deleting the deployment MUST cause the replicaset created by the deployment to be orphaned, also the Pods created by the deployments MUST be orphaned. 538 */ 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 // wait for deployment to create some rs 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 // Once the deployment object is gone, we'll know the GC has finished performing any relevant actions. 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 /* 635 Release: v1.9 636 Testname: Garbage Collector, delete replication controller, after owned pods 637 Description: Create a replication controller with maximum allocatable Pods between 10 and 100 replicas. Once RC is created and the all Pods are created, delete RC with deleteOptions.PropagationPolicy set to Foreground. Deleting the Replication Controller MUST cause pods created by that RC to be deleted before the RC is deleted. 638 */ 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 // wait for rc to create pods 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 // default client QPS is 20, deleting each pod requires 2 requests, so 30s should be enough 662 // TODO: 30s is enough assuming immediate processing of dependents following 663 // owner deletion, but in practice there can be a long delay between owner 664 // deletion and dependent deletion processing. For now, increase the timeout 665 // and investigate the processing delay. 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 // There shouldn't be any pods 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 // TODO: this should be an integration test 709 /* 710 Release: v1.9 711 Testname: Garbage Collector, multiple owners 712 Description: Create a replication controller RC1, with maximum allocatable Pods between 10 and 100 replicas. Create second replication controller RC2 and set RC2 as owner for half of those replicas. Once RC1 is created and the all Pods are created, delete RC1 with deleteOptions.PropagationPolicy set to Foreground. Half of the Pods that has RC2 as owner MUST not be deleted or have a deletion timestamp. Deleting the Replication Controller MUST not delete Pods that are owned by multiple replication controllers. 713 */ 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 // wait for rc1 to be stable 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 // TODO: shorten the timeout when we make GC's periodic API rediscovery more efficient. 757 // Tracked at https://github.com/kubernetes/kubernetes/issues/50046. 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 // half of the pods should still exist, 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 // they should only have 1 ownerReference left 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 // TODO: should be an integration test 813 /* 814 Release: v1.9 815 Testname: Garbage Collector, dependency cycle 816 Description: Create three pods, patch them with Owner references such that pod1 has pod3, pod2 has pod1 and pod3 has pod2 as owner references respectively. Delete pod1 MUST delete all pods. The dependency cycle MUST not block the garbage collection. 817 */ 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 // create circular dependency 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 // delete one pod, should result in the deletion of all pods 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 // TODO: shorten the timeout when we make GC's periodic API rediscovery more efficient. 857 // Tracked at https://github.com/kubernetes/kubernetes/issues/50046. 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 // Create a random custom resource definition and ensure it's available for 886 // use. 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 // Get a client for the custom resource. 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 // Create a custom owner resource. 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 // Create a custom dependent resource. 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 // Delete the owner. 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 // Create and delete an unrelated instance of the custom resource in foreground deletion mode, 957 // so we have a signal when GC is aware of this custom resource type 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 // Wait for the canary foreground finalization to complete, which means GC is aware of our new custom resource type 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 // Ensure the dependent is deleted. 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 // Ensure the owner is deleted. 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 // Create a random custom resource definition and ensure it's available for 1021 // use. 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 // Get a client for the custom resource. 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 // Create a custom owner resource. 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 // Create a custom dependent resource. 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 // Delete the owner and orphan the dependent. 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 // TODO(106575): Migrate away from generic polling function. 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 }