Repository: helm/helm (go)

Issue:
helm3: repo add no longer interactively asks for password 
Output of `helm version`: version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}


With helm v2, passing --username myname to helm repo add caused helm to interactively ask for a password which is positive for security reasons (not having a password show up in process lists and shell histories, the password being shadowed while entering).

helm v3 seems to have lost this ability, which looks like a regression to me.


Pull request:
feat(helm): prompt for password with helm repo add
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Make sure to read the Contributing Guide before submitting your PR: https://github.com/helm/helm/blob/master/CONTRIBUTING.md
2. If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.-->

**What this PR does / why we need it**:
Closes #7174

Previously in Helm 2, `helm repo add` would prompt for your password if you only provided the `--username` flag. This helps prevent someone's credentials from being logged in their shell's history, from showing up in process lists, and from being seen by others nearby.

**Special notes for your reviewer**:
This is my first time contributing to a large open-source project, please let me know if anything else is needed or desired and I'll do my best to address the concerns as soon as possible.

**If applicable**:
- [ ] this PR contains documentation
- [ ] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility


Patch:
diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go
index 9c67641e5..2fc697ff0 100644
--- a/cmd/helm/repo_add.go
+++ b/cmd/helm/repo_add.go
@@ -24,11 +24,13 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
 	"time"
 
 	"github.com/gofrs/flock"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
+	"golang.org/x/crypto/ssh/terminal"
 	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v3/cmd/helm/require"
@@ -114,6 +116,16 @@ func (o *repoAddOptions) run(out io.Writer) error {
 		return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
 	}
 
+	if o.username != "" && o.password == "" {
+		fmt.Fprint(out, "Password: ")
+		password, err := terminal.ReadPassword(syscall.Stdin)
+		fmt.Fprintln(out)
+		if err != nil {
+			return err
+		}
+		o.password = string(password)
+	}
+
 	c := repo.Entry{
 		Name:                  o.name,
 		URL:                   o.url,
diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go
index 67e76bd3d..223ead75a 100644
--- a/pkg/lint/lint.go
+++ b/pkg/lint/lint.go
@@ -32,6 +32,5 @@ func All(basedir string, values map[string]interface{}, namespace string, strict
 	rules.Chartfile(&linter)
 	rules.ValuesWithOverrides(&linter, values)
 	rules.Templates(&linter, values, namespace, strict)
-	rules.Dependencies(&linter)
 	return linter
 }
diff --git a/pkg/lint/rules/dependencies.go b/pkg/lint/rules/dependencies.go
deleted file mode 100644
index abecd1feb..000000000
--- a/pkg/lint/rules/dependencies.go
+++ /dev/null
@@ -1,82 +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 rules // import "helm.sh/helm/v3/pkg/lint/rules"
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/pkg/errors"
-
-	"helm.sh/helm/v3/pkg/chart"
-	"helm.sh/helm/v3/pkg/chart/loader"
-	"helm.sh/helm/v3/pkg/lint/support"
-)
-
-// Dependencies runs lints against a chart's dependencies
-//
-// See https://github.com/helm/helm/issues/7910
-func Dependencies(linter *support.Linter) {
-	c, err := loader.LoadDir(linter.ChartDir)
-	if !linter.RunLinterRule(support.ErrorSev, "", validateChartFormat(err)) {
-		return
-	}
-
-	linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependencyInMetadata(c))
-	linter.RunLinterRule(support.WarningSev, linter.ChartDir, validateDependencyInChartsDir(c))
-}
-
-func validateChartFormat(chartError error) error {
-	if chartError != nil {
-		return errors.Errorf("unable to load chart\n\t%s", chartError)
-	}
-	return nil
-}
-
-func validateDependencyInChartsDir(c *chart.Chart) (err error) {
-	dependencies := map[string]struct{}{}
-	missing := []string{}
-	for _, dep := range c.Dependencies() {
-		dependencies[dep.Metadata.Name] = struct{}{}
-	}
-	for _, dep := range c.Metadata.Dependencies {
-		if _, ok := dependencies[dep.Name]; !ok {
-			missing = append(missing, dep.Name)
-		}
-	}
-	if len(missing) > 0 {
-		err = fmt.Errorf("chart directory is missing these dependencies: %s", strings.Join(missing, ","))
-	}
-	return err
-}
-
-func validateDependencyInMetadata(c *chart.Chart) (err error) {
-	dependencies := map[string]struct{}{}
-	missing := []string{}
-	for _, dep := range c.Metadata.Dependencies {
-		dependencies[dep.Name] = struct{}{}
-	}
-	for _, dep := range c.Dependencies() {
-		if _, ok := dependencies[dep.Metadata.Name]; !ok {
-			missing = append(missing, dep.Metadata.Name)
-		}
-	}
-	if len(missing) > 0 {
-		err = fmt.Errorf("chart metadata is missing these dependencies: %s", strings.Join(missing, ","))
-	}
-	return err
-}


