mirror of https://github.com/k3s-io/k3s
openapi: Change reference to be first-class
References in the openapi are currently completely hidden from the model, and just passed through as we walk the tree. The problem is that they can have a different description and more importantly, different extensions. Change them to be first-class citizen, and fully part of the model. It means that visitors have to implement one more function and decide if something specific should be done with references. Validation is updated to just completely ignore them and passthrough (like it was done before).pull/6/head
parent
2f00e6d72c
commit
5ef3516780
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue