From fad5353ef830b1df98216041ceb411ab2e699105 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Fri, 13 May 2022 11:45:31 -0700 Subject: [PATCH] Integration test for openapi scale & status --- .../apiserver/openapi/openapi_merge_test.go | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 test/integration/apiserver/openapi/openapi_merge_test.go diff --git a/test/integration/apiserver/openapi/openapi_merge_test.go b/test/integration/apiserver/openapi/openapi_merge_test.go new file mode 100644 index 00000000000..2bc43d95072 --- /dev/null +++ b/test/integration/apiserver/openapi/openapi_merge_test.go @@ -0,0 +1,199 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package openapi + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/apiextensions-apiserver/test/integration/fixtures" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + genericfeatures "k8s.io/apiserver/pkg/features" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/dynamic" + kubernetes "k8s.io/client-go/kubernetes" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kube-openapi/pkg/spec3" + apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" + "k8s.io/kubernetes/test/integration/framework" +) + +const scaleSchemaName = "io.k8s.api.autoscaling.v1.Scale" +const statusSchemaName = "io.k8s.apimachinery.pkg.apis.meta.v1.Status" +const objectMetaSchemaName = "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + +func TestOpenAPIV3MultipleCRDsSameGV(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.OpenAPIV3, true)() + server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd()) + if err != nil { + t.Fatal(err) + } + defer server.TearDownFn() + config := server.ClientConfig + + apiExtensionClient, err := clientset.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + + fooWithSubresourceCRD := &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foosubs.cr.bar.com", + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Group: "cr.bar.com", + Scope: apiextensionsv1.NamespaceScoped, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "foosubs", + Kind: "FooSub", + }, + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ + { + Name: "v1", + Served: true, + Storage: true, + Schema: &apiextensionsv1.CustomResourceValidation{ + OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "spec": { + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "replicas": { + Type: "integer", + }, + }, + }, + "status": { + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "replicas": { + Type: "integer", + }, + }, + }, + }, + }, + }, + Subresources: &apiextensionsv1.CustomResourceSubresources{ + Status: &apiextensionsv1.CustomResourceSubresourceStatus{}, + Scale: &apiextensionsv1.CustomResourceSubresourceScale{ + SpecReplicasPath: ".spec.replicas", + StatusReplicasPath: ".status.replicas", + }, + }, + }, + }, + }, + } + + bazWithSubresourceCRD := &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bazsubs.cr.bar.com", + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Group: "cr.bar.com", + Scope: apiextensionsv1.NamespaceScoped, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "bazsubs", + Kind: "BazSub", + }, + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ + { + Name: "v1", + Served: true, + Storage: true, + Schema: &apiextensionsv1.CustomResourceValidation{ + OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "spec": { + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "replicas": { + Type: "integer", + }, + }, + }, + "status": { + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "replicas": { + Type: "integer", + }, + }, + }, + }, + }, + }, + Subresources: &apiextensionsv1.CustomResourceSubresources{ + Status: &apiextensionsv1.CustomResourceSubresourceStatus{}, + Scale: &apiextensionsv1.CustomResourceSubresourceScale{ + SpecReplicasPath: ".spec.replicas", + StatusReplicasPath: ".status.replicas", + }, + }, + }, + }, + }, + } + + _, err = fixtures.CreateNewV1CustomResourceDefinition(fooWithSubresourceCRD, apiExtensionClient, dynamicClient) + if err != nil { + t.Fatal(err) + } + + _, err = fixtures.CreateNewV1CustomResourceDefinition(bazWithSubresourceCRD, apiExtensionClient, dynamicClient) + if err != nil { + t.Fatal(err) + } + + // It takes a second for CRD specs to propagate to the aggregator + time.Sleep(4 * time.Second) + + jsonData, err := clientset.RESTClient().Get().AbsPath("/openapi/v3/apis/cr.bar.com/v1").Do(context.TODO()).Raw() + if err != nil { + t.Fatal(err) + } + + openAPISpec := spec3.OpenAPI{} + err = json.Unmarshal(jsonData, &openAPISpec) + if err != nil { + t.Fatal(err) + } + + // Ensure both CRD schemas are present in the OpenAPI + // Scale, status, and metadata dependencies must also be present + assert.Contains(t, openAPISpec.Components.Schemas, scaleSchemaName) + assert.Contains(t, openAPISpec.Components.Schemas, statusSchemaName) + assert.Contains(t, openAPISpec.Components.Schemas, objectMetaSchemaName) + assert.Contains(t, openAPISpec.Components.Schemas, "com.bar.cr.v1.BazSub") + assert.Contains(t, openAPISpec.Components.Schemas, "com.bar.cr.v1.FooSub") +}