1
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
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 )
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
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 var _ = SIGDescribe("Namespaces", framework.WithSerial(), func() {
236
237 f := framework.NewDefaultFramework("namespaces")
238 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
239
240
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
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
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
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
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
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
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