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 }