github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/action/pull.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 action
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	"github.com/pkg/errors"
    27  
    28  	"github.com/stefanmcshane/helm/pkg/chartutil"
    29  	"github.com/stefanmcshane/helm/pkg/cli"
    30  	"github.com/stefanmcshane/helm/pkg/downloader"
    31  	"github.com/stefanmcshane/helm/pkg/getter"
    32  	"github.com/stefanmcshane/helm/pkg/registry"
    33  	"github.com/stefanmcshane/helm/pkg/repo"
    34  )
    35  
    36  // Pull is the action for checking a given release's information.
    37  //
    38  // It provides the implementation of 'helm pull'.
    39  type Pull struct {
    40  	ChartPathOptions
    41  
    42  	Settings *cli.EnvSettings // TODO: refactor this out of pkg/action
    43  
    44  	Devel       bool
    45  	Untar       bool
    46  	VerifyLater bool
    47  	UntarDir    string
    48  	DestDir     string
    49  	cfg         *Configuration
    50  }
    51  
    52  type PullOpt func(*Pull)
    53  
    54  func WithConfig(cfg *Configuration) PullOpt {
    55  	return func(p *Pull) {
    56  		p.cfg = cfg
    57  	}
    58  }
    59  
    60  // NewPull creates a new Pull object.
    61  func NewPull() *Pull {
    62  	return NewPullWithOpts()
    63  }
    64  
    65  // NewPullWithOpts creates a new pull, with configuration options.
    66  func NewPullWithOpts(opts ...PullOpt) *Pull {
    67  	p := &Pull{}
    68  	for _, fn := range opts {
    69  		fn(p)
    70  	}
    71  
    72  	return p
    73  }
    74  
    75  // Run executes 'helm pull' against the given release.
    76  func (p *Pull) Run(chartRef string) (string, error) {
    77  	var out strings.Builder
    78  
    79  	c := downloader.ChartDownloader{
    80  		Out:     &out,
    81  		Keyring: p.Keyring,
    82  		Verify:  downloader.VerifyNever,
    83  		Getters: getter.All(p.Settings),
    84  		Options: []getter.Option{
    85  			getter.WithBasicAuth(p.Username, p.Password),
    86  			getter.WithPassCredentialsAll(p.PassCredentialsAll),
    87  			getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
    88  			getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
    89  		},
    90  		RegistryClient:   p.cfg.RegistryClient,
    91  		RepositoryConfig: p.Settings.RepositoryConfig,
    92  		RepositoryCache:  p.Settings.RepositoryCache,
    93  	}
    94  
    95  	if registry.IsOCI(chartRef) {
    96  		c.Options = append(c.Options,
    97  			getter.WithRegistryClient(p.cfg.RegistryClient))
    98  	}
    99  
   100  	if p.Verify {
   101  		c.Verify = downloader.VerifyAlways
   102  	} else if p.VerifyLater {
   103  		c.Verify = downloader.VerifyLater
   104  	}
   105  
   106  	// If untar is set, we fetch to a tempdir, then untar and copy after
   107  	// verification.
   108  	dest := p.DestDir
   109  	if p.Untar {
   110  		var err error
   111  		dest, err = ioutil.TempDir("", "helm-")
   112  		if err != nil {
   113  			return out.String(), errors.Wrap(err, "failed to untar")
   114  		}
   115  		defer os.RemoveAll(dest)
   116  	}
   117  
   118  	if p.RepoURL != "" {
   119  		chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings))
   120  		if err != nil {
   121  			return out.String(), err
   122  		}
   123  		chartRef = chartURL
   124  	}
   125  
   126  	saved, v, err := c.DownloadTo(chartRef, p.Version, dest)
   127  	if err != nil {
   128  		return out.String(), err
   129  	}
   130  
   131  	if p.Verify {
   132  		for name := range v.SignedBy.Identities {
   133  			fmt.Fprintf(&out, "Signed by: %v\n", name)
   134  		}
   135  		fmt.Fprintf(&out, "Using Key With Fingerprint: %X\n", v.SignedBy.PrimaryKey.Fingerprint)
   136  		fmt.Fprintf(&out, "Chart Hash Verified: %s\n", v.FileHash)
   137  	}
   138  
   139  	// After verification, untar the chart into the requested directory.
   140  	if p.Untar {
   141  		ud := p.UntarDir
   142  		if !filepath.IsAbs(ud) {
   143  			ud = filepath.Join(p.DestDir, ud)
   144  		}
   145  		// Let udCheck to check conflict file/dir without replacing ud when untarDir is the current directory(.).
   146  		udCheck := ud
   147  		if udCheck == "." {
   148  			_, udCheck = filepath.Split(chartRef)
   149  		} else {
   150  			_, chartName := filepath.Split(chartRef)
   151  			udCheck = filepath.Join(udCheck, chartName)
   152  		}
   153  
   154  		if _, err := os.Stat(udCheck); err != nil {
   155  			if err := os.MkdirAll(udCheck, 0755); err != nil {
   156  				return out.String(), errors.Wrap(err, "failed to untar (mkdir)")
   157  			}
   158  
   159  		} else {
   160  			return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck)
   161  		}
   162  
   163  		return out.String(), chartutil.ExpandFile(ud, saved)
   164  	}
   165  	return out.String(), nil
   166  }