mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
Merge pull request #5365 from bacongobbler/remove-pkg-tiller
ref: remove pkg/helm, pkg/hapi, pkg/tiller
This commit is contained in:
commit
e5094169d7
222 changed files with 3809 additions and 9056 deletions
|
|
@ -35,7 +35,7 @@ func TestCreateCmd(t *testing.T) {
|
|||
cname := "testchart"
|
||||
|
||||
// Run a create
|
||||
if _, err := executeCommand(nil, "create "+cname); err != nil {
|
||||
if _, _, err := executeActionCommand("create " + cname); err != nil {
|
||||
t.Errorf("Failed to run create: %s", err)
|
||||
return
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ func TestCreateStarterCmd(t *testing.T) {
|
|||
defer testChdir(t, tdir)()
|
||||
|
||||
// Run a create
|
||||
if _, err := executeCommand(nil, fmt.Sprintf("--home='%s' create --starter=starterchart %s", hh.String(), cname)); err != nil {
|
||||
if _, _, err := executeActionCommand(fmt.Sprintf("--home='%s' create --starter=starterchart %s", hh.String(), cname)); err != nil {
|
||||
t.Errorf("Failed to run create: %s", err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,18 +16,13 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
const dependencyDesc = `
|
||||
|
|
@ -103,14 +98,8 @@ func newDependencyCmd(out io.Writer) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
type dependencyLisOptions struct {
|
||||
chartpath string
|
||||
}
|
||||
|
||||
func newDependencyListCmd(out io.Writer) *cobra.Command {
|
||||
o := &dependencyLisOptions{
|
||||
chartpath: ".",
|
||||
}
|
||||
client := action.NewDependency()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list CHART",
|
||||
|
|
@ -119,151 +108,12 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
|
|||
Long: dependencyListDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
o.chartpath = filepath.Clean(args[0])
|
||||
chartpath = filepath.Clean(args[0])
|
||||
}
|
||||
return o.run(out)
|
||||
return client.List(chartpath, out)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *dependencyLisOptions) run(out io.Writer) error {
|
||||
c, err := loader.Load(o.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Metadata.Dependencies == nil {
|
||||
fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(o.chartpath, "charts"))
|
||||
return nil
|
||||
}
|
||||
|
||||
o.printDependencies(out, c.Metadata.Dependencies)
|
||||
fmt.Fprintln(out)
|
||||
o.printMissing(out, c.Metadata.Dependencies)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *dependencyLisOptions) dependencyStatus(dep *chart.Dependency) string {
|
||||
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
|
||||
archives, err := filepath.Glob(filepath.Join(o.chartpath, "charts", filename))
|
||||
if err != nil {
|
||||
return "bad pattern"
|
||||
} else if len(archives) > 1 {
|
||||
return "too many matches"
|
||||
} else if len(archives) == 1 {
|
||||
archive := archives[0]
|
||||
if _, err := os.Stat(archive); err == nil {
|
||||
c, err := loader.Load(archive)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
if c.Name() != dep.Name {
|
||||
return "misnamed"
|
||||
}
|
||||
|
||||
if c.Metadata.Version != dep.Version {
|
||||
constraint, err := semver.NewConstraint(dep.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(c.Metadata.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
if constraint.Check(v) {
|
||||
return "ok"
|
||||
}
|
||||
return "wrong version"
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
}
|
||||
|
||||
folder := filepath.Join(o.chartpath, "charts", dep.Name)
|
||||
if fi, err := os.Stat(folder); err != nil {
|
||||
return "missing"
|
||||
} else if !fi.IsDir() {
|
||||
return "mispackaged"
|
||||
}
|
||||
|
||||
c, err := loader.Load(folder)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
|
||||
if c.Name() != dep.Name {
|
||||
return "misnamed"
|
||||
}
|
||||
|
||||
if c.Metadata.Version != dep.Version {
|
||||
constraint, err := semver.NewConstraint(dep.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(c.Metadata.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
if constraint.Check(v) {
|
||||
return "unpacked"
|
||||
}
|
||||
return "wrong version"
|
||||
}
|
||||
|
||||
return "unpacked"
|
||||
}
|
||||
|
||||
// printDependencies prints all of the dependencies in the yaml file.
|
||||
func (o *dependencyLisOptions) printDependencies(out io.Writer, reqs []*chart.Dependency) {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 80
|
||||
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
|
||||
for _, row := range reqs {
|
||||
table.AddRow(row.Name, row.Version, row.Repository, o.dependencyStatus(row))
|
||||
}
|
||||
fmt.Fprintln(out, table)
|
||||
}
|
||||
|
||||
// printMissing prints warnings about charts that are present on disk, but are
|
||||
// not in Charts.yaml.
|
||||
func (o *dependencyLisOptions) printMissing(out io.Writer, reqs []*chart.Dependency) {
|
||||
folder := filepath.Join(o.chartpath, "charts/*")
|
||||
files, err := filepath.Glob(folder)
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Warning: %s\n", err)
|
||||
}
|
||||
// Skip anything that is not a directory and not a tgz file.
|
||||
if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
|
||||
continue
|
||||
}
|
||||
c, err := loader.Load(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, d := range reqs {
|
||||
if d.Name == c.Name() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,14 @@ package main
|
|||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
)
|
||||
|
|
@ -36,17 +40,8 @@ If no lock file is found, 'helm dependency build' will mirror the behavior
|
|||
of 'helm dependency update'.
|
||||
`
|
||||
|
||||
type dependencyBuildOptions struct {
|
||||
keyring string // --keyring
|
||||
verify bool // --verify
|
||||
|
||||
chartpath string
|
||||
}
|
||||
|
||||
func newDependencyBuildCmd(out io.Writer) *cobra.Command {
|
||||
o := &dependencyBuildOptions{
|
||||
chartpath: ".",
|
||||
}
|
||||
client := action.NewDependency()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build CHART",
|
||||
|
|
@ -54,31 +49,38 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
|
|||
Long: dependencyBuildDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
o.chartpath = args[0]
|
||||
chartpath = filepath.Clean(args[0])
|
||||
}
|
||||
return o.run(out)
|
||||
man := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: chartpath,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: client.Keyring,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if client.Verify {
|
||||
man.Verify = downloader.VerifyIfPossible
|
||||
}
|
||||
if settings.Debug {
|
||||
man.Debug = true
|
||||
}
|
||||
return man.Build()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&o.verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *dependencyBuildOptions) run(out io.Writer) error {
|
||||
man := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: o.chartpath,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: o.keyring,
|
||||
Getters: getter.All(settings),
|
||||
// defaultKeyring returns the expanded path to the default keyring.
|
||||
func defaultKeyring() string {
|
||||
if v, ok := os.LookupEnv("GNUPGHOME"); ok {
|
||||
return filepath.Join(v, "pubring.gpg")
|
||||
}
|
||||
if o.verify {
|
||||
man.Verify = downloader.VerifyIfPossible
|
||||
}
|
||||
|
||||
return man.Build()
|
||||
return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func TestDependencyBuildCmd(t *testing.T) {
|
|||
}
|
||||
|
||||
cmd := fmt.Sprintf("--home='%s' dependency build '%s'", hh, hh.Path(chartname))
|
||||
out, err := executeCommand(nil, cmd)
|
||||
_, out, err := executeActionCommand(cmd)
|
||||
|
||||
// In the first pass, we basically want the same results as an update.
|
||||
if err != nil {
|
||||
|
|
@ -72,7 +72,7 @@ func TestDependencyBuildCmd(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err = executeCommand(nil, cmd)
|
||||
_, out, err = executeActionCommand(cmd)
|
||||
if err != nil {
|
||||
t.Logf("Output: %s", out)
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
const dependencyUpDesc = `
|
||||
|
|
@ -42,23 +42,9 @@ reason, an update command will not remove charts unless they are (a) present
|
|||
in the Chart.yaml file, but (b) at the wrong version.
|
||||
`
|
||||
|
||||
// dependencyUpdateOptions describes a 'helm dependency update'
|
||||
type dependencyUpdateOptions struct {
|
||||
keyring string // --keyring
|
||||
skipRefresh bool // --skip-refresh
|
||||
verify bool // --verify
|
||||
|
||||
// args
|
||||
chartpath string
|
||||
|
||||
helmhome helmpath.Home
|
||||
}
|
||||
|
||||
// newDependencyUpdateCmd creates a new dependency update command.
|
||||
func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
|
||||
o := &dependencyUpdateOptions{
|
||||
chartpath: ".",
|
||||
}
|
||||
client := action.NewDependency()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "update CHART",
|
||||
|
|
@ -67,37 +53,32 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
|
|||
Long: dependencyUpDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
o.chartpath = filepath.Clean(args[0])
|
||||
chartpath = filepath.Clean(args[0])
|
||||
}
|
||||
o.helmhome = settings.Home
|
||||
return o.run(out)
|
||||
man := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: chartpath,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: client.Keyring,
|
||||
SkipUpdate: client.SkipRefresh,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if client.Verify {
|
||||
man.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if settings.Debug {
|
||||
man.Debug = true
|
||||
}
|
||||
return man.Update()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&o.verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&o.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
f.BoolVar(&o.skipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
|
||||
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// run runs the full dependency update process.
|
||||
func (o *dependencyUpdateOptions) run(out io.Writer) error {
|
||||
man := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: o.chartpath,
|
||||
HelmHome: o.helmhome,
|
||||
Keyring: o.keyring,
|
||||
SkipUpdate: o.skipRefresh,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if o.verify {
|
||||
man.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if settings.Debug {
|
||||
man.Debug = true
|
||||
}
|
||||
return man.Update()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
|
@ -52,7 +51,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := executeCommand(nil, fmt.Sprintf("--home='%s' dependency update '%s'", hh.String(), hh.Path(chartname)))
|
||||
_, out, err := executeActionCommand(fmt.Sprintf("--home='%s' dependency update '%s'", hh.String(), hh.Path(chartname)))
|
||||
if err != nil {
|
||||
t.Logf("Output: %s", out)
|
||||
t.Fatal(err)
|
||||
|
|
@ -95,7 +94,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err = executeCommand(nil, fmt.Sprintf("--home='%s' dependency update '%s'", hh, hh.Path(chartname)))
|
||||
_, out, err = executeActionCommand(fmt.Sprintf("--home='%s' dependency update '%s'", hh, hh.Path(chartname)))
|
||||
if err != nil {
|
||||
t.Logf("Output: %s", out)
|
||||
t.Fatal(err)
|
||||
|
|
@ -133,7 +132,7 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := executeCommand(nil, fmt.Sprintf("--home='%s' dependency update --skip-refresh %s", hh, hh.Path(chartname)))
|
||||
_, out, err := executeActionCommand(fmt.Sprintf("--home='%s' dependency update --skip-refresh %s", hh, hh.Path(chartname)))
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure to find the repo with skipRefresh")
|
||||
}
|
||||
|
|
@ -164,13 +163,8 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
o := &dependencyUpdateOptions{}
|
||||
o.helmhome = hh
|
||||
o.chartpath = hh.Path(chartname)
|
||||
|
||||
if err := o.run(out); err != nil {
|
||||
output := out.String()
|
||||
_, output, err := executeActionCommand(fmt.Sprintf("--home='%s' dependency update %s", hh, hh.Path(chartname)))
|
||||
if err != nil {
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -178,14 +172,14 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
|
|||
// Chart repo is down
|
||||
srv.Stop()
|
||||
|
||||
if err := o.run(out); err == nil {
|
||||
output := out.String()
|
||||
_, output, err = executeActionCommand(fmt.Sprintf("--home='%s' dependency update %s", hh, hh.Path(chartname)))
|
||||
if err == nil {
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal("Expected error, got nil")
|
||||
}
|
||||
|
||||
// Make sure charts dir still has dependencies
|
||||
files, err := ioutil.ReadDir(filepath.Join(o.chartpath, "charts"))
|
||||
files, err := ioutil.ReadDir(filepath.Join(hh.Path(chartname), "charts"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -201,7 +195,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
|
|||
}
|
||||
|
||||
// Make sure tmpcharts is deleted
|
||||
if _, err := os.Stat(filepath.Join(o.chartpath, "tmpcharts")); !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(filepath.Join(hh.Path(chartname), "tmpcharts")); !os.IsNotExist(err) {
|
||||
t.Fatalf("tmpcharts dir still exists")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
var getHelp = `
|
||||
|
|
@ -38,16 +38,8 @@ By default, this prints a human readable collection of information about the
|
|||
chart, the supplied values, and the generated manifest file.
|
||||
`
|
||||
|
||||
type getOptions struct {
|
||||
version int // --revision
|
||||
|
||||
release string
|
||||
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &getOptions{client: client}
|
||||
func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewGet(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get RELEASE_NAME",
|
||||
|
|
@ -55,25 +47,19 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: getHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.release = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printRelease(out, res)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
|
||||
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
|
||||
cmd.AddCommand(newGetValuesCmd(client, out))
|
||||
cmd.AddCommand(newGetManifestCmd(client, out))
|
||||
cmd.AddCommand(newGetHooksCmd(client, out))
|
||||
cmd.AddCommand(newGetValuesCmd(cfg, out))
|
||||
cmd.AddCommand(newGetManifestCmd(cfg, out))
|
||||
cmd.AddCommand(newGetHooksCmd(cfg, out))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (g *getOptions) run(out io.Writer) error {
|
||||
res, err := g.client.ReleaseContent(g.release, g.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printRelease(out, res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
const getHooksHelp = `
|
||||
|
|
@ -32,14 +32,8 @@ This command downloads hooks for a given release.
|
|||
Hooks are formatted in YAML and separated by the YAML '---\n' separator.
|
||||
`
|
||||
|
||||
type getHooksOptions struct {
|
||||
release string
|
||||
client helm.Interface
|
||||
version int
|
||||
}
|
||||
|
||||
func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &getHooksOptions{client: client}
|
||||
func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewGet(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "hooks RELEASE_NAME",
|
||||
|
|
@ -47,24 +41,18 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: getHooksHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.release = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, hook := range res.Hooks {
|
||||
fmt.Fprintf(out, "---\n# %s\n%s", hook.Name, hook.Manifest)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
|
||||
|
||||
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *getHooksOptions) run(out io.Writer) error {
|
||||
res, err := o.client.ReleaseContent(o.release, o.version)
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, o.release)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hook := range res.Hooks {
|
||||
fmt.Fprintf(out, "---\n# %s\n%s", hook.Name, hook.Manifest)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestGetHooks(t *testing.T) {
|
||||
|
|
@ -28,7 +27,7 @@ func TestGetHooks(t *testing.T) {
|
|||
name: "get hooks with release",
|
||||
cmd: "get hooks aeneas",
|
||||
golden: "output/get-hooks.txt",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})},
|
||||
}, {
|
||||
name: "get hooks without args",
|
||||
cmd: "get hooks",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
var getManifestHelp = `
|
||||
|
|
@ -34,16 +34,8 @@ 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.
|
||||
`
|
||||
|
||||
type getManifestOptions struct {
|
||||
version int // --revision
|
||||
|
||||
release string
|
||||
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &getManifestOptions{client: client}
|
||||
func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewGet(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "manifest RELEASE_NAME",
|
||||
|
|
@ -51,22 +43,16 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: getManifestHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.release = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, res.Manifest)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
|
||||
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getManifest implements 'helm get manifest'
|
||||
func (o *getManifestOptions) run(out io.Writer) error {
|
||||
res, err := o.client.ReleaseContent(o.release, o.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, res.Manifest)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestGetManifest(t *testing.T) {
|
||||
|
|
@ -28,7 +27,7 @@ func TestGetManifest(t *testing.T) {
|
|||
name: "get manifest with release",
|
||||
cmd: "get manifest juno",
|
||||
golden: "output/get-manifest.txt",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "juno"})},
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "juno"})},
|
||||
}, {
|
||||
name: "get manifest without args",
|
||||
cmd: "get manifest",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestGetCmd(t *testing.T) {
|
||||
|
|
@ -28,7 +27,7 @@ func TestGetCmd(t *testing.T) {
|
|||
name: "get with a release",
|
||||
cmd: "get thomas-guide",
|
||||
golden: "output/get-release.txt",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
}, {
|
||||
name: "get requires release name arg",
|
||||
cmd: "get",
|
||||
|
|
|
|||
|
|
@ -20,29 +20,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
var getValuesHelp = `
|
||||
This command downloads a values file for a given release.
|
||||
`
|
||||
|
||||
type getValuesOptions struct {
|
||||
allValues bool // --all
|
||||
version int // --revision
|
||||
|
||||
release string
|
||||
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &getValuesOptions{client: client}
|
||||
func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewGetValues(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "values RELEASE_NAME",
|
||||
|
|
@ -50,43 +39,17 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: getValuesHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.release = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, res)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&o.allValues, "all", "a", false, "dump all (computed) values")
|
||||
cmd.Flags().IntVar(&o.version, "revision", 0, "get the named release with revision")
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
f.BoolVarP(&client.AllValues, "all", "a", false, "dump all (computed) values")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getValues implements 'helm get values'
|
||||
func (o *getValuesOptions) run(out io.Writer) error {
|
||||
res, err := o.client.ReleaseContent(o.release, o.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the user wants all values, compute the values and return.
|
||||
if o.allValues {
|
||||
cfg, err := chartutil.CoalesceValues(res.Chart, res.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgStr, err := cfg.YAML()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, cfgStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
resConfig, err := yaml.Marshal(res.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, string(resConfig))
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestGetValuesCmd(t *testing.T) {
|
||||
|
|
@ -28,7 +27,7 @@ func TestGetValuesCmd(t *testing.T) {
|
|||
name: "get values with a release",
|
||||
cmd: "get values thomas-guide",
|
||||
golden: "output/get-values.txt",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
}, {
|
||||
name: "get values requires release name arg",
|
||||
cmd: "get values",
|
||||
|
|
|
|||
|
|
@ -27,15 +27,14 @@ import (
|
|||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/environment"
|
||||
"k8s.io/helm/pkg/cli"
|
||||
"k8s.io/helm/pkg/kube"
|
||||
"k8s.io/helm/pkg/storage"
|
||||
"k8s.io/helm/pkg/storage/driver"
|
||||
)
|
||||
|
||||
var (
|
||||
settings environment.EnvSettings
|
||||
settings cli.EnvSettings
|
||||
config genericclioptions.RESTClientGetter
|
||||
configOnce sync.Once
|
||||
)
|
||||
|
|
@ -52,45 +51,13 @@ func logf(format string, v ...interface{}) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
cmd := newRootCmd(nil, newActionConfig(false), os.Stdout, os.Args[1:])
|
||||
cmd := newRootCmd(newActionConfig(false), os.Stdout, os.Args[1:])
|
||||
if err := cmd.Execute(); err != nil {
|
||||
logf("%+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// ensureHelmClient returns a new helm client impl. if h is not nil.
|
||||
func ensureHelmClient(h helm.Interface, allNamespaces bool) helm.Interface {
|
||||
if h != nil {
|
||||
return h
|
||||
}
|
||||
return newClient(allNamespaces)
|
||||
}
|
||||
|
||||
func newClient(allNamespaces bool) helm.Interface {
|
||||
kc := kube.New(kubeConfig())
|
||||
kc.Log = logf
|
||||
|
||||
clientset, err := kc.KubernetesClientSet()
|
||||
if err != nil {
|
||||
// TODO return error
|
||||
log.Fatal(err)
|
||||
}
|
||||
var namespace string
|
||||
if !allNamespaces {
|
||||
namespace = getNamespace()
|
||||
}
|
||||
// TODO add other backends
|
||||
d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace))
|
||||
d.Log = logf
|
||||
|
||||
return helm.NewClient(
|
||||
helm.KubeClient(kc),
|
||||
helm.Driver(d),
|
||||
helm.Discovery(clientset.Discovery()),
|
||||
)
|
||||
}
|
||||
|
||||
func newActionConfig(allNamespaces bool) *action.Configuration {
|
||||
kc := kube.New(kubeConfig())
|
||||
kc.Log = logf
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ import (
|
|||
|
||||
"k8s.io/helm/internal/test"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/kube"
|
||||
"k8s.io/helm/pkg/release"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/storage"
|
||||
"k8s.io/helm/pkg/storage/driver"
|
||||
"k8s.io/helm/pkg/tiller/environment"
|
||||
)
|
||||
|
||||
func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() }
|
||||
|
|
@ -66,11 +65,13 @@ func runTestCmd(t *testing.T, tests []cmdTestCase) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
|
||||
c := &helm.FakeClient{
|
||||
Rels: tt.rels,
|
||||
TestRunStatus: tt.testRunStatus,
|
||||
storage := storageFixture()
|
||||
for _, rel := range tt.rels {
|
||||
if err := storage.Create(rel); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
out, err := executeCommand(c, tt.cmd)
|
||||
_, out, err := executeActionCommandC(storage, tt.cmd)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("expected error, got '%v'", err)
|
||||
}
|
||||
|
|
@ -115,12 +116,12 @@ func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command,
|
|||
|
||||
actionConfig := &action.Configuration{
|
||||
Releases: store,
|
||||
KubeClient: &environment.PrintingKubeClient{Out: ioutil.Discard},
|
||||
KubeClient: &kube.PrintingKubeClient{Out: ioutil.Discard},
|
||||
Discovery: fake.NewSimpleClientset().Discovery(),
|
||||
Log: func(format string, v ...interface{}) {},
|
||||
}
|
||||
|
||||
root := newRootCmd(nil, actionConfig, buf, args)
|
||||
root := newRootCmd(actionConfig, buf, args)
|
||||
root.SetOutput(buf)
|
||||
root.SetArgs(args)
|
||||
|
||||
|
|
@ -136,35 +137,11 @@ type cmdTestCase struct {
|
|||
golden string
|
||||
wantError bool
|
||||
// Rels are the available releases at the start of the test.
|
||||
rels []*release.Release
|
||||
testRunStatus map[string]release.TestRunStatus
|
||||
rels []*release.Release
|
||||
}
|
||||
|
||||
// deprecated: Switch to executeActionCommandC
|
||||
func executeCommand(c helm.Interface, cmd string) (string, error) {
|
||||
_, output, err := executeCommandC(c, cmd)
|
||||
return output, err
|
||||
}
|
||||
|
||||
// deprecated: Switch to executeActionCommandC
|
||||
func executeCommandC(client helm.Interface, cmd string) (*cobra.Command, string, error) {
|
||||
args, err := shellwords.Parse(cmd)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
actionConfig := &action.Configuration{
|
||||
Releases: storage.Init(driver.NewMemory()),
|
||||
}
|
||||
|
||||
root := newRootCmd(client, actionConfig, buf, args)
|
||||
root.SetOutput(buf)
|
||||
root.SetArgs(args)
|
||||
|
||||
c, err := root.ExecuteC()
|
||||
|
||||
return c, buf.String(), err
|
||||
func executeActionCommand(cmd string) (*cobra.Command, string, error) {
|
||||
return executeActionCommandC(storageFixture(), cmd)
|
||||
}
|
||||
|
||||
// ensureTestHome creates a home directory like ensureHome, but without remote references.
|
||||
|
|
|
|||
|
|
@ -17,31 +17,15 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
type releaseInfo struct {
|
||||
Revision int `json:"revision"`
|
||||
Updated string `json:"updated"`
|
||||
Status string `json:"status"`
|
||||
Chart string `json:"chart"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type releaseHistory []releaseInfo
|
||||
|
||||
var historyHelp = `
|
||||
History prints historical revisions for a given release.
|
||||
|
||||
|
|
@ -58,18 +42,8 @@ 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
|
||||
`
|
||||
|
||||
type historyOptions struct {
|
||||
colWidth uint // --col-width
|
||||
max int // --max
|
||||
outputFormat string // --output
|
||||
|
||||
release string
|
||||
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newHistoryCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &historyOptions{client: c}
|
||||
func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewHistory(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "history RELEASE_NAME",
|
||||
|
|
@ -78,94 +52,18 @@ func newHistoryCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"hist"},
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
o.release = args[0]
|
||||
return o.run(out)
|
||||
history, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, history)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&o.max, "max", 256, "maximum number of revision to include in history")
|
||||
f.UintVar(&o.colWidth, "col-width", 60, "specifies the max column width of output")
|
||||
f.StringVarP(&o.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)")
|
||||
f.StringVarP(&client.OutputFormat, "output", "o", action.Table.String(), "prints the output in the specified format (json|table|yaml)")
|
||||
f.IntVar(&client.Max, "max", 256, "maximum number of revision to include in history")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *historyOptions) run(out io.Writer) error {
|
||||
rels, err := o.client.ReleaseHistory(o.release, o.max)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rels) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
releaseHistory := getReleaseHistory(rels)
|
||||
|
||||
var history []byte
|
||||
var formattingError error
|
||||
|
||||
switch o.outputFormat {
|
||||
case "yaml":
|
||||
history, formattingError = yaml.Marshal(releaseHistory)
|
||||
case "json":
|
||||
history, formattingError = json.Marshal(releaseHistory)
|
||||
case "table":
|
||||
history = formatAsTable(releaseHistory, o.colWidth)
|
||||
default:
|
||||
return errors.Errorf("unknown output format %q", o.outputFormat)
|
||||
}
|
||||
|
||||
if formattingError != nil {
|
||||
return formattingError
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, string(history))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
|
||||
for i := len(rls) - 1; i >= 0; i-- {
|
||||
r := rls[i]
|
||||
c := formatChartname(r.Chart)
|
||||
s := r.Info.Status.String()
|
||||
v := r.Version
|
||||
d := r.Info.Description
|
||||
|
||||
rInfo := releaseInfo{
|
||||
Revision: v,
|
||||
Status: s,
|
||||
Chart: c,
|
||||
Description: d,
|
||||
}
|
||||
if !r.Info.LastDeployed.IsZero() {
|
||||
rInfo.Updated = r.Info.LastDeployed.String()
|
||||
|
||||
}
|
||||
history = append(history, rInfo)
|
||||
}
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
func formatAsTable(releases releaseHistory, colWidth uint) []byte {
|
||||
tbl := uitable.New()
|
||||
|
||||
tbl.MaxColWidth = colWidth
|
||||
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION")
|
||||
for i := 0; i <= len(releases)-1; i++ {
|
||||
r := releases[i]
|
||||
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.Description)
|
||||
}
|
||||
return tbl.Bytes()
|
||||
}
|
||||
|
||||
func formatChartname(c *chart.Chart) string {
|
||||
if c == nil || c.Metadata == nil {
|
||||
// This is an edge case that has happened in prod, though we don't
|
||||
// know how: https://github.com/helm/helm/issues/1347
|
||||
return "MISSING"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,12 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
rpb "k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestHistoryCmd(t *testing.T) {
|
||||
mk := func(name string, vers int, status rpb.Status) *rpb.Release {
|
||||
return helm.ReleaseMock(&helm.MockReleaseOptions{
|
||||
mk := func(name string, vers int, status release.Status) *release.Release {
|
||||
return release.Mock(&release.MockReleaseOptions{
|
||||
Name: name,
|
||||
Version: vers,
|
||||
Status: status,
|
||||
|
|
@ -35,35 +34,35 @@ func TestHistoryCmd(t *testing.T) {
|
|||
tests := []cmdTestCase{{
|
||||
name: "get history for release",
|
||||
cmd: "history angry-bird",
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.StatusDeployed),
|
||||
mk("angry-bird", 3, rpb.StatusSuperseded),
|
||||
mk("angry-bird", 2, rpb.StatusSuperseded),
|
||||
mk("angry-bird", 1, rpb.StatusSuperseded),
|
||||
rels: []*release.Release{
|
||||
mk("angry-bird", 4, release.StatusDeployed),
|
||||
mk("angry-bird", 3, release.StatusSuperseded),
|
||||
mk("angry-bird", 2, release.StatusSuperseded),
|
||||
mk("angry-bird", 1, release.StatusSuperseded),
|
||||
},
|
||||
golden: "output/history.txt",
|
||||
}, {
|
||||
name: "get history with max limit set",
|
||||
cmd: "history angry-bird --max 2",
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.StatusDeployed),
|
||||
mk("angry-bird", 3, rpb.StatusSuperseded),
|
||||
rels: []*release.Release{
|
||||
mk("angry-bird", 4, release.StatusDeployed),
|
||||
mk("angry-bird", 3, release.StatusSuperseded),
|
||||
},
|
||||
golden: "output/history-limit.txt",
|
||||
}, {
|
||||
name: "get history with yaml output format",
|
||||
cmd: "history angry-bird --output yaml",
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.StatusDeployed),
|
||||
mk("angry-bird", 3, rpb.StatusSuperseded),
|
||||
rels: []*release.Release{
|
||||
mk("angry-bird", 4, release.StatusDeployed),
|
||||
mk("angry-bird", 3, release.StatusSuperseded),
|
||||
},
|
||||
golden: "output/history.yaml",
|
||||
}, {
|
||||
name: "get history with json output format",
|
||||
cmd: "history angry-bird --output json",
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.StatusDeployed),
|
||||
mk("angry-bird", 3, rpb.StatusSuperseded),
|
||||
rels: []*release.Release{
|
||||
mk("angry-bird", 4, release.StatusDeployed),
|
||||
mk("angry-bird", 3, release.StatusSuperseded),
|
||||
},
|
||||
golden: "output/history.json",
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
"k8s.io/helm/pkg/plugin/installer"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
)
|
||||
|
||||
const testPluginsFile = "testdata/plugins.yaml"
|
||||
|
|
|
|||
|
|
@ -17,29 +17,19 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/helm/pkg/release"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
const installDesc = `
|
||||
|
|
@ -104,268 +94,119 @@ To see the list of chart repositories, use 'helm repo list'. To search for
|
|||
charts in a repository, use 'helm search'.
|
||||
`
|
||||
|
||||
type installOptions struct {
|
||||
name string // arg 0
|
||||
dryRun bool // --dry-run
|
||||
disableHooks bool // --disable-hooks
|
||||
replace bool // --replace
|
||||
nameTemplate string // --name-template
|
||||
timeout int64 // --timeout
|
||||
wait bool // --wait
|
||||
devel bool // --devel
|
||||
depUp bool // --dep-up
|
||||
chartPath string // arg 1
|
||||
generateName bool // --generate-name
|
||||
|
||||
valuesOptions
|
||||
chartPathOptions
|
||||
|
||||
cfg *action.Configuration
|
||||
|
||||
// LEGACY: Here until we get upgrade converted
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
o := &installOptions{cfg: cfg}
|
||||
client := action.NewInstall(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [NAME] [CHART]",
|
||||
Short: "install a chart",
|
||||
Long: installDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
debug("Original chart version: %q", o.version)
|
||||
if o.version == "" && o.devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
o.version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
name, chart, err := o.nameAndChart(args)
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
rel, err := runInstall(args, client, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.name = name // FIXME
|
||||
|
||||
cp, err := o.locateChart(chart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.chartPath = cp
|
||||
|
||||
return o.run(out)
|
||||
action.PrintRelease(out, rel)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&o.generateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
|
||||
f.BoolVar(&o.dryRun, "dry-run", false, "simulate an install")
|
||||
f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during install")
|
||||
f.BoolVar(&o.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
|
||||
f.StringVar(&o.nameTemplate, "name-template", "", "specify template used to name the release")
|
||||
f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
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.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(&o.depUp, "dep-up", false, "run helm dependency update before installing the chart")
|
||||
o.valuesOptions.addFlags(f)
|
||||
o.chartPathOptions.addFlags(f)
|
||||
addInstallFlags(cmd.Flags(), client)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// nameAndChart returns the name of the release and the chart that should be used.
|
||||
//
|
||||
// This will read the flags and handle name generation if necessary.
|
||||
func (o *installOptions) nameAndChart(args []string) (string, string, error) {
|
||||
flagsNotSet := func() error {
|
||||
if o.generateName {
|
||||
return errors.New("cannot set --generate-name and also specify a name")
|
||||
}
|
||||
if o.nameTemplate != "" {
|
||||
return errors.New("cannot set --name-template and also specify a name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if len(args) == 2 {
|
||||
return args[0], args[1], flagsNotSet()
|
||||
}
|
||||
|
||||
if o.nameTemplate != "" {
|
||||
newName, err := templateName(o.nameTemplate)
|
||||
return newName, args[0], err
|
||||
}
|
||||
|
||||
if !o.generateName {
|
||||
return "", args[0], errors.New("must either provide a name or specify --generate-name")
|
||||
}
|
||||
|
||||
base := filepath.Base(args[0])
|
||||
if base == "." || base == "" {
|
||||
base = "chart"
|
||||
}
|
||||
newName := fmt.Sprintf("%s-%d", base, time.Now().Unix())
|
||||
|
||||
return newName, args[0], nil
|
||||
func addInstallFlags(f *pflag.FlagSet, client *action.Install) {
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
|
||||
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
|
||||
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
|
||||
f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&client.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.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
|
||||
f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
|
||||
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
|
||||
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart")
|
||||
addValueOptionsFlags(f, &client.ValueOptions)
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
}
|
||||
|
||||
func (o *installOptions) run(out io.Writer) error {
|
||||
debug("CHART PATH: %s\n", o.chartPath)
|
||||
func addValueOptionsFlags(f *pflag.FlagSet, v *action.ValueOptions) {
|
||||
f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL(can specify multiple)")
|
||||
f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
}
|
||||
|
||||
rawVals, err := o.mergedValues()
|
||||
if err != nil {
|
||||
return err
|
||||
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
|
||||
f.StringVar(&c.Version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
|
||||
f.BoolVar(&c.Verify, "verify", false, "verify the package before installing it")
|
||||
f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
|
||||
f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart")
|
||||
f.StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart")
|
||||
f.StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart")
|
||||
f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
}
|
||||
|
||||
func runInstall(args []string, client *action.Install, out io.Writer) (*release.Release, error) {
|
||||
debug("Original chart version: %q", client.Version)
|
||||
if client.Version == "" && client.Devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
client.Version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
// If template is specified, try to run the template.
|
||||
if o.nameTemplate != "" {
|
||||
o.name, err = templateName(o.nameTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Print the final name so the user knows what the final name of the release is.
|
||||
fmt.Fprintf(out, "FINAL NAME: %s\n", o.name)
|
||||
name, chart, err := client.NameAndChart(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.ReleaseName = name
|
||||
|
||||
cp, err := client.ChartPathOptions.LocateChart(chart, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debug("CHART PATH: %s\n", cp)
|
||||
|
||||
if err := client.ValueOptions.MergeValues(settings); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check chart dependencies to make sure all are present in /charts
|
||||
chartRequested, err := loader.Load(o.chartPath)
|
||||
chartRequested, err := loader.Load(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
validInstallableChart, err := chartutil.IsChartInstallable(chartRequested)
|
||||
if !validInstallableChart {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req := chartRequested.Metadata.Dependencies; req != nil {
|
||||
// If checkDependencies returns an error, we have unfulfilled dependencies.
|
||||
// If CheckDependencies returns an error, we have unfulfilled dependencies.
|
||||
// As of Helm 2.4.0, this is treated as a stopping condition:
|
||||
// https://github.com/helm/helm/issues/2209
|
||||
if err := checkDependencies(chartRequested, req); err != nil {
|
||||
if o.depUp {
|
||||
if err := action.CheckDependencies(chartRequested, req); err != nil {
|
||||
if client.DependencyUpdate {
|
||||
man := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: o.chartPath,
|
||||
ChartPath: cp,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: o.keyring,
|
||||
Keyring: client.ChartPathOptions.Keyring,
|
||||
SkipUpdate: false,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if err := man.Update(); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inst := action.NewInstall(o.cfg)
|
||||
inst.DryRun = o.dryRun
|
||||
inst.DisableHooks = o.disableHooks
|
||||
inst.Replace = o.replace
|
||||
inst.Wait = o.wait
|
||||
inst.Devel = o.devel
|
||||
inst.Timeout = o.timeout
|
||||
inst.Namespace = getNamespace()
|
||||
inst.ReleaseName = o.name
|
||||
rel, err := inst.Run(chartRequested, rawVals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.printRelease(out, rel)
|
||||
return nil
|
||||
}
|
||||
|
||||
// printRelease prints info about a release
|
||||
func (o *installOptions) printRelease(out io.Writer, rel *release.Release) {
|
||||
if rel == nil {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(out, "NAME: %s\n", rel.Name)
|
||||
if settings.Debug {
|
||||
printRelease(out, rel)
|
||||
}
|
||||
if !rel.Info.LastDeployed.IsZero() {
|
||||
fmt.Fprintf(out, "LAST DEPLOYED: %s\n", rel.Info.LastDeployed)
|
||||
}
|
||||
fmt.Fprintf(out, "NAMESPACE: %s\n", rel.Namespace)
|
||||
fmt.Fprintf(out, "STATUS: %s\n", rel.Info.Status.String())
|
||||
fmt.Fprintf(out, "\n")
|
||||
if len(rel.Info.Resources) > 0 {
|
||||
re := regexp.MustCompile(" +")
|
||||
|
||||
w := tabwriter.NewWriter(out, 0, 0, 2, ' ', tabwriter.TabIndent)
|
||||
fmt.Fprintf(w, "RESOURCES:\n%s\n", re.ReplaceAllString(rel.Info.Resources, "\t"))
|
||||
w.Flush()
|
||||
}
|
||||
if rel.Info.LastTestSuiteRun != nil {
|
||||
lastRun := rel.Info.LastTestSuiteRun
|
||||
fmt.Fprintf(out, "TEST SUITE:\n%s\n%s\n\n%s\n",
|
||||
fmt.Sprintf("Last Started: %s", lastRun.StartedAt),
|
||||
fmt.Sprintf("Last Completed: %s", lastRun.CompletedAt),
|
||||
formatTestResults(lastRun.Results))
|
||||
}
|
||||
|
||||
if len(rel.Info.Notes) > 0 {
|
||||
fmt.Fprintf(out, "NOTES:\n%s\n", rel.Info.Notes)
|
||||
}
|
||||
}
|
||||
|
||||
// Merges source and destination map, preferring values from the source map
|
||||
func mergeValues(dest, src map[string]interface{}) map[string]interface{} {
|
||||
for k, v := range src {
|
||||
// If the key doesn't exist already, then just set the key to that value
|
||||
if _, exists := dest[k]; !exists {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
nextMap, ok := v.(map[string]interface{})
|
||||
// If it isn't another map, overwrite the value
|
||||
if !ok {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
// Edge case: If the key exists in the destination, but isn't a map
|
||||
destMap, isMap := dest[k].(map[string]interface{})
|
||||
// If the source map has a map for this key, prefer it
|
||||
if !isMap {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
// If we got to this point, it is a map in both, so merge them
|
||||
dest[k] = mergeValues(destMap, nextMap)
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
||||
func templateName(nameTemplate string) (string, error) {
|
||||
t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var b bytes.Buffer
|
||||
err = t.Execute(&b, nil)
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
func checkDependencies(ch *chart.Chart, reqs []*chart.Dependency) error {
|
||||
var missing []string
|
||||
|
||||
OUTER:
|
||||
for _, r := range reqs {
|
||||
for _, d := range ch.Dependencies() {
|
||||
if d.Name() == r.Name {
|
||||
continue OUTER
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
missing = append(missing, r.Name)
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return errors.Errorf("found in Chart.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
return nil
|
||||
client.Namespace = getNamespace()
|
||||
return client.Run(chartRequested)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -142,133 +140,3 @@ func TestInstall(t *testing.T) {
|
|||
|
||||
runTestActionCmd(t, tests)
|
||||
}
|
||||
|
||||
type nameTemplateTestCase struct {
|
||||
tpl string
|
||||
expected string
|
||||
expectedErrorStr string
|
||||
}
|
||||
|
||||
func TestNameTemplate(t *testing.T) {
|
||||
testCases := []nameTemplateTestCase{
|
||||
// Just a straight up nop please
|
||||
{
|
||||
tpl: "foobar",
|
||||
expected: "foobar",
|
||||
expectedErrorStr: "",
|
||||
},
|
||||
// Random numbers at the end for fun & profit
|
||||
{
|
||||
tpl: "foobar-{{randNumeric 6}}",
|
||||
expected: "foobar-[0-9]{6}$",
|
||||
expectedErrorStr: "",
|
||||
},
|
||||
// Random numbers in the middle for fun & profit
|
||||
{
|
||||
tpl: "foobar-{{randNumeric 4}}-baz",
|
||||
expected: "foobar-[0-9]{4}-baz$",
|
||||
expectedErrorStr: "",
|
||||
},
|
||||
// No such function
|
||||
{
|
||||
tpl: "foobar-{{randInt}}",
|
||||
expected: "",
|
||||
expectedErrorStr: "function \"randInt\" not defined",
|
||||
},
|
||||
// Invalid template
|
||||
{
|
||||
tpl: "foobar-{{",
|
||||
expected: "",
|
||||
expectedErrorStr: "unexpected unclosed action",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
n, err := templateName(tc.tpl)
|
||||
if err != nil {
|
||||
if tc.expectedErrorStr == "" {
|
||||
t.Errorf("Was not expecting error, but got: %v", err)
|
||||
continue
|
||||
}
|
||||
re, compErr := regexp.Compile(tc.expectedErrorStr)
|
||||
if compErr != nil {
|
||||
t.Errorf("Expected error string failed to compile: %v", compErr)
|
||||
continue
|
||||
}
|
||||
if !re.MatchString(err.Error()) {
|
||||
t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err == nil && tc.expectedErrorStr != "" {
|
||||
t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr)
|
||||
}
|
||||
|
||||
if tc.expected != "" {
|
||||
re, err := regexp.Compile(tc.expected)
|
||||
if err != nil {
|
||||
t.Errorf("Expected string failed to compile: %v", err)
|
||||
continue
|
||||
}
|
||||
if !re.MatchString(n) {
|
||||
t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeValues(t *testing.T) {
|
||||
nestedMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "stuff",
|
||||
},
|
||||
}
|
||||
anotherNestedMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "things",
|
||||
"awesome": "stuff",
|
||||
},
|
||||
}
|
||||
flatMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": "stuff",
|
||||
}
|
||||
anotherFlatMap := map[string]interface{}{
|
||||
"testing": "fun",
|
||||
}
|
||||
|
||||
testMap := mergeValues(flatMap, nestedMap)
|
||||
equal := reflect.DeepEqual(testMap, nestedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
|
||||
}
|
||||
|
||||
testMap = mergeValues(nestedMap, flatMap)
|
||||
equal = reflect.DeepEqual(testMap, flatMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
|
||||
}
|
||||
|
||||
testMap = mergeValues(nestedMap, anotherNestedMap)
|
||||
equal = reflect.DeepEqual(testMap, anotherNestedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
|
||||
}
|
||||
|
||||
testMap = mergeValues(anotherFlatMap, anotherNestedMap)
|
||||
expectedMap := map[string]interface{}{
|
||||
"testing": "fun",
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "things",
|
||||
"awesome": "stuff",
|
||||
},
|
||||
}
|
||||
equal = reflect.DeepEqual(testMap, expectedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
175
cmd/helm/lint.go
175
cmd/helm/lint.go
|
|
@ -19,19 +19,12 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/lint"
|
||||
"k8s.io/helm/pkg/lint/support"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
var longLintHelp = `
|
||||
|
|
@ -43,159 +36,43 @@ it will emit [ERROR] messages. If it encounters issues that break with conventio
|
|||
or recommendation, it will emit [WARNING] messages.
|
||||
`
|
||||
|
||||
type lintOptions struct {
|
||||
strict bool
|
||||
paths []string
|
||||
|
||||
valuesOptions
|
||||
}
|
||||
|
||||
func newLintCmd(out io.Writer) *cobra.Command {
|
||||
o := &lintOptions{paths: []string{"."}}
|
||||
client := action.NewLint()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "lint PATH",
|
||||
Short: "examines a chart for possible issues",
|
||||
Long: longLintHelp,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
paths := []string{"."}
|
||||
if len(args) > 0 {
|
||||
o.paths = args
|
||||
paths = args
|
||||
}
|
||||
return o.run(out)
|
||||
client.Namespace = getNamespace()
|
||||
if err := client.ValueOptions.MergeValues(settings); err != nil {
|
||||
return err
|
||||
}
|
||||
result := client.Run(paths)
|
||||
var message strings.Builder
|
||||
fmt.Fprintf(&message, "%d chart(s) linted, %d chart(s) failed\n", result.TotalChartsLinted, len(result.Errors))
|
||||
for _, err := range result.Errors {
|
||||
fmt.Fprintf(&message, "\t%s\n", err)
|
||||
}
|
||||
for _, msg := range result.Messages {
|
||||
fmt.Fprintf(&message, "\t%s\n", msg)
|
||||
}
|
||||
|
||||
if len(result.Errors) > 0 {
|
||||
return errors.New(message.String())
|
||||
}
|
||||
fmt.Fprintf(out, message.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
fs := cmd.Flags()
|
||||
fs.BoolVar(&o.strict, "strict", false, "fail on lint warnings")
|
||||
o.valuesOptions.addFlags(fs)
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
|
||||
addValueOptionsFlags(f, &client.ValueOptions)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var errLintNoChart = errors.New("no chart found for linting (missing Chart.yaml)")
|
||||
|
||||
func (o *lintOptions) run(out io.Writer) error {
|
||||
var lowestTolerance int
|
||||
if o.strict {
|
||||
lowestTolerance = support.WarningSev
|
||||
} else {
|
||||
lowestTolerance = support.ErrorSev
|
||||
}
|
||||
|
||||
// Get the raw values
|
||||
rvals, err := o.vals()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var total int
|
||||
var failures int
|
||||
for _, path := range o.paths {
|
||||
if linter, err := lintChart(path, rvals, getNamespace(), o.strict); err != nil {
|
||||
fmt.Println("==> Skipping", path)
|
||||
fmt.Println(err)
|
||||
if err == errLintNoChart {
|
||||
failures = failures + 1
|
||||
}
|
||||
} else {
|
||||
fmt.Println("==> Linting", path)
|
||||
|
||||
if len(linter.Messages) == 0 {
|
||||
fmt.Println("Lint OK")
|
||||
}
|
||||
|
||||
for _, msg := range linter.Messages {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
total = total + 1
|
||||
if linter.HighestSeverity >= lowestTolerance {
|
||||
failures = failures + 1
|
||||
}
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%d chart(s) linted", total)
|
||||
if failures > 0 {
|
||||
return errors.Errorf("%s, %d chart(s) failed", msg, failures)
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "%s, no failures\n", msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) {
|
||||
var chartPath string
|
||||
linter := support.Linter{}
|
||||
|
||||
if strings.HasSuffix(path, ".tgz") {
|
||||
tempDir, err := ioutil.TempDir("", "helm-lint")
|
||||
if err != nil {
|
||||
return linter, err
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return linter, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err = chartutil.Expand(tempDir, file); err != nil {
|
||||
return linter, err
|
||||
}
|
||||
|
||||
lastHyphenIndex := strings.LastIndex(filepath.Base(path), "-")
|
||||
if lastHyphenIndex <= 0 {
|
||||
return linter, errors.Errorf("unable to parse chart archive %q, missing '-'", filepath.Base(path))
|
||||
}
|
||||
base := filepath.Base(path)[:lastHyphenIndex]
|
||||
chartPath = filepath.Join(tempDir, base)
|
||||
} else {
|
||||
chartPath = path
|
||||
}
|
||||
|
||||
// Guard: Error out of this is not a chart.
|
||||
if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil {
|
||||
return linter, errLintNoChart
|
||||
}
|
||||
|
||||
return lint.All(chartPath, vals, namespace, strict), nil
|
||||
}
|
||||
|
||||
func (o *lintOptions) vals() (map[string]interface{}, error) {
|
||||
base := map[string]interface{}{}
|
||||
|
||||
// User specified a values files via -f/--values
|
||||
for _, filePath := range o.valueFiles {
|
||||
currentMap := map[string]interface{}{}
|
||||
bytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(bytes, ¤tMap); err != nil {
|
||||
return base, errors.Wrapf(err, "failed to parse %s", filePath)
|
||||
}
|
||||
// Merge with the previous map
|
||||
base = mergeValues(base, currentMap)
|
||||
}
|
||||
|
||||
// User specified a value via --set
|
||||
for _, value := range o.values {
|
||||
if err := strvals.ParseInto(value, base); err != nil {
|
||||
return base, errors.Wrap(err, "failed parsing --set data")
|
||||
}
|
||||
}
|
||||
|
||||
// User specified a value via --set-string
|
||||
for _, value := range o.stringValues {
|
||||
if err := strvals.ParseIntoString(value, base); err != nil {
|
||||
return base, errors.Wrap(err, "failed parsing --set-string data")
|
||||
}
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
|
|
|||
149
cmd/helm/list.go
149
cmd/helm/list.go
|
|
@ -19,14 +19,11 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
)
|
||||
|
||||
var listHelp = `
|
||||
|
|
@ -39,11 +36,11 @@ By default, it lists only releases that are deployed or failed. Flags like
|
|||
By default, items are sorted alphabetically. Use the '-d' flag to sort by
|
||||
release date.
|
||||
|
||||
If an argument is provided, it will be treated as a filter. Filters are
|
||||
If the --filter flag is provided, it will be treated as a filter. Filters are
|
||||
regular expressions (Perl compatible) that are applied to the list of releases.
|
||||
Only items that match the filter will be returned.
|
||||
|
||||
$ helm list 'ara[a-z]+'
|
||||
$ helm list --filter 'ara[a-z]+'
|
||||
NAME UPDATED CHART
|
||||
maudlin-arachnid Mon May 9 16:07:08 2016 alpine-0.1.0
|
||||
|
||||
|
|
@ -56,143 +53,51 @@ server's default, which may be much higher than 256. Pairing the '--max'
|
|||
flag with the '--offset' flag allows you to page through results.
|
||||
`
|
||||
|
||||
type listOptions struct {
|
||||
// flags
|
||||
all bool // --all
|
||||
allNamespaces bool // --all-namespaces
|
||||
byDate bool // --date
|
||||
colWidth uint // --col-width
|
||||
uninstalled bool // --uninstalled
|
||||
uninstalling bool // --uninstalling
|
||||
deployed bool // --deployed
|
||||
failed bool // --failed
|
||||
limit int // --max
|
||||
offset int // --offset
|
||||
pending bool // --pending
|
||||
short bool // --short
|
||||
sortDesc bool // --reverse
|
||||
superseded bool // --superseded
|
||||
|
||||
filter string
|
||||
}
|
||||
|
||||
func newListCmd(actionConfig *action.Configuration, out io.Writer) *cobra.Command {
|
||||
o := &listOptions{}
|
||||
func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewList(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [FILTER]",
|
||||
Use: "list",
|
||||
Short: "list releases",
|
||||
Long: listHelp,
|
||||
Aliases: []string{"ls"},
|
||||
Args: require.MaximumNArgs(1),
|
||||
Args: require.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
o.filter = strings.Join(args, " ")
|
||||
if client.AllNamespaces {
|
||||
client.SetConfiguration(newActionConfig(true))
|
||||
}
|
||||
client.All = client.Limit == -1
|
||||
client.SetStateMask()
|
||||
|
||||
if o.allNamespaces {
|
||||
actionConfig = newActionConfig(true)
|
||||
}
|
||||
results, err := client.Run()
|
||||
|
||||
lister := action.NewList(actionConfig)
|
||||
lister.All = o.limit == -1
|
||||
lister.AllNamespaces = o.allNamespaces
|
||||
lister.Limit = o.limit
|
||||
lister.Offset = o.offset
|
||||
lister.Filter = o.filter
|
||||
|
||||
// Set StateMask
|
||||
lister.StateMask = o.setStateMask()
|
||||
|
||||
// Set sorter
|
||||
lister.Sort = action.ByNameAsc
|
||||
if o.sortDesc {
|
||||
lister.Sort = action.ByNameDesc
|
||||
}
|
||||
if o.byDate {
|
||||
lister.Sort = action.ByDate
|
||||
}
|
||||
|
||||
results, err := lister.Run()
|
||||
|
||||
if o.short {
|
||||
if client.Short {
|
||||
for _, res := range results {
|
||||
fmt.Fprintln(out, res.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, formatList(results, 90))
|
||||
fmt.Fprintln(out, action.FormatList(results))
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&o.short, "short", "q", false, "output short (quiet) listing format")
|
||||
f.BoolVarP(&o.byDate, "date", "d", false, "sort by release date")
|
||||
f.BoolVarP(&o.sortDesc, "reverse", "r", false, "reverse the sort order")
|
||||
f.IntVarP(&o.limit, "max", "m", 256, "maximum number of releases to fetch")
|
||||
f.IntVarP(&o.offset, "offset", "o", 0, "next release name in the list, used to offset from start value")
|
||||
f.BoolVarP(&o.all, "all", "a", false, "show all releases, not just the ones marked deployed")
|
||||
f.BoolVar(&o.uninstalled, "uninstalled", false, "show uninstalled releases")
|
||||
f.BoolVar(&o.superseded, "superseded", false, "show superseded releases")
|
||||
f.BoolVar(&o.uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
|
||||
f.BoolVar(&o.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
|
||||
f.BoolVar(&o.failed, "failed", false, "show failed releases")
|
||||
f.BoolVar(&o.pending, "pending", false, "show pending releases")
|
||||
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")
|
||||
f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format")
|
||||
f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
|
||||
f.BoolVarP(&client.SortDesc, "reverse", "r", false, "reverse the sort order")
|
||||
f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed")
|
||||
f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases")
|
||||
f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases")
|
||||
f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
|
||||
f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
|
||||
f.BoolVar(&client.Failed, "failed", false, "show failed releases")
|
||||
f.BoolVar(&client.Pending, "pending", false, "show pending releases")
|
||||
f.BoolVar(&client.AllNamespaces, "all-namespaces", false, "list releases across all namespaces")
|
||||
f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch")
|
||||
f.IntVarP(&client.Offset, "offset", "o", 0, "next release name in the list, used to offset from start value")
|
||||
f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// setStateMask calculates the state mask based on parameters.
|
||||
func (o *listOptions) setStateMask() action.ListStates {
|
||||
if o.all {
|
||||
return action.ListAll
|
||||
}
|
||||
|
||||
state := action.ListStates(0)
|
||||
if o.deployed {
|
||||
state |= action.ListDeployed
|
||||
}
|
||||
if o.uninstalled {
|
||||
state |= action.ListUninstalled
|
||||
}
|
||||
if o.uninstalling {
|
||||
state |= action.ListUninstalling
|
||||
}
|
||||
if o.pending {
|
||||
state |= action.ListPendingInstall | action.ListPendingRollback | action.ListPendingUpgrade
|
||||
}
|
||||
if o.failed {
|
||||
state |= action.ListFailed
|
||||
}
|
||||
|
||||
// Apply a default
|
||||
if state == 0 {
|
||||
return action.ListDeployed | action.ListFailed
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func formatList(rels []*release.Release, colWidth uint) string {
|
||||
table := uitable.New()
|
||||
|
||||
table.MaxColWidth = colWidth
|
||||
table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE")
|
||||
for _, r := range rels {
|
||||
md := r.Chart.Metadata
|
||||
c := fmt.Sprintf("%s-%s", md.Name, md.Version)
|
||||
t := "-"
|
||||
if tspb := r.Info.LastDeployed; !tspb.IsZero() {
|
||||
t = tspb.String()
|
||||
}
|
||||
s := r.Info.Status.String()
|
||||
v := r.Version
|
||||
n := r.Namespace
|
||||
table.AddRow(r.Name, v, t, s, c, n)
|
||||
}
|
||||
return table.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main // import "k8s.io/helm/cmd/helm"
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Values Options
|
||||
|
||||
type valuesOptions struct {
|
||||
valueFiles []string // --values
|
||||
values []string // --set
|
||||
stringValues []string // --set-string
|
||||
}
|
||||
|
||||
func (o *valuesOptions) addFlags(fs *pflag.FlagSet) {
|
||||
fs.StringSliceVarP(&o.valueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL(can specify multiple)")
|
||||
fs.StringArrayVar(&o.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
fs.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)")
|
||||
}
|
||||
|
||||
// mergeValues merges values from files specified via -f/--values and
|
||||
// directly via --set or --set-string, marshaling them to YAML
|
||||
func (o *valuesOptions) mergedValues() (map[string]interface{}, error) {
|
||||
base := map[string]interface{}{}
|
||||
|
||||
// User specified a values files via -f/--values
|
||||
for _, filePath := range o.valueFiles {
|
||||
currentMap := map[string]interface{}{}
|
||||
|
||||
bytes, err := readFile(filePath)
|
||||
if err != nil {
|
||||
return base, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(bytes, ¤tMap); err != nil {
|
||||
return base, errors.Wrapf(err, "failed to parse %s", filePath)
|
||||
}
|
||||
// Merge with the previous map
|
||||
base = mergeValues(base, currentMap)
|
||||
}
|
||||
|
||||
// User specified a value via --set
|
||||
for _, value := range o.values {
|
||||
if err := strvals.ParseInto(value, base); err != nil {
|
||||
return base, errors.Wrap(err, "failed parsing --set data")
|
||||
}
|
||||
}
|
||||
|
||||
// User specified a value via --set-string
|
||||
for _, value := range o.stringValues {
|
||||
if err := strvals.ParseIntoString(value, base); err != nil {
|
||||
return base, errors.Wrap(err, "failed parsing --set-string data")
|
||||
}
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// readFile load a file from stdin, the local directory, or a remote file with a url.
|
||||
func readFile(filePath string) ([]byte, error) {
|
||||
if strings.TrimSpace(filePath) == "-" {
|
||||
return ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
u, _ := url.Parse(filePath)
|
||||
p := getter.All(settings)
|
||||
|
||||
// FIXME: maybe someone handle other protocols like ftp.
|
||||
getterConstructor, err := p.ByScheme(u.Scheme)
|
||||
|
||||
if err != nil {
|
||||
return ioutil.ReadFile(filePath)
|
||||
}
|
||||
|
||||
getter, err := getterConstructor(filePath, "", "", "")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
data, err := getter.Get(filePath)
|
||||
return data.Bytes(), err
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Chart Path Options
|
||||
|
||||
type chartPathOptions struct {
|
||||
caFile string // --ca-file
|
||||
certFile string // --cert-file
|
||||
keyFile string // --key-file
|
||||
keyring string // --keyring
|
||||
password string // --password
|
||||
repoURL string // --repo
|
||||
username string // --username
|
||||
verify bool // --verify
|
||||
version string // --version
|
||||
}
|
||||
|
||||
// defaultKeyring returns the expanded path to the default keyring.
|
||||
func defaultKeyring() string {
|
||||
if v, ok := os.LookupEnv("GNUPGHOME"); ok {
|
||||
return filepath.Join(v, "pubring.gpg")
|
||||
}
|
||||
return os.ExpandEnv("$HOME/.gnupg/pubring.gpg")
|
||||
}
|
||||
|
||||
func (o *chartPathOptions) addFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
|
||||
fs.BoolVar(&o.verify, "verify", false, "verify the package before installing it")
|
||||
fs.StringVar(&o.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
|
||||
fs.StringVar(&o.repoURL, "repo", "", "chart repository url where to locate the requested chart")
|
||||
fs.StringVar(&o.username, "username", "", "chart repository username where to locate the requested chart")
|
||||
fs.StringVar(&o.password, "password", "", "chart repository password where to locate the requested chart")
|
||||
fs.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
fs.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
fs.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
}
|
||||
|
||||
func (o *chartPathOptions) locateChart(name string) (string, error) {
|
||||
return locateChartPath(o.repoURL, o.username, o.password, name, o.version, o.keyring, o.certFile, o.keyFile, o.caFile, o.verify)
|
||||
}
|
||||
|
||||
// locateChartPath looks for a chart directory in known places, and returns either the full path or an error.
|
||||
//
|
||||
// This does not ensure that the chart is well-formed; only that the requested filename exists.
|
||||
//
|
||||
// Order of resolution:
|
||||
// - relative to current working directory
|
||||
// - if path is absolute or begins with '.', error out here
|
||||
// - chart repos in $HELM_HOME
|
||||
// - URL
|
||||
//
|
||||
// If 'verify' is true, this will attempt to also verify the chart.
|
||||
func locateChartPath(repoURL, username, password, name, version, keyring,
|
||||
certFile, keyFile, caFile string, verify bool) (string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
version = strings.TrimSpace(version)
|
||||
|
||||
if _, err := os.Stat(name); err == nil {
|
||||
abs, err := filepath.Abs(name)
|
||||
if err != nil {
|
||||
return abs, err
|
||||
}
|
||||
if verify {
|
||||
if _, err := downloader.VerifyChart(abs, keyring); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return abs, nil
|
||||
}
|
||||
if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
|
||||
return name, errors.Errorf("path %q not found", name)
|
||||
}
|
||||
|
||||
crepo := filepath.Join(settings.Home.Repository(), name)
|
||||
if _, err := os.Stat(crepo); err == nil {
|
||||
return filepath.Abs(crepo)
|
||||
}
|
||||
|
||||
dl := downloader.ChartDownloader{
|
||||
HelmHome: settings.Home,
|
||||
Out: os.Stdout,
|
||||
Keyring: keyring,
|
||||
Getters: getter.All(settings),
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if verify {
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if repoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthRepoURL(repoURL, username, password, name, version,
|
||||
certFile, keyFile, caFile, getter.All(settings))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = chartURL
|
||||
}
|
||||
|
||||
if _, err := os.Stat(settings.Home.Archive()); os.IsNotExist(err) {
|
||||
os.MkdirAll(settings.Home.Archive(), 0744)
|
||||
}
|
||||
|
||||
filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive())
|
||||
if err == nil {
|
||||
lname, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return filename, err
|
||||
}
|
||||
debug("Fetched %s to %s\n", name, filename)
|
||||
return lname, nil
|
||||
} else if settings.Debug {
|
||||
return filename, err
|
||||
}
|
||||
|
||||
return filename, errors.Errorf("failed to download %q (hint: running `helm repo update` may help)", name)
|
||||
}
|
||||
|
|
@ -20,22 +20,15 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"k8s.io/helm/pkg/action"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
)
|
||||
|
||||
const packageDesc = `
|
||||
|
|
@ -49,186 +42,68 @@ Chart.yaml file, and (if found) build the current directory into a chart.
|
|||
Versioned chart archives are used by Helm package repositories.
|
||||
`
|
||||
|
||||
type packageOptions struct {
|
||||
appVersion string // --app-version
|
||||
dependencyUpdate bool // --dependency-update
|
||||
destination string // --destination
|
||||
key string // --key
|
||||
keyring string // --keyring
|
||||
sign bool // --sign
|
||||
version string // --version
|
||||
|
||||
valuesOptions
|
||||
|
||||
path string
|
||||
|
||||
home helmpath.Home
|
||||
}
|
||||
|
||||
func newPackageCmd(out io.Writer) *cobra.Command {
|
||||
o := &packageOptions{}
|
||||
client := action.NewPackage()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "package [CHART_PATH] [...]",
|
||||
Short: "package a chart directory into a chart archive",
|
||||
Long: packageDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.home = settings.Home
|
||||
if len(args) == 0 {
|
||||
return errors.Errorf("need at least one argument, the path to the chart")
|
||||
}
|
||||
if o.sign {
|
||||
if o.key == "" {
|
||||
if client.Sign {
|
||||
if client.Key == "" {
|
||||
return errors.New("--key is required for signing a package")
|
||||
}
|
||||
if o.keyring == "" {
|
||||
if client.Keyring == "" {
|
||||
return errors.New("--keyring is required for signing a package")
|
||||
}
|
||||
}
|
||||
if err := client.ValueOptions.MergeValues(settings); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
o.path = args[i]
|
||||
if err := o.run(out); err != nil {
|
||||
path, err := filepath.Abs(args[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if client.DependencyUpdate {
|
||||
downloadManager := &downloader.Manager{
|
||||
Out: ioutil.Discard,
|
||||
ChartPath: path,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: client.Keyring,
|
||||
Getters: getter.All(settings),
|
||||
Debug: settings.Debug,
|
||||
}
|
||||
|
||||
if err := downloadManager.Update(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p, err := client.Run(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", p)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&o.sign, "sign", false, "use a PGP private key to sign this package")
|
||||
f.StringVar(&o.key, "key", "", "name of the key to use when signing. Used if --sign is true")
|
||||
f.StringVar(&o.keyring, "keyring", defaultKeyring(), "location of a public keyring")
|
||||
f.StringVar(&o.version, "version", "", "set the version on the chart to this semver version")
|
||||
f.StringVar(&o.appVersion, "app-version", "", "set the appVersion on the chart to this version")
|
||||
f.StringVarP(&o.destination, "destination", "d", ".", "location to write the chart.")
|
||||
f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
|
||||
o.valuesOptions.addFlags(f)
|
||||
f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package")
|
||||
f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true")
|
||||
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring")
|
||||
f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version")
|
||||
f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version")
|
||||
f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.")
|
||||
f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
|
||||
addValueOptionsFlags(f, &client.ValueOptions)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *packageOptions) run(out io.Writer) error {
|
||||
path, err := filepath.Abs(o.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.dependencyUpdate {
|
||||
downloadManager := &downloader.Manager{
|
||||
Out: out,
|
||||
ChartPath: path,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: o.keyring,
|
||||
Getters: getter.All(settings),
|
||||
Debug: settings.Debug,
|
||||
}
|
||||
|
||||
if err := downloadManager.Update(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ch, err := loader.LoadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validChartType, err := chartutil.IsValidChartType(ch)
|
||||
if !validChartType {
|
||||
return err
|
||||
}
|
||||
|
||||
overrideVals, err := o.mergedValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
combinedVals, err := chartutil.CoalesceValues(ch, overrideVals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ch.Values = combinedVals
|
||||
|
||||
// If version is set, modify the version.
|
||||
if len(o.version) != 0 {
|
||||
if err := setVersion(ch, o.version); err != nil {
|
||||
return err
|
||||
}
|
||||
debug("Setting version to %s", o.version)
|
||||
}
|
||||
|
||||
if o.appVersion != "" {
|
||||
ch.Metadata.AppVersion = o.appVersion
|
||||
debug("Setting appVersion to %s", o.appVersion)
|
||||
}
|
||||
|
||||
if reqs := ch.Metadata.Dependencies; reqs != nil {
|
||||
if err := checkDependencies(ch, reqs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var dest string
|
||||
if o.destination == "." {
|
||||
// Save to the current working directory.
|
||||
dest, err = os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Otherwise save to set destination
|
||||
dest = o.destination
|
||||
}
|
||||
|
||||
name, err := chartutil.Save(ch, dest)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to save")
|
||||
}
|
||||
fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", name)
|
||||
|
||||
if o.sign {
|
||||
err = o.clearsign(name)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func setVersion(ch *chart.Chart, ver string) error {
|
||||
// Verify that version is a Version, and error out if it is not.
|
||||
if _, err := semver.NewVersion(ver); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the version field on the chart.
|
||||
ch.Metadata.Version = ver
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *packageOptions) clearsign(filename string) error {
|
||||
// Load keyring
|
||||
signer, err := provenance.NewFromKeyring(o.keyring, o.key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signer.DecryptKey(promptUser); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := signer.ClearSign(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
debug(sig)
|
||||
|
||||
return ioutil.WriteFile(filename+".prov", []byte(sig), 0755)
|
||||
}
|
||||
|
||||
// promptUser implements provenance.PassphraseFetcher
|
||||
func promptUser(name string) ([]byte, error) {
|
||||
fmt.Printf("Password for key %q > ", name)
|
||||
pw, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||
fmt.Println()
|
||||
return pw, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,30 +31,9 @@ import (
|
|||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
)
|
||||
|
||||
func TestSetVersion(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{
|
||||
Name: "prow",
|
||||
Version: "0.0.1",
|
||||
},
|
||||
}
|
||||
expect := "1.2.3-beta.5"
|
||||
if err := setVersion(c, expect); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Metadata.Version != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, c.Metadata.Version)
|
||||
}
|
||||
|
||||
if err := setVersion(c, "monkeyface"); err == nil {
|
||||
t.Error("Expected bogus version to return an error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackage(t *testing.T) {
|
||||
statExe := "stat"
|
||||
statFileMsg := "no such file or directory"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
"k8s.io/helm/pkg/plugin/installer"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
)
|
||||
|
||||
type pluginListOptions struct {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
"k8s.io/helm/pkg/plugin/installer"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
var printReleaseTemplate = `REVISION: {{.Release.Version}}
|
||||
|
|
|
|||
112
cmd/helm/pull.go
112
cmd/helm/pull.go
|
|
@ -19,18 +19,11 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
const pullDesc = `
|
||||
|
|
@ -48,20 +41,8 @@ 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.
|
||||
`
|
||||
|
||||
type pullOptions struct {
|
||||
destdir string // --destination
|
||||
devel bool // --devel
|
||||
untar bool // --untar
|
||||
untardir string // --untardir
|
||||
verifyLater bool // --prov
|
||||
|
||||
chartRef string
|
||||
|
||||
chartPathOptions
|
||||
}
|
||||
|
||||
func newPullCmd(out io.Writer) *cobra.Command {
|
||||
o := &pullOptions{}
|
||||
client := action.NewPull()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "pull [chart URL | repo/chartname] [...]",
|
||||
|
|
@ -70,95 +51,30 @@ func newPullCmd(out io.Writer) *cobra.Command {
|
|||
Long: pullDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if o.version == "" && o.devel {
|
||||
client.Settings = settings
|
||||
if client.Version == "" && client.Devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
o.version = ">0.0.0-0"
|
||||
client.Version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
o.chartRef = args[i]
|
||||
if err := o.run(out); err != nil {
|
||||
output, err := client.Run(args[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(out, output)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
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(&o.untar, "untar", false, "if set to true, will untar the chart after downloading it")
|
||||
f.BoolVar(&o.verifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
|
||||
f.StringVar(&o.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
|
||||
f.StringVarP(&o.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
|
||||
|
||||
o.chartPathOptions.addFlags(f)
|
||||
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
|
||||
f.BoolVar(&client.Untar, "untar", false, "if set to true, will untar the chart after downloading it")
|
||||
f.BoolVar(&client.VerifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
|
||||
f.StringVar(&client.UntarDir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
|
||||
f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *pullOptions) run(out io.Writer) error {
|
||||
c := downloader.ChartDownloader{
|
||||
HelmHome: settings.Home,
|
||||
Out: out,
|
||||
Keyring: o.keyring,
|
||||
Verify: downloader.VerifyNever,
|
||||
Getters: getter.All(settings),
|
||||
Username: o.username,
|
||||
Password: o.password,
|
||||
}
|
||||
|
||||
if o.verify {
|
||||
c.Verify = downloader.VerifyAlways
|
||||
} else if o.verifyLater {
|
||||
c.Verify = downloader.VerifyLater
|
||||
}
|
||||
|
||||
// If untar is set, we fetch to a tempdir, then untar and copy after
|
||||
// verification.
|
||||
dest := o.destdir
|
||||
if o.untar {
|
||||
var err error
|
||||
dest, err = ioutil.TempDir("", "helm-")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to untar")
|
||||
}
|
||||
defer os.RemoveAll(dest)
|
||||
}
|
||||
|
||||
if o.repoURL != "" {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
o.chartRef = chartURL
|
||||
}
|
||||
|
||||
saved, v, err := c.DownloadTo(o.chartRef, o.version, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.verify {
|
||||
fmt.Fprintf(out, "Verification: %v\n", v)
|
||||
}
|
||||
|
||||
// After verification, untar the chart into the requested directory.
|
||||
if o.untar {
|
||||
ud := o.untardir
|
||||
if !filepath.IsAbs(ud) {
|
||||
ud = filepath.Join(o.destdir, ud)
|
||||
}
|
||||
if fi, err := os.Stat(ud); err != nil {
|
||||
if err := os.MkdirAll(ud, 0755); err != nil {
|
||||
return errors.Wrap(err, "failed to untar (mkdir)")
|
||||
}
|
||||
|
||||
} else if !fi.IsDir() {
|
||||
return errors.Errorf("failed to untar: %s is not a directory", ud)
|
||||
}
|
||||
|
||||
return chartutil.ExpandFile(ud, saved)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func TestPullCmd(t *testing.T) {
|
|||
os.Mkdir(outdir, 0755)
|
||||
|
||||
cmd := strings.Join(append(tt.args, "-d", "'"+outdir+"'", "--home", "'"+hh.String()+"'"), " ")
|
||||
out, err := executeCommand(nil, "fetch "+cmd)
|
||||
_, out, err := executeActionCommand("fetch " + cmd)
|
||||
if err != nil {
|
||||
if tt.wantError {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
const releaseTestDesc = `
|
||||
|
|
@ -35,15 +35,8 @@ 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.
|
||||
`
|
||||
|
||||
type releaseTestOptions struct {
|
||||
name string
|
||||
client helm.Interface
|
||||
timeout int64
|
||||
cleanup bool
|
||||
}
|
||||
|
||||
func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &releaseTestOptions{client: c}
|
||||
func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewReleaseTesting(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "test [RELEASE]",
|
||||
|
|
@ -51,47 +44,37 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: releaseTestDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.name = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
c, errc := client.Run(args[0])
|
||||
testErr := &testErr{}
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err == nil && testErr.failed > 0 {
|
||||
return testErr.Error()
|
||||
}
|
||||
return err
|
||||
case res, ok := <-c:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
if res.Status == release.TestRunFailure {
|
||||
testErr.failed++
|
||||
}
|
||||
fmt.Fprintf(out, res.Msg+"\n")
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&o.cleanup, "cleanup", false, "delete test pods upon completion")
|
||||
f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&client.Cleanup, "cleanup", false, "delete test pods upon completion")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *releaseTestOptions) run(out io.Writer) (err error) {
|
||||
c, errc := o.client.RunReleaseTest(
|
||||
o.name,
|
||||
helm.ReleaseTestTimeout(o.timeout),
|
||||
helm.ReleaseTestCleanup(o.cleanup),
|
||||
)
|
||||
testErr := &testErr{}
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err == nil && testErr.failed > 0 {
|
||||
return testErr.Error()
|
||||
}
|
||||
return err
|
||||
case res, ok := <-c:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
if res.Status == release.TestRunFailure {
|
||||
testErr.failed++
|
||||
}
|
||||
fmt.Fprintf(out, res.Msg+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testErr struct {
|
||||
failed int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,49 +18,79 @@ package main
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestReleaseTesting(t *testing.T) {
|
||||
timestamp := time.Unix(1452902400, 0).UTC()
|
||||
|
||||
tests := []cmdTestCase{{
|
||||
name: "basic test",
|
||||
cmd: "test example-release",
|
||||
testRunStatus: map[string]release.TestRunStatus{"PASSED: green lights everywhere": release.TestRunSuccess},
|
||||
golden: "output/test.txt",
|
||||
name: "successful test",
|
||||
cmd: "status test-success",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{
|
||||
Name: "test-success",
|
||||
TestSuiteResults: []*release.TestRun{
|
||||
{
|
||||
Name: "test-success",
|
||||
Status: release.TestRunSuccess,
|
||||
StartedAt: timestamp,
|
||||
CompletedAt: timestamp,
|
||||
},
|
||||
},
|
||||
})},
|
||||
golden: "output/test-success.txt",
|
||||
}, {
|
||||
name: "test failure",
|
||||
cmd: "test example-fail",
|
||||
testRunStatus: map[string]release.TestRunStatus{"FAILURE: red lights everywhere": release.TestRunFailure},
|
||||
wantError: true,
|
||||
golden: "output/test-failure.txt",
|
||||
name: "test failure",
|
||||
cmd: "status test-failure",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{
|
||||
Name: "test-failure",
|
||||
TestSuiteResults: []*release.TestRun{
|
||||
{
|
||||
Name: "test-failure",
|
||||
Status: release.TestRunFailure,
|
||||
StartedAt: timestamp,
|
||||
CompletedAt: timestamp,
|
||||
},
|
||||
},
|
||||
})},
|
||||
golden: "output/test-failure.txt",
|
||||
}, {
|
||||
name: "test unknown",
|
||||
cmd: "test example-unknown",
|
||||
testRunStatus: map[string]release.TestRunStatus{"UNKNOWN: yellow lights everywhere": release.TestRunUnknown},
|
||||
golden: "output/test-unknown.txt",
|
||||
name: "test unknown",
|
||||
cmd: "status test-unknown",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{
|
||||
Name: "test-unknown",
|
||||
TestSuiteResults: []*release.TestRun{
|
||||
{
|
||||
Name: "test-unknown",
|
||||
Status: release.TestRunUnknown,
|
||||
StartedAt: timestamp,
|
||||
CompletedAt: timestamp,
|
||||
},
|
||||
},
|
||||
})},
|
||||
golden: "output/test-unknown.txt",
|
||||
}, {
|
||||
name: "test error",
|
||||
cmd: "test example-error",
|
||||
testRunStatus: map[string]release.TestRunStatus{"ERROR: yellow lights everywhere": release.TestRunFailure},
|
||||
wantError: true,
|
||||
golden: "output/test-error.txt",
|
||||
name: "test running",
|
||||
cmd: "status test-running",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{
|
||||
Name: "test-running",
|
||||
TestSuiteResults: []*release.TestRun{
|
||||
{
|
||||
Name: "test-running",
|
||||
Status: release.TestRunRunning,
|
||||
StartedAt: timestamp,
|
||||
CompletedAt: timestamp,
|
||||
},
|
||||
},
|
||||
})},
|
||||
golden: "output/test-running.txt",
|
||||
}, {
|
||||
name: "test running",
|
||||
cmd: "test example-running",
|
||||
testRunStatus: map[string]release.TestRunStatus{"RUNNING: things are happpeningggg": release.TestRunRunning},
|
||||
golden: "output/test-running.txt",
|
||||
}, {
|
||||
name: "multiple tests example",
|
||||
cmd: "test example-suite",
|
||||
testRunStatus: map[string]release.TestRunStatus{
|
||||
"RUNNING: things are happpeningggg": release.TestRunRunning,
|
||||
"PASSED: party time": release.TestRunSuccess,
|
||||
"RUNNING: things are happening again": release.TestRunRunning,
|
||||
"FAILURE: good thing u checked :)": release.TestRunFailure,
|
||||
"RUNNING: things are happpeningggg yet again": release.TestRunRunning,
|
||||
"PASSED: feel free to party again": release.TestRunSuccess},
|
||||
wantError: true,
|
||||
name: "test with no tests",
|
||||
cmd: "test no-tests",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "no-tests"})},
|
||||
golden: "output/test-no-tests.txt",
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
const rollbackDesc = `
|
||||
|
|
@ -36,20 +34,8 @@ second is a revision (version) number. To see revision numbers, run
|
|||
'helm history RELEASE'.
|
||||
`
|
||||
|
||||
type rollbackOptions struct {
|
||||
name string
|
||||
revision int
|
||||
dryRun bool
|
||||
recreate bool
|
||||
force bool
|
||||
disableHooks bool
|
||||
client helm.Interface
|
||||
timeout int64
|
||||
wait bool
|
||||
}
|
||||
|
||||
func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &rollbackOptions{client: c}
|
||||
func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewRollback(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rollback [RELEASE] [REVISION]",
|
||||
|
|
@ -57,45 +43,25 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: rollbackDesc,
|
||||
Args: require.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.name = args[0]
|
||||
|
||||
v64, err := strconv.ParseInt(args[1], 10, 32)
|
||||
_, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid revision number '%q'", args[1])
|
||||
return err
|
||||
}
|
||||
|
||||
o.revision = int(v64)
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
fmt.Fprintf(out, "Rollback was a success! Happy Helming!\n")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&o.dryRun, "dry-run", false, "simulate a rollback")
|
||||
f.BoolVar(&o.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
|
||||
f.BoolVar(&o.force, "force", false, "force resource update through delete/recreate if needed")
|
||||
f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during rollback")
|
||||
f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
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.IntVarP(&client.Version, "version", "v", 0, "revision number to rollback to (default: rollback to previous release)")
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate a rollback")
|
||||
f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
|
||||
f.BoolVar(&client.Force, "force", false, "force resource update through delete/recreate if needed")
|
||||
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during rollback")
|
||||
f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&client.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
|
||||
}
|
||||
|
||||
func (o *rollbackOptions) run(out io.Writer) error {
|
||||
_, err := o.client.RollbackRelease(
|
||||
o.name,
|
||||
helm.RollbackDryRun(o.dryRun),
|
||||
helm.RollbackRecreate(o.recreate),
|
||||
helm.RollbackForce(o.force),
|
||||
helm.RollbackDisableHooks(o.disableHooks),
|
||||
helm.RollbackVersion(o.revision),
|
||||
helm.RollbackTimeout(o.timeout),
|
||||
helm.RollbackWait(o.wait))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Rollback was a success! Happy Helming!\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,25 +18,47 @@ package main
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestRollbackCmd(t *testing.T) {
|
||||
rels := []*release.Release{
|
||||
{
|
||||
Name: "funny-honey",
|
||||
Info: &release.Info{Status: release.StatusSuperseded},
|
||||
Chart: &chart.Chart{},
|
||||
Version: 1,
|
||||
},
|
||||
{
|
||||
Name: "funny-honey",
|
||||
Info: &release.Info{Status: release.StatusDeployed},
|
||||
Chart: &chart.Chart{},
|
||||
Version: 2,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []cmdTestCase{{
|
||||
name: "rollback a release",
|
||||
cmd: "rollback funny-honey 1",
|
||||
golden: "output/rollback.txt",
|
||||
rels: rels,
|
||||
}, {
|
||||
name: "rollback a release with timeout",
|
||||
cmd: "rollback funny-honey 1 --timeout 120",
|
||||
golden: "output/rollback-timeout.txt",
|
||||
rels: rels,
|
||||
}, {
|
||||
name: "rollback a release with wait",
|
||||
cmd: "rollback funny-honey 1 --wait",
|
||||
golden: "output/rollback-wait.txt",
|
||||
rels: rels,
|
||||
}, {
|
||||
name: "rollback a release without revision",
|
||||
cmd: "rollback funny-honey",
|
||||
golden: "output/rollback-no-args.txt",
|
||||
rels: rels,
|
||||
wantError: true,
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/registry"
|
||||
)
|
||||
|
||||
|
|
@ -50,8 +49,7 @@ Environment:
|
|||
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
|
||||
`
|
||||
|
||||
// TODO: 'c helm.Interface' is deprecated in favor of actionConfig
|
||||
func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command {
|
||||
func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "helm",
|
||||
Short: "The Helm package manager for Kubernetes.",
|
||||
|
|
@ -92,15 +90,15 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri
|
|||
newChartCmd(actionConfig, out),
|
||||
|
||||
// release commands
|
||||
newGetCmd(c, out),
|
||||
newHistoryCmd(c, out),
|
||||
newGetCmd(actionConfig, out),
|
||||
newHistoryCmd(actionConfig, out),
|
||||
newInstallCmd(actionConfig, out),
|
||||
newListCmd(actionConfig, out),
|
||||
newReleaseTestCmd(c, out),
|
||||
newRollbackCmd(c, out),
|
||||
newStatusCmd(c, out),
|
||||
newUninstallCmd(c, out),
|
||||
newUpgradeCmd(c, out),
|
||||
newReleaseTestCmd(actionConfig, out),
|
||||
newRollbackCmd(actionConfig, out),
|
||||
newStatusCmd(actionConfig, out),
|
||||
newUninstallCmd(actionConfig, out),
|
||||
newUpgradeCmd(actionConfig, out),
|
||||
|
||||
newCompletionCmd(out),
|
||||
newHomeCmd(out),
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func TestRootCmd(t *testing.T) {
|
|||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
cmd, _, err := executeCommandC(nil, tt.args)
|
||||
cmd, _, err := executeActionCommand(tt.args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/search"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
120
cmd/helm/show.go
120
cmd/helm/show.go
|
|
@ -19,14 +19,11 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
const showDesc = `
|
||||
|
|
@ -51,24 +48,8 @@ This command inspects a chart (directory, file, or URL) and displays the content
|
|||
of the README file
|
||||
`
|
||||
|
||||
type showOptions struct {
|
||||
chartpath string
|
||||
output string
|
||||
|
||||
chartPathOptions
|
||||
}
|
||||
|
||||
const (
|
||||
chartOnly = "chart"
|
||||
valuesOnly = "values"
|
||||
readmeOnly = "readme"
|
||||
all = "all"
|
||||
)
|
||||
|
||||
var readmeFileNames = []string{"readme.md", "readme.txt", "readme"}
|
||||
|
||||
func newShowCmd(out io.Writer) *cobra.Command {
|
||||
o := &showOptions{output: all}
|
||||
client := action.NewShow(action.ShowAll)
|
||||
|
||||
showCommand := &cobra.Command{
|
||||
Use: "show [CHART]",
|
||||
|
|
@ -77,12 +58,16 @@ func newShowCmd(out io.Writer) *cobra.Command {
|
|||
Long: showDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cp, err := o.locateChart(args[0])
|
||||
cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.chartpath = cp
|
||||
return o.run(out)
|
||||
output, err := client.Run(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(out, output)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -92,13 +77,17 @@ func newShowCmd(out io.Writer) *cobra.Command {
|
|||
Long: showValuesDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.output = valuesOnly
|
||||
cp, err := o.locateChart(args[0])
|
||||
client.OutputFormat = action.ShowValues
|
||||
cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.chartpath = cp
|
||||
return o.run(out)
|
||||
output, err := client.Run(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(out, output)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -108,13 +97,17 @@ func newShowCmd(out io.Writer) *cobra.Command {
|
|||
Long: showChartDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.output = chartOnly
|
||||
cp, err := o.locateChart(args[0])
|
||||
client.OutputFormat = action.ShowChart
|
||||
cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.chartpath = cp
|
||||
return o.run(out)
|
||||
output, err := client.Run(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(out, output)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -124,19 +117,23 @@ func newShowCmd(out io.Writer) *cobra.Command {
|
|||
Long: readmeChartDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.output = readmeOnly
|
||||
cp, err := o.locateChart(args[0])
|
||||
client.OutputFormat = action.ShowReadme
|
||||
cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.chartpath = cp
|
||||
return o.run(out)
|
||||
output, err := client.Run(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(out, output)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmds := []*cobra.Command{showCommand, readmeSubCmd, valuesSubCmd, chartSubCmd}
|
||||
for _, subCmd := range cmds {
|
||||
o.chartPathOptions.addFlags(subCmd.Flags())
|
||||
addChartPathOptionsFlags(subCmd.Flags(), &client.ChartPathOptions)
|
||||
}
|
||||
|
||||
for _, subCmd := range cmds[1:] {
|
||||
|
|
@ -145,52 +142,3 @@ func newShowCmd(out io.Writer) *cobra.Command {
|
|||
|
||||
return showCommand
|
||||
}
|
||||
|
||||
func (i *showOptions) run(out io.Writer) error {
|
||||
chrt, err := loader.Load(i.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cf, err := yaml.Marshal(chrt.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.output == chartOnly || i.output == all {
|
||||
fmt.Fprintln(out, string(cf))
|
||||
}
|
||||
|
||||
if (i.output == valuesOnly || i.output == all) && chrt.Values != nil {
|
||||
if i.output == all {
|
||||
fmt.Fprintln(out, "---")
|
||||
}
|
||||
b, err := yaml.Marshal(chrt.Values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, string(b))
|
||||
}
|
||||
|
||||
if i.output == readmeOnly || i.output == all {
|
||||
if i.output == all {
|
||||
fmt.Fprintln(out, "---")
|
||||
}
|
||||
readme := findReadme(chrt.Files)
|
||||
if readme == nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintln(out, string(readme.Data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findReadme(files []*chart.File) (file *chart.File) {
|
||||
for _, file := range files {
|
||||
for _, n := range readmeFileNames {
|
||||
if strings.EqualFold(file.Name, n) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,20 +18,14 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/gosuri/uitable/util/strutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/action"
|
||||
)
|
||||
|
||||
var statusHelp = `
|
||||
|
|
@ -45,15 +39,8 @@ The status consists of:
|
|||
- additional notes provided by the chart
|
||||
`
|
||||
|
||||
type statusOptions struct {
|
||||
release string
|
||||
client helm.Interface
|
||||
version int
|
||||
outfmt string
|
||||
}
|
||||
|
||||
func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
o := &statusOptions{client: client}
|
||||
func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewStatus(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "status RELEASE_NAME",
|
||||
|
|
@ -61,88 +48,44 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
|||
Long: statusHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.release = args[0]
|
||||
o.client = ensureHelmClient(o.client, false)
|
||||
return o.run(out)
|
||||
rel, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outfmt, err := action.ParseOutputFormat(client.OutputFormat)
|
||||
// We treat an invalid format type as the default
|
||||
if err != nil && err != action.ErrInvalidFormatType {
|
||||
return err
|
||||
}
|
||||
|
||||
switch outfmt {
|
||||
case "":
|
||||
action.PrintRelease(out, rel)
|
||||
return nil
|
||||
case action.JSON:
|
||||
data, err := json.Marshal(rel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Marshal JSON output")
|
||||
}
|
||||
out.Write(data)
|
||||
return nil
|
||||
case action.YAML:
|
||||
data, err := yaml.Marshal(rel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Marshal YAML output")
|
||||
}
|
||||
out.Write(data)
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("unknown output format %q", outfmt)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().IntVar(&o.version, "revision", 0, "if set, display the status of the named release with revision")
|
||||
cmd.PersistentFlags().StringVarP(&o.outfmt, "output", "o", "", "output the status in the specified format (json or yaml)")
|
||||
f := cmd.PersistentFlags()
|
||||
f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision")
|
||||
f.StringVarP(&client.OutputFormat, "output", "o", "", "output the status in the specified format (json or yaml)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *statusOptions) run(out io.Writer) error {
|
||||
res, err := o.client.ReleaseContent(o.release, o.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch o.outfmt {
|
||||
case "":
|
||||
PrintStatus(out, res)
|
||||
return nil
|
||||
case "json":
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Marshal JSON output")
|
||||
}
|
||||
out.Write(data)
|
||||
return nil
|
||||
case "yaml":
|
||||
data, err := yaml.Marshal(res)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Marshal YAML output")
|
||||
}
|
||||
out.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Errorf("unknown output format %q", o.outfmt)
|
||||
}
|
||||
|
||||
// PrintStatus prints out the status of a release. Shared because also used by
|
||||
// install / upgrade
|
||||
func PrintStatus(out io.Writer, res *release.Release) {
|
||||
if !res.Info.LastDeployed.IsZero() {
|
||||
fmt.Fprintf(out, "LAST DEPLOYED: %s\n", res.Info.LastDeployed)
|
||||
}
|
||||
fmt.Fprintf(out, "NAMESPACE: %s\n", res.Namespace)
|
||||
fmt.Fprintf(out, "STATUS: %s\n", res.Info.Status.String())
|
||||
fmt.Fprintf(out, "\n")
|
||||
if len(res.Info.Resources) > 0 {
|
||||
re := regexp.MustCompile(" +")
|
||||
|
||||
w := tabwriter.NewWriter(out, 0, 0, 2, ' ', tabwriter.TabIndent)
|
||||
fmt.Fprintf(w, "RESOURCES:\n%s\n", re.ReplaceAllString(res.Info.Resources, "\t"))
|
||||
w.Flush()
|
||||
}
|
||||
if res.Info.LastTestSuiteRun != nil {
|
||||
lastRun := res.Info.LastTestSuiteRun
|
||||
fmt.Fprintf(out, "TEST SUITE:\n%s\n%s\n\n%s\n",
|
||||
fmt.Sprintf("Last Started: %s", lastRun.StartedAt),
|
||||
fmt.Sprintf("Last Completed: %s", lastRun.CompletedAt),
|
||||
formatTestResults(lastRun.Results))
|
||||
}
|
||||
|
||||
if len(res.Info.Notes) > 0 {
|
||||
fmt.Fprintf(out, "NOTES:\n%s\n", res.Info.Notes)
|
||||
}
|
||||
}
|
||||
|
||||
func formatTestResults(results []*release.TestRun) string {
|
||||
tbl := uitable.New()
|
||||
tbl.MaxColWidth = 50
|
||||
tbl.AddRow("TEST", "STATUS", "INFO", "STARTED", "COMPLETED")
|
||||
for i := 0; i < len(results); i++ {
|
||||
r := results[i]
|
||||
n := r.Name
|
||||
s := strutil.PadRight(r.Status.String(), 10, ' ')
|
||||
i := r.Info
|
||||
ts := r.StartedAt
|
||||
tc := r.CompletedAt
|
||||
tbl.AddRow(n, s, i, ts, tc)
|
||||
}
|
||||
return tbl.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,15 +20,18 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
|
||||
"k8s.io/helm/pkg/release"
|
||||
)
|
||||
|
||||
func TestStatusCmd(t *testing.T) {
|
||||
releasesMockWithStatus := func(info *release.Info) []*release.Release {
|
||||
info.LastDeployed = time.Unix(1452902400, 0).UTC()
|
||||
return []*release.Release{{
|
||||
Name: "flummoxed-chickadee",
|
||||
Info: info,
|
||||
Name: "flummoxed-chickadee",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
}}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,39 +19,23 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/engine"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
util "k8s.io/helm/pkg/releaseutil"
|
||||
"k8s.io/helm/pkg/tiller"
|
||||
)
|
||||
|
||||
const defaultDirectoryPermission = 0755
|
||||
|
||||
var (
|
||||
whitespaceRegex = regexp.MustCompile(`^\s*$`)
|
||||
|
||||
// defaultKubeVersion is the default value of --kube-version flag
|
||||
defaultKubeVersion = fmt.Sprintf("%s.%s", chartutil.DefaultKubeVersion.Major, chartutil.DefaultKubeVersion.Minor)
|
||||
"k8s.io/helm/pkg/action"
|
||||
"k8s.io/helm/pkg/kube"
|
||||
"k8s.io/helm/pkg/storage"
|
||||
"k8s.io/helm/pkg/storage/driver"
|
||||
)
|
||||
|
||||
const templateDesc = `
|
||||
Render chart templates locally and display the output.
|
||||
|
||||
This does not require Tiller. However, any values that would normally be
|
||||
This does not require Helm. However, any values that would normally be
|
||||
looked up or retrieved in-cluster will be faked locally. Additionally, none
|
||||
of the server-side testing of chart validity (e.g. whether an API is supported)
|
||||
is done.
|
||||
|
|
@ -61,232 +45,38 @@ To render just one template in a chart, use '-x':
|
|||
$ helm template mychart -x templates/deployment.yaml
|
||||
`
|
||||
|
||||
type templateOptions struct {
|
||||
nameTemplate string // --name-template
|
||||
showNotes bool // --notes
|
||||
releaseName string // --name
|
||||
renderFiles []string // --execute
|
||||
kubeVersion string // --kube-version
|
||||
outputDir string // --output-dir
|
||||
|
||||
valuesOptions
|
||||
|
||||
chartPath string
|
||||
}
|
||||
|
||||
func newTemplateCmd(out io.Writer) *cobra.Command {
|
||||
o := &templateOptions{}
|
||||
customConfig := &action.Configuration{
|
||||
// Add mock objects in here so it doesn't use Kube API server
|
||||
Releases: storage.Init(driver.NewMemory()),
|
||||
KubeClient: &kube.PrintingKubeClient{Out: ioutil.Discard},
|
||||
Discovery: fake.NewSimpleClientset().Discovery(),
|
||||
Log: func(format string, v ...interface{}) {
|
||||
fmt.Fprintf(out, format, v...)
|
||||
},
|
||||
}
|
||||
|
||||
client := action.NewInstall(customConfig)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "template CHART",
|
||||
Short: fmt.Sprintf("locally render templates"),
|
||||
Long: templateDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// 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 {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.DryRun = true
|
||||
client.ReleaseName = "RELEASE-NAME"
|
||||
client.Replace = true // Skip the name check
|
||||
rel, err := runInstall(args, client, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.run(out)
|
||||
fmt.Fprintln(out, strings.TrimSpace(rel.Manifest))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&o.showNotes, "notes", false, "show the computed NOTES.txt file as well")
|
||||
f.StringVarP(&o.releaseName, "name", "", "RELEASE-NAME", "release name")
|
||||
f.StringArrayVarP(&o.renderFiles, "execute", "x", []string{}, "only execute the given templates")
|
||||
f.StringVar(&o.nameTemplate, "name-template", "", "specify template used to name the release")
|
||||
f.StringVar(&o.kubeVersion, "kube-version", defaultKubeVersion, "kubernetes version used as Capabilities.KubeVersion.Major/Minor")
|
||||
f.StringVar(&o.outputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
|
||||
o.valuesOptions.addFlags(f)
|
||||
addInstallFlags(cmd.Flags(), client)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *templateOptions) run(out io.Writer) error {
|
||||
// verify specified templates exist relative to chart
|
||||
rf := []string{}
|
||||
var af string
|
||||
var err error
|
||||
if len(o.renderFiles) > 0 {
|
||||
for _, f := range o.renderFiles {
|
||||
if !filepath.IsAbs(f) {
|
||||
af, err = filepath.Abs(filepath.Join(o.chartPath, f))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not resolve template path")
|
||||
}
|
||||
} else {
|
||||
af = f
|
||||
}
|
||||
rf = append(rf, af)
|
||||
|
||||
if _, err := os.Stat(af); err != nil {
|
||||
return errors.Wrap(err, "could not resolve template path")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verify that output-dir exists if provided
|
||||
if o.outputDir != "" {
|
||||
if _, err := os.Stat(o.outputDir); os.IsNotExist(err) {
|
||||
return errors.Errorf("output-dir '%s' does not exist", o.outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
// get combined values and create config
|
||||
config, err := o.mergedValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If template is specified, try to run the template.
|
||||
if o.nameTemplate != "" {
|
||||
o.releaseName, err = templateName(o.nameTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Check chart dependencies to make sure all are present in /charts
|
||||
c, err := loader.Load(o.chartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validInstallableChart, err := chartutil.IsChartInstallable(c)
|
||||
if !validInstallableChart {
|
||||
return err
|
||||
}
|
||||
|
||||
if req := c.Metadata.Dependencies; req != nil {
|
||||
if err := checkDependencies(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: o.releaseName,
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(c, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// kubernetes version
|
||||
kv, err := semver.NewVersion(o.kubeVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse a kubernetes version")
|
||||
}
|
||||
|
||||
caps := chartutil.DefaultCapabilities
|
||||
caps.KubeVersion.Major = fmt.Sprint(kv.Major())
|
||||
caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
|
||||
caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
|
||||
|
||||
vals, err := chartutil.ToRenderValues(c, config, options, caps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rendered, err := engine.Render(c, vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listManifests := []tiller.Manifest{}
|
||||
// extract kind and name
|
||||
re := regexp.MustCompile("kind:(.*)\n")
|
||||
for k, v := range rendered {
|
||||
match := re.FindStringSubmatch(v)
|
||||
h := "Unknown"
|
||||
if len(match) == 2 {
|
||||
h = strings.TrimSpace(match[1])
|
||||
}
|
||||
m := tiller.Manifest{Name: k, Content: v, Head: &util.SimpleHead{Kind: h}}
|
||||
listManifests = append(listManifests, m)
|
||||
}
|
||||
in := func(needle string, haystack []string) bool {
|
||||
// make needle path absolute
|
||||
// NOTE: chart manifest names always use backslashes as path separators, even on Windows
|
||||
d := strings.Split(needle, "/")
|
||||
dd := d[1:]
|
||||
an := filepath.Join(o.chartPath, strings.Join(dd, string(os.PathSeparator)))
|
||||
|
||||
for _, h := range haystack {
|
||||
if h == an {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if settings.Debug {
|
||||
rel := &release.Release{
|
||||
Name: o.releaseName,
|
||||
Chart: c,
|
||||
Config: config,
|
||||
Version: 1,
|
||||
Info: &release.Info{LastDeployed: time.Now()},
|
||||
}
|
||||
printRelease(out, rel)
|
||||
}
|
||||
|
||||
for _, m := range tiller.SortByKind(listManifests) {
|
||||
b := filepath.Base(m.Name)
|
||||
switch {
|
||||
case len(o.renderFiles) > 0 && !in(m.Name, rf):
|
||||
continue
|
||||
case !o.showNotes && b == "NOTES.txt":
|
||||
continue
|
||||
case strings.HasPrefix(b, "_"):
|
||||
continue
|
||||
case whitespaceRegex.MatchString(m.Content):
|
||||
// blank template after execution
|
||||
continue
|
||||
case o.outputDir != "":
|
||||
if err := writeToFile(out, o.outputDir, m.Name, m.Content); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(out, "---\n# Source: %s\n", m.Name)
|
||||
fmt.Fprintln(out, m.Content)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// write the <data> to <output-dir>/<name>
|
||||
func writeToFile(out io.Writer, outputDir, name, data string) error {
|
||||
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
|
||||
|
||||
if err := ensureDirectoryForFile(outfileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(outfileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if _, err = f.WriteString(fmt.Sprintf("##---\n# Source: %s\n%s", name, data)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "wrote %s\n", outfileName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if the directory exists to create file. creates if don't exists
|
||||
func ensureDirectoryForFile(file string) error {
|
||||
baseDir := path.Dir(file)
|
||||
if _, err := os.Stat(baseDir); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.MkdirAll(baseDir, defaultDirectoryPermission)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ import (
|
|||
var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1"
|
||||
|
||||
func TestTemplateCmd(t *testing.T) {
|
||||
absChartPath, err := filepath.Abs(chartPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tests := []cmdTestCase{
|
||||
{
|
||||
name: "check name",
|
||||
|
|
@ -37,24 +33,9 @@ func TestTemplateCmd(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "check set name",
|
||||
cmd: fmt.Sprintf("template '%s' -x '%s' --set service.name=apache", chartPath, filepath.Join("templates", "service.yaml")),
|
||||
cmd: fmt.Sprintf("template '%s' --set service.name=apache", chartPath),
|
||||
golden: "output/template-set.txt",
|
||||
},
|
||||
{
|
||||
name: "check execute absolute",
|
||||
cmd: fmt.Sprintf("template '%s' -x '%s' --set service.name=apache", chartPath, filepath.Join(absChartPath, "templates", "service.yaml")),
|
||||
golden: "output/template-absolute.txt",
|
||||
},
|
||||
{
|
||||
name: "check release name",
|
||||
cmd: fmt.Sprintf("template '%s' --name test", chartPath),
|
||||
golden: "output/template-name.txt",
|
||||
},
|
||||
{
|
||||
name: "check notes",
|
||||
cmd: fmt.Sprintf("template '%s' --notes", chartPath),
|
||||
golden: "output/template-notes.txt",
|
||||
},
|
||||
{
|
||||
name: "check values files",
|
||||
cmd: fmt.Sprintf("template '%s' --values '%s'", chartPath, filepath.Join(chartPath, "/charts/subchartA/values.yaml")),
|
||||
|
|
@ -65,11 +46,6 @@ func TestTemplateCmd(t *testing.T) {
|
|||
cmd: fmt.Sprintf(`template '%s' --name-template='foobar-{{ b64enc "abc" }}-baz'`, chartPath),
|
||||
golden: "output/template-name-template.txt",
|
||||
},
|
||||
{
|
||||
name: "check kube version",
|
||||
cmd: fmt.Sprintf("template '%s' --kube-version 1.6", chartPath),
|
||||
golden: "output/template-kube-version.txt",
|
||||
},
|
||||
{
|
||||
name: "check no args",
|
||||
cmd: "template",
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Error: FindFirstFile \no\such\chart: The system cannot find the path specified.
|
||||
|
|
@ -1 +0,0 @@
|
|||
WARNING: no dependencies at testdata\testcharts\alpine\charts
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: aeneas
|
||||
NAME: aeneas
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
FINAL NAME: FOOBAR
|
||||
NAME: FOOBAR
|
||||
NAME: FOOBAR
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: aeneas
|
||||
NAME: aeneas
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: virgil
|
||||
NAME: virgil
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: virgil
|
||||
NAME: virgil
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: foobar
|
||||
NAME: foobar
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: virgil
|
||||
NAME: virgil
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: virgil
|
||||
NAME: virgil
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
NAME: apollo
|
||||
NAME: apollo
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
2
cmd/helm/testdata/output/install.txt
vendored
2
cmd/helm/testdata/output/install.txt
vendored
|
|
@ -1,4 +1,4 @@
|
|||
NAME: aeneas
|
||||
NAME: aeneas
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
atlas-guide
|
||||
thomas-guide
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
NAME REVISION UPDATED STATUS CHART NAMESPACE
|
||||
thomas-guide 1 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 default
|
||||
thomas-guide 2 1977-09-02 22:04:05 +0000 UTC failed foo-0.1.0-beta.1 default
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
atlas-guide
|
||||
crazy-maps
|
||||
thomas-guide
|
||||
wild-idea
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
NAME REVISION UPDATED STATUS CHART NAMESPACE
|
||||
thomas-guide 1 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 default
|
||||
2
cmd/helm/testdata/output/list.txt
vendored
2
cmd/helm/testdata/output/list.txt
vendored
|
|
@ -1,2 +0,0 @@
|
|||
NAME REVISION UPDATED STATUS CHART NAMESPACE
|
||||
atlas 1 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 default
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
NAME: flummoxed-chickadee
|
||||
LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
|
||||
NAMESPACE:
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
NAME: flummoxed-chickadee
|
||||
LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
|
||||
NAMESPACE:
|
||||
STATUS: deployed
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
NAME: flummoxed-chickadee
|
||||
LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
|
||||
NAMESPACE:
|
||||
STATUS: deployed
|
||||
|
|
|
|||
1
cmd/helm/testdata/output/status.txt
vendored
1
cmd/helm/testdata/output/status.txt
vendored
|
|
@ -1,3 +1,4 @@
|
|||
NAME: flummoxed-chickadee
|
||||
LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
|
||||
NAMESPACE:
|
||||
STATUS: deployed
|
||||
|
|
|
|||
22
cmd/helm/testdata/output/template-absolute.txt
vendored
22
cmd/helm/testdata/output/template-absolute.txt
vendored
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart1
|
||||
labels:
|
||||
chart: "subchart1-0.1.0"
|
||||
release-name: "RELEASE-NAME"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "9"
|
||||
kube-version/gitversion: "v1.9.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
# Source: subchart1/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart1
|
||||
labels:
|
||||
chart: "subchart1-0.1.0"
|
||||
release-name: "RELEASE-NAME"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "6"
|
||||
kube-version/gitversion: "v1.6.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
@ -15,7 +15,6 @@ spec:
|
|||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -33,7 +32,6 @@ spec:
|
|||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -55,4 +53,3 @@ spec:
|
|||
name: nginx
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
|
|||
58
cmd/helm/testdata/output/template-name.txt
vendored
58
cmd/helm/testdata/output/template-name.txt
vendored
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
# Source: subchart1/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart1
|
||||
labels:
|
||||
chart: "subchart1-0.1.0"
|
||||
release-name: "test"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "9"
|
||||
kube-version/gitversion: "v1.9.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
61
cmd/helm/testdata/output/template-notes.txt
vendored
61
cmd/helm/testdata/output/template-notes.txt
vendored
|
|
@ -1,61 +0,0 @@
|
|||
---
|
||||
# Source: subchart1/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart1
|
||||
labels:
|
||||
chart: "subchart1-0.1.0"
|
||||
release-name: "RELEASE-NAME"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "9"
|
||||
kube-version/gitversion: "v1.9.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/NOTES.txt
|
||||
Sample notes for subchart1
|
||||
35
cmd/helm/testdata/output/template-set.txt
vendored
35
cmd/helm/testdata/output/template-set.txt
vendored
|
|
@ -1,4 +1,38 @@
|
|||
---
|
||||
# Source: subchart1/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
|
@ -19,4 +53,3 @@ spec:
|
|||
name: apache
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ spec:
|
|||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -33,7 +32,6 @@ spec:
|
|||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -55,4 +53,3 @@ spec:
|
|||
name: apache
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
|
|||
3
cmd/helm/testdata/output/template.txt
vendored
3
cmd/helm/testdata/output/template.txt
vendored
|
|
@ -15,7 +15,6 @@ spec:
|
|||
name: apache
|
||||
selector:
|
||||
app: subcharta
|
||||
|
||||
---
|
||||
# Source: subchart1/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -33,7 +32,6 @@ spec:
|
|||
name: nginx
|
||||
selector:
|
||||
app: subchartb
|
||||
|
||||
---
|
||||
# Source: subchart1/templates/service.yaml
|
||||
apiVersion: v1
|
||||
|
|
@ -55,4 +53,3 @@ spec:
|
|||
name: nginx
|
||||
selector:
|
||||
app: subchart1
|
||||
|
||||
|
|
|
|||
2
cmd/helm/testdata/output/test-error.txt
vendored
2
cmd/helm/testdata/output/test-error.txt
vendored
|
|
@ -1,2 +0,0 @@
|
|||
ERROR: yellow lights everywhere
|
||||
Error: 1 test(s) failed
|
||||
13
cmd/helm/testdata/output/test-failure.txt
vendored
13
cmd/helm/testdata/output/test-failure.txt
vendored
|
|
@ -1,2 +1,11 @@
|
|||
FAILURE: red lights everywhere
|
||||
Error: 1 test(s) failed
|
||||
NAME: test-failure
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
TEST SUITE:
|
||||
Last Started: 1977-09-02 22:04:05 +0000 UTC
|
||||
Last Completed: 1977-09-02 22:04:05 +0000 UTC
|
||||
|
||||
TEST STATUS INFO STARTED COMPLETED
|
||||
test-failure failure 2016-01-16 00:00:00 +0000 UTC 2016-01-16 00:00:00 +0000 UTC
|
||||
|
|
|
|||
1
cmd/helm/testdata/output/test-no-tests.txt
vendored
Normal file
1
cmd/helm/testdata/output/test-no-tests.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
No Tests Found
|
||||
12
cmd/helm/testdata/output/test-running.txt
vendored
12
cmd/helm/testdata/output/test-running.txt
vendored
|
|
@ -1 +1,11 @@
|
|||
RUNNING: things are happpeningggg
|
||||
NAME: test-running
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
TEST SUITE:
|
||||
Last Started: 1977-09-02 22:04:05 +0000 UTC
|
||||
Last Completed: 1977-09-02 22:04:05 +0000 UTC
|
||||
|
||||
TEST STATUS INFO STARTED COMPLETED
|
||||
test-running running 2016-01-16 00:00:00 +0000 UTC 2016-01-16 00:00:00 +0000 UTC
|
||||
|
|
|
|||
11
cmd/helm/testdata/output/test-success.txt
vendored
Normal file
11
cmd/helm/testdata/output/test-success.txt
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
NAME: test-success
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
TEST SUITE:
|
||||
Last Started: 1977-09-02 22:04:05 +0000 UTC
|
||||
Last Completed: 1977-09-02 22:04:05 +0000 UTC
|
||||
|
||||
TEST STATUS INFO STARTED COMPLETED
|
||||
test-success success 2016-01-16 00:00:00 +0000 UTC 2016-01-16 00:00:00 +0000 UTC
|
||||
12
cmd/helm/testdata/output/test-unknown.txt
vendored
12
cmd/helm/testdata/output/test-unknown.txt
vendored
|
|
@ -1 +1,11 @@
|
|||
UNKNOWN: yellow lights everywhere
|
||||
NAME: test-unknown
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
TEST SUITE:
|
||||
Last Started: 1977-09-02 22:04:05 +0000 UTC
|
||||
Last Completed: 1977-09-02 22:04:05 +0000 UTC
|
||||
|
||||
TEST STATUS INFO STARTED COMPLETED
|
||||
test-unknown unknown 2016-01-16 00:00:00 +0000 UTC 2016-01-16 00:00:00 +0000 UTC
|
||||
|
|
|
|||
1
cmd/helm/testdata/output/test.txt
vendored
1
cmd/helm/testdata/output/test.txt
vendored
|
|
@ -1 +0,0 @@
|
|||
PASSED: green lights everywhere
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
Release "crazy-bunny" has been upgraded. Happy Helming!
|
||||
NAME: crazy-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=crazy-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
Release "zany-bunny" has been upgraded. Happy Helming!
|
||||
NAME: zany-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=zany-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
Release "funny-bunny" has been upgraded. Happy Helming!
|
||||
NAME: funny-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
Release "funny-bunny" has been upgraded. Happy Helming!
|
||||
NAME: funny-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
Release "funny-bunny" has been upgraded. Happy Helming!
|
||||
NAME: funny-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
Release "crazy-bunny" has been upgraded. Happy Helming!
|
||||
NAME: crazy-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=crazy-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
6
cmd/helm/testdata/output/upgrade.txt
vendored
6
cmd/helm/testdata/output/upgrade.txt
vendored
|
|
@ -1,5 +1,11 @@
|
|||
Release "funny-bunny" has been upgraded. Happy Helming!
|
||||
NAME: funny-bunny
|
||||
LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
|
||||
NOTES:
|
||||
1. Get the application URL by running these commands:
|
||||
export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue