k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/apimachinery/namespace.go (about) 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 }