Test patch:
diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go
index 9079935d4..7638acb84 100644
--- a/cmd/helm/lint_test.go
+++ b/cmd/helm/lint_test.go
@@ -27,7 +27,7 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) {
 		name:      "lint good chart with bad subcharts",
 		cmd:       fmt.Sprintf("lint %s", testChart),
 		golden:    "output/lint-chart-with-bad-subcharts.txt",
-		wantError: true,
+		wantError: false,
 	}, {
 		name:      "lint good chart with bad subcharts using --with-subcharts flag",
 		cmd:       fmt.Sprintf("lint --with-subcharts %s", testChart),
diff --git a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt
index e77aa387f..cda011b57 100644
--- a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt
+++ b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt
@@ -1,8 +1,6 @@
 ==> Linting testdata/testcharts/chart-with-bad-subcharts
 [INFO] Chart.yaml: icon is recommended
 [WARNING] templates/: directory not found
-[ERROR] : unable to load chart
-	error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
 
 ==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart
 [ERROR] Chart.yaml: name is required
@@ -10,11 +8,9 @@
 [ERROR] Chart.yaml: version is required
 [INFO] Chart.yaml: icon is recommended
 [WARNING] templates/: directory not found
-[ERROR] : unable to load chart
-	validation: chart.metadata.name is required
 
 ==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/good-subchart
 [INFO] Chart.yaml: icon is recommended
 [WARNING] templates/: directory not found
 
-Error: 3 chart(s) linted, 2 chart(s) failed
+Error: 3 chart(s) linted, 1 chart(s) failed
diff --git a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt
index 265e555f7..51f3f3718 100644
--- a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt
+++ b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt
@@ -1,7 +1,5 @@
 ==> Linting testdata/testcharts/chart-with-bad-subcharts
 [INFO] Chart.yaml: icon is recommended
 [WARNING] templates/: directory not found
-[ERROR] : unable to load chart
-	error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
 
-Error: 1 chart(s) linted, 1 chart(s) failed
+1 chart(s) linted, 0 chart(s) failed
diff --git a/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml
index a6754b24f..0daa5c188 100644
--- a/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml
+++ b/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml
@@ -1,1 +1,1 @@
-description: Bad subchart
+description: Bad subchart
\ No newline at end of file
diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go
index 3a45b2992..306c13cee 100644
--- a/pkg/chartutil/save_test.go
+++ b/pkg/chartutil/save_test.go
@@ -30,13 +30,15 @@ import (
 	"testing"
 	"time"
 
-	"helm.sh/helm/v3/internal/test/ensure"
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chart/loader"
 )
 
 func TestSave(t *testing.T) {
-	tmp := ensure.TempDir(t)
+	tmp, err := ioutil.TempDir("", "helm-")
+	if err != nil {
+		t.Fatal(err)
+	}
 	defer os.RemoveAll(tmp)
 
 	for _, dest := range []string{tmp, path.Join(tmp, "newdir")} {
diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go
index 29ed67026..e7ff4cd7a 100644
--- a/pkg/lint/lint_test.go
+++ b/pkg/lint/lint_test.go
@@ -38,12 +38,12 @@ const goodChartDir = "rules/testdata/goodone"
 
 func TestBadChart(t *testing.T) {
 	m := All(badChartDir, values, namespace, strict).Messages
-	if len(m) != 8 {
+	if len(m) != 7 {
 		t.Errorf("Number of errors %v", len(m))
 		t.Errorf("All didn't fail with expected errors, got %#v", m)
 	}
-	// There should be one INFO, 2 WARNINGs and 2 ERROR messages, check for them
-	var i, w, e, e2, e3, e4, e5, e6 bool
+	// There should be one INFO, 2 WARNINGs and one ERROR messages, check for them
+	var i, w, e, e2, e3, e4, e5 bool
 	for _, msg := range m {
 		if msg.Severity == support.InfoSev {
 			if strings.Contains(msg.Err.Error(), "icon is recommended") {
@@ -74,13 +74,9 @@ func TestBadChart(t *testing.T) {
 			if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
 				e5 = true
 			}
-			// This comes from the dependency check, which loads dependency info from the Chart.yaml
-			if strings.Contains(msg.Err.Error(), "unable to load chart") {
-				e6 = true
-			}
 		}
 	}
-	if !e || !e2 || !e3 || !e4 || !e5 || !w || !i || !e6 {
+	if !e || !e2 || !e3 || !e4 || !e5 || !w || !i {
 		t.Errorf("Didn't find all the expected errors, got %#v", m)
 	}
 }
diff --git a/pkg/lint/rules/dependencies_test.go b/pkg/lint/rules/dependencies_test.go
deleted file mode 100644
index 075190eac..000000000
--- a/pkg/lint/rules/dependencies_test.go
+++ /dev/null
@@ -1,99 +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 rules
-
-import (
-	"os"
-	"path/filepath"
-	"testing"
-
-	"helm.sh/helm/v3/internal/test/ensure"
-	"helm.sh/helm/v3/pkg/chart"
-	"helm.sh/helm/v3/pkg/chartutil"
-	"helm.sh/helm/v3/pkg/lint/support"
-)
-
-func chartWithBadDependencies() chart.Chart {
-	badChartDeps := chart.Chart{
-		Metadata: &chart.Metadata{
-			Name:       "badchart",
-			Version:    "0.1.0",
-			APIVersion: "v2",
-			Dependencies: []*chart.Dependency{
-				{
-					Name: "sub2",
-				},
-				{
-					Name: "sub3",
-				},
-			},
-		},
-	}
-
-	badChartDeps.SetDependencies(
-		&chart.Chart{
-			Metadata: &chart.Metadata{
-				Name:       "sub1",
-				Version:    "0.1.0",
-				APIVersion: "v2",
-			},
-		},
-		&chart.Chart{
-			Metadata: &chart.Metadata{
-				Name:       "sub2",
-				Version:    "0.1.0",
-				APIVersion: "v2",
-			},
-		},
-	)
-	return badChartDeps
-}
-
-func TestValidateDependencyInChartsDir(t *testing.T) {
-	c := chartWithBadDependencies()
-
-	if err := validateDependencyInChartsDir(&c); err == nil {
-		t.Error("chart should have been flagged for missing deps in chart directory")
-	}
-}
-
-func TestValidateDependencyInMetadata(t *testing.T) {
-	c := chartWithBadDependencies()
-
-	if err := validateDependencyInMetadata(&c); err == nil {
-		t.Errorf("chart should have been flagged for missing deps in chart metadata")
-	}
-}
-
-func TestDependencies(t *testing.T) {
-	tmp := ensure.TempDir(t)
-	defer os.RemoveAll(tmp)
-
-	c := chartWithBadDependencies()
-	err := chartutil.SaveDir(&c, tmp)
-	if err != nil {
-		t.Fatal(err)
-	}
-	linter := support.Linter{ChartDir: filepath.Join(tmp, c.Metadata.Name)}
-
-	Dependencies(&linter)
-	if l := len(linter.Messages); l != 2 {
-		t.Errorf("expected 2 linter errors for bad chart dependencies. Got %d.", l)
-		for i, msg := range linter.Messages {
-			t.Logf("Message: %d, Error: %#v", i, msg)
-		}
-	}
-}