github.com/latiif/helm@v2.15.0+incompatible/cmd/helm/repo_add.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  package main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"syscall"
    24  	"time"
    25  
    26  	"golang.org/x/crypto/ssh/terminal"
    27  
    28  	"github.com/gofrs/flock"
    29  	"github.com/spf13/cobra"
    30  
    31  	"k8s.io/helm/pkg/getter"
    32  	"k8s.io/helm/pkg/helm/helmpath"
    33  	"k8s.io/helm/pkg/repo"
    34  )
    35  
    36  type repoAddCmd struct {
    37  	name     string
    38  	url      string
    39  	username string
    40  	password string
    41  	home     helmpath.Home
    42  	noupdate bool
    43  
    44  	certFile string
    45  	keyFile  string
    46  	caFile   string
    47  
    48  	out io.Writer
    49  }
    50  
    51  func newRepoAddCmd(out io.Writer) *cobra.Command {
    52  	add := &repoAddCmd{out: out}
    53  
    54  	cmd := &cobra.Command{
    55  		Use:   "add [flags] [NAME] [URL]",
    56  		Short: "Add a chart repository",
    57  		RunE: func(cmd *cobra.Command, args []string) error {
    58  			if err := checkArgsLength(len(args), "name for the chart repository", "the url of the chart repository"); err != nil {
    59  				return err
    60  			}
    61  
    62  			add.name = args[0]
    63  			add.url = args[1]
    64  			add.home = settings.Home
    65  
    66  			return add.run()
    67  		},
    68  	}
    69  
    70  	f := cmd.Flags()
    71  	f.StringVar(&add.username, "username", "", "Chart repository username")
    72  	f.StringVar(&add.password, "password", "", "Chart repository password")
    73  	f.BoolVar(&add.noupdate, "no-update", false, "Raise error if repo is already registered")
    74  	f.StringVar(&add.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
    75  	f.StringVar(&add.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
    76  	f.StringVar(&add.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
    77  
    78  	return cmd
    79  }
    80  
    81  func (a *repoAddCmd) run() error {
    82  	if a.username != "" && a.password == "" {
    83  		fmt.Fprint(a.out, "Password:")
    84  		password, err := readPassword()
    85  		fmt.Fprintln(a.out)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		a.password = password
    90  	}
    91  
    92  	if err := addRepository(a.name, a.url, a.username, a.password, a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil {
    93  		return err
    94  	}
    95  	fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name)
    96  	return nil
    97  }
    98  
    99  func readPassword() (string, error) {
   100  	password, err := terminal.ReadPassword(int(syscall.Stdin))
   101  	if err != nil {
   102  		return "", err
   103  	}
   104  	return string(password), nil
   105  }
   106  
   107  func addRepository(name, url, username, password string, home helmpath.Home, certFile, keyFile, caFile string, noUpdate bool) error {
   108  	f, err := repo.LoadRepositoriesFile(home.RepositoryFile())
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	if noUpdate && f.Has(name) {
   114  		return fmt.Errorf("repository name (%s) already exists, please specify a different name", name)
   115  	}
   116  
   117  	cif := home.CacheIndex(name)
   118  	c := repo.Entry{
   119  		Name:     name,
   120  		Cache:    cif,
   121  		URL:      url,
   122  		Username: username,
   123  		Password: password,
   124  		CertFile: certFile,
   125  		KeyFile:  keyFile,
   126  		CAFile:   caFile,
   127  	}
   128  
   129  	r, err := repo.NewChartRepository(&c, getter.All(settings))
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	if err := r.DownloadIndexFile(home.Cache()); err != nil {
   135  		return fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error())
   136  	}
   137  
   138  	// Lock the repository file for concurrent goroutines or processes synchronization
   139  	fileLock := flock.New(home.RepositoryFile())
   140  	lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   141  	defer cancel()
   142  	locked, err := fileLock.TryLockContext(lockCtx, time.Second)
   143  	if err == nil && locked {
   144  		defer fileLock.Unlock()
   145  	}
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	// Re-read the repositories file before updating it as its content may have been changed
   151  	// by a concurrent execution after the first read and before being locked
   152  	f, err = repo.LoadRepositoriesFile(home.RepositoryFile())
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	f.Update(&c)
   158  
   159  	return f.WriteFile(home.RepositoryFile(), 0644)
   160  }