Merge pull request #4027 from adamreese/dev-v3-ref-cmd

ref(cmd): consistent naming of cmd variables
pull/4029/head
Adam Reese 6 years ago committed by GitHub
commit b5f52905df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -37,16 +37,11 @@ For example, 'helm create foo' will create a directory structure that looks
something like this: something like this:
foo/ foo/
| .helmignore # Contains patterns to ignore when packaging Helm charts.
|- .helmignore # Contains patterns to ignore when packaging Helm charts. Chart.yaml # Information about your chart
| values.yaml # The default values for your templates
|- Chart.yaml # Information about your chart charts/ # Charts that this chart depends on
| templates/ # The template files
|- values.yaml # The default values for your templates
|
|- charts/ # Charts that this chart depends on
|
|- templates/ # The template files
'helm create' takes a path for an argument. If directories in the given path 'helm create' takes a path for an argument. If directories in the given path
do not exist, Helm will attempt to create them as it goes. If the given do not exist, Helm will attempt to create them as it goes. If the given
@ -54,38 +49,40 @@ destination exists and there are files in that directory, conflicting files
will be overwritten, but other files will be left alone. will be overwritten, but other files will be left alone.
` `
type createCmd struct { type createOptions struct {
home helmpath.Home starter string // --starter
name string
out io.Writer // args
starter string name string
home helmpath.Home
} }
func newCreateCmd(out io.Writer) *cobra.Command { func newCreateCmd(out io.Writer) *cobra.Command {
cc := &createCmd{out: out} o := &createOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create NAME", Use: "create NAME",
Short: "create a new chart with the given name", Short: "create a new chart with the given name",
Long: createDesc, Long: createDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cc.home = settings.Home o.home = settings.Home
if len(args) == 0 { if len(args) == 0 {
return errors.New("the name of the new chart is required") return errors.New("the name of the new chart is required")
} }
cc.name = args[0] o.name = args[0]
return cc.run() return o.run(out)
}, },
} }
cmd.Flags().StringVarP(&cc.starter, "starter", "p", "", "the named Helm starter scaffold") cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "the named Helm starter scaffold")
return cmd return cmd
} }
func (c *createCmd) run() error { func (o *createOptions) run(out io.Writer) error {
fmt.Fprintf(c.out, "Creating %s\n", c.name) fmt.Fprintf(out, "Creating %s\n", o.name)
chartname := filepath.Base(c.name) chartname := filepath.Base(o.name)
cfile := &chart.Metadata{ cfile := &chart.Metadata{
Name: chartname, Name: chartname,
Description: "A Helm chart for Kubernetes", Description: "A Helm chart for Kubernetes",
@ -94,12 +91,12 @@ func (c *createCmd) run() error {
APIVersion: chartutil.APIVersionv1, APIVersion: chartutil.APIVersionv1,
} }
if c.starter != "" { if o.starter != "" {
// Create from the starter // Create from the starter
lstarter := filepath.Join(c.home.Starters(), c.starter) lstarter := filepath.Join(o.home.Starters(), o.starter)
return chartutil.CreateFrom(cfile, filepath.Dir(c.name), lstarter) return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter)
} }
_, err := chartutil.Create(cfile, filepath.Dir(c.name)) _, err := chartutil.Create(cfile, filepath.Dir(o.name))
return err return err
} }

@ -34,22 +34,20 @@ Use the '--dry-run' flag to see which releases will be deleted without actually
deleting them. deleting them.
` `
type deleteCmd struct { type deleteOptions struct {
name string disableHooks bool // --no-hooks
dryRun bool dryRun bool // --dry-run
disableHooks bool purge bool // --purge
purge bool timeout int64 // --timeout
timeout int64
// args
out io.Writer name string
client helm.Interface client helm.Interface
} }
func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command { func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
del := &deleteCmd{ o := &deleteOptions{client: c}
out: out,
client: c,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete [flags] RELEASE_NAME [...]", Use: "delete [flags] RELEASE_NAME [...]",
@ -61,39 +59,39 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errors.New("command 'delete' requires a release name") return errors.New("command 'delete' requires a release name")
} }
del.client = ensureHelmClient(del.client, false) o.client = ensureHelmClient(o.client, false)
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
del.name = args[i] o.name = args[i]
if err := del.run(); err != nil { if err := o.run(out); err != nil {
return err return err
} }
fmt.Fprintf(out, "release \"%s\" deleted\n", del.name) fmt.Fprintf(out, "release \"%s\" deleted\n", o.name)
} }
return nil return nil
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&del.dryRun, "dry-run", false, "simulate a delete") f.BoolVar(&o.dryRun, "dry-run", false, "simulate a delete")
f.BoolVar(&del.disableHooks, "no-hooks", false, "prevent hooks from running during deletion") f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during deletion")
f.BoolVar(&del.purge, "purge", false, "remove the release from the store and make its name free for later use") f.BoolVar(&o.purge, "purge", false, "remove the release from the store and make its name free for later use")
f.Int64Var(&del.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
return cmd return cmd
} }
func (d *deleteCmd) run() error { func (o *deleteOptions) run(out io.Writer) error {
opts := []helm.DeleteOption{ opts := []helm.DeleteOption{
helm.DeleteDryRun(d.dryRun), helm.DeleteDryRun(o.dryRun),
helm.DeleteDisableHooks(d.disableHooks), helm.DeleteDisableHooks(o.disableHooks),
helm.DeletePurge(d.purge), helm.DeletePurge(o.purge),
helm.DeleteTimeout(d.timeout), helm.DeleteTimeout(o.timeout),
} }
res, err := d.client.DeleteRelease(d.name, opts...) res, err := o.client.DeleteRelease(o.name, opts...)
if res != nil && res.Info != "" { if res != nil && res.Info != "" {
fmt.Fprintln(d.out, res.Info) fmt.Fprintln(out, res.Info)
} }
return err return err

@ -102,13 +102,14 @@ func newDependencyCmd(out io.Writer) *cobra.Command {
return cmd return cmd
} }
type dependencyListCmd struct { type dependencyLisOptions struct {
out io.Writer
chartpath string chartpath string
} }
func newDependencyListCmd(out io.Writer) *cobra.Command { func newDependencyListCmd(out io.Writer) *cobra.Command {
dlc := &dependencyListCmd{out: out} o := &dependencyLisOptions{
chartpath: ".",
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list [flags] CHART", Use: "list [flags] CHART",
@ -116,20 +117,17 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
Short: "list the dependencies for the given chart", Short: "list the dependencies for the given chart",
Long: dependencyListDesc, Long: dependencyListDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cp := "."
if len(args) > 0 { if len(args) > 0 {
cp = args[0] o.chartpath = filepath.Clean(args[0])
} }
return o.run(out)
dlc.chartpath = filepath.Clean(cp)
return dlc.run()
}, },
} }
return cmd return cmd
} }
func (l *dependencyListCmd) run() error { func (o *dependencyLisOptions) run(out io.Writer) error {
c, err := chartutil.Load(l.chartpath) c, err := chartutil.Load(o.chartpath)
if err != nil { if err != nil {
return err return err
} }
@ -137,21 +135,21 @@ func (l *dependencyListCmd) run() error {
r, err := chartutil.LoadRequirements(c) r, err := chartutil.LoadRequirements(c)
if err != nil { if err != nil {
if err == chartutil.ErrRequirementsNotFound { if err == chartutil.ErrRequirementsNotFound {
fmt.Fprintf(l.out, "WARNING: no requirements at %s/charts\n", l.chartpath) fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath)
return nil return nil
} }
return err return err
} }
l.printRequirements(r, l.out) o.printRequirements(out, r)
fmt.Fprintln(l.out) fmt.Fprintln(out)
l.printMissing(r) o.printMissing(out, r)
return nil return nil
} }
func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string { func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) string {
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
archives, err := filepath.Glob(filepath.Join(l.chartpath, "charts", filename)) archives, err := filepath.Glob(filepath.Join(o.chartpath, "charts", filename))
if err != nil { if err != nil {
return "bad pattern" return "bad pattern"
} else if len(archives) > 1 { } else if len(archives) > 1 {
@ -187,7 +185,7 @@ func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string {
} }
} }
folder := filepath.Join(l.chartpath, "charts", dep.Name) folder := filepath.Join(o.chartpath, "charts", dep.Name)
if fi, err := os.Stat(folder); err != nil { if fi, err := os.Stat(folder); err != nil {
return "missing" return "missing"
} else if !fi.IsDir() { } else if !fi.IsDir() {
@ -224,29 +222,29 @@ func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string {
} }
// printRequirements prints all of the requirements in the yaml file. // printRequirements prints all of the requirements in the yaml file.
func (l *dependencyListCmd) printRequirements(reqs *chartutil.Requirements, out io.Writer) { func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chartutil.Requirements) {
table := uitable.New() table := uitable.New()
table.MaxColWidth = 80 table.MaxColWidth = 80
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
for _, row := range reqs.Dependencies { for _, row := range reqs.Dependencies {
table.AddRow(row.Name, row.Version, row.Repository, l.dependencyStatus(row)) table.AddRow(row.Name, row.Version, row.Repository, o.dependencyStatus(row))
} }
fmt.Fprintln(out, table) fmt.Fprintln(out, table)
} }
// printMissing prints warnings about charts that are present on disk, but are not in the requirements. // printMissing prints warnings about charts that are present on disk, but are not in the requirements.
func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements) { func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chartutil.Requirements) {
folder := filepath.Join(l.chartpath, "charts/*") folder := filepath.Join(o.chartpath, "charts/*")
files, err := filepath.Glob(folder) files, err := filepath.Glob(folder)
if err != nil { if err != nil {
fmt.Fprintln(l.out, err) fmt.Fprintln(out, err)
return return
} }
for _, f := range files { for _, f := range files {
fi, err := os.Stat(f) fi, err := os.Stat(f)
if err != nil { if err != nil {
fmt.Fprintf(l.out, "Warning: %s\n", err) fmt.Fprintf(out, "Warning: %s\n", err)
} }
// Skip anything that is not a directory and not a tgz file. // Skip anything that is not a directory and not a tgz file.
if !fi.IsDir() && filepath.Ext(f) != ".tgz" { if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
@ -254,7 +252,7 @@ func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements) {
} }
c, err := chartutil.Load(f) c, err := chartutil.Load(f)
if err != nil { if err != nil {
fmt.Fprintf(l.out, "WARNING: %q is not a chart.\n", f) fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
continue continue
} }
found := false found := false
@ -265,7 +263,7 @@ func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements) {
} }
} }
if !found { if !found {
fmt.Fprintf(l.out, "WARNING: %q is not in requirements.yaml.\n", f) fmt.Fprintf(out, "WARNING: %q is not in requirements.yaml.\n", f)
} }
} }

