Repository: helm/helm (go)

Issue:
helm repo add now fails in v3.3.2 and v3.3.3
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->

### Description

The command **`helm repo add <name> <url>`** fails as it is no longer idempotent.  This worked fine in version `v3.3.1` and earlier.  It now fails in `v3.3.2` and `v3.3.3`.  This is a breaking change that has broken automation relying on `helm` and tools `helmfile`.

### Steps  to Reproduce

```bash 
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
```

### Expected Results

```
"jaegertracing" has been added to your repositories
```

I expected that the command would be idempotent and would not fail.  As the repository was already added, it would not need to be added again, and thus should to exist with an error.

### Actual Results

Despite the repository already in the desired state, helm exits with an error, even though there is no error.  `helm` will exit with `1` indicated an error.  This destroys any automation scripts and tools like `helmfile` to function correctly.

```
Error: repository name (jaegertracing) already exists, please specify a different name
```


Output of `helm version`:
```
version.BuildInfo{Version:"v3.3.3", GitCommit:"55e3ca022e40fe200fbc855938995f40b2a68ce0", GitTreeState:"dirty", GoVersion:"go1.15.2"}
```

Output of `kubectl version`:

```
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.6", GitCommit:"dff82dc0de47299ab66c83c626e08b245ab19037", GitTreeState:"clean", BuildDate:"2020-07-15T23:30:39Z", GoVersion:"go1.14.4", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"17+", GitVersion:"v1.17.9-eks-4c6976", GitCommit:"4c6976793196d70bc5cd29d56ce5440c9473648e", GitTreeState:"clean", BuildDate:"2020-07-17T18:46:04Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
```

Cloud Provider/Platform (AKS, GKE, Minikube etc.): 

* EKS (K8S `v1.17.9-eks-4c6976`)


Pull request:
Fixing issue with idempotent repo add
A security issue fixed in 3.3.2 caught repos with the same name
being added a second time and produced an error. This caused an
issue for tools, such as helmfile, that will add the same name
with the same configuration multiple times.

This fix checks that the configuration on the existing and new
repo are the same. If there is no change it notes it and exists
with a 0 exit code. If there is a change the existing error is
returned (for reverse compat). If --force-update is given the
user opts in to changing the config for the name.

Closes #8771

- [ ] this PR contains documentation
- [x] this PR contains unit tests
- [x] this PR has been tested for backwards compatibility


Patch:
diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go
index 1c2162bfa..f79c213c0 100644
--- a/cmd/helm/repo_add.go
+++ b/cmd/helm/repo_add.go
@@ -116,11 +116,6 @@ func (o *repoAddOptions) run(out io.Writer) error {
 		return err
 	}
 
-	// If the repo exists and --force-update was not specified, error out.
-	if !o.forceUpdate && f.Has(o.name) {
-		return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
-	}
-
 	if o.username != "" && o.password == "" {
 		fd := int(os.Stdin.Fd())
 		fmt.Fprint(out, "Password: ")
@@ -143,6 +138,23 @@ func (o *repoAddOptions) run(out io.Writer) error {
 		InsecureSkipTLSverify: o.insecureSkipTLSverify,
 	}
 
+	// If the repo exists do one of two things:
+	// 1. If the configuration for the name is the same continue without error
+	// 2. When the config is different require --force-update
+	if !o.forceUpdate && f.Has(o.name) {
+		existing := f.Get(o.name)
+		if c != *existing {
+
+			// The input coming in for the name is different from what is already
+			// configured. Return an error.
+			return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
+		}
+
+		// The add is idempotent so do nothing
+		fmt.Fprintf(out, "%q already exists with the same configuration, skipping\n", o.name)
+		return nil
+	}
+
 	r, err := repo.NewChartRepository(&c, getter.All(settings))
 	if err != nil {
 		return err


Test patch:
diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go
index d358ad970..f3bc54985 100644
--- a/cmd/helm/repo_add_test.go
+++ b/cmd/helm/repo_add_test.go
@@ -40,14 +40,38 @@ func TestRepoAddCmd(t *testing.T) {
 	}
 	defer srv.Stop()
 
+	// A second test server is setup to verify URL changing
+	srv2, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer srv2.Stop()
+
 	tmpdir := ensure.TempDir(t)
 	repoFile := filepath.Join(tmpdir, "repositories.yaml")
 
-	tests := []cmdTestCase{{
-		name:   "add a repository",
-		cmd:    fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir),
-		golden: "output/repo-add.txt",
-	}}
+	tests := []cmdTestCase{
+		{
+			name:   "add a repository",
+			cmd:    fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir),
+			golden: "output/repo-add.txt",
+		},
+		{
+			name:   "add repository second time",
+			cmd:    fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir),
+			golden: "output/repo-add2.txt",
+		},
+		{
+			name:      "add repository different url",
+			cmd:       fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv2.URL(), repoFile, tmpdir),
+			wantError: true,
+		},
+		{
+			name:   "add repository second time",
+			cmd:    fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s --force-update", srv2.URL(), repoFile, tmpdir),
+			golden: "output/repo-add.txt",
+		},
+	}
 
 	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/testdata/output/repo-add2.txt b/cmd/helm/testdata/output/repo-add2.txt
new file mode 100644
index 000000000..263ffa9e4
--- /dev/null
+++ b/cmd/helm/testdata/output/repo-add2.txt
@@ -0,0 +1,1 @@
+"test-name" already exists with the same configuration, skipping