diff --git a/kyaml/openapi/openapi.go b/kyaml/openapi/openapi.go index 5f1ace8e5..a349388fa 100644 --- a/kyaml/openapi/openapi.go +++ b/kyaml/openapi/openapi.go @@ -6,6 +6,7 @@ package openapi import ( "encoding/json" "fmt" + "io/ioutil" "sync" "github.com/go-openapi/spec" @@ -44,6 +45,66 @@ func SchemaForResourceType(t yaml.TypeMeta) *ResourceSchema { return &ResourceSchema{Schema: rs} } +// SupplementaryOpenAPIFieldName is the conventional field name (JSON/YAML) containing +// supplementary OpenAPI definitions. +const SupplementaryOpenAPIFieldName = "openAPI" + +// AddSchemaFromFile reads the file at path and parses the OpenAPI definitions +// from the field "openAPI" +func AddSchemaFromFile(path string) error { + return AddSchemaFromFileUsingField(path, SupplementaryOpenAPIFieldName) +} + +// AddSchemaFromFileUsingField reads the file at path and parses the OpenAPI definitions +// from the specified field. If field is the empty string, use the whole document as +// OpenAPI. +func AddSchemaFromFileUsingField(path, field string) error { + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + // parse the yaml file (json is a subset of yaml, so will also parse) + y, err := yaml.Parse(string(b)) + if err != nil { + return err + } + + if field != "" { + // get the field containing the openAPI + m := y.Field(field) + if yaml.IsFieldEmpty(m) { + // doesn't contain openAPI definitions + return nil + } + y = m.Value + } + + oAPI, err := y.String() + if err != nil { + return err + } + + // convert the yaml openAPI to a JSON string by unmarshalling it to an + // interface{} and the marshalling it to a string + var o interface{} + err = yaml.Unmarshal([]byte(oAPI), &o) + if err != nil { + return err + } + j, err := json.Marshal(o) + if err != nil { + return err + } + + // add the json schema to the global schema + _, err = AddSchema(j) + if err != nil { + return err + } + return nil +} + // AddSchema parses s, and adds definitions from s to the global schema. func AddSchema(s []byte) (*spec.Schema, error) { return parse(s) @@ -296,41 +357,6 @@ func resolve(root interface{}, ref *spec.Ref) (*spec.Schema, error) { } } -func PopulateDefsInOpenAPI(s string) error { - y, err := yaml.Parse(s) - if err != nil { - return err - } - // get the field containing the openAPI - f := y.Field("openAPI") - - defs, err := f.Value.String() - if err != nil { - return err - } - - // convert the yaml openAPI to an interface{} - // which can be marshalled into json - var o interface{} - err = yaml.Unmarshal([]byte(defs), &o) - if err != nil { - return err - } - - // convert the interface{} into a json string - j, err := json.Marshal(o) - if err != nil { - return err - } - - // add the json schema to the global schema - _, err = AddSchema(j) - if err != nil { - return err - } - return nil -} - func rootSchema() *spec.Schema { initSchema() return &globalSchema.schema diff --git a/kyaml/openapi/openapi_test.go b/kyaml/openapi/openapi_test.go index 988818f5f..bf0228986 100644 --- a/kyaml/openapi/openapi_test.go +++ b/kyaml/openapi/openapi_test.go @@ -5,6 +5,7 @@ package openapi import ( "fmt" + "io/ioutil" "testing" "github.com/stretchr/testify/assert" @@ -123,8 +124,8 @@ func TestSchemaForResourceType(t *testing.T) { } } -func TestPopulateDefsInOpenAPI_Setter(t *testing.T) { - globalSchema = openapiData{} +func TestAddSchemaFromFile(t *testing.T) { + ResetOpenAPI() inputyaml := ` openAPI: definitions: @@ -134,8 +135,15 @@ openAPI: name: image-name value: "nginx" ` - err := PopulateDefsInOpenAPI(inputyaml) + f, err := ioutil.TempFile("", "openapi-") + if !assert.NoError(t, err) { + t.FailNow() + } + if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) { + t.FailNow() + } + err = AddSchemaFromFile(f.Name()) if !assert.NoError(t, err) { t.FailNow() } @@ -153,7 +161,7 @@ openAPI: } func TestPopulateDefsInOpenAPI_Substitution(t *testing.T) { - globalSchema = openapiData{} + ResetOpenAPI() inputyaml := ` openAPI: definitions: @@ -178,11 +186,18 @@ openAPI: - marker: "IMAGE_TAG" ref: "#/definitions/io.k8s.cli.setters.image-tag" ` - err := PopulateDefsInOpenAPI(inputyaml) + f, err := ioutil.TempFile("", "openapi-") if !assert.NoError(t, err) { t.FailNow() } + if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) { + t.FailNow() + } + + if !assert.NoError(t, AddSchemaFromFile(f.Name())) { + t.FailNow() + } s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.cli.substitutions.image"}`) @@ -199,3 +214,26 @@ openAPI: ` map[marker:IMAGE_TAG ref:#/definitions/io.k8s.cli.setters.image-tag]]]]]`, fmt.Sprintf("%v", s.Schema.Extensions)) } + +func TestAddSchemaFromFile_empty(t *testing.T) { + ResetOpenAPI() + inputyaml := ` +kind: Example + ` + + f, err := ioutil.TempFile("", "openapi-") + if !assert.NoError(t, err) { + t.FailNow() + } + if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) { + t.FailNow() + } + + if !assert.NoError(t, AddSchemaFromFile(f.Name())) { + t.FailNow() + } + + if !assert.Equal(t, len(globalSchema.schema.Definitions), 0) { + t.FailNow() + } +}