Repository: helm/helm (go)

Issue:
helm repo pull does not use CA File
Output of `helm version`:

version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"84de17e7e5255e5033967f005d4b4c05c024317c", GitTreeState:"dirty", GoVersion:"go1.13.4"}

Also applies for the release:
version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}

Output of `kubectl version`:
N/A

Cloud Provider/Platform (AKS, GKE, Minikube etc.): 
N/A


We have a custom helm chart repo on a self-signed certificate. Pulling does not work unless using absolute URLs.

```
$ helm pull example/chart
Error: Get https://servername/repository/helm/chart-0.0.1.tgz: x509: certificate signed by unknown authority
```

Even passing `--cert-file` on the command line does not help. (the ca cert file is in the repo config, so should be picked up from there)

I have a patch locally that address the issue with not looking at the repo config. Will look into the issue with the command flag not being used either, too.
Will push a PR as soon as I've cleaned it up a bit.


Pull request:
Fixes #7055 helm pull ca file usage
Added test case for chart_download to verify http TLS options.

Did not find any tests for the cli pull action, but fixed it in place without test coverage.


Patch:
diff --git a/pkg/action/pull.go b/pkg/action/pull.go
index aaf63861e..b0a3d2598 100644
--- a/pkg/action/pull.go
+++ b/pkg/action/pull.go
@@ -63,6 +63,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
 		Getters: getter.All(p.Settings),
 		Options: []getter.Option{
 			getter.WithBasicAuth(p.Username, p.Password),
+			getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
 		},
 		RepositoryConfig: p.Settings.RepositoryConfig,
 		RepositoryCache:  p.Settings.RepositoryCache,
diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go
index 8e251bc89..f3d4321c5 100644
--- a/pkg/downloader/chart_downloader.go
+++ b/pkg/downloader/chart_downloader.go
@@ -214,6 +214,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 		c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password))
 	}
 
+	if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" {
+		c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile))
+	}
+
 	// Next, we need to load the index, and actually look up the chart.
 	idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
 	i, err := repo.LoadIndexFile(idxFile)


Test patch:
diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go
index 80249e240..e0692c8c8 100644
--- a/pkg/downloader/chart_downloader_test.go
+++ b/pkg/downloader/chart_downloader_test.go
@@ -80,6 +80,67 @@ func TestResolveChartRef(t *testing.T) {
 	}
 }
 
+func TestResolveChartOpts(t *testing.T) {
+	tests := []struct {
+		name, ref, version string
+		expect             []getter.Option
+	}{
+		{
+			name: "repo with CA-file",
+			ref:  "testing-ca-file/foo",
+			expect: []getter.Option{
+				getter.WithURL("https://example.com/foo-1.2.3.tgz"),
+				getter.WithTLSClientConfig("cert", "key", "ca"),
+			},
+		},
+	}
+
+	c := ChartDownloader{
+		Out:              os.Stderr,
+		RepositoryConfig: repoConfig,
+		RepositoryCache:  repoCache,
+		Getters: getter.All(&cli.EnvSettings{
+			RepositoryConfig: repoConfig,
+			RepositoryCache:  repoCache,
+		}),
+	}
+
+	// snapshot options
+	snapshotOpts := c.Options
+
+	for _, tt := range tests {
+		// reset chart downloader options for each test case
+		c.Options = snapshotOpts
+
+		expect, err := getter.NewHTTPGetter(tt.expect...)
+		if err != nil {
+			t.Errorf("%s: failed to setup http client: %s", tt.name, err)
+			continue
+		}
+
+		u, err := c.ResolveChartVersion(tt.ref, tt.version)
+		if err != nil {
+			t.Errorf("%s: failed with error %s", tt.name, err)
+			continue
+		}
+
+		got, err := getter.NewHTTPGetter(
+			append(
+				c.Options,
+				getter.WithURL(u.String()),
+			)...,
+		)
+		if err != nil {
+			t.Errorf("%s: failed to create http client: %s", tt.name, err)
+			continue
+		}
+
+		if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) {
+			t.Errorf("%s: expected %s, got %s", tt.name, expect, got)
+		}
+	}
+}
+
 func TestVerifyChart(t *testing.T) {
 	v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
 	if err != nil {
diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml
index 374d95c8a..430865269 100644
--- a/pkg/downloader/testdata/repositories.yaml
+++ b/pkg/downloader/testdata/repositories.yaml
@@ -15,4 +15,9 @@ repositories:
   - name: testing-relative
     url: "http://example.com/helm"
   - name: testing-relative-trailing-slash
-    url: "http://example.com/helm/"
\ No newline at end of file
+    url: "http://example.com/helm/"
+  - name: testing-ca-file
+    url: "https://example.com"
+    certFile: "cert"
+    keyFile: "key"
+    caFile: "ca"
diff --git a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
new file mode 100644
index 000000000..17cdde1c6
--- /dev/null
+++ b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+entries:
+  foo:
+    - name: foo
+      description: Foo Chart
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - https://example.com/foo-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d