@ -36,48 +36,50 @@ If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'. of 'helm dependency update'.
` `
type dependencyBuildCmd struct { type dependencyBuildOptions struct {
out io.Writer keyring string // --keyring
verify bool // --verify
// args
chartpath string chartpath string
verify bool
keyring string helmhome helmpath.Home
helmhome helmpath.Home
} }
func newDependencyBuildCmd(out io.Writer) *cobra.Command { func newDependencyBuildCmd(out io.Writer) *cobra.Command {
dbc := &dependencyBuildCmd{out: out} o := &dependencyBuildOptions{
chartpath: ".",
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "build [flags] CHART", Use: "build [flags] CHART",
Short: "rebuild the charts/ directory based on the requirements.lock file", Short: "rebuild the charts/ directory based on the requirements.lock file",
Long: dependencyBuildDesc, Long: dependencyBuildDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
dbc.helmhome = settings.Home o.helmhome = settings.Home
dbc.chartpath = "."
if len(args) > 0 { if len(args) > 0 {
dbc.chartpath = args[0] o.chartpath = args[0]
} }
return dbc.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&dbc.verify, "verify", false, "verify the packages against signatures") f.BoolVar(&o.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "keyring containing public keys") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
return cmd return cmd
} }
func (d *dependencyBuildCmd) run() error { func (o *dependencyBuildOptions) run(out io.Writer) error {
man := &downloader.Manager{ man := &downloader.Manager{
Out: d.out, Out: out,
ChartPath: d.chartpath, ChartPath: o.chartpath,
HelmHome: d.helmhome, HelmHome: o.helmhome,
Keyring: d.keyring, Keyring: o.keyring,
Getters: getter.All(settings), Getters: getter.All(settings),
} }
if d.verify { if o.verify {
man.Verify = downloader.VerifyIfPossible man.Verify = downloader.VerifyIfPossible
} }

@ -41,19 +41,23 @@ reason, an update command will not remove charts unless they are (a) present
in the requirements.yaml file, but (b) at the wrong version. in the requirements.yaml file, but (b) at the wrong version.
` `
// dependencyUpdateCmd describes a 'helm dependency update' // dependencyUpdateOptions describes a 'helm dependency update'
type dependencyUpdateCmd struct { type dependencyUpdateOptions struct {
out io.Writer keyring string // --keyring
chartpath string skipRefresh bool // --skip-refresh
helmhome helmpath.Home verify bool // --verify
verify bool
keyring string // args
skipRefresh bool chartpath string
helmhome helmpath.Home
} }
// newDependencyUpdateCmd creates a new dependency update command. // newDependencyUpdateCmd creates a new dependency update command.
func newDependencyUpdateCmd(out io.Writer) *cobra.Command { func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
duc := &dependencyUpdateCmd{out: out} o := &dependencyUpdateOptions{
chartpath: ".",
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update [flags] CHART", Use: "update [flags] CHART",
@ -61,42 +65,33 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
Short: "update charts/ based on the contents of requirements.yaml", Short: "update charts/ based on the contents of requirements.yaml",
Long: dependencyUpDesc, Long: dependencyUpDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cp := "."
if len(args) > 0 { if len(args) > 0 {
cp = args[0] o.chartpath = filepath.Clean(args[0])
} }
o.helmhome = settings.Home
var err error return o.run(out)
duc.chartpath, err = filepath.Abs(cp)
if err != nil {
return err
}
duc.helmhome = settings.Home
return duc.run()
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&duc.verify, "verify", false, "verify the packages against signatures") f.BoolVar(&o.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&duc.keyring, "keyring", defaultKeyring(), "keyring containing public keys") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&duc.skipRefresh, "skip-refresh", false, "do not refresh the local repository cache") f.BoolVar(&o.skipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
return cmd return cmd
} }
// run runs the full dependency update process. // run runs the full dependency update process.
func (d *dependencyUpdateCmd) run() error { func (o *dependencyUpdateOptions) run(out io.Writer) error {
man := &downloader.Manager{ man := &downloader.Manager{
Out: d.out, Out: out,
ChartPath: d.chartpath, ChartPath: o.chartpath,
HelmHome: d.helmhome, HelmHome: o.helmhome,
Keyring: d.keyring, Keyring: o.keyring,
SkipUpdate: d.skipRefresh, SkipUpdate: o.skipRefresh,
Getters: getter.All(settings), Getters: getter.All(settings),
} }
if d.verify { if o.verify {
man.Verify = downloader.VerifyAlways man.Verify = downloader.VerifyAlways
} }
if settings.Debug { if settings.Debug {

@ -190,11 +190,11 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
} }
out := bytes.NewBuffer(nil) out := bytes.NewBuffer(nil)
duc := &dependencyUpdateCmd{out: out} o := &dependencyUpdateOptions{}
duc.helmhome = helmpath.Home(hh) o.helmhome = helmpath.Home(hh)
duc.chartpath = hh.Path(chartname) o.chartpath = hh.Path(chartname)
if err := duc.run(); err != nil { if err := o.run(out); err != nil {
output := out.String() output := out.String()
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal(err) t.Fatal(err)
@ -203,14 +203,14 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
// Chart repo is down // Chart repo is down
srv.Stop() srv.Stop()
if err := duc.run(); err == nil { if err := o.run(out); err == nil {
output := out.String() output := out.String()
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal("Expected error, got nil") t.Fatal("Expected error, got nil")
} }
// Make sure charts dir still has dependencies // Make sure charts dir still has dependencies
files, err := ioutil.ReadDir(filepath.Join(duc.chartpath, "charts")) files, err := ioutil.ReadDir(filepath.Join(o.chartpath, "charts"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -226,7 +226,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
} }
// Make sure tmpcharts is deleted // Make sure tmpcharts is deleted
if _, err := os.Stat(filepath.Join(duc.chartpath, "tmpcharts")); !os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(o.chartpath, "tmpcharts")); !os.IsNotExist(err) {
t.Fatalf("tmpcharts dir still exists") t.Fatalf("tmpcharts dir still exists")
} }
} }

@ -37,15 +37,14 @@ It can also generate bash autocompletions.
$ helm docs markdown -dir mydocs/ $ helm docs markdown -dir mydocs/
` `
type docsCmd struct { type docsOptions struct {
out io.Writer
dest string dest string
docTypeString string docTypeString string
topCmd *cobra.Command topCmd *cobra.Command
} }
func newDocsCmd(out io.Writer) *cobra.Command { func newDocsCmd(out io.Writer) *cobra.Command {
dc := &docsCmd{out: out} o := &docsOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "docs", Use: "docs",
@ -53,28 +52,28 @@ func newDocsCmd(out io.Writer) *cobra.Command {
Long: docsDesc, Long: docsDesc,
Hidden: true, Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
dc.topCmd = cmd.Root() o.topCmd = cmd.Root()
return dc.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.StringVar(&dc.dest, "dir", "./", "directory to which documentation is written") f.StringVar(&o.dest, "dir", "./", "directory to which documentation is written")
f.StringVar(&dc.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)") f.StringVar(&o.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)")
return cmd return cmd
} }
func (d *docsCmd) run() error { func (o *docsOptions) run(out io.Writer) error {
switch d.docTypeString { switch o.docTypeString {
case "markdown", "mdown", "md": case "markdown", "mdown", "md":
return doc.GenMarkdownTree(d.topCmd, d.dest) return doc.GenMarkdownTree(o.topCmd, o.dest)
case "man": case "man":
manHdr := &doc.GenManHeader{Title: "HELM", Section: "1"} manHdr := &doc.GenManHeader{Title: "HELM", Section: "1"}
return doc.GenManTree(d.topCmd, manHdr, d.dest) return doc.GenManTree(o.topCmd, manHdr, o.dest)
case "bash": case "bash":
return d.topCmd.GenBashCompletionFile(filepath.Join(d.dest, "completions.bash")) return o.topCmd.GenBashCompletionFile(filepath.Join(o.dest, "completions.bash"))
default: default:
return fmt.Errorf("unknown doc type %q. Try 'markdown' or 'man'", d.docTypeString) return fmt.Errorf("unknown doc type %q. Try 'markdown' or 'man'", o.docTypeString)
} }
} }

@ -24,6 +24,7 @@ import (
"path/filepath" "path/filepath"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/downloader" "k8s.io/helm/pkg/downloader"
"k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/getter"
@ -45,31 +46,27 @@ file, and MUST pass the verification process. Failure in any part of this will
result in an error, and the chart will not be saved locally. result in an error, and the chart will not be saved locally.
` `
type fetchCmd struct { type fetchOptions struct {
untar bool caFile string // --ca-file
untardir string certFile string // --cert-file
chartRef string destdir string // --destination
destdir string devel bool // --devel
version string keyFile string // --key-file
repoURL string keyring string // --keyring
username string password string // --password
password string repoURL string // --repo
untar bool // --untar
verify bool untardir string // --untardir
verifyLater bool username string // --username
keyring string verify bool // --verify
verifyLater bool // --prov
version string // --version
certFile string chartRef string
keyFile string
caFile string
devel bool
out io.Writer
} }
func newFetchCmd(out io.Writer) *cobra.Command { func newFetchCmd(out io.Writer) *cobra.Command {
fch := &fetchCmd{out: out} o := &fetchOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "fetch [flags] [chart URL | repo/chartname] [...]", Use: "fetch [flags] [chart URL | repo/chartname] [...]",
@ -80,14 +77,14 @@ func newFetchCmd(out io.Writer) *cobra.Command {
return fmt.Errorf("need at least one argument, url or repo/name of the chart") return fmt.Errorf("need at least one argument, url or repo/name of the chart")
} }
if fch.version == "" && fch.devel { if o.version == "" && o.devel {
debug("setting version to >0.0.0-0") debug("setting version to >0.0.0-0")
fch.version = ">0.0.0-0" o.version = ">0.0.0-0"
} }
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
fch.chartRef = args[i] o.chartRef = args[i]
if err := fch.run(); err != nil { if err := o.run(out); err != nil {
return err return err
} }
} }
@ -96,45 +93,45 @@ func newFetchCmd(out io.Writer) *cobra.Command {
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&fch.untar, "untar", false, "if set to true, will untar the chart after downloading it") f.BoolVar(&o.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.StringVar(&fch.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded") f.BoolVar(&o.untar, "untar", false, "if set to true, will untar the chart after downloading it")
f.BoolVar(&fch.verify, "verify", false, "verify the package against its signature") f.BoolVar(&o.verify, "verify", false, "verify the package against its signature")
f.BoolVar(&fch.verifyLater, "prov", false, "fetch the provenance file, but don't perform verification") f.BoolVar(&o.verifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
f.StringVar(&fch.version, "version", "", "specific version of a chart. Without this, the latest version is fetched") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "keyring containing public keys") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVarP(&fch.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&fch.repoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVar(&fch.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.password, "password", "", "chart repository password")
f.StringVar(&fch.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&fch.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&o.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
f.BoolVar(&fch.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") f.StringVar(&o.username, "username", "", "chart repository username")
f.StringVar(&fch.username, "username", "", "chart repository username") f.StringVar(&o.version, "version", "", "specific version of a chart. Without this, the latest version is fetched")
f.StringVar(&fch.password, "password", "", "chart repository password") f.StringVarP(&o.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
return cmd return cmd
} }
func (f *fetchCmd) run() error { func (o *fetchOptions) run(out io.Writer) error {
c := downloader.ChartDownloader{ c := downloader.ChartDownloader{
HelmHome: settings.Home, HelmHome: settings.Home,
Out: f.out, Out: out,
Keyring: f.keyring, Keyring: o.keyring,
Verify: downloader.VerifyNever, Verify: downloader.VerifyNever,
Getters: getter.All(settings), Getters: getter.All(settings),
Username: f.username, Username: o.username,
Password: f.password, Password: o.password,
} }
if f.verify { if o.verify {
c.Verify = downloader.VerifyAlways c.Verify = downloader.VerifyAlways
} else if f.verifyLater { } else if o.verifyLater {
c.Verify = downloader.VerifyLater c.Verify = downloader.VerifyLater
} }
// If untar is set, we fetch to a tempdir, then untar and copy after // If untar is set, we fetch to a tempdir, then untar and copy after
// verification. // verification.
dest := f.destdir dest := o.destdir
if f.untar { if o.untar {
var err error var err error
dest, err = ioutil.TempDir("", "helm-") dest, err = ioutil.TempDir("", "helm-")
if err != nil { if err != nil {
@ -143,28 +140,28 @@ func (f *fetchCmd) run() error {
defer os.RemoveAll(dest) defer os.RemoveAll(dest)
} }
if f.repoURL != "" { if o.repoURL != "" {
chartURL, err := repo.FindChartInAuthRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) chartURL, err := repo.FindChartInAuthRepoURL(o.repoURL, o.username, o.password, o.chartRef, o.version, o.certFile, o.keyFile, o.caFile, getter.All(settings))
if err != nil { if err != nil {
return err return err
} }
f.chartRef = chartURL o.chartRef = chartURL
} }
saved, v, err := c.DownloadTo(f.chartRef, f.version, dest) saved, v, err := c.DownloadTo(o.chartRef, o.version, dest)
if err != nil { if err != nil {
return err return err
} }
if f.verify { if o.verify {
fmt.Fprintf(f.out, "Verification: %v\n", v) fmt.Fprintf(out, "Verification: %v\n", v)
} }
// After verification, untar the chart into the requested directory. // After verification, untar the chart into the requested directory.
if f.untar { if o.untar {
ud := f.untardir ud := o.untardir
if !filepath.IsAbs(ud) { if !filepath.IsAbs(ud) {
ud = filepath.Join(f.destdir, ud) ud = filepath.Join(o.destdir, ud)
} }
if fi, err := os.Stat(ud); err != nil { if fi, err := os.Stat(ud); err != nil {
if err := os.MkdirAll(ud, 0755); err != nil { if err := os.MkdirAll(ud, 0755); err != nil {

@ -40,19 +40,16 @@ chart, the supplied values, and the generated manifest file.
var errReleaseRequired = errors.New("release name is required") var errReleaseRequired = errors.New("release name is required")
type getCmd struct { type getOptions struct {
version int // --revision
release string release string
version int
out io.Writer
client helm.Interface client helm.Interface
} }
func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
get := &getCmd{ o := &getOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "get [flags] RELEASE_NAME", Use: "get [flags] RELEASE_NAME",
@ -62,13 +59,13 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
get.release = args[0] o.release = args[0]
get.client = ensureHelmClient(get.client, false) o.client = ensureHelmClient(o.client, false)
return get.run() return o.run(out)
}, },
} }
cmd.Flags().IntVar(&get.version, "revision", 0, "get the named release with revision") cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
cmd.AddCommand(newGetValuesCmd(client, out)) cmd.AddCommand(newGetValuesCmd(client, out))
cmd.AddCommand(newGetManifestCmd(client, out)) cmd.AddCommand(newGetManifestCmd(client, out))
@ -77,11 +74,10 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
return cmd return cmd
} }
// getCmd is the command that implements 'helm get' func (g *getOptions) run(out io.Writer) error {
func (g *getCmd) run() error {
res, err := g.client.ReleaseContent(g.release, g.version) res, err := g.client.ReleaseContent(g.release, g.version)
if err != nil { if err != nil {
return err return err
} }
return printRelease(g.out, res) return printRelease(out, res)
} }

@ -31,18 +31,15 @@ This command downloads hooks for a given release.
Hooks are formatted in YAML and separated by the YAML '---\n' separator. Hooks are formatted in YAML and separated by the YAML '---\n' separator.
` `
type getHooksCmd struct { type getHooksOptions struct {
release string release string
out io.Writer
client helm.Interface client helm.Interface
version int version int
} }
func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
ghc := &getHooksCmd{ o := &getHooksOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "hooks [flags] RELEASE_NAME", Use: "hooks [flags] RELEASE_NAME",
Short: "download all hooks for a named release", Short: "download all hooks for a named release",
@ -51,24 +48,24 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
ghc.release = args[0] o.release = args[0]
ghc.client = ensureHelmClient(ghc.client, false) o.client = ensureHelmClient(o.client, false)
return ghc.run() return o.run(out)
}, },
} }
cmd.Flags().IntVar(&ghc.version, "revision", 0, "get the named release with revision") cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
return cmd return cmd
} }
func (g *getHooksCmd) run() error { func (o *getHooksOptions) run(out io.Writer) error {
res, err := g.client.ReleaseContent(g.release, g.version) res, err := o.client.ReleaseContent(o.release, o.version)
if err != nil { if err != nil {
fmt.Fprintln(g.out, g.release) fmt.Fprintln(out, o.release)
return err return err
} }
for _, hook := range res.Hooks { for _, hook := range res.Hooks {
fmt.Fprintf(g.out, "---\n# %s\n%s", hook.Name, hook.Manifest) fmt.Fprintf(out, "---\n# %s\n%s", hook.Name, hook.Manifest)
} }
return nil return nil
} }

@ -33,18 +33,17 @@ were generated from this release's chart(s). If a chart is dependent on other
charts, those resources will also be included in the manifest. charts, those resources will also be included in the manifest.
` `
type getManifestCmd struct { type getManifestOptions struct {
version int // --revision
release string release string
out io.Writer
client helm.Interface client helm.Interface
version int
} }
func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
get := &getManifestCmd{ o := &getManifestOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "manifest [flags] RELEASE_NAME", Use: "manifest [flags] RELEASE_NAME",
Short: "download the manifest for a named release", Short: "download the manifest for a named release",
@ -53,22 +52,22 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
get.release = args[0] o.release = args[0]
get.client = ensureHelmClient(get.client, false) o.client = ensureHelmClient(o.client, false)
return get.run() return o.run(out)
}, },
} }
cmd.Flags().IntVar(&get.version, "revision", 0, "get the named release with revision") cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
return cmd return cmd
} }
// getManifest implements 'helm get manifest' // getManifest implements 'helm get manifest'
func (g *getManifestCmd) run() error { func (o *getManifestOptions) run(out io.Writer) error {
res, err := g.client.ReleaseContent(g.release, g.version) res, err := o.client.ReleaseContent(o.release, o.version)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(g.out, res.Manifest) fmt.Fprintln(out, res.Manifest)
return nil return nil
} }

