k8s.io/kubernetes@v1.29.3/test/e2e/apimachinery/discovery.go (about) 1 /* 2 Copyright 2019 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 "path" 23 "strings" 24 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 utilversion "k8s.io/apimachinery/pkg/util/version" 28 "k8s.io/apiserver/pkg/endpoints/discovery" 29 clientdiscovery "k8s.io/client-go/discovery" 30 "k8s.io/kubernetes/test/e2e/framework" 31 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 32 "k8s.io/kubernetes/test/utils/crd" 33 "k8s.io/kubernetes/test/utils/format" 34 admissionapi "k8s.io/pod-security-admission/api" 35 36 "github.com/onsi/ginkgo/v2" 37 "github.com/onsi/gomega" 38 ) 39 40 var storageVersionServerVersion = utilversion.MustParseSemantic("v1.13.99") 41 var _ = SIGDescribe("Discovery", func() { 42 f := framework.NewDefaultFramework("discovery") 43 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 44 45 var namespaceName string 46 47 ginkgo.BeforeEach(func() { 48 namespaceName = f.Namespace.Name 49 50 e2eskipper.SkipUnlessServerVersionGTE(storageVersionServerVersion, f.ClientSet.Discovery()) 51 52 ginkgo.By("Setting up server cert") 53 setupServerCert(namespaceName, serviceName) 54 }) 55 56 ginkgo.It("should accurately determine present and missing resources", func(ctx context.Context) { 57 // checks that legacy api group resources function 58 ok, err := clientdiscovery.IsResourceEnabled(f.ClientSet.Discovery(), schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}) 59 framework.ExpectNoError(err) 60 if !ok { 61 framework.Failf("namespace.v1 should always be present") 62 } 63 // checks that non-legacy api group resources function 64 ok, err = clientdiscovery.IsResourceEnabled(f.ClientSet.Discovery(), schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}) 65 framework.ExpectNoError(err) 66 if !ok { 67 framework.Failf("deployments.v1.apps should always be present") 68 } 69 // checks that nonsense resources in existing api groups function 70 ok, err = clientdiscovery.IsResourceEnabled(f.ClientSet.Discovery(), schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "please-dont-ever-create-this"}) 71 framework.ExpectNoError(err) 72 if ok { 73 framework.Failf("please-dont-ever-create-this.v1.apps should never be present") 74 } 75 // checks that resources resources in nonsense api groups function 76 ok, err = clientdiscovery.IsResourceEnabled(f.ClientSet.Discovery(), schema.GroupVersionResource{Group: "not-these-apps", Version: "v1", Resource: "deployments"}) 77 framework.ExpectNoError(err) 78 if ok { 79 framework.Failf("deployments.v1.not-these-apps should never be present") 80 } 81 }) 82 83 ginkgo.It("Custom resource should have storage version hash", func(ctx context.Context) { 84 testcrd, err := crd.CreateTestCRD(f) 85 if err != nil { 86 return 87 } 88 ginkgo.DeferCleanup(testcrd.CleanUp) 89 spec := testcrd.Crd.Spec 90 resources, err := testcrd.APIExtensionClient.Discovery().ServerResourcesForGroupVersion(spec.Group + "/" + spec.Versions[0].Name) 91 if err != nil { 92 framework.Failf("failed to find the discovery doc for %v: %v", resources, err) 93 } 94 found := false 95 var storageVersion string 96 for _, v := range spec.Versions { 97 if v.Storage { 98 storageVersion = v.Name 99 } 100 } 101 // DISCLAIMER: the algorithm of deriving the storageVersionHash 102 // is an implementation detail, which shouldn't be relied on by 103 // the clients. The following calculation is for test purpose 104 // only. 105 expected := discovery.StorageVersionHash(spec.Group, storageVersion, spec.Names.Kind) 106 107 for _, r := range resources.APIResources { 108 if r.Name == spec.Names.Plural { 109 found = true 110 if r.StorageVersionHash != expected { 111 framework.Failf("expected storageVersionHash of %s/%s/%s to be %s, got %s", r.Group, r.Version, r.Name, expected, r.StorageVersionHash) 112 } 113 } 114 } 115 if !found { 116 framework.Failf("didn't find resource %s in the discovery doc", spec.Names.Plural) 117 } 118 }) 119 120 /* 121 Release : v1.19 122 Testname: Discovery, confirm the PreferredVersion for each api group 123 Description: Ensure that a list of apis is retrieved. 124 Each api group found MUST return a valid PreferredVersion unless the group suffix is example.com. 125 */ 126 framework.ConformanceIt("should validate PreferredVersion for each APIGroup", func(ctx context.Context) { 127 128 // get list of APIGroup endpoints 129 list := &metav1.APIGroupList{} 130 err := f.ClientSet.Discovery().RESTClient().Get().AbsPath("/apis/").Do(ctx).Into(list) 131 framework.ExpectNoError(err, "Failed to find /apis/") 132 framework.ExpectNotEqual(len(list.Groups), 0, "Missing APIGroups") 133 134 for _, group := range list.Groups { 135 if strings.HasSuffix(group.Name, ".example.com") { 136 // ignore known example dynamic API groups that are added/removed during the e2e test run 137 continue 138 } 139 framework.Logf("Checking APIGroup: %v", group.Name) 140 141 // locate APIGroup endpoint 142 checkGroup := &metav1.APIGroup{} 143 apiPath := "/apis/" + group.Name + "/" 144 err = f.ClientSet.Discovery().RESTClient().Get().AbsPath(apiPath).Do(ctx).Into(checkGroup) 145 framework.ExpectNoError(err, "Fail to access: %s", apiPath) 146 framework.ExpectNotEqual(len(checkGroup.Versions), 0, "No version found for %v", group.Name) 147 framework.Logf("PreferredVersion.GroupVersion: %s", checkGroup.PreferredVersion.GroupVersion) 148 framework.Logf("Versions found %v", checkGroup.Versions) 149 150 // confirm that the PreferredVersion is a valid version 151 match := false 152 for _, version := range checkGroup.Versions { 153 if version.GroupVersion == checkGroup.PreferredVersion.GroupVersion { 154 framework.Logf("%s matches %s", version.GroupVersion, checkGroup.PreferredVersion.GroupVersion) 155 match = true 156 break 157 } 158 } 159 if !match { 160 framework.Failf("Failed to find a valid version for PreferredVersion %s in versions:\n%s", checkGroup.PreferredVersion.GroupVersion, format.Object(checkGroup.Versions, 1)) 161 } 162 } 163 }) 164 165 /* 166 Release: v1.28 167 Testname: Discovery, confirm the groupVerion and a resourcefrom each apiGroup 168 Description: A resourceList MUST be found for each apiGroup that is retrieved. 169 For each apiGroup the groupVersion MUST equal the groupVersion as reported by 170 the schema. From each resourceList a valid resource MUST be found. 171 */ 172 framework.ConformanceIt("should locate the groupVersion and a resource within each APIGroup", func(ctx context.Context) { 173 174 tests := []struct { 175 apiBasePath string 176 apiGroup string 177 apiVersion string 178 validResource string 179 }{ 180 { 181 apiBasePath: "/api", 182 apiGroup: "", 183 apiVersion: "v1", 184 validResource: "namespaces", 185 }, 186 { 187 apiBasePath: "/apis", 188 apiGroup: "admissionregistration.k8s.io", 189 apiVersion: "v1", 190 validResource: "validatingwebhookconfigurations", 191 }, 192 { 193 apiBasePath: "/apis", 194 apiGroup: "apiextensions.k8s.io", 195 apiVersion: "v1", 196 validResource: "customresourcedefinitions", 197 }, 198 { 199 apiBasePath: "/apis", 200 apiGroup: "apiregistration.k8s.io", 201 apiVersion: "v1", 202 validResource: "apiservices", 203 }, 204 { 205 apiBasePath: "/apis", 206 apiGroup: "apps", 207 apiVersion: "v1", 208 validResource: "deployments", 209 }, 210 { 211 apiBasePath: "/apis", 212 apiGroup: "authentication.k8s.io", 213 apiVersion: "v1", 214 validResource: "tokenreviews", 215 }, 216 { 217 apiBasePath: "/apis", 218 apiGroup: "authorization.k8s.io", 219 apiVersion: "v1", 220 validResource: "selfsubjectaccessreviews", 221 }, 222 { 223 apiBasePath: "/apis", 224 apiGroup: "autoscaling", 225 apiVersion: "v1", 226 validResource: "horizontalpodautoscalers", 227 }, 228 { 229 apiBasePath: "/apis", 230 apiGroup: "autoscaling", 231 apiVersion: "v2", 232 validResource: "horizontalpodautoscalers", 233 }, 234 { 235 apiBasePath: "/apis", 236 apiGroup: "batch", 237 apiVersion: "v1", 238 validResource: "jobs", 239 }, 240 { 241 apiBasePath: "/apis", 242 apiGroup: "certificates.k8s.io", 243 apiVersion: "v1", 244 validResource: "certificatesigningrequests", 245 }, 246 { 247 apiBasePath: "/apis", 248 apiGroup: "coordination.k8s.io", 249 apiVersion: "v1", 250 validResource: "leases", 251 }, 252 { 253 apiBasePath: "/apis", 254 apiGroup: "discovery.k8s.io", 255 apiVersion: "v1", 256 validResource: "endpointslices", 257 }, 258 { 259 apiBasePath: "/apis", 260 apiGroup: "events.k8s.io", 261 apiVersion: "v1", 262 validResource: "events", 263 }, 264 { 265 apiBasePath: "/apis", 266 apiGroup: "networking.k8s.io", 267 apiVersion: "v1", 268 validResource: "ingresses", 269 }, 270 { 271 apiBasePath: "/apis", 272 apiGroup: "node.k8s.io", 273 apiVersion: "v1", 274 validResource: "runtimeclasses", 275 }, 276 { 277 apiBasePath: "/apis", 278 apiGroup: "policy", 279 apiVersion: "v1", 280 validResource: "poddisruptionbudgets", 281 }, 282 { 283 apiBasePath: "/apis", 284 apiGroup: "scheduling.k8s.io", 285 apiVersion: "v1", 286 validResource: "priorityclasses", 287 }, 288 { 289 apiBasePath: "/apis", 290 apiGroup: "storage.k8s.io", 291 apiVersion: "v1", 292 validResource: "csinodes", 293 }, 294 } 295 296 for _, t := range tests { 297 resourceList := &metav1.APIResourceList{} 298 apiPath := path.Join(t.apiBasePath, t.apiGroup, t.apiVersion) 299 ginkgo.By(fmt.Sprintf("Requesting APIResourceList from %q", apiPath)) 300 err := f.ClientSet.Discovery().RESTClient().Get().AbsPath(apiPath).Do(ctx).Into(resourceList) 301 framework.ExpectNoError(err, "Fail to access: %s", apiPath) 302 gomega.Expect(resourceList.GroupVersion).To(gomega.Equal((schema.GroupVersion{Group: t.apiGroup, Version: t.apiVersion}).String())) 303 304 foundResource := false 305 for _, r := range resourceList.APIResources { 306 if t.validResource == r.Name { 307 foundResource = true 308 break 309 } 310 } 311 gomega.Expect(foundResource).To(gomega.BeTrue(), "Resource %q was not found inside of resourceList\n%#v", t.validResource, resourceList.APIResources) 312 } 313 }) 314 })