github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/downloader/chart_downloader_test.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package downloader
    17  
    18  import (
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/stefanmcshane/helm/internal/test/ensure"
    24  	"github.com/stefanmcshane/helm/pkg/cli"
    25  	"github.com/stefanmcshane/helm/pkg/getter"
    26  	"github.com/stefanmcshane/helm/pkg/repo"
    27  	"github.com/stefanmcshane/helm/pkg/repo/repotest"
    28  )
    29  
    30  const (
    31  	repoConfig = "testdata/repositories.yaml"
    32  	repoCache  = "testdata/repository"
    33  )
    34  
    35  func TestResolveChartRef(t *testing.T) {
    36  	tests := []struct {
    37  		name, ref, expect, version string
    38  		fail                       bool
    39  	}{
    40  		{name: "full URL", ref: "http://example.com/foo-1.2.3.tgz", expect: "http://example.com/foo-1.2.3.tgz"},
    41  		{name: "full URL, HTTPS", ref: "https://example.com/foo-1.2.3.tgz", expect: "https://example.com/foo-1.2.3.tgz"},
    42  		{name: "full URL, with authentication", ref: "http://username:password@example.com/foo-1.2.3.tgz", expect: "http://username:password@example.com/foo-1.2.3.tgz"},
    43  		{name: "reference, testing repo", ref: "testing/alpine", expect: "http://example.com/alpine-1.2.3.tgz"},
    44  		{name: "reference, version, testing repo", ref: "testing/alpine", version: "0.2.0", expect: "http://example.com/alpine-0.2.0.tgz"},
    45  		{name: "reference, version, malformed repo", ref: "malformed/alpine", version: "1.2.3", expect: "http://dl.example.com/alpine-1.2.3.tgz"},
    46  		{name: "reference, querystring repo", ref: "testing-querystring/alpine", expect: "http://example.com/alpine-1.2.3.tgz?key=value"},
    47  		{name: "reference, testing-relative repo", ref: "testing-relative/foo", expect: "http://example.com/helm/charts/foo-1.2.3.tgz"},
    48  		{name: "reference, testing-relative repo", ref: "testing-relative/bar", expect: "http://example.com/helm/bar-1.2.3.tgz"},
    49  		{name: "reference, testing-relative-trailing-slash repo", ref: "testing-relative-trailing-slash/foo", expect: "http://example.com/helm/charts/foo-1.2.3.tgz"},
    50  		{name: "reference, testing-relative-trailing-slash repo", ref: "testing-relative-trailing-slash/bar", expect: "http://example.com/helm/bar-1.2.3.tgz"},
    51  		{name: "encoded URL", ref: "encoded-url/foobar", expect: "http://example.com/with%2Fslash/charts/foobar-4.2.1.tgz"},
    52  		{name: "full URL, HTTPS, irrelevant version", ref: "https://example.com/foo-1.2.3.tgz", version: "0.1.0", expect: "https://example.com/foo-1.2.3.tgz", fail: true},
    53  		{name: "full URL, file", ref: "file:///foo-1.2.3.tgz", fail: true},
    54  		{name: "invalid", ref: "invalid-1.2.3", fail: true},
    55  		{name: "not found", ref: "nosuchthing/invalid-1.2.3", fail: true},
    56  	}
    57  
    58  	c := ChartDownloader{
    59  		Out:              os.Stderr,
    60  		RepositoryConfig: repoConfig,
    61  		RepositoryCache:  repoCache,
    62  		Getters: getter.All(&cli.EnvSettings{
    63  			RepositoryConfig: repoConfig,
    64  			RepositoryCache:  repoCache,
    65  		}),
    66  	}
    67  
    68  	for _, tt := range tests {
    69  		u, err := c.ResolveChartVersion(tt.ref, tt.version)
    70  		if err != nil {
    71  			if tt.fail {
    72  				continue
    73  			}
    74  			t.Errorf("%s: failed with error %q", tt.name, err)
    75  			continue
    76  		}
    77  		if got := u.String(); got != tt.expect {
    78  			t.Errorf("%s: expected %s, got %s", tt.name, tt.expect, got)
    79  		}
    80  	}
    81  }
    82  
    83  func TestResolveChartOpts(t *testing.T) {
    84  	tests := []struct {
    85  		name, ref, version string
    86  		expect             []getter.Option
    87  	}{
    88  		{
    89  			name: "repo with CA-file",
    90  			ref:  "testing-ca-file/foo",
    91  			expect: []getter.Option{
    92  				getter.WithURL("https://example.com/foo-1.2.3.tgz"),
    93  				getter.WithTLSClientConfig("cert", "key", "ca"),
    94  			},
    95  		},
    96  	}
    97  
    98  	c := ChartDownloader{
    99  		Out:              os.Stderr,
   100  		RepositoryConfig: repoConfig,
   101  		RepositoryCache:  repoCache,
   102  		Getters: getter.All(&cli.EnvSettings{
   103  			RepositoryConfig: repoConfig,
   104  			RepositoryCache:  repoCache,
   105  		}),
   106  	}
   107  
   108  	// snapshot options
   109  	snapshotOpts := c.Options
   110  
   111  	for _, tt := range tests {
   112  		// reset chart downloader options for each test case
   113  		c.Options = snapshotOpts
   114  
   115  		expect, err := getter.NewHTTPGetter(tt.expect...)
   116  		if err != nil {
   117  			t.Errorf("%s: failed to setup http client: %s", tt.name, err)
   118  			continue
   119  		}
   120  
   121  		u, err := c.ResolveChartVersion(tt.ref, tt.version)
   122  		if err != nil {
   123  			t.Errorf("%s: failed with error %s", tt.name, err)
   124  			continue
   125  		}
   126  
   127  		got, err := getter.NewHTTPGetter(
   128  			append(
   129  				c.Options,
   130  				getter.WithURL(u.String()),
   131  			)...,
   132  		)
   133  		if err != nil {
   134  			t.Errorf("%s: failed to create http client: %s", tt.name, err)
   135  			continue
   136  		}
   137  
   138  		if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) {
   139  			t.Errorf("%s: expected %s, got %s", tt.name, expect, got)
   140  		}
   141  	}
   142  }
   143  
   144  func TestVerifyChart(t *testing.T) {
   145  	v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	// The verification is tested at length in the provenance package. Here,
   150  	// we just want a quick sanity check that the v is not empty.
   151  	if len(v.FileHash) == 0 {
   152  		t.Error("Digest missing")
   153  	}
   154  }
   155  
   156  func TestIsTar(t *testing.T) {
   157  	tests := map[string]bool{
   158  		"foo.tgz":           true,
   159  		"foo/bar/baz.tgz":   true,
   160  		"foo-1.2.3.4.5.tgz": true,
   161  		"foo.tar.gz":        false, // for our purposes
   162  		"foo.tgz.1":         false,
   163  		"footgz":            false,
   164  	}
   165  
   166  	for src, expect := range tests {
   167  		if isTar(src) != expect {
   168  			t.Errorf("%q should be %t", src, expect)
   169  		}
   170  	}
   171  }
   172  
   173  func TestDownloadTo(t *testing.T) {
   174  	srv := repotest.NewTempServerWithCleanupAndBasicAuth(t, "testdata/*.tgz*")
   175  	defer srv.Stop()
   176  	if err := srv.CreateIndex(); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	if err := srv.LinkIndices(); err != nil {
   181  		t.Fatal(err)
   182  	}
   183  
   184  	c := ChartDownloader{
   185  		Out:              os.Stderr,
   186  		Verify:           VerifyAlways,
   187  		Keyring:          "testdata/helm-test-key.pub",
   188  		RepositoryConfig: repoConfig,
   189  		RepositoryCache:  repoCache,
   190  		Getters: getter.All(&cli.EnvSettings{
   191  			RepositoryConfig: repoConfig,
   192  			RepositoryCache:  repoCache,
   193  		}),
   194  		Options: []getter.Option{
   195  			getter.WithBasicAuth("username", "password"),
   196  			getter.WithPassCredentialsAll(false),
   197  		},
   198  	}
   199  	cname := "/signtest-0.1.0.tgz"
   200  	dest := srv.Root()
   201  	where, v, err := c.DownloadTo(srv.URL()+cname, "", dest)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  
   206  	if expect := filepath.Join(dest, cname); where != expect {
   207  		t.Errorf("Expected download to %s, got %s", expect, where)
   208  	}
   209  
   210  	if v.FileHash == "" {
   211  		t.Error("File hash was empty, but verification is required.")
   212  	}
   213  
   214  	if _, err := os.Stat(filepath.Join(dest, cname)); err != nil {
   215  		t.Error(err)
   216  	}
   217  }
   218  
   219  func TestDownloadTo_TLS(t *testing.T) {
   220  	// Set up mock server w/ tls enabled
   221  	srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*")
   222  	srv.Stop()
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	srv.StartTLS()
   227  	defer srv.Stop()
   228  	if err := srv.CreateIndex(); err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	if err := srv.LinkIndices(); err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	repoConfig := filepath.Join(srv.Root(), "repositories.yaml")
   236  	repoCache := srv.Root()
   237  
   238  	c := ChartDownloader{
   239  		Out:              os.Stderr,
   240  		Verify:           VerifyAlways,
   241  		Keyring:          "testdata/helm-test-key.pub",
   242  		RepositoryConfig: repoConfig,
   243  		RepositoryCache:  repoCache,
   244  		Getters: getter.All(&cli.EnvSettings{
   245  			RepositoryConfig: repoConfig,
   246  			RepositoryCache:  repoCache,
   247  		}),
   248  		Options: []getter.Option{},
   249  	}
   250  	cname := "test/signtest"
   251  	dest := srv.Root()
   252  	where, v, err := c.DownloadTo(cname, "", dest)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  
   257  	target := filepath.Join(dest, "signtest-0.1.0.tgz")
   258  	if expect := target; where != expect {
   259  		t.Errorf("Expected download to %s, got %s", expect, where)
   260  	}
   261  
   262  	if v.FileHash == "" {
   263  		t.Error("File hash was empty, but verification is required.")
   264  	}
   265  
   266  	if _, err := os.Stat(target); err != nil {
   267  		t.Error(err)
   268  	}
   269  }
   270  
   271  func TestDownloadTo_VerifyLater(t *testing.T) {
   272  	defer ensure.HelmHome(t)()
   273  
   274  	dest := ensure.TempDir(t)
   275  	defer os.RemoveAll(dest)
   276  
   277  	// Set up a fake repo
   278  	srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	defer srv.Stop()
   283  	if err := srv.LinkIndices(); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  
   287  	c := ChartDownloader{
   288  		Out:              os.Stderr,
   289  		Verify:           VerifyLater,
   290  		RepositoryConfig: repoConfig,
   291  		RepositoryCache:  repoCache,
   292  		Getters: getter.All(&cli.EnvSettings{
   293  			RepositoryConfig: repoConfig,
   294  			RepositoryCache:  repoCache,
   295  		}),
   296  	}
   297  	cname := "/signtest-0.1.0.tgz"
   298  	where, _, err := c.DownloadTo(srv.URL()+cname, "", dest)
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  
   303  	if expect := filepath.Join(dest, cname); where != expect {
   304  		t.Errorf("Expected download to %s, got %s", expect, where)
   305  	}
   306  
   307  	if _, err := os.Stat(filepath.Join(dest, cname)); err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	if _, err := os.Stat(filepath.Join(dest, cname+".prov")); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  }
   314  
   315  func TestScanReposForURL(t *testing.T) {
   316  	c := ChartDownloader{
   317  		Out:              os.Stderr,
   318  		Verify:           VerifyLater,
   319  		RepositoryConfig: repoConfig,
   320  		RepositoryCache:  repoCache,
   321  		Getters: getter.All(&cli.EnvSettings{
   322  			RepositoryConfig: repoConfig,
   323  			RepositoryCache:  repoCache,
   324  		}),
   325  	}
   326  
   327  	u := "http://example.com/alpine-0.2.0.tgz"
   328  	rf, err := repo.LoadFile(repoConfig)
   329  	if err != nil {
   330  		t.Fatal(err)
   331  	}
   332  
   333  	entry, err := c.scanReposForURL(u, rf)
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  
   338  	if entry.Name != "testing" {
   339  		t.Errorf("Unexpected repo %q for URL %q", entry.Name, u)
   340  	}
   341  
   342  	// A lookup failure should produce an ErrNoOwnerRepo
   343  	u = "https://no.such.repo/foo/bar-1.23.4.tgz"
   344  	if _, err = c.scanReposForURL(u, rf); err != ErrNoOwnerRepo {
   345  		t.Fatalf("expected ErrNoOwnerRepo, got %v", err)
   346  	}
   347  }