@ -30,19 +30,18 @@ var getValuesHelp = `
This command downloads a values file for a given release. This command downloads a values file for a given release.
` `
type getValuesCmd struct { type getValuesOptions struct {
release string allValues bool // --all
allValues bool version int // --revision
out io.Writer
client helm.Interface release string
version int
client helm.Interface
} }
func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
get := &getValuesCmd{ o := &getValuesOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "values [flags] RELEASE_NAME", Use: "values [flags] RELEASE_NAME",
Short: "download the values file for a named release", Short: "download the values file for a named release",
@ -51,26 +50,26 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
get.release = args[0] o.release = args[0]
get.client = ensureHelmClient(get.client, false) o.client = ensureHelmClient(o.client, false)
return get.run() return o.run(out)
}, },
} }
cmd.Flags().IntVar(&get.version, "revision", 0, "get the named release with revision") cmd.Flags().BoolVarP(&o.allValues, "all", "a", false, "dump all (computed) values")
cmd.Flags().BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values") cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
return cmd return cmd
} }
// getValues implements 'helm get values' // getValues implements 'helm get values'
func (g *getValuesCmd) run() error { func (o *getValuesOptions) run(out io.Writer) error {
res, err := g.client.ReleaseContent(g.release, g.version) res, err := o.client.ReleaseContent(o.release, o.version)
if err != nil { if err != nil {
return err return err
} }
// If the user wants all values, compute the values and return. // If the user wants all values, compute the values and return.
if g.allValues { if o.allValues {
cfg, err := chartutil.CoalesceValues(res.Chart, res.Config) cfg, err := chartutil.CoalesceValues(res.Chart, res.Config)
if err != nil { if err != nil {
return err return err
@ -79,10 +78,10 @@ func (g *getValuesCmd) run() error {
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(g.out, cfgStr) fmt.Fprintln(out, cfgStr)
return nil return nil
} }
fmt.Fprintln(g.out, string(res.Config)) fmt.Fprintln(out, string(res.Config))
return nil return nil
} }

@ -30,13 +30,13 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm"
helm_env "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
) )
var ( var (
settings helm_env.EnvSettings settings environment.EnvSettings
config clientcmd.ClientConfig config clientcmd.ClientConfig
configOnce sync.Once configOnce sync.Once
) )

@ -56,17 +56,18 @@ The historical release set is printed as a formatted table, e.g:
4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 Upgraded successfully 4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 Upgraded successfully
` `
type historyCmd struct { type historyOptions struct {
max int colWidth uint // --col-width
rls string max int // --max
out io.Writer outputFormat string // --output
helmc helm.Interface
colWidth uint release string
outputFormat string
client helm.Interface
} }
func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command { func newHistoryCmd(c helm.Interface, out io.Writer) *cobra.Command {
his := &historyCmd{out: w, helmc: c} o := &historyOptions{client: c}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "history [flags] RELEASE_NAME", Use: "history [flags] RELEASE_NAME",
@ -77,22 +78,22 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
his.helmc = ensureHelmClient(his.helmc, false) o.client = ensureHelmClient(o.client, false)
his.rls = args[0] o.release = args[0]
return his.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.IntVar(&his.max, "max", 256, "maximum number of revision to include in history") f.IntVar(&o.max, "max", 256, "maximum number of revision to include in history")
f.UintVar(&his.colWidth, "col-width", 60, "specifies the max column width of output") f.UintVar(&o.colWidth, "col-width", 60, "specifies the max column width of output")
f.StringVarP(&his.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)") f.StringVarP(&o.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)")
return cmd return cmd
} }
func (cmd *historyCmd) run() error { func (o *historyOptions) run(out io.Writer) error {
rels, err := cmd.helmc.ReleaseHistory(cmd.rls, cmd.max) rels, err := o.client.ReleaseHistory(o.release, o.max)
if err != nil { if err != nil {
return err return err
} }
@ -105,22 +106,22 @@ func (cmd *historyCmd) run() error {
var history []byte var history []byte
var formattingError error var formattingError error
switch cmd.outputFormat { switch o.outputFormat {
case "yaml": case "yaml":
history, formattingError = yaml.Marshal(releaseHistory) history, formattingError = yaml.Marshal(releaseHistory)
case "json": case "json":
history, formattingError = json.Marshal(releaseHistory) history, formattingError = json.Marshal(releaseHistory)
case "table": case "table":
history = formatAsTable(releaseHistory, cmd.colWidth) history = formatAsTable(releaseHistory, o.colWidth)
default: default:
return fmt.Errorf("unknown output format %q", cmd.outputFormat) return fmt.Errorf("unknown output format %q", o.outputFormat)
} }
if formattingError != nil { if formattingError != nil {
return formattingError return formattingError
} }
fmt.Fprintln(cmd.out, string(history)) fmt.Fprintln(out, string(history))
return nil return nil
} }

@ -33,18 +33,20 @@ const initDesc = `
This command sets up local configuration in $HELM_HOME (default ~/.helm/). This command sets up local configuration in $HELM_HOME (default ~/.helm/).
` `
const stableRepository = "stable" const (
stableRepository = "stable"
defaultStableRepositoryURL = "https://kubernetes-charts.storage.googleapis.com"
)
var stableRepositoryURL = "https://kubernetes-charts.storage.googleapis.com" type initOptions struct {
skipRefresh bool // --skip-refresh
stableRepositoryURL string // --stable-repo-url
type initCmd struct { home helmpath.Home
skipRefresh bool
out io.Writer
home helmpath.Home
} }
func newInitCmd(out io.Writer) *cobra.Command { func newInitCmd(out io.Writer) *cobra.Command {
i := &initCmd{out: out} o := &initOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
@ -54,31 +56,31 @@ func newInitCmd(out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return errors.New("This command does not accept arguments") return errors.New("This command does not accept arguments")
} }
i.home = settings.Home o.home = settings.Home
return i.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache") f.BoolVar(&o.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache")
f.StringVar(&stableRepositoryURL, "stable-repo-url", stableRepositoryURL, "URL for stable repository") f.StringVar(&o.stableRepositoryURL, "stable-repo-url", defaultStableRepositoryURL, "URL for stable repository")
return cmd return cmd
} }
// run initializes local config and installs Tiller to Kubernetes cluster. // run initializes local config.
func (i *initCmd) run() error { func (o *initOptions) run(out io.Writer) error {
if err := ensureDirectories(i.home, i.out); err != nil { if err := ensureDirectories(o.home, out); err != nil {
return err return err
} }
if err := ensureDefaultRepos(i.home, i.out, i.skipRefresh); err != nil { if err := ensureDefaultRepos(o.home, out, o.skipRefresh, o.stableRepositoryURL); err != nil {
return err return err
} }
if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil { if err := ensureRepoFileFormat(o.home.RepositoryFile(), out); err != nil {
return err return err
} }
fmt.Fprintf(i.out, "$HELM_HOME has been configured at %s.\n", settings.Home) fmt.Fprintf(out, "$HELM_HOME has been configured at %s.\n", settings.Home)
fmt.Fprintln(i.out, "Happy Helming!") fmt.Fprintln(out, "Happy Helming!")
return nil return nil
} }
@ -108,12 +110,12 @@ func ensureDirectories(home helmpath.Home, out io.Writer) error {
return nil return nil
} }
func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) error { func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool, url string) error {
repoFile := home.RepositoryFile() repoFile := home.RepositoryFile()
if fi, err := os.Stat(repoFile); err != nil { if fi, err := os.Stat(repoFile); err != nil {
fmt.Fprintf(out, "Creating %s \n", repoFile) fmt.Fprintf(out, "Creating %s \n", repoFile)
f := repo.NewRepoFile() f := repo.NewRepoFile()
sr, err := initStableRepo(home.CacheIndex(stableRepository), out, skipRefresh, home) sr, err := initRepo(url, home.CacheIndex(stableRepository), out, skipRefresh, home)
if err != nil { if err != nil {
return err return err
} }
@ -127,11 +129,11 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) err
return nil return nil
} }
func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool, home helmpath.Home) (*repo.Entry, error) { func initRepo(url, cacheFile string, out io.Writer, skipRefresh bool, home helmpath.Home) (*repo.Entry, error) {
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL) fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, url)
c := repo.Entry{ c := repo.Entry{
Name: stableRepository, Name: stableRepository,
URL: stableRepositoryURL, URL: url,
Cache: cacheFile, Cache: cacheFile,
} }
r, err := repo.NewChartRepository(&c, getter.All(settings)) r, err := repo.NewChartRepository(&c, getter.All(settings))
@ -146,7 +148,7 @@ func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool, home helm
// In this case, the cacheFile is always absolute. So passing empty string // In this case, the cacheFile is always absolute. So passing empty string
// is safe. // is safe.
if err := r.DownloadIndexFile(""); err != nil { if err := r.DownloadIndexFile(""); err != nil {
return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error()) return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error())
} }
return &c, nil return &c, nil

