diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index b32400c5f..f6da14496 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -163,6 +163,26 @@ func TestInstall(t *testing.T) { wantError: true, golden: "output/schema-negative-cli.txt", }, + // Install with subchart, values from yaml, schematized with errors + { + name: "install with schema file and schematized subchart, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart", + wantError: true, + golden: "output/subchart-schema-negative.txt", + }, + // Install with subchart, values from yaml, extra values from cli, schematized with errors + { + name: "install with schema file and schematized subchart, extra values from cli", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=25", + golden: "output/subchart-schema-cli.txt", + }, + // Install with subchart, values from yaml, extra values from cli, schematized with errors + { + name: "install with schema file and schematized subchart, extra values from cli, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25", + wantError: true, + golden: "output/subchart-schema-cli-negative.txt", + }, } runTestActionCmd(t, tests) diff --git a/cmd/helm/testdata/output/schema-negative-cli.txt b/cmd/helm/testdata/output/schema-negative-cli.txt index aac1e2f17..26bc92b1b 100644 --- a/cmd/helm/testdata/output/schema-negative-cli.txt +++ b/cmd/helm/testdata/output/schema-negative-cli.txt @@ -1,3 +1,4 @@ -Error: values don't meet the specification of the schema: +Error: values don't meet the specifications of the schema(s) in the following chart(s): +empty: - age: Must be greater than or equal to 0/1 diff --git a/cmd/helm/testdata/output/schema-negative.txt b/cmd/helm/testdata/output/schema-negative.txt index 4d2376d52..2ea97b7d0 100644 --- a/cmd/helm/testdata/output/schema-negative.txt +++ b/cmd/helm/testdata/output/schema-negative.txt @@ -1,4 +1,5 @@ -Error: values don't meet the specification of the schema: +Error: values don't meet the specifications of the schema(s) in the following chart(s): +empty: - (root): employmentInfo is required - age: Must be greater than or equal to 0/1 diff --git a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt new file mode 100644 index 000000000..86f6e87a2 --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt @@ -0,0 +1,4 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +subchart-with-schema: +- age: Must be greater than or equal to 0/1 + diff --git a/cmd/helm/testdata/output/subchart-schema-cli.txt b/cmd/helm/testdata/output/subchart-schema-cli.txt new file mode 100644 index 000000000..f694bfdf1 --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-cli.txt @@ -0,0 +1,5 @@ +NAME: schema +LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC +NAMESPACE: default +STATUS: deployed + diff --git a/cmd/helm/testdata/output/subchart-schema-negative.txt b/cmd/helm/testdata/output/subchart-schema-negative.txt new file mode 100644 index 000000000..5a84170fd --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-negative.txt @@ -0,0 +1,6 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +chart-without-schema: +- (root): lastname is required +subchart-with-schema: +- (root): age is required + diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/.helmignore b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml new file mode 100644 index 000000000..4e24c2ebb --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +name: chart-without-schema +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml new file mode 100644 index 000000000..b5a77c5db --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +name: subchart-with-schema +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/README.md b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/README.md new file mode 100644 index 000000000..ed73c1797 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/README.md @@ -0,0 +1,3 @@ +#Empty + +This space intentionally left blank. diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json new file mode 100644 index 000000000..4ff791844 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "age" + ] +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json new file mode 100644 index 000000000..f30948038 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + } + }, + "required": [ + "firstname", + "lastname" + ] +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml new file mode 100644 index 000000000..c9deafc00 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml @@ -0,0 +1 @@ +firstname: "John" diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 2f00473a8..7edc75233 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -17,6 +17,7 @@ limitations under the License. package chartutil import ( + "bytes" "fmt" "io" "io/ioutil" @@ -135,7 +136,33 @@ func ReadValuesFile(filename string) (Values, error) { } // ValidateAgainstSchema checks that values does not violate the structure laid out in schema -func ValidateAgainstSchema(values Values, schemaJSON []byte) error { +func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { + var sb strings.Builder + if chrt.Schema != nil { + err := ValidateAgainstSingleSchema(values, chrt.Schema) + if err != nil { + sb.WriteString(fmt.Sprintf("%s:\n", chrt.Name())) + sb.WriteString(err.Error()) + } + } + + // For each dependency, recurively call this function with the coalesced values + for _, subchrt := range chrt.Dependencies() { + subchrtValues := values[subchrt.Name()].(map[string]interface{}) + if err := ValidateAgainstSchema(subchrt, subchrtValues); err != nil { + sb.WriteString(err.Error()) + } + } + + if sb.Len() > 0 { + return errors.New(sb.String()) + } + + return nil +} + +// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema +func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error { valuesData, err := yaml.Marshal(values) if err != nil { return err @@ -144,6 +171,9 @@ func ValidateAgainstSchema(values Values, schemaJSON []byte) error { if err != nil { return err } + if bytes.Equal(valuesJSON, []byte("null")) { + valuesJSON = []byte("{}") + } schemaLoader := gojsonschema.NewBytesLoader(schemaJSON) valuesLoader := gojsonschema.NewBytesLoader(valuesJSON) @@ -154,7 +184,6 @@ func ValidateAgainstSchema(values Values, schemaJSON []byte) error { if !result.Valid() { var sb strings.Builder - sb.WriteString("values don't meet the specification of the schema:\n") for _, desc := range result.Errors() { sb.WriteString(fmt.Sprintf("- %s\n", desc)) } @@ -362,10 +391,9 @@ func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options return top, err } - if chrt.Schema != nil { - if err := ValidateAgainstSchema(vals, chrt.Schema); err != nil { - return top, err - } + if err := ValidateAgainstSchema(chrt, vals); err != nil { + errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" + return top, fmt.Errorf(errFmt, err.Error()) } top["Values"] = vals diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go index 99289843b..e8433b362 100644 --- a/pkg/chartutil/values_test.go +++ b/pkg/chartutil/values_test.go @@ -161,7 +161,7 @@ func TestReadValuesFile(t *testing.T) { matchValues(t, data) } -func TestValidateAgainstSchema(t *testing.T) { +func TestValidateAgainstSingleSchema(t *testing.T) { values, err := ReadValuesFile("./testdata/test-values.yaml") if err != nil { t.Fatalf("Error reading YAML file: %s", err) @@ -171,12 +171,12 @@ func TestValidateAgainstSchema(t *testing.T) { t.Fatalf("Error reading YAML file: %s", err) } - if err := ValidateAgainstSchema(values, schema); err != nil { + if err := ValidateAgainstSingleSchema(values, schema); err != nil { t.Errorf("Error validating Values against Schema: %s", err) } } -func TestValidateAgainstSchemaNegative(t *testing.T) { +func TestValidateAgainstSingleSchemaNegative(t *testing.T) { values, err := ReadValuesFile("./testdata/test-values-negative.yaml") if err != nil { t.Fatalf("Error reading YAML file: %s", err) @@ -187,7 +187,7 @@ func TestValidateAgainstSchemaNegative(t *testing.T) { } var errString string - if err := ValidateAgainstSchema(values, schema); err == nil { + if err := ValidateAgainstSingleSchema(values, schema); err == nil { t.Fatalf("Expected an error, but got nil") } else { errString = err.Error() diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go index d8c9f1435..a0310e934 100644 --- a/pkg/lint/rules/values.go +++ b/pkg/lint/rules/values.go @@ -63,5 +63,5 @@ func validateValuesFile(valuesPath string) error { if err != nil { return err } - return chartutil.ValidateAgainstSchema(values, schema) + return chartutil.ValidateAgainstSingleSchema(values, schema) }