k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/util/proto/openapi_test.go (about) 1 /* 2 Copyright 2017 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 proto_test 18 19 import ( 20 "path/filepath" 21 22 openapi_v3 "github.com/google/gnostic-models/openapiv3" 23 . "github.com/onsi/ginkgo/v2" 24 . "github.com/onsi/gomega" 25 26 "k8s.io/kube-openapi/pkg/util/proto" 27 "k8s.io/kube-openapi/pkg/util/proto/testing" 28 ) 29 30 var fakeSchema = testing.Fake{Path: filepath.Join("testdata", "swagger.json")} 31 var fakeSchemaNext = testing.Fake{Path: filepath.Join("testdata", "swagger_next.json")} 32 var fakeSchemaV300 = testing.FakeV3{Path: filepath.Join("testdata", "openapi_v3_0_0")} 33 34 var _ = Describe("Reading apps/v1beta1/Deployment from v1.8 openAPIData", func() { 35 var models proto.Models 36 BeforeEach(func() { 37 s, err := fakeSchema.OpenAPISchema() 38 Expect(err).To(BeNil()) 39 models, err = proto.NewOpenAPIData(s) 40 Expect(err).To(BeNil()) 41 }) 42 43 model := "io.k8s.api.apps.v1beta1.Deployment" 44 var schema proto.Schema 45 It("should lookup the Schema by its model name", func() { 46 schema = models.LookupModel(model) 47 Expect(schema).ToNot(BeNil()) 48 }) 49 50 var deployment *proto.Kind 51 It("should be a Kind", func() { 52 deployment = schema.(*proto.Kind) 53 Expect(deployment).ToNot(BeNil()) 54 }) 55 56 It("should have a path", func() { 57 Expect(deployment.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1beta1.Deployment"})) 58 }) 59 60 It("should have a kind key of type string", func() { 61 Expect(deployment.Fields).To(HaveKey("kind")) 62 key := deployment.Fields["kind"].(*proto.Primitive) 63 Expect(key).ToNot(BeNil()) 64 Expect(key.Type).To(Equal("string")) 65 Expect(key.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1beta1.Deployment", ".kind"})) 66 }) 67 68 It("should have a apiVersion key of type string", func() { 69 Expect(deployment.Fields).To(HaveKey("apiVersion")) 70 key := deployment.Fields["apiVersion"].(*proto.Primitive) 71 Expect(key).ToNot(BeNil()) 72 Expect(key.Type).To(Equal("string")) 73 Expect(key.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1beta1.Deployment", ".apiVersion"})) 74 }) 75 76 It("should have a metadata key of type Reference", func() { 77 Expect(deployment.Fields).To(HaveKey("metadata")) 78 key := deployment.Fields["metadata"].(proto.Reference) 79 Expect(key).ToNot(BeNil()) 80 Expect(key.Reference()).To(Equal("io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta")) 81 subSchema := key.SubSchema().(*proto.Kind) 82 Expect(subSchema).ToNot(BeNil()) 83 }) 84 85 var status *proto.Kind 86 It("should have a status key of type Reference", func() { 87 Expect(deployment.Fields).To(HaveKey("status")) 88 key := deployment.Fields["status"].(proto.Reference) 89 Expect(key).ToNot(BeNil()) 90 Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentStatus")) 91 status = key.SubSchema().(*proto.Kind) 92 Expect(status).ToNot(BeNil()) 93 }) 94 95 It("should have a valid DeploymentStatus", func() { 96 By("having availableReplicas key") 97 Expect(status.Fields).To(HaveKey("availableReplicas")) 98 replicas := status.Fields["availableReplicas"].(*proto.Primitive) 99 Expect(replicas).ToNot(BeNil()) 100 Expect(replicas.Type).To(Equal("integer")) 101 102 By("having conditions key") 103 Expect(status.Fields).To(HaveKey("conditions")) 104 conditions := status.Fields["conditions"].(*proto.Array) 105 Expect(conditions).ToNot(BeNil()) 106 Expect(conditions.GetName()).To(Equal(`Array of Reference to "io.k8s.api.apps.v1beta1.DeploymentCondition"`)) 107 Expect(conditions.GetExtensions()).To(Equal(map[string]interface{}{ 108 "x-kubernetes-patch-merge-key": "type", 109 "x-kubernetes-patch-strategy": "merge", 110 })) 111 condition := conditions.SubType.(proto.Reference) 112 Expect(condition.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentCondition")) 113 }) 114 115 var spec *proto.Kind 116 It("should have a spec key of type Reference", func() { 117 Expect(deployment.Fields).To(HaveKey("spec")) 118 key := deployment.Fields["spec"].(proto.Reference) 119 Expect(key).ToNot(BeNil()) 120 Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentSpec")) 121 spec = key.SubSchema().(*proto.Kind) 122 Expect(spec).ToNot(BeNil()) 123 }) 124 125 It("should have a spec with no gvk", func() { 126 _, found := spec.GetExtensions()["x-kubernetes-group-version-kind"] 127 Expect(found).To(BeFalse()) 128 }) 129 130 It("should have a spec with a PodTemplateSpec sub-field", func() { 131 Expect(spec.Fields).To(HaveKey("template")) 132 key := spec.Fields["template"].(proto.Reference) 133 Expect(key).ToNot(BeNil()) 134 Expect(key.Reference()).To(Equal("io.k8s.api.core.v1.PodTemplateSpec")) 135 }) 136 }) 137 138 var _ = Describe("Reading apps/v1beta1/Deployment from v1.11 openAPIData", func() { 139 var models proto.Models 140 BeforeEach(func() { 141 s, err := fakeSchemaNext.OpenAPISchema() 142 Expect(err).To(BeNil()) 143 models, err = proto.NewOpenAPIData(s) 144 Expect(err).To(BeNil()) 145 }) 146 147 model := "io.k8s.api.apps.v1beta1.Deployment" 148 var schema proto.Schema 149 It("should lookup the Schema by its model name", func() { 150 schema = models.LookupModel(model) 151 Expect(schema).ToNot(BeNil()) 152 }) 153 154 var deployment *proto.Kind 155 It("should be a Kind", func() { 156 deployment = schema.(*proto.Kind) 157 Expect(deployment).ToNot(BeNil()) 158 }) 159 }) 160 161 var _ = Describe("Reading apps/v1beta1/ControllerRevision from v1.11 openAPIData", func() { 162 var models proto.Models 163 BeforeEach(func() { 164 s, err := fakeSchemaNext.OpenAPISchema() 165 Expect(err).To(BeNil()) 166 models, err = proto.NewOpenAPIData(s) 167 Expect(err).To(BeNil()) 168 }) 169 170 model := "io.k8s.api.apps.v1beta1.ControllerRevision" 171 var schema proto.Schema 172 It("should lookup the Schema by its model name", func() { 173 schema = models.LookupModel(model) 174 Expect(schema).ToNot(BeNil()) 175 }) 176 177 var cr *proto.Kind 178 It("data property should be map[string]Arbitrary", func() { 179 cr = schema.(*proto.Kind) 180 Expect(cr).ToNot(BeNil()) 181 Expect(cr.Fields).To(HaveKey("data")) 182 183 data := cr.Fields["data"].(*proto.Map) 184 Expect(data).ToNot(BeNil()) 185 Expect(data.GetName()).To(Equal("Map of Arbitrary value (primitive, object or array)")) 186 Expect(data.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1beta1.ControllerRevision", ".data"})) 187 188 arbitrary := data.SubType.(*proto.Arbitrary) 189 Expect(arbitrary).ToNot(BeNil()) 190 Expect(arbitrary.GetName()).To(Equal("Arbitrary value (primitive, object or array)")) 191 Expect(arbitrary.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1beta1.ControllerRevision", ".data"})) 192 }) 193 }) 194 195 var _ = Describe("Reading authorization.k8s.io/v1/SubjectAccessReview from openAPIData", func() { 196 var models proto.Models 197 BeforeEach(func() { 198 s, err := fakeSchema.OpenAPISchema() 199 Expect(err).To(BeNil()) 200 models, err = proto.NewOpenAPIData(s) 201 Expect(err).To(BeNil()) 202 }) 203 204 model := "io.k8s.api.authorization.v1.LocalSubjectAccessReview" 205 var schema proto.Schema 206 It("should lookup the Schema by its model", func() { 207 schema = models.LookupModel(model) 208 Expect(schema).ToNot(BeNil()) 209 }) 210 211 var sarspec *proto.Kind 212 It("should be a Kind and have a spec", func() { 213 sar := schema.(*proto.Kind) 214 Expect(sar).ToNot(BeNil()) 215 Expect(sar.Fields).To(HaveKey("spec")) 216 specRef := sar.Fields["spec"].(proto.Reference) 217 Expect(specRef).ToNot(BeNil()) 218 Expect(specRef.Reference()).To(Equal("io.k8s.api.authorization.v1.SubjectAccessReviewSpec")) 219 sarspec = specRef.SubSchema().(*proto.Kind) 220 Expect(sarspec).ToNot(BeNil()) 221 }) 222 223 It("should have a valid SubjectAccessReviewSpec", func() { 224 Expect(sarspec.Fields).To(HaveKey("extra")) 225 extra := sarspec.Fields["extra"].(*proto.Map) 226 Expect(extra).ToNot(BeNil()) 227 Expect(extra.GetName()).To(Equal("Map of Array of string")) 228 Expect(extra.GetPath().Get()).To(Equal([]string{"io.k8s.api.authorization.v1.SubjectAccessReviewSpec", ".extra"})) 229 array := extra.SubType.(*proto.Array) 230 Expect(array).ToNot(BeNil()) 231 Expect(array.GetName()).To(Equal("Array of string")) 232 Expect(array.GetPath().Get()).To(Equal([]string{"io.k8s.api.authorization.v1.SubjectAccessReviewSpec", ".extra"})) 233 str := array.SubType.(*proto.Primitive) 234 Expect(str).ToNot(BeNil()) 235 Expect(str.Type).To(Equal("string")) 236 Expect(str.GetName()).To(Equal("string")) 237 Expect(str.GetPath().Get()).To(Equal([]string{"io.k8s.api.authorization.v1.SubjectAccessReviewSpec", ".extra"})) 238 }) 239 }) 240 241 var _ = Describe("Path", func() { 242 It("can be created by NewPath", func() { 243 path := proto.NewPath("key") 244 Expect(path.String()).To(Equal("key")) 245 }) 246 It("can create and print complex paths", func() { 247 key := proto.NewPath("key") 248 array := key.ArrayPath(12) 249 field := array.FieldPath("subKey") 250 251 Expect(field.String()).To(Equal("key[12].subKey")) 252 }) 253 It("has a length", func() { 254 key := proto.NewPath("key") 255 array := key.ArrayPath(12) 256 field := array.FieldPath("subKey") 257 258 Expect(field.Len()).To(Equal(3)) 259 }) 260 It("can look like an array", func() { 261 key := proto.NewPath("key") 262 array := key.ArrayPath(12) 263 field := array.FieldPath("subKey") 264 265 Expect(field.Get()).To(Equal([]string{"key", "[12]", ".subKey"})) 266 }) 267 }) 268 269 var _ = Describe("Reading apps/v1/Deployment from v3.0.0 openAPIData", func() { 270 var deployment *proto.Kind 271 BeforeEach(func() { 272 var models proto.Models 273 s, schemaErr := fakeSchemaV300.OpenAPIV3Schema("apps/v1") 274 models, modelsErr := proto.NewOpenAPIV3Data(s) 275 276 Expect(schemaErr).To(BeNil()) 277 Expect(modelsErr).To(BeNil()) 278 279 model := "io.k8s.api.apps.v1.Deployment" 280 schema := models.LookupModel(model) 281 Expect(schema).ToNot(BeNil()) 282 283 deployment = schema.(*proto.Kind) 284 Expect(deployment).ToNot(BeNil()) 285 }) 286 287 It("should have a path", func() { 288 Expect(deployment.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1.Deployment"})) 289 }) 290 291 It("should have a kind key of type string", func() { 292 Expect(deployment.Fields).To(HaveKey("kind")) 293 key := deployment.Fields["kind"].(*proto.Primitive) 294 Expect(key).ToNot(BeNil()) 295 Expect(key.Type).To(Equal("string")) 296 Expect(key.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1.Deployment", ".kind"})) 297 }) 298 299 It("should have a apiVersion key of type string", func() { 300 Expect(deployment.Fields).To(HaveKey("apiVersion")) 301 key := deployment.Fields["apiVersion"].(*proto.Primitive) 302 Expect(key).ToNot(BeNil()) 303 Expect(key.Type).To(Equal("string")) 304 Expect(key.GetPath().Get()).To(Equal([]string{"io.k8s.api.apps.v1.Deployment", ".apiVersion"})) 305 }) 306 307 It("should have a metadata key of type Reference", func() { 308 Expect(deployment.Fields).To(HaveKey("metadata")) 309 key := deployment.Fields["metadata"].(proto.Reference) 310 Expect(key).ToNot(BeNil()) 311 Expect(key.Reference()).To(Equal("io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta")) 312 subSchema := key.SubSchema().(*proto.Kind) 313 Expect(subSchema).ToNot(BeNil()) 314 }) 315 316 Describe("status", func() { 317 var status *proto.Kind 318 BeforeEach(func() { 319 Expect(deployment.Fields).To(HaveKey("status")) 320 key := deployment.Fields["status"].(proto.Reference) 321 Expect(key).ToNot(BeNil()) 322 Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1.DeploymentStatus")) 323 status = key.SubSchema().(*proto.Kind) 324 Expect(status).ToNot(BeNil()) 325 }) 326 327 It("should have a valid DeploymentStatus", func() { 328 By("having availableReplicas key") 329 Expect(status.Fields).To(HaveKey("availableReplicas")) 330 replicas := status.Fields["availableReplicas"].(*proto.Primitive) 331 Expect(replicas).ToNot(BeNil()) 332 Expect(replicas.Type).To(Equal("integer")) 333 334 By("having conditions key") 335 Expect(status.Fields).To(HaveKey("conditions")) 336 conditions := status.Fields["conditions"].(*proto.Array) 337 Expect(conditions).ToNot(BeNil()) 338 Expect(conditions.GetName()).To(Equal(`Array of Reference to "io.k8s.api.apps.v1.DeploymentCondition"`)) 339 Expect(conditions.GetExtensions()).To(Equal(map[string]interface{}{ 340 "x-kubernetes-patch-merge-key": "type", 341 "x-kubernetes-patch-strategy": "merge", 342 })) 343 condition := conditions.SubType.(proto.Reference) 344 Expect(condition.Reference()).To(Equal("io.k8s.api.apps.v1.DeploymentCondition")) 345 }) 346 }) 347 348 Describe("spec subschema", func() { 349 var spec *proto.Kind 350 BeforeEach(func() { 351 Expect(deployment.Fields).To(HaveKey("spec")) 352 key, _ := deployment.Fields["spec"].(proto.Reference) 353 Expect(key).ToNot(BeNil()) 354 Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1.DeploymentSpec")) 355 spec = key.SubSchema().(*proto.Kind) 356 Expect(spec).ToNot(BeNil()) 357 }) 358 359 It("should have a spec with no gvk", func() { 360 _, found := spec.GetExtensions()["x-kubernetes-group-version-kind"] 361 Expect(found).To(BeFalse()) 362 }) 363 364 It("should have a spec with a PodTemplateSpec sub-field", func() { 365 Expect(spec.Fields).To(HaveKey("template")) 366 key := spec.Fields["template"].(proto.Reference) 367 Expect(key).ToNot(BeNil()) 368 Expect(key.Reference()).To(Equal("io.k8s.api.core.v1.PodTemplateSpec")) 369 }) 370 }) 371 }) 372 373 var _ = Describe("Reading v3 OpenAPI spec with x-kubernetes-group-version-kind", func() { 374 spec := []byte(`{ 375 "openapi": "3.0.0", 376 "info": { 377 "title": "Kubernetes", 378 "version": "v1.24.0" 379 }, 380 "paths": { 381 "/foo": { 382 "get": { 383 "responses": { 384 "200": { 385 "description": "OK", 386 "content": { 387 "application/json": { 388 "schema": { 389 "$ref": "#/components/schemas/Foo" 390 } 391 } 392 } 393 } 394 } 395 } 396 } 397 }, 398 "components": { 399 "schemas": { 400 "Foo": { 401 "type": "object", 402 "properties": {}, 403 "x-kubernetes-group-version-kind": [ 404 { 405 "group": "foo", 406 "kind": "Foo", 407 "version": "v1" 408 } 409 ] 410 } 411 } 412 } 413 }`) 414 var schema proto.Schema 415 416 BeforeEach(func() { 417 document, err := openapi_v3.ParseDocument(spec) 418 Expect(err).To(BeNil()) 419 420 models, modelsErr := proto.NewOpenAPIV3Data(document) 421 Expect(modelsErr).To(BeNil()) 422 423 model := "Foo" 424 schema = models.LookupModel(model) 425 Expect(schema).ToNot(BeNil()) 426 }) 427 428 It("should have an extension with gvk", func() { 429 _, found := schema.GetExtensions()["x-kubernetes-group-version-kind"] 430 Expect(found).To(BeTrue()) 431 }) 432 433 It("should convert to proto.Kind type", func() { 434 foo, ok := schema.(*proto.Kind) 435 Expect(ok).To(BeTrue()) 436 Expect(foo).ToNot(BeNil()) 437 }) 438 })