k8s.io/kubernetes@v1.29.3/test/e2e/common/node/podtemplates.go (about)

     1  /*
     2  Copyright 2020 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 node
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  
    23  	"time"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	utilrand "k8s.io/apimachinery/pkg/util/rand"
    29  	"k8s.io/apimachinery/pkg/util/uuid"
    30  	"k8s.io/apimachinery/pkg/util/wait"
    31  	"k8s.io/client-go/util/retry"
    32  	"k8s.io/kubernetes/test/e2e/framework"
    33  	imageutils "k8s.io/kubernetes/test/utils/image"
    34  	admissionapi "k8s.io/pod-security-admission/api"
    35  
    36  	"github.com/onsi/ginkgo/v2"
    37  	"github.com/onsi/gomega"
    38  )
    39  
    40  const (
    41  	podTemplateRetryPeriod  = 1 * time.Second
    42  	podTemplateRetryTimeout = 1 * time.Minute
    43  )
    44  
    45  var _ = SIGDescribe("PodTemplates", func() {
    46  	f := framework.NewDefaultFramework("podtemplate")
    47  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    48  	/*
    49  	   Release: v1.19
    50  	   Testname: PodTemplate lifecycle
    51  	   Description: Attempt to create a PodTemplate. Patch the created PodTemplate. Fetching the PodTemplate MUST reflect changes.
    52  	          By fetching all the PodTemplates via a Label selector it MUST find the PodTemplate by it's static label and updated value. The PodTemplate must be deleted.
    53  	*/
    54  	framework.ConformanceIt("should run the lifecycle of PodTemplates", func(ctx context.Context) {
    55  		testNamespaceName := f.Namespace.Name
    56  		podTemplateName := "nginx-pod-template-" + string(uuid.NewUUID())
    57  
    58  		// get a list of PodTemplates (in all namespaces to hit endpoint)
    59  		podTemplateList, err := f.ClientSet.CoreV1().PodTemplates("").List(ctx, metav1.ListOptions{
    60  			LabelSelector: "podtemplate-static=true",
    61  		})
    62  		framework.ExpectNoError(err, "failed to list all PodTemplates")
    63  		gomega.Expect(podTemplateList.Items).To(gomega.BeEmpty(), "unable to find templates")
    64  
    65  		// create a PodTemplate
    66  		_, err = f.ClientSet.CoreV1().PodTemplates(testNamespaceName).Create(ctx, &v1.PodTemplate{
    67  			ObjectMeta: metav1.ObjectMeta{
    68  				Name: podTemplateName,
    69  				Labels: map[string]string{
    70  					"podtemplate-static": "true",
    71  				},
    72  			},
    73  			Template: v1.PodTemplateSpec{
    74  				Spec: v1.PodSpec{
    75  					Containers: []v1.Container{
    76  						{Name: "nginx", Image: imageutils.GetE2EImage(imageutils.Nginx)},
    77  					},
    78  				},
    79  			},
    80  		}, metav1.CreateOptions{})
    81  		framework.ExpectNoError(err, "failed to create PodTemplate")
    82  
    83  		// get template
    84  		podTemplateRead, err := f.ClientSet.CoreV1().PodTemplates(testNamespaceName).Get(ctx, podTemplateName, metav1.GetOptions{})
    85  		framework.ExpectNoError(err, "failed to get created PodTemplate")
    86  		gomega.Expect(podTemplateRead.ObjectMeta.Name).To(gomega.Equal(podTemplateName))
    87  
    88  		// patch template
    89  		podTemplatePatch, err := json.Marshal(map[string]interface{}{
    90  			"metadata": map[string]interface{}{
    91  				"labels": map[string]string{
    92  					"podtemplate": "patched",
    93  				},
    94  			},
    95  		})
    96  		framework.ExpectNoError(err, "failed to marshal patch data")
    97  		_, err = f.ClientSet.CoreV1().PodTemplates(testNamespaceName).Patch(ctx, podTemplateName, types.StrategicMergePatchType, []byte(podTemplatePatch), metav1.PatchOptions{})
    98  		framework.ExpectNoError(err, "failed to patch PodTemplate")
    99  
   100  		// get template (ensure label is there)
   101  		podTemplateRead, err = f.ClientSet.CoreV1().PodTemplates(testNamespaceName).Get(ctx, podTemplateName, metav1.GetOptions{})
   102  		framework.ExpectNoError(err, "failed to get PodTemplate")
   103  		gomega.Expect(podTemplateRead.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("podtemplate", "patched"), "failed to patch template, new label not found")
   104  
   105  		// delete the PodTemplate
   106  		err = f.ClientSet.CoreV1().PodTemplates(testNamespaceName).Delete(ctx, podTemplateName, metav1.DeleteOptions{})
   107  		framework.ExpectNoError(err, "failed to delete PodTemplate")
   108  
   109  		// list the PodTemplates
   110  		podTemplateList, err = f.ClientSet.CoreV1().PodTemplates("").List(ctx, metav1.ListOptions{
   111  			LabelSelector: "podtemplate-static=true",
   112  		})
   113  		framework.ExpectNoError(err, "failed to list PodTemplate")
   114  		gomega.Expect(podTemplateList.Items).To(gomega.BeEmpty(), "PodTemplate list returned items, failed to delete PodTemplate")
   115  	})
   116  
   117  	/*
   118  		Release: v1.19
   119  		Testname: PodTemplate, delete a collection
   120  		Description: A set of Pod Templates is created with a label selector which MUST be found when listed.
   121  		The set of Pod Templates is deleted and MUST NOT show up when listed by its label selector.
   122  	*/
   123  	framework.ConformanceIt("should delete a collection of pod templates", func(ctx context.Context) {
   124  		podTemplateNames := []string{"test-podtemplate-1", "test-podtemplate-2", "test-podtemplate-3"}
   125  
   126  		ginkgo.By("Create set of pod templates")
   127  		// create a set of pod templates in test namespace
   128  		for _, podTemplateName := range podTemplateNames {
   129  			_, err := f.ClientSet.CoreV1().PodTemplates(f.Namespace.Name).Create(ctx, &v1.PodTemplate{
   130  				ObjectMeta: metav1.ObjectMeta{
   131  					Name:   podTemplateName,
   132  					Labels: map[string]string{"podtemplate-set": "true"},
   133  				},
   134  				Template: v1.PodTemplateSpec{
   135  					Spec: v1.PodSpec{
   136  						Containers: []v1.Container{
   137  							{Name: "token-test", Image: imageutils.GetE2EImage(imageutils.Agnhost)},
   138  						},
   139  					},
   140  				},
   141  			}, metav1.CreateOptions{})
   142  			framework.ExpectNoError(err, "failed to create pod template")
   143  			framework.Logf("created %v", podTemplateName)
   144  		}
   145  
   146  		ginkgo.By("get a list of pod templates with a label in the current namespace")
   147  		// get a list of pod templates
   148  		podTemplateList, err := f.ClientSet.CoreV1().PodTemplates(f.Namespace.Name).List(ctx, metav1.ListOptions{
   149  			LabelSelector: "podtemplate-set=true",
   150  		})
   151  		framework.ExpectNoError(err, "failed to get a list of pod templates")
   152  
   153  		gomega.Expect(podTemplateList.Items).To(gomega.HaveLen(len(podTemplateNames)), "looking for expected number of pod templates")
   154  
   155  		ginkgo.By("delete collection of pod templates")
   156  		// delete collection
   157  
   158  		framework.Logf("requesting DeleteCollection of pod templates")
   159  		err = f.ClientSet.CoreV1().PodTemplates(f.Namespace.Name).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
   160  			LabelSelector: "podtemplate-set=true"})
   161  		framework.ExpectNoError(err, "failed to delete all pod templates")
   162  
   163  		ginkgo.By("check that the list of pod templates matches the requested quantity")
   164  
   165  		err = wait.PollImmediate(podTemplateRetryPeriod, podTemplateRetryTimeout, checkPodTemplateListQuantity(ctx, f, "podtemplate-set=true", 0))
   166  		framework.ExpectNoError(err, "failed to count required pod templates")
   167  
   168  	})
   169  
   170  	/*
   171  	   Release: v1.24
   172  	   Testname: PodTemplate, replace
   173  	   Description: Attempt to create a PodTemplate which MUST succeed.
   174  	   Attempt to replace the PodTemplate to include a new annotation
   175  	   which MUST succeed. The annotation MUST be found in the new PodTemplate.
   176  	*/
   177  	framework.ConformanceIt("should replace a pod template", func(ctx context.Context) {
   178  		ptClient := f.ClientSet.CoreV1().PodTemplates(f.Namespace.Name)
   179  		ptName := "podtemplate-" + utilrand.String(5)
   180  
   181  		ginkgo.By("Create a pod template")
   182  		ptResource, err := ptClient.Create(ctx, &v1.PodTemplate{
   183  			ObjectMeta: metav1.ObjectMeta{
   184  				Name: ptName,
   185  			},
   186  			Template: v1.PodTemplateSpec{
   187  				Spec: v1.PodSpec{
   188  					Containers: []v1.Container{
   189  						{Name: "e2e-test", Image: imageutils.GetE2EImage(imageutils.Agnhost)},
   190  					},
   191  				},
   192  			},
   193  		}, metav1.CreateOptions{})
   194  		framework.ExpectNoError(err, "failed to create pod template")
   195  
   196  		ginkgo.By("Replace a pod template")
   197  		var updatedPT *v1.PodTemplate
   198  
   199  		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   200  			ptResource, err = ptClient.Get(ctx, ptName, metav1.GetOptions{})
   201  			framework.ExpectNoError(err, "Unable to get pod template %s", ptName)
   202  			ptResource.Annotations = map[string]string{
   203  				"updated": "true",
   204  			}
   205  			updatedPT, err = ptClient.Update(ctx, ptResource, metav1.UpdateOptions{})
   206  			return err
   207  		})
   208  		framework.ExpectNoError(err)
   209  		gomega.Expect(updatedPT.Annotations).To(gomega.HaveKeyWithValue("updated", "true"), "updated object should have the applied annotation")
   210  		framework.Logf("Found updated podtemplate annotation: %#v\n", updatedPT.Annotations["updated"])
   211  	})
   212  
   213  })
   214  
   215  func checkPodTemplateListQuantity(ctx context.Context, f *framework.Framework, label string, quantity int) func() (bool, error) {
   216  	return func() (bool, error) {
   217  		var err error
   218  
   219  		framework.Logf("requesting list of pod templates to confirm quantity")
   220  
   221  		list, err := f.ClientSet.CoreV1().PodTemplates(f.Namespace.Name).List(ctx, metav1.ListOptions{
   222  			LabelSelector: label})
   223  
   224  		if err != nil {
   225  			return false, err
   226  		}
   227  
   228  		if len(list.Items) != quantity {
   229  			return false, err
   230  		}
   231  		return true, nil
   232  	}
   233  }