From 68f253472f5e35c77f21cad01c28105a1a48c5f3 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 21 Mar 2016 16:47:33 -0600 Subject: [PATCH 1/2] feat(cli): implement helm deployment list Closes #243 --- cmd/helm/deployment.go | 49 ++++++++++++++++++- cmd/helm/helm.go | 16 ++++-- pkg/format/messages.go | 48 +++++++++++++++--- .../list.go => pkg/format/messages_test.go | 32 +++++------- 4 files changed, 115 insertions(+), 30 deletions(-) rename cmd/helm/list.go => pkg/format/messages_test.go (58%) diff --git a/cmd/helm/deployment.go b/cmd/helm/deployment.go index 6ba9d236f..fca91e1fe 100644 --- a/cmd/helm/deployment.go +++ b/cmd/helm/deployment.go @@ -17,18 +17,24 @@ limitations under the License. package main import ( + "errors" + "regexp" + "github.com/codegangsta/cli" + "github.com/kubernetes/helm/pkg/format" ) func init() { + //addCommands(listCmd()) addCommands(deploymentCommands()) } func deploymentCommands() cli.Command { return cli.Command{ // Names following form prescribed here: http://is.gd/QUSEOF - Name: "deployment", - Usage: "Perform deployment-centered operations.", + Name: "deployment", + Aliases: []string{"dep"}, + Usage: "Perform deployment-centered operations.", Subcommands: []cli.Command{ { Name: "config", @@ -50,7 +56,46 @@ func deploymentCommands() cli.Command { Name: "list", Usage: "list all deployments, or filter by an optional pattern", ArgsUsage: "PATTERN", + Action: func(c *cli.Context) { run(c, list) }, }, }, } } + +func listCmd() cli.Command { + return cli.Command{ + Name: "list", + Usage: "Lists the deployments in the cluster", + Action: func(c *cli.Context) { run(c, list) }, + } +} + +func list(c *cli.Context) error { + list, err := NewClient(c).ListDeployments() + if err != nil { + return err + } + args := c.Args() + if len(args) >= 1 { + pattern := args[0] + r, err := regexp.Compile(pattern) + if err != nil { + return err + } + + newlist := []string{} + for _, i := range list { + if r.MatchString(i) { + newlist = append(newlist, i) + } + } + list = newlist + } + + if len(list) == 0 { + return errors.New("no deployments found") + } + + format.List(list) + return nil +} diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index c7a02585d..cb41d1d2e 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -21,6 +21,7 @@ import ( "github.com/codegangsta/cli" "github.com/kubernetes/helm/pkg/client" + "github.com/kubernetes/helm/pkg/format" "github.com/kubernetes/helm/pkg/version" ) @@ -30,6 +31,12 @@ func init() { addCommands(cmds()...) } +// Debug indicates whether the process is in debug mode. +// +// This is set at app start-up time, based on the presence of the --debug +// flag. +var Debug bool + func main() { app := cli.NewApp() app.Name = "helm" @@ -55,6 +62,10 @@ func main() { Usage: "Enable verbose debugging output", }, } + app.Before = func(c *cli.Context) error { + Debug = c.GlobalBool("debug") + return nil + } app.Run(os.Args) } @@ -72,7 +83,7 @@ func addCommands(cmds ...cli.Command) { func run(c *cli.Context, f func(c *cli.Context) error) { if err := f(c); err != nil { - os.Stderr.Write([]byte(err.Error())) + format.Err(err) os.Exit(1) } } @@ -80,7 +91,6 @@ func run(c *cli.Context, f func(c *cli.Context) error) { // NewClient creates a new client instance preconfigured for CLI usage. func NewClient(c *cli.Context) *client.Client { host := c.GlobalString("host") - debug := c.GlobalBool("debug") timeout := c.GlobalInt("timeout") - return client.NewClient(host).SetDebug(debug).SetTimeout(timeout) + return client.NewClient(host).SetDebug(Debug).SetTimeout(timeout) } diff --git a/pkg/format/messages.go b/pkg/format/messages.go index 066862bcb..858b93394 100644 --- a/pkg/format/messages.go +++ b/pkg/format/messages.go @@ -17,41 +17,77 @@ limitations under the License. package format import ( + "bytes" "fmt" + "io" "os" + "reflect" + "sort" "github.com/ghodss/yaml" ) +// Stdout is the output this library will write to. +var Stdout io.Writer = os.Stdout + +// Stderr is the error output this library will write to. +var Stderr io.Writer = os.Stderr + // This is all just placeholder. // Err prints an error message to Stderr. -func Err(msg string, v ...interface{}) { +func Err(message interface{}, v ...interface{}) { + var msg string + val := reflect.Indirect(reflect.ValueOf(message)) + if val.Kind() == reflect.String { + msg = message.(string) + } else if z, ok := message.(fmt.Stringer); ok { + msg = z.String() + } else if z, ok := message.(error); ok { + msg = z.Error() + } + msg = "[ERROR] " + msg + "\n" - fmt.Fprintf(os.Stderr, msg, v...) + fmt.Fprintf(Stderr, msg, v...) } // Info prints an informational message to Stdout. func Info(msg string, v ...interface{}) { msg = "[INFO] " + msg + "\n" - fmt.Fprintf(os.Stdout, msg, v...) + fmt.Fprintf(Stdout, msg, v...) } // Msg prints a raw message to Stdout. func Msg(msg string, v ...interface{}) { - fmt.Fprintf(os.Stdout, msg, v...) + fmt.Fprintf(Stdout, msg, v...) } // Success is an achievement marked by pretty output. func Success(msg string, v ...interface{}) { msg = "[Success] " + msg + "\n" - fmt.Fprintf(os.Stdout, msg, v...) + fmt.Fprintf(Stdout, msg, v...) } // Warning emits a warning message. func Warning(msg string, v ...interface{}) { msg = "[Warning] " + msg + "\n" - fmt.Fprintf(os.Stdout, msg, v...) + fmt.Fprintf(Stdout, msg, v...) +} + +// List prints a list of strings to Stdout. +// +// This sorts lexicographically. +func List(list []string) { + sort.Strings(list) + // Buffer and then flush all at once to avoid concurrency-based interleaving. + var b bytes.Buffer + for _, v := range list { + if v == "" { + v = "[empty]" + } + fmt.Fprintf(&b, "%s\n", v) + } + Stdout.Write(b.Bytes()) } // YAML prints an object in YAML format. diff --git a/cmd/helm/list.go b/pkg/format/messages_test.go similarity index 58% rename from cmd/helm/list.go rename to pkg/format/messages_test.go index 9d418ff21..b08085881 100644 --- a/cmd/helm/list.go +++ b/pkg/format/messages_test.go @@ -14,29 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package format import ( - "github.com/codegangsta/cli" - "github.com/kubernetes/helm/pkg/format" + "bytes" + "os" + "testing" ) -func init() { - addCommands(listCmd()) -} - -func listCmd() cli.Command { - return cli.Command{ - Name: "list", - Usage: "Lists the deployments in the cluster", - Action: func(c *cli.Context) { run(c, list) }, - } -} +func TestList(t *testing.T) { + var b bytes.Buffer + in := []string{"ddd", "ccc", "aaa", "bbb"} + expect := "aaa\nbbb\nccc\nddd\n" + Stdout = &b + defer func() { Stdout = os.Stdout }() -func list(c *cli.Context) error { - list, err := NewClient(c).ListDeployments() - if err != nil { - return err + List(in) + if b.String() != expect { + t.Errorf("Expected %q, got %q", expect, b.String()) } - return format.YAML(list) } From 9a92f99dce2a68ff279230246d67dbd76756c6d8 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 21 Mar 2016 17:09:09 -0600 Subject: [PATCH 2/2] fix(cli): remove 'helm list' Also, make the debug bool locally scoped. --- cmd/helm/deployment.go | 9 --------- cmd/helm/helm.go | 8 ++++---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/cmd/helm/deployment.go b/cmd/helm/deployment.go index fca91e1fe..c4b7b7dee 100644 --- a/cmd/helm/deployment.go +++ b/cmd/helm/deployment.go @@ -25,7 +25,6 @@ import ( ) func init() { - //addCommands(listCmd()) addCommands(deploymentCommands()) } @@ -62,14 +61,6 @@ func deploymentCommands() cli.Command { } } -func listCmd() cli.Command { - return cli.Command{ - Name: "list", - Usage: "Lists the deployments in the cluster", - Action: func(c *cli.Context) { run(c, list) }, - } -} - func list(c *cli.Context) error { list, err := NewClient(c).ListDeployments() if err != nil { diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index cb41d1d2e..d1e44923a 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -31,11 +31,11 @@ func init() { addCommands(cmds()...) } -// Debug indicates whether the process is in debug mode. +// debug indicates whether the process is in debug mode. // // This is set at app start-up time, based on the presence of the --debug // flag. -var Debug bool +var debug bool func main() { app := cli.NewApp() @@ -63,7 +63,7 @@ func main() { }, } app.Before = func(c *cli.Context) error { - Debug = c.GlobalBool("debug") + debug = c.GlobalBool("debug") return nil } app.Run(os.Args) @@ -92,5 +92,5 @@ func run(c *cli.Context, f func(c *cli.Context) error) { func NewClient(c *cli.Context) *client.Client { host := c.GlobalString("host") timeout := c.GlobalInt("timeout") - return client.NewClient(host).SetDebug(Debug).SetTimeout(timeout) + return client.NewClient(host).SetDebug(debug).SetTimeout(timeout) }