github.com/koderover/helm@v2.17.0+incompatible/pkg/repo/index_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package repo 18 19 import ( 20 "io/ioutil" 21 "net/http" 22 "os" 23 "path/filepath" 24 "strings" 25 "testing" 26 27 "k8s.io/helm/pkg/getter" 28 "k8s.io/helm/pkg/helm/environment" 29 "k8s.io/helm/pkg/proto/hapi/chart" 30 ) 31 32 const ( 33 testfile = "testdata/local-index.yaml" 34 chartmuseumtestfile = "testdata/chartmuseum-index.yaml" 35 unorderedTestfile = "testdata/local-index-unordered.yaml" 36 testRepo = "test-repo" 37 indexWithDuplicates = ` 38 apiVersion: v1 39 entries: 40 nginx: 41 - urls: 42 - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz 43 name: nginx 44 description: string 45 version: 0.2.0 46 home: https://github.com/something/else 47 digest: "sha256:1234567890abcdef" 48 nginx: 49 - urls: 50 - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz 51 - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz 52 name: alpine 53 description: string 54 version: 1.0.0 55 home: https://github.com/something 56 digest: "sha256:1234567890abcdef" 57 ` 58 ) 59 60 func TestIndexFile(t *testing.T) { 61 i := NewIndexFile() 62 i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") 63 i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.1"}, "cutter-0.1.1.tgz", "http://example.com/charts", "sha256:1234567890abc") 64 i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.0"}, "cutter-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890abc") 65 i.Add(&chart.Metadata{Name: "cutter", Version: "0.2.0"}, "cutter-0.2.0.tgz", "http://example.com/charts", "sha256:1234567890abc") 66 i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+alpha"}, "setter-0.1.9+alpha.tgz", "http://example.com/charts", "sha256:1234567890abc") 67 i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+beta"}, "setter-0.1.9+beta.tgz", "http://example.com/charts", "sha256:1234567890abc") 68 69 i.SortEntries() 70 71 if i.APIVersion != APIVersionV1 { 72 t.Error("Expected API version v1") 73 } 74 75 if len(i.Entries) != 3 { 76 t.Errorf("Expected 3 charts. Got %d", len(i.Entries)) 77 } 78 79 if i.Entries["clipper"][0].Name != "clipper" { 80 t.Errorf("Expected clipper, got %s", i.Entries["clipper"][0].Name) 81 } 82 83 if len(i.Entries["cutter"]) != 3 { 84 t.Error("Expected three cutters.") 85 } 86 87 // Test that the sort worked. 0.2 should be at the first index for Cutter. 88 if v := i.Entries["cutter"][0].Version; v != "0.2.0" { 89 t.Errorf("Unexpected first version: %s", v) 90 } 91 92 cv, err := i.Get("setter", "0.1.9") 93 if err == nil && strings.Index(cv.Metadata.Version, "0.1.9") < 0 { 94 t.Errorf("Unexpected version: %s", cv.Metadata.Version) 95 } 96 97 cv, err = i.Get("setter", "0.1.9+alpha") 98 if err != nil || cv.Metadata.Version != "0.1.9+alpha" { 99 t.Errorf("Expected version: 0.1.9+alpha") 100 } 101 } 102 103 func TestLoadIndex(t *testing.T) { 104 tests := []struct { 105 Name string 106 Filename string 107 }{ 108 { 109 Name: "regular index file", 110 Filename: testfile, 111 }, 112 113 { 114 Name: "chartmuseum index file", 115 Filename: chartmuseumtestfile, 116 }, 117 } 118 119 for _, tc := range tests { 120 tc := tc 121 t.Run(tc.Name, func(t *testing.T) { 122 t.Parallel() 123 b, err := ioutil.ReadFile(tc.Filename) 124 if err != nil { 125 t.Fatal(err) 126 } 127 i, err := loadIndex(b) 128 if err != nil { 129 t.Fatal(err) 130 } 131 verifyLocalIndex(t, i) 132 }) 133 } 134 } 135 136 // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. 137 func TestLoadIndex_Duplicates(t *testing.T) { 138 if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil { 139 t.Errorf("Expected an error when duplicate entries are present") 140 } 141 } 142 143 func TestLoadIndexFile(t *testing.T) { 144 i, err := LoadIndexFile(testfile) 145 if err != nil { 146 t.Fatal(err) 147 } 148 verifyLocalIndex(t, i) 149 } 150 151 func TestLoadUnorderedIndex(t *testing.T) { 152 b, err := ioutil.ReadFile(unorderedTestfile) 153 if err != nil { 154 t.Fatal(err) 155 } 156 i, err := loadIndex(b) 157 if err != nil { 158 t.Fatal(err) 159 } 160 verifyLocalIndex(t, i) 161 } 162 163 func TestMerge(t *testing.T) { 164 ind1 := NewIndexFile() 165 ind1.Add(&chart.Metadata{ 166 Name: "dreadnought", 167 Version: "0.1.0", 168 }, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa") 169 170 ind2 := NewIndexFile() 171 ind2.Add(&chart.Metadata{ 172 Name: "dreadnought", 173 Version: "0.2.0", 174 }, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb") 175 ind2.Add(&chart.Metadata{ 176 Name: "doughnut", 177 Version: "0.2.0", 178 }, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb") 179 180 ind1.Merge(ind2) 181 182 if len(ind1.Entries) != 2 { 183 t.Errorf("Expected 2 entries, got %d", len(ind1.Entries)) 184 vs := ind1.Entries["dreadnaught"] 185 if len(vs) != 2 { 186 t.Errorf("Expected 2 versions, got %d", len(vs)) 187 } 188 v := vs[0] 189 if v.Version != "0.2.0" { 190 t.Errorf("Expected %q version to be 0.2.0, got %s", v.Name, v.Version) 191 } 192 } 193 194 } 195 196 func TestDownloadIndexFile(t *testing.T) { 197 t.Run("should download index file", func(t *testing.T) { 198 srv, err := startLocalServerForTests(nil) 199 if err != nil { 200 t.Fatal(err) 201 } 202 defer srv.Close() 203 204 dirName, err := ioutil.TempDir("", "tmp") 205 if err != nil { 206 t.Fatal(err) 207 } 208 defer os.RemoveAll(dirName) 209 210 indexFilePath := filepath.Join(dirName, testRepo+"-index.yaml") 211 r, err := NewChartRepository(&Entry{ 212 Name: testRepo, 213 URL: srv.URL, 214 Cache: indexFilePath, 215 }, getter.All(environment.EnvSettings{})) 216 if err != nil { 217 t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) 218 } 219 220 if err := r.DownloadIndexFile(""); err != nil { 221 t.Errorf("%#v", err) 222 } 223 224 if _, err := os.Stat(indexFilePath); err != nil { 225 t.Errorf("error finding created index file: %#v", err) 226 } 227 228 b, err := ioutil.ReadFile(indexFilePath) 229 if err != nil { 230 t.Errorf("error reading index file: %#v", err) 231 } 232 233 i, err := loadIndex(b) 234 if err != nil { 235 t.Errorf("Index %q failed to parse: %s", testfile, err) 236 return 237 } 238 239 verifyLocalIndex(t, i) 240 }) 241 242 t.Run("should not decode the path in the repo url while downloading index", func(t *testing.T) { 243 chartRepoURLPath := "/some%2Fpath/test" 244 fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") 245 if err != nil { 246 t.Fatal(err) 247 } 248 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 249 if r.URL.RawPath == chartRepoURLPath+"/index.yaml" { 250 w.Write(fileBytes) 251 } 252 }) 253 srv, err := startLocalServerForTests(handler) 254 if err != nil { 255 t.Fatal(err) 256 } 257 defer srv.Close() 258 259 dirName, err := ioutil.TempDir("", "tmp") 260 if err != nil { 261 t.Fatal(err) 262 } 263 defer os.RemoveAll(dirName) 264 265 indexFilePath := filepath.Join(dirName, testRepo+"-index.yaml") 266 r, err := NewChartRepository(&Entry{ 267 Name: testRepo, 268 URL: srv.URL + chartRepoURLPath, 269 Cache: indexFilePath, 270 }, getter.All(environment.EnvSettings{})) 271 if err != nil { 272 t.Errorf("Problem creating chart repository from %s: %v", "testrepo", err) 273 } 274 275 if err := r.DownloadIndexFile(""); err != nil { 276 t.Errorf("%#v", err) 277 } 278 279 if _, err := os.Stat(indexFilePath); err != nil { 280 t.Errorf("error finding created index file: %#v", err) 281 } 282 283 b, err := ioutil.ReadFile(indexFilePath) 284 if err != nil { 285 t.Errorf("error reading index file: %#v", err) 286 } 287 288 i, err := loadIndex(b) 289 if err != nil { 290 t.Errorf("Index %q failed to parse: %s", testfile, err) 291 return 292 } 293 294 verifyLocalIndex(t, i) 295 }) 296 } 297 298 func verifyLocalIndex(t *testing.T, i *IndexFile) { 299 numEntries := len(i.Entries) 300 if numEntries != 3 { 301 t.Errorf("Expected 3 entries in index file but got %d", numEntries) 302 } 303 304 alpine, ok := i.Entries["alpine"] 305 if !ok { 306 t.Errorf("'alpine' section not found.") 307 return 308 } 309 310 if l := len(alpine); l != 1 { 311 t.Errorf("'alpine' should have 1 chart, got %d", l) 312 return 313 } 314 315 nginx, ok := i.Entries["nginx"] 316 if !ok || len(nginx) != 2 { 317 t.Error("Expected 2 nginx entries") 318 return 319 } 320 321 expects := []*ChartVersion{ 322 { 323 Metadata: &chart.Metadata{ 324 Name: "alpine", 325 Description: "string", 326 Version: "1.0.0", 327 Keywords: []string{"linux", "alpine", "small", "sumtin"}, 328 Home: "https://github.com/something", 329 }, 330 URLs: []string{ 331 "https://charts.helm.sh/stable/alpine-1.0.0.tgz", 332 "http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz", 333 }, 334 Digest: "sha256:1234567890abcdef", 335 }, 336 { 337 Metadata: &chart.Metadata{ 338 Name: "nginx", 339 Description: "string", 340 Version: "0.2.0", 341 Keywords: []string{"popular", "web server", "proxy"}, 342 Home: "https://github.com/something/else", 343 }, 344 URLs: []string{ 345 "https://charts.helm.sh/stable/nginx-0.2.0.tgz", 346 }, 347 Digest: "sha256:1234567890abcdef", 348 }, 349 { 350 Metadata: &chart.Metadata{ 351 Name: "nginx", 352 Description: "string", 353 Version: "0.1.0", 354 Keywords: []string{"popular", "web server", "proxy"}, 355 Home: "https://github.com/something", 356 }, 357 URLs: []string{ 358 "https://charts.helm.sh/stable/nginx-0.1.0.tgz", 359 }, 360 Digest: "sha256:1234567890abcdef", 361 }, 362 } 363 tests := []*ChartVersion{alpine[0], nginx[0], nginx[1]} 364 365 for i, tt := range tests { 366 expect := expects[i] 367 if tt.Name != expect.Name { 368 t.Errorf("Expected name %q, got %q", expect.Name, tt.Name) 369 } 370 if tt.Description != expect.Description { 371 t.Errorf("Expected description %q, got %q", expect.Description, tt.Description) 372 } 373 if tt.Version != expect.Version { 374 t.Errorf("Expected version %q, got %q", expect.Version, tt.Version) 375 } 376 if tt.Digest != expect.Digest { 377 t.Errorf("Expected digest %q, got %q", expect.Digest, tt.Digest) 378 } 379 if tt.Home != expect.Home { 380 t.Errorf("Expected home %q, got %q", expect.Home, tt.Home) 381 } 382 383 for i, url := range tt.URLs { 384 if url != expect.URLs[i] { 385 t.Errorf("Expected URL %q, got %q", expect.URLs[i], url) 386 } 387 } 388 for i, kw := range tt.Keywords { 389 if kw != expect.Keywords[i] { 390 t.Errorf("Expected keywords %q, got %q", expect.Keywords[i], kw) 391 } 392 } 393 } 394 } 395 396 func TestIndexDirectory(t *testing.T) { 397 dir := filepath.Join("testdata", "repository") 398 index, err := IndexDirectory(dir, "http://localhost:8080") 399 if err != nil { 400 t.Fatal(err) 401 } 402 403 if l := len(index.Entries); l != 3 { 404 t.Fatalf("Expected 3 entries, got %d", l) 405 } 406 407 // Other things test the entry generation more thoroughly. We just test a 408 // few fields. 409 410 corpus := []struct{ chartName, downloadLink string }{ 411 {"frobnitz", "http://localhost:8080/frobnitz-1.2.3.tgz"}, 412 {"zarthal", "http://localhost:8080/universe/zarthal-1.0.0.tgz"}, 413 } 414 415 for _, test := range corpus { 416 cname := test.chartName 417 frobs, ok := index.Entries[cname] 418 if !ok { 419 t.Fatalf("Could not read chart %s", cname) 420 } 421 422 frob := frobs[0] 423 if len(frob.Digest) == 0 { 424 t.Errorf("Missing digest of file %s.", frob.Name) 425 } 426 if frob.URLs[0] != test.downloadLink { 427 t.Errorf("Unexpected URLs: %v", frob.URLs) 428 } 429 if frob.Name != cname { 430 t.Errorf("Expected %q, got %q", cname, frob.Name) 431 } 432 } 433 } 434 435 func TestLoadUnversionedIndex(t *testing.T) { 436 data, err := ioutil.ReadFile("testdata/unversioned-index.yaml") 437 if err != nil { 438 t.Fatal(err) 439 } 440 441 ind, err := loadUnversionedIndex(data) 442 if err != nil { 443 t.Fatal(err) 444 } 445 446 if l := len(ind.Entries); l != 2 { 447 t.Fatalf("Expected 2 entries, got %d", l) 448 } 449 450 if l := len(ind.Entries["mysql"]); l != 3 { 451 t.Fatalf("Expected 3 mysql versions, got %d", l) 452 } 453 } 454 455 func TestIndexAdd(t *testing.T) { 456 i := NewIndexFile() 457 i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") 458 459 if i.Entries["clipper"][0].URLs[0] != "http://example.com/charts/clipper-0.1.0.tgz" { 460 t.Errorf("Expected http://example.com/charts/clipper-0.1.0.tgz, got %s", i.Entries["clipper"][0].URLs[0]) 461 } 462 463 i.Add(&chart.Metadata{Name: "alpine", Version: "0.1.0"}, "/home/charts/alpine-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") 464 465 if i.Entries["alpine"][0].URLs[0] != "http://example.com/charts/alpine-0.1.0.tgz" { 466 t.Errorf("Expected http://example.com/charts/alpine-0.1.0.tgz, got %s", i.Entries["alpine"][0].URLs[0]) 467 } 468 469 i.Add(&chart.Metadata{Name: "deis", Version: "0.1.0"}, "/home/charts/deis-0.1.0.tgz", "http://example.com/charts/", "sha256:1234567890") 470 471 if i.Entries["deis"][0].URLs[0] != "http://example.com/charts/deis-0.1.0.tgz" { 472 t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0]) 473 } 474 }