// loadChartRepositories reads the repositories.yaml, and then builds a map of // ChartRepositories. // // The key is the local name (which is only present in the repositories.yaml). func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) { indices := map[string]*repo.ChartRepository{} repoyaml := m.HelmHome.RepositoryFile() // Load repositories.yaml file rf, err := repo.LoadRepositoriesFile(repoyaml) if err != nil { return indices, fmt.Errorf("failed to load %s: %s", repoyaml, err) } for _, re := range rf.Repositories { lname := re.Name cacheindex := m.HelmHome.CacheIndex(lname) index, err := repo.LoadIndexFile(cacheindex) if err != nil { return indices, err } cr := &repo.ChartRepository{ URL: re.URL, IndexFile: index, } indices[lname] = cr } return indices, nil }
func TestRepoIndexCmd(t *testing.T) { dir, err := ioutil.TempDir("", "helm-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) if err := os.Link("testdata/testcharts/compressedchart-0.1.0.tgz", filepath.Join(dir, "compressedchart-0.1.0.tgz")); err != nil { t.Fatal(err) } buf := bytes.NewBuffer(nil) c := newRepoIndexCmd(buf) if err := c.RunE(c, []string{dir}); err != nil { t.Errorf("%q", err) } index, err := repo.LoadIndexFile(filepath.Join(dir, "index.yaml")) if err != nil { t.Fatal(err) } if len(index.Entries) != 1 { t.Errorf("expected 1 entry, got %v: %#v", len(index.Entries), index.Entries) } }
func searchCacheForPattern(dir string, search string) ([]string, error) { fileList := []string{} filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { if !f.IsDir() { fileList = append(fileList, path) } return nil }) matches := []string{} for _, f := range fileList { index, err := repo.LoadIndexFile(f) if err != nil { return matches, fmt.Errorf("index %s corrupted: %s", f, err) } m := searchChartRefsForPattern(search, index.Entries) repoName := strings.TrimSuffix(filepath.Base(f), "-index.yaml") for _, c := range m { // TODO: Is it possible for this file to be missing? Or to have // an extension other than .tgz? Should the actual filename be in // the YAML? fname := filepath.Join(repoName, c+".tgz") matches = append(matches, fname) } } return matches, nil }
// Resolve resolves dependencies and returns a lock file with the resolution. func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string) (*chartutil.RequirementsLock, error) { d, err := HashReq(reqs) if err != nil { return nil, err } // Now we clone the dependencies, locking as we go. locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) missing := []string{} for i, d := range reqs.Dependencies { constraint, err := semver.NewConstraint(d.Version) if err != nil { return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %s", d.Name, err) } repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(repoNames[d.Name])) if err != nil { return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) } vs, ok := repoIndex.Entries[d.Name] if !ok { return nil, fmt.Errorf("%s chart not found in repo %s", d.Name, d.Repository) } locked[i] = &chartutil.Dependency{ Name: d.Name, Repository: d.Repository, } found := false // The version are already sorted and hence the first one to satisfy the constraint is used for _, ver := range vs { v, err := semver.NewVersion(ver.Version) if err != nil || len(ver.URLs) == 0 { // Not a legit entry. continue } if constraint.Check(v) { found = true locked[i].Version = v.Original() break } } if !found { missing = append(missing, d.Name) } } if len(missing) > 0 { return nil, fmt.Errorf("Can't get a valid version for repositories %s. Try changing the version constraint in requirements.yaml", strings.Join(missing, ", ")) } return &chartutil.RequirementsLock{ Generated: time.Now(), Digest: d, Dependencies: locked, }, nil }
func searchTestRunner(t *testing.T, tc searchTestCase) { cf, err := repo.LoadIndexFile(testFile) if err != nil { t.Errorf("Failed to load index file : %s : %s", testFile, err) } u := searchChartRefsForPattern(tc.in, cf.Entries) validateEntries(t, tc.in, u, tc.expectedOut) }
// ResolveChartVersion resolves a chart reference to a URL. // // A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path. // // A version is a SemVer string (1.2.3-beta.1+f334a6789). // // - For fully qualified URLs, the version will be ignored (since URLs aren't versioned) // - For a chart reference // * If version is non-empty, this will return the URL for that version // * If version is empty, this will return the URL for the latest version // * If no version can be found, an error is returned func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) { // See if it's already a full URL. // FIXME: Why do we use url.ParseRequestURI instead of url.Parse? u, err := url.ParseRequestURI(ref) if err == nil { // If it has a scheme and host and path, it's a full URL if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { return u, nil } return u, fmt.Errorf("invalid chart url format: %s", ref) } r, err := repo.LoadRepositoriesFile(c.HelmHome.RepositoryFile()) if err != nil { return u, err } // See if it's of the form: repo/path_to_chart p := strings.SplitN(ref, "/", 2) if len(p) < 2 { return u, fmt.Errorf("invalid chart url format: %s", ref) } repoName := p[0] chartName := p[1] rf, err := findRepoEntry(repoName, r.Repositories) if err != nil { return u, err } if rf.URL == "" { return u, fmt.Errorf("no URL found for repository %q", repoName) } // Next, we need to load the index, and actually look up the chart. i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(repoName)) if err != nil { return u, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) } cv, err := i.Get(chartName, version) if err != nil { return u, fmt.Errorf("chart %q not found in %s index. (try 'helm repo update'). %s", chartName, repoName, err) } if len(cv.URLs) == 0 { return u, fmt.Errorf("chart %q has no downloadable URLs", ref) } return url.Parse(cv.URLs[0]) }
func index(dir, url, mergeTo string) error { chartRepo, err := repo.LoadChartRepository(dir, url) if err != nil { return err } if mergeTo != "" { old, err := repo.LoadIndexFile(mergeTo) if err != nil { return err } return chartRepo.MergeIndex(old) } return chartRepo.Index() }
func index(dir, url, mergeTo string) error { out := filepath.Join(dir, "index.yaml") i, err := repo.IndexDirectory(dir, url) if err != nil { return err } if mergeTo != "" { i2, err := repo.LoadIndexFile(mergeTo) if err != nil { return fmt.Errorf("Merge failed: %s", err) } i.Merge(i2) } i.SortEntries() return i.WriteFile(out, 0755) }
func (s *searchCmd) buildIndex() (*search.Index, error) { // Load the repositories.yaml rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) if err != nil { return nil, err } i := search.NewIndex() for n := range rf.Repositories { f := s.helmhome.CacheIndex(n) ind, err := repo.LoadIndexFile(f) if err != nil { fmt.Fprintf(s.out, "WARNING: Repo %q is corrupt. Try 'helm update': %s", f, err) continue } i.AddRepo(n, ind) } return i, nil }
func (s *searchCmd) buildIndex() (*search.Index, error) { // Load the repositories.yaml rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) if err != nil { return nil, err } i := search.NewIndex() for _, re := range rf.Repositories { n := re.Name f := s.helmhome.CacheIndex(n) ind, err := repo.LoadIndexFile(f) if err != nil { fmt.Fprintf(s.out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) continue } i.AddRepo(n, ind, s.versions) } return i, nil }
func TestDependencyUpdateCmd(t *testing.T) { // Set up a testing helm home oldhome := helmHome hh, err := tempHelmHome() if err != nil { t.Fatal(err) } helmHome = hh defer func() { os.RemoveAll(hh) helmHome = oldhome }() srv := repotest.NewServer(hh) defer srv.Stop() copied, err := srv.CopyCharts("testdata/testcharts/*.tgz") t.Logf("Copied charts:\n%s", strings.Join(copied, "\n")) t.Logf("Listening on directory %s", srv.Root()) chartname := "depup" if err := createTestingChart(hh, chartname, srv.URL()); err != nil { t.Fatal(err) } out := bytes.NewBuffer(nil) duc := &dependencyUpdateCmd{out: out} duc.helmhome = helmpath.Home(hh) duc.chartpath = filepath.Join(hh, chartname) if err := duc.run(); err != nil { output := out.String() t.Logf("Output: %s", output) t.Fatal(err) } output := out.String() // This is written directly to stdout, so we have to capture as is. if !strings.Contains(output, `update from the "test" chart repository`) { t.Errorf("Repo did not get updated\n%s", output) } // Make sure the actual file got downloaded. expect := filepath.Join(hh, chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } hash, err := provenance.DigestFile(expect) if err != nil { t.Fatal(err) } i, err := repo.LoadIndexFile(cacheIndexFile("test")) if err != nil { t.Fatal(err) } if h := i.Entries["reqtest-0.1.0"].Digest; h != hash { t.Errorf("Failed hash match: expected %s, got %s", hash, h) } t.Logf("Results: %s", out.String()) }
func TestRepoIndexCmd(t *testing.T) { dir, err := ioutil.TempDir("", "helm-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) comp := filepath.Join(dir, "compressedchart-0.1.0.tgz") if err := os.Link("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil { t.Fatal(err) } comp2 := filepath.Join(dir, "compressedchart-0.2.0.tgz") if err := os.Link("testdata/testcharts/compressedchart-0.2.0.tgz", comp2); err != nil { t.Fatal(err) } buf := bytes.NewBuffer(nil) c := newRepoIndexCmd(buf) if err := c.RunE(c, []string{dir}); err != nil { t.Error(err) } destIndex := filepath.Join(dir, "index.yaml") index, err := repo.LoadIndexFile(destIndex) if err != nil { t.Fatal(err) } if len(index.Entries) != 1 { t.Errorf("expected 1 entry, got %d: %#v", len(index.Entries), index.Entries) } vs := index.Entries["compressedchart"] if len(vs) != 2 { t.Errorf("expected 2 versions, got %d: %#v", len(vs), vs) } expectedVersion := "0.2.0" if vs[0].Version != expectedVersion { t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) } // Test with `--merge` // Remove first two charts. if err := os.Remove(comp); err != nil { t.Fatal(err) } if err := os.Remove(comp2); err != nil { t.Fatal(err) } // Add a new chart and a new version of an existing chart if err := os.Link("testdata/testcharts/reqtest-0.1.0.tgz", filepath.Join(dir, "reqtest-0.1.0.tgz")); err != nil { t.Fatal(err) } if err := os.Link("testdata/testcharts/compressedchart-0.3.0.tgz", filepath.Join(dir, "compressedchart-0.3.0.tgz")); err != nil { t.Fatal(err) } c.ParseFlags([]string{"--merge", destIndex}) if err := c.RunE(c, []string{dir}); err != nil { t.Error(err) } index, err = repo.LoadIndexFile(destIndex) if err != nil { t.Fatal(err) } if len(index.Entries) != 2 { t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries) } vs = index.Entries["compressedchart"] if len(vs) != 3 { t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs) } expectedVersion = "0.3.0" if vs[0].Version != expectedVersion { t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) } }
func TestDependencyBuildCmd(t *testing.T) { oldhome := helmHome hh, err := tempHelmHome(t) if err != nil { t.Fatal(err) } helmHome = hh defer func() { os.RemoveAll(hh) helmHome = oldhome }() srv := repotest.NewServer(hh) defer srv.Stop() _, err = srv.CopyCharts("testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } chartname := "depbuild" if err := createTestingChart(hh, chartname, srv.URL()); err != nil { t.Fatal(err) } out := bytes.NewBuffer(nil) dbc := &dependencyBuildCmd{out: out} dbc.helmhome = helmpath.Home(hh) dbc.chartpath = filepath.Join(hh, chartname) // In the first pass, we basically want the same results as an update. if err := dbc.run(); err != nil { output := out.String() t.Logf("Output: %s", output) t.Fatal(err) } output := out.String() if !strings.Contains(output, `update from the "test" chart repository`) { t.Errorf("Repo did not get updated\n%s", output) } // Make sure the actual file got downloaded. expect := filepath.Join(hh, chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } // In the second pass, we want to remove the chart's request dependency, // then see if it restores from the lock. lockfile := filepath.Join(hh, chartname, "requirements.lock") if _, err := os.Stat(lockfile); err != nil { t.Fatal(err) } if err := os.RemoveAll(expect); err != nil { t.Fatal(err) } if err := dbc.run(); err != nil { output := out.String() t.Logf("Output: %s", output) t.Fatal(err) } // Now repeat the test that the dependency exists. expect = filepath.Join(hh, chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } // Make sure that build is also fetching the correct version. hash, err := provenance.DigestFile(expect) if err != nil { t.Fatal(err) } i, err := repo.LoadIndexFile(dbc.helmhome.CacheIndex("test")) if err != nil { t.Fatal(err) } reqver := i.Entries["reqtest"][0] if h := reqver.Digest; h != hash { t.Errorf("Failed hash match: expected %s, got %s", hash, h) } if v := reqver.Version; v != "0.1.0" { t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v) } }