Repository: helm/helm
Pull request #10628 (2022-01-30 21:43:26)

Avoid querying for OCI tags can explicit version provided in chart dependencies
Some OCI registries do not support listing tags as an available endpoint. When specifying a chart located in one of these registries, failures occur.

This enhancement aligns the functionality where tag listing is only queried whenever either no version is specified or a semver range is provided. 

Related to #10624


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7a9c4874d..f3599607b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -13,7 +13,7 @@ jobs:
 
     environment:
       GOCACHE: "/tmp/go/cache"
-      GOLANGCI_LINT_VERSION: "1.43.0"
+      GOLANGCI_LINT_VERSION: "1.36.0"
 
     steps:
       - checkout
diff --git a/.golangci.yml b/.golangci.yml
index 2807ede34..5c2445711 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -8,12 +8,12 @@ linters:
     - dupl
     - gofmt
     - goimports
+    - golint
     - gosimple
     - govet
     - ineffassign
     - misspell
     - nakedret
-    - revive
     - structcheck
     - unused
     - varcheck
diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go
index a97d49a93..70002b0b0 100644
--- a/cmd/helm/load_plugins.go
+++ b/cmd/helm/load_plugins.go
@@ -313,7 +313,7 @@ func loadFile(path string) (*pluginCommand, error) {
 	cmds := new(pluginCommand)
 	b, err := ioutil.ReadFile(path)
 	if err != nil {
-		return cmds, fmt.Errorf("file (%s) not provided by plugin. No plugin auto-completion possible", path)
+		return cmds, errors.New(fmt.Sprintf("File (%s) not provided by plugin. No plugin auto-completion possible.", path))
 	}
 
 	err = yaml.Unmarshal(b, cmds)
diff --git a/cmd/helm/push.go b/cmd/helm/push.go
index d2cf2693e..ed5d1699a 100644
--- a/cmd/helm/push.go
+++ b/cmd/helm/push.go
@@ -24,7 +24,6 @@ import (
 
 	"helm.sh/helm/v3/cmd/helm/require"
 	"helm.sh/helm/v3/pkg/action"
-	"helm.sh/helm/v3/pkg/pusher"
 )
 
 const pushDesc = `
@@ -42,23 +41,6 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 		Short: "push a chart to remote",
 		Long:  pushDesc,
 		Args:  require.MinimumNArgs(2),
-		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-			if len(args) == 0 {
-				// Do file completion for the chart file to push
-				return nil, cobra.ShellCompDirectiveDefault
-			}
-			if len(args) == 1 {
-				providers := []pusher.Provider(pusher.All(settings))
-				var comps []string
-				for _, p := range providers {
-					for _, scheme := range p.Schemes {
-						comps = append(comps, fmt.Sprintf("%s://", scheme))
-					}
-				}
-				return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
-			}
-			return nil, cobra.ShellCompDirectiveNoFileComp
-		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			chartRef := args[0]
 			remote := args[1]
diff --git a/cmd/helm/registry_login.go b/cmd/helm/registry_login.go
index 8abf91e77..84f9f27b5 100644
--- a/cmd/helm/registry_login.go
+++ b/cmd/helm/registry_login.go
@@ -41,11 +41,10 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
 	var passwordFromStdinOpt, insecureOpt bool
 
 	cmd := &cobra.Command{
-		Use:               "login [host]",
-		Short:             "login to a registry",
-		Long:              registryLoginDesc,
-		Args:              require.MinimumNArgs(1),
-		ValidArgsFunction: noCompletions,
+		Use:   "login [host]",
+		Short: "login to a registry",
+		Long:  registryLoginDesc,
+		Args:  require.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			hostname := args[0]
 
diff --git a/cmd/helm/registry_logout.go b/cmd/helm/registry_logout.go
index 0084f8c09..8d6a54d97 100644
--- a/cmd/helm/registry_logout.go
+++ b/cmd/helm/registry_logout.go
@@ -31,11 +31,10 @@ Remove credentials stored for a remote registry.
 
 func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	return &cobra.Command{
-		Use:               "logout [host]",
-		Short:             "logout from a registry",
-		Long:              registryLogoutDesc,
-		Args:              require.MinimumNArgs(1),
-		ValidArgsFunction: noCompletions,
+		Use:   "logout [host]",
+		Short: "logout from a registry",
+		Long:  registryLogoutDesc,
+		Args:  require.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			hostname := args[0]
 			return action.NewRegistryLogout(cfg).Run(out, hostname)
diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go
index 27661674c..ae09f9195 100644
--- a/cmd/helm/repo_update.go
+++ b/cmd/helm/repo_update.go
@@ -133,8 +133,8 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdate
 	wg.Wait()
 
 	if len(repoFailList) > 0 && failOnRepoUpdateFail {
-		return fmt.Errorf("Failed to update the following repositories: %s",
-			repoFailList)
+		return errors.New(fmt.Sprintf("Failed to update the following repositories: %s",
+			repoFailList))
 	}
 
 	fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈")
diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go
index f794f6bca..34232fcfa 100644
--- a/cmd/helm/search_repo.go
+++ b/cmd/helm/search_repo.go
@@ -326,7 +326,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell
 	cobra.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions), settings.Debug)
 
 	// Now handle completions for url prefixes
-	for _, url := range []string{"oci://\tChart OCI prefix", "https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
+	for _, url := range []string{"https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
 		if strings.HasPrefix(toComplete, url) {
 			// The user already put in the full url prefix; we don't have
 			// anything to add, but make sure the shell does not default
diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go
index c9472dfb4..5e8921f96 100644
--- a/internal/resolver/resolver.go
+++ b/internal/resolver/resolver.go
@@ -135,22 +135,36 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
 			found = false
 		} else {
 			version = d.Version
-			// Retrieve list of tags for repository
-			ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name)
-			tags, err := r.registryClient.Tags(ref)
-			if err != nil {
-				return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository)
-			}
 
-			vs = make(repo.ChartVersions, len(tags))
-			for ti, t := range tags {
-				// Mock chart version objects
-				version := &repo.ChartVersion{
+			// Check to see if an explicit version has been provided
+			_, err := semver.NewVersion(version)
+
+			// Use an explicit version, otherwise search for tags
+			if err == nil {
+				vs = []*repo.ChartVersion{{
 					Metadata: &chart.Metadata{
-						Version: t,
+						Version: version,
 					},
+				}}
+
+			} else {
+				// Retrieve list of tags for repository
+				ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name)
+				tags, err := r.registryClient.Tags(ref)
+				if err != nil {
+					return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository)
+				}
+
+				vs = make(repo.ChartVersions, len(tags))
+				for ti, t := range tags {
+					// Mock chart version objects
+					version := &repo.ChartVersion{
+						Metadata: &chart.Metadata{
+							Version: t,
+						},
+					}
+					vs[ti] = version
 				}
-				vs[ti] = version
 			}
 		}
 
diff --git a/pkg/action/install.go b/pkg/action/install.go
index 3872ed5c9..32be904b4 100644
--- a/pkg/action/install.go
+++ b/pkg/action/install.go
@@ -344,10 +344,8 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
 		return rel, err
 	}
 	rChan := make(chan resultMessage)
-	doneChan := make(chan struct{})
-	defer close(doneChan)
 	go i.performInstall(rChan, rel, toBeAdopted, resources)
-	go i.handleContext(ctx, rChan, doneChan, rel)
+	go i.handleContext(ctx, rChan, rel)
 	result := <-rChan
 	//start preformInstall go routine
 	return result.r, result.e
@@ -418,14 +416,12 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
 
 	i.reportToRun(c, rel, nil)
 }
-func (i *Install) handleContext(ctx context.Context, c chan<- resultMessage, done chan struct{}, rel *release.Release) {
-	select {
-	case <-ctx.Done():
+func (i *Install) handleContext(ctx context.Context, c chan<- resultMessage, rel *release.Release) {
+	go func() {
+		<-ctx.Done()
 		err := ctx.Err()
 		i.reportToRun(c, rel, err)
-	case <-done:
-		return
-	}
+	}()
 }
 func (i *Install) reportToRun(c chan<- resultMessage, rel *release.Release, err error) {
 	i.Lock.Lock()
diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go
index c98b524bc..e228f52dc 100644
--- a/pkg/action/upgrade.go
+++ b/pkg/action/upgrade.go
@@ -326,11 +326,11 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
 	rChan := make(chan resultMessage)
 	ctxChan := make(chan resultMessage)
 	doneChan := make(chan interface{})
-	defer close(doneChan)
 	go u.releasingUpgrade(rChan, upgradedRelease, current, target, originalRelease)
 	go u.handleContext(ctx, doneChan, ctxChan, upgradedRelease)
 	select {
 	case result := <-rChan:
+		doneChan <- true
 		return result.r, result.e
 	case result := <-ctxChan:
 		return result.r, result.e
@@ -351,15 +351,17 @@ func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Re
 
 // Setup listener for SIGINT and SIGTERM
 func (u *Upgrade) handleContext(ctx context.Context, done chan interface{}, c chan<- resultMessage, upgradedRelease *release.Release) {
-	select {
-	case <-ctx.Done():
-		err := ctx.Err()
-
-		// when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens.
-		u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err)
-	case <-done:
-		return
-	}
+	go func() {
+		select {
+		case <-ctx.Done():
+			err := ctx.Err()
+
+			// when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens.
+			u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err)
+		case <-done:
+			return
+		}
+	}()
 }
 func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *release.Release, current kube.ResourceList, target kube.ResourceList, originalRelease *release.Release) {
 	// pre-upgrade hooks
diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go
index 05c090cb6..8c45d533b 100644
--- a/pkg/chartutil/validate_name.go
+++ b/pkg/chartutil/validate_name.go
@@ -40,15 +40,15 @@ var (
 	errMissingName = errors.New("no name provided")
 
 	// errInvalidName indicates that an invalid release name was provided
-	errInvalidName = fmt.Errorf(
+	errInvalidName = errors.New(fmt.Sprintf(
 		"invalid release name, must match regex %s and the length must not be longer than 53",
-		validName.String())
+		validName.String()))
 
 	// errInvalidKubernetesName indicates that the name does not meet the Kubernetes
 	// restrictions on metadata names.
-	errInvalidKubernetesName = fmt.Errorf(
+	errInvalidKubernetesName = errors.New(fmt.Sprintf(
 		"invalid metadata name, must match regex %s and the length must not be longer than 253",
-		validName.String())
+		validName.String()))
 )
 
 const (
diff --git a/pkg/registry/client.go b/pkg/registry/client.go
index 0107136d3..213a9dc49 100644
--- a/pkg/registry/client.go
+++ b/pkg/registry/client.go
@@ -112,13 +112,6 @@ func NewClient(options ...ClientOption) (*Client, error) {
 					return registryauth.EmptyCredential, errors.New("unable to retrieve credentials")
 				}
 
-				// A blank returned username and password value is a bearer token
-				if username == "" && password != "" {
-					return registryauth.Credential{
-						RefreshToken: password,
-					}, nil
-				}
-
 				return registryauth.Credential{
 					Username: username,
 					Password: password,
@@ -303,8 +296,9 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
 
 	numDescriptors := len(descriptors)
 	if numDescriptors < minNumDescriptors {
-		return nil, fmt.Errorf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d",
-			minNumDescriptors, numDescriptors)
+		return nil, errors.New(
+			fmt.Sprintf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d",
+				minNumDescriptors, numDescriptors))
 	}
 	var configDescriptor *ocispec.Descriptor
 	var chartDescriptor *ocispec.Descriptor
@@ -324,19 +318,22 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
 		}
 	}
 	if configDescriptor == nil {
-		return nil, fmt.Errorf("could not load config with mediatype %s", ConfigMediaType)
+		return nil, errors.New(
+			fmt.Sprintf("could not load config with mediatype %s", ConfigMediaType))
 	}
 	if operation.withChart && chartDescriptor == nil {
-		return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s",
-			ChartLayerMediaType)
+		return nil, errors.New(
+			fmt.Sprintf("manifest does not contain a layer with mediatype %s",
+				ChartLayerMediaType))
 	}
 	var provMissing bool
 	if operation.withProv && provDescriptor == nil {
 		if operation.ignoreMissingProv {
 			provMissing = true
 		} else {
-			return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s",
-				ProvLayerMediaType)
+			return nil, errors.New(
+				fmt.Sprintf("manifest does not contain a layer with mediatype %s",
+					ProvLayerMediaType))
 		}
 	}
 	result := &PullResult{
diff --git a/pkg/uploader/chart_uploader.go b/pkg/uploader/chart_uploader.go
index d7e940406..2aa04937a 100644
--- a/pkg/uploader/chart_uploader.go
+++ b/pkg/uploader/chart_uploader.go
@@ -46,7 +46,7 @@ func (c *ChartUploader) UploadTo(ref, remote string) error {
 	}
 
 	if u.Scheme == "" {
-		return fmt.Errorf("scheme prefix missing from remote (e.g. \"%s://\")", registry.OCIScheme)
+		return errors.New(fmt.Sprintf("scheme prefix missing from remote (e.g. \"%s://\")", registry.OCIScheme))
 	}
 
 	p, err := c.Pushers.ByScheme(u.Scheme)


Test patch:
diff --git a/cmd/helm/completion_test.go b/cmd/helm/completion_test.go
index 1143d6445..6d53616d0 100644
--- a/cmd/helm/completion_test.go
+++ b/cmd/helm/completion_test.go
@@ -47,10 +47,10 @@ func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) {
 	}
 	if !strings.Contains(out, "ShellCompDirectiveNoFileComp") != shouldBePerformed {
 		if shouldBePerformed {
-			t.Errorf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
+			t.Error(fmt.Sprintf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName))
 		} else {
 
-			t.Errorf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
+			t.Error(fmt.Sprintf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName))
 		}
 		t.Log(out)
 	}
diff --git a/cmd/helm/push_test.go b/cmd/helm/push_test.go
deleted file mode 100644
index 8e56d99dc..000000000
--- a/cmd/helm/push_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Copyright The Helm Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package main
-
-import (
-	"testing"
-)
-
-func TestPushFileCompletion(t *testing.T) {
-	checkFileCompletion(t, "push", true)
-	checkFileCompletion(t, "push package.tgz", false)
-	checkFileCompletion(t, "push package.tgz oci://localhost:5000", false)
-}
diff --git a/cmd/helm/registry_login_test.go b/cmd/helm/registry_login_test.go
deleted file mode 100644
index 517fe08e1..000000000
--- a/cmd/helm/registry_login_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-Copyright The Helm Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package main
-
-import (
-	"testing"
-)
-
-func TestRegistryLoginFileCompletion(t *testing.T) {
-	checkFileCompletion(t, "registry login", false)
-}
diff --git a/cmd/helm/registry_logout_test.go b/cmd/helm/registry_logout_test.go
deleted file mode 100644
index 31f716725..000000000
--- a/cmd/helm/registry_logout_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-Copyright The Helm Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package main
-
-import (
-	"testing"
-)
-
-func TestRegistryLogoutFileCompletion(t *testing.T) {
-	checkFileCompletion(t, "registry logout", false)
-}
diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go
index 39520d620..3d08d7889 100644
--- a/pkg/action/install_test.go
+++ b/pkg/action/install_test.go
@@ -29,7 +29,6 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 
 	"helm.sh/helm/v3/internal/test"
 	"helm.sh/helm/v3/pkg/chart"
@@ -57,12 +56,9 @@ func installAction(t *testing.T) *Install {
 
 func TestInstallRelease(t *testing.T) {
 	is := assert.New(t)
-	req := require.New(t)
-
 	instAction := installAction(t)
 	vals := map[string]interface{}{}
-	ctx, done := context.WithCancel(context.Background())
-	res, err := instAction.RunWithContext(ctx, buildChart(), vals)
+	res, err := instAction.Run(buildChart(), vals)
 	if err != nil {
 		t.Fatalf("Failed install: %s", err)
 	}
@@ -81,14 +77,6 @@ func TestInstallRelease(t *testing.T) {
 	is.NotEqual(len(rel.Manifest), 0)
 	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
 	is.Equal(rel.Info.Description, "Install complete")
-
-	// Detecting previous bug where context termination after successful release
-	// caused release to fail.
-	done()
-	time.Sleep(time.Millisecond * 100)
-	lastRelease, err := instAction.cfg.Releases.Last(rel.Name)
-	req.NoError(err)
-	is.Equal(lastRelease.Info.Status, release.StatusDeployed)
 }
 
 func TestInstallReleaseWithValues(t *testing.T) {
diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go
index 62922b373..7c286093e 100644
--- a/pkg/action/upgrade_test.go
+++ b/pkg/action/upgrade_test.go
@@ -40,33 +40,6 @@ func upgradeAction(t *testing.T) *Upgrade {
 	return upAction
 }
 
-func TestUpgradeRelease_Success(t *testing.T) {
-	is := assert.New(t)
-	req := require.New(t)
-
-	upAction := upgradeAction(t)
-	rel := releaseStub()
-	rel.Name = "previous-release"
-	rel.Info.Status = release.StatusDeployed
-	req.NoError(upAction.cfg.Releases.Create(rel))
-
-	upAction.Wait = true
-	vals := map[string]interface{}{}
-
-	ctx, done := context.WithCancel(context.Background())
-	res, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals)
-	done()
-	req.NoError(err)
-	is.Equal(res.Info.Status, release.StatusDeployed)
-
-	// Detecting previous bug where context termination after successful release
-	// caused release to fail.
-	time.Sleep(time.Millisecond * 100)
-	lastRelease, err := upAction.cfg.Releases.Last(rel.Name)
-	req.NoError(err)
-	is.Equal(lastRelease.Info.Status, release.StatusDeployed)
-}
-
 func TestUpgradeRelease_Wait(t *testing.T) {
 	is := assert.New(t)
 	req := require.New(t)
diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go
index d2affa79d..1c3dc815e 100644
--- a/pkg/cli/environment_test.go
+++ b/pkg/cli/environment_test.go
@@ -51,9 +51,9 @@ func TestEnvSettings(t *testing.T) {
 		ns, kcontext string
 		debug        bool
 		maxhistory   int
-		kubeAsUser   string
-		kubeAsGroups []string
-		kubeCaFile   string
+		kAsUser      string
+		kAsGroups    []string
+		kCaFile      string
 	}{
 		{
 			name:       "defaults",
@@ -61,35 +61,35 @@ func TestEnvSettings(t *testing.T) {
 			maxhistory: defaultMaxHistory,
 		},
 		{
-			name:         "with flags set",
-			args:         "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/tmp/ca.crt",
-			ns:           "myns",
-			debug:        true,
-			maxhistory:   defaultMaxHistory,
-			kubeAsUser:   "poro",
-			kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
-			kubeCaFile:   "/tmp/ca.crt",
+			name:       "with flags set",
+			args:       "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/tmp/ca.crt",
+			ns:         "myns",
+			debug:      true,
+			maxhistory: defaultMaxHistory,
+			kAsUser:    "poro",
+			kAsGroups:  []string{"admins", "teatime", "snackeaters"},
+			kCaFile:    "/tmp/ca.crt",
 		},
 		{
-			name:         "with envvars set",
-			envvars:      map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
-			ns:           "yourns",
-			maxhistory:   5,
-			debug:        true,
-			kubeAsUser:   "pikachu",
-			kubeAsGroups: []string{"operators", "snackeaters", "partyanimals"},
-			kubeCaFile:   "/tmp/ca.crt",
+			name:       "with envvars set",
+			envvars:    map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
+			ns:         "yourns",
+			maxhistory: 5,
+			debug:      true,
+			kAsUser:    "pikachu",
+			kAsGroups:  []string{"operators", "snackeaters", "partyanimals"},
+			kCaFile:    "/tmp/ca.crt",
 		},
 		{
-			name:         "with flags and envvars set",
-			args:         "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/my/ca.crt",
-			envvars:      map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
-			ns:           "myns",
-			debug:        true,
-			maxhistory:   5,
-			kubeAsUser:   "poro",
-			kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
-			kubeCaFile:   "/my/ca.crt",
+			name:       "with flags and envvars set",
+			args:       "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/my/ca.crt",
+			envvars:    map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
+			ns:         "myns",
+			debug:      true,
+			maxhistory: 5,
+			kAsUser:    "poro",
+			kAsGroups:  []string{"admins", "teatime", "snackeaters"},
+			kCaFile:    "/my/ca.crt",
 		},
 	}
 
@@ -119,14 +119,14 @@ func TestEnvSettings(t *testing.T) {
 			if settings.MaxHistory != tt.maxhistory {
 				t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory)
 			}
-			if tt.kubeAsUser != settings.KubeAsUser {
-				t.Errorf("expected kAsUser %q, got %q", tt.kubeAsUser, settings.KubeAsUser)
+			if tt.kAsUser != settings.KubeAsUser {
+				t.Errorf("expected kAsUser %q, got %q", tt.kAsUser, settings.KubeAsUser)
 			}
-			if !reflect.DeepEqual(tt.kubeAsGroups, settings.KubeAsGroups) {
-				t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kubeAsGroups), len(settings.KubeAsGroups))
+			if !reflect.DeepEqual(tt.kAsGroups, settings.KubeAsGroups) {
+				t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kAsGroups), len(settings.KubeAsGroups))
 			}
-			if tt.kubeCaFile != settings.KubeCaFile {
-				t.Errorf("expected kCaFile %q, got %q", tt.kubeCaFile, settings.KubeCaFile)
+			if tt.kCaFile != settings.KubeCaFile {
+				t.Errorf("expected kCaFile %q, got %q", tt.kCaFile, settings.KubeCaFile)
 			}
 		})
 	}