func TestRepoAdd(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, "OK") })) helmHome, _ = ioutil.TempDir("", "helm_home") defer os.Remove(helmHome) os.Mkdir(filepath.Join(helmHome, repositoryDir), 0755) os.Mkdir(cacheDirectory(), 0755) if err := ioutil.WriteFile(repositoriesFile(), []byte("example-repo: http://exampleurl.com"), 0666); err != nil { t.Errorf("%#v", err) } if err := addRepository(testName, ts.URL); err != nil { t.Errorf("%s", err) } f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { t.Errorf("%s", err) } _, ok := f.Repositories[testName] if !ok { t.Errorf("%s was not successfully inserted into %s", testName, repositoriesFile()) } if err := insertRepoLine(testName, ts.URL); err == nil { t.Errorf("Duplicate repository name was added") } }
func TestRepoAdd(t *testing.T) { ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*") if err != nil { t.Fatal(err) } oldhome := homePath() helmHome = thome hh := helmpath.Home(thome) defer func() { ts.Stop() helmHome = oldhome os.Remove(thome) }() if err := ensureTestHome(hh, t); err != nil { t.Fatal(err) } if err := addRepository(testName, ts.URL(), hh); err != nil { t.Error(err) } f, err := repo.LoadRepositoriesFile(hh.RepositoryFile()) if err != nil { t.Error(err) } if !f.Has(testName) { t.Errorf("%s was not successfully inserted into %s", testName, hh.RepositoryFile()) } }
func fetchChart(pname string) error { f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } // get download url u, err := mapRepoArg(pname, f.Repositories) if err != nil { return err } resp, err := http.Get(u.String()) if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("Failed to fetch %s : %s", u.String(), resp.Status) } defer resp.Body.Close() if untarFile { return chartutil.Expand(untarDir, resp.Body) } p := strings.Split(u.String(), "/") return saveChartFile(p[len(p)-1], resp.Body) }
// hasAllRepos ensures that all of the referenced deps are in the local repo cache. func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error { rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) if err != nil { return err } repos := rf.Repositories // Verify that all repositories referenced in the deps are actually known // by Helm. missing := []string{} for _, dd := range deps { found := false if dd.Repository == "" { found = true } else { for _, repo := range repos { if urlsAreEqual(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { found = true } } } if !found { missing = append(missing, dd.Repository) } } if len(missing) > 0 { return fmt.Errorf("no repository definition for %s. Try 'helm repo add'", strings.Join(missing, ", ")) } return nil }
// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file. func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, error) { rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) if err != nil { return nil, err } repos := rf.Repositories reposMap := make(map[string]string) // Verify that all repositories referenced in the deps are actually known // by Helm. missing := []string{} for _, dd := range deps { found := false for _, repo := range repos { if urlsAreEqual(repo.URL, dd.Repository) { found = true reposMap[dd.Name] = repo.Name break } } if !found { missing = append(missing, dd.Repository) } } if len(missing) > 0 { return nil, fmt.Errorf("no repository definition for %s. Try 'helm repo add'", strings.Join(missing, ", ")) } return reposMap, nil }
// 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 }
// ensureHome checks to see if $HELM_HOME exists // // If $HELM_HOME does not exist, this function will create it. func ensureHome(home helmpath.Home, out io.Writer) error { configDirectories := []string{home.String(), home.Repository(), home.Cache(), home.LocalRepository()} for _, p := range configDirectories { if fi, err := os.Stat(p); err != nil { fmt.Fprintf(out, "Creating %s \n", p) if err := os.MkdirAll(p, 0755); err != nil { return fmt.Errorf("Could not create %s: %s", p, err) } } else if !fi.IsDir() { return fmt.Errorf("%s must be a directory", p) } } repoFile := home.RepositoryFile() if fi, err := os.Stat(repoFile); err != nil { fmt.Fprintf(out, "Creating %s \n", repoFile) r := repo.NewRepoFile() r.Add(&repo.Entry{ Name: stableRepository, URL: stableRepositoryURL, Cache: "stable-index.yaml", }, &repo.Entry{ Name: localRepository, URL: localRepositoryURL, Cache: "local-index.yaml", }) if err := r.WriteFile(repoFile, 0644); err != nil { return err } cif := home.CacheIndex(stableRepository) if err := repo.DownloadIndexFile(stableRepository, stableRepositoryURL, cif); err != nil { fmt.Fprintf(out, "WARNING: Failed to download %s: %s (run 'helm repo update')\n", stableRepository, err) } } else if fi.IsDir() { return fmt.Errorf("%s must be a file, not a directory", repoFile) } if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate { fmt.Fprintln(out, "Updating repository file format...") if err := r.WriteFile(repoFile, 0644); err != nil { return err } } localRepoIndexFile := home.LocalRepository(localRepoIndexFilePath) if fi, err := os.Stat(localRepoIndexFile); err != nil { fmt.Fprintf(out, "Creating %s \n", localRepoIndexFile) i := repo.NewIndexFile() if err := i.WriteFile(localRepoIndexFile, 0644); err != nil { return err } //TODO: take this out and replace with helm update functionality os.Symlink(localRepoIndexFile, home.CacheIndex("local")) } else if fi.IsDir() { return fmt.Errorf("%s must be a file, not a directory", localRepoIndexFile) } fmt.Fprintf(out, "$HELM_HOME has been configured at %s.\n", helmHome) return nil }
// ResolveChartRef resolves a chart reference to a URL. // // A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path. func (c *ChartDownloader) ResolveChartRef(ref string) (*url.URL, error) { // See if it's already a full URL. 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.Split(ref, "/") if len(p) > 1 { if baseURL, ok := r.Repositories[p[0]]; ok { if !strings.HasSuffix(baseURL, "/") { baseURL = baseURL + "/" } return url.ParseRequestURI(baseURL + strings.Join(p[1:], "/")) } return u, fmt.Errorf("No such repo: %s", p[0]) } return u, fmt.Errorf("Invalid chart url format: %s", ref) }
func TestRepoRemove(t *testing.T) { home := createTmpHome() helmHome = home if err := ensureHome(); err != nil { t.Errorf("%s", err) } if err := removeRepoLine(testName); err == nil { t.Errorf("Expected error removing %s, but did not get one.", testName) } if err := insertRepoLine(testName, testURL); err != nil { t.Errorf("%s", err) } if err := removeRepoLine(testName); err != nil { t.Errorf("Error removing %s from repositories", testName) } f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { t.Errorf("%s", err) } if _, ok := f.Repositories[testName]; ok { t.Errorf("%s was not successfully removed from repositories list", testName) } }
// UpdateRepositories updates all of the local repos to the latest. func (m *Manager) UpdateRepositories() error { rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) if err != nil { return err } repos := rf.Repositories if len(repos) > 0 { // This prints warnings straight to out. m.parallelRepoUpdate(repos) } return nil }
// ensureTestHome creates a home directory like ensureHome, but without remote references. // // t is used only for logging. func ensureTestHome(home helmpath.Home, t *testing.T) error { configDirectories := []string{home.String(), home.Repository(), home.Cache(), home.LocalRepository(), home.Plugins(), home.Starters()} for _, p := range configDirectories { if fi, err := os.Stat(p); err != nil { if err := os.MkdirAll(p, 0755); err != nil { return fmt.Errorf("Could not create %s: %s", p, err) } } else if !fi.IsDir() { return fmt.Errorf("%s must be a directory", p) } } repoFile := home.RepositoryFile() if fi, err := os.Stat(repoFile); err != nil { rf := repo.NewRepoFile() rf.Add(&repo.Entry{ Name: "charts", URL: "http://example.com/foo", Cache: "charts-index.yaml", }, &repo.Entry{ Name: "local", URL: "http://localhost.com:7743/foo", Cache: "local-index.yaml", }) if err := rf.WriteFile(repoFile, 0644); err != nil { return err } } else if fi.IsDir() { return fmt.Errorf("%s must be a file, not a directory", repoFile) } if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate { t.Log("Updating repository file format...") if err := r.WriteFile(repoFile, 0644); err != nil { return err } } localRepoIndexFile := home.LocalRepository(localRepoIndexFilePath) if fi, err := os.Stat(localRepoIndexFile); err != nil { i := repo.NewIndexFile() if err := i.WriteFile(localRepoIndexFile, 0644); err != nil { return err } //TODO: take this out and replace with helm update functionality os.Symlink(localRepoIndexFile, home.CacheIndex("local")) } else if fi.IsDir() { return fmt.Errorf("%s must be a file, not a directory", localRepoIndexFile) } t.Logf("$HELM_HOME has been configured at %s.\n", helmHome) return nil }
func (u *repoUpdateCmd) run() error { f, err := repo.LoadRepositoriesFile(u.home.RepositoryFile()) if err != nil { return err } if len(f.Repositories) == 0 { return errors.New("no repositories found. You must add one before updating") } u.update(f.Repositories, flagDebug, u.out, u.home) return nil }
func runUpdate(cmd *cobra.Command, args []string) error { f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } if len(f.Repositories) == 0 { return errors.New("no repositories found. You must add one before updating") } updateCharts(f.Repositories, verboseUpdate) return nil }
// 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 updateRepoLine(name, url string, home helmpath.Home) error { cif := home.CacheIndex(name) f, err := repo.LoadRepositoriesFile(home.RepositoryFile()) if err != nil { return err } f.Update(&repo.Entry{ Name: name, URL: url, Cache: filepath.Base(cif), }) return f.WriteFile(home.RepositoryFile(), 0666) }
func runRepoList(cmd *cobra.Command, args []string) error { f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } if len(f.Repositories) == 0 { return errors.New("no repositories to show") } table := uitable.New() table.MaxColWidth = 50 table.AddRow("NAME", "URL") for k, v := range f.Repositories { table.AddRow(k, v) } fmt.Println(table) return nil }
func insertRepoLine(name, url string, home helmpath.Home) error { cif := home.CacheIndex(name) f, err := repo.LoadRepositoriesFile(home.RepositoryFile()) if err != nil { return err } if f.Has(name) { return fmt.Errorf("The repository name you provided (%s) already exists. Please specify a different name.", name) } f.Add(&repo.Entry{ Name: name, URL: url, Cache: filepath.Base(cif), }) return f.WriteFile(home.RepositoryFile(), 0644) }
func (a *repoListCmd) run() error { f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile()) if err != nil { return err } if len(f.Repositories) == 0 { return errors.New("no repositories to show") } table := uitable.New() table.MaxColWidth = 50 table.AddRow("NAME", "URL") for _, re := range f.Repositories { table.AddRow(re.Name, re.URL) } fmt.Fprintln(a.out, table) return nil }
func (a *repoListCmd) run() error { f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } if len(f.Repositories) == 0 { return errors.New("no repositories to show") } table := uitable.New() table.MaxColWidth = 50 table.AddRow("NAME", "URL") for k, v := range f.Repositories { table.AddRow(k, v) } fmt.Println(table) return nil }
func insertRepoLine(name, url string) error { f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } _, ok := f.Repositories[name] if ok { return fmt.Errorf("The repository name you provided (%s) already exists. Please specify a different name.", name) } if f.Repositories == nil { f.Repositories = make(map[string]string) } f.Repositories[name] = url b, _ := yaml.Marshal(&f.Repositories) return ioutil.WriteFile(repositoriesFile(), b, 0666) }
func TestRepoRemove(t *testing.T) { testURL := "https://test-url.com" b := bytes.NewBuffer(nil) home, err := tempHelmHome(t) if err != nil { t.Fatal(err) } defer os.Remove(home) hh := helmpath.Home(home) if err := removeRepoLine(b, testName, hh); err == nil { t.Errorf("Expected error removing %s, but did not get one.", testName) } if err := insertRepoLine(testName, testURL, hh); err != nil { t.Error(err) } mf, _ := os.Create(hh.CacheIndex(testName)) mf.Close() b.Reset() if err := removeRepoLine(b, testName, hh); err != nil { t.Errorf("Error removing %s from repositories", testName) } if !strings.Contains(b.String(), "has been removed") { t.Errorf("Unexpected output: %s", b.String()) } if _, err := os.Stat(hh.CacheIndex(testName)); err == nil { t.Errorf("Error cache file was not removed for repository %s", testName) } f, err := repo.LoadRepositoriesFile(hh.RepositoryFile()) if err != nil { t.Error(err) } if f.Has(testName) { t.Errorf("%s was not successfully removed from repositories list", testName) } }
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 removeRepoLine(out io.Writer, name string, home helmpath.Home) error { repoFile := home.RepositoryFile() r, err := repo.LoadRepositoriesFile(repoFile) if err != nil { return err } if !r.Remove(name) { return fmt.Errorf("no repo named %q found", name) } if err := r.WriteFile(repoFile, 0644); err != nil { return err } if err := removeRepoCache(name, home); err != nil { return err } fmt.Fprintf(out, "%q has been removed from your repositories\n", name) return nil }
func removeRepoLine(name string) error { r, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { return err } _, ok := r.Repositories[name] if ok { delete(r.Repositories, name) b, err := yaml.Marshal(&r.Repositories) if err != nil { return err } if err := ioutil.WriteFile(repositoriesFile(), b, 0666); err != nil { return err } } else { return fmt.Errorf("The repository, %s, does not exist in your repositories list", name) } return nil }
func TestRepoRemove(t *testing.T) { testURL := "https://test-url.com" home := createTmpHome() helmHome = home if err := ensureHome(); err != nil { t.Errorf("%s", err) } if err := removeRepoLine(testName); err == nil { t.Errorf("Expected error removing %s, but did not get one.", testName) } if err := insertRepoLine(testName, testURL); err != nil { t.Errorf("%s", err) } mf, _ := os.Create(cacheIndexFile(testName)) mf.Close() if err := removeRepoLine(testName); err != nil { t.Errorf("Error removing %s from repositories", testName) } if _, err := os.Stat(cacheIndexFile(testName)); err == nil { t.Errorf("Error cache file was not removed for repository %s", testName) } f, err := repo.LoadRepositoriesFile(repositoriesFile()) if err != nil { t.Errorf("%s", err) } if _, ok := f.Repositories[testName]; ok { t.Errorf("%s was not successfully removed from repositories list", testName) } }