diff --git a/cmd/helm.go b/cmd/helm.go index 36d08d642..8821160bf 100644 --- a/cmd/helm.go +++ b/cmd/helm.go @@ -4,6 +4,7 @@ import ( "os" "github.com/codegangsta/cli" + "github.com/deis/helm-dm/format" ) var version = "0.0.1" @@ -24,7 +25,18 @@ func commands() []cli.Command { Name: "install", Usage: "Initialize the client and install DM on Kubernetes.", Description: ``, - Action: func(c *cli.Context) { install() }, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "dry-run", + Usage: "Show what would be installed, but don't install anything.", + }, + }, + Action: func(c *cli.Context) { + if err := install(c.Bool("dry-run")); err != nil { + format.Error(err.Error()) + os.Exit(1) + } + }, }, { Name: "target", diff --git a/cmd/install.go b/cmd/install.go index 5abd44b2a..1d44be082 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -1,13 +1,25 @@ package main import ( + "errors" + "github.com/deis/helm-dm/dm" "github.com/deis/helm-dm/format" "github.com/deis/helm-dm/kubectl" ) -func install() error { - runner := &kubectl.PrintRunner{} +var ErrAlreadyInstalled error = errors.New("Already Installed") + +func install(dryRun bool) error { + var runner kubectl.Runner + if dryRun { + runner = &kubectl.PrintRunner{} + } else { + runner = &kubectl.RealRunner{} + if dm.IsInstalled(runner) { + return ErrAlreadyInstalled + } + } out, err := dm.Install(runner) if err != nil { format.Error("Error installing: %s %s", out, err) diff --git a/dm/install.go b/dm/install.go index 65899bb61..ad2aabb75 100644 --- a/dm/install.go +++ b/dm/install.go @@ -1,6 +1,7 @@ package dm import ( + "github.com/deis/helm-dm/format" "github.com/deis/helm-dm/kubectl" ) @@ -13,6 +14,18 @@ func Install(runner kubectl.Runner) (string, error) { return string(o), err } +// IsInstalled checks whether DM has been installed. +func IsInstalled(runner kubectl.Runner) bool { + // Basically, we test "all-or-nothing" here: if this returns without error + // we know that we have both the namespace and the manager API server. + out, err := runner.GetByKind("rc", "manager-rc", "dm") + if err != nil { + format.Error("Installation not found: %s %s", out, err) + return false + } + return true +} + // InstallYAML is the installation YAML for DM. const InstallYAML = ` ###################################################################### diff --git a/format/messages.go b/format/messages.go index 0eec683e1..b90d90851 100644 --- a/format/messages.go +++ b/format/messages.go @@ -8,12 +8,12 @@ import ( // This is all just placeholder. func Error(msg string, v ...interface{}) { - msg = "[ERROR]" + msg + "\n" + msg = "[ERROR] " + msg + "\n" fmt.Fprintf(os.Stderr, msg, v...) } func Info(msg string, v ...interface{}) { - msg = "[INFO]" + msg + "\n" + msg = "[INFO] " + msg + "\n" fmt.Fprintf(os.Stdout, msg, v...) } diff --git a/kubectl/get.go b/kubectl/get.go index bacef13d5..6bdc0e433 100644 --- a/kubectl/get.go +++ b/kubectl/get.go @@ -13,6 +13,17 @@ func (r RealRunner) Get(stdin []byte, ns string) ([]byte, error) { return cmd.CombinedOutput() } +func (r RealRunner) GetByKind(kind, name, ns string) (string, error) { + args := []string{"get", kind, name} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + cmd := command(args...) + o, err := cmd.CombinedOutput() + return string(o), err +} + // Get returns the commands to kubectl func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) { args := []string{"get", "-f", "-"} @@ -25,3 +36,13 @@ func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) { return []byte(cmd.String()), nil } + +func (r PrintRunner) GetByKind(kind, name, ns string) (string, error) { + args := []string{"get", kind, name} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + cmd := command(args...) + return cmd.String(), nil +} diff --git a/kubectl/kubectl.go b/kubectl/kubectl.go index 466944398..0e51169a4 100644 --- a/kubectl/kubectl.go +++ b/kubectl/kubectl.go @@ -13,6 +13,13 @@ type Runner interface { Delete(string, string, string) ([]byte, error) // Get returns Kubernetes resources Get([]byte, string) ([]byte, error) + + // GetByKind gets an entry by kind, name, and namespace. + // + // If name is omitted, all entries of that kind are returned. + // + // If NS is omitted, the default NS is assumed. + GetByKind(kind, name, ns string) (string, error) } // RealRunner implements Runner to execute kubectl commands