github.com/canthefason/helm@v2.2.1-0.20170221172616-16b043b8d505+incompatible/cmd/helm/package.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"syscall"
    27  
    28  	"github.com/Masterminds/semver"
    29  	"github.com/spf13/cobra"
    30  	"golang.org/x/crypto/ssh/terminal"
    31  
    32  	"k8s.io/helm/cmd/helm/helmpath"
    33  	"k8s.io/helm/pkg/chartutil"
    34  	"k8s.io/helm/pkg/proto/hapi/chart"
    35  	"k8s.io/helm/pkg/provenance"
    36  	"k8s.io/helm/pkg/repo"
    37  )
    38  
    39  const packageDesc = `
    40  This command packages a chart into a versioned chart archive file. If a path
    41  is given, this will look at that path for a chart (which must contain a
    42  Chart.yaml file) and then package that directory.
    43  
    44  If no path is given, this will look in the present working directory for a
    45  Chart.yaml file, and (if found) build the current directory into a chart.
    46  
    47  Versioned chart archives are used by Helm package repositories.
    48  `
    49  
    50  type packageCmd struct {
    51  	save    bool
    52  	sign    bool
    53  	path    string
    54  	key     string
    55  	keyring string
    56  	version string
    57  	out     io.Writer
    58  	home    helmpath.Home
    59  }
    60  
    61  func newPackageCmd(out io.Writer) *cobra.Command {
    62  	pkg := &packageCmd{
    63  		out: out,
    64  	}
    65  
    66  	cmd := &cobra.Command{
    67  		Use:   "package [flags] [CHART_PATH] [...]",
    68  		Short: "package a chart directory into a chart archive",
    69  		Long:  packageDesc,
    70  		RunE: func(cmd *cobra.Command, args []string) error {
    71  			pkg.home = helmpath.Home(homePath())
    72  			if len(args) == 0 {
    73  				return fmt.Errorf("This command needs at least one argument, the path to the chart.")
    74  			}
    75  			if pkg.sign {
    76  				if pkg.key == "" {
    77  					return errors.New("--key is required for signing a package")
    78  				}
    79  				if pkg.keyring == "" {
    80  					return errors.New("--keyring is required for signing a package")
    81  				}
    82  			}
    83  			for i := 0; i < len(args); i++ {
    84  				pkg.path = args[i]
    85  				if err := pkg.run(cmd, args); err != nil {
    86  					return err
    87  				}
    88  			}
    89  			return nil
    90  		},
    91  	}
    92  
    93  	f := cmd.Flags()
    94  	f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
    95  	f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
    96  	f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true")
    97  	f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring")
    98  	f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version")
    99  
   100  	return cmd
   101  }
   102  
   103  func (p *packageCmd) run(cmd *cobra.Command, args []string) error {
   104  	path, err := filepath.Abs(p.path)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	ch, err := chartutil.LoadDir(path)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	// If version is set, modify the version.
   115  	if len(p.version) != 0 {
   116  		if err := setVersion(ch, p.version); err != nil {
   117  			return err
   118  		}
   119  		if flagDebug {
   120  			fmt.Fprintf(p.out, "Setting version to %s", p.version)
   121  		}
   122  	}
   123  
   124  	if filepath.Base(path) != ch.Metadata.Name {
   125  		return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name)
   126  	}
   127  
   128  	if reqs, err := chartutil.LoadRequirements(ch); err == nil {
   129  		checkDependencies(ch, reqs, p.out)
   130  	}
   131  
   132  	// Save to the current working directory.
   133  	cwd, err := os.Getwd()
   134  	if err != nil {
   135  		return err
   136  	}
   137  	name, err := chartutil.Save(ch, cwd)
   138  	if err == nil && flagDebug {
   139  		fmt.Fprintf(p.out, "Saved %s to current directory\n", name)
   140  	}
   141  
   142  	// Save to $HELM_HOME/local directory. This is second, because we don't want
   143  	// the case where we saved here, but didn't save to the default destination.
   144  	if p.save {
   145  		lr := p.home.LocalRepository()
   146  		if err := repo.AddChartToLocalRepo(ch, lr); err != nil {
   147  			return err
   148  		} else if flagDebug {
   149  			fmt.Fprintf(p.out, "Saved %s to %s\n", name, lr)
   150  		}
   151  	}
   152  
   153  	if p.sign {
   154  		err = p.clearsign(name)
   155  	}
   156  
   157  	return err
   158  }
   159  
   160  func setVersion(ch *chart.Chart, ver string) error {
   161  	// Verify that version is a SemVer, and error out if it is not.
   162  	if _, err := semver.NewVersion(ver); err != nil {
   163  		return err
   164  	}
   165  
   166  	// Set the version field on the chart.
   167  	ch.Metadata.Version = ver
   168  	return nil
   169  }
   170  
   171  func (p *packageCmd) clearsign(filename string) error {
   172  	// Load keyring
   173  	signer, err := provenance.NewFromKeyring(p.keyring, p.key)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	if err := signer.DecryptKey(promptUser); err != nil {
   179  		return err
   180  	}
   181  
   182  	sig, err := signer.ClearSign(filename)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	if flagDebug {
   188  		fmt.Fprintln(p.out, sig)
   189  	}
   190  
   191  	return ioutil.WriteFile(filename+".prov", []byte(sig), 0755)
   192  }
   193  
   194  // promptUser implements provenance.PassphraseFetcher
   195  func promptUser(name string) ([]byte, error) {
   196  	fmt.Printf("Password for key %q >  ", name)
   197  	pw, err := terminal.ReadPassword(int(syscall.Stdin))
   198  	fmt.Println()
   199  	return pw, err
   200  }