github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/cmd/tools/helm/repo_update.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  /*
    18  NOTICE: This file's 'package' and some functionality has been modified / removed to fit within Jackal's package structure.
    19  */
    20  
    21  // Package helm is a copy of the main package from helm to include a subset of the helm CLI in Jackal
    22  package helm
    23  
    24  import (
    25  	"fmt"
    26  	"io"
    27  	"sync"
    28  
    29  	"github.com/pkg/errors"
    30  	"github.com/spf13/cobra"
    31  
    32  	"helm.sh/helm/v3/cmd/helm/require"
    33  	"helm.sh/helm/v3/pkg/getter"
    34  	"helm.sh/helm/v3/pkg/repo"
    35  )
    36  
    37  const updateDesc = `
    38  Update gets the latest information about charts from the respective chart repositories.
    39  Information is cached locally, where it is used by commands like 'helm search'.
    40  
    41  You can optionally specify a list of repositories you want to update.
    42  	$ jackal tools helm repo update <repo_name> ...
    43  To update all the repositories, use 'jackal tools helm repo update'.
    44  `
    45  
    46  var errNoRepositories = errors.New("no repositories found. You must add one before updating")
    47  
    48  type repoUpdateOptions struct {
    49  	update               func([]*repo.ChartRepository, io.Writer, bool) error
    50  	repoFile             string
    51  	repoCache            string
    52  	names                []string
    53  	failOnRepoUpdateFail bool
    54  }
    55  
    56  func newRepoUpdateCmd(out io.Writer) *cobra.Command {
    57  	o := &repoUpdateOptions{update: updateCharts}
    58  
    59  	cmd := &cobra.Command{
    60  		Use:     "update [REPO1 [REPO2 ...]]",
    61  		Aliases: []string{"up"},
    62  		Short:   "update information of available charts locally from chart repositories",
    63  		Long:    updateDesc,
    64  		Args:    require.MinimumNArgs(0),
    65  		ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    66  			return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp
    67  		},
    68  		RunE: func(_ *cobra.Command, args []string) error {
    69  			o.repoFile = settings.RepositoryConfig
    70  			o.repoCache = settings.RepositoryCache
    71  			o.names = args
    72  			return o.run(out)
    73  		},
    74  	}
    75  
    76  	f := cmd.Flags()
    77  
    78  	// Adding this flag for Helm 3 as stop gap functionality for https://github.com/helm/helm/issues/10016.
    79  	// This should be deprecated in Helm 4 by update to the behaviour of `helm repo update` command.
    80  	f.BoolVar(&o.failOnRepoUpdateFail, "fail-on-repo-update-fail", false, "update fails if any of the repository updates fail")
    81  
    82  	return cmd
    83  }
    84  
    85  func (o *repoUpdateOptions) run(out io.Writer) error {
    86  	f, err := repo.LoadFile(o.repoFile)
    87  	switch {
    88  	case isNotExist(err):
    89  		return errNoRepositories
    90  	case err != nil:
    91  		return errors.Wrapf(err, "failed loading file: %s", o.repoFile)
    92  	case len(f.Repositories) == 0:
    93  		return errNoRepositories
    94  	}
    95  
    96  	var repos []*repo.ChartRepository
    97  	updateAllRepos := len(o.names) == 0
    98  
    99  	if !updateAllRepos {
   100  		// Fail early if the user specified an invalid repo to update
   101  		if err := checkRequestedRepos(o.names, f.Repositories); err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	for _, cfg := range f.Repositories {
   107  		if updateAllRepos || isRepoRequested(cfg.Name, o.names) {
   108  			r, err := repo.NewChartRepository(cfg, getter.All(settings))
   109  			if err != nil {
   110  				return err
   111  			}
   112  			if o.repoCache != "" {
   113  				r.CachePath = o.repoCache
   114  			}
   115  			repos = append(repos, r)
   116  		}
   117  	}
   118  
   119  	return o.update(repos, out, o.failOnRepoUpdateFail)
   120  }
   121  
   122  func updateCharts(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
   123  	fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...")
   124  	var wg sync.WaitGroup
   125  	var repoFailList []string
   126  	for _, re := range repos {
   127  		wg.Add(1)
   128  		go func(re *repo.ChartRepository) {
   129  			defer wg.Done()
   130  			if _, err := re.DownloadIndexFile(); err != nil {
   131  				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)
   132  				repoFailList = append(repoFailList, re.Config.URL)
   133  			} else {
   134  				fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name)
   135  			}
   136  		}(re)
   137  	}
   138  	wg.Wait()
   139  
   140  	if len(repoFailList) > 0 && failOnRepoUpdateFail {
   141  		return fmt.Errorf("failed to update the following repositories: %s",
   142  			repoFailList)
   143  	}
   144  
   145  	fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈")
   146  	return nil
   147  }
   148  
   149  func checkRequestedRepos(requestedRepos []string, validRepos []*repo.Entry) error {
   150  	for _, requestedRepo := range requestedRepos {
   151  		found := false
   152  		for _, repo := range validRepos {
   153  			if requestedRepo == repo.Name {
   154  				found = true
   155  				break
   156  			}
   157  		}
   158  		if !found {
   159  			return errors.Errorf("no repositories found matching '%s'.  Nothing will be updated", requestedRepo)
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  func isRepoRequested(repoName string, requestedRepos []string) bool {
   166  	for _, requestedRepo := range requestedRepos {
   167  		if repoName == requestedRepo {
   168  			return true
   169  		}
   170  	}
   171  	return false
   172  }