@@ -20,6 +20,7 @@ import (
20
20
"context"
21
21
"encoding/json"
22
22
"fmt"
23
+ "k8s.io/kubernetes/pkg/features"
23
24
"strings"
24
25
"sync"
25
26
"time"
@@ -475,3 +476,106 @@ func unstructuredToNamespace(obj *unstructured.Unstructured) (*v1.Namespace, err
475
476
476
477
return ns , err
477
478
}
479
+
480
+ var _ = SIGDescribe ("OrderedNamespaceDeletion" , func () {
481
+ f := framework .NewDefaultFramework ("namespacedeletion" )
482
+ f .NamespacePodSecurityLevel = admissionapi .LevelBaseline
483
+
484
+ f .It ("namespace deletion should delete pod first" , feature .OrderedNamespaceDeletion , framework .WithFeatureGate (features .OrderedNamespaceDeletion ), func (ctx context.Context ) {
485
+ ensurePodsAreRemovedFirstInOrderedNamespaceDeletion (ctx , f )
486
+ })
487
+ })
488
+
489
+ func ensurePodsAreRemovedFirstInOrderedNamespaceDeletion (ctx context.Context , f * framework.Framework ) {
490
+ ginkgo .By ("Creating a test namespace" )
491
+ namespaceName := "nsdeletetest"
492
+ namespace , err := f .CreateNamespace (ctx , namespaceName , nil )
493
+ framework .ExpectNoError (err , "failed to create namespace: %s" , namespaceName )
494
+ nsName := namespace .Name
495
+
496
+ ginkgo .By ("Waiting for a default service account to be provisioned in namespace" )
497
+ err = framework .WaitForDefaultServiceAccountInNamespace (ctx , f .ClientSet , nsName )
498
+ framework .ExpectNoError (err , "failure while waiting for a default service account to be provisioned in namespace: %s" , nsName )
499
+
500
+ ginkgo .By ("Creating a pod with finalizer in the namespace" )
501
+ podName := "test-pod"
502
+ pod := & v1.Pod {
503
+ ObjectMeta : metav1.ObjectMeta {
504
+ Name : podName ,
505
+ Finalizers : []string {
506
+ "e2e.example.com/finalizer" ,
507
+ },
508
+ },
509
+ Spec : v1.PodSpec {
510
+ Containers : []v1.Container {
511
+ {
512
+ Name : "nginx" ,
513
+ Image : imageutils .GetPauseImageName (),
514
+ },
515
+ },
516
+ },
517
+ }
518
+ pod , err = f .ClientSet .CoreV1 ().Pods (nsName ).Create (ctx , pod , metav1.CreateOptions {})
519
+ framework .ExpectNoError (err , "failed to create pod %s in namespace: %s" , podName , nsName )
520
+
521
+ ginkgo .By ("Waiting for the pod to have running status" )
522
+ framework .ExpectNoError (e2epod .WaitForPodRunningInNamespace (ctx , f .ClientSet , pod ))
523
+
524
+ configMapName := "test-configmap"
525
+ ginkgo .By (fmt .Sprintf ("Creating a configmap %q in namespace %q" , configMapName , nsName ))
526
+ configMap := & v1.ConfigMap {
527
+ ObjectMeta : metav1.ObjectMeta {
528
+ Name : configMapName ,
529
+ Namespace : nsName ,
530
+ },
531
+ Data : map [string ]string {
532
+ "key" : "value" ,
533
+ },
534
+ }
535
+ _ , err = f .ClientSet .CoreV1 ().ConfigMaps (nsName ).Create (ctx , configMap , metav1.CreateOptions {})
536
+ framework .ExpectNoError (err , "failed to create configmap %q in namespace %q" , configMapName , nsName )
537
+
538
+ ginkgo .By ("Deleting the namespace" )
539
+ err = f .ClientSet .CoreV1 ().Namespaces ().Delete (ctx , nsName , metav1.DeleteOptions {})
540
+ framework .ExpectNoError (err , "failed to delete namespace: %s" , nsName )
541
+ // wait 10 seconds to allow the namespace controller to process
542
+ time .Sleep (10 * time .Second )
543
+ ginkgo .By ("the pod should be deleted before processing deletion for other resources" )
544
+ framework .ExpectNoError (wait .PollUntilContextTimeout (ctx , 2 * time .Second , 60 * time .Second , true ,
545
+ func (ctx context.Context ) (bool , error ) {
546
+ _ , err = f .ClientSet .CoreV1 ().ConfigMaps (nsName ).Get (ctx , configMapName , metav1.GetOptions {})
547
+ framework .ExpectNoError (err , "configmap %q should still exist in namespace %q" , configMapName , nsName )
548
+ // the pod should exist and has a deletionTimestamp set
549
+ pod , err = f .ClientSet .CoreV1 ().Pods (nsName ).Get (ctx , pod .Name , metav1.GetOptions {})
550
+ framework .ExpectNoError (err , "failed to get pod %q in namespace %q" , pod .Name , nsName )
551
+ if pod .DeletionTimestamp == nil {
552
+ framework .Failf ("Pod %q in namespace %q does not have a metadata.deletionTimestamp set" , pod .Name , nsName )
553
+ }
554
+ _ , err = f .ClientSet .CoreV1 ().Namespaces ().Get (ctx , nsName , metav1.GetOptions {})
555
+ if err != nil && apierrors .IsNotFound (err ) {
556
+ return false , fmt .Errorf ("namespace %s was deleted unexpectedly" , nsName )
557
+ }
558
+ return true , nil
559
+ }))
560
+
561
+ ginkgo .By (fmt .Sprintf ("Removing finalizer from pod %q in namespace %q" , podName , nsName ))
562
+ err = retry .RetryOnConflict (retry .DefaultRetry , func () error {
563
+ pod , err = f .ClientSet .CoreV1 ().Pods (nsName ).Get (ctx , podName , metav1.GetOptions {})
564
+ framework .ExpectNoError (err , "failed to get pod %q in namespace %q" , pod .Name , nsName )
565
+ pod .Finalizers = []string {}
566
+ _ , err = f .ClientSet .CoreV1 ().Pods (nsName ).Update (ctx , pod , metav1.UpdateOptions {})
567
+ return err
568
+ })
569
+ framework .ExpectNoError (err , "failed to update pod %q and remove finalizer in namespace %q" , podName , nsName )
570
+
571
+ ginkgo .By ("Waiting for the namespace to be removed." )
572
+ maxWaitSeconds := int64 (60 ) + * pod .Spec .TerminationGracePeriodSeconds
573
+ framework .ExpectNoError (wait .PollUntilContextTimeout (ctx , 1 * time .Second , time .Duration (maxWaitSeconds )* time .Second , true ,
574
+ func (ctx context.Context ) (bool , error ) {
575
+ _ , err = f .ClientSet .CoreV1 ().Namespaces ().Get (ctx , namespace .Name , metav1.GetOptions {})
576
+ if err != nil && apierrors .IsNotFound (err ) {
577
+ return true , nil
578
+ }
579
+ return false , nil
580
+ }))
581
+ }
0 commit comments