Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Commit

Permalink
feat: add --set and --values options to 'helm package'
Browse files Browse the repository at this point in the history
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 helm#3141

Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>
  • Loading branch information
adshmh authored and rajatjindal committed Feb 24, 2018
1 parent 6af75a8 commit cabeac8
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 1 deletion.
18 changes: 18 additions & 0 deletions cmd/helm/package.go
Expand Up @@ -53,6 +53,8 @@ type packageCmd struct {
save bool
sign bool
path string
valueFiles valueFiles
values []string
key string
keyring string
version string
Expand Down Expand Up @@ -95,6 +97,8 @@ func newPackageCmd(out io.Writer) *cobra.Command {
}

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.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")
Expand Down Expand Up @@ -133,6 +137,20 @@ func (p *packageCmd) run() error {
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 len(p.version) != 0 {
if err := setVersion(ch, p.version); err != nil {
Expand Down
140 changes: 140 additions & 0 deletions cmd/helm/package_test.go
Expand Up @@ -21,6 +21,7 @@ import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -122,6 +123,13 @@ func TestPackage(t *testing.T) {
hasfile: "chart-missing-deps-0.1.0.tgz",
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.
Expand Down Expand Up @@ -233,6 +241,138 @@ 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"},
},
}

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) {
dest := cmd.Flags()
for f, v := range flags {
Expand Down
4 changes: 3 additions & 1 deletion docs/helm/helm_package.md
Expand Up @@ -29,7 +29,9 @@ helm package [flags] [CHART_PATH] [...]
--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")
--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
-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
```

Expand All @@ -46,4 +48,4 @@ helm package [flags] [CHART_PATH] [...]
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.

###### Auto generated by spf13/cobra on 25-Jan-2018
###### Auto generated by spf13/cobra on 8-Feb-2018

0 comments on commit cabeac8

Please sign in to comment.