diff --git a/kyaml/go.mod b/kyaml/go.mod index 3acc35ab7..91b3470aa 100644 --- a/kyaml/go.mod +++ b/kyaml/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/davecgh/go-spew v1.1.1 github.com/go-errors/errors v1.0.1 + github.com/go-openapi/errors v0.19.2 github.com/go-openapi/spec v0.19.5 github.com/go-openapi/strfmt v0.19.5 github.com/go-openapi/validate v0.19.8 diff --git a/kyaml/setters2/set.go b/kyaml/setters2/set.go index ea624bfae..c95b0f0bc 100644 --- a/kyaml/setters2/set.go +++ b/kyaml/setters2/set.go @@ -248,8 +248,23 @@ func validateAgainstSchema(ext *CliExtension, sch *spec.Schema) error { var inputYAML string if len(ext.Setter.ListValues) > 0 { - tmpl, err := template.New("validator"). - Parse(`{{.key}}:{{block "list" .values}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}`) + // tmplText contains the template we will use to produce a yaml + // document that we can use for validation. + var tmplText string + if sch.Items != nil && sch.Items.Schema != nil && + sch.Items.Schema.Type.Contains("string") { + // If string is one of the legal types for the value, we + // output it with quotes in the yaml document to make sure it + // is later parsed as a string. + tmplText = `{{.key}}:{{block "list" .values}}{{"\n"}}{{range .}}{{printf "- %q\n" .}}{{end}}{{end}}` + } else { + // If string is not specifically set as the type, we just + // let the yaml unmarshaller detect the correct type. Thus, we + // do not add quotes around the value. + tmplText = `{{.key}}:{{block "list" .values}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}` + } + + tmpl, err := template.New("validator").Parse(tmplText) if err != nil { return err } @@ -263,7 +278,15 @@ func validateAgainstSchema(ext *CliExtension, sch *spec.Schema) error { } inputYAML = builder.String() } else { - inputYAML = fmt.Sprintf("%s: %s", ext.Setter.Name, ext.Setter.Value) + var format string + // Only add quotes around the value is string is one of the + // types in the schema. + if sch.Type.Contains("string") { + format = "%s: \"%s\"" + } else { + format = "%s: %s" + } + inputYAML = fmt.Sprintf(format, ext.Setter.Name, ext.Setter.Value) } input := map[string]interface{}{} diff --git a/kyaml/setters2/set_test.go b/kyaml/setters2/set_test.go index 8b75aefc0..189b76750 100644 --- a/kyaml/setters2/set_test.go +++ b/kyaml/setters2/set_test.go @@ -1296,6 +1296,8 @@ openAPI: } func TestValidateAgainstSchema(t *testing.T) { + maxLength := int64(3) + testCases := []struct { name string setter *setter @@ -1303,6 +1305,15 @@ func TestValidateAgainstSchema(t *testing.T) { shouldValidate bool expectedErrorMsg string }{ + { + name: "no schema", + setter: &setter{ + Name: "foo", + Value: "bar", + }, + schema: spec.SchemaProps{}, + shouldValidate: true, + }, { name: "simple string value", setter: &setter{ @@ -1348,7 +1359,7 @@ func TestValidateAgainstSchema(t *testing.T) { shouldValidate: true, }, { - name: "number value should not be accepted as string", + name: "number value should be accepted as integer", setter: &setter{ Name: "foo", Value: "45", @@ -1358,6 +1369,19 @@ func TestValidateAgainstSchema(t *testing.T) { }, shouldValidate: true, }, + { + name: "string type allows string-specific validations", + setter: &setter{ + Name: "foo", + Value: "1234", + }, + schema: spec.SchemaProps{ + Type: []string{"string"}, + MaxLength: &maxLength, + }, + shouldValidate: false, + expectedErrorMsg: "foo in body should be at most 3 chars long", + }, { name: "list with int values", setter: &setter{ @@ -1395,6 +1419,44 @@ func TestValidateAgainstSchema(t *testing.T) { shouldValidate: false, expectedErrorMsg: "foo in body must be of type integer", }, + { + name: "all values should satisfy type string", + setter: &setter{ + Name: "foo", + Value: "1234", + }, + schema: spec.SchemaProps{ + Type: []string{"string"}, + }, + shouldValidate: true, + }, + { + name: "all values should satisfy type string even in arrays", + setter: &setter{ + Name: "foo", + ListValues: []string{"123", "456"}, + }, + schema: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + }, + }, + }, + }, + shouldValidate: true, + }, + { + name: "List values without any schema", + setter: &setter{ + Name: "foo", + ListValues: []string{"123", "true", "abc"}, + }, + schema: spec.SchemaProps{}, + shouldValidate: true, + }, } for i := range testCases {