pull/31274/merge
Benoit Tigeot 1 week ago committed by GitHub
commit b77570ec50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

@ -42,7 +42,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 {
@ -53,7 +53,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)
@ -67,8 +70,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
@ -78,7 +79,7 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
}
if !skipSchemaValidation {
return util.ValidateAgainstSingleSchema(coalescedValues, schema)
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,14 @@ 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: "archived-chart-with-schema-ref",
chartPath: "testdata/charts/chart-with-schema-ref.tgz",
},
{
name: "chart-with-schema-negative",
chartPath: "testdata/charts/chart-with-schema-negative",

@ -0,0 +1,3 @@
apiVersion: v2
name: chart-with-schema-ref
version: 0.1.0

@ -0,0 +1,4 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string"
}

@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": { "$ref": "name.schema.json" }
}
}

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

@ -23,6 +23,7 @@ import (
"fmt"
"log/slog"
"net/http"
"path/filepath"
"strings"
"sync"
"time"
@ -74,14 +75,39 @@ 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 != "" {
var err error
absChartPath, err = filepath.Abs(chartDir)
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())
var schemaPath string
if absChartPath != "" {
// Use the chart directory for $ref resolution
schemaPath = filepath.Join(absChartPath, "values.schema.json")
} else {
// No chart directory (e.g., chart loaded from .tgz archive).
// Use a synthetic path - $ref resolution will not work, but main schema validation will.
schemaPath = "/values.schema.json"
}
err := ValidateAgainstSingleSchemaWithPath(values, chrt.Schema(), schemaPath)
if err != nil {
fmt.Fprintf(&sb, "%s:\n", chrt.Name())
sb.WriteString(err.Error())
@ -108,7 +134,12 @@ func ValidateAgainstSchema(ch chart.Charter, values map[string]any) error {
continue
}
if err := ValidateAgainstSchema(subchart, subchartValues); err != nil {
var subchartPath string
if absChartPath != "" {
subchartPath = filepath.Join(absChartPath, "charts", sub.Name())
}
// If absChartPath is empty (archived chart), pass empty string to disable $ref resolution for subcharts too
if err := ValidateAgainstSchemaWithPath(subchart, subchartValues, subchartPath); err != nil {
sb.WriteString(err.Error())
}
}
@ -122,6 +153,12 @@ 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) (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)
@ -146,12 +183,14 @@ func ValidateAgainstSingleSchema(values common.Values, schemaJSON []byte) (reter
compiler := jsonschema.NewCompiler()
compiler.UseLoader(loader)
err = compiler.AddResource("file:///values.schema.json", schema)
schemaURL := fmt.Sprintf("file://%s", schemaPath)
err = compiler.AddResource(schemaURL, schema)
if err != nil {
return err
}
validator, err := compiler.Compile("file:///values.schema.json")
validator, err := compiler.Compile(schemaURL)
if err != nil {
return err
}
@ -209,7 +248,12 @@ 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.
errStr = strings.TrimPrefix(errStr, "jsonschema validation failed with 'file:///values.schema.json#'\n")
// Remove the "jsonschema validation failed with 'file://...#'" line regardless of the path
if strings.HasPrefix(errStr, "jsonschema validation failed with 'file://") {
if idx := strings.Index(errStr, "#'\n"); idx != -1 {
errStr = errStr[idx+3:] // Skip past "#'\n"
}
}
// The extra new line is needed for when there are sub-charts.
return errStr + "\n"

@ -20,6 +20,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
@ -177,11 +178,12 @@ func TestValidateAgainstSchemaNegative(t *testing.T) {
errString = err.Error()
}
expectedErrString := `subchart:
- at '': missing property 'age'
`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
expectedValidationError := "missing property 'age'"
if !strings.Contains(errString, "subchart:") {
t.Errorf("Error string should contain 'subchart:', got: %s", errString)
}
if !strings.Contains(errString, expectedValidationError) {
t.Errorf("Error string should contain '%s', got: %s", expectedValidationError, errString)
}
}
@ -241,12 +243,63 @@ func TestValidateAgainstSchema2020Negative(t *testing.T) {
errString = err.Error()
}
expectedErrString := `subchart:
- at '/data': no items match contains schema
- at '/data/0': got number, want string
`
if errString != expectedErrString {
t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString)
expectedValidationErrors := []string{
"no items match contains schema",
"got number, want string",
}
if !strings.Contains(errString, "subchart:") {
t.Errorf("Error string should contain 'subchart:', got: %s", errString)
}
for _, expectedErr := range expectedValidationErrors {
if !strings.Contains(errString, expectedErr) {
t.Errorf("Error string should contain '%s', got: %s", expectedErr, errString)
}
}
}
// TestValidateWithRelativeSchemaReferences tests schema validation with relative $ref paths
// This mimics the behavior of "helm lint ." where the schema is in the current directory
func TestValidateWithRelativeSchemaReferencesCurrentDir(t *testing.T) {
values, err := common.ReadValuesFile("./testdata/current-dir-test/test-values.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
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)
}
absSchemaPath, err := filepath.Abs(schemaPath)
if err != nil {
t.Fatalf("Error getting absolute path: %s", err)
}
if err := ValidateAgainstSingleSchemaWithPath(values, schema, absSchemaPath); err != nil {
t.Errorf("Error validating Values against Schema with relative references: %s", err)
}
}
// TestValidateWithRelativeSchemaReferencesSubfolder tests schema validation with relative $ref paths
// This mimics the behavior of "helm lint subfolder" where the schema is in a subdirectory
func TestValidateWithRelativeSchemaReferencesSubfolder(t *testing.T) {
values, err := common.ReadValuesFile("./testdata/subdir-test/subfolder/test-values.yaml")
if err != nil {
t.Fatalf("Error reading YAML file: %s", err)
}
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)
}
absSchemaPath, err := filepath.Abs(schemaPath)
if err != nil {
t.Fatalf("Error getting absolute path: %s", err)
}
if err := ValidateAgainstSingleSchemaWithPath(values, schema, absSchemaPath); err != nil {
t.Errorf("Error validating Values against Schema with relative references from subfolder: %s", err)
}
}

