From 1832d525b0690de34c03b1c8d953c7d82b72755d Mon Sep 17 00:00:00 2001 From: Yuya Takeyama Date: Tue, 18 Sep 2018 06:13:32 +0900 Subject: [PATCH 01/10] Add --no-sudo option (#4649) Signed-off-by: Yuya Takeyama --- scripts/get | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/get b/scripts/get index 29cd47e1a..326660a89 100755 --- a/scripts/get +++ b/scripts/get @@ -18,6 +18,7 @@ # the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get PROJECT_NAME="helm" +USE_SUDO="true" : ${HELM_INSTALL_DIR:="/usr/local/bin"} @@ -50,7 +51,7 @@ initOS() { runAsRoot() { local CMD="$*" - if [ $EUID -ne 0 ]; then + if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then CMD="sudo $CMD" fi @@ -180,6 +181,7 @@ help () { echo -e "\t[--help|-h ] ->> prints this help" echo -e "\t[--version|-v ] . When not defined it defaults to latest" echo -e "\te.g. --version v2.4.0 or -v latest" + echo -e "\t[--no-sudo] ->> install without sudo" } # cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 @@ -209,6 +211,9 @@ while [[ $# -gt 0 ]]; do exit 0 fi ;; + '--no-sudo') + USE_SUDO="false" + ;; '--help'|-h) help exit 0 From b7ea1c612da4aa2a5e205c2d56612f37e328b097 Mon Sep 17 00:00:00 2001 From: Yuya Takeyama Date: Tue, 18 Sep 2018 06:52:21 +0900 Subject: [PATCH 02/10] Install tiller as well if it exists (#4648) * Install tiller as well if it exists Signed-off-by: Yuya Takeyama * Fix message ref: https://github.com/helm/helm/pull/4648#discussion_r218229227 Signed-off-by: Yuya Takeyama --- scripts/get | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/get b/scripts/get index 326660a89..40fb2f69f 100755 --- a/scripts/get +++ b/scripts/get @@ -18,6 +18,7 @@ # the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get PROJECT_NAME="helm" +TILLER_NAME="tiller" USE_SUDO="true" : ${HELM_INSTALL_DIR:="/usr/local/bin"} @@ -142,8 +143,16 @@ installFile() { mkdir -p "$HELM_TMP" tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" - echo "Preparing to install into ${HELM_INSTALL_DIR}" + TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME" + echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}" runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" + echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" + if [ -x "$TILLER_TMP_BIN" ]; then + runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR" + echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME" + else + echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation" + fi } # fail_trap is executed if an error occurs. @@ -165,7 +174,6 @@ fail_trap() { # testVersion tests the installed client to make sure it is working. testVersion() { set +e - echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" HELM="$(which $PROJECT_NAME)" if [ "$?" = "1" ]; then echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' From 4dd9047586f0cfa8ed77a21d2f1062c382f27d3e Mon Sep 17 00:00:00 2001 From: Qiang Li Date: Tue, 18 Sep 2018 13:55:22 -0700 Subject: [PATCH 03/10] fix(helm): fix selector typo in service template for 'helm create' (#4663) changed instancelease to instance in service template closes #4661 Signed-off-by: Qiang Li --- pkg/chartutil/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index cff3b89eb..43fb5f7d3 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -232,7 +232,7 @@ spec: name: http selector: app.kubernetes.io/name: {{ include ".name" . }} - app.kubernetes.io/instancelease: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} ` const defaultNotes = `1. Get the application URL by running these commands: From fbda50a452b6db58faf2016b17c206ee27ddc999 Mon Sep 17 00:00:00 2001 From: Caleb Delnay Date: Tue, 18 Sep 2018 17:00:30 -0400 Subject: [PATCH 04/10] Fix credentials not set for ResolveChartVersion default HTTP client (#4662) Fixes Issue #4299 and Issue #4445 Signed-off-by: Caleb Delnay --- pkg/downloader/chart_downloader.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 92c8f9165..5e6287299 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -170,8 +170,11 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge if err != nil { return u, nil, err } - getter, err := getterConstructor(ref, "", "", "") - return u, getter, err + g, err := getterConstructor(ref, "", "", "") + if t, ok := g.(*getter.HttpGetter); ok { + t.SetCredentials(c.Username, c.Password) + } + return u, g, err } return u, nil, err } From 8be42bae885a04b4acc242cf420911145b32ee1c Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 19 Sep 2018 11:20:32 -0700 Subject: [PATCH 05/10] fix(helm): fix regression with TLS flags/environment variables not being parsed (#4657) * fix(helm): fix regression with TLS flags/envvars This change fixes some of the assumptions made in an earlier commit. Helm's TLS flags and environment variables were not respected because they were parsed well before execution (during settings.AddFlagsTLS()), causing erroneous behaviour at runtime. By re-introducing environment.Init(), Helm can properly parse environment variables at the correct time. One change that had to occur in this PR is the fact that we need to call settings.Init() each time we call settings.AddFlagsTLS(). This is because each command owns its own FlagSet, so we need to parse each flagset to read and propagate the environment variables correctly. I also noticed that we were maintaining two separate variables for each TLS value. Refactoring out some of the older code to all use the settings object makes the code much cleaner to read and fixes an issue where setting a flag or environment variable would propagate to the settings object, but we'd be reading from tlsEnable. I've also added some unit tests to ensure this regression doesn't occur again. Signed-off-by: Matthew Fisher * fix bug where os.ExpandEnv() on the default value causes differing behaviour Signed-off-by: Matthew Fisher * add more context to the TODO/FIXME messages Signed-off-by: Matthew Fisher --- cmd/helm/delete.go | 3 + cmd/helm/get.go | 3 + cmd/helm/get_hooks.go | 4 + cmd/helm/get_manifest.go | 4 + cmd/helm/get_notes.go | 3 + cmd/helm/get_values.go | 4 + cmd/helm/helm.go | 49 ++-- cmd/helm/helm_test.go | 310 +++++++++++++++++++++++ cmd/helm/history.go | 3 + cmd/helm/init.go | 18 ++ cmd/helm/install.go | 3 + cmd/helm/list.go | 3 + cmd/helm/release_testing.go | 3 + cmd/helm/reset.go | 3 + cmd/helm/rollback.go | 3 + cmd/helm/status.go | 3 + cmd/helm/upgrade.go | 3 + cmd/helm/version.go | 3 + pkg/helm/environment/environment.go | 49 ++-- pkg/helm/environment/environment_test.go | 1 + 20 files changed, 429 insertions(+), 46 deletions(-) diff --git a/cmd/helm/delete.go b/cmd/helm/delete.go index b78956ab6..4f52ffdd9 100755 --- a/cmd/helm/delete.go +++ b/cmd/helm/delete.go @@ -85,6 +85,9 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command { f.Int64Var(&del.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.StringVar(&del.description, "description", "", "specify a description for the release") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 719e0779d..20a4c042f 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -79,6 +79,9 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { cmd.AddCommand(newGetHooksCmd(nil, out)) cmd.AddCommand(newGetNotesCmd(nil, out)) + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go index 1f288245d..310b2ec73 100644 --- a/cmd/helm/get_hooks.go +++ b/cmd/helm/get_hooks.go @@ -60,6 +60,10 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command { f := cmd.Flags() settings.AddFlagsTLS(f) f.Int32Var(&ghc.version, "revision", 0, "get the named release with revision") + + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index 206c9d295..1cc7e3543 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -63,6 +63,10 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { f := cmd.Flags() settings.AddFlagsTLS(f) f.Int32Var(&get.version, "revision", 0, "get the named release with revision") + + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go index eaa3bc815..c7c3d7797 100644 --- a/cmd/helm/get_notes.go +++ b/cmd/helm/get_notes.go @@ -63,6 +63,9 @@ func newGetNotesCmd(client helm.Interface, out io.Writer) *cobra.Command { settings.AddFlagsTLS(f) f.Int32Var(&get.version, "revision", 0, "get the notes of the named release with revision") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index 38eb5f4bb..7cdfa636f 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -65,6 +65,10 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { f.Int32Var(&get.version, "revision", 0, "get the named release with revision") f.BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values") f.StringVar(&get.output, "output", "yaml", "output the specified format (json or yaml)") + + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 75fa2dc38..7f2bf369a 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -40,13 +40,6 @@ import ( ) var ( - tlsServerName string // overrides the server name used to verify the hostname on the returned certificates from the server. - tlsCaCertFile string // path to TLS CA certificate file - tlsCertFile string // path to TLS certificate file - tlsKeyFile string // path to TLS key file - tlsVerify bool // enable TLS and verify remote certificates - tlsEnable bool // enable TLS - tillerTunnel *kube.Tunnel settings helm_env.EnvSettings ) @@ -87,9 +80,21 @@ func newRootCmd(args []string) *cobra.Command { Long: globalUsage, SilenceUsage: true, PersistentPreRun: func(*cobra.Command, []string) { - tlsCaCertFile = os.ExpandEnv(tlsCaCertFile) - tlsCertFile = os.ExpandEnv(tlsCertFile) - tlsKeyFile = os.ExpandEnv(tlsKeyFile) + if settings.TLSCaCertFile == helm_env.DefaultTLSCaCert || settings.TLSCaCertFile == "" { + settings.TLSCaCertFile = settings.Home.TLSCaCert() + } else { + settings.TLSCaCertFile = os.ExpandEnv(settings.TLSCaCertFile) + } + if settings.TLSCertFile == helm_env.DefaultTLSCert || settings.TLSCertFile == "" { + settings.TLSCertFile = settings.Home.TLSCert() + } else { + settings.TLSCertFile = os.ExpandEnv(settings.TLSCertFile) + } + if settings.TLSKeyFile == helm_env.DefaultTLSKeyFile || settings.TLSKeyFile == "" { + settings.TLSKeyFile = settings.Home.TLSKey() + } else { + settings.TLSKeyFile = os.ExpandEnv(settings.TLSKeyFile) + } }, PersistentPostRun: func(*cobra.Command, []string) { teardown() @@ -143,6 +148,9 @@ func newRootCmd(args []string) *cobra.Command { flags.Parse(args) + // set defaults from environment + settings.Init(flags) + // Find and add plugins loadPlugins(cmd, out) @@ -275,24 +283,15 @@ func newClient() helm.Interface { options := []helm.Option{helm.Host(settings.TillerHost), helm.ConnectTimeout(settings.TillerConnectionTimeout)} if settings.TLSVerify || settings.TLSEnable { - if tlsCaCertFile == "" { - tlsCaCertFile = settings.Home.TLSCaCert() - } - if tlsCertFile == "" { - tlsCertFile = settings.Home.TLSCert() - } - if tlsKeyFile == "" { - tlsKeyFile = settings.Home.TLSKey() - } - debug("Host=%q, Key=%q, Cert=%q, CA=%q\n", tlsServerName, tlsKeyFile, tlsCertFile, tlsCaCertFile) + debug("Host=%q, Key=%q, Cert=%q, CA=%q\n", settings.TLSServerName, settings.TLSKeyFile, settings.TLSCertFile, settings.TLSCaCertFile) tlsopts := tlsutil.Options{ - ServerName: tlsServerName, - KeyFile: tlsKeyFile, - CertFile: tlsCertFile, + ServerName: settings.TLSServerName, + KeyFile: settings.TLSKeyFile, + CertFile: settings.TLSCertFile, InsecureSkipVerify: true, } - if tlsVerify { - tlsopts.CaCertFile = tlsCaCertFile + if settings.TLSVerify { + tlsopts.CaCertFile = settings.TLSCaCertFile tlsopts.InsecureSkipVerify = false } tlscfg, err := tlsutil.ClientConfig(tlsopts) diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index c872af8c3..3551eb534 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -30,6 +30,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/repo" @@ -229,6 +230,315 @@ func TestRootCmd(t *testing.T) { } } +func TestTLSFlags(t *testing.T) { + cleanup := resetEnv() + defer cleanup() + + homePath := os.Getenv("HELM_HOME") + if homePath == "" { + homePath = filepath.Join(os.Getenv("HOME"), ".helm") + } + + home := helmpath.Home(homePath) + + tests := []struct { + name string + args []string + envars map[string]string + settings environment.EnvSettings + }{ + { + name: "defaults", + args: []string{"version", "-c"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls enable", + args: []string{"version", "-c", "--tls"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: true, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls verify", + args: []string{"version", "-c", "--tls-verify"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: true, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls servername", + args: []string{"version", "-c", "--tls-hostname=foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "foo", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls cacert", + args: []string{"version", "-c", "--tls-ca-cert=/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: "/foo", + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls cert", + args: []string{"version", "-c", "--tls-cert=/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: "/foo", + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls key", + args: []string{"version", "-c", "--tls-key=/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: "/foo", + }, + }, + { + name: "tls enable envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_ENABLE": "true"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: true, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls verify envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_VERIFY": "true"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: true, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls servername envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_HOSTNAME": "foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "foo", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls cacert envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_CA_CERT": "/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: "/foo", + TLSCertFile: home.TLSCert(), + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls cert envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_CERT": "/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: "/foo", + TLSKeyFile: home.TLSKey(), + }, + }, + { + name: "tls key envvar", + args: []string{"version", "-c"}, + envars: map[string]string{"HELM_TLS_KEY": "/foo"}, + settings: environment.EnvSettings{ + TillerHost: "", + TillerConnectionTimeout: 300, + TillerNamespace: "kube-system", + Home: home, + Debug: false, + KubeContext: "", + KubeConfig: "", + TLSEnable: false, + TLSVerify: false, + TLSServerName: "", + TLSCaCertFile: home.TLSCaCert(), + TLSCertFile: home.TLSCert(), + TLSKeyFile: "/foo", + }, + }, + } + + // ensure not set locally + tlsEnvvars := []string{ + "HELM_TLS_HOSTNAME", + "HELM_TLS_CA_CERT", + "HELM_TLS_CERT", + "HELM_TLS_KEY", + "HELM_TLS_VERIFY", + "HELM_TLS_ENABLE", + } + + for i := range tlsEnvvars { + os.Unsetenv(tlsEnvvars[i]) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + for k, v := range tt.envars { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + + cmd := newRootCmd(tt.args) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tt.args) + cmd.Run = func(*cobra.Command, []string) {} + if err := cmd.Execute(); err != nil { + t.Errorf("unexpected error: %s", err) + } + + if settings != tt.settings { + t.Errorf("expected settings %v, got %v", tt.settings, settings) + } + }) + } +} + func resetEnv() func() { origSettings := settings origEnv := os.Environ() diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 51bc34e75..365346e89 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -93,6 +93,9 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command { f.UintVar(&his.colWidth, "col-width", 60, "specifies the max column width of output") f.StringVarP(&his.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 4be54e675..b628dc008 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -70,6 +70,12 @@ var ( // This is the IPv4 loopback, not localhost, because we have to force IPv4 // for Dockerized Helm: https://github.com/kubernetes/helm/issues/1410 localRepositoryURL = "http://127.0.0.1:8879/charts" + tlsServerName string // overrides the server name used to verify the hostname on the returned certificates from the server. + tlsCaCertFile string // path to TLS CA certificate file + tlsCertFile string // path to TLS certificate file + tlsKeyFile string // path to TLS key file + tlsVerify bool // enable TLS and verify remote certificates + tlsEnable bool // enable TLS ) type initCmd struct { @@ -121,6 +127,10 @@ func newInitCmd(out io.Writer) *cobra.Command { f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache") f.BoolVar(&i.wait, "wait", false, "block until Tiller is running and ready to receive requests") + // TODO: replace TLS flags with pkg/helm/environment.AddFlagsTLS() in Helm 3 + // + // NOTE (bacongobbler): we can't do this in Helm 2 because the flag names differ, and `helm init --tls-ca-cert` + // doesn't conform with the rest of the TLS flag names (should be --tiller-tls-ca-cert in Helm 3) f.BoolVar(&tlsEnable, "tiller-tls", false, "install Tiller with TLS enabled") f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install Tiller with TLS enabled and to verify remote certificates") f.StringVar(&tlsKeyFile, "tiller-tls-key", "", "path to TLS key file to install with Tiller") @@ -166,6 +176,14 @@ func (i *initCmd) tlsOptions() error { return errors.New("missing required TLS CA file") } } + + // FIXME: remove once we use pkg/helm/environment.AddFlagsTLS() in Helm 3 + settings.TLSEnable = tlsEnable + settings.TLSVerify = tlsVerify + settings.TLSServerName = tlsServerName + settings.TLSCaCertFile = tlsCaCertFile + settings.TLSCertFile = tlsCertFile + settings.TLSKeyFile = tlsKeyFile } return nil } diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 7f84f3355..c5c6b9a49 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -220,6 +220,9 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&inst.depUp, "dep-up", false, "run helm dependency update before installing the chart") f.StringVar(&inst.description, "description", "", "specify a description for the release") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 384fca619..3ca3fbbfa 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -140,6 +140,9 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { // TODO: Do we want this as a feature of 'helm list'? //f.BoolVar(&list.superseded, "history", true, "show historical releases") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index c7231cf04..f39d9b81f 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -68,6 +68,9 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command { f.Int64Var(&rlsTest.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&rlsTest.cleanup, "cleanup", false, "delete test pods upon completion") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/reset.go b/cmd/helm/reset.go index ffae0a613..eb1dce7f1 100644 --- a/cmd/helm/reset.go +++ b/cmd/helm/reset.go @@ -81,6 +81,9 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command { f.BoolVarP(&d.force, "force", "f", false, "forces Tiller uninstall even if there are releases installed, or if Tiller is not in ready state. Releases are not deleted.)") f.BoolVar(&d.removeHelmHome, "remove-helm-home", false, "if set deletes $HELM_HOME") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index a06b205c8..0c46fa818 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -87,6 +87,9 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&rollback.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.StringVar(&rollback.description, "description", "", "specify a description for the release") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/status.go b/cmd/helm/status.go index fe53081a4..b03453adc 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -81,6 +81,9 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command { f.Int32Var(&status.version, "revision", 0, "if set, display the status of the named release with revision") f.StringVarP(&status.outfmt, "output", "o", "", "output the status in the specified format (json or yaml)") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 905a2c175..d05987b8a 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -177,6 +177,9 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.MarkDeprecated("disable-hooks", "use --no-hooks instead") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/cmd/helm/version.go b/cmd/helm/version.go index 1c292e19d..a803a990b 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -83,6 +83,9 @@ func newVersionCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&version.short, "short", false, "print the version number") f.StringVar(&version.template, "template", "", "template for version string format") + // set defaults from environment + settings.InitTLS(f) + return cmd } diff --git a/pkg/helm/environment/environment.go b/pkg/helm/environment/environment.go index 05d955d69..76348c3bd 100644 --- a/pkg/helm/environment/environment.go +++ b/pkg/helm/environment/environment.go @@ -87,17 +87,6 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&s.Debug, "debug", false, "enable verbose output") fs.StringVar(&s.TillerNamespace, "tiller-namespace", "kube-system", "namespace of Tiller") fs.Int64Var(&s.TillerConnectionTimeout, "tiller-connection-timeout", int64(300), "the duration (in seconds) Helm will wait to establish a connection to tiller") - - envMap := map[string]string{ - "debug": "HELM_DEBUG", - "home": "HELM_HOME", - "host": "HELM_HOST", - "tiller-namespace": "TILLER_NAMESPACE", - } - - for name, envar := range envMap { - setFlagFromEnv(name, envar, fs) - } } // AddFlagsTLS adds the flags for supporting client side TLS to the given flagset. @@ -108,23 +97,38 @@ func (s *EnvSettings) AddFlagsTLS(fs *pflag.FlagSet) { fs.StringVar(&s.TLSKeyFile, "tls-key", DefaultTLSKeyFile, "path to TLS key file") fs.BoolVar(&s.TLSVerify, "tls-verify", DefaultTLSVerify, "enable TLS for request and verify remote") fs.BoolVar(&s.TLSEnable, "tls", DefaultTLSEnable, "enable TLS for request") +} - envMap := map[string]string{ - "tls-hostname": "HELM_TLS_HOSTNAME", - "tls-ca-cert": "HELM_TLS_CA_CERT", - "tls-cert": "HELM_TLS_CERT", - "tls-key": "HELM_TLS_KEY", - "tls-verify": "HELM_TLS_VERIFY", - "tls": "HELM_TLS_ENABLE", +// Init sets values from the environment. +func (s *EnvSettings) Init(fs *pflag.FlagSet) { + for name, envar := range envMap { + setFlagFromEnv(name, envar, fs) } +} - for name, envar := range envMap { +// InitTLS sets TLS values from the environment. +func (s *EnvSettings) InitTLS(fs *pflag.FlagSet) { + for name, envar := range tlsEnvMap { setFlagFromEnv(name, envar, fs) } } -// Init is deprecated; calling `.AddFlags` or `.AddFlagsTLS` directly will set the flags to their default values from the environment, so this is a no-op. -func (s *EnvSettings) Init(fs *pflag.FlagSet) {} +// envMap maps flag names to envvars +var envMap = map[string]string{ + "debug": "HELM_DEBUG", + "home": "HELM_HOME", + "host": "HELM_HOST", + "tiller-namespace": "TILLER_NAMESPACE", +} + +var tlsEnvMap = map[string]string{ + "tls-hostname": "HELM_TLS_HOSTNAME", + "tls-ca-cert": "HELM_TLS_CA_CERT", + "tls-cert": "HELM_TLS_CERT", + "tls-key": "HELM_TLS_KEY", + "tls-verify": "HELM_TLS_VERIFY", + "tls": "HELM_TLS_ENABLE", +} // PluginDirs is the path to the plugin directories. func (s EnvSettings) PluginDirs() string { @@ -134,6 +138,9 @@ func (s EnvSettings) PluginDirs() string { return s.Home.Plugins() } +// setFlagFromEnv looks up and sets a flag if the corresponding environment variable changed. +// if the flag with the corresponding name was set during fs.Parse(), then the environment +// variable is ignored. func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) { if fs.Changed(name) { return diff --git a/pkg/helm/environment/environment_test.go b/pkg/helm/environment/environment_test.go index fb05254ed..675582cf6 100644 --- a/pkg/helm/environment/environment_test.go +++ b/pkg/helm/environment/environment_test.go @@ -123,6 +123,7 @@ func TestEnvSettings(t *testing.T) { flags.Parse(tt.args) settings.Init(flags) + settings.InitTLS(flags) if settings.Home != helmpath.Home(tt.home) { t.Errorf("expected home %q, got %q", tt.home, settings.Home) From 12ace315eaac2e0e1bcc5ba79551dd90f3ebdd3e Mon Sep 17 00:00:00 2001 From: Colin Panisset Date: Thu, 27 Sep 2018 10:38:39 +1000 Subject: [PATCH 06/10] Don't assume that the returned error from the storage driver isn't nil (#3625) (#4627) Signed-off-by: Colin Panisset --- pkg/tiller/release_server.go | 21 +++++++++--- pkg/tiller/release_server_test.go | 57 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index e913579aa..e562be203 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -202,15 +202,28 @@ func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) { return "", fmt.Errorf("a release named %s already exists.\nRun: helm ls --all %s; to check the status of the release\nOr run: helm del --purge %s; to delete it", start, start, start) } + moniker := moniker.New() + newname, err := s.createUniqName(moniker) + if err != nil { + return "ERROR", err + } + + s.Log("info: Created new release name %s", newname) + return newname, nil + +} + +func (s *ReleaseServer) createUniqName(m moniker.Namer) (string, error) { maxTries := 5 for i := 0; i < maxTries; i++ { - namer := moniker.New() - name := namer.NameSep("-") + name := m.NameSep("-") if len(name) > releaseNameMaxLen { name = name[:releaseNameMaxLen] } - if _, err := s.env.Releases.Get(name, 1); strings.Contains(err.Error(), "not found") { - return name, nil + if _, err := s.env.Releases.Get(name, 1); err != nil { + if strings.Contains(err.Error(), "not found") { + return name, nil + } } s.Log("info: generated name %s is taken. Searching again.", name) } diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index f3fca7390..b8adb4bb2 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -28,6 +28,7 @@ import ( "github.com/ghodss/yaml" "github.com/golang/protobuf/ptypes/timestamp" + "github.com/technosophos/moniker" "golang.org/x/net/context" "google.golang.org/grpc/metadata" "k8s.io/kubernetes/pkg/apis/core" @@ -380,6 +381,62 @@ func TestUniqName(t *testing.T) { } } +type fakeNamer struct { + name string +} + +func NewFakeNamer(nam string) moniker.Namer { + return &fakeNamer{ + name: nam, + } +} + +func (f *fakeNamer) Name() string { + return f.NameSep(" ") +} + +func (f *fakeNamer) NameSep(sep string) string { + return f.name +} + +func TestCreateUniqueName(t *testing.T) { + rs := rsFixture() + + rel1 := releaseStub() + rel1.Name = "happy-panda" + + rs.env.Releases.Create(rel1) + + tests := []struct { + name string + expect string + err bool + }{ + {"happy-panda", "ERROR", true}, + {"wobbly-octopus", "[a-z]+-[a-z]+", false}, + } + + for _, tt := range tests { + m := NewFakeNamer(tt.name) + u, err := rs.createUniqName(m) + if err != nil { + if tt.err { + continue + } + t.Fatal(err) + } + if tt.err { + t.Errorf("Expected an error for %q", tt.name) + } + if match, err := regexp.MatchString(tt.expect, u); err != nil { + t.Fatal(err) + } else if !match { + t.Errorf("Expected %q to match %q", u, tt.expect) + } + } + +} + func releaseWithKeepStub(rlsName string) *release.Release { ch := &chart.Chart{ Metadata: &chart.Metadata{ From 597c4fb421306f9673f43a27b0927af3884da84b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 28 Sep 2018 08:22:52 -0600 Subject: [PATCH 07/10] docs(provenance): update explanation of new GnuPG format (#4710) Signed-off-by: Matt Butcher --- docs/provenance.md | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/provenance.md b/docs/provenance.md index 331074e8c..d8f9e4089 100644 --- a/docs/provenance.md +++ b/docs/provenance.md @@ -22,12 +22,17 @@ Prerequisites: - A valid PGP keypair in a binary (not ASCII-armored) format - The `helm` command line tool -- GnuPG command line tools (optional) +- GnuPG >=2.1 command line tools (optional) - Keybase command line tools (optional) **NOTE:** If your PGP private key has a passphrase, you will be prompted to enter that passphrase for any commands that support the `--sign` option. +**NOTE:** The keyfile format for GnuPG changed in version 2.1. Prior to that release +it was unnecessary to export keys out of GnuPG, and you could instead point Helm +at your `*.gpg` files. With 2.1, the new `.kbx` format was introduced, and this +format is not supported by Helm. + Creating a new chart is the same as before: ``` @@ -42,10 +47,10 @@ the name under which the signing key is known and the keyring containing the cor $ helm package --sign --key 'helm signing key' --keyring path/to/keyring.secret mychart ``` -**TIP:** for GnuPG users, your secret keyring is in `~/.gnupg/secring.gpg`. You can +**TIP:** for GnuPG users, your secret keyring is in `~/.gnupg/secring.kbx`. You can use `gpg --list-secret-keys` to list the keys you have. -**Warning:** the GnuPG v2 store your secret keyring using a new format 'kbx' on the default location '~/.gnupg/pubring.kbx'. Please use the following command to convert your keyring to the legacy gpg format: +**Warning:** the GnuPG v2.1 store your secret keyring using a new format 'kbx' on the default location '~/.gnupg/pubring.kbx'. Please use the following command to convert your keyring to the legacy gpg format: ``` $ gpg --export-secret-keys >~/.gnupg/secring.gpg @@ -95,24 +100,16 @@ Prerequisites: The first step is to import your keybase keys into your local GnuPG keyring: ``` -$ keybase pgp export -s | gpg --import +$ keybase pgp export -s > secring.gpg ``` -This will convert your Keybase key into the OpenPGP format, and then import it -locally into your `~/.gnupg/secring.gpg` file. - -You can double check by running `gpg --list-secret-keys`. +This will convert your Keybase key into the OpenPGP format, and then place it +locally into your `secring.gpg` file. -``` -$ gpg --list-secret-keys 1 ↵ -/Users/mattbutcher/.gnupg/secring.gpg -------------------------------------- -sec 2048R/1FC18762 2016-07-25 -uid technosophos (keybase.io/technosophos) -ssb 2048R/D125E546 2016-07-25 -``` +> Tip: If you need to add a Keybase key to an existing keyring, you will need to +> do `keybase pgp export -s | gpg --import && gpg --export-secret-keys --outfile secring.gpg` -Note that your secret key will have an identifier string: +Your secret key will have an identifier string: ``` technosophos (keybase.io/technosophos) From 4faacc6f8c8406b3f20d7127eed8ee0b06015ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Prud=27homme?= Date: Fri, 28 Sep 2018 19:02:48 +0200 Subject: [PATCH 08/10] Empty helm.sh/resource-policy annotation should not keep the resource (#4713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Prud'homme --- pkg/tiller/resource_policy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go index cca2391d8..fadae7d57 100644 --- a/pkg/tiller/resource_policy.go +++ b/pkg/tiller/resource_policy.go @@ -52,8 +52,9 @@ func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) { resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType)) if resourcePolicyType == keepPolicy { keep = append(keep, m) + } else { + remaining = append(remaining, m) } - } return keep, remaining } From fc35de70d9fc8aa85b8943e1c5979f44eb7f0e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Prud=27homme?= Date: Sat, 13 Oct 2018 19:36:13 +0200 Subject: [PATCH 09/10] Add a way to delete the resource and keeping backward compatibility --- docs/charts_tips_and_tricks.md | 3 +++ pkg/tiller/resource_policy.go | 18 +++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/charts_tips_and_tricks.md b/docs/charts_tips_and_tricks.md index a83b44457..a23ff694e 100644 --- a/docs/charts_tips_and_tricks.md +++ b/docs/charts_tips_and_tricks.md @@ -219,6 +219,9 @@ orphaned. Helm will no longer manage it in any way. This can lead to problems if using `helm install --replace` on a release that has already been deleted, but has kept resources. +For backward compatibility, an `"helm.sh/resource-policy"` annotation with an empty +value also means to keep the resource. Any other value means to delete the resource. + ## Using "Partials" and Template Includes Sometimes you want to create some reusable parts in your chart, whether diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go index fadae7d57..d2f38d605 100644 --- a/pkg/tiller/resource_policy.go +++ b/pkg/tiller/resource_policy.go @@ -34,29 +34,33 @@ const resourcePolicyAnno = "helm.sh/resource-policy" const keepPolicy = "keep" func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) { - remaining := []Manifest{} - keep := []Manifest{} + toDelete := []Manifest{} + toKeep := []Manifest{} for _, m := range manifests { if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { - remaining = append(remaining, m) + toDelete = append(toDelete, m) continue } resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno] if !ok { - remaining = append(remaining, m) + toDelete = append(toDelete, m) continue } resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType)) if resourcePolicyType == keepPolicy { - keep = append(keep, m) + toKeep = append(toKeep, m) + } else if resourcePolicyType == "" { + // Empty annotation means keep in Helm 2 + toKeep = append(toKeep, m) } else { - remaining = append(remaining, m) + // Any other annotation means delete in Helm 2 + toDelete = append(toDelete, m) } } - return keep, remaining + return toKeep, toDelete } func summarizeKeptManifests(manifests []Manifest, kubeClient environment.KubeClient, namespace string) string { From 968b26dcc0e46c6282db060f1998b5352c10c97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Prud=27homme?= Date: Sat, 13 Oct 2018 19:36:13 +0200 Subject: [PATCH 10/10] Add a way to delete the resource and keeping backward compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Prud'homme --- docs/charts_tips_and_tricks.md | 3 +++ pkg/tiller/resource_policy.go | 18 +++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/charts_tips_and_tricks.md b/docs/charts_tips_and_tricks.md index a83b44457..a23ff694e 100644 --- a/docs/charts_tips_and_tricks.md +++ b/docs/charts_tips_and_tricks.md @@ -219,6 +219,9 @@ orphaned. Helm will no longer manage it in any way. This can lead to problems if using `helm install --replace` on a release that has already been deleted, but has kept resources. +For backward compatibility, an `"helm.sh/resource-policy"` annotation with an empty +value also means to keep the resource. Any other value means to delete the resource. + ## Using "Partials" and Template Includes Sometimes you want to create some reusable parts in your chart, whether diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go index fadae7d57..d2f38d605 100644 --- a/pkg/tiller/resource_policy.go +++ b/pkg/tiller/resource_policy.go @@ -34,29 +34,33 @@ const resourcePolicyAnno = "helm.sh/resource-policy" const keepPolicy = "keep" func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) { - remaining := []Manifest{} - keep := []Manifest{} + toDelete := []Manifest{} + toKeep := []Manifest{} for _, m := range manifests { if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { - remaining = append(remaining, m) + toDelete = append(toDelete, m) continue } resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno] if !ok { - remaining = append(remaining, m) + toDelete = append(toDelete, m) continue } resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType)) if resourcePolicyType == keepPolicy { - keep = append(keep, m) + toKeep = append(toKeep, m) + } else if resourcePolicyType == "" { + // Empty annotation means keep in Helm 2 + toKeep = append(toKeep, m) } else { - remaining = append(remaining, m) + // Any other annotation means delete in Helm 2 + toDelete = append(toDelete, m) } } - return keep, remaining + return toKeep, toDelete } func summarizeKeptManifests(manifests []Manifest, kubeClient environment.KubeClient, namespace string) string {