diff --git a/pkg/cmd/util/factory.go b/pkg/cmd/util/factory.go index cd806d62..e6414f3e 100644 --- a/pkg/cmd/util/factory.go +++ b/pkg/cmd/util/factory.go @@ -62,8 +62,10 @@ type Factory interface { // Returns a schema that can validate objects stored on disk. Validator(validationDirective string) (validation.Schema, error) - // OpenAPISchema returns the parsed openapi schema definition - OpenAPISchema() (openapi.Resources, error) + + // Used for retrieving openapi v2 resources. + openapi.OpenAPIResourcesGetter + // OpenAPIV3Schema returns a client for fetching parsed schemas for // any group version OpenAPIV3Client() (openapiclient.Client, error) diff --git a/pkg/cmd/util/factory_client_access.go b/pkg/cmd/util/factory_client_access.go index 3943fc30..4b47d5a3 100644 --- a/pkg/cmd/util/factory_client_access.go +++ b/pkg/cmd/util/factory_client_access.go @@ -154,13 +154,8 @@ func (f *factoryImpl) Validator(validationDirective string) (validation.Schema, return validation.NullSchema{}, nil } - resources, err := f.OpenAPISchema() - if err != nil { - return nil, err - } - schema := validation.ConjunctiveSchema{ - validation.NewSchemaValidation(resources), + validation.NewSchemaValidation(f), validation.NoDoubleKeySchema{}, } diff --git a/pkg/util/openapi/openapi.go b/pkg/util/openapi/openapi.go index de2fffad..74955da3 100644 --- a/pkg/util/openapi/openapi.go +++ b/pkg/util/openapi/openapi.go @@ -24,6 +24,13 @@ import ( "sigs.k8s.io/yaml" ) +// OpenAPIResourcesGetter represents a function to return +// OpenAPI V2 resource specifications. Used for lazy-loading +// these resource specifications. +type OpenAPIResourcesGetter interface { + OpenAPISchema() (Resources, error) +} + // Resources interface describe a resources provider, that can give you // resource based on group-version-kind. type Resources interface { diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 3ba3213d..47c74e5b 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -29,14 +29,14 @@ import ( // schemaValidation validates the object against an OpenAPI schema. type schemaValidation struct { - resources openapi.Resources + resourcesGetter openapi.OpenAPIResourcesGetter } // NewSchemaValidation creates a new Schema that can be used // to validate objects. -func NewSchemaValidation(resources openapi.Resources) Schema { +func NewSchemaValidation(resourcesGetter openapi.OpenAPIResourcesGetter) Schema { return &schemaValidation{ - resources: resources, + resourcesGetter: resourcesGetter, } } @@ -56,7 +56,6 @@ func (v *schemaValidation) ValidateBytes(data []byte) error { if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) { return utilerrors.NewAggregate(v.validateList(obj)) } - return utilerrors.NewAggregate(v.validateResource(obj, gvk)) } @@ -81,7 +80,12 @@ func (v *schemaValidation) validateList(object interface{}) []error { } func (v *schemaValidation) validateResource(obj interface{}, gvk schema.GroupVersionKind) []error { - resource := v.resources.LookupResource(gvk) + // This lazy-loads the OpenAPI V2 specifications, caching the specs. + resources, err := v.resourcesGetter.OpenAPISchema() + if err != nil { + return []error{err} + } + resource := resources.LookupResource(gvk) if resource == nil { // resource is not present, let's just skip validation. return nil diff --git a/pkg/validation/validation_test.go b/pkg/validation/validation_test.go index 0247a821..d0cc8eb6 100644 --- a/pkg/validation/validation_test.go +++ b/pkg/validation/validation_test.go @@ -32,14 +32,22 @@ import ( var fakeSchema = testing.Fake{Path: filepath.Join("..", "..", "testdata", "openapi", "swagger.json")} +// fakeResourcesGetter implements the OpenAPIResourcesGetter interface, returning the +// openapi resources from the swagger.json (above). +type fakeResourcesGetter struct{} + +func (r *fakeResourcesGetter) OpenAPISchema() (openapi.Resources, error) { + doc, err := fakeSchema.OpenAPISchema() + if err != nil { + return nil, err + } + return openapi.NewOpenAPIData(doc) +} + var _ = Describe("resource validation using OpenAPI Schema", func() { var validator Schema BeforeEach(func() { - s, err := fakeSchema.OpenAPISchema() - Expect(err).ToNot(HaveOccurred()) - resources, err := openapi.NewOpenAPIData(s) - Expect(err).ToNot(HaveOccurred()) - validator = NewSchemaValidation(resources) + validator = NewSchemaValidation(&fakeResourcesGetter{}) Expect(validator).ToNot(BeNil()) })