...

Source file src/helm.sh/helm/v3/pkg/action/package.go

Documentation: helm.sh/helm/v3/pkg/action

     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  	"bufio"
    21  	"fmt"
    22  	"os"
    23  	"syscall"
    24  
    25  	"github.com/Masterminds/semver/v3"
    26  	"github.com/pkg/errors"
    27  	"golang.org/x/term"
    28  
    29  	"helm.sh/helm/v3/pkg/chart/loader"
    30  	"helm.sh/helm/v3/pkg/chartutil"
    31  	"helm.sh/helm/v3/pkg/provenance"
    32  )
    33  
    34  // Package is the action for packaging a chart.
    35  //
    36  // It provides the implementation of 'helm package'.
    37  type Package struct {
    38  	Sign             bool
    39  	Key              string
    40  	Keyring          string
    41  	PassphraseFile   string
    42  	Version          string
    43  	AppVersion       string
    44  	Destination      string
    45  	DependencyUpdate bool
    46  
    47  	RepositoryConfig string
    48  	RepositoryCache  string
    49  }
    50  
    51  // NewPackage creates a new Package object with the given configuration.
    52  func NewPackage() *Package {
    53  	return &Package{}
    54  }
    55  
    56  // Run executes 'helm package' against the given chart and returns the path to the packaged chart.
    57  func (p *Package) Run(path string, _ map[string]interface{}) (string, error) {
    58  	ch, err := loader.LoadDir(path)
    59  	if err != nil {
    60  		return "", err
    61  	}
    62  
    63  	// If version is set, modify the version.
    64  	if p.Version != "" {
    65  		ch.Metadata.Version = p.Version
    66  	}
    67  
    68  	if err := validateVersion(ch.Metadata.Version); err != nil {
    69  		return "", err
    70  	}
    71  
    72  	if p.AppVersion != "" {
    73  		ch.Metadata.AppVersion = p.AppVersion
    74  	}
    75  
    76  	if reqs := ch.Metadata.Dependencies; reqs != nil {
    77  		if err := CheckDependencies(ch, reqs); err != nil {
    78  			return "", err
    79  		}
    80  	}
    81  
    82  	var dest string
    83  	if p.Destination == "." {
    84  		// Save to the current working directory.
    85  		dest, err = os.Getwd()
    86  		if err != nil {
    87  			return "", err
    88  		}
    89  	} else {
    90  		// Otherwise save to set destination
    91  		dest = p.Destination
    92  	}
    93  
    94  	name, err := chartutil.Save(ch, dest)
    95  	if err != nil {
    96  		return "", errors.Wrap(err, "failed to save")
    97  	}
    98  
    99  	if p.Sign {
   100  		err = p.Clearsign(name)
   101  	}
   102  
   103  	return name, err
   104  }
   105  
   106  // validateVersion Verify that version is a Version, and error out if it is not.
   107  func validateVersion(ver string) error {
   108  	if _, err := semver.NewVersion(ver); err != nil {
   109  		return err
   110  	}
   111  	return nil
   112  }
   113  
   114  // Clearsign signs a chart
   115  func (p *Package) Clearsign(filename string) error {
   116  	// Load keyring
   117  	signer, err := provenance.NewFromKeyring(p.Keyring, p.Key)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	passphraseFetcher := promptUser
   123  	if p.PassphraseFile != "" {
   124  		passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin)
   125  		if err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	if err := signer.DecryptKey(passphraseFetcher); err != nil {
   131  		return err
   132  	}
   133  
   134  	sig, err := signer.ClearSign(filename)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	return os.WriteFile(filename+".prov", []byte(sig), 0644)
   140  }
   141  
   142  // promptUser implements provenance.PassphraseFetcher
   143  func promptUser(name string) ([]byte, error) {
   144  	fmt.Printf("Password for key %q >  ", name)
   145  	// syscall.Stdin is not an int in all environments and needs to be coerced
   146  	// into one there (e.g., Windows)
   147  	pw, err := term.ReadPassword(int(syscall.Stdin))
   148  	fmt.Println()
   149  	return pw, err
   150  }
   151  
   152  func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) {
   153  	file, err := openPassphraseFile(passphraseFile, stdin)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	defer file.Close()
   158  
   159  	reader := bufio.NewReader(file)
   160  	passphrase, _, err := reader.ReadLine()
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	return func(_ string) ([]byte, error) {
   165  		return passphrase, nil
   166  	}, nil
   167  }
   168  
   169  func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) {
   170  	if passphraseFile == "-" {
   171  		stat, err := stdin.Stat()
   172  		if err != nil {
   173  			return nil, err
   174  		}
   175  		if (stat.Mode() & os.ModeNamedPipe) == 0 {
   176  			return nil, errors.New("specified reading passphrase from stdin, without input on stdin")
   177  		}
   178  		return stdin, nil
   179  	}
   180  	return os.Open(passphraseFile)
   181  }
   182  

View as plain text