...

Source file src/k8s.io/apiextensions-apiserver/test/integration/helpers.go

Documentation: k8s.io/apiextensions-apiserver/test/integration

     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 integration
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    25  	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    26  	"k8s.io/apimachinery/pkg/api/errors"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/client-go/dynamic"
    32  	_ "k8s.io/component-base/logs/testinit" // enable logging flags
    33  )
    34  
    35  var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
    36  
    37  func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
    38  	return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name)
    39  }
    40  
    41  func instantiateVersionedCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1.CustomResourceDefinition, version string) (*unstructured.Unstructured, error) {
    42  	createdInstance, err := client.Create(context.TODO(), instanceToCreate, metav1.CreateOptions{})
    43  	if err != nil {
    44  		t.Logf("%#v", createdInstance)
    45  		return nil, err
    46  	}
    47  	createdObjectMeta, err := meta.Accessor(createdInstance)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	// it should have a UUID
    52  	if len(createdObjectMeta.GetUID()) == 0 {
    53  		t.Errorf("missing uuid: %#v", createdInstance)
    54  	}
    55  	createdTypeMeta, err := meta.TypeAccessor(createdInstance)
    56  	if err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	if e, a := definition.Spec.Group+"/"+version, createdTypeMeta.GetAPIVersion(); e != a {
    60  		t.Errorf("expected %v, got %v", e, a)
    61  	}
    62  	if e, a := definition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
    63  		t.Errorf("expected %v, got %v", e, a)
    64  	}
    65  	return createdInstance, nil
    66  }
    67  
    68  func newNamespacedCustomResourceVersionedClient(ns string, client dynamic.Interface, crd *apiextensionsv1.CustomResourceDefinition, version string) dynamic.ResourceInterface {
    69  	gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural}
    70  
    71  	if crd.Spec.Scope != apiextensionsv1.ClusterScoped {
    72  		return client.Resource(gvr).Namespace(ns)
    73  	}
    74  	return client.Resource(gvr)
    75  }
    76  
    77  func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1.CustomResourceDefinition) dynamic.ResourceInterface {
    78  	return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name)
    79  }
    80  
    81  // UpdateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
    82  func UpdateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1.CustomResourceDefinition)) (*apiextensionsv1.CustomResourceDefinition, error) {
    83  	for i := 0; i < 5; i++ {
    84  		crd, err := client.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
    85  		if err != nil {
    86  			return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err)
    87  		}
    88  		update(crd)
    89  		crd, err = client.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
    90  		if err == nil {
    91  			return crd, nil
    92  		}
    93  		if !errors.IsConflict(err) {
    94  			return nil, fmt.Errorf("failed to update CustomResourceDefinition %q: %v", name, err)
    95  		}
    96  	}
    97  	return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
    98  }
    99  
   100  // UpdateV1CustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
   101  func UpdateV1CustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1.CustomResourceDefinition)) (*apiextensionsv1.CustomResourceDefinition, error) {
   102  	for i := 0; i < 5; i++ {
   103  		crd, err := client.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{})
   104  		if err != nil {
   105  			return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err)
   106  		}
   107  		update(crd)
   108  		crd, err = client.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{})
   109  		if err == nil {
   110  			return crd, nil
   111  		}
   112  		if !errors.IsConflict(err) {
   113  			return nil, fmt.Errorf("failed to update CustomResourceDefinition %q: %v", name, err)
   114  		}
   115  	}
   116  	return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
   117  }
   118  
   119  // getSchemaForVersion returns the validation schema for given version in given CRD.
   120  func getSchemaForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) (*apiextensionsv1.CustomResourceValidation, error) {
   121  	for _, v := range crd.Spec.Versions {
   122  		if version == v.Name {
   123  			return v.Schema, nil
   124  		}
   125  	}
   126  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   127  }
   128  
   129  // getSubresourcesForVersion returns the subresources for given version in given CRD.
   130  func getSubresourcesForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) (*apiextensionsv1.CustomResourceSubresources, error) {
   131  	for _, v := range crd.Spec.Versions {
   132  		if version == v.Name {
   133  			return v.Subresources, nil
   134  		}
   135  	}
   136  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   137  }
   138  
   139  // getColumnsForVersion returns the columns for given version in given CRD.
   140  // NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
   141  // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
   142  // the original CRD object instead.
   143  func getColumnsForVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) ([]apiextensionsv1.CustomResourceColumnDefinition, error) {
   144  	for _, v := range crd.Spec.Versions {
   145  		if version == v.Name {
   146  			return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
   147  		}
   148  	}
   149  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   150  }
   151  
   152  // serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
   153  // NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
   154  // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
   155  // the original CRD object instead.
   156  func serveDefaultColumnsIfEmpty(columns []apiextensionsv1.CustomResourceColumnDefinition) []apiextensionsv1.CustomResourceColumnDefinition {
   157  	if len(columns) > 0 {
   158  		return columns
   159  	}
   160  	return []apiextensionsv1.CustomResourceColumnDefinition{
   161  		{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
   162  	}
   163  }
   164  

View as plain text