mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
fix(schema): Add schema $ref resolution for all commands
Extends JSON schema validation to support relative $ref imports across helm lint, install, template, and upgrade commands. Addresses George's review comments: - Pass chartDir directly instead of deriving from valuesPath - Create new functions instead of changing public API signatures - Use absolute file paths for proper URL-based $ref resolution Implementation: - Added ValidateAgainstSchemaWithPath to resolve $ref using absolute file paths instead of root-relative URLs - Thread chartDir through Install/Upgrade structs and all validation functions - Updated both v2 (public) and v3 (internal) lint rules Fixes relative $ref resolution when running commands like: helm lint ../sample-chart helm install release ../sample-chart Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
This commit is contained in:
parent
30d803283d
commit
a7884d228d
25 changed files with 140 additions and 70 deletions
|
|
@ -97,7 +97,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
|
|||
return
|
||||
}
|
||||
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidation(chart, cvals, options, caps, skipSchemaValidation)
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidationAndPath(chart, cvals, options, caps, skipSchemaValidation, linter.ChartDir)
|
||||
if err != nil {
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]any,
|
|||
return
|
||||
}
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides, skipSchemaValidation))
|
||||
linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(linter.ChartDir, valueOverrides, skipSchemaValidation))
|
||||
}
|
||||
|
||||
func validateValuesFileExistence(valuesPath string) error {
|
||||
|
|
@ -52,7 +52,10 @@ func validateValuesFileExistence(valuesPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaValidation bool) error {
|
||||
func validateValuesFile(chartDir string, overrides map[string]any, skipSchemaValidation bool) error {
|
||||
valuesPath := filepath.Join(chartDir, "values.yaml")
|
||||
schemaPath := filepath.Join(chartDir, "values.schema.json")
|
||||
|
||||
values, err := common.ReadValuesFile(valuesPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse YAML: %w", err)
|
||||
|
|
@ -66,8 +69,6 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
|
|||
coalescedValues := util.CoalesceTables(make(map[string]any, len(overrides)), overrides)
|
||||
coalescedValues = util.CoalesceTables(coalescedValues, values)
|
||||
|
||||
ext := filepath.Ext(valuesPath)
|
||||
schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json"
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if len(schema) == 0 {
|
||||
return nil
|
||||
|
|
@ -75,9 +76,9 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(schemaPath)
|
||||
|
||||
if !skipSchemaValidation {
|
||||
return util.ValidateAgainstSingleSchema(coalescedValues, schema, baseDir)
|
||||
return util.ValidateAgainstSingleSchemaWithPath(coalescedValues, schema, schemaPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -66,8 +66,7 @@ func TestValidateValuesFileWellFormed(t *testing.T) {
|
|||
not:well[]{}formed
|
||||
`
|
||||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml))
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, map[string]any{}, false); err == nil {
|
||||
if err := validateValuesFile(tmpdir, map[string]any{}, false); err == nil {
|
||||
t.Fatal("expected values file to fail parsing")
|
||||
}
|
||||
}
|
||||
|
|
@ -77,8 +76,7 @@ func TestValidateValuesFileSchema(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, map[string]any{}, false); err != nil {
|
||||
if err := validateValuesFile(tmpdir, map[string]any{}, false); err != nil {
|
||||
t.Fatalf("Failed validation with %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,9 +87,7 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, map[string]any{}, false)
|
||||
err := validateValuesFile(tmpdir, map[string]any{}, false)
|
||||
if err == nil {
|
||||
t.Fatal("expected values file to fail parsing")
|
||||
}
|
||||
|
|
@ -105,9 +101,7 @@ func TestValidateValuesFileSchemaFailureButWithSkipSchemaValidation(t *testing.T
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, map[string]any{}, true)
|
||||
err := validateValuesFile(tmpdir, map[string]any{}, true)
|
||||
if err != nil {
|
||||
t.Fatal("expected values file to pass parsing because of skipSchemaValidation")
|
||||
}
|
||||
|
|
@ -121,8 +115,7 @@ func TestValidateValuesFileSchemaOverrides(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, overrides, false); err != nil {
|
||||
if err := validateValuesFile(tmpdir, overrides, false); err != nil {
|
||||
t.Fatalf("Failed validation with %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -157,9 +150,7 @@ func TestValidateValuesFile(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(tt.yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, tt.overrides, false)
|
||||
err := validateValuesFile(tmpdir, tt.overrides, false)
|
||||
|
||||
switch {
|
||||
case err != nil && tt.errorMessage == "":
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ type Install struct {
|
|||
// Used by helm template to add the release as part of OutputDir path
|
||||
// OutputDir/<ReleaseName>
|
||||
UseReleaseName bool
|
||||
// ChartDir is the local directory path of the chart, used for resolving
|
||||
// relative $ref in JSON schemas. Empty for remote charts.
|
||||
ChartDir string
|
||||
// TakeOwnership will ignore the check for helm annotations and take ownership of the resources.
|
||||
TakeOwnership bool
|
||||
PostRenderer postrenderer.PostRenderer
|
||||
|
|
@ -358,7 +361,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
|||
IsInstall: !isUpgrade,
|
||||
IsUpgrade: isUpgrade,
|
||||
}
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidation(chrt, vals, options, caps, i.SkipSchemaValidation)
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidationAndPath(chrt, vals, options, caps, i.SkipSchemaValidation, i.ChartDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ func TestLintChart(t *testing.T) {
|
|||
name: "chart-with-schema",
|
||||
chartPath: "testdata/charts/chart-with-schema",
|
||||
},
|
||||
{
|
||||
name: "chart-with-schema-ref",
|
||||
chartPath: "testdata/charts/chart-with-schema-ref",
|
||||
},
|
||||
{
|
||||
name: "chart-with-schema-negative",
|
||||
chartPath: "testdata/charts/chart-with-schema-negative",
|
||||
|
|
|
|||
3
pkg/action/testdata/charts/chart-with-schema-ref/Chart.yaml
vendored
Normal file
3
pkg/action/testdata/charts/chart-with-schema-ref/Chart.yaml
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
apiVersion: v2
|
||||
name: chart-with-schema-ref
|
||||
version: 0.1.0
|
||||
4
pkg/action/testdata/charts/chart-with-schema-ref/name.schema.json
vendored
Normal file
4
pkg/action/testdata/charts/chart-with-schema-ref/name.schema.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "string"
|
||||
}
|
||||
7
pkg/action/testdata/charts/chart-with-schema-ref/values.schema.json
vendored
Normal file
7
pkg/action/testdata/charts/chart-with-schema-ref/values.schema.json
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "$ref": "name.schema.json" }
|
||||
}
|
||||
}
|
||||
1
pkg/action/testdata/charts/chart-with-schema-ref/values.yaml
vendored
Normal file
1
pkg/action/testdata/charts/chart-with-schema-ref/values.yaml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
name: "test"
|
||||
|
|
@ -113,6 +113,9 @@ type Upgrade struct {
|
|||
HideNotes bool
|
||||
// SkipSchemaValidation determines if JSON schema validation is disabled.
|
||||
SkipSchemaValidation bool
|
||||
// ChartDir is the local directory path of the chart, used for resolving
|
||||
// relative $ref in JSON schemas. Empty for remote charts.
|
||||
ChartDir string
|
||||
// Description is the description of this operation
|
||||
Description string
|
||||
Labels map[string]string
|
||||
|
|
@ -291,7 +294,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
|
|||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidation(chart, vals, options, caps, u.SkipSchemaValidation)
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidationAndPath(chart, vals, options, caps, u.SkipSchemaValidation, u.ChartDir)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,14 +75,31 @@ func newHTTPURLLoader() *HTTPURLLoader {
|
|||
|
||||
// ValidateAgainstSchema checks that values does not violate the structure laid out in schema
|
||||
func ValidateAgainstSchema(ch chart.Charter, values map[string]any) error {
|
||||
return ValidateAgainstSchemaWithPath(ch, values, "")
|
||||
}
|
||||
|
||||
func ValidateAgainstSchemaWithPath(ch chart.Charter, values map[string]any, chartDir string) error {
|
||||
chrt, err := chart.NewAccessor(ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var absChartPath string
|
||||
if chartDir != "" {
|
||||
absChartPath, err = filepath.Abs(chartDir)
|
||||
} else {
|
||||
absChartPath, err = filepath.Abs(chrt.ChartFullPath())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
if chrt.Schema() != nil {
|
||||
slog.Debug("chart name", "chart-name", chrt.Name())
|
||||
err := ValidateAgainstSingleSchema(values, chrt.Schema(), chrt.ChartFullPath())
|
||||
|
||||
schemaPath := filepath.Join(absChartPath, "values.schema.json")
|
||||
err = ValidateAgainstSingleSchemaWithPath(values, chrt.Schema(), schemaPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(&sb, "%s:\n", chrt.Name())
|
||||
sb.WriteString(err.Error())
|
||||
|
|
@ -109,7 +126,8 @@ func ValidateAgainstSchema(ch chart.Charter, values map[string]any) error {
|
|||
continue
|
||||
}
|
||||
|
||||
if err := ValidateAgainstSchema(subchart, subchartValues); err != nil {
|
||||
subchartPath := filepath.Join(absChartPath, "charts", sub.Name())
|
||||
if err := ValidateAgainstSchemaWithPath(subchart, subchartValues, subchartPath); err != nil {
|
||||
sb.WriteString(err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +140,13 @@ func ValidateAgainstSchema(ch chart.Charter, values map[string]any) error {
|
|||
}
|
||||
|
||||
// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema
|
||||
func ValidateAgainstSingleSchema(values common.Values, schemaJSON []byte, absBaseDir string) (reterr error) {
|
||||
func ValidateAgainstSingleSchema(values common.Values, schemaJSON []byte) (reterr error) {
|
||||
return ValidateAgainstSingleSchemaWithPath(values, schemaJSON, "/values.schema.json")
|
||||
}
|
||||
|
||||
// ValidateAgainstSingleSchemaWithPath checks that values does not violate the structure laid out in this schema.
|
||||
// schemaPath is the absolute path to the schema file, used to resolve relative $ref references.
|
||||
func ValidateAgainstSingleSchemaWithPath(values common.Values, schemaJSON []byte, schemaPath string) (reterr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
reterr = fmt.Errorf("unable to validate schema: %s", r)
|
||||
|
|
@ -147,13 +171,14 @@ func ValidateAgainstSingleSchema(values common.Values, schemaJSON []byte, absBas
|
|||
|
||||
compiler := jsonschema.NewCompiler()
|
||||
compiler.UseLoader(loader)
|
||||
base := "file://" + filepath.ToSlash(absBaseDir) + "/values.schema.json"
|
||||
err = compiler.AddResource(base, schema)
|
||||
|
||||
schemaURL := fmt.Sprintf("file://%s", schemaPath)
|
||||
err = compiler.AddResource(schemaURL, schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validator, err := compiler.Compile(base)
|
||||
validator, err := compiler.Compile(schemaURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -211,11 +236,7 @@ func (e JSONSchemaValidationError) Error() string {
|
|||
|
||||
// This string prefixes all of our error details. Further up the stack of helm error message
|
||||
// building more detail is provided to users. This is removed.
|
||||
if strings.HasPrefix(errStr, "jsonschema validation failed with ") {
|
||||
if idx := strings.Index(errStr, "#'\n"); idx != -1 {
|
||||
errStr = errStr[idx+3:]
|
||||
}
|
||||
}
|
||||
errStr = strings.TrimPrefix(errStr, "jsonschema validation failed with 'file:///values.schema.json#'\n")
|
||||
|
||||
// The extra new line is needed for when there are sub-charts.
|
||||
return errStr + "\n"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func TestValidateAgainstSingleSchema(t *testing.T) {
|
|||
t.Fatalf("Error reading YAML file: %s", err)
|
||||
}
|
||||
|
||||
if err := ValidateAgainstSingleSchema(values, schema, ""); err != nil {
|
||||
if err := ValidateAgainstSingleSchema(values, schema); err != nil {
|
||||
t.Errorf("Error validating Values against Schema: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ func TestValidateAgainstInvalidSingleSchema(t *testing.T) {
|
|||
}
|
||||
|
||||
var errString string
|
||||
if err := ValidateAgainstSingleSchema(values, schema, ""); err == nil {
|
||||
if err := ValidateAgainstSingleSchema(values, schema); err == nil {
|
||||
t.Fatalf("Expected an error, but got nil")
|
||||
} else {
|
||||
errString = err.Error()
|
||||
|
|
@ -78,7 +78,7 @@ func TestValidateAgainstSingleSchemaNegative(t *testing.T) {
|
|||
}
|
||||
|
||||
var errString string
|
||||
if err := ValidateAgainstSingleSchema(values, schema, ""); err == nil {
|
||||
if err := ValidateAgainstSingleSchema(values, schema); err == nil {
|
||||
t.Fatalf("Expected an error, but got nil")
|
||||
} else {
|
||||
errString = err.Error()
|
||||
|
|
@ -264,19 +264,18 @@ func TestValidateWithRelativeSchemaReferencesCurrentDir(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Error reading YAML file: %s", err)
|
||||
}
|
||||
schema, err := os.ReadFile("./testdata/current-dir-test/values.schema.json")
|
||||
schemaPath := "./testdata/current-dir-test/values.schema.json"
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading JSON schema file: %s", err)
|
||||
}
|
||||
|
||||
// Test with absolute base directory - this should work with your fix
|
||||
baseDir := "./testdata/current-dir-test"
|
||||
absBaseDir, err := filepath.Abs(baseDir)
|
||||
absSchemaPath, err := filepath.Abs(schemaPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting absolute path: %s", err)
|
||||
}
|
||||
|
||||
if err := ValidateAgainstSingleSchema(values, schema, absBaseDir); err != nil {
|
||||
if err := ValidateAgainstSingleSchemaWithPath(values, schema, absSchemaPath); err != nil {
|
||||
t.Errorf("Error validating Values against Schema with relative references: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -288,18 +287,18 @@ func TestValidateWithRelativeSchemaReferencesSubfolder(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Error reading YAML file: %s", err)
|
||||
}
|
||||
schema, err := os.ReadFile("./testdata/subdir-test/subfolder/values.schema.json")
|
||||
schemaPath := "./testdata/subdir-test/subfolder/values.schema.json"
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading JSON schema file: %s", err)
|
||||
}
|
||||
|
||||
baseDir := "./testdata/subdir-test/subfolder"
|
||||
absBaseDir, err := filepath.Abs(baseDir)
|
||||
absSchemaPath, err := filepath.Abs(schemaPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting absolute path: %s", err)
|
||||
}
|
||||
|
||||
if err := ValidateAgainstSingleSchema(values, schema, absBaseDir); err != nil {
|
||||
if err := ValidateAgainstSingleSchemaWithPath(values, schema, absSchemaPath); err != nil {
|
||||
t.Errorf("Error validating Values against Schema with relative references from subfolder: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ func ToRenderValues(chrt chart.Charter, chrtVals map[string]any, options common.
|
|||
//
|
||||
// This takes both ReleaseOptions and Capabilities to merge into the render values.
|
||||
func ToRenderValuesWithSchemaValidation(chrt chart.Charter, chrtVals map[string]any, options common.ReleaseOptions, caps *common.Capabilities, skipSchemaValidation bool) (common.Values, error) {
|
||||
return ToRenderValuesWithSchemaValidationAndPath(chrt, chrtVals, options, caps, skipSchemaValidation, "")
|
||||
}
|
||||
|
||||
// ToRenderValuesWithSchemaValidationAndPath is like ToRenderValuesWithSchemaValidation but accepts chartDir
|
||||
// for resolving relative $ref in JSON schemas.
|
||||
func ToRenderValuesWithSchemaValidationAndPath(chrt chart.Charter, chrtVals map[string]any, options common.ReleaseOptions, caps *common.Capabilities, skipSchemaValidation bool, chartDir string) (common.Values, error) {
|
||||
if caps == nil {
|
||||
caps = common.DefaultCapabilities
|
||||
}
|
||||
|
|
@ -60,7 +66,7 @@ func ToRenderValuesWithSchemaValidation(chrt chart.Charter, chrtVals map[string]
|
|||
}
|
||||
|
||||
if !skipSchemaValidation {
|
||||
if err := ValidateAgainstSchema(chrt, vals); err != nil {
|
||||
if err := ValidateAgainstSchemaWithPath(chrt, vals, chartDir); err != nil {
|
||||
return top, fmt.Errorf("values don't meet the specifications of the schema(s) in the following chart(s):\n%w", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ func (t *templateLinter) Lint() {
|
|||
return
|
||||
}
|
||||
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidation(chart, cvals, options, caps, t.skipSchemaValidation)
|
||||
valuesToRender, err := util.ToRenderValuesWithSchemaValidationAndPath(chart, cvals, options, caps, t.skipSchemaValidation, t.linter.ChartDir)
|
||||
if err != nil {
|
||||
t.linter.RunLinterRule(support.ErrorSev, templatesDir, err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]any,
|
|||
return
|
||||
}
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides, skipSchemaValidation))
|
||||
linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(linter.ChartDir, valueOverrides, skipSchemaValidation))
|
||||
}
|
||||
|
||||
func validateValuesFileExistence(valuesPath string) error {
|
||||
|
|
@ -52,7 +52,10 @@ func validateValuesFileExistence(valuesPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaValidation bool) error {
|
||||
func validateValuesFile(chartDir string, overrides map[string]any, skipSchemaValidation bool) error {
|
||||
valuesPath := filepath.Join(chartDir, "values.yaml")
|
||||
schemaPath := filepath.Join(chartDir, "values.schema.json")
|
||||
|
||||
values, err := common.ReadValuesFile(valuesPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse YAML: %w", err)
|
||||
|
|
@ -66,8 +69,6 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
|
|||
coalescedValues := util.CoalesceTables(make(map[string]any, len(overrides)), overrides)
|
||||
coalescedValues = util.CoalesceTables(coalescedValues, values)
|
||||
|
||||
ext := filepath.Ext(valuesPath)
|
||||
schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json"
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if len(schema) == 0 {
|
||||
return nil
|
||||
|
|
@ -75,9 +76,9 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(schemaPath)
|
||||
|
||||
if !skipSchemaValidation {
|
||||
return util.ValidateAgainstSingleSchema(coalescedValues, schema, baseDir)
|
||||
return util.ValidateAgainstSingleSchemaWithPath(coalescedValues, schema, schemaPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -66,8 +66,7 @@ func TestValidateValuesFileWellFormed(t *testing.T) {
|
|||
not:well[]{}formed
|
||||
`
|
||||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml))
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, map[string]any{}, false); err == nil {
|
||||
if err := validateValuesFile(tmpdir, map[string]any{}, false); err == nil {
|
||||
t.Fatal("expected values file to fail parsing")
|
||||
}
|
||||
}
|
||||
|
|
@ -77,8 +76,7 @@ func TestValidateValuesFileSchema(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, map[string]any{}, false); err != nil {
|
||||
if err := validateValuesFile(tmpdir, map[string]any{}, false); err != nil {
|
||||
t.Fatalf("Failed validation with %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,9 +87,7 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, map[string]any{}, false)
|
||||
err := validateValuesFile(tmpdir, map[string]any{}, false)
|
||||
if err == nil {
|
||||
t.Fatal("expected values file to fail parsing")
|
||||
}
|
||||
|
|
@ -105,9 +101,7 @@ func TestValidateValuesFileSchemaFailureButWithSkipSchemaValidation(t *testing.T
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, map[string]any{}, true)
|
||||
err := validateValuesFile(tmpdir, map[string]any{}, true)
|
||||
if err != nil {
|
||||
t.Fatal("expected values file to pass parsing because of skipSchemaValidation")
|
||||
}
|
||||
|
|
@ -121,8 +115,7 @@ func TestValidateValuesFileSchemaOverrides(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
if err := validateValuesFile(valfile, overrides, false); err != nil {
|
||||
if err := validateValuesFile(tmpdir, overrides, false); err != nil {
|
||||
t.Fatalf("Failed validation with %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -157,9 +150,7 @@ func TestValidateValuesFile(t *testing.T) {
|
|||
tmpdir := ensure.TempFile(t, "values.yaml", []byte(tt.yaml))
|
||||
createTestingSchema(t, tmpdir)
|
||||
|
||||
valfile := filepath.Join(tmpdir, "values.yaml")
|
||||
|
||||
err := validateValuesFile(valfile, tt.overrides, false)
|
||||
err := validateValuesFile(tmpdir, tt.overrides, false)
|
||||
|
||||
switch {
|
||||
case err != nil && tt.errorMessage == "":
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.ChartDir = cp
|
||||
|
||||
slog.Debug("Chart path", "path", cp)
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,11 @@ func TestInstall(t *testing.T) {
|
|||
cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25 --skip-schema-validation",
|
||||
golden: "output/schema.txt",
|
||||
},
|
||||
{
|
||||
name: "install with schema file containing $ref",
|
||||
cmd: "install reftest testdata/testcharts/chart-with-schema-ref",
|
||||
golden: "output/schema-ref.txt",
|
||||
},
|
||||
// Install deprecated chart
|
||||
{
|
||||
name: "install with warning about deprecated chart",
|
||||
|
|
|
|||
|
|
@ -166,6 +166,11 @@ func TestTemplateCmd(t *testing.T) {
|
|||
cmd: fmt.Sprintf("template '%s' -f %s/extra_values.yaml", chartPath, chartPath),
|
||||
golden: "output/template-subchart-cm-set-file.txt",
|
||||
},
|
||||
{
|
||||
name: "template with schema file containing $ref",
|
||||
cmd: "template reftest testdata/testcharts/chart-with-schema-ref",
|
||||
golden: "output/template-schema-ref.txt",
|
||||
},
|
||||
}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
|
|
|||
7
pkg/cmd/testdata/output/schema-ref.txt
vendored
Normal file
7
pkg/cmd/testdata/output/schema-ref.txt
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
NAME: reftest
|
||||
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
REVISION: 1
|
||||
DESCRIPTION: Install complete
|
||||
TEST SUITE: None
|
||||
1
pkg/cmd/testdata/output/template-schema-ref.txt
vendored
Normal file
1
pkg/cmd/testdata/output/template-schema-ref.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
8
pkg/cmd/testdata/output/upgrade-schema-ref.txt
vendored
Normal file
8
pkg/cmd/testdata/output/upgrade-schema-ref.txt
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Release "reftest" has been upgraded. Happy Helming!
|
||||
NAME: reftest
|
||||
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
REVISION: 2
|
||||
DESCRIPTION: Upgrade complete
|
||||
TEST SUITE: None
|
||||
1
pkg/cmd/testdata/testcharts/chart-with-schema-ref
vendored
Symbolic link
1
pkg/cmd/testdata/testcharts/chart-with-schema-ref
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../action/testdata/charts/chart-with-schema-ref
|
||||
|
|
@ -186,6 +186,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.ChartDir = chartPath
|
||||
|
||||
p := getter.All(settings)
|
||||
vals, err := valueOpts.MergeValues(p)
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ func TestUpgradeCmd(t *testing.T) {
|
|||
golden: "output/upgrade-uninstalled-with-keep-history.txt",
|
||||
rels: []*release.Release{relWithStatusMock("funny-bunny", 2, ch, rcommon.StatusUninstalled)},
|
||||
},
|
||||
{
|
||||
name: "upgrade with schema file containing $ref",
|
||||
cmd: "upgrade reftest testdata/testcharts/chart-with-schema-ref",
|
||||
golden: "output/upgrade-schema-ref.txt",
|
||||
rels: []*release.Release{relMock("reftest", 1, ch)},
|
||||
},
|
||||
}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue