k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/apimachinery/openapiv3.go (about) 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 })