diff --git a/pkg/kubectl/cmd/util/openapi/document.go b/pkg/kubectl/cmd/util/openapi/document.go index 23e83b3dda1..6b2f6782b94 100644 --- a/pkg/kubectl/cmd/util/openapi/document.go +++ b/pkg/kubectl/cmd/util/openapi/document.go @@ -164,8 +164,9 @@ func (d *Definitions) parseReference(s *openapi_v2.Schema, path *Path) (Schema, if _, ok := d.models[reference]; !ok { return nil, newSchemaError(path, "unknown model in reference: %q", reference) } - return &Reference{ - Reference: reference, + return &Ref{ + BaseSchema: d.parseBaseSchema(s, path), + reference: reference, definitions: d, }, nil } @@ -303,38 +304,27 @@ func (d *Definitions) LookupResource(gvk schema.GroupVersionKind) Schema { return model } -// SchemaReference doesn't match a specific type. It's mostly a -// pass-through type. -type Reference struct { - Reference string +type Ref struct { + BaseSchema + reference string definitions *Definitions } -var _ Schema = &Reference{} +var _ Reference = &Ref{} -func (r *Reference) GetSubSchema() Schema { - return r.definitions.models[r.Reference] +func (r *Ref) Reference() string { + return r.reference } -func (r *Reference) Accept(s SchemaVisitor) { - r.GetSubSchema().Accept(s) +func (r *Ref) SubSchema() Schema { + return r.definitions.models[r.reference] } -func (r *Reference) GetDescription() string { - return r.GetSubSchema().GetDescription() +func (r *Ref) Accept(v SchemaVisitor) { + v.VisitReference(r) } -func (r *Reference) GetExtensions() map[string]interface{} { - return r.GetSubSchema().GetExtensions() -} - -func (*Reference) GetPath() *Path { - // Reference never has a path, because it can be referenced from - // multiple locations. - return &Path{} -} - -func (r *Reference) GetName() string { - return r.Reference +func (r *Ref) GetName() string { + return fmt.Sprintf("Reference to %q", r.reference) } diff --git a/pkg/kubectl/cmd/util/openapi/openapi.go b/pkg/kubectl/cmd/util/openapi/openapi.go index 81f1f0eae5b..8e6cf0639f4 100644 --- a/pkg/kubectl/cmd/util/openapi/openapi.go +++ b/pkg/kubectl/cmd/util/openapi/openapi.go @@ -49,11 +49,13 @@ type Resources interface { // - Map is a map of string to one and only one given subtype // - Primitive can be string, integer, number and boolean. // - Kind is an object with specific fields mapping to specific types. +// - Reference is a link to another definition. type SchemaVisitor interface { VisitArray(*Array) VisitMap(*Map) VisitPrimitive(*Primitive) VisitKind(*Kind) + VisitReference(Reference) } // Schema is the base definition of an openapi type. @@ -219,3 +221,11 @@ func (p *Primitive) GetName() string { } return fmt.Sprintf("%s (%s)", p.Type, p.Format) } + +// Reference implementation depends on the type of document. +type Reference interface { + Schema + + Reference() string + SubSchema() Schema +} diff --git a/pkg/kubectl/cmd/util/openapi/openapi_test.go b/pkg/kubectl/cmd/util/openapi/openapi_test.go index e2eb2fa0c0d..93736c93ca7 100644 --- a/pkg/kubectl/cmd/util/openapi/openapi_test.go +++ b/pkg/kubectl/cmd/util/openapi/openapi_test.go @@ -78,20 +78,20 @@ var _ = Describe("Reading apps/v1beta1/Deployment from openAPIData", func() { It("should have a metadata key of type Reference", func() { Expect(deployment.Fields).To(HaveKey("metadata")) - key := deployment.Fields["metadata"].(*openapi.Reference) + key := deployment.Fields["metadata"].(openapi.Reference) Expect(key).ToNot(BeNil()) - Expect(key.Reference).To(Equal("io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta")) - subSchema := key.GetSubSchema().(*openapi.Kind) + Expect(key.Reference()).To(Equal("io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta")) + subSchema := key.SubSchema().(*openapi.Kind) Expect(subSchema).ToNot(BeNil()) }) var status *openapi.Kind It("should have a status key of type Reference", func() { Expect(deployment.Fields).To(HaveKey("status")) - key := deployment.Fields["status"].(*openapi.Reference) + key := deployment.Fields["status"].(openapi.Reference) Expect(key).ToNot(BeNil()) - Expect(key.Reference).To(Equal("io.k8s.api.apps.v1beta1.DeploymentStatus")) - status = key.GetSubSchema().(*openapi.Kind) + Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentStatus")) + status = key.SubSchema().(*openapi.Kind) Expect(status).ToNot(BeNil()) }) @@ -106,22 +106,22 @@ var _ = Describe("Reading apps/v1beta1/Deployment from openAPIData", func() { Expect(status.Fields).To(HaveKey("conditions")) conditions := status.Fields["conditions"].(*openapi.Array) Expect(conditions).ToNot(BeNil()) - Expect(conditions.GetName()).To(Equal("Array of io.k8s.api.apps.v1beta1.DeploymentCondition")) + Expect(conditions.GetName()).To(Equal(`Array of Reference to "io.k8s.api.apps.v1beta1.DeploymentCondition"`)) Expect(conditions.GetExtensions()).To(Equal(map[string]interface{}{ "x-kubernetes-patch-merge-key": "type", "x-kubernetes-patch-strategy": "merge", })) - condition := conditions.SubType.(*openapi.Reference) - Expect(condition.Reference).To(Equal("io.k8s.api.apps.v1beta1.DeploymentCondition")) + condition := conditions.SubType.(openapi.Reference) + Expect(condition.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentCondition")) }) var spec *openapi.Kind It("should have a spec key of type Reference", func() { Expect(deployment.Fields).To(HaveKey("spec")) - key := deployment.Fields["spec"].(*openapi.Reference) + key := deployment.Fields["spec"].(openapi.Reference) Expect(key).ToNot(BeNil()) - Expect(key.Reference).To(Equal("io.k8s.api.apps.v1beta1.DeploymentSpec")) - spec = key.GetSubSchema().(*openapi.Kind) + Expect(key.Reference()).To(Equal("io.k8s.api.apps.v1beta1.DeploymentSpec")) + spec = key.SubSchema().(*openapi.Kind) Expect(spec).ToNot(BeNil()) }) @@ -132,9 +132,9 @@ var _ = Describe("Reading apps/v1beta1/Deployment from openAPIData", func() { It("should have a spec with a PodTemplateSpec sub-field", func() { Expect(spec.Fields).To(HaveKey("template")) - key := spec.Fields["template"].(*openapi.Reference) + key := spec.Fields["template"].(openapi.Reference) Expect(key).ToNot(BeNil()) - Expect(key.Reference).To(Equal("io.k8s.api.core.v1.PodTemplateSpec")) + Expect(key.Reference()).To(Equal("io.k8s.api.core.v1.PodTemplateSpec")) }) }) @@ -164,10 +164,10 @@ var _ = Describe("Reading authorization.k8s.io/v1/SubjectAccessReview from openA sar := schema.(*openapi.Kind) Expect(sar).ToNot(BeNil()) Expect(sar.Fields).To(HaveKey("spec")) - specRef := sar.Fields["spec"].(*openapi.Reference) + specRef := sar.Fields["spec"].(openapi.Reference) Expect(specRef).ToNot(BeNil()) - Expect(specRef.Reference).To(Equal("io.k8s.api.authorization.v1.SubjectAccessReviewSpec")) - sarspec = specRef.GetSubSchema().(*openapi.Kind) + Expect(specRef.Reference()).To(Equal("io.k8s.api.authorization.v1.SubjectAccessReviewSpec")) + sarspec = specRef.SubSchema().(*openapi.Kind) Expect(sarspec).ToNot(BeNil()) }) diff --git a/pkg/kubectl/cmd/util/openapi/validation/types.go b/pkg/kubectl/cmd/util/openapi/validation/types.go index 335444c6c24..2c16c2b16fc 100644 --- a/pkg/kubectl/cmd/util/openapi/validation/types.go +++ b/pkg/kubectl/cmd/util/openapi/validation/types.go @@ -127,6 +127,11 @@ func (item *mapItem) VisitKind(schema *openapi.Kind) { } } +func (item *mapItem) VisitReference(schema openapi.Reference) { + // passthrough + schema.SubSchema().Accept(item) +} + // arrayItem represents a yaml array. type arrayItem struct { baseItem @@ -165,6 +170,11 @@ func (item *arrayItem) VisitKind(schema *openapi.Kind) { item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "array", Actual: "map"}) } +func (item *arrayItem) VisitReference(schema openapi.Reference) { + // passthrough + schema.SubSchema().Accept(item) +} + // primitiveItem represents a yaml value. type primitiveItem struct { baseItem @@ -216,6 +226,11 @@ func (item *primitiveItem) VisitKind(schema *openapi.Kind) { item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: item.Kind}) } +func (item *primitiveItem) VisitReference(schema openapi.Reference) { + // passthrough + schema.SubSchema().Accept(item) +} + // itemFactory creates the relevant item type/visitor based on the current yaml type. func itemFactory(path openapi.Path, v interface{}) (ValidationItem, error) { // We need to special case for no-type fields in yaml (e.g. empty item in list)