...

Source file src/k8s.io/kubernetes/test/e2e/apimachinery/namespace.go

Documentation: k8s.io/kubernetes/test/e2e/apimachinery

     1  /*
     2  Copyright 2014 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  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/apimachinery/pkg/util/intstr"
    34  	utilrand "k8s.io/apimachinery/pkg/util/rand"
    35  	"k8s.io/apimachinery/pkg/util/uuid"
    36  	"k8s.io/apimachinery/pkg/util/wait"
    37  	clientscheme "k8s.io/client-go/kubernetes/scheme"
    38  	"k8s.io/client-go/util/retry"
    39  	"k8s.io/kubernetes/test/e2e/feature"
    40  	"k8s.io/kubernetes/test/e2e/framework"
    41  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    42  	imageutils "k8s.io/kubernetes/test/utils/image"
    43  	admissionapi "k8s.io/pod-security-admission/api"
    44  
    45  	"github.com/onsi/ginkgo/v2"
    46  	"github.com/onsi/gomega"
    47  )
    48  
    49  func extinguish(ctx context.Context, f *framework.Framework, totalNS int, maxAllowedAfterDel int, maxSeconds int) {
    50  	ginkgo.By("Creating testing namespaces")
    51  	wg := &sync.WaitGroup{}
    52  	wg.Add(totalNS)
    53  	for n := 0; n < totalNS; n++ {
    54  		go func(n int) {
    55  			defer wg.Done()
    56  			defer ginkgo.GinkgoRecover()
    57  			ns := fmt.Sprintf("nslifetest-%v", n)
    58  			_, err := f.CreateNamespace(ctx, ns, nil)
    59  			framework.ExpectNoError(err, "failed to create namespace: %s", ns)
    60  		}(n)
    61  	}
    62  	wg.Wait()
    63  
    64  	//Wait 10 seconds, then SEND delete requests for all the namespaces.
    65  	ginkgo.By("Waiting 10 seconds")
    66  	time.Sleep(10 * time.Second)
    67  	deleteFilter := []string{"nslifetest"}
    68  	deleted, err := framework.DeleteNamespaces(ctx, f.ClientSet, deleteFilter, nil /* skipFilter */)
    69  	framework.ExpectNoError(err, "failed to delete namespace(s) containing: %s", deleteFilter)
    70  	gomega.Expect(deleted).To(gomega.HaveLen(totalNS))
    71  
    72  	ginkgo.By("Waiting for namespaces to vanish")
    73  	//Now POLL until all namespaces have been eradicated.
    74  	framework.ExpectNoError(wait.Poll(2*time.Second, time.Duration(maxSeconds)*time.Second,
    75  		func() (bool, error) {
    76  			var cnt = 0
    77  			nsList, err := f.ClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
    78  			if err != nil {
    79  				return false, err
    80  			}
    81  			for _, item := range nsList.Items {
    82  				if strings.Contains(item.Name, "nslifetest") {
    83  					cnt++
    84  				}
    85  			}
    86  			if cnt > maxAllowedAfterDel {
    87  				framework.Logf("Remaining namespaces : %v", cnt)
    88  				return false, nil
    89  			}
    90  			return true, nil
    91  		}))
    92  }
    93  
    94  func ensurePodsAreRemovedWhenNamespaceIsDeleted(ctx context.Context, f *framework.Framework) {
    95  	ginkgo.By("Creating a test namespace")
    96  	namespaceName := "nsdeletetest"
    97  	namespace, err := f.CreateNamespace(ctx, namespaceName, nil)
    98  	framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName)
    99  
   100  	ginkgo.By("Waiting for a default service account to be provisioned in namespace")
   101  	err = framework.WaitForDefaultServiceAccountInNamespace(ctx, f.ClientSet, namespace.Name)
   102  	framework.ExpectNoError(err, "failure while waiting for a default service account to be provisioned in namespace: %s", namespace.Name)
   103  
   104  	ginkgo.By("Creating a pod in the namespace")
   105  	podName := "test-pod"
   106  	pod := &v1.Pod{
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Name: podName,
   109  		},
   110  		Spec: v1.PodSpec{
   111  			Containers: []v1.Container{
   112  				{
   113  					Name:  "nginx",
   114  					Image: imageutils.GetPauseImageName(),
   115  				},
   116  			},
   117  		},
   118  	}
   119  	pod, err = f.ClientSet.CoreV1().Pods(namespace.Name).Create(ctx, pod, metav1.CreateOptions{})
   120  	framework.ExpectNoError(err, "failed to create pod %s in namespace: %s", podName, namespace.Name)
   121  
   122  	ginkgo.By("Waiting for the pod to have running status")
   123  	framework.ExpectNoError(e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod))
   124  
   125  	ginkgo.By("Deleting the namespace")
   126  	err = f.ClientSet.CoreV1().Namespaces().Delete(ctx, namespace.Name, metav1.DeleteOptions{})
   127  	framework.ExpectNoError(err, "failed to delete namespace: %s", namespace.Name)
   128  
   129  	ginkgo.By("Waiting for the namespace to be removed.")
   130  	maxWaitSeconds := int64(60) + *pod.Spec.TerminationGracePeriodSeconds
   131  	framework.ExpectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
   132  		func() (bool, error) {
   133  			_, err = f.ClientSet.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{})
   134  			if err != nil && apierrors.IsNotFound(err) {
   135  				return true, nil
   136  			}
   137  			return false, nil
   138  		}))
   139  
   140  	ginkgo.By("Recreating the namespace")
   141  	namespace, err = f.CreateNamespace(ctx, namespaceName, nil)
   142  	framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName)
   143  
   144  	ginkgo.By("Verifying there are no pods in the namespace")
   145  	_, err = f.ClientSet.CoreV1().Pods(namespace.Name).Get(ctx, pod.Name, metav1.GetOptions{})
   146  	gomega.Expect(err).To(gomega.HaveOccurred(), "failed to get pod %s in namespace: %s", pod.Name, namespace.Name)
   147  }
   148  
   149  func ensureServicesAreRemovedWhenNamespaceIsDeleted(ctx context.Context, f *framework.Framework) {
   150  	var err error
   151  
   152  	ginkgo.By("Creating a test namespace")
   153  	namespaceName := "nsdeletetest"
   154  	namespace, err := f.CreateNamespace(ctx, namespaceName, nil)
   155  	framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName)
   156  
   157  	ginkgo.By("Waiting for a default service account to be provisioned in namespace")
   158  	err = framework.WaitForDefaultServiceAccountInNamespace(ctx, f.ClientSet, namespace.Name)
   159  	framework.ExpectNoError(err, "failure while waiting for a default service account to be provisioned in namespace: %s", namespace.Name)
   160  
   161  	ginkgo.By("Creating a service in the namespace")
   162  	serviceName := "test-service"
   163  	labels := map[string]string{
   164  		"foo": "bar",
   165  		"baz": "blah",
   166  	}
   167  	service := &v1.Service{
   168  		ObjectMeta: metav1.ObjectMeta{
   169  			Name: serviceName,
   170  		},
   171  		Spec: v1.ServiceSpec{
   172  			Selector: labels,
   173  			Ports: []v1.ServicePort{{
   174  				Port:       80,
   175  				TargetPort: intstr.FromInt32(80),
   176  			}},
   177  		},
   178  	}
   179  	service, err = f.ClientSet.CoreV1().Services(namespace.Name).Create(ctx, service, metav1.CreateOptions{})
   180  	framework.ExpectNoError(err, "failed to create service %s in namespace %s", serviceName, namespace.Name)
   181  
   182  	ginkgo.By("Deleting the namespace")
   183  	err = f.ClientSet.CoreV1().Namespaces().Delete(ctx, namespace.Name, metav1.DeleteOptions{})
   184  	framework.ExpectNoError(err, "failed to delete namespace: %s", namespace.Name)
   185  
   186  	ginkgo.By("Waiting for the namespace to be removed.")
   187  	maxWaitSeconds := int64(60)
   188  	framework.ExpectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
   189  		func() (bool, error) {
   190  			_, err = f.ClientSet.CoreV1().Namespaces().Get(ctx, namespace.Name, metav1.GetOptions{})
   191  			if err != nil && apierrors.IsNotFound(err) {
   192  				return true, nil
   193  			}
   194  			return false, nil
   195  		}))
   196  
   197  	ginkgo.By("Recreating the namespace")
   198  	namespace, err = f.CreateNamespace(ctx, namespaceName, nil)
   199  	framework.ExpectNoError(err, "failed to create namespace: %s", namespaceName)
   200  
   201  	ginkgo.By("Verifying there is no service in the namespace")
   202  	_, err = f.ClientSet.CoreV1().Services(namespace.Name).Get(ctx, service.Name, metav1.GetOptions{})
   203  	gomega.Expect(err).To(gomega.HaveOccurred(), "failed to get service %s in namespace: %s", service.Name, namespace.Name)
   204  }
   205  
   206  // This test must run [Serial] due to the impact of running other parallel
   207  // tests can have on its performance.  Each test that follows the common
   208  // test framework follows this pattern:
   209  //  1. Create a Namespace
   210  //  2. Do work that generates content in that namespace
   211  //  3. Delete a Namespace
   212  //
   213  // Creation of a Namespace is non-trivial since it requires waiting for a
   214  // ServiceAccount to be generated.
   215  // Deletion of a Namespace is non-trivial and performance intensive since
   216  // its an orchestrated process.  The controller that handles deletion must
   217  // query the namespace for all existing content, and then delete each piece
   218  // of content in turn.  As the API surface grows to add more KIND objects
   219  // that could exist in a Namespace, the number of calls that the namespace
   220  // controller must orchestrate grows since it must LIST, DELETE (1x1) each
   221  // KIND.
   222  // There is work underway to improve this, but it's
   223  // most likely not going to get significantly better until etcd v3.
   224  // Going back to this test, this test generates 100 Namespace objects, and then
   225  // rapidly deletes all of them.  This causes the NamespaceController to observe
   226  // and attempt to process a large number of deletes concurrently.  In effect,
   227  // it's like running 100 traditional e2e tests in parallel.  If the namespace
   228  // controller orchestrating deletes is slowed down deleting another test's
   229  // content then this test may fail.  Since the goal of this test is to soak
   230  // Namespace creation, and soak Namespace deletion, its not appropriate to
   231  // further soak the cluster with other parallel Namespace deletion activities
   232  // that each have a variable amount of content in the associated Namespace.
   233  // When run in [Serial] this test appears to delete Namespace objects at a
   234  // rate of approximately 1 per second.
   235  var _ = SIGDescribe("Namespaces", framework.WithSerial(), func() {
   236  
   237  	f := framework.NewDefaultFramework("namespaces")
   238  	f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
   239  
   240  	/*
   241  		Release: v1.11
   242  		Testname: namespace-deletion-removes-pods
   243  		Description: Ensure that if a namespace is deleted then all pods are removed from that namespace.
   244  	*/
   245  	framework.ConformanceIt("should ensure that all pods are removed when a namespace is deleted", func(ctx context.Context) {
   246  		ensurePodsAreRemovedWhenNamespaceIsDeleted(ctx, f)
   247  	})
   248  
   249  	/*
   250  		Release: v1.11
   251  		Testname: namespace-deletion-removes-services
   252  		Description: Ensure that if a namespace is deleted then all services are removed from that namespace.
   253  	*/
   254  	framework.ConformanceIt("should ensure that all services are removed when a namespace is deleted", func(ctx context.Context) {
   255  		ensureServicesAreRemovedWhenNamespaceIsDeleted(ctx, f)
   256  	})
   257  
   258  	ginkgo.It("should delete fast enough (90 percent of 100 namespaces in 150 seconds)", func(ctx context.Context) {
   259  		extinguish(ctx, f, 100, 10, 150)
   260  	})
   261  
   262  	// On hold until etcd3; see #7372
   263  	f.It("should always delete fast (ALL of 100 namespaces in 150 seconds)", feature.ComprehensiveNamespaceDraining, func(ctx context.Context) {
   264  		extinguish(ctx, f, 100, 0, 150)
   265  	})
   266  
   267  	/*
   268  	   Release: v1.18
   269  	   Testname: Namespace patching
   270  	   Description: A Namespace is created.
   271  	   The Namespace is patched.
   272  	   The Namespace and MUST now include the new Label.
   273  	*/
   274  	framework.ConformanceIt("should patch a Namespace", func(ctx context.Context) {
   275  		ginkgo.By("creating a Namespace")
   276  		namespaceName := "nspatchtest-" + string(uuid.NewUUID())
   277  		ns, err := f.CreateNamespace(ctx, namespaceName, nil)
   278  		framework.ExpectNoError(err, "failed creating Namespace")
   279  		namespaceName = ns.ObjectMeta.Name
   280  
   281  		ginkgo.By("patching the Namespace")
   282  		nspatch, err := json.Marshal(map[string]interface{}{
   283  			"metadata": map[string]interface{}{
   284  				"labels": map[string]string{"testLabel": "testValue"},
   285  			},
   286  		})
   287  		framework.ExpectNoError(err, "failed to marshal JSON patch data")
   288  		_, err = f.ClientSet.CoreV1().Namespaces().Patch(ctx, namespaceName, types.StrategicMergePatchType, nspatch, metav1.PatchOptions{})
   289  		framework.ExpectNoError(err, "failed to patch Namespace")
   290  
   291  		ginkgo.By("get the Namespace and ensuring it has the label")
   292  		namespace, err := f.ClientSet.CoreV1().Namespaces().Get(ctx, namespaceName, metav1.GetOptions{})
   293  		framework.ExpectNoError(err, "failed to get Namespace")
   294  		gomega.Expect(namespace.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("testLabel", "testValue"), "namespace not patched")
   295  	})
   296  
   297  	/*
   298  		Release: v1.25
   299  		Testname: Namespace, apply changes to a namespace status
   300  		Description: Getting the current namespace status MUST succeed. The reported status
   301  		phase MUST be active. Given the patching of the namespace status, the fields MUST
   302  		equal the new values. Given the updating of the namespace status, the fields MUST
   303  		equal the new values.
   304  	*/
   305  	framework.ConformanceIt("should apply changes to a namespace status", func(ctx context.Context) {
   306  		ns := f.Namespace.Name
   307  		dc := f.DynamicClient
   308  		nsResource := v1.SchemeGroupVersion.WithResource("namespaces")
   309  		nsClient := f.ClientSet.CoreV1().Namespaces()
   310  
   311  		ginkgo.By("Read namespace status")
   312  
   313  		unstruct, err := dc.Resource(nsResource).Get(ctx, ns, metav1.GetOptions{}, "status")
   314  		framework.ExpectNoError(err, "failed to fetch NamespaceStatus %s", ns)
   315  		nsStatus, err := unstructuredToNamespace(unstruct)
   316  		framework.ExpectNoError(err, "Getting the status of the namespace %s", ns)
   317  		gomega.Expect(nsStatus.Status.Phase).To(gomega.Equal(v1.NamespaceActive), "The phase returned was %v", nsStatus.Status.Phase)
   318  		framework.Logf("Status: %#v", nsStatus.Status)
   319  
   320  		ginkgo.By("Patch namespace status")
   321  
   322  		nsCondition := v1.NamespaceCondition{
   323  			Type:    "StatusPatch",
   324  			Status:  v1.ConditionTrue,
   325  			Reason:  "E2E",
   326  			Message: "Patched by an e2e test",
   327  		}
   328  		nsConditionJSON, err := json.Marshal(nsCondition)
   329  		framework.ExpectNoError(err, "failed to marshal namespace condition")
   330  
   331  		patchedStatus, err := nsClient.Patch(ctx, ns, types.MergePatchType,
   332  			[]byte(`{"metadata":{"annotations":{"e2e-patched-ns-status":"`+ns+`"}},"status":{"conditions":[`+string(nsConditionJSON)+`]}}`),
   333  			metav1.PatchOptions{}, "status")
   334  		framework.ExpectNoError(err, "Failed to patch status. err: %v ", err)
   335  		gomega.Expect(patchedStatus.Annotations).To(gomega.HaveKeyWithValue("e2e-patched-ns-status", ns), "patched object should have the applied annotation")
   336  		gomega.Expect(string(patchedStatus.Status.Conditions[len(patchedStatus.Status.Conditions)-1].Reason)).To(gomega.Equal("E2E"), "The Reason returned was %v", patchedStatus.Status.Conditions[0].Reason)
   337  		gomega.Expect(string(patchedStatus.Status.Conditions[len(patchedStatus.Status.Conditions)-1].Message)).To(gomega.Equal("Patched by an e2e test"), "The Message returned was %v", patchedStatus.Status.Conditions[0].Reason)
   338  		framework.Logf("Status.Condition: %#v", patchedStatus.Status.Conditions[len(patchedStatus.Status.Conditions)-1])
   339  
   340  		ginkgo.By("Update namespace status")
   341  		var statusUpdated *v1.Namespace
   342  
   343  		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   344  			unstruct, err := dc.Resource(nsResource).Get(ctx, ns, metav1.GetOptions{}, "status")
   345  			framework.ExpectNoError(err, "failed to fetch NamespaceStatus %s", ns)
   346  			statusToUpdate, err := unstructuredToNamespace(unstruct)
   347  			framework.ExpectNoError(err, "Getting the status of the namespace %s", ns)
   348  
   349  			statusToUpdate.Status.Conditions = append(statusToUpdate.Status.Conditions, v1.NamespaceCondition{
   350  				Type:    "StatusUpdate",
   351  				Status:  v1.ConditionTrue,
   352  				Reason:  "E2E",
   353  				Message: "Updated by an e2e test",
   354  			})
   355  			statusUpdated, err = nsClient.UpdateStatus(ctx, statusToUpdate, metav1.UpdateOptions{})
   356  
   357  			return err
   358  		})
   359  		framework.ExpectNoError(err, "failed to update namespace status %s", ns)
   360  		gomega.Expect(statusUpdated.Status.Conditions).To(gomega.HaveLen(len(statusUpdated.Status.Conditions)), "updated object should have the applied condition, got %#v", statusUpdated.Status.Conditions)
   361  		gomega.Expect(statusUpdated.Status.Conditions[len(statusUpdated.Status.Conditions)-1].Type).To(gomega.Equal(v1.NamespaceConditionType("StatusUpdate")), "updated object should have the approved condition, got %#v", statusUpdated.Status.Conditions)
   362  		gomega.Expect(statusUpdated.Status.Conditions[len(statusUpdated.Status.Conditions)-1].Message).To(gomega.Equal("Updated by an e2e test"), "The Message returned was %v", statusUpdated.Status.Conditions[0].Message)
   363  		framework.Logf("Status.Condition: %#v", statusUpdated.Status.Conditions[len(statusUpdated.Status.Conditions)-1])
   364  	})
   365  
   366  	/*
   367  		Release: v1.26
   368  		Testname: Namespace, apply update to a namespace
   369  		Description: When updating the namespace it MUST
   370  		succeed and the field MUST equal the new value.
   371  	*/
   372  	framework.ConformanceIt("should apply an update to a Namespace", func(ctx context.Context) {
   373  		var err error
   374  		var updatedNamespace *v1.Namespace
   375  		ns := f.Namespace.Name
   376  		cs := f.ClientSet
   377  
   378  		ginkgo.By(fmt.Sprintf("Updating Namespace %q", ns))
   379  		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   380  			updatedNamespace, err = cs.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{})
   381  			framework.ExpectNoError(err, "Unable to get Namespace %q", ns)
   382  
   383  			updatedNamespace.Labels[ns] = "updated"
   384  			updatedNamespace, err = cs.CoreV1().Namespaces().Update(ctx, updatedNamespace, metav1.UpdateOptions{})
   385  			return err
   386  		})
   387  		framework.ExpectNoError(err, "failed to update Namespace: %q", ns)
   388  		gomega.Expect(updatedNamespace.ObjectMeta.Labels).To(gomega.HaveKeyWithValue(ns, "updated"), "Failed to update namespace %q. Current Labels: %#v", ns, updatedNamespace.Labels)
   389  		framework.Logf("Namespace %q now has labels, %#v", ns, updatedNamespace.Labels)
   390  	})
   391  
   392  	/*
   393  		Release: v1.26
   394  		Testname: Namespace, apply finalizer to a namespace
   395  		Description: Attempt to create a Namespace which MUST be succeed.
   396  		Updating the namespace with a fake finalizer MUST succeed. The
   397  		fake finalizer MUST be found. Removing the fake finalizer from
   398  		the namespace MUST succeed and MUST NOT be found.
   399  	*/
   400  	framework.ConformanceIt("should apply a finalizer to a Namespace", func(ctx context.Context) {
   401  
   402  		fakeFinalizer := v1.FinalizerName("e2e.example.com/fakeFinalizer")
   403  		var updatedNamespace *v1.Namespace
   404  		nsName := "e2e-ns-" + utilrand.String(5)
   405  
   406  		ginkgo.By(fmt.Sprintf("Creating namespace %q", nsName))
   407  		testNamespace, err := f.CreateNamespace(ctx, nsName, nil)
   408  		framework.ExpectNoError(err, "failed creating Namespace")
   409  		ns := testNamespace.ObjectMeta.Name
   410  		nsClient := f.ClientSet.CoreV1().Namespaces()
   411  		framework.Logf("Namespace %q has %#v", testNamespace.Name, testNamespace.Spec.Finalizers)
   412  
   413  		ginkgo.By(fmt.Sprintf("Adding e2e finalizer to namespace %q", ns))
   414  		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   415  			updateNamespace, err := nsClient.Get(ctx, ns, metav1.GetOptions{})
   416  			framework.ExpectNoError(err, "Unable to get Namespace %q", ns)
   417  
   418  			updateNamespace.Spec.Finalizers = append(updateNamespace.Spec.Finalizers, fakeFinalizer)
   419  			updatedNamespace, err = nsClient.Finalize(ctx, updateNamespace, metav1.UpdateOptions{})
   420  			return err
   421  		})
   422  		framework.ExpectNoError(err, "failed to add finalizer to the namespace: %q", ns)
   423  
   424  		var foundFinalizer bool
   425  		for _, item := range updatedNamespace.Spec.Finalizers {
   426  			if item == fakeFinalizer {
   427  				foundFinalizer = true
   428  				break
   429  			}
   430  		}
   431  		if !foundFinalizer {
   432  			framework.Failf("Finalizer %q was not found. Namespace %q has %#v", fakeFinalizer, updatedNamespace.Name, updatedNamespace.Spec.Finalizers)
   433  		}
   434  		framework.Logf("Namespace %q has %#v", updatedNamespace.Name, updatedNamespace.Spec.Finalizers)
   435  
   436  		ginkgo.By(fmt.Sprintf("Removing e2e finalizer from namespace %q", ns))
   437  		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   438  			updatedNamespace, err = nsClient.Get(ctx, ns, metav1.GetOptions{})
   439  			framework.ExpectNoError(err, "Unable to get namespace %q", ns)
   440  
   441  			var finalizerList []v1.FinalizerName
   442  			for _, item := range updatedNamespace.Spec.Finalizers {
   443  				if item != fakeFinalizer {
   444  					finalizerList = append(finalizerList, item)
   445  				}
   446  			}
   447  			updatedNamespace.Spec.Finalizers = finalizerList
   448  			updatedNamespace, err = nsClient.Finalize(ctx, updatedNamespace, metav1.UpdateOptions{})
   449  			return err
   450  		})
   451  		framework.ExpectNoError(err, "failed to remove finalizer from namespace: %q", ns)
   452  
   453  		foundFinalizer = false
   454  		for _, item := range updatedNamespace.Spec.Finalizers {
   455  			if item == fakeFinalizer {
   456  				foundFinalizer = true
   457  				break
   458  			}
   459  		}
   460  		if foundFinalizer {
   461  			framework.Failf("Finalizer %q was found. Namespace %q has %#v", fakeFinalizer, updatedNamespace.Name, updatedNamespace.Spec.Finalizers)
   462  		}
   463  		framework.Logf("Namespace %q has %#v", updatedNamespace.Name, updatedNamespace.Spec.Finalizers)
   464  	})
   465  
   466  })
   467  
   468  func unstructuredToNamespace(obj *unstructured.Unstructured) (*v1.Namespace, error) {
   469  	json, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  	ns := &v1.Namespace{}
   474  	err = runtime.DecodeInto(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), json, ns)
   475  
   476  	return ns, err
   477  }
   478  

View as plain text