This checks subcharts for schema validation

The final coalesced values for a given chart will be validated against
that chart's schema, as well as any dependent subchart's schema

Signed-off-by: Ian Howell <ian.howell0@gmail.com>
pull/5350/head
Ian Howell 7 years ago
parent 1f0f973d5a
commit 02dc0c3f5b

@ -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)

@ -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

@ -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

@ -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

@ -0,0 +1,5 @@
NAME: schema
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
NAMESPACE: default
STATUS: deployed

@ -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

@ -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

@ -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

@ -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

@ -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"
]
}

@ -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"
]
}

@ -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

@ -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()

@ -63,5 +63,5 @@ func validateValuesFile(valuesPath string) error {
if err != nil {
return err
}
return chartutil.ValidateAgainstSchema(values, schema)
return chartutil.ValidateAgainstSingleSchema(values, schema)
}

Loading…
Cancel
Save