github.com/strongmonkey/helm@v2.7.2+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/pkg/chartutil"
    33  	"k8s.io/helm/pkg/downloader"
    34  	"k8s.io/helm/pkg/getter"
    35  	"k8s.io/helm/pkg/helm/helmpath"
    36  	"k8s.io/helm/pkg/proto/hapi/chart"
    37  	"k8s.io/helm/pkg/provenance"
    38  	"k8s.io/helm/pkg/repo"
    39  )
    40  
    41  const packageDesc = `
    42  This command packages a chart into a versioned chart archive file. If a path
    43  is given, this will look at that path for a chart (which must contain a
    44  Chart.yaml file) and then package that directory.
    45  
    46  If no path is given, this will look in the present working directory for a
    47  Chart.yaml file, and (if found) build the current directory into a chart.
    48  
    49  Versioned chart archives are used by Helm package repositories.
    50  `
    51  
    52  type packageCmd struct {
    53  	save             bool
    54  	sign             bool
    55  	path             string
    56  	key              string
    57  	keyring          string
    58  	version          string
    59  	destination      string
    60  	dependencyUpdate bool
    61  
    62  	out  io.Writer
    63  	home helmpath.Home
    64  }
    65  
    66  func newPackageCmd(out io.Writer) *cobra.Command {
    67  	pkg := &packageCmd{out: out}
    68  
    69  	cmd := &cobra.Command{
    70  		Use:   "package [flags] [CHART_PATH] [...]",
    71  		Short: "package a chart directory into a chart archive",
    72  		Long:  packageDesc,
    73  		RunE: func(cmd *cobra.Command, args []string) error {
    74  			pkg.home = settings.Home
    75  			if len(args) == 0 {
    76  				return fmt.Errorf("need at least one argument, the path to the chart")
    77  			}
    78  			if pkg.sign {
    79  				if pkg.key == "" {
    80  					return errors.New("--key is required for signing a package")
    81  				}
    82  				if pkg.keyring == "" {
    83  					return errors.New("--keyring is required for signing a package")
    84  				}
    85  			}
    86  			for i := 0; i < len(args); i++ {
    87  				pkg.path = args[i]
    88  				if err := pkg.run(); err != nil {
    89  					return err
    90  				}
    91  			}
    92  			return nil
    93  		},
    94  	}
    95  
    96  	f := cmd.Flags()
    97  	f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
    98  	f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
    99  	f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true")
   100  	f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring")
   101  	f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version")
   102  	f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.")
   103  	f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
   104  
   105  	return cmd
   106  }
   107  
   108  func (p *packageCmd) run() error {
   109  	path, err := filepath.Abs(p.path)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	if p.dependencyUpdate {
   115  		downloadManager := &downloader.Manager{
   116  			Out:       p.out,
   117  			ChartPath: path,
   118  			HelmHome:  settings.Home,
   119  			Keyring:   p.keyring,
   120  			Getters:   getter.All(settings),
   121  			Debug:     settings.Debug,
   122  		}
   123  
   124  		if err := downloadManager.Update(); err != nil {
   125  			return err
   126  		}
   127  	}
   128  
   129  	ch, err := chartutil.LoadDir(path)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// If version is set, modify the version.
   135  	if len(p.version) != 0 {
   136  		if err := setVersion(ch, p.version); err != nil {
   137  			return err
   138  		}
   139  		debug("Setting version to %s", p.version)
   140  	}
   141  
   142  	if filepath.Base(path) != ch.Metadata.Name {
   143  		return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name)
   144  	}
   145  
   146  	if reqs, err := chartutil.LoadRequirements(ch); err == nil {
   147  		if err := checkDependencies(ch, reqs); err != nil {
   148  			return err
   149  		}
   150  	} else {
   151  		if err != chartutil.ErrRequirementsNotFound {
   152  			return err
   153  		}
   154  	}
   155  
   156  	var dest string
   157  	if p.destination == "." {
   158  		// Save to the current working directory.
   159  		dest, err = os.Getwd()
   160  		if err != nil {
   161  			return err
   162  		}
   163  	} else {
   164  		// Otherwise save to set destination
   165  		dest = p.destination
   166  	}
   167  
   168  	name, err := chartutil.Save(ch, dest)
   169  	if err == nil {
   170  		fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name)
   171  	} else {
   172  		return fmt.Errorf("Failed to save: %s", err)
   173  	}
   174  
   175  	// Save to $HELM_HOME/local directory. This is second, because we don't want
   176  	// the case where we saved here, but didn't save to the default destination.
   177  	if p.save {
   178  		lr := p.home.LocalRepository()
   179  		if err := repo.AddChartToLocalRepo(ch, lr); err != nil {
   180  			return err
   181  		}
   182  		debug("Successfully saved %s to %s\n", name, lr)
   183  	}
   184  
   185  	if p.sign {
   186  		err = p.clearsign(name)
   187  	}
   188  
   189  	return err
   190  }
   191  
   192  func setVersion(ch *chart.Chart, ver string) error {
   193  	// Verify that version is a SemVer, and error out if it is not.
   194  	if _, err := semver.NewVersion(ver); err != nil {
   195  		return err
   196  	}
   197  
   198  	// Set the version field on the chart.
   199  	ch.Metadata.Version = ver
   200  	return nil
   201  }
   202  
   203  func (p *packageCmd) clearsign(filename string) error {
   204  	// Load keyring
   205  	signer, err := provenance.NewFromKeyring(p.keyring, p.key)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	if err := signer.DecryptKey(promptUser); err != nil {
   211  		return err
   212  	}
   213  
   214  	sig, err := signer.ClearSign(filename)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	debug(sig)
   220  
   221  	return ioutil.WriteFile(filename+".prov", []byte(sig), 0755)
   222  }
   223  
   224  // promptUser implements provenance.PassphraseFetcher
   225  func promptUser(name string) ([]byte, error) {
   226  	fmt.Printf("Password for key %q >  ", name)
   227  	pw, err := terminal.ReadPassword(int(syscall.Stdin))
   228  	fmt.Println()
   229  	return pw, err
   230  }