diff --git a/cmd/helm/deployment.go b/cmd/helm/deployment.go index 6ba9d236f..c4b7b7dee 100644 --- a/cmd/helm/deployment.go +++ b/cmd/helm/deployment.go @@ -17,7 +17,11 @@ limitations under the License. package main import ( + "errors" + "regexp" + "github.com/codegangsta/cli" + "github.com/kubernetes/helm/pkg/format" ) func init() { @@ -27,8 +31,9 @@ func init() { 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 +55,38 @@ 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 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..d1e44923a 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) } 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) }