@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": ["name"]
}

@ -0,0 +1,3 @@
user:
name: "John Doe"
age: 30

@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"user": {
"$ref": "./base.schema.json"
},
"age": {
"type": "integer",
"minimum": 0
}
},
"required": ["user", "age"]
}

@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"config": {
"type": "string"
}
},
"required": ["config"]
}

@ -0,0 +1,3 @@
appConfig:
config: "production"
replicas: 3

@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"appConfig": {
"$ref": "../shared.schema.json"
},
"replicas": {
"type": "integer",
"minimum": 1
}
},
"required": ["appConfig", "replicas"]
}

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

@ -42,7 +42,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 {
@ -53,7 +53,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)
@ -67,8 +70,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
@ -78,7 +79,7 @@ func validateValuesFile(valuesPath string, overrides map[string]any, skipSchemaV
}
if !skipSchemaValidation {
return util.ValidateAgainstSingleSchema(coalescedValues, schema)
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 == "":

@ -249,6 +249,13 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
return nil, err
}
// Only set ChartDir for directory-based charts to enable $ref resolution.
// Archived charts (.tgz) are loaded into memory without filesystem extraction,
// so $ref resolution is not supported for them.
if fi, err := os.Stat(cp); err == nil && fi.IsDir() {
client.ChartDir = cp
}
slog.Debug("Chart path", "path", cp)
p := getter.All(settings)

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

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

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

@ -0,0 +1,3 @@
apiVersion: v2
name: chart-with-schema-ref
version: 0.1.0

@ -0,0 +1,4 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string"
}

@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": { "$ref": "name.schema.json" }
}
}

@ -187,6 +187,13 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return err
}
// Only set ChartDir for directory-based charts to enable $ref resolution.
// Archived charts (.tgz) are loaded into memory without filesystem extraction,
// so $ref resolution is not supported for them.
if fi, err := os.Stat(chartPath); err == nil && fi.IsDir() {
client.ChartDir = chartPath
}
p := getter.All(settings)
vals, err := valueOpts.MergeValues(p)
if err != nil {

@ -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…
Cancel
Save