...

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

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

     1  /*
     2  Copyright 2018 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  	"fmt"
    22  
    23  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    24  	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    25  	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
    26  	"k8s.io/apimachinery/pkg/api/meta"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/apimachinery/pkg/watch"
    32  	"k8s.io/client-go/dynamic"
    33  	"k8s.io/kubernetes/test/e2e/framework"
    34  	admissionapi "k8s.io/pod-security-admission/api"
    35  
    36  	"github.com/onsi/ginkgo/v2"
    37  )
    38  
    39  var _ = SIGDescribe("CustomResourceDefinition Watch [Privileged:ClusterAdmin]", func() {
    40  
    41  	f := framework.NewDefaultFramework("crd-watch")
    42  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    43  
    44  	ginkgo.Context("CustomResourceDefinition Watch", func() {
    45  		/*
    46  			Release: v1.16
    47  			Testname: Custom Resource Definition, watch
    48  			Description: Create a Custom Resource Definition. Attempt to watch it; the watch MUST observe create,
    49  			modify and delete events.
    50  		*/
    51  		framework.ConformanceIt("watch on custom resource definition objects", func(ctx context.Context) {
    52  
    53  			const (
    54  				watchCRNameA = "name1"
    55  				watchCRNameB = "name2"
    56  			)
    57  
    58  			config, err := framework.LoadConfig()
    59  			if err != nil {
    60  				framework.Failf("failed to load config: %v", err)
    61  			}
    62  
    63  			apiExtensionClient, err := clientset.NewForConfig(config)
    64  			if err != nil {
    65  				framework.Failf("failed to initialize apiExtensionClient: %v", err)
    66  			}
    67  
    68  			noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
    69  			noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, f.DynamicClient)
    70  			if err != nil {
    71  				framework.Failf("failed to create CustomResourceDefinition: %v", err)
    72  			}
    73  
    74  			defer func() {
    75  				err = fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient)
    76  				if err != nil {
    77  					framework.Failf("failed to delete CustomResourceDefinition: %v", err)
    78  				}
    79  			}()
    80  
    81  			ns := ""
    82  			noxuResourceClient, err := newNamespacedCustomResourceClient(ns, f.DynamicClient, noxuDefinition)
    83  			framework.ExpectNoError(err, "creating custom resource client")
    84  
    85  			watchA, err := watchCRWithName(ctx, noxuResourceClient, watchCRNameA)
    86  			framework.ExpectNoError(err, "failed to watch custom resource: %s", watchCRNameA)
    87  
    88  			watchB, err := watchCRWithName(ctx, noxuResourceClient, watchCRNameB)
    89  			framework.ExpectNoError(err, "failed to watch custom resource: %s", watchCRNameB)
    90  
    91  			testCrA := fixtures.NewNoxuInstance(ns, watchCRNameA)
    92  			testCrB := fixtures.NewNoxuInstance(ns, watchCRNameB)
    93  
    94  			ginkgo.By("Creating first CR ")
    95  			testCrA, err = instantiateCustomResource(ctx, testCrA, noxuResourceClient, noxuDefinition)
    96  			framework.ExpectNoError(err, "failed to instantiate custom resource: %+v", testCrA)
    97  			expectEvent(watchA, watch.Added, testCrA)
    98  			expectNoEvent(watchB, watch.Added, testCrA)
    99  
   100  			ginkgo.By("Creating second CR")
   101  			testCrB, err = instantiateCustomResource(ctx, testCrB, noxuResourceClient, noxuDefinition)
   102  			framework.ExpectNoError(err, "failed to instantiate custom resource: %+v", testCrB)
   103  			expectEvent(watchB, watch.Added, testCrB)
   104  			expectNoEvent(watchA, watch.Added, testCrB)
   105  
   106  			ginkgo.By("Modifying first CR")
   107  			err = patchCustomResource(ctx, noxuResourceClient, watchCRNameA)
   108  			framework.ExpectNoError(err, "failed to patch custom resource: %s", watchCRNameA)
   109  			expectEvent(watchA, watch.Modified, nil)
   110  			expectNoEvent(watchB, watch.Modified, nil)
   111  
   112  			ginkgo.By("Modifying second CR")
   113  			err = patchCustomResource(ctx, noxuResourceClient, watchCRNameB)
   114  			framework.ExpectNoError(err, "failed to patch custom resource: %s", watchCRNameB)
   115  			expectEvent(watchB, watch.Modified, nil)
   116  			expectNoEvent(watchA, watch.Modified, nil)
   117  
   118  			ginkgo.By("Deleting first CR")
   119  			err = deleteCustomResource(ctx, noxuResourceClient, watchCRNameA)
   120  			framework.ExpectNoError(err, "failed to delete custom resource: %s", watchCRNameA)
   121  			expectEvent(watchA, watch.Deleted, nil)
   122  			expectNoEvent(watchB, watch.Deleted, nil)
   123  
   124  			ginkgo.By("Deleting second CR")
   125  			err = deleteCustomResource(ctx, noxuResourceClient, watchCRNameB)
   126  			framework.ExpectNoError(err, "failed to delete custom resource: %s", watchCRNameB)
   127  			expectEvent(watchB, watch.Deleted, nil)
   128  			expectNoEvent(watchA, watch.Deleted, nil)
   129  		})
   130  	})
   131  })
   132  
   133  func watchCRWithName(ctx context.Context, crdResourceClient dynamic.ResourceInterface, name string) (watch.Interface, error) {
   134  	return crdResourceClient.Watch(
   135  		ctx,
   136  		metav1.ListOptions{
   137  			FieldSelector:  "metadata.name=" + name,
   138  			TimeoutSeconds: int64ptr(600),
   139  		},
   140  	)
   141  }
   142  
   143  func instantiateCustomResource(ctx context.Context, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
   144  	createdInstance, err := client.Create(ctx, instanceToCreate, metav1.CreateOptions{})
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	createdObjectMeta, err := meta.Accessor(createdInstance)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	// it should have a UUID
   153  	if len(createdObjectMeta.GetUID()) == 0 {
   154  		return nil, fmt.Errorf("missing uuid: %#v", createdInstance)
   155  	}
   156  	createdTypeMeta, err := meta.TypeAccessor(createdInstance)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	if len(definition.Spec.Versions) != 1 {
   161  		return nil, fmt.Errorf("expected exactly one version, got %v", definition.Spec.Versions)
   162  	}
   163  	if e, a := definition.Spec.Group+"/"+definition.Spec.Versions[0].Name, createdTypeMeta.GetAPIVersion(); e != a {
   164  		return nil, fmt.Errorf("expected %v, got %v", e, a)
   165  	}
   166  	if e, a := definition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
   167  		return nil, fmt.Errorf("expected %v, got %v", e, a)
   168  	}
   169  	return createdInstance, nil
   170  }
   171  
   172  func patchCustomResource(ctx context.Context, client dynamic.ResourceInterface, name string) error {
   173  	_, err := client.Patch(
   174  		ctx,
   175  		name,
   176  		types.JSONPatchType,
   177  		[]byte(`[{ "op": "add", "path": "/dummy", "value": "test" }]`),
   178  		metav1.PatchOptions{})
   179  	return err
   180  }
   181  
   182  func deleteCustomResource(ctx context.Context, client dynamic.ResourceInterface, name string) error {
   183  	return client.Delete(ctx, name, metav1.DeleteOptions{})
   184  }
   185  
   186  func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1.CustomResourceDefinition) (dynamic.ResourceInterface, error) {
   187  	if len(crd.Spec.Versions) != 1 {
   188  		return nil, fmt.Errorf("expected exactly one version, got %v", crd.Spec.Versions)
   189  	}
   190  	gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Versions[0].Name, Resource: crd.Spec.Names.Plural}
   191  
   192  	if crd.Spec.Scope != apiextensionsv1.ClusterScoped {
   193  		return client.Resource(gvr).Namespace(ns), nil
   194  	}
   195  	return client.Resource(gvr), nil
   196  
   197  }
   198  

View as plain text