diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index e07eb8685..41e4efedf 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -44,12 +44,13 @@ var deprecatedRepos = map[string]string{ } type repoAddOptions struct { - name string - url string - username string - password string - forceUpdate bool - allowDeprecatedRepos bool + name string + url string + username string + password string + forceUpdate bool + allowDeprecatedRepos bool + hideValidationWarnings bool certFile string keyFile string @@ -91,6 +92,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the repository") f.BoolVar(&o.allowDeprecatedRepos, "allow-deprecated-repos", false, "by default, this command will not allow adding official repos that have been permanently deleted. This disables that behavior") + f.BoolVar(&o.hideValidationWarnings, "hide-validation-warnings", false, "hide validation warnings while indexing repository") return cmd } @@ -187,7 +189,12 @@ func (o *repoAddOptions) run(out io.Writer) error { if o.repoCache != "" { r.CachePath = o.repoCache } - if _, err := r.DownloadIndexFile(); err != nil { + if o.hideValidationWarnings { + _, err = r.DownloadIndexFileHideValidationWarnings() + } else { + _, err = r.DownloadIndexFile() + } + if err != nil { return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url) } diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index 23dca194a..1e67b66ee 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/v3/cmd/helm/require" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/repo" ) @@ -37,9 +36,10 @@ Information is cached locally, where it is used by commands like 'helm search'. var errNoRepositories = errors.New("no repositories found. You must add one before updating") type repoUpdateOptions struct { - update func([]*repo.ChartRepository, io.Writer) - repoFile string - repoCache string + hideValidationWarnings bool + update func([]*repo.ChartRepository, io.Writer, bool) + repoFile string + repoCache string } func newRepoUpdateCmd(out io.Writer) *cobra.Command { @@ -50,18 +50,21 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { Aliases: []string{"up"}, Short: "update information of available charts locally from chart repositories", Long: updateDesc, - Args: require.NoArgs, ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache - return o.run(out) + return o.run(out, args) }, } + + f := cmd.Flags() + f.BoolVar(&o.hideValidationWarnings, "hide-validation-warnings", false, "hide validation warnings while indexing repository") + return cmd } -func (o *repoUpdateOptions) run(out io.Writer) error { +func (o *repoUpdateOptions) run(out io.Writer, args []string) error { f, err := repo.LoadFile(o.repoFile) switch { case isNotExist(err): @@ -84,18 +87,24 @@ func (o *repoUpdateOptions) run(out io.Writer) error { repos = append(repos, r) } - o.update(repos, out) + o.update(repos, out, o.hideValidationWarnings) return nil } -func updateCharts(repos []*repo.ChartRepository, out io.Writer) { +func updateCharts(repos []*repo.ChartRepository, out io.Writer, hideValidationWarnings bool) { fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...") var wg sync.WaitGroup for _, re := range repos { wg.Add(1) go func(re *repo.ChartRepository) { defer wg.Done() - if _, err := re.DownloadIndexFile(); err != nil { + var err error + if hideValidationWarnings { + _, err = re.DownloadIndexFileHideValidationWarnings() + } else { + _, err = re.DownloadIndexFile() + } + if err != nil { fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err) } else { fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name) diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index 83ef24349..5f22f392c 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -35,7 +35,7 @@ func TestUpdateCmd(t *testing.T) { var out bytes.Buffer // Instead of using the HTTP updater, we provide our own for this test. // The TestUpdateCharts test verifies the HTTP behavior independently. - updater := func(repos []*repo.ChartRepository, out io.Writer) { + updater := func(repos []*repo.ChartRepository, out io.Writer, hideValidationWarnings bool) { for _, re := range repos { fmt.Fprintln(out, re.Config.Name) } @@ -44,7 +44,7 @@ func TestUpdateCmd(t *testing.T) { update: updater, repoFile: "testdata/repositories.yaml", } - if err := o.run(&out); err != nil { + if err := o.run(&out, []string{}); err != nil { t.Fatal(err) } @@ -71,7 +71,7 @@ func TestUpdateCustomCacheCmd(t *testing.T) { repoCache: cachePath, } b := ioutil.Discard - if err := o.run(b); err != nil { + if err := o.run(b, []string{}); err != nil { t.Fatal(err) } if _, err := os.Stat(filepath.Join(cachePath, "test-index.yaml")); err != nil { @@ -98,7 +98,7 @@ func TestUpdateCharts(t *testing.T) { } b := bytes.NewBuffer(nil) - updateCharts([]*repo.ChartRepository{r}, b) + updateCharts([]*repo.ChartRepository{r}, b, false) got := b.String() if strings.Contains(got, "Unable to get an update") { diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index ba692a2e7..9831d9d7f 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -63,14 +63,15 @@ Repositories are managed with 'helm repo' commands. const searchMaxScore = 25 type searchRepoOptions struct { - versions bool - regexp bool - devel bool - version string - maxColWidth uint - repoFile string - repoCacheDir string - outputFormat output.Format + versions bool + regexp bool + devel bool + hideValidationWarnings bool + version string + maxColWidth uint + repoFile string + repoCacheDir string + outputFormat output.Format } func newSearchRepoCmd(out io.Writer) *cobra.Command { @@ -91,6 +92,7 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command { f.BoolVarP(&o.regexp, "regexp", "r", false, "use regular expressions for searching repositories you have added") f.BoolVarP(&o.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line, for repositories you have added") f.BoolVar(&o.devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") + f.BoolVar(&o.hideValidationWarnings, "hide-validation-warnings", false, "hide validation warnings while indexing repository") f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added") f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") bindOutputFlag(cmd, &o.outputFormat) @@ -184,7 +186,12 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) { for _, re := range rf.Repositories { n := re.Name f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) - ind, err := repo.LoadIndexFile(f) + var ind *repo.IndexFile + if o.hideValidationWarnings { + ind, err = repo.LoadIndexFileHideValidationWarnings(f) + } else { + ind, err = repo.LoadIndexFile(f) + } if err != nil { warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n) warning("%s", err) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 09b94fd42..938b13b4b 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -115,6 +115,23 @@ func (r *ChartRepository) Load() error { // DownloadIndexFile fetches the index from a repository. func (r *ChartRepository) DownloadIndexFile() (string, error) { + return r.downloadIndexFile(false) +} + +func (r *ChartRepository) DownloadIndexFileHideValidationWarnings() (string, error) { + return r.downloadIndexFile(true) +} + +// Index generates an index for the chart repository and writes an index.yaml file. +func (r *ChartRepository) Index() error { + err := r.generateIndex() + if err != nil { + return err + } + return r.saveIndexFile() +} + +func (r *ChartRepository) downloadIndexFile(hideValidationWarnings bool) (string, error) { parsedURL, err := url.Parse(r.Config.URL) if err != nil { return "", err @@ -139,7 +156,7 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) { return "", err } - indexFile, err := loadIndex(index, r.Config.URL) + indexFile, err := loadIndex(index, r.Config.URL, hideValidationWarnings) if err != nil { return "", err } @@ -159,15 +176,6 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) { return fname, ioutil.WriteFile(fname, index, 0644) } -// Index generates an index for the chart repository and writes an index.yaml file. -func (r *ChartRepository) Index() error { - err := r.generateIndex() - if err != nil { - return err - } - return r.saveIndexFile() -} - func (r *ChartRepository) saveIndexFile() error { index, err := yaml.Marshal(r.IndexFile) if err != nil { diff --git a/pkg/repo/index.go b/pkg/repo/index.go index e86b17349..06cc66e03 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -106,7 +106,20 @@ func LoadIndexFile(path string) (*IndexFile, error) { if err != nil { return nil, err } - i, err := loadIndex(b, path) + i, err := loadIndex(b, path, false) + if err != nil { + return nil, errors.Wrapf(err, "error loading %s", path) + } + return i, nil +} + +// LoadIndexFile takes a file at the given path and returns an IndexFile object +func LoadIndexFileHideValidationWarnings(path string) (*IndexFile, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + i, err := loadIndex(b, path, true) if err != nil { return nil, errors.Wrapf(err, "error loading %s", path) } @@ -324,7 +337,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { // // The source parameter is only used for logging. // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails. -func loadIndex(data []byte, source string) (*IndexFile, error) { +func loadIndex(data []byte, source string, hideValidationWarnings bool) (*IndexFile, error) { i := &IndexFile{} if err := yaml.UnmarshalStrict(data, i); err != nil { return i, err @@ -336,7 +349,9 @@ func loadIndex(data []byte, source string) (*IndexFile, error) { cvs[idx].APIVersion = chart.APIVersionV1 } if err := cvs[idx].Validate(); err != nil { - log.Printf("skipping loading invalid entry for chart %q %q from %s: %s", name, cvs[idx].Version, source, err) + if !hideValidationWarnings { + log.Printf("skipping loading invalid entry for chart %q %q from %s: %s", name, cvs[idx].Version, source, err) + } cvs = append(cvs[:idx], cvs[idx+1:]...) } } diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 47f3c6b2e..1e8e1a9cd 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -147,7 +147,7 @@ func TestLoadIndex(t *testing.T) { // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. func TestLoadIndex_Duplicates(t *testing.T) { - if _, err := loadIndex([]byte(indexWithDuplicates), "indexWithDuplicates"); err == nil { + if _, err := loadIndex([]byte(indexWithDuplicates), "indexWithDuplicates", false); err == nil { t.Errorf("Expected an error when duplicate entries are present") } }