...

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

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

     1  /*
     2  Copyright 2023 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  	"reflect"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/onsi/ginkgo/v2"
    29  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    30  	apiextensionclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    31  	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime/schema"
    34  	"k8s.io/apimachinery/pkg/util/wait"
    35  	"k8s.io/apiserver/pkg/storage/names"
    36  	"k8s.io/client-go/dynamic"
    37  	"k8s.io/client-go/openapi3"
    38  	aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
    39  	"k8s.io/kube-openapi/pkg/spec3"
    40  	imageutils "k8s.io/kubernetes/test/utils/image"
    41  	admissionapi "k8s.io/pod-security-admission/api"
    42  	samplev1beta1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1beta1"
    43  
    44  	"k8s.io/kubernetes/test/e2e/framework"
    45  
    46  	// ensure libs have a chance to initialize
    47  	_ "github.com/stretchr/testify/assert"
    48  )
    49  
    50  var _ = SIGDescribe("OpenAPIV3", func() {
    51  	f := framework.NewDefaultFramework("openapiv3")
    52  	f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
    53  
    54  	/*
    55  		Release : v1.27
    56  		Testname: OpenAPI V3 RoundTrip
    57  		Description: Fetch the OpenAPI v3 of all built-in group versions. The OpenAPI specs MUST roundtrip successfully.
    58  	*/
    59  	ginkgo.It("should round trip OpenAPI V3 for all built-in group versions", func(ctx context.Context) {
    60  		c := openapi3.NewRoot(f.ClientSet.Discovery().OpenAPIV3())
    61  		gvs, err := c.GroupVersions()
    62  		framework.ExpectNoError(err)
    63  		// List of built in types that do not contain the k8s.io suffix
    64  		builtinGVs := map[string]bool{
    65  			"apps":        true,
    66  			"autoscaling": true,
    67  			"batch":       true,
    68  			"policy":      true,
    69  		}
    70  
    71  		for _, gv := range gvs {
    72  			// Prevent race conditions with looking up gvs of CRDs and
    73  			// other aggregated apiservers added by other tests
    74  			if !strings.HasSuffix(gv.Group, "k8s.io") && !builtinGVs[gv.Group] {
    75  				continue
    76  			}
    77  			spec1, err := c.GVSpec(gv)
    78  			framework.ExpectNoError(err)
    79  			specMarshalled, err := json.Marshal(spec1)
    80  			framework.ExpectNoError(err)
    81  			var spec2 spec3.OpenAPI
    82  			json.Unmarshal(specMarshalled, &spec2)
    83  
    84  			if !reflect.DeepEqual(*spec1, spec2) {
    85  				diff := cmp.Diff(*spec1, spec2)
    86  				framework.Failf("%s", diff)
    87  			}
    88  		}
    89  	})
    90  
    91  	/*
    92  		Release : v1.27
    93  		Testname: OpenAPI V3 CustomResourceDefinition
    94  		Description: Create a CustomResourceDefinition. The OpenAPI V3 document of the CustomResourceDefinition MUST be created. The OpenAPI V3 MUST be round trippable.
    95  	*/
    96  	ginkgo.It("should publish OpenAPI V3 for CustomResourceDefinition", func(ctx context.Context) {
    97  		config, err := framework.LoadConfig()
    98  		framework.ExpectNoError(err)
    99  		apiExtensionClient, err := apiextensionclientset.NewForConfig(config)
   100  		framework.ExpectNoError(err)
   101  		dynamicClient, err := dynamic.NewForConfig(config)
   102  		framework.ExpectNoError(err)
   103  		resourceName := "testcrd"
   104  		// Generate a CRD with random group name to avoid group conflict with other tests that run in parallel.
   105  		groupName := fmt.Sprintf("%s.example.com", names.SimpleNameGenerator.GenerateName("group"))
   106  		crd := &apiextensionsv1.CustomResourceDefinition{
   107  			ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%ss.%s", resourceName, groupName)},
   108  			Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   109  				Group: groupName,
   110  				Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   111  					{
   112  						Name:    "v1beta1",
   113  						Served:  true,
   114  						Storage: true,
   115  						Schema:  fixtures.AllowAllSchema(),
   116  					},
   117  				},
   118  				Names: apiextensionsv1.CustomResourceDefinitionNames{
   119  					Plural:   resourceName + "s",
   120  					Singular: resourceName,
   121  					Kind:     resourceName,
   122  					ListKind: resourceName + "List",
   123  				},
   124  				Scope: apiextensionsv1.NamespaceScoped,
   125  			},
   126  		}
   127  		gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Versions[0].Name}
   128  		_, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
   129  		defer func() {
   130  			_ = fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionClient)
   131  		}()
   132  
   133  		framework.ExpectNoError(err)
   134  		c := openapi3.NewRoot(f.ClientSet.Discovery().OpenAPIV3())
   135  		var openAPISpec *spec3.OpenAPI
   136  		// Poll for the OpenAPI to be updated with the new CRD
   137  		err = wait.PollUntilContextTimeout(context.Background(), time.Second*1, wait.ForeverTestTimeout, false, func(context.Context) (bool, error) {
   138  			openAPISpec, err = c.GVSpec(gv)
   139  			if err == nil {
   140  				return true, nil
   141  			}
   142  			return false, nil
   143  		})
   144  		framework.ExpectNoError(err, "timed out getting new CustomResourceDefinition")
   145  
   146  		specMarshalled, err := json.Marshal(openAPISpec)
   147  		framework.ExpectNoError(err)
   148  		var spec2 spec3.OpenAPI
   149  		json.Unmarshal(specMarshalled, &spec2)
   150  
   151  		if !reflect.DeepEqual(*openAPISpec, spec2) {
   152  			diff := cmp.Diff(*openAPISpec, spec2)
   153  			framework.Failf("%s", diff)
   154  		}
   155  
   156  		err = fixtures.DeleteV1CustomResourceDefinition(crd, apiExtensionClient)
   157  		framework.ExpectNoError(err, "deleting CustomResourceDefinition")
   158  		// Poll for the OpenAPI to be updated with the deleted CRD
   159  		err = wait.PollUntilContextTimeout(ctx, time.Second*1, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
   160  			_, err = c.GVSpec(gv)
   161  			if err == nil {
   162  				return false, nil
   163  			}
   164  			_, isNotFound := err.(*openapi3.GroupVersionNotFoundError)
   165  			return isNotFound, nil
   166  		})
   167  		framework.ExpectNoError(err, "should not contain OpenAPI V3 for deleted CustomResourceDefinition")
   168  	})
   169  
   170  	/*
   171  		Release : v1.27
   172  		Testname: OpenAPI V3 Aggregated APIServer
   173  		Description: Create an Aggregated APIServer. The OpenAPI V3 for the aggregated apiserver MUST be aggregated by the aggregator and published. The specification MUST be round trippable.
   174  	*/
   175  	ginkgo.It("should contain OpenAPI V3 for Aggregated APIServer", func(ctx context.Context) {
   176  		config, err := framework.LoadConfig()
   177  		framework.ExpectNoError(err)
   178  		aggrclient, err := aggregatorclient.NewForConfig(config)
   179  		framework.ExpectNoError(err)
   180  		names := generateSampleAPIServerObjectNames(f.Namespace.Name)
   181  		SetUpSampleAPIServer(ctx, f, aggrclient, imageutils.GetE2EImage(imageutils.APIServer), names, samplev1beta1.GroupName, "v1beta1")
   182  		defer cleanupSampleAPIServer(ctx, f.ClientSet, aggrclient, names, "v1beta1.wardle.example.com")
   183  
   184  		c := openapi3.NewRoot(f.ClientSet.Discovery().OpenAPIV3())
   185  		gv := schema.GroupVersion{Group: samplev1beta1.GroupName, Version: "v1beta1"}
   186  		var openAPISpec *spec3.OpenAPI
   187  		// Poll for the OpenAPI to be updated with the new aggregated apiserver.
   188  		wait.Poll(time.Second*1, wait.ForeverTestTimeout, func() (bool, error) {
   189  			openAPISpec, err = c.GVSpec(gv)
   190  			if err == nil {
   191  				return true, nil
   192  			}
   193  			return false, nil
   194  		})
   195  
   196  		specMarshalled, err := json.Marshal(openAPISpec)
   197  		framework.ExpectNoError(err)
   198  		var spec2 spec3.OpenAPI
   199  		json.Unmarshal(specMarshalled, &spec2)
   200  
   201  		if !reflect.DeepEqual(*openAPISpec, spec2) {
   202  			diff := cmp.Diff(*openAPISpec, spec2)
   203  			framework.Failf("%s", diff)
   204  		}
   205  
   206  		cleanupSampleAPIServer(ctx, f.ClientSet, aggrclient, names, "v1beta1.wardle.example.com")
   207  		// Poll for the OpenAPI to be updated with the deleted aggregated apiserver.
   208  		err = wait.PollUntilContextTimeout(ctx, time.Second*1, wait.ForeverTestTimeout, true, func(_ context.Context) (bool, error) {
   209  			_, err = c.GVSpec(gv)
   210  			if err == nil {
   211  				return false, nil
   212  			}
   213  			_, isNotFound := err.(*openapi3.GroupVersionNotFoundError)
   214  			return isNotFound, nil
   215  		})
   216  		framework.ExpectNoError(err, "should not contain OpenAPI V3 for deleted APIService")
   217  	})
   218  })
   219  

View as plain text