From d32c20fd5ce8a835999cc736a9460e88cb8891ca Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 12:49:10 -0700 Subject: [PATCH 01/14] ref(cmd): move flags out of init() --- cmd/helm/helm.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 3a42044e0..779403d8a 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -63,30 +63,35 @@ Environment: $HELM_HOST Set an alternative Tiller host. The format is host:port (default ":44134"). ` -// RootCommand is the top-level command for Helm. -var RootCommand = &cobra.Command{ - Use: "helm", - Short: "The Helm package manager for Kubernetes.", - Long: globalUsage, - PersistentPostRun: teardown, - SilenceUsage: true, -} - -func init() { +func newRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "helm", + Short: "The Helm package manager for Kubernetes.", + Long: globalUsage, + SilenceUsage: true, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + teardown() + }, + } home := os.Getenv(homeEnvVar) if home == "" { home = "$HOME/.helm" } thost := os.Getenv(hostEnvVar) - p := RootCommand.PersistentFlags() + p := cmd.PersistentFlags() p.StringVar(&helmHome, "home", home, "location of your Helm config. Overrides $HELM_HOME.") p.StringVar(&tillerHost, "host", thost, "address of tiller. Overrides $HELM_HOST.") p.StringVarP(&tillerNamespace, "namespace", "", "", "kubernetes namespace") p.BoolVarP(&flagDebug, "debug", "", false, "enable verbose output") + return cmd } +// RootCommand is the top-level command for Helm. +var RootCommand = newRootCmd() + func main() { - if err := RootCommand.Execute(); err != nil { + cmd := RootCommand + if err := cmd.Execute(); err != nil { os.Exit(1) } } @@ -112,7 +117,7 @@ func setupConnection(c *cobra.Command, args []string) error { return nil } -func teardown(c *cobra.Command, args []string) { +func teardown() { if tunnel != nil { tunnel.Close() } From 8cb39ce5cc2cb2054c52475e403aafb590d36c2b Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 12:50:45 -0700 Subject: [PATCH 02/14] ref(cmd): refactor out globals and init() --- cmd/helm/list.go | 78 +++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 01e22f4e7..fc62677cf 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -18,6 +18,8 @@ package main import ( "fmt" + "io" + "os" "strings" "github.com/gosuri/uitable" @@ -52,51 +54,59 @@ server's default, which may be much higher than 256. Pairing the '--max' flag with the '--offset' flag allows you to page through results. ` -var listCommand = &cobra.Command{ - Use: "list [flags] [FILTER]", - Short: "list releases", - Long: listHelp, - RunE: listCmd, - Aliases: []string{"ls"}, - PersistentPreRunE: setupConnection, +type lister struct { + long bool + max int + offset string + byDate bool + sortDesc bool + out io.Writer } -var ( - listLong bool - listMax int - listOffset string - listByDate bool - listSortDesc bool -) +func newListCmd(out io.Writer) *cobra.Command { + list := &lister{ + out: out, + } + cmd := &cobra.Command{ + Use: "list [flags] [FILTER]", + Short: "list releases", + Long: listHelp, + Aliases: []string{"ls"}, + PersistentPreRunE: setupConnection, + RunE: func(cmd *cobra.Command, args []string) error { + return list.run(args) + }, + } + f := cmd.Flags() + f.BoolVarP(&list.long, "long", "l", false, "output long listing format") + f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date") + f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order") + f.IntVarP(&list.max, "max", "m", 256, "maximum number of releases to fetch") + f.StringVarP(&list.offset, "offset", "o", "", "the next release name in the list, used to offset from start value") + return cmd +} func init() { - f := listCommand.Flags() - f.BoolVarP(&listLong, "long", "l", false, "output long listing format") - f.BoolVarP(&listByDate, "date", "d", false, "sort by release date") - f.BoolVarP(&listSortDesc, "reverse", "r", false, "reverse the sort order") - f.IntVarP(&listMax, "max", "m", 256, "maximum number of releases to fetch") - f.StringVarP(&listOffset, "offset", "o", "", "the next release name in the list, used to offset from start value") - - RootCommand.AddCommand(listCommand) + RootCommand.AddCommand(newListCmd(os.Stdout)) } -func listCmd(cmd *cobra.Command, args []string) error { +func (l *lister) run(args []string) error { var filter string if len(args) > 0 { filter = strings.Join(args, " ") } sortBy := services.ListSort_NAME - if listByDate { + if l.byDate { sortBy = services.ListSort_LAST_RELEASED } sortOrder := services.ListSort_ASC - if listSortDesc { + if l.sortDesc { sortOrder = services.ListSort_DESC } - res, err := helm.ListReleases(listMax, listOffset, sortBy, sortOrder, filter) + res, err := helm.ListReleases(l.max, l.offset, sortBy, sortOrder, filter) if err != nil { return prettyError(err) } @@ -106,21 +116,23 @@ func listCmd(cmd *cobra.Command, args []string) error { } if res.Next != "" { - fmt.Printf("\tnext: %s", res.Next) + fmt.Fprintf(l.out, "\tnext: %s", res.Next) } rels := res.Releases - if listLong { - return formatList(rels) + + if l.long { + fmt.Fprintln(l.out, formatList(rels)) + return nil } for _, r := range rels { - fmt.Println(r.Name) + fmt.Fprintln(l.out, r.Name) } return nil } -func formatList(rels []*release.Release) error { +func formatList(rels []*release.Release) string { table := uitable.New() table.MaxColWidth = 30 table.AddRow("NAME", "UPDATED", "STATUS", "CHART") @@ -130,7 +142,5 @@ func formatList(rels []*release.Release) error { s := r.Info.Status.Code.String() table.AddRow(r.Name, t, s, c) } - fmt.Println(table) - - return nil + return table.String() } From b9904281115652b234a81da9def2fd98f1543d36 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 12:52:35 -0700 Subject: [PATCH 03/14] test(cmd): add pattern for testing cmd package --- cmd/helm/list_test.go | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 cmd/helm/list_test.go diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go new file mode 100644 index 000000000..4e82a9eeb --- /dev/null +++ b/cmd/helm/list_test.go @@ -0,0 +1,89 @@ +package main + +import ( + "bytes" + "testing" + + "k8s.io/helm/pkg/helm" +) + +// Stubbed out tests at two diffent layers +// TestList() is testing the command action +// TestListCmd() is testing command line interface + +// TODO mock tiller responses + +func TestList(t *testing.T) { + helm.Config.ServAddr = ":44134" + + tests := []struct { + name string + lister *lister + expected string + err bool + }{ + { + name: "with a release", + lister: &lister{}, + expected: "understood-coral", + }, + { + name: "list --long", + lister: &lister{long: true}, + expected: "NAME \tUPDATED \tSTATUS \tCHART \nunderstood-coral\tTue Jun 28 12:29:54 2016\tDEPLOYED\tnginx-0.1.0", + }, + } + + var buf bytes.Buffer + for _, tt := range tests { + tt.lister.out = &buf + err := tt.lister.run([]string{}) + if (err != nil) != tt.err { + t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) + } + actual := string(bytes.TrimSpace(buf.Bytes())) + if actual != tt.expected { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + } + buf.Reset() + } +} + +func TestListCmd(t *testing.T) { + helm.Config.ServAddr = ":44134" + + tests := []struct { + name string + args []string + flags map[string]string + expected string + err bool + }{ + { + name: "with a release", + expected: "understood-coral", + }, + { + name: "list --long", + flags: map[string]string{"long": "1"}, + expected: "NAME \tUPDATED \tSTATUS \tCHART \nunderstood-coral\tTue Jun 28 12:29:54 2016\tDEPLOYED\tnginx-0.1.0", + }, + } + + var buf bytes.Buffer + for _, tt := range tests { + cmd := newListCmd(&buf) + for flag, value := range tt.flags { + cmd.Flags().Set(flag, value) + } + err := cmd.RunE(cmd, tt.args) + if (err != nil) != tt.err { + t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) + } + actual := string(bytes.TrimSpace(buf.Bytes())) + if actual != tt.expected { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + } + buf.Reset() + } +} From 73f1bef3c995aabe76a41b2b72b377d13fd6e9b4 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 16:58:54 -0700 Subject: [PATCH 04/14] test(cmd): add helm client mocking --- cmd/helm/helm.go | 4 ++ cmd/helm/list.go | 37 +++++++++-------- cmd/helm/list_test.go | 97 +++++++++++++++++++++++++++++++++---------- pkg/helm/interface.go | 30 +++++++++++++ 4 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 pkg/helm/interface.go diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 779403d8a..4552df3fc 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -90,7 +90,11 @@ func newRootCmd() *cobra.Command { var RootCommand = newRootCmd() func main() { + out := os.Stdout + client := helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) + cmd := RootCommand + cmd.AddCommand(newListCmd(client, out)) if err := cmd.Execute(); err != nil { os.Exit(1) } diff --git a/cmd/helm/list.go b/cmd/helm/list.go index fc62677cf..a767cbec9 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -19,7 +19,6 @@ package main import ( "fmt" "io" - "os" "strings" "github.com/gosuri/uitable" @@ -55,17 +54,20 @@ flag with the '--offset' flag allows you to page through results. ` type lister struct { + filter string long bool - max int + limit int offset string byDate bool sortDesc bool out io.Writer + client helm.Interface } -func newListCmd(out io.Writer) *cobra.Command { +func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { list := &lister{ - out: out, + client: client, + out: out, } cmd := &cobra.Command{ Use: "list [flags] [FILTER]", @@ -74,28 +76,22 @@ func newListCmd(out io.Writer) *cobra.Command { Aliases: []string{"ls"}, PersistentPreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { - return list.run(args) + if len(args) > 0 { + list.filter = strings.Join(args, " ") + } + return list.run() }, } f := cmd.Flags() f.BoolVarP(&list.long, "long", "l", false, "output long listing format") f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date") f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order") - f.IntVarP(&list.max, "max", "m", 256, "maximum number of releases to fetch") + f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch") f.StringVarP(&list.offset, "offset", "o", "", "the next release name in the list, used to offset from start value") return cmd } -func init() { - RootCommand.AddCommand(newListCmd(os.Stdout)) -} - -func (l *lister) run(args []string) error { - var filter string - if len(args) > 0 { - filter = strings.Join(args, " ") - } - +func (l *lister) run() error { sortBy := services.ListSort_NAME if l.byDate { sortBy = services.ListSort_LAST_RELEASED @@ -106,7 +102,14 @@ func (l *lister) run(args []string) error { sortOrder = services.ListSort_DESC } - res, err := helm.ListReleases(l.max, l.offset, sortBy, sortOrder, filter) + res, err := l.client.ListReleases( + helm.ReleaseListLimit(l.limit), + helm.ReleaseListOffset(l.offset), + helm.ReleaseListFilter(l.filter), + helm.ReleaseListSort(int32(sortBy)), + helm.ReleaseListOrder(int32(sortOrder)), + ) + if err != nil { return prettyError(err) } diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 4e82a9eeb..56eee07f3 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -4,18 +4,51 @@ import ( "bytes" "testing" + "github.com/golang/protobuf/ptypes/timestamp" + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + rls "k8s.io/helm/pkg/proto/hapi/services" ) -// Stubbed out tests at two diffent layers -// TestList() is testing the command action -// TestListCmd() is testing command line interface +func releaseMock(name string) *release.Release { + date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} + return &release.Release{ + Name: name, + Info: &release.Info{ + FirstDeployed: &date, + LastDeployed: &date, + Status: &release.Status{Code: release.Status_DEPLOYED}, + }, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "foo", + Version: "0.1.0-beta.1", + }, + Templates: []*chart.Template{ + {Name: "foo.tpl", Data: []byte("Hello")}, + }, + }, + Config: &chart.Config{Raw: `name = "value"`}, + } +} -// TODO mock tiller responses +type fakeReleaseLister struct { + helm.Interface + rels []*release.Release + err error +} -func TestList(t *testing.T) { - helm.Config.ServAddr = ":44134" +func (fl *fakeReleaseLister) ListReleases(opts ...helm.ReleaseListOption) (*rls.ListReleasesResponse, error) { + resp := &rls.ListReleasesResponse{ + Count: int64(len(fl.rels)), + Releases: fl.rels, + } + return resp, fl.err +} +func TestListRun(t *testing.T) { tests := []struct { name string lister *lister @@ -23,21 +56,34 @@ func TestList(t *testing.T) { err bool }{ { - name: "with a release", - lister: &lister{}, - expected: "understood-coral", + name: "with a release", + lister: &lister{ + client: &fakeReleaseLister{ + rels: []*release.Release{ + releaseMock("thomas-guide"), + }, + }, + }, + expected: "thomas-guide", }, { - name: "list --long", - lister: &lister{long: true}, - expected: "NAME \tUPDATED \tSTATUS \tCHART \nunderstood-coral\tTue Jun 28 12:29:54 2016\tDEPLOYED\tnginx-0.1.0", + name: "list --long", + lister: &lister{ + client: &fakeReleaseLister{ + rels: []*release.Release{ + releaseMock("atlas"), + }, + }, + long: true, + }, + expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\tFri Sep 2 15:04:05 1977\tDEPLOYED\tfoo-0.1.0-beta.1", }, } var buf bytes.Buffer for _, tt := range tests { tt.lister.out = &buf - err := tt.lister.run([]string{}) + err := tt.lister.run() if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } @@ -50,29 +96,38 @@ func TestList(t *testing.T) { } func TestListCmd(t *testing.T) { - helm.Config.ServAddr = ":44134" - tests := []struct { name string args []string flags map[string]string + client helm.Interface expected string err bool }{ { - name: "with a release", - expected: "understood-coral", + name: "with a release", + client: &fakeReleaseLister{ + rels: []*release.Release{ + releaseMock("thomas-guide"), + }, + }, + expected: "thomas-guide", }, { - name: "list --long", - flags: map[string]string{"long": "1"}, - expected: "NAME \tUPDATED \tSTATUS \tCHART \nunderstood-coral\tTue Jun 28 12:29:54 2016\tDEPLOYED\tnginx-0.1.0", + name: "list --long", + flags: map[string]string{"long": "1"}, + client: &fakeReleaseLister{ + rels: []*release.Release{ + releaseMock("atlas"), + }, + }, + expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\tFri Sep 2 15:04:05 1977\tDEPLOYED\tfoo-0.1.0-beta.1", }, } var buf bytes.Buffer for _, tt := range tests { - cmd := newListCmd(&buf) + cmd := newListCmd(tt.client, &buf) for flag, value := range tt.flags { cmd.Flags().Set(flag, value) } diff --git a/pkg/helm/interface.go b/pkg/helm/interface.go new file mode 100644 index 000000000..7919adb8d --- /dev/null +++ b/pkg/helm/interface.go @@ -0,0 +1,30 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 helm + +import ( + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +// Interface for helm client for mocking in tests +type Interface interface { + ListReleases(opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) + InstallRelease(chStr string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) + DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) + ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) + UpdateRelease(rlsName string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) +} From 5013da3d2534ded6bee37288c257caeb613b64c7 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 20:38:58 -0700 Subject: [PATCH 05/14] fix(helm): add ReleaseContent to interface --- pkg/helm/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/helm/interface.go b/pkg/helm/interface.go index 7919adb8d..4cba4db43 100644 --- a/pkg/helm/interface.go +++ b/pkg/helm/interface.go @@ -27,4 +27,5 @@ type Interface interface { DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) UpdateRelease(rlsName string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) + ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) } From 4d92bd086f40c44b63cb46db6810d21fe7681974 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 22:36:47 -0700 Subject: [PATCH 06/14] fix(cmd): lazy load client --- cmd/helm/helm.go | 12 ++++++------ cmd/helm/list.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 4552df3fc..886be4ef0 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -19,6 +19,7 @@ package main import ( "errors" "fmt" + "io" "os" "strings" @@ -63,7 +64,7 @@ Environment: $HELM_HOST Set an alternative Tiller host. The format is host:port (default ":44134"). ` -func newRootCmd() *cobra.Command { +func newRootCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "helm", Short: "The Helm package manager for Kubernetes.", @@ -83,18 +84,17 @@ func newRootCmd() *cobra.Command { p.StringVar(&tillerHost, "host", thost, "address of tiller. Overrides $HELM_HOST.") p.StringVarP(&tillerNamespace, "namespace", "", "", "kubernetes namespace") p.BoolVarP(&flagDebug, "debug", "", false, "enable verbose output") + + cmd.AddCommand(newListCmd(nil, out)) + return cmd } // RootCommand is the top-level command for Helm. -var RootCommand = newRootCmd() +var RootCommand = newRootCmd(os.Stdout) func main() { - out := os.Stdout - client := helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) - cmd := RootCommand - cmd.AddCommand(newListCmd(client, out)) if err := cmd.Execute(); err != nil { os.Exit(1) } diff --git a/cmd/helm/list.go b/cmd/helm/list.go index a767cbec9..42537a71f 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -65,10 +65,7 @@ type lister struct { } func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { - list := &lister{ - client: client, - out: out, - } + list := &lister{out: out} cmd := &cobra.Command{ Use: "list [flags] [FILTER]", Short: "list releases", @@ -79,6 +76,9 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { if len(args) > 0 { list.filter = strings.Join(args, " ") } + if list.client == nil { + list.client = helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) + } return list.run() }, } From 6453c992415d154061ee09ae736682143a59ed7c Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 23:19:51 -0700 Subject: [PATCH 07/14] fix(cmd): rename list cmd context --- cmd/helm/list.go | 6 +++--- cmd/helm/list_test.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 42537a71f..0bc454266 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -53,7 +53,7 @@ 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 lister struct { +type listCmd struct { filter string long bool limit int @@ -65,7 +65,7 @@ type lister struct { } func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { - list := &lister{out: out} + list := &listCmd{out: out} cmd := &cobra.Command{ Use: "list [flags] [FILTER]", Short: "list releases", @@ -91,7 +91,7 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { return cmd } -func (l *lister) run() error { +func (l *listCmd) run() error { sortBy := services.ListSort_NAME if l.byDate { sortBy = services.ListSort_LAST_RELEASED diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 56eee07f3..dace068f7 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -51,13 +51,13 @@ func (fl *fakeReleaseLister) ListReleases(opts ...helm.ReleaseListOption) (*rls. func TestListRun(t *testing.T) { tests := []struct { name string - lister *lister + listCmd *listCmd expected string err bool }{ { name: "with a release", - lister: &lister{ + listCmd: &listCmd{ client: &fakeReleaseLister{ rels: []*release.Release{ releaseMock("thomas-guide"), @@ -68,7 +68,7 @@ func TestListRun(t *testing.T) { }, { name: "list --long", - lister: &lister{ + listCmd: &listCmd{ client: &fakeReleaseLister{ rels: []*release.Release{ releaseMock("atlas"), @@ -82,8 +82,8 @@ func TestListRun(t *testing.T) { var buf bytes.Buffer for _, tt := range tests { - tt.lister.out = &buf - err := tt.lister.run() + tt.listCmd.out = &buf + err := tt.listCmd.run() if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } From e339cc7e0c17f177c32e0f8b6f52023b6c0eb5ec Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 28 Jun 2016 23:23:10 -0700 Subject: [PATCH 08/14] fix(cmd): fix lazy load client --- cmd/helm/list.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 0bc454266..ede7cb01d 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -65,7 +65,10 @@ type listCmd struct { } func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { - list := &listCmd{out: out} + list := &listCmd{ + out: out, + client: client, + } cmd := &cobra.Command{ Use: "list [flags] [FILTER]", Short: "list releases", From 5e3044a65ffd2236b93eb4d56201d16c4e9cf23c Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 29 Jun 2016 21:40:38 -0700 Subject: [PATCH 09/14] ref(cmd): refactor get command --- cmd/helm/get.go | 139 +++++++++++++++++++++++++++++------------------ cmd/helm/helm.go | 1 + 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 2ec9217c7..ab3ef238e 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -19,6 +19,7 @@ package main import ( "errors" "fmt" + "io" "time" "github.com/spf13/cobra" @@ -53,48 +54,88 @@ 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. ` -var allValues = false - var errReleaseRequired = errors.New("release name is required") -var getCommand = &cobra.Command{ - Use: "get [flags] RELEASE_NAME", - Short: "download a named release", - Long: getHelp, - RunE: getCmd, - PersistentPreRunE: setupConnection, +type getCmd struct { + release string + out io.Writer + client helm.Interface } -var getValuesCommand = &cobra.Command{ - Use: "values [flags] RELEASE_NAME", - Short: "download the values file for a named release", - Long: getValuesHelp, - RunE: getValues, +func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { + get := &getCmd{ + out: out, + client: client, + } + cmd := &cobra.Command{ + Use: "get [flags] RELEASE_NAME", + Short: "download a named release", + Long: getHelp, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errReleaseRequired + } + get.release = args[0] + return get.run() + }, + PersistentPreRunE: setupConnection, + } + cmd.AddCommand(newGetValuesCmd(client, out)) + cmd.AddCommand(newGetManifestCmd(client, out)) + return cmd } -var getManifestCommand = &cobra.Command{ - Use: "manifest [flags] RELEASE_NAME", - Short: "download the manifest for a named release", - Long: getManifestHelp, - RunE: getManifest, +type getValuesCmd struct { + allValues bool + getCmd } -func init() { - // 'get values' flags. - getValuesCommand.PersistentFlags().BoolVarP(&allValues, "all", "a", false, "dump all (computed) values") +func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { + get := &getValuesCmd{} + get.out = out + get.client = client + cmd := &cobra.Command{ + Use: "values [flags] RELEASE_NAME", + Short: "download the values file for a named release", + Long: getValuesHelp, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errReleaseRequired + } + get.release = args[0] + return get.run() + }, + } + cmd.Flags().BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values") + return cmd +} - getCommand.AddCommand(getValuesCommand) - getCommand.AddCommand(getManifestCommand) - RootCommand.AddCommand(getCommand) +type getManifestCmd struct { + getCmd } -// getCmd is the command that implements 'helm get' -func getCmd(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errReleaseRequired +func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { + get := &getManifestCmd{} + get.out = out + get.client = client + cmd := &cobra.Command{ + Use: "manifest [flags] RELEASE_NAME", + Short: "download the manifest for a named release", + Long: getManifestHelp, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errReleaseRequired + } + get.release = args[0] + return get.run() + }, } + return cmd +} - res, err := helm.GetReleaseContent(args[0]) +// getCmd is the command that implements 'helm get' +func (g *getCmd) run() error { + res, err := helm.GetReleaseContent(g.release) if err != nil { return prettyError(err) } @@ -108,30 +149,26 @@ func getCmd(cmd *cobra.Command, args []string) error { return err } - fmt.Printf("CHART: %s-%s\n", res.Release.Chart.Metadata.Name, res.Release.Chart.Metadata.Version) - fmt.Printf("RELEASED: %s\n", timeconv.Format(res.Release.Info.LastDeployed, time.ANSIC)) - fmt.Println("USER-SUPPLIED VALUES:") - fmt.Println(res.Release.Config.Raw) - fmt.Println("COMPUTED VALUES:") - fmt.Println(cfgStr) - fmt.Println("MANIFEST:") - fmt.Println(res.Release.Manifest) + fmt.Fprintf(g.out, "CHART: %s-%s\n", res.Release.Chart.Metadata.Name, res.Release.Chart.Metadata.Version) + fmt.Fprintf(g.out, "RELEASED: %s\n", timeconv.Format(res.Release.Info.LastDeployed, time.ANSIC)) + fmt.Fprintln(g.out, "USER-SUPPLIED VALUES:") + fmt.Fprintln(g.out, res.Release.Config.Raw) + fmt.Fprintln(g.out, "COMPUTED VALUES:") + fmt.Fprintln(g.out, cfgStr) + fmt.Fprintln(g.out, "MANIFEST:") + fmt.Fprintln(g.out, res.Release.Manifest) return nil } // getValues implements 'helm get values' -func getValues(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errReleaseRequired - } - - res, err := helm.GetReleaseContent(args[0]) +func (g *getValuesCmd) run() error { + res, err := helm.GetReleaseContent(g.release) if err != nil { return prettyError(err) } // If the user wants all values, compute the values and return. - if allValues { + if g.allValues { cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil) if err != nil { return err @@ -140,24 +177,20 @@ func getValues(cmd *cobra.Command, args []string) error { if err != nil { return err } - fmt.Println(cfgStr) + fmt.Fprintln(g.out, cfgStr) return nil } - fmt.Println(res.Release.Config.Raw) + fmt.Fprintln(g.out, res.Release.Config.Raw) return nil } // getManifest implements 'helm get manifest' -func getManifest(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errReleaseRequired - } - - res, err := helm.GetReleaseContent(args[0]) +func (g *getManifestCmd) run() error { + res, err := helm.GetReleaseContent(g.release) if err != nil { return prettyError(err) } - fmt.Println(res.Release.Manifest) + fmt.Fprintln(g.out, res.Release.Manifest) return nil } diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 886be4ef0..e2a39c48b 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -86,6 +86,7 @@ func newRootCmd(out io.Writer) *cobra.Command { p.BoolVarP(&flagDebug, "debug", "", false, "enable verbose output") cmd.AddCommand(newListCmd(nil, out)) + cmd.AddCommand(newGetCmd(nil, out)) return cmd } From 7c01a28c5cda4f5587099ad3424787fad1fbc4b4 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 30 Jun 2016 15:02:12 -0700 Subject: [PATCH 10/14] test(cmd): add unit tests for get command --- cmd/helm/get.go | 108 +++--------------------------------- cmd/helm/get_manifest.go | 70 +++++++++++++++++++++++ cmd/helm/get_test.go | 49 ++++++++++++++++ cmd/helm/get_values.go | 84 ++++++++++++++++++++++++++++ cmd/helm/get_values_test.go | 49 ++++++++++++++++ cmd/helm/helm_test.go | 70 +++++++++++++++++++++++ cmd/helm/list_test.go | 48 ++-------------- 7 files changed, 334 insertions(+), 144 deletions(-) create mode 100644 cmd/helm/get_manifest.go create mode 100644 cmd/helm/get_test.go create mode 100644 cmd/helm/get_values.go create mode 100644 cmd/helm/get_values_test.go create mode 100644 cmd/helm/helm_test.go diff --git a/cmd/helm/get.go b/cmd/helm/get.go index ab3ef238e..6238773e1 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -42,18 +42,6 @@ By default, this prints a human readable collection of information about the chart, the supplied values, and the generated manifest file. ` -var getValuesHelp = ` -This command downloads a values file for a given release. -` - -var getManifestHelp = ` -This command fetches the generated manifest for a given release. - -A manifest is a YAML-encoded representation of the Kubernetes resources that -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. -` - var errReleaseRequired = errors.New("release name is required") type getCmd struct { @@ -68,74 +56,29 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { client: client, } cmd := &cobra.Command{ - Use: "get [flags] RELEASE_NAME", - Short: "download a named release", - Long: getHelp, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errReleaseRequired - } - get.release = args[0] - return get.run() - }, + Use: "get [flags] RELEASE_NAME", + Short: "download a named release", + Long: getHelp, PersistentPreRunE: setupConnection, - } - cmd.AddCommand(newGetValuesCmd(client, out)) - cmd.AddCommand(newGetManifestCmd(client, out)) - return cmd -} - -type getValuesCmd struct { - allValues bool - getCmd -} - -func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { - get := &getValuesCmd{} - get.out = out - get.client = client - cmd := &cobra.Command{ - Use: "values [flags] RELEASE_NAME", - Short: "download the values file for a named release", - Long: getValuesHelp, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errReleaseRequired } get.release = args[0] - return get.run() - }, - } - cmd.Flags().BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values") - return cmd -} - -type getManifestCmd struct { - getCmd -} - -func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { - get := &getManifestCmd{} - get.out = out - get.client = client - cmd := &cobra.Command{ - Use: "manifest [flags] RELEASE_NAME", - Short: "download the manifest for a named release", - Long: getManifestHelp, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errReleaseRequired + if get.client == nil { + get.client = helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) } - get.release = args[0] return get.run() }, } + cmd.AddCommand(newGetValuesCmd(client, out)) + cmd.AddCommand(newGetManifestCmd(client, out)) return cmd } // getCmd is the command that implements 'helm get' func (g *getCmd) run() error { - res, err := helm.GetReleaseContent(g.release) + res, err := g.client.ReleaseContent(g.release) if err != nil { return prettyError(err) } @@ -159,38 +102,3 @@ func (g *getCmd) run() error { fmt.Fprintln(g.out, res.Release.Manifest) return nil } - -// getValues implements 'helm get values' -func (g *getValuesCmd) run() error { - res, err := helm.GetReleaseContent(g.release) - if err != nil { - return prettyError(err) - } - - // If the user wants all values, compute the values and return. - if g.allValues { - cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil) - if err != nil { - return err - } - cfgStr, err := cfg.YAML() - if err != nil { - return err - } - fmt.Fprintln(g.out, cfgStr) - return nil - } - - fmt.Fprintln(g.out, res.Release.Config.Raw) - return nil -} - -// getManifest implements 'helm get manifest' -func (g *getManifestCmd) run() error { - res, err := helm.GetReleaseContent(g.release) - if err != nil { - return prettyError(err) - } - fmt.Fprintln(g.out, res.Release.Manifest) - return nil -} diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go new file mode 100644 index 000000000..21a76155c --- /dev/null +++ b/cmd/helm/get_manifest.go @@ -0,0 +1,70 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( + "fmt" + "io" + + "github.com/spf13/cobra" + + "k8s.io/helm/pkg/helm" +) + +var getManifestHelp = ` +This command fetches the generated manifest for a given release. + +A manifest is a YAML-encoded representation of the Kubernetes resources that +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 getManifestCmd struct { + release string + out io.Writer + client helm.Interface +} + +func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { + get := &getManifestCmd{ + out: out, + client: client, + } + cmd := &cobra.Command{ + Use: "manifest [flags] RELEASE_NAME", + Short: "download the manifest for a named release", + Long: getManifestHelp, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errReleaseRequired + } + get.release = args[0] + return get.run() + }, + } + return cmd +} + +// getManifest implements 'helm get manifest' +func (g *getManifestCmd) run() error { + res, err := helm.GetReleaseContent(g.release) + if err != nil { + return prettyError(err) + } + fmt.Fprintln(g.out, res.Release.Manifest) + return nil +} diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go new file mode 100644 index 000000000..3bfb56017 --- /dev/null +++ b/cmd/helm/get_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "bytes" + "testing" + + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" +) + +func TestGetCmd(t *testing.T) { + tests := []struct { + name string + args []string + client helm.Interface + expected string + err bool + }{ + { + name: "with a release", + client: &fakeReleaseClient{ + rels: []*release.Release{ + releaseMock("thomas-guide"), + }, + }, + args: []string{"thomas-guide"}, + expected: "CHART: foo-0.1.0-beta.1\nRELEASED: Fri Sep 2 15:04:05 1977\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nMANIFEST:", + }, + { + name: "requires release name arg", + client: &fakeReleaseClient{}, + err: true, + }, + } + + var buf bytes.Buffer + for _, tt := range tests { + cmd := newGetCmd(tt.client, &buf) + err := cmd.RunE(cmd, tt.args) + if (err != nil) != tt.err { + t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) + } + actual := string(bytes.TrimSpace(buf.Bytes())) + if actual != tt.expected { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + } + buf.Reset() + } +} diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go new file mode 100644 index 000000000..3765e762c --- /dev/null +++ b/cmd/helm/get_values.go @@ -0,0 +1,84 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( + "fmt" + "io" + + "github.com/spf13/cobra" + + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/helm" +) + +var getValuesHelp = ` +This command downloads a values file for a given release. +` + +type getValuesCmd struct { + release string + allValues bool + out io.Writer + client helm.Interface +} + +func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { + get := &getValuesCmd{ + out: out, + client: client, + } + cmd := &cobra.Command{ + Use: "values [flags] RELEASE_NAME", + Short: "download the values file for a named release", + Long: getValuesHelp, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errReleaseRequired + } + get.release = args[0] + return get.run() + }, + } + cmd.Flags().BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values") + return cmd +} + +// getValues implements 'helm get values' +func (g *getValuesCmd) run() error { + res, err := g.client.ReleaseContent(g.release) + if err != nil { + return prettyError(err) + } + + // If the user wants all values, compute the values and return. + if g.allValues { + cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config, nil) + if err != nil { + return err + } + cfgStr, err := cfg.YAML() + if err != nil { + return err + } + fmt.Fprintln(g.out, cfgStr) + return nil + } + + fmt.Fprintln(g.out, res.Release.Config.Raw) + return nil +} diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go new file mode 100644 index 000000000..5ccf9a5f6 --- /dev/null +++ b/cmd/helm/get_values_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "bytes" + "testing" + + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" +) + +func TestGetValuesCmd(t *testing.T) { + tests := []struct { + name string + args []string + client helm.Interface + expected string + err bool + }{ + { + name: "with a release", + client: &fakeReleaseClient{ + rels: []*release.Release{ + releaseMock("thomas-guide"), + }, + }, + args: []string{"thomas-guide"}, + expected: "name: \"value\"", + }, + { + name: "requires release name arg", + client: &fakeReleaseClient{}, + err: true, + }, + } + + var buf bytes.Buffer + for _, tt := range tests { + cmd := newGetValuesCmd(tt.client, &buf) + err := cmd.RunE(cmd, tt.args) + if (err != nil) != tt.err { + t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) + } + actual := string(bytes.TrimSpace(buf.Bytes())) + if actual != tt.expected { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + } + buf.Reset() + } +} diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go new file mode 100644 index 000000000..8cee5f0aa --- /dev/null +++ b/cmd/helm/helm_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "github.com/golang/protobuf/ptypes/timestamp" + + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +func releaseMock(name string) *release.Release { + date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} + return &release.Release{ + Name: name, + Info: &release.Info{ + FirstDeployed: &date, + LastDeployed: &date, + Status: &release.Status{Code: release.Status_DEPLOYED}, + }, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "foo", + Version: "0.1.0-beta.1", + }, + Templates: []*chart.Template{ + {Name: "foo.tpl", Data: []byte("Hello")}, + }, + }, + Config: &chart.Config{Raw: `name: "value"`}, + } +} + +type fakeReleaseClient struct { + rels []*release.Release + err error +} + +func (c *fakeReleaseClient) ListReleases(opts ...helm.ReleaseListOption) (*rls.ListReleasesResponse, error) { + resp := &rls.ListReleasesResponse{ + Count: int64(len(c.rels)), + Releases: c.rels, + } + return resp, c.err +} + +func (c *fakeReleaseClient) InstallRelease(chStr string, opts ...helm.InstallOption) (*rls.InstallReleaseResponse, error) { + return nil, nil +} + +func (c *fakeReleaseClient) DeleteRelease(rlsName string, opts ...helm.DeleteOption) (*rls.UninstallReleaseResponse, error) { + return nil, nil +} + +func (c *fakeReleaseClient) ReleaseStatus(rlsName string, opts ...helm.StatusOption) (*rls.GetReleaseStatusResponse, error) { + return nil, nil +} + +func (c *fakeReleaseClient) UpdateRelease(rlsName string, opts ...helm.UpdateOption) (*rls.UpdateReleaseResponse, error) { + return nil, nil +} + +func (c *fakeReleaseClient) ReleaseContent(rlsName string, opts ...helm.ContentOption) (resp *rls.GetReleaseContentResponse, err error) { + if len(c.rels) > 0 { + resp = &rls.GetReleaseContentResponse{ + Release: c.rels[0], + } + } + return resp, c.err +} diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index dace068f7..bc87039c5 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -4,50 +4,10 @@ import ( "bytes" "testing" - "github.com/golang/protobuf/ptypes/timestamp" - "k8s.io/helm/pkg/helm" - "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" - rls "k8s.io/helm/pkg/proto/hapi/services" ) -func releaseMock(name string) *release.Release { - date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} - return &release.Release{ - Name: name, - Info: &release.Info{ - FirstDeployed: &date, - LastDeployed: &date, - Status: &release.Status{Code: release.Status_DEPLOYED}, - }, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "foo", - Version: "0.1.0-beta.1", - }, - Templates: []*chart.Template{ - {Name: "foo.tpl", Data: []byte("Hello")}, - }, - }, - Config: &chart.Config{Raw: `name = "value"`}, - } -} - -type fakeReleaseLister struct { - helm.Interface - rels []*release.Release - err error -} - -func (fl *fakeReleaseLister) ListReleases(opts ...helm.ReleaseListOption) (*rls.ListReleasesResponse, error) { - resp := &rls.ListReleasesResponse{ - Count: int64(len(fl.rels)), - Releases: fl.rels, - } - return resp, fl.err -} - func TestListRun(t *testing.T) { tests := []struct { name string @@ -58,7 +18,7 @@ func TestListRun(t *testing.T) { { name: "with a release", listCmd: &listCmd{ - client: &fakeReleaseLister{ + client: &fakeReleaseClient{ rels: []*release.Release{ releaseMock("thomas-guide"), }, @@ -69,7 +29,7 @@ func TestListRun(t *testing.T) { { name: "list --long", listCmd: &listCmd{ - client: &fakeReleaseLister{ + client: &fakeReleaseClient{ rels: []*release.Release{ releaseMock("atlas"), }, @@ -106,7 +66,7 @@ func TestListCmd(t *testing.T) { }{ { name: "with a release", - client: &fakeReleaseLister{ + client: &fakeReleaseClient{ rels: []*release.Release{ releaseMock("thomas-guide"), }, @@ -116,7 +76,7 @@ func TestListCmd(t *testing.T) { { name: "list --long", flags: map[string]string{"long": "1"}, - client: &fakeReleaseLister{ + client: &fakeReleaseClient{ rels: []*release.Release{ releaseMock("atlas"), }, From 1ba822c7a4fba6ea1ca3c48f42ea44d7d5a5319e Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 30 Jun 2016 15:06:31 -0700 Subject: [PATCH 11/14] docs(cmd): add missing license headers --- cmd/helm/get_test.go | 16 ++++++++++++++++ cmd/helm/get_values_test.go | 16 ++++++++++++++++ cmd/helm/helm_test.go | 16 ++++++++++++++++ cmd/helm/list_test.go | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 3bfb56017..1b9b9a0a5 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index 5ccf9a5f6..e93d417f3 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 8cee5f0aa..1a0a8a338 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index bc87039c5..5aadc4ae3 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 ( From 2fb8b60765a454c0308c1e46560fbcba5e416d0a Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 30 Jun 2016 15:27:14 -0700 Subject: [PATCH 12/14] fix(cmd): load client inside subcommand --- cmd/helm/get.go | 4 ++-- cmd/helm/get_manifest.go | 5 ++++- cmd/helm/get_values.go | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 6238773e1..784bd6084 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -71,8 +71,8 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { return get.run() }, } - cmd.AddCommand(newGetValuesCmd(client, out)) - cmd.AddCommand(newGetManifestCmd(client, out)) + cmd.AddCommand(newGetValuesCmd(nil, out)) + cmd.AddCommand(newGetManifestCmd(nil, out)) return cmd } diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index 21a76155c..20f61443e 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -53,6 +53,9 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { return errReleaseRequired } get.release = args[0] + if get.client == nil { + get.client = helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) + } return get.run() }, } @@ -61,7 +64,7 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { // getManifest implements 'helm get manifest' func (g *getManifestCmd) run() error { - res, err := helm.GetReleaseContent(g.release) + res, err := g.client.ReleaseContent(g.release) if err != nil { return prettyError(err) } diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index 3765e762c..b62833db1 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -51,6 +51,9 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { return errReleaseRequired } get.release = args[0] + if get.client == nil { + get.client = helm.NewClient(helm.HelmHost(helm.Config.ServAddr)) + } return get.run() }, } From 4db6cd93bb5a88de1d79fd5a692553fc8e725352 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Jul 2016 11:17:20 -0700 Subject: [PATCH 13/14] fix(test): match output using regexp --- cmd/helm/get_test.go | 9 +++++---- cmd/helm/list_test.go | 17 +++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 1b9b9a0a5..7dce68eb1 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "regexp" "testing" "k8s.io/helm/pkg/helm" @@ -40,7 +41,7 @@ func TestGetCmd(t *testing.T) { }, }, args: []string{"thomas-guide"}, - expected: "CHART: foo-0.1.0-beta.1\nRELEASED: Fri Sep 2 15:04:05 1977\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nMANIFEST:", + expected: "CHART: foo-0.1.0-beta.1\nRELEASED: (.*)\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nMANIFEST:", }, { name: "requires release name arg", @@ -56,9 +57,9 @@ func TestGetCmd(t *testing.T) { if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } - actual := string(bytes.TrimSpace(buf.Bytes())) - if actual != tt.expected { - t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + re := regexp.MustCompile(tt.expected) + if !re.Match(buf.Bytes()) { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, buf.String()) } buf.Reset() } diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 5aadc4ae3..845641bc2 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "regexp" "testing" "k8s.io/helm/pkg/helm" @@ -52,7 +53,7 @@ func TestListRun(t *testing.T) { }, long: true, }, - expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\tFri Sep 2 15:04:05 1977\tDEPLOYED\tfoo-0.1.0-beta.1", + expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1", }, } @@ -63,9 +64,9 @@ func TestListRun(t *testing.T) { if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } - actual := string(bytes.TrimSpace(buf.Bytes())) - if actual != tt.expected { - t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + re := regexp.MustCompile(tt.expected) + if !re.Match(buf.Bytes()) { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, buf.String()) } buf.Reset() } @@ -97,7 +98,7 @@ func TestListCmd(t *testing.T) { releaseMock("atlas"), }, }, - expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\tFri Sep 2 15:04:05 1977\tDEPLOYED\tfoo-0.1.0-beta.1", + expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1", }, } @@ -111,9 +112,9 @@ func TestListCmd(t *testing.T) { if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } - actual := string(bytes.TrimSpace(buf.Bytes())) - if actual != tt.expected { - t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, actual) + re := regexp.MustCompile(tt.expected) + if !re.Match(buf.Bytes()) { + t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, buf.String()) } buf.Reset() } From 37cf2b9e7d470402fdb8c93245239b35059167c0 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Jul 2016 11:42:12 -0700 Subject: [PATCH 14/14] ref(tests): simplify creating fake client --- cmd/helm/get_test.go | 21 +++++++++------------ cmd/helm/get_values_test.go | 21 +++++++++------------ cmd/helm/list_test.go | 20 +++++++++----------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 7dce68eb1..0ee6ff356 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -21,7 +21,6 @@ import ( "regexp" "testing" - "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/release" ) @@ -29,30 +28,28 @@ func TestGetCmd(t *testing.T) { tests := []struct { name string args []string - client helm.Interface + resp *release.Release expected string err bool }{ { - name: "with a release", - client: &fakeReleaseClient{ - rels: []*release.Release{ - releaseMock("thomas-guide"), - }, - }, + name: "with a release", + resp: releaseMock("thomas-guide"), args: []string{"thomas-guide"}, expected: "CHART: foo-0.1.0-beta.1\nRELEASED: (.*)\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nMANIFEST:", }, { - name: "requires release name arg", - client: &fakeReleaseClient{}, - err: true, + name: "requires release name arg", + err: true, }, } var buf bytes.Buffer for _, tt := range tests { - cmd := newGetCmd(tt.client, &buf) + c := &fakeReleaseClient{ + rels: []*release.Release{tt.resp}, + } + cmd := newGetCmd(c, &buf) err := cmd.RunE(cmd, tt.args) if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index e93d417f3..4a28ccb24 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -20,7 +20,6 @@ import ( "bytes" "testing" - "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/release" ) @@ -28,30 +27,28 @@ func TestGetValuesCmd(t *testing.T) { tests := []struct { name string args []string - client helm.Interface + resp *release.Release expected string err bool }{ { - name: "with a release", - client: &fakeReleaseClient{ - rels: []*release.Release{ - releaseMock("thomas-guide"), - }, - }, + name: "with a release", + resp: releaseMock("thomas-guide"), args: []string{"thomas-guide"}, expected: "name: \"value\"", }, { - name: "requires release name arg", - client: &fakeReleaseClient{}, - err: true, + name: "requires release name arg", + err: true, }, } var buf bytes.Buffer for _, tt := range tests { - cmd := newGetValuesCmd(tt.client, &buf) + c := &fakeReleaseClient{ + rels: []*release.Release{tt.resp}, + } + cmd := newGetValuesCmd(c, &buf) err := cmd.RunE(cmd, tt.args) if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 845641bc2..c3bec76ec 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -21,7 +21,6 @@ import ( "regexp" "testing" - "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/release" ) @@ -77,26 +76,22 @@ func TestListCmd(t *testing.T) { name string args []string flags map[string]string - client helm.Interface + resp []*release.Release expected string err bool }{ { name: "with a release", - client: &fakeReleaseClient{ - rels: []*release.Release{ - releaseMock("thomas-guide"), - }, + resp: []*release.Release{ + releaseMock("thomas-guide"), }, expected: "thomas-guide", }, { name: "list --long", flags: map[string]string{"long": "1"}, - client: &fakeReleaseClient{ - rels: []*release.Release{ - releaseMock("atlas"), - }, + resp: []*release.Release{ + releaseMock("atlas"), }, expected: "NAME \tUPDATED \tSTATUS \tCHART \natlas\t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1", }, @@ -104,7 +99,10 @@ func TestListCmd(t *testing.T) { var buf bytes.Buffer for _, tt := range tests { - cmd := newListCmd(tt.client, &buf) + c := &fakeReleaseClient{ + rels: tt.resp, + } + cmd := newListCmd(c, &buf) for flag, value := range tt.flags { cmd.Flags().Set(flag, value) }