@ -38,10 +38,10 @@ func TestEnsureHome(t *testing.T) {
if err := ensureDirectories(hh, b); err != nil { if err := ensureDirectories(hh, b); err != nil {
t.Error(err) t.Error(err)
} }
if err := ensureDefaultRepos(hh, b, false); err != nil { if err := ensureDefaultRepos(hh, b, false, defaultStableRepositoryURL); err != nil {
t.Error(err) t.Error(err)
} }
if err := ensureDefaultRepos(hh, b, true); err != nil { if err := ensureDefaultRepos(hh, b, true, defaultStableRepositoryURL); err != nil {
t.Error(err) t.Error(err)
} }
if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil { if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil {

@ -50,20 +50,18 @@ This command inspects a chart (directory, file, or URL) and displays the content
of the README file of the README file
` `
type inspectCmd struct { type inspectOptions struct {
chartpath string chartpath string
output string output string
verify bool verify bool
keyring string keyring string
out io.Writer
version string version string
repoURL string repoURL string
username string username string
password string password string
certFile string
certFile string keyFile string
keyFile string caFile string
caFile string
} }
const ( const (
@ -76,10 +74,7 @@ const (
var readmeFileNames = []string{"readme.md", "readme.txt", "readme"} var readmeFileNames = []string{"readme.md", "readme.txt", "readme"}
func newInspectCmd(out io.Writer) *cobra.Command { func newInspectCmd(out io.Writer) *cobra.Command {
insp := &inspectCmd{ o := &inspectOptions{output: all}
out: out,
output: all,
}
inspectCommand := &cobra.Command{ inspectCommand := &cobra.Command{
Use: "inspect [CHART]", Use: "inspect [CHART]",
@ -89,13 +84,13 @@ func newInspectCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, cp, err := locateChartPath(o.repoURL, o.username, o.password, args[0], o.version, o.verify, o.keyring,
insp.certFile, insp.keyFile, insp.caFile) o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
insp.chartpath = cp o.chartpath = cp
return insp.run() return o.run(out)
}, },
} }
@ -104,17 +99,17 @@ func newInspectCmd(out io.Writer) *cobra.Command {
Short: "shows inspect values", Short: "shows inspect values",
Long: inspectValuesDesc, Long: inspectValuesDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
insp.output = valuesOnly o.output = valuesOnly
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, cp, err := locateChartPath(o.repoURL, o.username, o.password, args[0], o.version, o.verify, o.keyring,
insp.certFile, insp.keyFile, insp.caFile) o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
insp.chartpath = cp o.chartpath = cp
return insp.run() return o.run(out)
}, },
} }
@ -123,17 +118,17 @@ func newInspectCmd(out io.Writer) *cobra.Command {
Short: "shows inspect chart", Short: "shows inspect chart",
Long: inspectChartDesc, Long: inspectChartDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
insp.output = chartOnly o.output = chartOnly
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, cp, err := locateChartPath(o.repoURL, o.username, o.password, args[0], o.version, o.verify, o.keyring,
insp.certFile, insp.keyFile, insp.caFile) o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
insp.chartpath = cp o.chartpath = cp
return insp.run() return o.run(out)
}, },
} }
@ -142,17 +137,17 @@ func newInspectCmd(out io.Writer) *cobra.Command {
Short: "shows inspect readme", Short: "shows inspect readme",
Long: readmeChartDesc, Long: readmeChartDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
insp.output = readmeOnly o.output = readmeOnly
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, cp, err := locateChartPath(o.repoURL, o.username, o.password, args[0], o.version, o.verify, o.keyring,
insp.certFile, insp.keyFile, insp.caFile) o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
insp.chartpath = cp o.chartpath = cp
return insp.run() return o.run(out)
}, },
} }
@ -160,56 +155,56 @@ func newInspectCmd(out io.Writer) *cobra.Command {
vflag := "verify" vflag := "verify"
vdesc := "verify the provenance data for this chart" vdesc := "verify the provenance data for this chart"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().BoolVar(&insp.verify, vflag, false, vdesc) subCmd.Flags().BoolVar(&o.verify, vflag, false, vdesc)
} }
kflag := "keyring" kflag := "keyring"
kdesc := "path to the keyring containing public verification keys" kdesc := "path to the keyring containing public verification keys"
kdefault := defaultKeyring() kdefault := defaultKeyring()
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc) subCmd.Flags().StringVar(&o.keyring, kflag, kdefault, kdesc)
} }
verflag := "version" verflag := "version"
verdesc := "version of the chart. By default, the newest chart is shown" verdesc := "version of the chart. By default, the newest chart is shown"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.version, verflag, "", verdesc) subCmd.Flags().StringVar(&o.version, verflag, "", verdesc)
} }
repoURL := "repo" repoURL := "repo"
repoURLdesc := "chart repository url where to locate the requested chart" repoURLdesc := "chart repository url where to locate the requested chart"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) subCmd.Flags().StringVar(&o.repoURL, repoURL, "", repoURLdesc)
} }
username := "username" username := "username"
usernamedesc := "chart repository username where to locate the requested chart" usernamedesc := "chart repository username where to locate the requested chart"
inspectCommand.Flags().StringVar(&insp.username, username, "", usernamedesc) inspectCommand.Flags().StringVar(&o.username, username, "", usernamedesc)
valuesSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc) valuesSubCmd.Flags().StringVar(&o.username, username, "", usernamedesc)
chartSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc) chartSubCmd.Flags().StringVar(&o.username, username, "", usernamedesc)
password := "password" password := "password"
passworddesc := "chart repository password where to locate the requested chart" passworddesc := "chart repository password where to locate the requested chart"
inspectCommand.Flags().StringVar(&insp.password, password, "", passworddesc) inspectCommand.Flags().StringVar(&o.password, password, "", passworddesc)
valuesSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc) valuesSubCmd.Flags().StringVar(&o.password, password, "", passworddesc)
chartSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc) chartSubCmd.Flags().StringVar(&o.password, password, "", passworddesc)
certFile := "cert-file" certFile := "cert-file"
certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle" certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc) subCmd.Flags().StringVar(&o.certFile, certFile, "", certFiledesc)
} }
keyFile := "key-file" keyFile := "key-file"
keyFiledesc := "identify HTTPS client using this SSL key file" keyFiledesc := "identify HTTPS client using this SSL key file"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc) subCmd.Flags().StringVar(&o.keyFile, keyFile, "", keyFiledesc)
} }
caFile := "ca-file" caFile := "ca-file"
caFiledesc := "chart repository url where to locate the requested chart" caFiledesc := "chart repository url where to locate the requested chart"
for _, subCmd := range cmds { for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc) subCmd.Flags().StringVar(&o.caFile, caFile, "", caFiledesc)
} }
for _, subCmd := range cmds[1:] { for _, subCmd := range cmds[1:] {
@ -219,7 +214,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
return inspectCommand return inspectCommand
} }
func (i *inspectCmd) run() error { func (i *inspectOptions) run(out io.Writer) error {
chrt, err := chartutil.Load(i.chartpath) chrt, err := chartutil.Load(i.chartpath)
if err != nil { if err != nil {
return err return err
@ -230,25 +225,25 @@ func (i *inspectCmd) run() error {
} }
if i.output == chartOnly || i.output == all { if i.output == chartOnly || i.output == all {
fmt.Fprintln(i.out, string(cf)) fmt.Fprintln(out, string(cf))
} }
if (i.output == valuesOnly || i.output == all) && chrt.Values != nil { if (i.output == valuesOnly || i.output == all) && chrt.Values != nil {
if i.output == all { if i.output == all {
fmt.Fprintln(i.out, "---") fmt.Fprintln(out, "---")
} }
fmt.Fprintln(i.out, string(chrt.Values)) fmt.Fprintln(out, string(chrt.Values))
} }
if i.output == readmeOnly || i.output == all { if i.output == readmeOnly || i.output == all {
if i.output == all { if i.output == all {
fmt.Fprintln(i.out, "---") fmt.Fprintln(out, "---")
} }
readme := findReadme(chrt.Files) readme := findReadme(chrt.Files)
if readme == nil { if readme == nil {
return nil return nil
} }
fmt.Fprintln(i.out, string(readme.Data)) fmt.Fprintln(out, string(readme.Data))
} }
return nil return nil
} }

@ -26,12 +26,11 @@ import (
func TestInspect(t *testing.T) { func TestInspect(t *testing.T) {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
insp := &inspectCmd{ o := &inspectOptions{
chartpath: "testdata/testcharts/alpine", chartpath: "testdata/testcharts/alpine",
output: all, output: all,
out: b,
} }
insp.run() o.run(b)
// Load the data from the textfixture directly. // Load the data from the textfixture directly.
cdata, err := ioutil.ReadFile("testdata/testcharts/alpine/Chart.yaml") cdata, err := ioutil.ReadFile("testdata/testcharts/alpine/Chart.yaml")
@ -68,12 +67,11 @@ func TestInspect(t *testing.T) {
// Regression tests for missing values. See issue #1024. // Regression tests for missing values. See issue #1024.
b.Reset() b.Reset()
insp = &inspectCmd{ o = &inspectOptions{
chartpath: "testdata/testcharts/novals", chartpath: "testdata/testcharts/novals",
output: "values", output: "values",
out: b,
} }
insp.run() o.run(b)
if b.Len() != 0 { if b.Len() != 0 {
t.Errorf("expected empty values buffer, got %q", b.String()) t.Errorf("expected empty values buffer, got %q", b.String())
} }

@ -104,7 +104,7 @@ To see the list of chart repositories, use 'helm repo list'. To search for
charts in a repository, use 'helm search'. charts in a repository, use 'helm search'.
` `
type installCmd struct { type installOptions struct {
name string // --name name string // --name
valueFiles valueFiles // --values valueFiles valueFiles // --values
dryRun bool // --dry-run dryRun bool // --dry-run
@ -128,7 +128,6 @@ type installCmd struct {
caFile string // --ca-file caFile string // --ca-file
chartPath string // arg chartPath string // arg
out io.Writer
client helm.Interface client helm.Interface
} }
@ -150,10 +149,7 @@ func (v *valueFiles) Set(value string) error {
} }
func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
inst := &installCmd{ o := &installOptions{client: c}
out: out,
client: c,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "install [CHART]", Use: "install [CHART]",
@ -164,69 +160,69 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
return err return err
} }
debug("Original chart version: %q", inst.version) debug("Original chart version: %q", o.version)
if inst.version == "" && inst.devel { if o.version == "" && o.devel {
debug("setting version to >0.0.0-0") debug("setting version to >0.0.0-0")
inst.version = ">0.0.0-0" o.version = ">0.0.0-0"
} }
cp, err := locateChartPath(inst.repoURL, inst.username, inst.password, args[0], inst.version, inst.verify, inst.keyring, cp, err := locateChartPath(o.repoURL, o.username, o.password, args[0], o.version, o.verify, o.keyring,
inst.certFile, inst.keyFile, inst.caFile) o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
inst.chartPath = cp o.chartPath = cp
inst.client = ensureHelmClient(inst.client, false) o.client = ensureHelmClient(o.client, false)
return inst.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.VarP(&o.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringVarP(&inst.name, "name", "", "", "release name. If unspecified, it will autogenerate one for you") f.StringVarP(&o.name, "name", "", "", "release name. If unspecified, it will autogenerate one for you")
f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install") f.BoolVar(&o.dryRun, "dry-run", false, "simulate an install")
f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") f.BoolVar(&o.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
f.StringArrayVar(&inst.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&inst.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&o.nameTemplate, "name-template", "", "specify template used to name the release")
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it") f.BoolVar(&o.verify, "verify", false, "verify the package before installing it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed") f.StringVar(&o.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&inst.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&o.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.StringVar(&inst.repoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&o.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&inst.username, "username", "", "chart repository username where to locate the requested chart") f.StringVar(&o.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&o.password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&inst.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&inst.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&inst.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&inst.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") f.BoolVar(&o.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.BoolVar(&inst.depUp, "dep-up", false, "run helm dependency update before installing the chart") f.BoolVar(&o.depUp, "dep-up", false, "run helm dependency update before installing the chart")
return cmd return cmd
} }
func (i *installCmd) run() error { func (o *installOptions) run(out io.Writer) error {
debug("CHART PATH: %s\n", i.chartPath) debug("CHART PATH: %s\n", o.chartPath)
rawVals, err := vals(i.valueFiles, i.values, i.stringValues) rawVals, err := vals(o.valueFiles, o.values, o.stringValues)
if err != nil { if err != nil {
return err return err
} }
// If template is specified, try to run the template. // If template is specified, try to run the template.
if i.nameTemplate != "" { if o.nameTemplate != "" {
i.name, err = generateName(i.nameTemplate) o.name, err = generateName(o.nameTemplate)
if err != nil { if err != nil {
return err return err
} }
// Print the final name so the user knows what the final name of the release is. // Print the final name so the user knows what the final name of the release is.
fmt.Printf("FINAL NAME: %s\n", i.name) fmt.Printf("FINAL NAME: %s\n", o.name)
} }
// Check chart requirements to make sure all dependencies are present in /charts // Check chart requirements to make sure all dependencies are present in /charts
chartRequested, err := chartutil.Load(i.chartPath) chartRequested, err := chartutil.Load(o.chartPath)
if err != nil { if err != nil {
return err return err
} }
@ -236,10 +232,10 @@ func (i *installCmd) run() error {
// As of Helm 2.4.0, this is treated as a stopping condition: // As of Helm 2.4.0, this is treated as a stopping condition:
// https://github.com/kubernetes/helm/issues/2209 // https://github.com/kubernetes/helm/issues/2209
if err := checkDependencies(chartRequested, req); err != nil { if err := checkDependencies(chartRequested, req); err != nil {
if i.depUp { if o.depUp {
man := &downloader.Manager{ man := &downloader.Manager{
Out: i.out, Out: out,
ChartPath: i.chartPath, ChartPath: o.chartPath,
HelmHome: settings.Home, HelmHome: settings.Home,
Keyring: defaultKeyring(), Keyring: defaultKeyring(),
SkipUpdate: false, SkipUpdate: false,
@ -257,16 +253,16 @@ func (i *installCmd) run() error {
return fmt.Errorf("cannot load requirements: %v", err) return fmt.Errorf("cannot load requirements: %v", err)
} }
rel, err := i.client.InstallReleaseFromChart( rel, err := o.client.InstallReleaseFromChart(
chartRequested, chartRequested,
getNamespace(), getNamespace(),
helm.ValueOverrides(rawVals), helm.ValueOverrides(rawVals),
helm.ReleaseName(i.name), helm.ReleaseName(o.name),
helm.InstallDryRun(i.dryRun), helm.InstallDryRun(o.dryRun),
helm.InstallReuseName(i.replace), helm.InstallReuseName(o.replace),
helm.InstallDisableHooks(i.disableHooks), helm.InstallDisableHooks(o.disableHooks),
helm.InstallTimeout(i.timeout), helm.InstallTimeout(o.timeout),
helm.InstallWait(i.wait)) helm.InstallWait(o.wait))
if err != nil { if err != nil {
return err return err
} }
@ -274,19 +270,19 @@ func (i *installCmd) run() error {
if rel == nil { if rel == nil {
return nil return nil
} }
i.printRelease(rel) o.printRelease(out, rel)
// If this is a dry run, we can't display status. // If this is a dry run, we can't display status.
if i.dryRun { if o.dryRun {
return nil return nil
} }
// Print the status like status command does // Print the status like status command does
status, err := i.client.ReleaseStatus(rel.Name, 0) status, err := o.client.ReleaseStatus(rel.Name, 0)
if err != nil { if err != nil {
return err return err
} }
PrintStatus(i.out, status) PrintStatus(out, status)
return nil return nil
} }
@ -363,14 +359,14 @@ func vals(valueFiles valueFiles, values []string, stringValues []string) ([]byte
} }
// printRelease prints info about a release if the Debug is true. // printRelease prints info about a release if the Debug is true.
func (i *installCmd) printRelease(rel *release.Release) { func (o *installOptions) printRelease(out io.Writer, rel *release.Release) {
if rel == nil { if rel == nil {
return return
} }
// TODO: Switch to text/template like everything else. // TODO: Switch to text/template like everything else.
fmt.Fprintf(i.out, "NAME: %s\n", rel.Name) fmt.Fprintf(out, "NAME: %s\n", rel.Name)
if settings.Debug { if settings.Debug {
printRelease(i.out, rel) printRelease(out, rel)
} }
} }

@ -43,60 +43,57 @@ it will emit [ERROR] messages. If it encounters issues that break with conventio
or recommendation, it will emit [WARNING] messages. or recommendation, it will emit [WARNING] messages.
` `
type lintCmd struct { type lintOptions struct {
valueFiles valueFiles valueFiles valueFiles
values []string values []string
sValues []string sValues []string
strict bool strict bool
paths []string paths []string
out io.Writer
} }
func newLintCmd(out io.Writer) *cobra.Command { func newLintCmd(out io.Writer) *cobra.Command {
l := &lintCmd{ o := &lintOptions{paths: []string{"."}}
paths: []string{"."},
out: out,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "lint [flags] PATH", Use: "lint [flags] PATH",
Short: "examines a chart for possible issues", Short: "examines a chart for possible issues",
Long: longLintHelp, Long: longLintHelp,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 { if len(args) > 0 {
l.paths = args o.paths = args
} }
return l.run() return o.run(out)
}, },
} }
cmd.Flags().VarP(&l.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") cmd.Flags().VarP(&o.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
cmd.Flags().StringArrayVar(&l.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") cmd.Flags().StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().StringArrayVar(&l.sValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") cmd.Flags().StringArrayVar(&o.sValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().BoolVar(&l.strict, "strict", false, "fail on lint warnings") cmd.Flags().BoolVar(&o.strict, "strict", false, "fail on lint warnings")
return cmd return cmd
} }
var errLintNoChart = errors.New("No chart found for linting (missing Chart.yaml)") var errLintNoChart = errors.New("No chart found for linting (missing Chart.yaml)")
func (l *lintCmd) run() error { func (o *lintOptions) run(out io.Writer) error {
var lowestTolerance int var lowestTolerance int
if l.strict { if o.strict {
lowestTolerance = support.WarningSev lowestTolerance = support.WarningSev
} else { } else {
lowestTolerance = support.ErrorSev lowestTolerance = support.ErrorSev
} }
// Get the raw values // Get the raw values
rvals, err := l.vals() rvals, err := o.vals()
if err != nil { if err != nil {
return err return err
} }
var total int var total int
var failures int var failures int
for _, path := range l.paths { for _, path := range o.paths {
if linter, err := lintChart(path, rvals, getNamespace(), l.strict); err != nil { if linter, err := lintChart(path, rvals, getNamespace(), o.strict); err != nil {
fmt.Println("==> Skipping", path) fmt.Println("==> Skipping", path)
fmt.Println(err) fmt.Println(err)
if err == errLintNoChart { if err == errLintNoChart {
@ -126,7 +123,7 @@ func (l *lintCmd) run() error {
return fmt.Errorf("%s, %d chart(s) failed", msg, failures) return fmt.Errorf("%s, %d chart(s) failed", msg, failures)
} }
fmt.Fprintf(l.out, "%s, no failures\n", msg) fmt.Fprintf(out, "%s, no failures\n", msg)
return nil return nil
} }
@ -170,11 +167,11 @@ func lintChart(path string, vals []byte, namespace string, strict bool) (support
return lint.All(chartPath, vals, namespace, strict), nil return lint.All(chartPath, vals, namespace, strict), nil
} }
func (l *lintCmd) vals() ([]byte, error) { func (o *lintOptions) vals() ([]byte, error) {
base := map[string]interface{}{} base := map[string]interface{}{}
// User specified a values files via -f/--values // User specified a values files via -f/--values
for _, filePath := range l.valueFiles { for _, filePath := range o.valueFiles {
currentMap := map[string]interface{}{} currentMap := map[string]interface{}{}
bytes, err := ioutil.ReadFile(filePath) bytes, err := ioutil.ReadFile(filePath)
if err != nil { if err != nil {
@ -189,14 +186,14 @@ func (l *lintCmd) vals() ([]byte, error) {
} }
// User specified a value via --set // User specified a value via --set
for _, value := range l.values { for _, value := range o.values {
if err := strvals.ParseInto(value, base); err != nil { if err := strvals.ParseInto(value, base); err != nil {
return []byte{}, fmt.Errorf("failed parsing --set data: %s", err) return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
} }
} }
// User specified a value via --set-string // User specified a value via --set-string
for _, value := range l.sValues { for _, value := range o.sValues {
if err := strvals.ParseIntoString(value, base); err != nil { if err := strvals.ParseIntoString(value, base); err != nil {
return []byte{}, fmt.Errorf("failed parsing --set-string data: %s", err) return []byte{}, fmt.Errorf("failed parsing --set-string data: %s", err)
} }

@ -56,31 +56,31 @@ server's default, which may be much higher than 256. Pairing the '--max'
flag with the '--offset' flag allows you to page through results. flag with the '--offset' flag allows you to page through results.
` `
type listCmd struct { type listOptions struct {
filter string // flags
short bool all bool // --all
limit int allNamespaces bool // --all-namespaces
offset string byDate bool // --date
byDate bool colWidth uint // --col-width
sortDesc bool deleted bool // --deleted
out io.Writer deleting bool // --deleting
all bool deployed bool // --deployed
deleted bool failed bool // --failed
deleting bool limit int // --max
deployed bool offset string // --offset
failed bool pending bool // --pending
superseded bool short bool // --short
pending bool sortDesc bool // --reverse
client helm.Interface superseded bool // --superseded
colWidth uint
allNamespaces bool // args
filter string
client helm.Interface
} }
func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
list := &listCmd{ o := &listOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list [flags] [FILTER]", Use: "list [flags] [FILTER]",
@ -89,48 +89,49 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 { if len(args) > 0 {
list.filter = strings.Join(args, " ") o.filter = strings.Join(args, " ")
} }
list.client = ensureHelmClient(list.client, list.allNamespaces) o.client = ensureHelmClient(o.client, o.allNamespaces)
return list.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVarP(&list.short, "short", "q", false, "output short (quiet) listing format") f.BoolVarP(&o.short, "short", "q", false, "output short (quiet) listing format")
f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date") f.BoolVarP(&o.byDate, "date", "d", false, "sort by release date")
f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order") f.BoolVarP(&o.sortDesc, "reverse", "r", false, "reverse the sort order")
f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch") f.IntVarP(&o.limit, "max", "m", 256, "maximum number of releases to fetch")
f.StringVarP(&list.offset, "offset", "o", "", "next release name in the list, used to offset from start value") f.StringVarP(&o.offset, "offset", "o", "", "next release name in the list, used to offset from start value")
f.BoolVarP(&list.all, "all", "a", false, "show all releases, not just the ones marked deployed") f.BoolVarP(&o.all, "all", "a", false, "show all releases, not just the ones marked deployed")
f.BoolVar(&list.deleted, "deleted", false, "show deleted releases") f.BoolVar(&o.deleted, "deleted", false, "show deleted releases")
f.BoolVar(&list.deleting, "deleting", false, "show releases that are currently being deleted") f.BoolVar(&o.superseded, "superseded", false, "show superseded releases")
f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") f.BoolVar(&o.deleting, "deleting", false, "show releases that are currently being deleted")
f.BoolVar(&list.failed, "failed", false, "show failed releases") f.BoolVar(&o.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
f.BoolVar(&list.pending, "pending", false, "show pending releases") f.BoolVar(&o.failed, "failed", false, "show failed releases")
f.UintVar(&list.colWidth, "col-width", 60, "specifies the max column width of output") f.BoolVar(&o.pending, "pending", false, "show pending releases")
f.BoolVar(&list.allNamespaces, "all-namespaces", false, "list releases across all namespaces") f.UintVar(&o.colWidth, "col-width", 60, "specifies the max column width of output")
f.BoolVar(&o.allNamespaces, "all-namespaces", false, "list releases across all namespaces")
return cmd return cmd
} }
func (l *listCmd) run() error { func (o *listOptions) run(out io.Writer) error {
sortBy := hapi.SortByName sortBy := hapi.SortByName
if l.byDate { if o.byDate {
sortBy = hapi.SortByLastReleased sortBy = hapi.SortByLastReleased
} }
sortOrder := hapi.SortAsc sortOrder := hapi.SortAsc
if l.sortDesc { if o.sortDesc {
sortOrder = hapi.SortDesc sortOrder = hapi.SortDesc
} }
stats := l.statusCodes() stats := o.statusCodes()
res, err := l.client.ListReleases( res, err := o.client.ListReleases(
helm.ReleaseListLimit(l.limit), helm.ReleaseListLimit(o.limit),
helm.ReleaseListOffset(l.offset), helm.ReleaseListOffset(o.offset),
helm.ReleaseListFilter(l.filter), helm.ReleaseListFilter(o.filter),
helm.ReleaseListSort(sortBy), helm.ReleaseListSort(sortBy),
helm.ReleaseListOrder(sortOrder), helm.ReleaseListOrder(sortOrder),
helm.ReleaseListStatuses(stats), helm.ReleaseListStatuses(stats),
@ -146,13 +147,13 @@ func (l *listCmd) run() error {
rels := filterList(res) rels := filterList(res)
if l.short { if o.short {
for _, r := range rels { for _, r := range rels {
fmt.Fprintln(l.out, r.Name) fmt.Fprintln(out, r.Name)
} }
return nil return nil
} }
fmt.Fprintln(l.out, formatList(rels, l.colWidth)) fmt.Fprintln(out, formatList(rels, o.colWidth))
return nil return nil
} }
@ -181,8 +182,8 @@ func filterList(rels []*release.Release) []*release.Release {
} }
// statusCodes gets the list of status codes that are to be included in the results. // statusCodes gets the list of status codes that are to be included in the results.
func (l *listCmd) statusCodes() []release.ReleaseStatus { func (o *listOptions) statusCodes() []release.ReleaseStatus {
if l.all { if o.all {
return []release.ReleaseStatus{ return []release.ReleaseStatus{
release.StatusUnknown, release.StatusUnknown,
release.StatusDeployed, release.StatusDeployed,
@ -195,22 +196,22 @@ func (l *listCmd) statusCodes() []release.ReleaseStatus {
} }
} }
status := []release.ReleaseStatus{} status := []release.ReleaseStatus{}
if l.deployed { if o.deployed {
status = append(status, release.StatusDeployed) status = append(status, release.StatusDeployed)
} }
if l.deleted { if o.deleted {
status = append(status, release.StatusDeleted) status = append(status, release.StatusDeleted)
} }
if l.deleting { if o.deleting {
status = append(status, release.StatusDeleting) status = append(status, release.StatusDeleting)
} }
if l.failed { if o.failed {
status = append(status, release.StatusFailed) status = append(status, release.StatusFailed)
} }
if l.superseded { if o.superseded {
status = append(status, release.StatusSuperseded) status = append(status, release.StatusSuperseded)
} }
if l.pending { if o.pending {
status = append(status, release.StatusPendingInstall, release.StatusPendingUpgrade, release.StatusPendingRollback) status = append(status, release.StatusPendingInstall, release.StatusPendingUpgrade, release.StatusPendingRollback)
} }

@ -49,46 +49,47 @@ Chart.yaml file, and (if found) build the current directory into a chart.
Versioned chart archives are used by Helm package repositories. Versioned chart archives are used by Helm package repositories.
` `
type packageCmd struct { type packageOptions struct {
sign bool appVersion string // --app-version
path string dependencyUpdate bool // --dependency-update
valueFiles valueFiles destination string // --destination
values []string key string // --key
stringValues []string keyring string // --keyring
key string sign bool // --sign
keyring string stringValues []string // --set-string
version string valueFiles valueFiles // --values
appVersion string values []string // --set
destination string version string // --version
dependencyUpdate bool
// args
out io.Writer path string
home helmpath.Home home helmpath.Home
} }
func newPackageCmd(out io.Writer) *cobra.Command { func newPackageCmd(out io.Writer) *cobra.Command {
pkg := &packageCmd{out: out} o := &packageOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "package [flags] [CHART_PATH] [...]", Use: "package [flags] [CHART_PATH] [...]",
Short: "package a chart directory into a chart archive", Short: "package a chart directory into a chart archive",
Long: packageDesc, Long: packageDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
pkg.home = settings.Home o.home = settings.Home
if len(args) == 0 { if len(args) == 0 {
return fmt.Errorf("need at least one argument, the path to the chart") return fmt.Errorf("need at least one argument, the path to the chart")
} }
if pkg.sign { if o.sign {
if pkg.key == "" { if o.key == "" {
return errors.New("--key is required for signing a package") return errors.New("--key is required for signing a package")
} }
if pkg.keyring == "" { if o.keyring == "" {
return errors.New("--keyring is required for signing a package") return errors.New("--keyring is required for signing a package")
} }
} }
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
pkg.path = args[i] o.path = args[i]
if err := pkg.run(); err != nil { if err := o.run(out); err != nil {
return err return err
} }
} }
@ -97,32 +98,32 @@ func newPackageCmd(out io.Writer) *cobra.Command {
} }
f := cmd.Flags() f := cmd.Flags()
f.VarP(&pkg.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.VarP(&o.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringArrayVar(&pkg.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&pkg.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") f.BoolVar(&o.sign, "sign", false, "use a PGP private key to sign this package")
f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") f.StringVar(&o.key, "key", "", "name of the key to use when signing. Used if --sign is true")
f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "location of a public keyring")
f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version") f.StringVar(&o.version, "version", "", "set the version on the chart to this semver version")
f.StringVar(&pkg.appVersion, "app-version", "", "set the appVersion on the chart to this version") f.StringVar(&o.appVersion, "app-version", "", "set the appVersion on the chart to this version")
f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.") f.StringVarP(&o.destination, "destination", "d", ".", "location to write the chart.")
f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`) f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
return cmd return cmd
} }
func (p *packageCmd) run() error { func (o *packageOptions) run(out io.Writer) error {
path, err := filepath.Abs(p.path) path, err := filepath.Abs(o.path)
if err != nil { if err != nil {
return err return err
} }
if p.dependencyUpdate { if o.dependencyUpdate {
downloadManager := &downloader.Manager{ downloadManager := &downloader.Manager{
Out: p.out, Out: out,
ChartPath: path, ChartPath: path,
HelmHome: settings.Home, HelmHome: settings.Home,
Keyring: p.keyring, Keyring: o.keyring,
Getters: getter.All(settings), Getters: getter.All(settings),
Debug: settings.Debug, Debug: settings.Debug,
} }
@ -137,7 +138,7 @@ func (p *packageCmd) run() error {
return err return err
} }
overrideVals, err := vals(p.valueFiles, p.values, p.stringValues) overrideVals, err := vals(o.valueFiles, o.values, o.stringValues)
if err != nil { if err != nil {
return err return err
} }
@ -152,16 +153,16 @@ func (p *packageCmd) run() error {
ch.Values = newVals ch.Values = newVals
// If version is set, modify the version. // If version is set, modify the version.
if len(p.version) != 0 { if len(o.version) != 0 {
if err := setVersion(ch, p.version); err != nil { if err := setVersion(ch, o.version); err != nil {
return err return err
} }
debug("Setting version to %s", p.version) debug("Setting version to %s", o.version)
} }
if p.appVersion != "" { if o.appVersion != "" {
ch.Metadata.AppVersion = p.appVersion ch.Metadata.AppVersion = o.appVersion
debug("Setting appVersion to %s", p.appVersion) debug("Setting appVersion to %s", o.appVersion)
} }
if filepath.Base(path) != ch.Metadata.Name { if filepath.Base(path) != ch.Metadata.Name {
@ -179,7 +180,7 @@ func (p *packageCmd) run() error {
} }
var dest string var dest string
if p.destination == "." { if o.destination == "." {
// Save to the current working directory. // Save to the current working directory.
dest, err = os.Getwd() dest, err = os.Getwd()
if err != nil { if err != nil {
@ -187,18 +188,18 @@ func (p *packageCmd) run() error {
} }
} else { } else {
// Otherwise save to set destination // Otherwise save to set destination
dest = p.destination dest = o.destination
} }
name, err := chartutil.Save(ch, dest) name, err := chartutil.Save(ch, dest)
if err == nil { if err == nil {
fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name) fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", name)
} else { } else {
return fmt.Errorf("Failed to save: %s", err) return fmt.Errorf("Failed to save: %s", err)
} }
if p.sign { if o.sign {
err = p.clearsign(name) err = o.clearsign(name)
} }
return err return err
@ -215,9 +216,9 @@ func setVersion(ch *chart.Chart, ver string) error {
return nil return nil
} }
func (p *packageCmd) clearsign(filename string) error { func (o *packageOptions) clearsign(filename string) error {
// Load keyring // Load keyring
signer, err := provenance.NewFromKeyring(p.keyring, p.key) signer, err := provenance.NewFromKeyring(o.keyring, o.key)
if err != nil { if err != nil {
return err return err
} }

@ -26,11 +26,10 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pluginInstallCmd struct { type pluginInstallOptions struct {
source string source string
version string version string
home helmpath.Home home helmpath.Home
out io.Writer
} }
const pluginInstallDesc = ` const pluginInstallDesc = `
@ -41,35 +40,35 @@ Example usage:
` `
func newPluginInstallCmd(out io.Writer) *cobra.Command { func newPluginInstallCmd(out io.Writer) *cobra.Command {
pcmd := &pluginInstallCmd{out: out} o := &pluginInstallOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "install [options] <path|url>...", Use: "install [options] <path|url>...",
Short: "install one or more Helm plugins", Short: "install one or more Helm plugins",
Long: pluginInstallDesc, Long: pluginInstallDesc,
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args) return o.complete(args)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return pcmd.run() return o.run(out)
}, },
} }
cmd.Flags().StringVar(&pcmd.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed") cmd.Flags().StringVar(&o.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed")
return cmd return cmd
} }
func (pcmd *pluginInstallCmd) complete(args []string) error { func (o *pluginInstallOptions) complete(args []string) error {
if err := checkArgsLength(len(args), "plugin"); err != nil { if err := checkArgsLength(len(args), "plugin"); err != nil {
return err return err
} }
pcmd.source = args[0] o.source = args[0]
pcmd.home = settings.Home o.home = settings.Home
return nil return nil
} }
func (pcmd *pluginInstallCmd) run() error { func (o *pluginInstallOptions) run(out io.Writer) error {
installer.Debug = settings.Debug installer.Debug = settings.Debug
i, err := installer.NewForSource(pcmd.source, pcmd.version, pcmd.home) i, err := installer.NewForSource(o.source, o.version, o.home)
if err != nil { if err != nil {
return err return err
} }
@ -87,6 +86,6 @@ func (pcmd *pluginInstallCmd) run() error {
return err return err
} }
fmt.Fprintf(pcmd.out, "Installed plugin: %s\n", p.Metadata.Name) fmt.Fprintf(out, "Installed plugin: %s\n", p.Metadata.Name)
return nil return nil
} }

@ -25,25 +25,24 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pluginListCmd struct { type pluginListOptions struct {
home helmpath.Home home helmpath.Home
out io.Writer
} }
func newPluginListCmd(out io.Writer) *cobra.Command { func newPluginListCmd(out io.Writer) *cobra.Command {
pcmd := &pluginListCmd{out: out} o := &pluginListOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Short: "list installed Helm plugins", Short: "list installed Helm plugins",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
pcmd.home = settings.Home o.home = settings.Home
return pcmd.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (pcmd *pluginListCmd) run() error { func (o *pluginListOptions) run(out io.Writer) error {
debug("pluginDirs: %s", settings.PluginDirs()) debug("pluginDirs: %s", settings.PluginDirs())
plugins, err := findPlugins(settings.PluginDirs()) plugins, err := findPlugins(settings.PluginDirs())
if err != nil { if err != nil {
@ -55,6 +54,6 @@ func (pcmd *pluginListCmd) run() error {
for _, p := range plugins { for _, p := range plugins {
table.AddRow(p.Metadata.Name, p.Metadata.Version, p.Metadata.Description) table.AddRow(p.Metadata.Name, p.Metadata.Version, p.Metadata.Description)
} }
fmt.Fprintln(pcmd.out, table) fmt.Fprintln(out, table)
return nil return nil
} }

@ -28,49 +28,48 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pluginRemoveCmd struct { type pluginRemoveOptions struct {
names []string names []string
home helmpath.Home home helmpath.Home
out io.Writer
} }
func newPluginRemoveCmd(out io.Writer) *cobra.Command { func newPluginRemoveCmd(out io.Writer) *cobra.Command {
pcmd := &pluginRemoveCmd{out: out} o := &pluginRemoveOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "remove <plugin>...", Use: "remove <plugin>...",
Short: "remove one or more Helm plugins", Short: "remove one or more Helm plugins",
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args) return o.complete(args)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return pcmd.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (pcmd *pluginRemoveCmd) complete(args []string) error { func (o *pluginRemoveOptions) complete(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.New("please provide plugin name to remove") return errors.New("please provide plugin name to remove")
} }
pcmd.names = args o.names = args
pcmd.home = settings.Home o.home = settings.Home
return nil return nil
} }
func (pcmd *pluginRemoveCmd) run() error { func (o *pluginRemoveOptions) run(out io.Writer) error {
debug("loading installed plugins from %s", settings.PluginDirs()) debug("loading installed plugins from %s", settings.PluginDirs())
plugins, err := findPlugins(settings.PluginDirs()) plugins, err := findPlugins(settings.PluginDirs())
if err != nil { if err != nil {
return err return err
} }
var errorPlugins []string var errorPlugins []string
for _, name := range pcmd.names { for _, name := range o.names {
if found := findPlugin(plugins, name); found != nil { if found := findPlugin(plugins, name); found != nil {
if err := removePlugin(found); err != nil { if err := removePlugin(found); err != nil {
errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to remove plugin %s, got error (%v)", name, err)) errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to remove plugin %s, got error (%v)", name, err))
} else { } else {
fmt.Fprintf(pcmd.out, "Removed plugin: %s\n", name) fmt.Fprintf(out, "Removed plugin: %s\n", name)
} }
} else { } else {
errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))

@ -29,37 +29,36 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pluginUpdateCmd struct { type pluginUpdateOptions struct {
names []string names []string
home helmpath.Home home helmpath.Home
out io.Writer
} }
func newPluginUpdateCmd(out io.Writer) *cobra.Command { func newPluginUpdateCmd(out io.Writer) *cobra.Command {
pcmd := &pluginUpdateCmd{out: out} o := &pluginUpdateOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update <plugin>...", Use: "update <plugin>...",
Short: "update one or more Helm plugins", Short: "update one or more Helm plugins",
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args) return o.complete(args)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return pcmd.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (pcmd *pluginUpdateCmd) complete(args []string) error { func (o *pluginUpdateOptions) complete(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.New("please provide plugin name to update") return errors.New("please provide plugin name to update")
} }
pcmd.names = args o.names = args
pcmd.home = settings.Home o.home = settings.Home
return nil return nil
} }
func (pcmd *pluginUpdateCmd) run() error { func (o *pluginUpdateOptions) run(out io.Writer) error {
installer.Debug = settings.Debug installer.Debug = settings.Debug
debug("loading installed plugins from %s", settings.PluginDirs()) debug("loading installed plugins from %s", settings.PluginDirs())
plugins, err := findPlugins(settings.PluginDirs()) plugins, err := findPlugins(settings.PluginDirs())
@ -68,12 +67,12 @@ func (pcmd *pluginUpdateCmd) run() error {
} }
var errorPlugins []string var errorPlugins []string
for _, name := range pcmd.names { for _, name := range o.names {
if found := findPlugin(plugins, name); found != nil { if found := findPlugin(plugins, name); found != nil {
if err := updatePlugin(found, pcmd.home); err != nil { if err := updatePlugin(found, o.home); err != nil {
errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err)) errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err))
} else { } else {
fmt.Fprintf(pcmd.out, "Updated plugin: %s\n", name) fmt.Fprintf(out, "Updated plugin: %s\n", name)
} }
} else { } else {
errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))

@ -33,19 +33,15 @@ The argument this command takes is the name of a deployed release.
The tests to be run are defined in the chart that was installed. The tests to be run are defined in the chart that was installed.
` `
type releaseTestCmd struct { type releaseTestOptions struct {
name string name string
out io.Writer
client helm.Interface client helm.Interface
timeout int64 timeout int64
cleanup bool cleanup bool
} }
func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command { func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
rlsTest := &releaseTestCmd{ o := &releaseTestOptions{client: c}
out: out,
client: c,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "test [RELEASE]", Use: "test [RELEASE]",
@ -56,24 +52,24 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
return err return err
} }
rlsTest.name = args[0] o.name = args[0]
rlsTest.client = ensureHelmClient(rlsTest.client, false) o.client = ensureHelmClient(o.client, false)
return rlsTest.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.Int64Var(&rlsTest.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&rlsTest.cleanup, "cleanup", false, "delete test pods upon completion") f.BoolVar(&o.cleanup, "cleanup", false, "delete test pods upon completion")
return cmd return cmd
} }
func (t *releaseTestCmd) run() (err error) { func (o *releaseTestOptions) run(out io.Writer) (err error) {
c, errc := t.client.RunReleaseTest( c, errc := o.client.RunReleaseTest(
t.name, o.name,
helm.ReleaseTestTimeout(t.timeout), helm.ReleaseTestTimeout(o.timeout),
helm.ReleaseTestCleanup(t.cleanup), helm.ReleaseTestCleanup(o.cleanup),
) )
testErr := &testErr{} testErr := &testErr{}
@ -92,12 +88,9 @@ func (t *releaseTestCmd) run() (err error) {
if res.Status == release.TestRunFailure { if res.Status == release.TestRunFailure {
testErr.failed++ testErr.failed++
} }
fmt.Fprintf(out, res.Msg+"\n")
fmt.Fprintf(t.out, res.Msg+"\n")
} }
} }
} }
type testErr struct { type testErr struct {

@ -27,7 +27,7 @@ import (
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
type repoAddCmd struct { type repoAddOptions struct {
name string name string
url string url string
username string username string
@ -38,12 +38,10 @@ type repoAddCmd struct {
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
out io.Writer
} }
func newRepoAddCmd(out io.Writer) *cobra.Command { func newRepoAddCmd(out io.Writer) *cobra.Command {
add := &repoAddCmd{out: out} o := &repoAddOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "add [flags] [NAME] [URL]", Use: "add [flags] [NAME] [URL]",
@ -53,30 +51,30 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
return err return err
} }
add.name = args[0] o.name = args[0]
add.url = args[1] o.url = args[1]
add.home = settings.Home o.home = settings.Home
return add.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.StringVar(&add.username, "username", "", "chart repository username") f.StringVar(&o.username, "username", "", "chart repository username")
f.StringVar(&add.password, "password", "", "chart repository password") f.StringVar(&o.password, "password", "", "chart repository password")
f.BoolVar(&add.noupdate, "no-update", false, "raise error if repo is already registered") f.BoolVar(&o.noupdate, "no-update", false, "raise error if repo is already registered")
f.StringVar(&add.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&add.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&add.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd return cmd
} }
func (a *repoAddCmd) run() error { func (o *repoAddOptions) run(out io.Writer) error {
if err := addRepository(a.name, a.url, a.username, a.password, a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil { if err := addRepository(o.name, o.url, o.username, o.password, o.home, o.certFile, o.keyFile, o.caFile, o.noupdate); err != nil {
return err return err
} }
fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name) fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
return nil return nil
} }

@ -38,15 +38,14 @@ flag. In this case, the charts found in the current directory will be merged
into the existing index, with local charts taking priority over existing charts. into the existing index, with local charts taking priority over existing charts.
` `
type repoIndexCmd struct { type repoIndexOptions struct {
dir string dir string
url string url string
out io.Writer
merge string merge string
} }
func newRepoIndexCmd(out io.Writer) *cobra.Command { func newRepoIndexCmd(out io.Writer) *cobra.Command {
index := &repoIndexCmd{out: out} o := &repoIndexOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "index [flags] [DIR]", Use: "index [flags] [DIR]",
@ -57,20 +56,20 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
return err return err
} }
index.dir = args[0] o.dir = args[0]
return index.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.StringVar(&index.url, "url", "", "url of chart repository") f.StringVar(&o.url, "url", "", "url of chart repository")
f.StringVar(&index.merge, "merge", "", "merge the generated index into the given index") f.StringVar(&o.merge, "merge", "", "merge the generated index into the given index")
return cmd return cmd
} }
func (i *repoIndexCmd) run() error { func (i *repoIndexOptions) run(out io.Writer) error {
path, err := filepath.Abs(i.dir) path, err := filepath.Abs(i.dir)
if err != nil { if err != nil {
return err return err

@ -28,28 +28,27 @@ import (
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
type repoListCmd struct { type repoListOptions struct {
out io.Writer
home helmpath.Home home helmpath.Home
} }
func newRepoListCmd(out io.Writer) *cobra.Command { func newRepoListCmd(out io.Writer) *cobra.Command {
list := &repoListCmd{out: out} o := &repoListOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list [flags]", Use: "list [flags]",
Short: "list chart repositories", Short: "list chart repositories",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
list.home = settings.Home o.home = settings.Home
return list.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (a *repoListCmd) run() error { func (o *repoListOptions) run(out io.Writer) error {
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile()) f, err := repo.LoadRepositoriesFile(o.home.RepositoryFile())
if err != nil { if err != nil {
return err return err
} }
@ -61,6 +60,6 @@ func (a *repoListCmd) run() error {
for _, re := range f.Repositories { for _, re := range f.Repositories {
table.AddRow(re.Name, re.URL) table.AddRow(re.Name, re.URL)
} }
fmt.Fprintln(a.out, table) fmt.Fprintln(out, table)
return nil return nil
} }

@ -27,14 +27,13 @@ import (
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
type repoRemoveCmd struct { type repoRemoveOptions struct {
out io.Writer
name string name string
home helmpath.Home home helmpath.Home
} }
func newRepoRemoveCmd(out io.Writer) *cobra.Command { func newRepoRemoveCmd(out io.Writer) *cobra.Command {
remove := &repoRemoveCmd{out: out} o := &repoRemoveOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "remove [flags] [NAME]", Use: "remove [flags] [NAME]",
@ -44,18 +43,18 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "name of chart repository"); err != nil { if err := checkArgsLength(len(args), "name of chart repository"); err != nil {
return err return err
} }
remove.name = args[0] o.name = args[0]
remove.home = settings.Home o.home = settings.Home
return remove.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (r *repoRemoveCmd) run() error { func (r *repoRemoveOptions) run(out io.Writer) error {
return removeRepoLine(r.out, r.name, r.home) return removeRepoLine(out, r.name, r.home)
} }
func removeRepoLine(out io.Writer, name string, home helmpath.Home) error { func removeRepoLine(out io.Writer, name string, home helmpath.Home) error {

@ -39,32 +39,29 @@ future releases.
var errNoRepositories = errors.New("no repositories found. You must add one before updating") var errNoRepositories = errors.New("no repositories found. You must add one before updating")
type repoUpdateCmd struct { type repoUpdateOptions struct {
update func([]*repo.ChartRepository, io.Writer, helmpath.Home) update func([]*repo.ChartRepository, io.Writer, helmpath.Home)
home helmpath.Home home helmpath.Home
out io.Writer
} }
func newRepoUpdateCmd(out io.Writer) *cobra.Command { func newRepoUpdateCmd(out io.Writer) *cobra.Command {
u := &repoUpdateCmd{ o := &repoUpdateOptions{update: updateCharts}
out: out,
update: updateCharts,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update", Use: "update",
Aliases: []string{"up"}, Aliases: []string{"up"},
Short: "update information of available charts locally from chart repositories", Short: "update information of available charts locally from chart repositories",
Long: updateDesc, Long: updateDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
u.home = settings.Home o.home = settings.Home
return u.run() return o.run(out)
}, },
} }
return cmd return cmd
} }
func (u *repoUpdateCmd) run() error { func (o *repoUpdateOptions) run(out io.Writer) error {
f, err := repo.LoadRepositoriesFile(u.home.RepositoryFile()) f, err := repo.LoadRepositoriesFile(o.home.RepositoryFile())
if err != nil { if err != nil {
return err return err
} }
@ -81,7 +78,7 @@ func (u *repoUpdateCmd) run() error {
repos = append(repos, r) repos = append(repos, r)
} }
u.update(repos, u.out, u.home) o.update(repos, out, o.home)
return nil return nil
} }

@ -51,12 +51,11 @@ func TestUpdateCmd(t *testing.T) {
fmt.Fprintln(out, re.Config.Name) fmt.Fprintln(out, re.Config.Name)
} }
} }
uc := &repoUpdateCmd{ o := &repoUpdateOptions{
update: updater, update: updater,
home: helmpath.Home(thome), home: helmpath.Home(thome),
out: out,
} }
if err := uc.run(); err != nil { if err := o.run(out); err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -34,24 +34,20 @@ second is a revision (version) number. To see revision numbers, run
'helm history RELEASE'. 'helm history RELEASE'.
` `
type rollbackCmd struct { type rollbackOptions struct {
name string name string
revision int revision int
dryRun bool dryRun bool
recreate bool recreate bool
force bool force bool
disableHooks bool disableHooks bool
out io.Writer
client helm.Interface client helm.Interface
timeout int64 timeout int64
wait bool wait bool
} }
func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
rollback := &rollbackCmd{ o := &rollbackOptions{client: c}
out: out,
client: c,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rollback [flags] [RELEASE] [REVISION]", Use: "rollback [flags] [RELEASE] [REVISION]",
@ -62,45 +58,45 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
return err return err
} }
rollback.name = args[0] o.name = args[0]
v64, err := strconv.ParseInt(args[1], 10, 32) v64, err := strconv.ParseInt(args[1], 10, 32)
if err != nil { if err != nil {
return fmt.Errorf("invalid revision number '%q': %s", args[1], err) return fmt.Errorf("invalid revision number '%q': %s", args[1], err)
} }
rollback.revision = int(v64) o.revision = int(v64)
rollback.client = ensureHelmClient(rollback.client, false) o.client = ensureHelmClient(o.client, false)
return rollback.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&rollback.dryRun, "dry-run", false, "simulate a rollback") f.BoolVar(&o.dryRun, "dry-run", false, "simulate a rollback")
f.BoolVar(&rollback.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&o.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&rollback.force, "force", false, "force resource update through delete/recreate if needed") f.BoolVar(&o.force, "force", false, "force resource update through delete/recreate if needed")
f.BoolVar(&rollback.disableHooks, "no-hooks", false, "prevent hooks from running during rollback") f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during rollback")
f.Int64Var(&rollback.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&rollback.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&o.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
return cmd return cmd
} }
func (r *rollbackCmd) run() error { func (o *rollbackOptions) run(out io.Writer) error {
_, err := r.client.RollbackRelease( _, err := o.client.RollbackRelease(
r.name, o.name,
helm.RollbackDryRun(r.dryRun), helm.RollbackDryRun(o.dryRun),
helm.RollbackRecreate(r.recreate), helm.RollbackRecreate(o.recreate),
helm.RollbackForce(r.force), helm.RollbackForce(o.force),
helm.RollbackDisableHooks(r.disableHooks), helm.RollbackDisableHooks(o.disableHooks),
helm.RollbackVersion(r.revision), helm.RollbackVersion(o.revision),
helm.RollbackTimeout(r.timeout), helm.RollbackTimeout(o.timeout),
helm.RollbackWait(r.wait)) helm.RollbackWait(o.wait))
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintf(r.out, "Rollback was a success! Happy Helming!\n") fmt.Fprintf(out, "Rollback was a success! Happy Helming!\n")
return nil return nil
} }

@ -40,8 +40,7 @@ Repositories are managed with 'helm repo' commands.
// searchMaxScore suggests that any score higher than this is not considered a match. // searchMaxScore suggests that any score higher than this is not considered a match.
const searchMaxScore = 25 const searchMaxScore = 25
type searchCmd struct { type searchOptions struct {
out io.Writer
helmhome helmpath.Home helmhome helmpath.Home
versions bool versions bool
@ -50,28 +49,28 @@ type searchCmd struct {
} }
func newSearchCmd(out io.Writer) *cobra.Command { func newSearchCmd(out io.Writer) *cobra.Command {
sc := &searchCmd{out: out} o := &searchOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "search [keyword]", Use: "search [keyword]",
Short: "search for a keyword in charts", Short: "search for a keyword in charts",
Long: searchDesc, Long: searchDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
sc.helmhome = settings.Home o.helmhome = settings.Home
return sc.run(args) return o.run(out, args)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching") f.BoolVarP(&o.regexp, "regexp", "r", false, "use regular expressions for searching")
f.BoolVarP(&sc.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line") f.BoolVarP(&o.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line")
f.StringVarP(&sc.version, "version", "v", "", "search using semantic versioning constraints") f.StringVarP(&o.version, "version", "v", "", "search using semantic versioning constraints")
return cmd return cmd
} }
func (s *searchCmd) run(args []string) error { func (o *searchOptions) run(out io.Writer, args []string) error {
index, err := s.buildIndex() index, err := o.buildIndex(out)
if err != nil { if err != nil {
return err return err
} }
@ -81,29 +80,29 @@ func (s *searchCmd) run(args []string) error {
res = index.All() res = index.All()
} else { } else {
q := strings.Join(args, " ") q := strings.Join(args, " ")
res, err = index.Search(q, searchMaxScore, s.regexp) res, err = index.Search(q, searchMaxScore, o.regexp)
if err != nil { if err != nil {
return err return err
} }
} }
search.SortScore(res) search.SortScore(res)
data, err := s.applyConstraint(res) data, err := o.applyConstraint(res)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(s.out, s.formatSearchResults(data)) fmt.Fprintln(out, o.formatSearchResults(data))
return nil return nil
} }
func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) { func (o *searchOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) {
if len(s.version) == 0 { if len(o.version) == 0 {
return res, nil return res, nil
} }
constraint, err := semver.NewConstraint(s.version) constraint, err := semver.NewConstraint(o.version)
if err != nil { if err != nil {
return res, fmt.Errorf("an invalid version/constraint format: %s", err) return res, fmt.Errorf("an invalid version/constraint format: %s", err)
} }
@ -117,7 +116,7 @@ func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, err
v, err := semver.NewVersion(r.Chart.Version) v, err := semver.NewVersion(r.Chart.Version)
if err != nil || constraint.Check(v) { if err != nil || constraint.Check(v) {
data = append(data, r) data = append(data, r)
if !s.versions { if !o.versions {
foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches
} }
} }
@ -126,7 +125,7 @@ func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, err
return data, nil return data, nil
} }
func (s *searchCmd) formatSearchResults(res []*search.Result) string { func (o *searchOptions) formatSearchResults(res []*search.Result) string {
if len(res) == 0 { if len(res) == 0 {
return "No results found" return "No results found"
} }
@ -139,9 +138,9 @@ func (s *searchCmd) formatSearchResults(res []*search.Result) string {
return table.String() return table.String()
} }
func (s *searchCmd) buildIndex() (*search.Index, error) { func (o *searchOptions) buildIndex(out io.Writer) (*search.Index, error) {
// Load the repositories.yaml // Load the repositories.yaml
rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) rf, err := repo.LoadRepositoriesFile(o.helmhome.RepositoryFile())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,14 +148,15 @@ func (s *searchCmd) buildIndex() (*search.Index, error) {
i := search.NewIndex() i := search.NewIndex()
for _, re := range rf.Repositories { for _, re := range rf.Repositories {
n := re.Name n := re.Name
f := s.helmhome.CacheIndex(n) f := o.helmhome.CacheIndex(n)
ind, err := repo.LoadIndexFile(f) ind, err := repo.LoadIndexFile(f)
if err != nil { if err != nil {
fmt.Fprintf(s.out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) // TODO should print to stderr
fmt.Fprintf(out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n)
continue continue
} }
i.AddRepo(n, ind, s.versions || len(s.version) > 0) i.AddRepo(n, ind, o.versions || len(o.version) > 0)
} }
return i, nil return i, nil
} }

@ -44,19 +44,15 @@ The status consists of:
- additional notes provided by the chart - additional notes provided by the chart
` `
type statusCmd struct { type statusOptions struct {
release string release string
out io.Writer
client helm.Interface client helm.Interface
version int version int
outfmt string outfmt string
} }
func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command { func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
status := &statusCmd{ o := &statusOptions{client: client}
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "status [flags] RELEASE_NAME", Use: "status [flags] RELEASE_NAME",
@ -66,45 +62,45 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errReleaseRequired return errReleaseRequired
} }
status.release = args[0] o.release = args[0]
status.client = ensureHelmClient(status.client, false) o.client = ensureHelmClient(o.client, false)
return status.run() return o.run(out)
}, },
} }
cmd.PersistentFlags().IntVar(&status.version, "revision", 0, "if set, display the status of the named release with revision") cmd.PersistentFlags().IntVar(&o.version, "revision", 0, "if set, display the status of the named release with revision")
cmd.PersistentFlags().StringVarP(&status.outfmt, "output", "o", "", "output the status in the specified format (json or yaml)") cmd.PersistentFlags().StringVarP(&o.outfmt, "output", "o", "", "output the status in the specified format (json or yaml)")
return cmd return cmd
} }
func (s *statusCmd) run() error { func (o *statusOptions) run(out io.Writer) error {
res, err := s.client.ReleaseStatus(s.release, s.version) res, err := o.client.ReleaseStatus(o.release, o.version)
if err != nil { if err != nil {
return err return err
} }
switch s.outfmt { switch o.outfmt {
case "": case "":
PrintStatus(s.out, res) PrintStatus(out, res)
return nil return nil
case "json": case "json":
data, err := json.Marshal(res) data, err := json.Marshal(res)
if err != nil { if err != nil {
return fmt.Errorf("Failed to Marshal JSON output: %s", err) return fmt.Errorf("Failed to Marshal JSON output: %s", err)
} }
s.out.Write(data) out.Write(data)
return nil return nil
case "yaml": case "yaml":
data, err := yaml.Marshal(res) data, err := yaml.Marshal(res)
if err != nil { if err != nil {
return fmt.Errorf("Failed to Marshal YAML output: %s", err) return fmt.Errorf("Failed to Marshal YAML output: %s", err)
} }
s.out.Write(data) out.Write(data)
return nil return nil
} }
return fmt.Errorf("Unknown output format %q", s.outfmt) return fmt.Errorf("Unknown output format %q", o.outfmt)
} }
// PrintStatus prints out the status of a release. Shared because also used by // PrintStatus prints out the status of a release. Shared because also used by

@ -60,10 +60,9 @@ To render just one template in a chart, use '-x':
$ helm template mychart -x templates/deployment.yaml $ helm template mychart -x templates/deployment.yaml
` `
type templateCmd struct { type templateOptions struct {
valueFiles valueFiles valueFiles valueFiles
chartPath string chartPath string
out io.Writer
values []string values []string
stringValues []string stringValues []string
nameTemplate string nameTemplate string
@ -75,52 +74,51 @@ type templateCmd struct {
} }
func newTemplateCmd(out io.Writer) *cobra.Command { func newTemplateCmd(out io.Writer) *cobra.Command {
o := &templateOptions{}
t := &templateCmd{
out: out,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "template [flags] CHART", Use: "template [flags] CHART",
Short: fmt.Sprintf("locally render templates"), Short: fmt.Sprintf("locally render templates"),
Long: templateDesc, Long: templateDesc,
RunE: t.run, RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("chart is required")
}
// verify chart path exists
if _, err := os.Stat(args[0]); err == nil {
if o.chartPath, err = filepath.Abs(args[0]); err != nil {
return err
}
} else {
return err
}
return o.run(out)
},
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&t.showNotes, "notes", false, "show the computed NOTES.txt file as well") f.BoolVar(&o.showNotes, "notes", false, "show the computed NOTES.txt file as well")
f.StringVarP(&t.releaseName, "name", "", "RELEASE-NAME", "release name") f.StringVarP(&o.releaseName, "name", "", "RELEASE-NAME", "release name")
f.StringArrayVarP(&t.renderFiles, "execute", "x", []string{}, "only execute the given templates") f.StringArrayVarP(&o.renderFiles, "execute", "x", []string{}, "only execute the given templates")
f.VarP(&t.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") f.VarP(&o.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
f.StringArrayVar(&t.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&t.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringVar(&t.nameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&o.nameTemplate, "name-template", "", "specify template used to name the release")
f.StringVar(&t.kubeVersion, "kube-version", defaultKubeVersion, "kubernetes version used as Capabilities.KubeVersion.Major/Minor") f.StringVar(&o.kubeVersion, "kube-version", defaultKubeVersion, "kubernetes version used as Capabilities.KubeVersion.Major/Minor")
f.StringVar(&t.outputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") f.StringVar(&o.outputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
return cmd return cmd
} }
func (t *templateCmd) run(cmd *cobra.Command, args []string) error { func (o *templateOptions) run(out io.Writer) error {
if len(args) < 1 {
return errors.New("chart is required")
}
// verify chart path exists
if _, err := os.Stat(args[0]); err == nil {
if t.chartPath, err = filepath.Abs(args[0]); err != nil {
return err
}
} else {
return err
}
// verify specified templates exist relative to chart // verify specified templates exist relative to chart
rf := []string{} rf := []string{}
var af string var af string
var err error var err error
if len(t.renderFiles) > 0 { if len(o.renderFiles) > 0 {
for _, f := range t.renderFiles { for _, f := range o.renderFiles {
if !filepath.IsAbs(f) { if !filepath.IsAbs(f) {
af, err = filepath.Abs(filepath.Join(t.chartPath, f)) af, err = filepath.Abs(filepath.Join(o.chartPath, f))
if err != nil { if err != nil {
return fmt.Errorf("could not resolve template path: %s", err) return fmt.Errorf("could not resolve template path: %s", err)
} }
@ -136,29 +134,29 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
} }
// verify that output-dir exists if provided // verify that output-dir exists if provided
if t.outputDir != "" { if o.outputDir != "" {
_, err = os.Stat(t.outputDir) _, err = os.Stat(o.outputDir)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return fmt.Errorf("output-dir '%s' does not exist", t.outputDir) return fmt.Errorf("output-dir '%s' does not exist", o.outputDir)
} }
} }
// get combined values and create config // get combined values and create config
config, err := vals(t.valueFiles, t.values, t.stringValues) config, err := vals(o.valueFiles, o.values, o.stringValues)
if err != nil { if err != nil {
return err return err
} }
// If template is specified, try to run the template. // If template is specified, try to run the template.
if t.nameTemplate != "" { if o.nameTemplate != "" {
t.releaseName, err = generateName(t.nameTemplate) o.releaseName, err = generateName(o.nameTemplate)
if err != nil { if err != nil {
return err return err
} }
} }
// Check chart requirements to make sure all dependencies are present in /charts // Check chart requirements to make sure all dependencies are present in /charts
c, err := chartutil.Load(t.chartPath) c, err := chartutil.Load(o.chartPath)
if err != nil { if err != nil {
return err return err
} }
@ -171,7 +169,7 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot load requirements: %v", err) return fmt.Errorf("cannot load requirements: %v", err)
} }
options := chartutil.ReleaseOptions{ options := chartutil.ReleaseOptions{
Name: t.releaseName, Name: o.releaseName,
Time: time.Now(), Time: time.Now(),
Namespace: getNamespace(), Namespace: getNamespace(),
} }
@ -195,7 +193,7 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
} }
// kubernetes version // kubernetes version
kv, err := semver.NewVersion(t.kubeVersion) kv, err := semver.NewVersion(o.kubeVersion)
if err != nil { if err != nil {
return fmt.Errorf("could not parse a kubernetes version: %v", err) return fmt.Errorf("could not parse a kubernetes version: %v", err)
} }
@ -208,14 +206,14 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
return err return err
} }
out, err := renderer.Render(c, vals) rendered, err := renderer.Render(c, vals)
listManifests := []tiller.Manifest{} listManifests := []tiller.Manifest{}
if err != nil { if err != nil {
return err return err
} }
// extract kind and name // extract kind and name
re := regexp.MustCompile("kind:(.*)\n") re := regexp.MustCompile("kind:(.*)\n")
for k, v := range out { for k, v := range rendered {
match := re.FindStringSubmatch(v) match := re.FindStringSubmatch(v)
h := "Unknown" h := "Unknown"
if len(match) == 2 { if len(match) == 2 {
@ -228,7 +226,7 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
// make needle path absolute // make needle path absolute
d := strings.Split(needle, string(os.PathSeparator)) d := strings.Split(needle, string(os.PathSeparator))
dd := d[1:] dd := d[1:]
an := filepath.Join(t.chartPath, strings.Join(dd, string(os.PathSeparator))) an := filepath.Join(o.chartPath, strings.Join(dd, string(os.PathSeparator)))
for _, h := range haystack { for _, h := range haystack {
if h == an { if h == an {
@ -239,34 +237,34 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
} }
if settings.Debug { if settings.Debug {
rel := &release.Release{ rel := &release.Release{
Name: t.releaseName, Name: o.releaseName,
Chart: c, Chart: c,
Config: config, Config: config,
Version: 1, Version: 1,
Info: &release.Info{LastDeployed: time.Now()}, Info: &release.Info{LastDeployed: time.Now()},
} }
printRelease(os.Stdout, rel) printRelease(out, rel)
} }
for _, m := range tiller.SortByKind(listManifests) { for _, m := range tiller.SortByKind(listManifests) {
if len(t.renderFiles) > 0 && !in(m.Name, rf) { if len(o.renderFiles) > 0 && !in(m.Name, rf) {
continue continue
} }
data := m.Content data := m.Content
b := filepath.Base(m.Name) b := filepath.Base(m.Name)
if !t.showNotes && b == "NOTES.txt" { if !o.showNotes && b == "NOTES.txt" {
continue continue
} }
if strings.HasPrefix(b, "_") { if strings.HasPrefix(b, "_") {
continue continue
} }
if t.outputDir != "" { if o.outputDir != "" {
// blank template after execution // blank template after execution
if whitespaceRegex.MatchString(data) { if whitespaceRegex.MatchString(data) {
continue continue
} }
err = writeToFile(t.outputDir, m.Name, data) err = writeToFile(o.outputDir, m.Name, data)
if err != nil { if err != nil {
return err return err
} }

@ -53,10 +53,9 @@ set for a key called 'foo', the 'newbar' value would take precedence:
$ helm upgrade --set foo=bar --set foo=newbar redis ./redis $ helm upgrade --set foo=bar --set foo=newbar redis ./redis
` `
type upgradeCmd struct { type upgradeOptions struct {
release string release string
chart string chart string
out io.Writer
client helm.Interface client helm.Interface
dryRun bool dryRun bool
recreate bool recreate bool
@ -84,11 +83,7 @@ type upgradeCmd struct {
} }
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
o := &upgradeOptions{client: client}
upgrade := &upgradeCmd{
out: out,
client: client,
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "upgrade [RELEASE] [CHART]", Use: "upgrade [RELEASE] [CHART]",
@ -99,82 +94,81 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
return err return err
} }
if upgrade.version == "" && upgrade.devel { if o.version == "" && o.devel {
debug("setting version to >0.0.0-0") debug("setting version to >0.0.0-0")
upgrade.version = ">0.0.0-0" o.version = ">0.0.0-0"
} }
upgrade.release = args[0] o.release = args[0]
upgrade.chart = args[1] o.chart = args[1]
upgrade.client = ensureHelmClient(upgrade.client, false) o.client = ensureHelmClient(o.client, false)
return upgrade.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.VarP(&o.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade") f.BoolVar(&o.dryRun, "dry-run", false, "simulate an upgrade")
f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&o.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed") f.BoolVar(&o.force, "force", false, "force resource update through delete/recreate if needed")
f.StringArrayVar(&upgrade.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&upgrade.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&o.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks") f.BoolVar(&o.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks") f.BoolVar(&o.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading") f.BoolVar(&o.verify, "verify", false, "verify the provenance of the chart before upgrading")
f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys")
f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install") f.BoolVarP(&o.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.StringVar(&o.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") f.BoolVar(&o.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.") f.BoolVar(&o.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.")
f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&o.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&o.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart") f.StringVar(&o.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&o.password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") f.BoolVar(&o.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.MarkDeprecated("disable-hooks", "use --no-hooks instead") f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
return cmd return cmd
} }
func (u *upgradeCmd) run() error { func (o *upgradeOptions) run(out io.Writer) error {
chartPath, err := locateChartPath(u.repoURL, u.username, u.password, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) chartPath, err := locateChartPath(o.repoURL, o.username, o.password, o.chart, o.version, o.verify, o.keyring, o.certFile, o.keyFile, o.caFile)
if err != nil { if err != nil {
return err return err
} }
if u.install { if o.install {
// If a release does not exist, install it. If another error occurs during // If a release does not exist, install it. If another error occurs during
// the check, ignore the error and continue with the upgrade. // the check, ignore the error and continue with the upgrade.
_, err := u.client.ReleaseHistory(u.release, 1) _, err := o.client.ReleaseHistory(o.release, 1)
if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound(u.release).Error()) { if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound(o.release).Error()) {
fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release) fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", o.release)
ic := &installCmd{ io := &installOptions{
chartPath: chartPath, chartPath: chartPath,
client: u.client, client: o.client,
out: u.out, name: o.release,
name: u.release, valueFiles: o.valueFiles,
valueFiles: u.valueFiles, dryRun: o.dryRun,
dryRun: u.dryRun, verify: o.verify,
verify: u.verify, disableHooks: o.disableHooks,
disableHooks: u.disableHooks, keyring: o.keyring,
keyring: u.keyring, values: o.values,
values: u.values, stringValues: o.stringValues,
stringValues: u.stringValues, timeout: o.timeout,
timeout: u.timeout, wait: o.wait,
wait: u.wait,
} }
return ic.run() return io.run(out)
} }
} }
rawVals, err := vals(u.valueFiles, u.values, u.stringValues) rawVals, err := vals(o.valueFiles, o.values, o.stringValues)
if err != nil { if err != nil {
return err return err
} }
@ -192,34 +186,34 @@ func (u *upgradeCmd) run() error {
return err return err
} }
resp, err := u.client.UpdateRelease( resp, err := o.client.UpdateRelease(
u.release, o.release,
chartPath, chartPath,
helm.UpdateValueOverrides(rawVals), helm.UpdateValueOverrides(rawVals),
helm.UpgradeDryRun(u.dryRun), helm.UpgradeDryRun(o.dryRun),
helm.UpgradeRecreate(u.recreate), helm.UpgradeRecreate(o.recreate),
helm.UpgradeForce(u.force), helm.UpgradeForce(o.force),
helm.UpgradeDisableHooks(u.disableHooks), helm.UpgradeDisableHooks(o.disableHooks),
helm.UpgradeTimeout(u.timeout), helm.UpgradeTimeout(o.timeout),
helm.ResetValues(u.resetValues), helm.ResetValues(o.resetValues),
helm.ReuseValues(u.reuseValues), helm.ReuseValues(o.reuseValues),
helm.UpgradeWait(u.wait)) helm.UpgradeWait(o.wait))
if err != nil { if err != nil {
return fmt.Errorf("UPGRADE FAILED: %v", err) return fmt.Errorf("UPGRADE FAILED: %v", err)
} }
if settings.Debug { if settings.Debug {
printRelease(u.out, resp) printRelease(out, resp)
} }
fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release) fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", o.release)
// Print the status like status command does // Print the status like status command does
status, err := u.client.ReleaseStatus(u.release, 0) status, err := o.client.ReleaseStatus(o.release, 0)
if err != nil { if err != nil {
return err return err
} }
PrintStatus(u.out, status) PrintStatus(out, status)
return nil return nil
} }

@ -35,15 +35,13 @@ This command can be used to verify a local chart. Several other commands provide
the 'helm package --sign' command. the 'helm package --sign' command.
` `
type verifyCmd struct { type verifyOptions struct {
keyring string keyring string
chartfile string chartfile string
out io.Writer
} }
func newVerifyCmd(out io.Writer) *cobra.Command { func newVerifyCmd(out io.Writer) *cobra.Command {
vc := &verifyCmd{out: out} o := &verifyOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "verify [flags] PATH", Use: "verify [flags] PATH",
@ -53,18 +51,18 @@ func newVerifyCmd(out io.Writer) *cobra.Command {
if len(args) == 0 { if len(args) == 0 {
return errors.New("a path to a package file is required") return errors.New("a path to a package file is required")
} }
vc.chartfile = args[0] o.chartfile = args[0]
return vc.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.StringVar(&vc.keyring, "keyring", defaultKeyring(), "keyring containing public keys") f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
return cmd return cmd
} }
func (v *verifyCmd) run() error { func (o *verifyOptions) run(out io.Writer) error {
_, err := downloader.VerifyChart(v.chartfile, v.keyring) _, err := downloader.VerifyChart(o.chartfile, o.keyring)
return err return err
} }

@ -40,39 +40,38 @@ version.BuildInfo{Version:"v2.0.0", GitCommit:"ff52399e51bb880526e9cd0ed8386f643
built, and "dirty" if the binary was built from locally modified code. built, and "dirty" if the binary was built from locally modified code.
` `
type versionCmd struct { type versionOptions struct {
out io.Writer
short bool short bool
template string template string
} }
func newVersionCmd(out io.Writer) *cobra.Command { func newVersionCmd(out io.Writer) *cobra.Command {
version := &versionCmd{out: out} o := &versionOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "version", Use: "version",
Short: "print the client version information", Short: "print the client version information",
Long: versionDesc, Long: versionDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return version.run() return o.run(out)
}, },
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&version.short, "short", false, "print the version number") f.BoolVar(&o.short, "short", false, "print the version number")
f.StringVar(&version.template, "template", "", "template for version string format") f.StringVar(&o.template, "template", "", "template for version string format")
return cmd return cmd
} }
func (v *versionCmd) run() error { func (o *versionOptions) run(out io.Writer) error {
if v.template != "" { if o.template != "" {
tt, err := template.New("_").Parse(v.template) tt, err := template.New("_").Parse(o.template)
if err != nil { if err != nil {
return err return err
} }
return tt.Execute(v.out, version.GetBuildInfo()) return tt.Execute(out, version.GetBuildInfo())
} }
fmt.Fprintln(v.out, formatVersion(v.short)) fmt.Fprintln(out, formatVersion(o.short))
return nil return nil
} }

Loading…
Cancel
Save