feat: add --set and --values options to 'helm package'

When 'helm package --set stringsArray' is run, this will set/override values
in the packaged chart. 'helm package --values valueFiles' uses one or more
value files to achieve the same.

Closes #3141

Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>
pull/3471/head
Arash Deshmeh 7 years ago
parent 06f7b7c0da
commit a930eb7ff4

@ -53,6 +53,8 @@ type packageCmd struct {
save bool save bool
sign bool sign bool
path string path string
valueFiles valueFiles
values []string
key string key string
keyring string keyring string
version string version string
@ -95,6 +97,8 @@ func newPackageCmd(out io.Writer) *cobra.Command {
} }
f := cmd.Flags() f := cmd.Flags()
f.VarP(&pkg.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringArrayVar(&pkg.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true")
@ -133,6 +137,20 @@ func (p *packageCmd) run() error {
return err return err
} }
overrideVals, err := vals(p.valueFiles, p.values)
if err != nil {
return err
}
combinedVals, err := chartutil.CoalesceValues(ch, &chart.Config{Raw: string(overrideVals)})
if err != nil {
return err
}
newVals, err := combinedVals.YAML()
if err != nil {
return err
}
ch.Values = &chart.Config{Raw: newVals}
// If version is set, modify the version. // If version is set, modify the version.
if len(p.version) != 0 { if len(p.version) != 0 {
if err := setVersion(ch, p.version); err != nil { if err := setVersion(ch, p.version); err != nil {

@ -21,6 +21,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"testing" "testing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -122,6 +123,13 @@ func TestPackage(t *testing.T) {
hasfile: "chart-missing-deps-0.1.0.tgz", hasfile: "chart-missing-deps-0.1.0.tgz",
err: true, err: true,
}, },
{
name: "package --values does-not-exist",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"values": "does-not-exist"},
expect: "does-not-exist: no such file or directory",
err: true,
},
} }
// Because these tests are destructive, we run them in a tempdir. // Because these tests are destructive, we run them in a tempdir.
@ -245,6 +253,150 @@ func TestSetAppVersion(t *testing.T) {
} }
} }
func TestPackageValues(t *testing.T) {
testCases := []struct {
desc string
args []string
valuefilesContents []string
flags map[string]string
expected []string
}{
{
desc: "helm package, single values file",
args: []string{"testdata/testcharts/alpine"},
valuefilesContents: []string{"Name: chart-name-foo"},
expected: []string{"Name: chart-name-foo"},
},
{
desc: "helm package, multiple values files",
args: []string{"testdata/testcharts/alpine"},
valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"},
expected: []string{"Name: chart-name-foo", "foo: bar"},
},
{
desc: "helm package, with set option",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"set": "Name=chart-name-foo"},
expected: []string{"Name: chart-name-foo"},
},
{
desc: "helm package, set takes precedence over value file",
args: []string{"testdata/testcharts/alpine"},
valuefilesContents: []string{"Name: chart-name-foo"},
flags: map[string]string{"set": "Name=chart-name-bar"},
expected: []string{"Name: chart-name-bar"},
},
}
thome, err := tempHelmHome(t)
if err != nil {
t.Fatal(err)
}
cleanup := resetEnv()
defer func() {
os.RemoveAll(thome.String())
cleanup()
}()
settings.Home = thome
for _, tc := range testCases {
var files []string
for _, contents := range tc.valuefilesContents {
f, err := createValuesFile(contents)
if err != nil {
t.Errorf("%q unexpected error creating temporary values file: %q", tc.desc, err)
}
defer os.RemoveAll(filepath.Dir(f))
files = append(files, f)
}
valueFiles := strings.Join(files, ",")
expected, err := chartutil.ReadValues([]byte(strings.Join(tc.expected, "\n")))
if err != nil {
t.Errorf("unexpected error parsing values: %q", err)
}
runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected)
}
}
func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) {
outputDir, err := ioutil.TempDir("", "helm-package")
if err != nil {
t.Errorf("unexpected error creating temporary output directory: %q", err)
}
defer os.RemoveAll(outputDir)
if len(flags) == 0 {
flags = make(map[string]string)
}
flags["destination"] = outputDir
if len(valueFiles) > 0 {
flags["values"] = valueFiles
}
cmd := newPackageCmd(&bytes.Buffer{})
setFlags(cmd, flags)
err = cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error: %q", err)
}
outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
verifyOutputChartExists(t, outputFile)
var actual chartutil.Values
actual, err = getChartValues(outputFile)
if err != nil {
t.Errorf("unexpected error extracting chart values: %q", err)
}
verifyValues(t, actual, expected)
}
func createValuesFile(data string) (string, error) {
outputDir, err := ioutil.TempDir("", "values-file")
if err != nil {
return "", err
}
outputFile := filepath.Join(outputDir, "values.yaml")
if err = ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil {
os.RemoveAll(outputFile)
return "", err
}
return outputFile, nil
}
func getChartValues(chartPath string) (chartutil.Values, error) {
chart, err := chartutil.Load(chartPath)
if err != nil {
return nil, err
}
return chartutil.ReadValues([]byte(chart.Values.Raw))
}
func verifyValues(t *testing.T, actual, expected chartutil.Values) {
for key, value := range expected.AsMap() {
if got := actual[key]; got != value {
t.Errorf("Expected %q, got %q (%v)", value, got, actual)
}
}
}
func verifyOutputChartExists(t *testing.T, chartPath string) {
if chartFile, err := os.Stat(chartPath); err != nil {
t.Errorf("expected file %q, got err %q", chartPath, err)
} else if chartFile.Size() == 0 {
t.Errorf("file %q has zero bytes.", chartPath)
}
}
func setFlags(cmd *cobra.Command, flags map[string]string) { func setFlags(cmd *cobra.Command, flags map[string]string) {
dest := cmd.Flags() dest := cmd.Flags()
for f, v := range flags { for f, v := range flags {

@ -29,7 +29,9 @@ helm package [flags] [CHART_PATH] [...]
--key string name of the key to use when signing. Used if --sign is true --key string name of the key to use when signing. Used if --sign is true
--keyring string location of a public keyring (default "~/.gnupg/pubring.gpg") --keyring string location of a public keyring (default "~/.gnupg/pubring.gpg")
--save save packaged chart to local chart repository (default true) --save save packaged chart to local chart repository (default true)
--set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
--sign use a PGP private key to sign this package --sign use a PGP private key to sign this package
-f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default [])
--version string set the version on the chart to this semver version --version string set the version on the chart to this semver version
``` ```
@ -47,4 +49,4 @@ helm package [flags] [CHART_PATH] [...]
### SEE ALSO ### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 8-Mar-2018 ###### Auto generated by spf13/cobra on 14-Mar-2018

Loading…
Cancel
Save