From 99d6fe134d9f82527502153a529b1c239fe34f6b Mon Sep 17 00:00:00 2001
From: David Gageot <david@gageot.net>
Date: Thu, 29 Oct 2015 11:50:51 +0100
Subject: [PATCH] Introduce CommandLine interface

Signed-off-by: David Gageot <david@gageot.net>
---
 commands/active.go          |  3 +--
 commands/commands.go        | 47 +++++++++++++++++++++++++++++++------
 commands/config.go          |  5 ++--
 commands/create.go          | 17 +++++++-------
 commands/env.go             |  3 +--
 commands/inspect.go         |  6 ++---
 commands/ip.go              |  4 +---
 commands/kill.go            |  4 +---
 commands/ls.go              |  3 +--
 commands/regeneratecerts.go |  3 +--
 commands/restart.go         |  4 +---
 commands/rm.go              |  5 ++--
 commands/scp.go             |  5 ++--
 commands/ssh.go             |  5 ++--
 commands/start.go           |  4 +---
 commands/status.go          |  3 +--
 commands/stop.go            |  4 +---
 commands/upgrade.go         |  4 +---
 commands/url.go             |  4 +---
 19 files changed, 70 insertions(+), 63 deletions(-)

diff --git a/commands/active.go b/commands/active.go
index 30544d069c..9d26b2fb70 100644
--- a/commands/active.go
+++ b/commands/active.go
@@ -6,7 +6,6 @@ import (
 	"os"
 	"strings"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/host"
 	"github.com/docker/machine/libmachine/persist"
 	"github.com/docker/machine/libmachine/state"
@@ -16,7 +15,7 @@ var (
 	errTooManyArguments = errors.New("Error: Too many arguments given")
 )
 
-func cmdActive(c *cli.Context) error {
+func cmdActive(c CommandLine) error {
 	if len(c.Args()) > 0 {
 		return errTooManyArguments
 	}
diff --git a/commands/commands.go b/commands/commands.go
index 1117177196..39f4c27e13 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -26,6 +26,39 @@ var (
 	ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument")
 )
 
+// CommandLine contains all the information passed to the commands on the command line.
+type CommandLine interface {
+	ShowHelp()
+
+	Application() *cli.App
+
+	Args() cli.Args
+
+	Bool(name string) bool
+
+	String(name string) string
+
+	StringSlice(name string) []string
+
+	GlobalString(name string) string
+
+	FlagNames() (names []string)
+
+	Generic(name string) interface{}
+}
+
+type contextCommandLine struct {
+	*cli.Context
+}
+
+func (c *contextCommandLine) ShowHelp() {
+	cli.ShowCommandHelp(c.Context, c.Command.Name)
+}
+
+func (c *contextCommandLine) Application() *cli.App {
+	return c.App
+}
+
 func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) {
 	d, err := rpcdriver.NewRpcClientDriver(rawContent, driverName)
 	if err != nil {
@@ -39,9 +72,9 @@ func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, erro
 	return d, nil
 }
 
-func fatalOnError(command func(context *cli.Context) error) func(context *cli.Context) {
+func fatalOnError(command func(commandLine CommandLine) error) func(context *cli.Context) {
 	return func(context *cli.Context) {
-		if err := command(context); err != nil {
+		if err := command(&contextCommandLine{context}); err != nil {
 			log.Fatal(err)
 		}
 	}
@@ -60,7 +93,7 @@ func confirmInput(msg string) (bool, error) {
 	return confirmed, nil
 }
 
-func getStore(c *cli.Context) persist.Store {
+func getStore(c CommandLine) persist.Store {
 	certInfo := getCertPathInfoFromContext(c)
 	return &persist.Filestore{
 		Path:             c.GlobalString("storage-path"),
@@ -115,7 +148,7 @@ func saveHost(store persist.Store, h *host.Host) error {
 	return nil
 }
 
-func getFirstArgHost(c *cli.Context) (*host.Host, error) {
+func getFirstArgHost(c CommandLine) (*host.Host, error) {
 	store := getStore(c)
 	hostName := c.Args().First()
 
@@ -139,7 +172,7 @@ func getFirstArgHost(c *cli.Context) (*host.Host, error) {
 	return h, nil
 }
 
-func getHostsFromContext(c *cli.Context) ([]*host.Host, error) {
+func getHostsFromContext(c CommandLine) ([]*host.Host, error) {
 	store := getStore(c)
 	hosts := []*host.Host{}
 
@@ -413,7 +446,7 @@ func consolidateErrs(errs []error) error {
 	return errors.New(strings.TrimSpace(finalErr))
 }
 
-func runActionWithContext(actionName string, c *cli.Context) error {
+func runActionWithContext(actionName string, c CommandLine) error {
 	store := getStore(c)
 
 	hosts, err := getHostsFromContext(c)
@@ -442,7 +475,7 @@ func runActionWithContext(actionName string, c *cli.Context) error {
 // codegangsta/cli will not set the cert paths if the storage-path is set to
 // something different so we cannot use the paths in the global options. le
 // sigh.
-func getCertPathInfoFromContext(c *cli.Context) cert.CertPathInfo {
+func getCertPathInfoFromContext(c CommandLine) cert.CertPathInfo {
 	caCertPath := c.GlobalString("tls-ca-cert")
 	caKeyPath := c.GlobalString("tls-ca-key")
 	clientCertPath := c.GlobalString("tls-client-cert")
diff --git a/commands/config.go b/commands/config.go
index 84a0abbc98..fe4e245bfe 100644
--- a/commands/config.go
+++ b/commands/config.go
@@ -6,7 +6,6 @@ import (
 	"os"
 	"strings"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/auth"
 	"github.com/docker/machine/libmachine/cert"
 	"github.com/docker/machine/libmachine/host"
@@ -27,7 +26,7 @@ Be advised that this will trigger a Docker daemon restart which will stop runnin
 `, e.hostUrl, e.wrappedErr)
 }
 
-func cmdConfig(c *cli.Context) error {
+func cmdConfig(c CommandLine) error {
 	// Ensure that log messages always go to stderr when this command is
 	// being run (it is intended to be run in a subshell)
 	log.SetOutWriter(os.Stderr)
@@ -54,7 +53,7 @@ func cmdConfig(c *cli.Context) error {
 	return nil
 }
 
-func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthOptions, error) {
+func runConnectionBoilerplate(h *host.Host, c CommandLine) (string, *auth.AuthOptions, error) {
 	hostState, err := h.Driver.GetState()
 	if err != nil {
 		// TODO: This is a common operation and should have a commonly
diff --git a/commands/create.go b/commands/create.go
index 75a571d96d..1560edc48d 100644
--- a/commands/create.go
+++ b/commands/create.go
@@ -118,7 +118,7 @@ var (
 	}
 )
 
-func cmdCreateInner(c *cli.Context) error {
+func cmdCreateInner(c CommandLine) error {
 	if len(c.Args()) > 1 {
 		return fmt.Errorf("Invalid command line. Found extra arguments %v", c.Args()[1:])
 	}
@@ -136,7 +136,7 @@ func cmdCreateInner(c *cli.Context) error {
 	}
 
 	if name == "" {
-		cli.ShowCommandHelp(c, "create")
+		c.ShowHelp()
 		return errNoMachineName
 	}
 
@@ -270,16 +270,15 @@ func flagHackLookup(flagName string) string {
 	return ""
 }
 
-func cmdCreateOuter(c *cli.Context) error {
+func cmdCreateOuter(c CommandLine) error {
 	const (
 		flagLookupMachineName = "flag-lookup"
 	)
-
 	driverName := flagHackLookup("--driver")
 
 	// We didn't recognize the driver name.
 	if driverName == "" {
-		cli.ShowCommandHelp(c, "create")
+		c.ShowHelp()
 		return nil // ?
 	}
 
@@ -314,8 +313,8 @@ func cmdCreateOuter(c *cli.Context) error {
 		return fmt.Errorf("Error trying to convert provided driver flags to cli flags: %s", err)
 	}
 
-	for i := range c.App.Commands {
-		cmd := &c.App.Commands[i]
+	for i := range c.Application().Commands {
+		cmd := &c.Application().Commands[i]
 		if cmd.HasName("create") {
 			cmd = addDriverFlagsToCommand(cliFlags, cmd)
 		}
@@ -327,10 +326,10 @@ func cmdCreateOuter(c *cli.Context) error {
 		}
 	}
 
-	return c.App.Run(os.Args)
+	return c.Application().Run(os.Args)
 }
 
-func getDriverOpts(c *cli.Context, mcnflags []mcnflag.Flag) drivers.DriverOptions {
+func getDriverOpts(c CommandLine, mcnflags []mcnflag.Flag) drivers.DriverOptions {
 	// TODO: This function is pretty damn YOLO and would benefit from some
 	// sanity checking around types and assertions.
 	//
diff --git a/commands/env.go b/commands/env.go
index d27c616c33..8dbc9fa849 100644
--- a/commands/env.go
+++ b/commands/env.go
@@ -8,7 +8,6 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/commands/mcndirs"
 	"github.com/docker/machine/libmachine/log"
 )
@@ -34,7 +33,7 @@ type ShellConfig struct {
 	NoProxyValue    string
 }
 
-func cmdEnv(c *cli.Context) error {
+func cmdEnv(c CommandLine) error {
 	// Ensure that log messages always go to stderr when this command is
 	// being run (it is intended to be run in a subshell)
 	log.SetOutWriter(os.Stderr)
diff --git a/commands/inspect.go b/commands/inspect.go
index d3b30bee37..e3d130ca05 100644
--- a/commands/inspect.go
+++ b/commands/inspect.go
@@ -5,8 +5,6 @@ import (
 	"fmt"
 	"os"
 	"text/template"
-
-	"github.com/docker/machine/cli"
 )
 
 var funcMap = template.FuncMap{
@@ -20,9 +18,9 @@ var funcMap = template.FuncMap{
 	},
 }
 
-func cmdInspect(c *cli.Context) error {
+func cmdInspect(c CommandLine) error {
 	if len(c.Args()) == 0 {
-		cli.ShowCommandHelp(c, "inspect")
+		c.ShowHelp()
 		return ErrExpectedOneMachine
 	}
 
diff --git a/commands/ip.go b/commands/ip.go
index f2e5d82890..ad1647e256 100644
--- a/commands/ip.go
+++ b/commands/ip.go
@@ -1,7 +1,5 @@
 package commands
 
-import "github.com/docker/machine/cli"
-
-func cmdIP(c *cli.Context) error {
+func cmdIP(c CommandLine) error {
 	return runActionWithContext("ip", c)
 }
diff --git a/commands/kill.go b/commands/kill.go
index 57c29041e2..2340338fbc 100644
--- a/commands/kill.go
+++ b/commands/kill.go
@@ -1,7 +1,5 @@
 package commands
 
-import "github.com/docker/machine/cli"
-
-func cmdKill(c *cli.Context) error {
+func cmdKill(c CommandLine) error {
 	return runActionWithContext("kill", c)
 }
diff --git a/commands/ls.go b/commands/ls.go
index 5da1e06992..349bb15975 100644
--- a/commands/ls.go
+++ b/commands/ls.go
@@ -10,7 +10,6 @@ import (
 	"text/tabwriter"
 	"time"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/drivers"
 	"github.com/docker/machine/libmachine/host"
 	"github.com/docker/machine/libmachine/log"
@@ -40,7 +39,7 @@ type HostListItem struct {
 	SwarmOptions *swarm.SwarmOptions
 }
 
-func cmdLs(c *cli.Context) error {
+func cmdLs(c CommandLine) error {
 	quiet := c.Bool("quiet")
 	filters, err := parseFilters(c.StringSlice("filter"))
 	if err != nil {
diff --git a/commands/regeneratecerts.go b/commands/regeneratecerts.go
index 4e55a93f62..ce7574a32e 100644
--- a/commands/regeneratecerts.go
+++ b/commands/regeneratecerts.go
@@ -1,11 +1,10 @@
 package commands
 
 import (
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/log"
 )
 
-func cmdRegenerateCerts(c *cli.Context) error {
+func cmdRegenerateCerts(c CommandLine) error {
 	if !c.Bool("force") {
 		ok, err := confirmInput("Regenerate TLS machine certs?  Warning: this is irreversible.")
 		if err != nil {
diff --git a/commands/restart.go b/commands/restart.go
index f1fbb2a2f2..18432ee03c 100644
--- a/commands/restart.go
+++ b/commands/restart.go
@@ -2,11 +2,9 @@ package commands
 
 import (
 	"github.com/docker/machine/libmachine/log"
-
-	"github.com/docker/machine/cli"
 )
 
-func cmdRestart(c *cli.Context) error {
+func cmdRestart(c CommandLine) error {
 	if err := runActionWithContext("restart", c); err != nil {
 		return err
 	}
diff --git a/commands/rm.go b/commands/rm.go
index 718f2fbf55..4ebd994126 100644
--- a/commands/rm.go
+++ b/commands/rm.go
@@ -4,13 +4,12 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/log"
 )
 
-func cmdRm(c *cli.Context) error {
+func cmdRm(c CommandLine) error {
 	if len(c.Args()) == 0 {
-		cli.ShowCommandHelp(c, "rm")
+		c.ShowHelp()
 		return errors.New("You must specify a machine name")
 	}
 
diff --git a/commands/scp.go b/commands/scp.go
index 78d44fdd2d..bb927477fb 100644
--- a/commands/scp.go
+++ b/commands/scp.go
@@ -7,7 +7,6 @@ import (
 	"os/exec"
 	"strings"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/log"
 	"github.com/docker/machine/libmachine/persist"
 )
@@ -53,10 +52,10 @@ func (s *storeHostInfoLoader) load(name string) (HostInfo, error) {
 	return host.Driver, nil
 }
 
-func cmdScp(c *cli.Context) error {
+func cmdScp(c CommandLine) error {
 	args := c.Args()
 	if len(args) != 2 {
-		cli.ShowCommandHelp(c, "scp")
+		c.ShowHelp()
 		return errWrongNumberArguments
 	}
 
diff --git a/commands/ssh.go b/commands/ssh.go
index 51c0ae47f1..30874d6043 100644
--- a/commands/ssh.go
+++ b/commands/ssh.go
@@ -3,15 +3,14 @@ package commands
 import (
 	"fmt"
 
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/state"
 )
 
-func cmdSSH(c *cli.Context) error {
+func cmdSSH(c CommandLine) error {
 	// Check for help flag -- Needed due to SkipFlagParsing
 	for _, arg := range c.Args() {
 		if arg == "-help" || arg == "--help" || arg == "-h" {
-			cli.ShowCommandHelp(c, "ssh")
+			c.ShowHelp()
 			return nil
 		}
 	}
diff --git a/commands/start.go b/commands/start.go
index 036707a970..348b0f8ce3 100644
--- a/commands/start.go
+++ b/commands/start.go
@@ -2,11 +2,9 @@ package commands
 
 import (
 	"github.com/docker/machine/libmachine/log"
-
-	"github.com/docker/machine/cli"
 )
 
-func cmdStart(c *cli.Context) error {
+func cmdStart(c CommandLine) error {
 	if err := runActionWithContext("start", c); err != nil {
 		return err
 	}
diff --git a/commands/status.go b/commands/status.go
index d87722062e..d660ac5382 100644
--- a/commands/status.go
+++ b/commands/status.go
@@ -1,11 +1,10 @@
 package commands
 
 import (
-	"github.com/docker/machine/cli"
 	"github.com/docker/machine/libmachine/log"
 )
 
-func cmdStatus(c *cli.Context) error {
+func cmdStatus(c CommandLine) error {
 	if len(c.Args()) != 1 {
 		return ErrExpectedOneMachine
 	}
diff --git a/commands/stop.go b/commands/stop.go
index 69920e5400..2a926945df 100644
--- a/commands/stop.go
+++ b/commands/stop.go
@@ -1,7 +1,5 @@
 package commands
 
-import "github.com/docker/machine/cli"
-
-func cmdStop(c *cli.Context) error {
+func cmdStop(c CommandLine) error {
 	return runActionWithContext("stop", c)
 }
diff --git a/commands/upgrade.go b/commands/upgrade.go
index 89327357d0..31e8469c03 100644
--- a/commands/upgrade.go
+++ b/commands/upgrade.go
@@ -1,7 +1,5 @@
 package commands
 
-import "github.com/docker/machine/cli"
-
-func cmdUpgrade(c *cli.Context) error {
+func cmdUpgrade(c CommandLine) error {
 	return runActionWithContext("upgrade", c)
 }
diff --git a/commands/url.go b/commands/url.go
index dc8704e441..3ec861f3cc 100644
--- a/commands/url.go
+++ b/commands/url.go
@@ -2,11 +2,9 @@ package commands
 
 import (
 	"fmt"
-
-	"github.com/docker/machine/cli"
 )
 
-func cmdURL(c *cli.Context) error {
+func cmdURL(c CommandLine) error {
 	if len(c.Args()) != 1 {
 		return ErrExpectedOneMachine
 	}