diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index ca60c9985df0f0dd5c3ce21829a2e75506edb757..b91efbbc642dc9724c6de1cce06fe354011d6438 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -21,10 +21,12 @@ import ( "fmt" "io" "io/ioutil" + "strings" "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/storage/driver" ) const upgradeDesc = ` @@ -48,6 +50,8 @@ type upgradeCmd struct { values *values verify bool keyring string + install bool + namespace string } func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { @@ -83,41 +87,45 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks") f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading") f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "the path to the keyring that contains public singing keys") + f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install") + f.StringVar(&upgrade.namespace, "namespace", "default", "the namespace to install the release into (only used if --install is set)") return cmd } -func (u *upgradeCmd) vals() ([]byte, error) { - var buffer bytes.Buffer - - // User specified a values file via -f/--values - if u.valuesFile != "" { - bytes, err := ioutil.ReadFile(u.valuesFile) - if err != nil { - return []byte{}, err - } - buffer.Write(bytes) - } - - // User specified value pairs via --set - // These override any values in the specified file - if len(u.values.pairs) > 0 { - bytes, err := u.values.yaml() - if err != nil { - return []byte{}, err - } - buffer.Write(bytes) - } - - return buffer.Bytes(), nil -} - func (u *upgradeCmd) run() error { chartPath, err := locateChartPath(u.chart, u.verify, u.keyring) if err != nil { return err } + if u.install { + // If a release does not exist, install it. If another error occurs during + // the check, ignore the error and continue with the upgrade. + // + // The returned error is a grpc.rpcError that wraps the message from the original error. + // So we're stuck doing string matching against the wrapped error, which is nested somewhere + // inside of the grpc.rpcError message. + _, err := u.client.ReleaseContent(u.release, helm.ContentReleaseVersion(1)) + if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound.Error()) { + fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release) + ic := &installCmd{ + chartPath: chartPath, + client: u.client, + out: u.out, + name: u.release, + valuesFile: u.valuesFile, + dryRun: u.dryRun, + verify: u.verify, + disableHooks: u.disableHooks, + keyring: u.keyring, + values: u.values, + namespace: u.namespace, + } + return ic.run() + } + } + rawVals, err := u.vals() if err != nil { return err @@ -139,5 +147,29 @@ func (u *upgradeCmd) run() error { PrintStatus(u.out, status) return nil +} + +func (u *upgradeCmd) vals() ([]byte, error) { + var buffer bytes.Buffer + + // User specified a values file via -f/--values + if u.valuesFile != "" { + bytes, err := ioutil.ReadFile(u.valuesFile) + if err != nil { + return []byte{}, err + } + buffer.Write(bytes) + } + // User specified value pairs via --set + // These override any values in the specified file + if len(u.values.pairs) > 0 { + bytes, err := u.values.yaml() + if err != nil { + return []byte{}, err + } + buffer.Write(bytes) + } + + return buffer.Bytes(), nil } diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index f51049e28388220ff9455cdda40188f6e62070e5..7eab8acde375304c63b2a5ceaf9c1a55526ff6e9 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -69,6 +69,13 @@ func TestUpgradeCmd(t *testing.T) { resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}), expected: "funny-bunny has been upgraded. Happy Helming!\n", }, + { + name: "install a release with 'upgrade --install'", + args: []string{"zany-bunny", chartPath}, + flags: []string{"-i"}, + resp: releaseMock(&releaseOptions{name: "zany-bunny", version: 1, chart: ch}), + expected: "zany-bunny has been upgraded. Happy Helming!\n", + }, } cmd := func(c *fakeReleaseClient, out io.Writer) *cobra.Command {