1
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
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
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
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
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
102
103
104
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
126 framework.ConformanceIt("should validate PreferredVersion for each APIGroup", func(ctx context.Context) {
127
128
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 gomega.Expect(list.Groups).ToNot(gomega.BeEmpty(), "Missing APIGroups")
133
134 for _, group := range list.Groups {
135 if strings.HasSuffix(group.Name, ".example.com") {
136
137 continue
138 }
139 framework.Logf("Checking APIGroup: %v", group.Name)
140
141
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 gomega.Expect(checkGroup.Versions).ToNot(gomega.BeEmpty(), "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
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
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 })
315
View as plain text