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  }