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