diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go index cd65fa885..292feeb55 100644 --- a/pkg/cli/values/options.go +++ b/pkg/cli/values/options.go @@ -21,8 +21,10 @@ import ( "encoding/json" "fmt" "io" + "io/fs" "net/url" "os" + "path/filepath" "strings" "helm.sh/helm/v4/pkg/chart/v2/loader" @@ -32,21 +34,74 @@ import ( // Options captures the different ways to specify values type Options struct { - ValueFiles []string // -f/--values - StringValues []string // --set-string - Values []string // --set - FileValues []string // --set-file - JSONValues []string // --set-json - LiteralValues []string // --set-literal + ValuesDirectories []string // -d / --values-directory + ValueFiles []string // -f / --values + StringValues []string // --set-string + Values []string // --set + FileValues []string // --set-file + JSONValues []string // --set-json + LiteralValues []string // --set-literal } -// MergeValues merges values from files specified via -f/--values and directly -// via --set-json, --set, --set-string, or --set-file, marshaling them to YAML -func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, error) { - base := map[string]interface{}{} +// MergeValues merges values from multiple sources according to Helm's precedence rules. +// +// The following list is ordered from lowest to highest precedence; items lower in the list override those above. +// i.e., values from sources later in the list take precedence over earlier ones: +// +// 1. -d / --values-directory : Values files from one or more directories. +// 2. -f / --values : Values files or URLs. +// 3. --set-json : Values provided as raw JSON. +// 4. --set : Inline key=value pairs. +// 5. --set-string : Inline key=value pairs (values always treated as strings). +// 6. --set-file : Values read from file contents. +// 7. --set-literal : Values provided as raw string literals. +// +// For example, if `captain=Luffy` is set via --values-directory (1) and `captain=Usopp` is set via --values (2), the +// final merged value for `captain` will be "Usopp". +// +// --- +// +// In case any supported flag is specified multiple times, the latter occurrence has higher precedence, i.e. overrides +// the former. +// +// For example, for `--set captain=Luffy --set captain=Usopp`, the final merged value for `captain` will be "Usopp". +// +// This applies to all values flags (-d/--values-directory, -f/--values, --set-json, --set, --set-string, --set-file, +// and --set-literal). +// +// --- +// +// Additional context: The precedence of default values. +// +// - By default, Helm reads values from the chart’s values.yaml file (if present). +// - These default values have the lowest precedence (level 0), meaning any values specified via the above-mentioned +// flags will override them. +// - If the same values.yaml file is explicitly provided using -f/--values, its values can override those loaded via +// -d/--values-directory. However, in that case, they are no longer considered default values. +// +// Note: Default values are not handled by this function, but understanding their precedence is important for the +// overall behavior. +func (opts *Options) MergeValues(p getter.Providers) (map[string]any, error) { + base := map[string]any{} - // User specified a values files via -f/--values - for _, filePath := range opts.ValueFiles { + var valuesFiles []string + + // 1. User specified directory(s) via -d/--values-directory. + for _, dir := range opts.ValuesDirectories { + // Recursively find all .yaml files in the directory. + files, err := listYAMLFilesRecursively(dir) + if err != nil { + // Error is already wrapped. + return nil, err + } + + valuesFiles = append(valuesFiles, files...) + } + + // 2. User specified values files via -f/--values. + valuesFiles = append(valuesFiles, opts.ValueFiles...) + + for _, filePath := range valuesFiles { raw, err := readFile(filePath, p) if err != nil { return nil, err @@ -59,12 +114,12 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er base = loader.MergeMaps(base, currentMap) } - // User specified a value via --set-json + // 3. User specified a value via --set-json. for _, value := range opts.JSONValues { trimmedValue := strings.TrimSpace(value) if len(trimmedValue) > 0 && trimmedValue[0] == '{' { // If value is JSON object format, parse it as map - var jsonMap map[string]interface{} + var jsonMap map[string]any if err := json.Unmarshal([]byte(trimmedValue), &jsonMap); err != nil { return nil, fmt.Errorf("failed parsing --set-json data JSON: %s", value) } @@ -77,23 +132,23 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er } } - // User specified a value via --set + // 4. User specified a value via --set. for _, value := range opts.Values { if err := strvals.ParseInto(value, base); err != nil { return nil, fmt.Errorf("failed parsing --set data: %w", err) } } - // User specified a value via --set-string + // 5. User specified a value via --set-string. for _, value := range opts.StringValues { if err := strvals.ParseIntoString(value, base); err != nil { return nil, fmt.Errorf("failed parsing --set-string data: %w", err) } } - // User specified a value via --set-file + // 6. User specified a value via --set-file. for _, value := range opts.FileValues { - reader := func(rs []rune) (interface{}, error) { + reader := func(rs []rune) (any, error) { bytes, err := readFile(string(rs), p) if err != nil { return nil, err @@ -105,7 +160,7 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er } } - // User specified a value via --set-literal + // 7. User specified a value via --set-literal. for _, value := range opts.LiteralValues { if err := strvals.ParseLiteralInto(value, base); err != nil { return nil, fmt.Errorf("failed parsing --set-literal data: %w", err) @@ -136,3 +191,74 @@ func readFile(filePath string, p getter.Providers) ([]byte, error) { } return data.Bytes(), nil } + +// listYAMLFilesRecursively walks a directory tree and returns a lexicographically sorted list of all YAML files. +// +// Example: (dir="foo") +// +// foo/ +// ├── bar/ +// │ └── bar.yaml +// ├── baz/ +// │ ├── baz.yaml +// │ └── qux.yaml +// ├── baz.txt +// └── foo.yaml +// +// Result: ["foo/bar/bar.yaml", "foo/baz/baz.yaml", "foo/baz/qux.yaml", "foo/foo.yaml"] +func listYAMLFilesRecursively(dir string) ([]string, error) { + var files []string + + // Check if the directory exists and is a directory. + info, err := os.Stat(dir) + if err != nil { + return nil, fmt.Errorf("failed to access values directory %q: %w", dir, err) + } + if !info.IsDir() { + return nil, fmt.Errorf("path %q is not a directory", dir) + } + + // Walk the directory tree in lexical order. For the above example, this will visit: + // 1. foo/bar + // 2. foo/bar/bar.yaml + // 3. foo/baz + // 4. foo/baz/baz.yaml + // 5. foo/baz/qux.yaml + // 6. foo/baz.txt + // 7. foo/foo.yaml + // + // The inner function filters the “.yaml” files as follows: + // 1. foo/bar/bar.yaml + // 2. foo/baz/baz.yaml + // 3. foo/baz/qux.yaml + // 4. foo/foo.yaml + // + // Note: Since filepath.WalkDir walks in lexical order, the returned list of files is also sorted lexicographically. + err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return fmt.Errorf("failed to walk directory %q: %w", path, err) + } + + // Collect YAML files (.yaml or .yml, case-insensitive), skipping directories. + if !d.IsDir() && isYamlFileExtension(d.Name()) { + files = append(files, path) + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to list files in directory %q: %w", dir, err) + } + + return files, nil +} + +// isYamlFileExtension checks if the given file name has a YAML file extension. It returns true for files ending with +// .yaml or .yml (case-insensitive). +func isYamlFileExtension(fileName string) bool { + // Extract file extension and convert to lower case for case-insensitive comparison. + ext := strings.ToLower(filepath.Ext(fileName)) + + // Check for .yaml or .yml extensions. + return ext == ".yaml" || ext == ".yml" +} diff --git a/pkg/cli/values/options_test.go b/pkg/cli/values/options_test.go index fe1afc5d2..361d06156 100644 --- a/pkg/cli/values/options_test.go +++ b/pkg/cli/values/options_test.go @@ -298,7 +298,7 @@ func TestMergeValuesCLI(t *testing.T) { tests := []struct { name string opts Options - expected map[string]interface{} + expected map[string]any wantErr bool }{ { @@ -306,8 +306,8 @@ func TestMergeValuesCLI(t *testing.T) { opts: Options{ JSONValues: []string{`{"foo": {"bar": "baz"}}`}, }, - expected: map[string]interface{}{ - "foo": map[string]interface{}{ + expected: map[string]any{ + "foo": map[string]any{ "bar": "baz", }, }, @@ -317,9 +317,9 @@ func TestMergeValuesCLI(t *testing.T) { opts: Options{ JSONValues: []string{"foo.bar=[1,2,3]"}, }, - expected: map[string]interface{}{ - "foo": map[string]interface{}{ - "bar": []interface{}{1.0, 2.0, 3.0}, + expected: map[string]any{ + "foo": map[string]any{ + "bar": []any{1.0, 2.0, 3.0}, }, }, }, @@ -328,7 +328,7 @@ func TestMergeValuesCLI(t *testing.T) { opts: Options{ Values: []string{"foo=bar"}, }, - expected: map[string]interface{}{ + expected: map[string]any{ "foo": "bar", }, }, @@ -337,7 +337,7 @@ func TestMergeValuesCLI(t *testing.T) { opts: Options{ StringValues: []string{"foo=123"}, }, - expected: map[string]interface{}{ + expected: map[string]any{ "foo": "123", }, }, @@ -346,7 +346,7 @@ func TestMergeValuesCLI(t *testing.T) { opts: Options{ LiteralValues: []string{"foo=true"}, }, - expected: map[string]interface{}{ + expected: map[string]any{ "foo": "true", }, }, @@ -358,7 +358,7 @@ func TestMergeValuesCLI(t *testing.T) { JSONValues: []string{`{"c": "foo1"}`}, LiteralValues: []string{"d=bar1"}, }, - expected: map[string]interface{}{ + expected: map[string]any{ "a": "foo", "b": "bar", "c": "foo1", @@ -372,6 +372,447 @@ func TestMergeValuesCLI(t *testing.T) { }, wantErr: true, }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory doesn't exist, the function should: + // - Return a nil map and an error. + name: "missing values directory", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/no-such-directory", + }, + }, + // Directory doesn't exist, so expect a nil map. + expected: nil, + // Directory doesn't exist, so expect an error. + wantErr: true, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains no files, the function should: + // - Return an empty map. + // - No errors should occur. + name: "values directory without files", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/values-directory-without-files", + }, + }, + // No YAML files, so expect an empty map. + expected: map[string]any{}, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains only non-YAML files, the function should: + // - Return an empty map. + // - No errors should occur. + name: "values directory with only non-YAML files", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/values-directory-with-only-non-yaml-files", + }, + }, + expected: map[string]any{}, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains single YAML file, the function should: + // - Read and parse the YAML file. + // - Return the result map, which contains the key-value pairs from the file. + // - No errors should occur. + name: "values directory with single YAML file", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/values-directory-with-single-yaml-file", + }, + }, + expected: map[string]any{ + // Field "alliances" is read from "alliances-law.yaml" file. + "alliances": []any{ + map[string]any{ + "name": "Heart Pirates", + "captain": "Trafalgar D. Water Law", + }, + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains one YAML file and one non-YAML file, the function should: + // - Read and parse the YAML file. + // - Return the result map, which contains the key-value pairs from the YAML file. + // - No errors should occur. + name: "values directory with one YAML file and one non-YAML file", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file", + }, + }, + expected: map[string]any{ + // Field "ship" is read from "ship-thousand-sunny.yaml" file. + "ship": map[string]any{ + "name": "Thousand Sunny", + "status": "Active", + "speed": "Fast", + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains multiple YAML files with no overlapping keys, the function should: + // - Read and parse all YAML files individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from all files are present, and none are overwritten + // (since there is no overlap). + // - No errors should occur. + name: "values directory with multiple YAML files, without overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-multiple-yaml-files-without-overlapping-keys", + }, + }, + expected: map[string]any{ + // Field "alliances" is read from "alliances-kid.yaml". + "alliances": []any{ + map[string]any{ + "name": "Kid Pirates", + "captain": "Eustass Kid", + }, + }, + // Field "ship" is read from "ship-going-merry.yaml". + "ship": map[string]any{ + "name": "Going Merry", + "status": "Destroyed", + "speed": "Slow", + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains multiple YAML files with few overlapping keys, the function should: + // - Read and parse all YAML files individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from all files are present, and for overlapping keys, the + // value from the last file (lexicographically) processed takes precedence (overwrites previous values). + // - No errors should occur. + name: "values directory with multiple YAML files, with few overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-multiple-yaml-files-with-few-overlapping-keys", + }, + }, + expected: map[string]any{ + "crew": []any{ + map[string]any{ + "name": "Monkey D. Luffy", + "role": "Captain", + // Field "bounty" is set to "30,000,000" in "1-crew-luffy-east-blue.yaml", and is overridden + // as "100,000,000" in "2-crew-luffy-alabasta.yaml". + "bounty": "100,000,000", + "age": float64(19), + "devil_fruit": "Gomu Gomu no Mi", + }, + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and contains multiple YAML files with all overlapping keys, the function should: + // - Read and parse all YAML files individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from the last file (lexicographically) overwriting all + // from previous files. + // - No errors should occur. + name: "values directory with multiple YAML files, with all overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-multiple-yaml-files-with-all-overlapping-keys", + }, + }, + expected: map[string]any{ + "ship": map[string]any{ + // Field "name" is set to "Going Merry" in "ship-going-merry.yaml", and is overridden as + // "Thousand Sunny" in "ship-thousand-sunny.yaml". + "name": "Thousand Sunny", + // Field "status" is set to "Destroyed" in "ship-going-merry.yaml", and is overridden as + // "Active" in "ship-thousand-sunny.yaml". + "status": "Active", + // Field "speed" is set to "Slow" in "ship-going-merry.yaml", and is overridden as "Fast" in + // "ship-thousand-sunny.yaml". + "speed": "Fast", + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and nested directories and YAML files with no overlapping keys, the function + // should: + // - Read and parse all YAML files, from all levels, individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from all files are present, and none are overwritten + // (since there is no overlap). + // - No errors should occur. + name: "values directory with nested directories and YAML files, without overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys", + }, + }, + expected: map[string]any{ + // Field "crew" is read from "1-crew-luffy-east-blue.yaml". + "crew": []any{ + map[string]any{ + "name": "Monkey D. Luffy", + "role": "Captain", + "bounty": "30,000,000", + "age": float64(19), + "devil_fruit": "Gomu Gomu no Mi", + }, + }, + // Field "alliances" is read from "subdir/alliances-grandfleet.yaml". + "alliances": []any{ + map[string]any{ + "name": "Straw Hat Grand Fleet", + "captain": "Various", + }, + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and nested directories and YAML files with few overlapping keys, the function + // should: + // - Read and parse all YAML files, from all levels, individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from all files are present, and for overlapping keys, the + // value from the last file (lexicographically) processed takes precedence (overwrites previous values). + // - No errors should occur. + name: "values directory with nested directories and YAML files, with few overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys", + }, + }, + expected: map[string]any{ + "crew": []any{ + map[string]any{ + "name": "Monkey D. Luffy", + "role": "Captain", + // Field "bounty" is set to "30,000,000" in "1-crew-luffy-east-blue.yaml", and is overridden + // as "100,000,000" in "subdir/2-crew-luffy-alabasta.yaml". + "bounty": "100,000,000", + "age": float64(19), + "devil_fruit": "Gomu Gomu no Mi", + }, + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files. + // If the directory exists and nested directories and YAML files with all overlapping keys, the function + // should: + // - Read and parse all YAML files, from all levels, individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from the last file (lexicographically) overwriting all + // from previous files. + // - No errors should occur. + name: "values directory with nested directories and YAML files, with all overlapping keys", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys", + }, + }, + expected: map[string]any{ + "ship": map[string]any{ + // Field "name" is set to "Going Merry" in "ship-going-merry.yaml", and is overridden as + // "Thousand Sunny" in "subdir/ship-thousand-sunny.yaml". + "name": "Thousand Sunny", + // Field "status" is set to "Destroyed" in "ship-going-merry.yaml", and is overridden as + // "Active" in "subdir/ship-thousand-sunny.yaml". + "status": "Active", + // Field "speed" is set to "Slow" in "ship-going-merry.yaml", and is overridden as "Fast" in + // "subdir/ship-thousand-sunny.yaml". + "speed": "Fast", + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used, the given directory is scanned recursively for YAML files + // with different extensions (case-insensitive). Ignore files with non-YAML extensions. + // If the directory exists and contains YAML files with different extensions (case-insensitive), the + // function should: + // - Read and parse all YAML files with different extensions (case-insensitive) individually. Ignore files + // with non-YAML extensions. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from all files are present. + // - No errors should occur. + name: "values directory with multiple YAML files, with different extensions case-insensitive, " + + "ignore other files", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive", + }, + }, + expected: map[string]any{ + // Field "crew" is read from "1-crew-luffy-east-blue.YML". + "crew": []any{ + map[string]any{ + "name": "Monkey D. Luffy", + "role": "Captain", + "bounty": "30,000,000", + "age": float64(19), + "devil_fruit": "Gomu Gomu no Mi", + }, + }, + // Field "alliances" is read from "alliances-kid.yml". + "alliances": []any{ + map[string]any{ + "name": "Kid Pirates", + "captain": "Eustass Kid", + }, + }, + // Field "ship" is read from "ship-going-merry.YAML". + "ship": map[string]any{ + "name": "Going Merry", + "status": "Destroyed", + "speed": "Slow", + }, + }, + // No error expected. + wantErr: false, + }, + { + // When the --values-directory (-d) flag is used to scan the directory recursively for YAML files, and the + // remaining input flags -f/--values, --set-string, --set, --set-file, --set-json, --set-literal are used to + // override values from the directory. + // If the directory exists and nested directories and YAML files with all overlapping keys, the function + // should: + // - Read and parse all YAML files, from all levels, individually. + // - Merge the resulting key-value maps into a single map. + // - Return the merged map, where key-value pairs from the last file (lexicographically) overwriting all + // from previous files. + // - No errors should occur. + name: "values directory with YAML files, and values overridden via all other flags " + + "(-f/--values, --set-string, --set, --set-file, --set-json, --set-literal)", + opts: Options{ + // Set using flag -d / --values-directory (read values from YAML files in the specified directory). + ValuesDirectories: []string{ + "testdata/one-piece-chart/values.d/" + + "values-directory-with-YAML-files-and-values-overridden-via-all-other-flags", + }, + + // Set using flag -f / --values (read values from the specified YAML file). + ValueFiles: []string{ + "testdata/one-piece-chart/values.d/shared-values/alliances-kid.yaml", + }, + + // Set using flag --set-string (inline string key=value pairs, value must be a string). + StringValues: []string{ + "crew[0].role=Legendary Captain", + }, + + // Set using flag --set (inline key=value pairs, value type is inferred). + Values: []string{ + // String value "ship.name" will be overridden via --set. + "ship.name=Going Merry", + // Integer value "crew[0].age" will be overridden via --set. + "crew[0].age=20", + }, + + // Set using flag --set-file (read value from specified file). + FileValues: []string{ + "crew[0].devil_fruit=testdata/one-piece-chart/values.d/shared-values/devil-fruit.txt", + }, + + // Set using flag --set-json (parse JSON string for value). + JSONValues: []string{ + `ship.status={"condition":"Destroyed","where":"Enies Lobby"}`, + }, + + // Set using flag --set-literal (force literal string value, even if it is parsable to any other type). + LiteralValues: []string{ + // String value "ship.speed" will be overridden via --set-literal. + "ship.speed=Slow", + // Integer value "crew[0].bounty" will be overridden via --set-literal. + // Note: --set-literal treats numbers as strings. + "crew[0].bounty=1,500,000,000", + }, + }, + expected: map[string]any{ + "alliances": []any{ // Added via -f / --values. + map[string]any{ + "captain": "Eustass Kid", + "name": "Kid Pirates", + }, + }, + "crew": []any{ + map[string]any{ + "name": "Monkey D. Luffy", + // Field "role" is overridden by --set-string. + "role": "Legendary Captain", + // Field "bounty" is overridden by --set-literal. + "bounty": "1,500,000,000", + // Field "devil_fruit" is overridden by --set-file. + "devil_fruit": "Hito Hito no Mi", + // Field "age" is overridden by --set. + // Note: --set treats numbers as int64. + "age": int64(20), + }, + }, + "ship": map[string]any{ + // Field "name" is overridden by --set. + "name": "Going Merry", + // Field "status" is overridden by --set-json. + "status": map[string]any{ + "condition": "Destroyed", + "where": "Enies Lobby", + }, + // Field "speed" is overridden by --set-literal. + "speed": "Slow", + }, + }, + // No error expected. + wantErr: false, + }, } for _, tt := range tests { diff --git a/pkg/cli/values/testdata/one-piece-chart/Chart.yaml b/pkg/cli/values/testdata/one-piece-chart/Chart.yaml new file mode 100644 index 000000000..b637cc812 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: onepiece-novals +description: Helm chart for testing --values-directory precedence with no defaults +type: application +version: 0.1.0 +appVersion: "1.0" diff --git a/pkg/cli/values/testdata/one-piece-chart/templates/NOTES.txt b/pkg/cli/values/testdata/one-piece-chart/templates/NOTES.txt new file mode 100644 index 000000000..385e1e1be --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/templates/NOTES.txt @@ -0,0 +1,3 @@ +Values merged into this release: + + kubectl get cm {{ include "onepiece-novals.fullname" . }}-values -o yaml diff --git a/pkg/cli/values/testdata/one-piece-chart/templates/_helpers.tpl b/pkg/cli/values/testdata/one-piece-chart/templates/_helpers.tpl new file mode 100644 index 000000000..d40237d6f --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/templates/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "onepiece-novals.fullname" -}} +{{- printf "%s" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/pkg/cli/values/testdata/one-piece-chart/templates/configmap.yaml b/pkg/cli/values/testdata/one-piece-chart/templates/configmap.yaml new file mode 100644 index 000000000..b0d77b787 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "onepiece-novals.fullname" . }}-values +data: + merged-values.yaml: | +{{ toYaml .Values | indent 4 }} diff --git a/pkg/cli/values/testdata/one-piece-chart/templates/deployment.yaml b/pkg/cli/values/testdata/one-piece-chart/templates/deployment.yaml new file mode 100644 index 000000000..e752e9710 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/templates/deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "one-piece-chart.fullname" . }}-app +spec: + replicas: 1 + selector: + matchLabels: + app: {{ include "one-piece-chart.fullname" . }} + template: + metadata: + labels: + app: {{ include "one-piece-chart.fullname" . }} + spec: + containers: + - name: onepiece-container + image: alpine:3.18 + command: ["/bin/sh", "-c"] + args: + - | + echo "Mounted bounty files at /bounties:" + for f in /bounties/*.yaml; do + [ -e "$f" ] || continue + echo "---" + echo "Member: $(basename "$f" .yaml)" + cat "$f" + done + sleep 3600 + volumeMounts: + - name: crew-bounties + mountPath: /bounties + readOnly: true + volumes: + - name: crew-bounties + configMap: + name: {{ include "one-piece-chart.fullname" . }}-crew diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/1-crew-luffy-east-blue.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/1-crew-luffy-east-blue.yaml new file mode 100644 index 000000000..4fc7fb565 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/1-crew-luffy-east-blue.yaml @@ -0,0 +1,6 @@ +crew: + - name: "Monkey D. Luffy" + role: "Captain" + bounty: "30,000,000" + age: 19 + devil_fruit: "Gomu Gomu no Mi" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/2-crew-luffy-alabasta.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/2-crew-luffy-alabasta.yaml new file mode 100644 index 000000000..eeda89370 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/2-crew-luffy-alabasta.yaml @@ -0,0 +1,6 @@ +crew: + - name: "Monkey D. Luffy" + role: "Captain" + bounty: "100,000,000" + age: 19 + devil_fruit: "Gomu Gomu no Mi" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-grandfleet.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-grandfleet.yaml new file mode 100644 index 000000000..ac02b9390 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-grandfleet.yaml @@ -0,0 +1,3 @@ +alliances: + - name: "Straw Hat Grand Fleet" + captain: "Various" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-kid.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-kid.yaml new file mode 100644 index 000000000..3072ba3ce --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-kid.yaml @@ -0,0 +1,3 @@ +alliances: + - name: "Kid Pirates" + captain: "Eustass Kid" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-law.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-law.yaml new file mode 100644 index 000000000..114127db8 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/alliances-law.yaml @@ -0,0 +1,3 @@ +alliances: + - name: "Heart Pirates" + captain: "Trafalgar D. Water Law" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/devil-fruit.txt b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/devil-fruit.txt new file mode 100644 index 000000000..d866a771e --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/devil-fruit.txt @@ -0,0 +1 @@ +Hito Hito no Mi \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ignore.md b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ignore.md new file mode 100644 index 000000000..5dc029801 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ignore.md @@ -0,0 +1,4 @@ +# This will be ignored anyway. +ship: + name: "Polar Tang" + status: "Active" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-going-merry.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-going-merry.yaml new file mode 100644 index 000000000..0b3ec4d6f --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-going-merry.yaml @@ -0,0 +1,4 @@ +ship: + name: "Going Merry" + status: "Destroyed" + speed: "Slow" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-thousand-sunny.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-thousand-sunny.yaml new file mode 100644 index 000000000..aaaf2e7b8 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/shared-values/ship-thousand-sunny.yaml @@ -0,0 +1,4 @@ +ship: + name: "Thousand Sunny" + status: "Active" + speed: "Fast" diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/1-crew-luffy-east-blue.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/1-crew-luffy-east-blue.yaml new file mode 120000 index 000000000..80636c148 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/1-crew-luffy-east-blue.yaml @@ -0,0 +1 @@ +../shared-values/1-crew-luffy-east-blue.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/alliances-law.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/alliances-law.yaml new file mode 120000 index 000000000..7bdb74f6f --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/alliances-law.yaml @@ -0,0 +1 @@ +../shared-values/alliances-law.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/ship-thousand-sunny.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/ship-thousand-sunny.yaml new file mode 120000 index 000000000..ffd956da0 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-YAML-files-and-values-overridden-via-all-other-flags/ship-thousand-sunny.yaml @@ -0,0 +1 @@ +../shared-values/ship-thousand-sunny.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-going-merry.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-going-merry.yaml new file mode 120000 index 000000000..2ed4b40be --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-going-merry.yaml @@ -0,0 +1 @@ +../shared-values/ship-going-merry.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-thousand-sunny.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-thousand-sunny.yaml new file mode 120000 index 000000000..ffd956da0 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-all-overlapping-keys/ship-thousand-sunny.yaml @@ -0,0 +1 @@ +../shared-values/ship-thousand-sunny.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/1-crew-luffy-east-blue.YML b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/1-crew-luffy-east-blue.YML new file mode 120000 index 000000000..80636c148 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/1-crew-luffy-east-blue.YML @@ -0,0 +1 @@ +../shared-values/1-crew-luffy-east-blue.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/alliances-kid.yml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/alliances-kid.yml new file mode 120000 index 000000000..8449d354a --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/alliances-kid.yml @@ -0,0 +1 @@ +../shared-values/alliances-kid.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ignore.md b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ignore.md new file mode 120000 index 000000000..96ca2481c --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ignore.md @@ -0,0 +1 @@ +../shared-values/ignore.md \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ship-going-merry.YAML b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ship-going-merry.YAML new file mode 120000 index 000000000..2ed4b40be --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-different-extensions-case-insensitive/ship-going-merry.YAML @@ -0,0 +1 @@ +../shared-values/ship-going-merry.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml new file mode 120000 index 000000000..80636c148 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml @@ -0,0 +1 @@ +../shared-values/1-crew-luffy-east-blue.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/2-crew-luffy-alabasta.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/2-crew-luffy-alabasta.yaml new file mode 120000 index 000000000..dd574823c --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-with-few-overlapping-keys/2-crew-luffy-alabasta.yaml @@ -0,0 +1 @@ +../shared-values/2-crew-luffy-alabasta.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/alliances-kid.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/alliances-kid.yaml new file mode 120000 index 000000000..8449d354a --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/alliances-kid.yaml @@ -0,0 +1 @@ +../shared-values/alliances-kid.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/ship-going-merry.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/ship-going-merry.yaml new file mode 120000 index 000000000..2ed4b40be --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-multiple-yaml-files-without-overlapping-keys/ship-going-merry.yaml @@ -0,0 +1 @@ +../shared-values/ship-going-merry.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/ship-going-merry.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/ship-going-merry.yaml new file mode 120000 index 000000000..2ed4b40be --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/ship-going-merry.yaml @@ -0,0 +1 @@ +../shared-values/ship-going-merry.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/subdir/ship-thousand-sunny.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/subdir/ship-thousand-sunny.yaml new file mode 120000 index 000000000..b57062c7e --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-all-overlapping-keys/subdir/ship-thousand-sunny.yaml @@ -0,0 +1 @@ +../../shared-values/ship-thousand-sunny.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml new file mode 120000 index 000000000..80636c148 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/1-crew-luffy-east-blue.yaml @@ -0,0 +1 @@ +../shared-values/1-crew-luffy-east-blue.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/subdir/2-crew-luffy-alabasta.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/subdir/2-crew-luffy-alabasta.yaml new file mode 120000 index 000000000..1e50b01c3 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-with-few-overlapping-keys/subdir/2-crew-luffy-alabasta.yaml @@ -0,0 +1 @@ +../../shared-values/2-crew-luffy-alabasta.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/1-crew-luffy-east-blue.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/1-crew-luffy-east-blue.yaml new file mode 120000 index 000000000..80636c148 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/1-crew-luffy-east-blue.yaml @@ -0,0 +1 @@ +../shared-values/1-crew-luffy-east-blue.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/subdir/alliances-grandfleet.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/subdir/alliances-grandfleet.yaml new file mode 120000 index 000000000..c0e22f871 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-nested-directories-and-YAML-files-without-overlapping-keys/subdir/alliances-grandfleet.yaml @@ -0,0 +1 @@ +../../shared-values/alliances-grandfleet.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ignore.md b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ignore.md new file mode 120000 index 000000000..96ca2481c --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ignore.md @@ -0,0 +1 @@ +../shared-values/ignore.md \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ship-thousand-sunny.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ship-thousand-sunny.yaml new file mode 120000 index 000000000..ffd956da0 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-one-yaml-file-and-one-non-yaml-file/ship-thousand-sunny.yaml @@ -0,0 +1 @@ +../shared-values/ship-thousand-sunny.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-only-non-yaml-files/ignore.md b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-only-non-yaml-files/ignore.md new file mode 120000 index 000000000..96ca2481c --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-only-non-yaml-files/ignore.md @@ -0,0 +1 @@ +../shared-values/ignore.md \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-single-yaml-file/alliances-law.yaml b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-single-yaml-file/alliances-law.yaml new file mode 120000 index 000000000..7bdb74f6f --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-with-single-yaml-file/alliances-law.yaml @@ -0,0 +1 @@ +../shared-values/alliances-law.yaml \ No newline at end of file diff --git a/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-without-files/.gitignore b/pkg/cli/values/testdata/one-piece-chart/values.d/values-directory-without-files/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/cli/values/testdata/one-piece-chart/values.yaml b/pkg/cli/values/testdata/one-piece-chart/values.yaml new file mode 100644 index 000000000..ed9e8d7f8 --- /dev/null +++ b/pkg/cli/values/testdata/one-piece-chart/values.yaml @@ -0,0 +1 @@ +# Intentionally empty – no defaults. \ No newline at end of file diff --git a/pkg/cmd/flags.go b/pkg/cmd/flags.go index b20772ef9..1c87fad28 100644 --- a/pkg/cmd/flags.go +++ b/pkg/cmd/flags.go @@ -47,6 +47,10 @@ const ( ) func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { + f.StringSliceVarP(&v.ValuesDirectories, "values-directory", "d", []string{}, + "specify values directory to scan recursively for \".yaml\" files. Files are loaded in lexical order. When "+ + "keys overlap, values from the later file override those from earlier ones. Note: This flag can be "+ + "specified multiple times.") f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)") f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go index 295f1ae37..9291f50d4 100644 --- a/pkg/cmd/install.go +++ b/pkg/cmd/install.go @@ -47,12 +47,14 @@ This command installs a chart archive. The install argument must be a chart reference, a path to a packaged chart, a path to an unpacked chart directory or a URL. -To override values in a chart, use either the '--values' flag and pass in a file -or use the '--set' flag and pass configuration from the command line, to force -a string value use '--set-string'. You can use '--set-file' to set individual -values from a file when the value itself is too long for the command line -or is dynamically generated. You can also use '--set-json' to set json values -(scalars/objects/arrays) from the command line. Additionally, you can use '--set-json' and passing json object as a string. +To override values in a chart, use the '-f'/'--values' flag to provide a file or +the '--set' flag to pass configuration directly from the command line. To force +a string value, use '--set-string'. Use '--set-file' when a value is too long +for the command line or dynamically generated. You can also use '--set-json' to +provide JSON values (scalars, objects, or arrays) from the command line, either +as a direct argument or by passing a JSON object as a string. Alternatively, use +the '-d'/'--values-directory' flag to specify a directory containing YAML files +when you have many values or shared configurations split across files. $ helm install -f myvalues.yaml myredis ./redis @@ -76,6 +78,9 @@ or $ helm install --set-json '{"master":{"sidecars":[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]}}' myredis ./redis +or + $ helm install -d values.d/ myredis ./redis + You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. For example, if both myvalues.yaml and override.yaml contained a key called 'Test', the value set in override.yaml would take precedence: @@ -96,6 +101,33 @@ And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}': $ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis +Key details about flag '-d'/'--values-directory': +- **Purpose:** + - Specify a directory containing values YAML files. +- **Behavior:** + - All YAML files in the directory and its nested subdirectories are loaded + recursively. Non-YAML files are skipped. + - Files within a single directory are processed in **lexicographical order**, + with later files overriding earlier ones when keys overlap. +- **Precedence:** + - This flag has the **lowest precedence** among all value flags + ('-d'/'--values-directory', '-f'/'--values', '--set', '--set-string', + '--set-file', '--set-json', '--set-literal'). Values from these other flags + override values from files in the specified directory. + - Exception: The chart's default 'values.yaml' has a **lower precedence** than + the '-d'/'--values-directory' flag, i.e., the values from files in the + directory can override it. To let default values override directory files, + include 'values.yaml' explicitly via '-f'/'--values'. +- **Multiple Directories:** + - The flag can be specified **multiple times**. + - When multiple directories are provided, files in directories specified later + override values from earlier directories. + - **Lexicographical ordering** applies within each directory (and its nested + subdirectories) and not between directories specified with multiple + '-d'/'--values-directory' flags. + + $ helm install -d default-values/ -d prod-overrides/ myredis ./redis + To check the generated manifests of a release without installing the chart, the --debug and --dry-run flags can be combined. diff --git a/pkg/cmd/upgrade.go b/pkg/cmd/upgrade.go index f32493e87..80d88f509 100644 --- a/pkg/cmd/upgrade.go +++ b/pkg/cmd/upgrade.go @@ -50,12 +50,14 @@ argument can be either: a chart reference('example/mariadb'), a path to a chart a packaged chart, or a fully qualified URL. For chart references, the latest version will be specified unless the '--version' flag is set. -To override values in a chart, use either the '--values' flag and pass in a file -or use the '--set' flag and pass configuration from the command line, to force string -values, use '--set-string'. You can use '--set-file' to set individual -values from a file when the value itself is too long for the command line -or is dynamically generated. You can also use '--set-json' to set json values -(scalars/objects/arrays) from the command line. Additionally, you can use '--set-json' and passing json object as a string. +To override values in a chart, use the '-f'/'--values' flag to provide a file or +the '--set' flag to pass configuration directly from the command line. To force +a string value, use '--set-string'. Use '--set-file' when a value is too long +for the command line or dynamically generated. You can also use '--set-json' to +provide JSON values (scalars, objects, or arrays) from the command line, either +as a direct argument or by passing a JSON object as a string. Alternatively, use +the '-d'/'--values-directory' flag to specify a directory containing YAML files +when you have many values or shared configurations split across files. You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. For example, if both myvalues.yaml and override.yaml @@ -76,6 +78,33 @@ or '--set' flags. Priority is given to new values. $ helm upgrade --reuse-values --set foo=bar --set foo=newbar redis ./redis +Key details about flag '-d'/'--values-directory': +- **Purpose:** + - Specify a directory containing values YAML files. +- **Behavior:** + - All YAML files in the directory and its nested subdirectories are loaded + recursively. Non-YAML files are skipped. + - Files within a single directory are processed in **lexicographical order**, + with later files overriding earlier ones when keys overlap. +- **Precedence:** + - This flag has the **lowest precedence** among all value flags + ('-d'/'--values-directory', '-f'/'--values', '--set', '--set-string', + '--set-file', '--set-json', '--set-literal'). Values from these other flags + override values from files in the specified directory. + - Exception: The chart's default 'values.yaml' has a **lower precedence** than + the '-d'/'--values-directory' flag, i.e., the values from files in the + directory can override it. To let default values override directory files, + include 'values.yaml' explicitly via '-f'/'--values'. +- **Multiple Directories:** + - The flag can be specified **multiple times**. + - When multiple directories are provided, files in directories specified later + override values from earlier directories. + - **Lexicographical ordering** applies within each directory (and its nested + subdirectories) and not between directories specified with multiple + '-d'/'--values-directory' flags. + + $ helm upgrade -d default-values/ -d prod-overrides/ myredis ./redis + The --dry-run flag will output all generated chart manifests, including Secrets which can contain sensitive values. To hide Kubernetes Secrets use the --hide-secret flag. Please carefully consider how and when these flags are used.