mirror of https://github.com/helm/helm.git
feat(*): Ports `--set-file` flag to v3
I made a few modifications from the original code to fit in with the new code layout and to clarify a few things. This is a port of #3758 Signed-off-by: Taylor Thomas <taylor.thomas@microsoft.com>
This commit is contained in:
parent
6419ff6961
commit
7599c5d489
|
@ -42,7 +42,9 @@ 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'.
|
||||
a string value use '--set-string'. In case a value is large and therefore
|
||||
you want not to use neither '--values' nor '--set', use '--set-file' to read the
|
||||
single large value from file.
|
||||
|
||||
$ helm install -f myvalues.yaml myredis ./redis
|
||||
|
||||
|
@ -54,6 +56,9 @@ or
|
|||
|
||||
$ helm install --set-string long_int=1234567890 myredis ./redis
|
||||
|
||||
or
|
||||
$ helm install --set-file my_script=dothings.sh 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:
|
||||
|
@ -140,6 +145,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
|
|||
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)")
|
||||
f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
|
||||
}
|
||||
|
||||
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
|
||||
|
|
|
@ -42,7 +42,9 @@ 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'.
|
||||
values, use '--set-string'. In case a value is large and therefore
|
||||
you want not to use neither '--values' nor '--set', use '--set-file' to read the
|
||||
single large value from file.
|
||||
|
||||
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
|
||||
|
|
|
@ -33,10 +33,11 @@ type Options struct {
|
|||
ValueFiles []string
|
||||
StringValues []string
|
||||
Values []string
|
||||
FileValues []string
|
||||
}
|
||||
|
||||
// MergeValues merges values from files specified via -f/--values and
|
||||
// directly via --set or --set-string, marshaling them to YAML
|
||||
// MergeValues merges values from files specified via -f/--values and directly
|
||||
// via --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{}{}
|
||||
|
||||
|
@ -70,6 +71,17 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er
|
|||
}
|
||||
}
|
||||
|
||||
// User specified a value via --set-file
|
||||
for _, value := range opts.FileValues {
|
||||
reader := func(rs []rune) (interface{}, error) {
|
||||
bytes, err := readFile(string(rs), p)
|
||||
return string(bytes), err
|
||||
}
|
||||
if err := strvals.ParseIntoFile(value, base, reader); err != nil {
|
||||
return nil, errors.Wrap(err, "failed parsing --set-file data")
|
||||
}
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,20 @@ func ParseInto(s string, dest map[string]interface{}) error {
|
|||
return t.parse()
|
||||
}
|
||||
|
||||
// ParseFile parses a set line, but its final value is loaded from the file at the path specified by the original value.
|
||||
//
|
||||
// A set line is of the form name1=path1,name2=path2
|
||||
//
|
||||
// When the files at path1 and path2 contained "val1" and "val2" respectively, the set line is consumed as
|
||||
// name1=val1,name2=val2
|
||||
func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error) {
|
||||
vals := map[string]interface{}{}
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newFileParser(scanner, vals, reader)
|
||||
err := t.parse()
|
||||
return vals, err
|
||||
}
|
||||
|
||||
// ParseIntoString parses a strvals line nad merges the result into dest.
|
||||
//
|
||||
// This method always returns a string as the value.
|
||||
|
@ -79,16 +93,36 @@ func ParseIntoString(s string, dest map[string]interface{}) error {
|
|||
return t.parse()
|
||||
}
|
||||
|
||||
// ParseIntoFile parses a filevals line and merges the result into dest.
|
||||
//
|
||||
// This method always returns a string as the value.
|
||||
func ParseIntoFile(s string, dest map[string]interface{}, reader RunesValueReader) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newFileParser(scanner, dest, reader)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// RunesValueReader is a function that takes the given value (a slice of runes)
|
||||
// and returns the parsed value
|
||||
type RunesValueReader func([]rune) (interface{}, error)
|
||||
|
||||
// parser is a simple parser that takes a strvals line and parses it into a
|
||||
// map representation.
|
||||
type parser struct {
|
||||
sc *bytes.Buffer
|
||||
data map[string]interface{}
|
||||
st bool
|
||||
sc *bytes.Buffer
|
||||
data map[string]interface{}
|
||||
reader RunesValueReader
|
||||
}
|
||||
|
||||
func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser {
|
||||
return &parser{sc: sc, data: data, st: stringBool}
|
||||
stringConverter := func(rs []rune) (interface{}, error) {
|
||||
return typedVal(rs, stringBool), nil
|
||||
}
|
||||
return &parser{sc: sc, data: data, reader: stringConverter}
|
||||
}
|
||||
|
||||
func newFileParser(sc *bytes.Buffer, data map[string]interface{}, reader RunesValueReader) *parser {
|
||||
return &parser{sc: sc, data: data, reader: reader}
|
||||
}
|
||||
|
||||
func (t *parser) parse() error {
|
||||
|
@ -152,8 +186,12 @@ func (t *parser) key(data map[string]interface{}) error {
|
|||
set(data, string(k), "")
|
||||
return e
|
||||
case ErrNotList:
|
||||
v, e := t.val()
|
||||
set(data, string(k), typedVal(v, t.st))
|
||||
rs, e := t.val()
|
||||
if e != nil && e != io.EOF {
|
||||
return e
|
||||
}
|
||||
v, e := t.reader(rs)
|
||||
set(data, string(k), v)
|
||||
return e
|
||||
default:
|
||||
return e
|
||||
|
@ -225,8 +263,12 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
|
|||
case io.EOF:
|
||||
return setIndex(list, i, ""), err
|
||||
case ErrNotList:
|
||||
v, e := t.val()
|
||||
return setIndex(list, i, typedVal(v, t.st)), e
|
||||
rs, e := t.val()
|
||||
if e != nil && e != io.EOF {
|
||||
return list, e
|
||||
}
|
||||
v, e := t.reader(rs)
|
||||
return setIndex(list, i, v), e
|
||||
default:
|
||||
return list, e
|
||||
}
|
||||
|
@ -274,7 +316,7 @@ func (t *parser) valList() ([]interface{}, error) {
|
|||
list := []interface{}{}
|
||||
stop := runeSet([]rune{',', '}'})
|
||||
for {
|
||||
switch v, last, err := runesUntil(t.sc, stop); {
|
||||
switch rs, last, err := runesUntil(t.sc, stop); {
|
||||
case err != nil:
|
||||
if err == io.EOF {
|
||||
err = errors.New("list must terminate with '}'")
|
||||
|
@ -285,10 +327,15 @@ func (t *parser) valList() ([]interface{}, error) {
|
|||
if r, _, e := t.sc.ReadRune(); e == nil && r != ',' {
|
||||
t.sc.UnreadRune()
|
||||
}
|
||||
list = append(list, typedVal(v, t.st))
|
||||
return list, nil
|
||||
v, e := t.reader(rs)
|
||||
list = append(list, v)
|
||||
return list, e
|
||||
case last == ',':
|
||||
list = append(list, typedVal(v, t.st))
|
||||
v, e := t.reader(rs)
|
||||
if e != nil {
|
||||
return list, e
|
||||
}
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,6 +358,39 @@ func TestParseInto(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseIntoFile(t *testing.T) {
|
||||
got := map[string]interface{}{}
|
||||
input := "name1=path1"
|
||||
expect := map[string]interface{}{
|
||||
"name1": "value1",
|
||||
}
|
||||
rs2v := func(rs []rune) (interface{}, error) {
|
||||
v := string(rs)
|
||||
if v != "path1" {
|
||||
t.Errorf("%s: RunesValueReader: Expected value path1, got %s", input, v)
|
||||
return "", nil
|
||||
}
|
||||
return "value1", nil
|
||||
}
|
||||
|
||||
if err := ParseIntoFile(input, got, rs2v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
y1, err := yaml.Marshal(expect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
y2, err := yaml.Marshal(got)
|
||||
if err != nil {
|
||||
t.Fatalf("Error serializing parsed value: %s", err)
|
||||
}
|
||||
|
||||
if string(y1) != string(y2) {
|
||||
t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToYAML(t *testing.T) {
|
||||
// The TestParse does the hard part. We just verify that YAML formatting is
|
||||
// happening.
|
||||
|
|
Loading…
Reference in New Issue