1
16
17 package repo
18
19 import (
20 "bytes"
21 "net/http"
22 "net/http/httptest"
23 "os"
24 "path/filepath"
25 "reflect"
26 "runtime"
27 "strings"
28 "testing"
29 "time"
30
31 "sigs.k8s.io/yaml"
32
33 "helm.sh/helm/v3/pkg/chart"
34 "helm.sh/helm/v3/pkg/cli"
35 "helm.sh/helm/v3/pkg/getter"
36 )
37
38 const (
39 testRepository = "testdata/repository"
40 testURL = "http://example-charts.com"
41 )
42
43 func TestLoadChartRepository(t *testing.T) {
44 r, err := NewChartRepository(&Entry{
45 Name: testRepository,
46 URL: testURL,
47 }, getter.All(&cli.EnvSettings{}))
48 if err != nil {
49 t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
50 }
51
52 if err := r.Load(); err != nil {
53 t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
54 }
55
56 paths := []string{
57 filepath.Join(testRepository, "frobnitz-1.2.3.tgz"),
58 filepath.Join(testRepository, "sprocket-1.1.0.tgz"),
59 filepath.Join(testRepository, "sprocket-1.2.0.tgz"),
60 filepath.Join(testRepository, "universe/zarthal-1.0.0.tgz"),
61 }
62
63 if r.Config.Name != testRepository {
64 t.Errorf("Expected %s as Name but got %s", testRepository, r.Config.Name)
65 }
66
67 if !reflect.DeepEqual(r.ChartPaths, paths) {
68 t.Errorf("Expected %#v but got %#v\n", paths, r.ChartPaths)
69 }
70
71 if r.Config.URL != testURL {
72 t.Errorf("Expected url for chart repository to be %s but got %s", testURL, r.Config.URL)
73 }
74 }
75
76 func TestIndex(t *testing.T) {
77 r, err := NewChartRepository(&Entry{
78 Name: testRepository,
79 URL: testURL,
80 }, getter.All(&cli.EnvSettings{}))
81 if err != nil {
82 t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
83 }
84
85 if err := r.Load(); err != nil {
86 t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
87 }
88
89 err = r.Index()
90 if err != nil {
91 t.Errorf("Error performing index: %v\n", err)
92 }
93
94 tempIndexPath := filepath.Join(testRepository, indexPath)
95 actual, err := LoadIndexFile(tempIndexPath)
96 defer os.Remove(tempIndexPath)
97 if err != nil {
98 t.Errorf("Error loading index file %v", err)
99 }
100 verifyIndex(t, actual)
101
102
103 err = r.Index()
104 if err != nil {
105 t.Errorf("Error performing re-index: %s\n", err)
106 }
107 second, err := LoadIndexFile(tempIndexPath)
108 if err != nil {
109 t.Errorf("Error re-loading index file %v", err)
110 }
111 verifyIndex(t, second)
112 }
113
114 type CustomGetter struct {
115 repoUrls []string
116 }
117
118 func (g *CustomGetter) Get(href string, _ ...getter.Option) (*bytes.Buffer, error) {
119 index := &IndexFile{
120 APIVersion: "v1",
121 Generated: time.Now(),
122 }
123 indexBytes, err := yaml.Marshal(index)
124 if err != nil {
125 return nil, err
126 }
127 g.repoUrls = append(g.repoUrls, href)
128 return bytes.NewBuffer(indexBytes), nil
129 }
130
131 func TestIndexCustomSchemeDownload(t *testing.T) {
132 repoName := "gcs-repo"
133 repoURL := "gs://some-gcs-bucket"
134 myCustomGetter := &CustomGetter{}
135 customGetterConstructor := func(_ ...getter.Option) (getter.Getter, error) {
136 return myCustomGetter, nil
137 }
138 providers := getter.Providers{{
139 Schemes: []string{"gs"},
140 New: customGetterConstructor,
141 }}
142 repo, err := NewChartRepository(&Entry{
143 Name: repoName,
144 URL: repoURL,
145 }, providers)
146 if err != nil {
147 t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err)
148 }
149 repo.CachePath = t.TempDir()
150
151 tempIndexFile, err := os.CreateTemp("", "test-repo")
152 if err != nil {
153 t.Fatalf("Failed to create temp index file: %v", err)
154 }
155 defer os.Remove(tempIndexFile.Name())
156
157 idx, err := repo.DownloadIndexFile()
158 if err != nil {
159 t.Fatalf("Failed to download index file to %s: %v", idx, err)
160 }
161
162 if len(myCustomGetter.repoUrls) != 1 {
163 t.Fatalf("Custom Getter.Get should be called once")
164 }
165
166 expectedRepoIndexURL := repoURL + "/index.yaml"
167 if myCustomGetter.repoUrls[0] != expectedRepoIndexURL {
168 t.Fatalf("Custom Getter.Get should be called with %s", expectedRepoIndexURL)
169 }
170 }
171
172 func verifyIndex(t *testing.T, actual *IndexFile) {
173 var empty time.Time
174 if actual.Generated.Equal(empty) {
175 t.Errorf("Generated should be greater than 0: %s", actual.Generated)
176 }
177
178 if actual.APIVersion != APIVersionV1 {
179 t.Error("Expected v1 API")
180 }
181
182 entries := actual.Entries
183 if numEntries := len(entries); numEntries != 3 {
184 t.Errorf("Expected 3 charts to be listed in index file but got %v", numEntries)
185 }
186
187 expects := map[string]ChartVersions{
188 "frobnitz": {
189 {
190 Metadata: &chart.Metadata{
191 Name: "frobnitz",
192 Version: "1.2.3",
193 },
194 },
195 },
196 "sprocket": {
197 {
198 Metadata: &chart.Metadata{
199 Name: "sprocket",
200 Version: "1.2.0",
201 },
202 },
203 {
204 Metadata: &chart.Metadata{
205 Name: "sprocket",
206 Version: "1.1.0",
207 },
208 },
209 },
210 "zarthal": {
211 {
212 Metadata: &chart.Metadata{
213 Name: "zarthal",
214 Version: "1.0.0",
215 },
216 },
217 },
218 }
219
220 for name, versions := range expects {
221 got, ok := entries[name]
222 if !ok {
223 t.Errorf("Could not find %q entry", name)
224 continue
225 }
226 if len(versions) != len(got) {
227 t.Errorf("Expected %d versions, got %d", len(versions), len(got))
228 continue
229 }
230 for i, e := range versions {
231 g := got[i]
232 if e.Name != g.Name {
233 t.Errorf("Expected %q, got %q", e.Name, g.Name)
234 }
235 if e.Version != g.Version {
236 t.Errorf("Expected %q, got %q", e.Version, g.Version)
237 }
238 if len(g.Keywords) != 3 {
239 t.Error("Expected 3 keywords.")
240 }
241 if len(g.Maintainers) != 2 {
242 t.Error("Expected 2 maintainers.")
243 }
244 if g.Created.Equal(empty) {
245 t.Error("Expected created to be non-empty")
246 }
247 if g.Description == "" {
248 t.Error("Expected description to be non-empty")
249 }
250 if g.Home == "" {
251 t.Error("Expected home to be non-empty")
252 }
253 if g.Digest == "" {
254 t.Error("Expected digest to be non-empty")
255 }
256 if len(g.URLs) != 1 {
257 t.Error("Expected exactly 1 URL")
258 }
259 }
260 }
261 }
262
263
264 func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) {
265 if handler == nil {
266 fileBytes, err := os.ReadFile("testdata/local-index.yaml")
267 if err != nil {
268 return nil, err
269 }
270 handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
271 w.Write(fileBytes)
272 })
273 }
274
275 return httptest.NewServer(handler), nil
276 }
277
278
279 func startLocalTLSServerForTests(handler http.Handler) (*httptest.Server, error) {
280 if handler == nil {
281 fileBytes, err := os.ReadFile("testdata/local-index.yaml")
282 if err != nil {
283 return nil, err
284 }
285 handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
286 w.Write(fileBytes)
287 })
288 }
289
290 return httptest.NewTLSServer(handler), nil
291 }
292
293 func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) {
294 srv, err := startLocalTLSServerForTests(nil)
295 if err != nil {
296 t.Fatal(err)
297 }
298 defer srv.Close()
299
300 chartURL, err := FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, false, getter.All(&cli.EnvSettings{}))
301 if err != nil {
302 t.Fatalf("%v", err)
303 }
304 if chartURL != "https://charts.helm.sh/stable/nginx-0.2.0.tgz" {
305 t.Errorf("%s is not the valid URL", chartURL)
306 }
307
308
309 _, err = FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, false, getter.All(&cli.EnvSettings{}))
310
311
312
313
314 if runtime.GOOS == "darwin" {
315 if !strings.Contains(err.Error(), "x509: “Acme Co” certificate is not trusted") && !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
316 t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err)
317 }
318 } else if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
319 t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err)
320 }
321 }
322
323 func TestFindChartInRepoURL(t *testing.T) {
324 srv, err := startLocalServerForTests(nil)
325 if err != nil {
326 t.Fatal(err)
327 }
328 defer srv.Close()
329
330 chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(&cli.EnvSettings{}))
331 if err != nil {
332 t.Fatalf("%v", err)
333 }
334 if chartURL != "https://charts.helm.sh/stable/nginx-0.2.0.tgz" {
335 t.Errorf("%s is not the valid URL", chartURL)
336 }
337
338 chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{}))
339 if err != nil {
340 t.Errorf("%s", err)
341 }
342 if chartURL != "https://charts.helm.sh/stable/nginx-0.1.0.tgz" {
343 t.Errorf("%s is not the valid URL", chartURL)
344 }
345 }
346
347 func TestErrorFindChartInRepoURL(t *testing.T) {
348
349 g := getter.All(&cli.EnvSettings{
350 RepositoryCache: t.TempDir(),
351 })
352
353 if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil {
354 t.Errorf("Expected error for bad chart URL, but did not get any errors")
355 } else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached`) {
356 t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err)
357 }
358
359 srv, err := startLocalServerForTests(nil)
360 if err != nil {
361 t.Fatal(err)
362 }
363 defer srv.Close()
364
365 if _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", g); err == nil {
366 t.Errorf("Expected error for chart not found, but did not get any errors")
367 } else if err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` {
368 t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
369 }
370
371 if _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", g); err == nil {
372 t.Errorf("Expected error for chart not found, but did not get any errors")
373 } else if err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` {
374 t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
375 }
376
377 if _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", g); err == nil {
378 t.Errorf("Expected error for no chart URLs available, but did not get any errors")
379 } else if err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
380 t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
381 }
382 }
383
384 func TestResolveReferenceURL(t *testing.T) {
385 for _, tt := range []struct {
386 baseURL, refURL, chartURL string
387 }{
388 {"http://localhost:8123/charts/", "nginx-0.2.0.tgz", "http://localhost:8123/charts/nginx-0.2.0.tgz"},
389 {"http://localhost:8123/charts-with-no-trailing-slash", "nginx-0.2.0.tgz", "http://localhost:8123/charts-with-no-trailing-slash/nginx-0.2.0.tgz"},
390 {"http://localhost:8123", "https://charts.helm.sh/stable/nginx-0.2.0.tgz", "https://charts.helm.sh/stable/nginx-0.2.0.tgz"},
391 {"http://localhost:8123/charts%2fwith%2fescaped%2fslash", "nginx-0.2.0.tgz", "http://localhost:8123/charts%2fwith%2fescaped%2fslash/nginx-0.2.0.tgz"},
392 {"http://localhost:8123/charts?with=queryparameter", "nginx-0.2.0.tgz", "http://localhost:8123/charts/nginx-0.2.0.tgz?with=queryparameter"},
393 } {
394 chartURL, err := ResolveReferenceURL(tt.baseURL, tt.refURL)
395 if err != nil {
396 t.Errorf("unexpected error in ResolveReferenceURL(%q, %q): %s", tt.baseURL, tt.refURL, err)
397 }
398 if chartURL != tt.chartURL {
399 t.Errorf("expected ResolveReferenceURL(%q, %q) to equal %q, got %q", tt.baseURL, tt.refURL, tt.chartURL, chartURL)
400 }
401 }
402 }
403
View as plain text