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  })