You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helm/pkg/repo/index_test.go

431 lines
12 KiB

/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package repo
import (
"bufio"
"bytes"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/chart"
)
const (
8 years ago
testfile = "testdata/local-index.yaml"
unorderedTestfile = "testdata/local-index-unordered.yaml"
8 years ago
testRepo = "test-repo"
)
func TestIndexFile(t *testing.T) {
8 years ago
i := NewIndexFile()
i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.1"}, "cutter-0.1.1.tgz", "http://example.com/charts", "sha256:1234567890abc")
i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.0"}, "cutter-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890abc")
i.Add(&chart.Metadata{Name: "cutter", Version: "0.2.0"}, "cutter-0.2.0.tgz", "http://example.com/charts", "sha256:1234567890abc")
i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+alpha"}, "setter-0.1.9+alpha.tgz", "http://example.com/charts", "sha256:1234567890abc")
i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+beta"}, "setter-0.1.9+beta.tgz", "http://example.com/charts", "sha256:1234567890abc")
i.SortEntries()
if i.APIVersion != APIVersionV1 {
t.Error("Expected API version v1")
}
if len(i.Entries) != 3 {
t.Errorf("Expected 3 charts. Got %d", len(i.Entries))
}
if i.Entries["clipper"][0].Name != "clipper" {
t.Errorf("Expected clipper, got %s", i.Entries["clipper"][0].Name)
}
if len(i.Entries["cutter"]) != 3 {
t.Error("Expected three cutters.")
}
// Test that the sort worked. 0.2 should be at the first index for Cutter.
if v := i.Entries["cutter"][0].Version; v != "0.2.0" {
t.Errorf("Unexpected first version: %s", v)
}
cv, err := i.Get("setter", "0.1.9")
if err == nil && !strings.Contains(cv.Metadata.Version, "0.1.9") {
t.Errorf("Unexpected version: %s", cv.Metadata.Version)
}
cv, err = i.Get("setter", "0.1.9+alpha")
if err != nil || cv.Metadata.Version != "0.1.9+alpha" {
t.Errorf("Expected version: 0.1.9+alpha")
}
}
func TestLoadIndex(t *testing.T) {
b, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
i, err := loadIndex(b)
if err != nil {
t.Fatal(err)
}
verifyLocalIndex(t, i)
}
func TestLoadIndexFile(t *testing.T) {
8 years ago
i, err := LoadIndexFile(testfile)
if err != nil {
t.Fatal(err)
}
verifyLocalIndex(t, i)
}
func TestLoadUnorderedIndex(t *testing.T) {
b, err := ioutil.ReadFile(unorderedTestfile)
if err != nil {
t.Fatal(err)
}
i, err := loadIndex(b)
if err != nil {
t.Fatal(err)
}
verifyLocalIndex(t, i)
}
func TestMerge(t *testing.T) {
8 years ago
ind1 := NewIndexFile()
ind1.Add(&chart.Metadata{
Name: "dreadnought",
Version: "0.1.0",
}, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa")
8 years ago
ind2 := NewIndexFile()
ind2.Add(&chart.Metadata{
Name: "dreadnought",
Version: "0.2.0",
}, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb")
ind2.Add(&chart.Metadata{
Name: "doughnut",
Version: "0.2.0",
}, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb")
ind1.Merge(ind2)
if len(ind1.Entries) != 2 {
t.Errorf("Expected 2 entries, got %d", len(ind1.Entries))
Spelling (#7258) * spelling: constraint Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: cryptographic Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dependency Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: doesnot Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: don't Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: unexpected Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: dreadnought Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: default Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: envvars Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: evaluates Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: execute Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: extractor Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: frobnitz Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: generated Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: implementation Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: jabba Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: keywords Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: kubernetes Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: override Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: package Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: parsable Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: progress Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: recursively Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: release Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: cache Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: representing Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: serializer Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: subchart Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: utilities Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
5 years ago
vs := ind1.Entries["dreadnought"]
if len(vs) != 2 {
t.Errorf("Expected 2 versions, got %d", len(vs))
}
v := vs[0]
if v.Version != "0.2.0" {
t.Errorf("Expected %q version to be 0.2.0, got %s", v.Name, v.Version)
}
}
}
func TestDownloadIndexFile(t *testing.T) {
t.Run("should download index file", func(t *testing.T) {
srv, err := startLocalServerForTests(nil)
if err != nil {
t.Fatal(err)
}
defer srv.Close()
r, err := NewChartRepository(&Entry{
Name: testRepo,
URL: srv.URL,
}, getter.All(&cli.EnvSettings{}))
if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepo, err)
}
idx, err := r.DownloadIndexFile()
if err != nil {
t.Fatalf("Failed to download index file to %s: %#v", idx, err)
}
if _, err := os.Stat(idx); err != nil {
t.Fatalf("error finding created index file: %#v", err)
}
b, err := ioutil.ReadFile(idx)
if err != nil {
t.Fatalf("error reading index file: %#v", err)
}
i, err := loadIndex(b)
if err != nil {
t.Fatalf("Index %q failed to parse: %s", testfile, err)
}
verifyLocalIndex(t, i)
// Check that charts file is also created
idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
if _, err := os.Stat(idx); err != nil {
t.Fatalf("error finding created charts file: %#v", err)
}
b, err = ioutil.ReadFile(idx)
if err != nil {
t.Fatalf("error reading charts file: %#v", err)
}
verifyLocalChartsFile(t, b, i)
})
t.Run("should not decode the path in the repo url while downloading index", func(t *testing.T) {
chartRepoURLPath := "/some%2Fpath/test"
fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml")
if err != nil {
t.Fatal(err)
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.RawPath == chartRepoURLPath+"/index.yaml" {
w.Write(fileBytes)
}
})
srv, err := startLocalServerForTests(handler)
if err != nil {
t.Fatal(err)
}
defer srv.Close()
r, err := NewChartRepository(&Entry{
Name: testRepo,
URL: srv.URL + chartRepoURLPath,
}, getter.All(&cli.EnvSettings{}))
if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepo, err)
}
idx, err := r.DownloadIndexFile()
if err != nil {
t.Fatalf("Failed to download index file to %s: %#v", idx, err)
}
if _, err := os.Stat(idx); err != nil {
t.Fatalf("error finding created index file: %#v", err)
}
b, err := ioutil.ReadFile(idx)
if err != nil {
t.Fatalf("error reading index file: %#v", err)
}
i, err := loadIndex(b)
if err != nil {
t.Fatalf("Index %q failed to parse: %s", testfile, err)
}
verifyLocalIndex(t, i)
// Check that charts file is also created
idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
if _, err := os.Stat(idx); err != nil {
t.Fatalf("error finding created charts file: %#v", err)
}
b, err = ioutil.ReadFile(idx)
if err != nil {
t.Fatalf("error reading charts file: %#v", err)
}
verifyLocalChartsFile(t, b, i)
})
}
8 years ago
func verifyLocalIndex(t *testing.T, i *IndexFile) {
numEntries := len(i.Entries)
if numEntries != 3 {
t.Errorf("Expected 3 entries in index file but got %d", numEntries)
}
alpine, ok := i.Entries["alpine"]
if !ok {
t.Fatalf("'alpine' section not found.")
}
if l := len(alpine); l != 1 {
t.Fatalf("'alpine' should have 1 chart, got %d", l)
}
nginx, ok := i.Entries["nginx"]
if !ok || len(nginx) != 2 {
t.Fatalf("Expected 2 nginx entries")
}
expects := []*ChartVersion{
{
Metadata: &chart.Metadata{
Name: "alpine",
Description: "string",
Version: "1.0.0",
Keywords: []string{"linux", "alpine", "small", "sumtin"},
Home: "https://github.com/something",
},
URLs: []string{
"https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz",
"http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz",
},
Digest: "sha256:1234567890abcdef",
},
{
Metadata: &chart.Metadata{
Name: "nginx",
Description: "string",
Version: "0.2.0",
Keywords: []string{"popular", "web server", "proxy"},
Home: "https://github.com/something/else",
},
URLs: []string{
"https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz",
},
Digest: "sha256:1234567890abcdef",
},
{
Metadata: &chart.Metadata{
Name: "nginx",
Description: "string",
Version: "0.1.0",
Keywords: []string{"popular", "web server", "proxy"},
Home: "https://github.com/something",
},
URLs: []string{
"https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz",
},
Digest: "sha256:1234567890abcdef",
},
}
tests := []*ChartVersion{alpine[0], nginx[0], nginx[1]}
for i, tt := range tests {
expect := expects[i]
if tt.Name != expect.Name {
t.Errorf("Expected name %q, got %q", expect.Name, tt.Name)
}
if tt.Description != expect.Description {
t.Errorf("Expected description %q, got %q", expect.Description, tt.Description)
}
if tt.Version != expect.Version {
t.Errorf("Expected version %q, got %q", expect.Version, tt.Version)
}
if tt.Digest != expect.Digest {
t.Errorf("Expected digest %q, got %q", expect.Digest, tt.Digest)
}
if tt.Home != expect.Home {
t.Errorf("Expected home %q, got %q", expect.Home, tt.Home)
}
for i, url := range tt.URLs {
if url != expect.URLs[i] {
t.Errorf("Expected URL %q, got %q", expect.URLs[i], url)
}
}
for i, kw := range tt.Keywords {
if kw != expect.Keywords[i] {
t.Errorf("Expected keywords %q, got %q", expect.Keywords[i], kw)
}
}
}
}
func verifyLocalChartsFile(t *testing.T, chartsContent []byte, indexContent *IndexFile) {
var expected, real []string
for chart := range indexContent.Entries {
expected = append(expected, chart)
}
sort.Strings(expected)
scanner := bufio.NewScanner(bytes.NewReader(chartsContent))
for scanner.Scan() {
real = append(real, scanner.Text())
}
sort.Strings(real)
if strings.Join(expected, " ") != strings.Join(real, " ") {
t.Errorf("Cached charts file content unexpected. Expected:\n%s\ngot:\n%s", expected, real)
}
}
func TestIndexDirectory(t *testing.T) {
dir := "testdata/repository"
8 years ago
index, err := IndexDirectory(dir, "http://localhost:8080")
if err != nil {
t.Fatal(err)
}
if l := len(index.Entries); l != 3 {
t.Fatalf("Expected 3 entries, got %d", l)
}
// Other things test the entry generation more thoroughly. We just test a
// few fields.
corpus := []struct{ chartName, downloadLink string }{
{"frobnitz", "http://localhost:8080/frobnitz-1.2.3.tgz"},
{"zarthal", "http://localhost:8080/universe/zarthal-1.0.0.tgz"},
}
for _, test := range corpus {
cname := test.chartName
frobs, ok := index.Entries[cname]
if !ok {
t.Fatalf("Could not read chart %s", cname)
}
frob := frobs[0]
if frob.Digest == "" {
t.Errorf("Missing digest of file %s.", frob.Name)
}
if frob.URLs[0] != test.downloadLink {
t.Errorf("Unexpected URLs: %v", frob.URLs)
}
if frob.Name != cname {
t.Errorf("Expected %q, got %q", cname, frob.Name)
}
}
}
func TestIndexAdd(t *testing.T) {
8 years ago
i := NewIndexFile()
i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
if i.Entries["clipper"][0].URLs[0] != "http://example.com/charts/clipper-0.1.0.tgz" {
t.Errorf("Expected http://example.com/charts/clipper-0.1.0.tgz, got %s", i.Entries["clipper"][0].URLs[0])
}
i.Add(&chart.Metadata{Name: "alpine", Version: "0.1.0"}, "/home/charts/alpine-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
if i.Entries["alpine"][0].URLs[0] != "http://example.com/charts/alpine-0.1.0.tgz" {
t.Errorf("Expected http://example.com/charts/alpine-0.1.0.tgz, got %s", i.Entries["alpine"][0].URLs[0])
}
i.Add(&chart.Metadata{Name: "deis", Version: "0.1.0"}, "/home/charts/deis-0.1.0.tgz", "http://example.com/charts/", "sha256:1234567890")
if i.Entries["deis"][0].URLs[0] != "http://example.com/charts/deis-0.1.0.tgz" {
t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0])
}
}