Merge pull request #2068 from dgageot/cleanup-for-more-tests

Make commands code easier to test
This commit is contained in:
Nathan LeClaire 2015-10-29 12:30:15 -07:00
commit fe94b4c44f
24 changed files with 224 additions and 178 deletions

View File

@ -12,20 +12,27 @@ import (
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
) )
func cmdActive(c *cli.Context) { var (
errTooManyArguments = errors.New("Error: Too many arguments given")
)
func cmdActive(c *cli.Context) error {
if len(c.Args()) > 0 { if len(c.Args()) > 0 {
fatal("Error: Too many arguments given.") return errTooManyArguments
} }
store := getStore(c) store := getStore(c)
host, err := getActiveHost(store) host, err := getActiveHost(store)
if err != nil { if err != nil {
fatalf("Error getting active host: %s", err) return fmt.Errorf("Error getting active host: %s", err)
} }
if host != nil { if host != nil {
fmt.Println(host.Name) fmt.Println(host.Name)
} }
return nil
} }
func getActiveHost(store persist.Store) (*host.Host, error) { func getActiveHost(store persist.Store) (*host.Host, error) {

View File

@ -19,8 +19,8 @@ import (
var ( var (
ErrUnknownShell = errors.New("Error: Unknown shell") ErrUnknownShell = errors.New("Error: Unknown shell")
ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments.") ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments")
ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument.") ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument")
) )
func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClientDriver, error) { func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClientDriver, error) {
@ -32,29 +32,25 @@ func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClient
return d, nil return d, nil
} }
func fatal(args ...interface{}) { func fatalOnError(command func(context *cli.Context) error) func(context *cli.Context) {
log.Fatal(args...) return func(context *cli.Context) {
if err := command(context); err != nil {
log.Fatal(err)
}
}
} }
func fatalf(fmtString string, args ...interface{}) { func confirmInput(msg string) (bool, error) {
log.Fatalf(fmtString, args...)
}
func confirmInput(msg string) bool {
fmt.Printf("%s (y/n): ", msg) fmt.Printf("%s (y/n): ", msg)
var resp string var resp string
_, err := fmt.Scanln(&resp) _, err := fmt.Scanln(&resp)
if err != nil { if err != nil {
fatal(err) return false, err
} }
if strings.Index(strings.ToLower(resp), "y") == 0 { confirmed := strings.Index(strings.ToLower(resp), "y") == 0
return true return confirmed, nil
}
return false
} }
func getStore(c *cli.Context) persist.Store { func getStore(c *cli.Context) persist.Store {
@ -112,17 +108,16 @@ func saveHost(store persist.Store, h *host.Host) error {
return nil return nil
} }
func getFirstArgHost(c *cli.Context) *host.Host { func getFirstArgHost(c *cli.Context) (*host.Host, error) {
store := getStore(c) store := getStore(c)
hostName := c.Args().First() hostName := c.Args().First()
exists, err := store.Exists(hostName) exists, err := store.Exists(hostName)
if err != nil { if err != nil {
fatalf("Error checking if host %q exists: %s", hostName, err) return nil, fmt.Errorf("Error checking if host %q exists: %s", hostName, err)
} }
if !exists { if !exists {
fatalf("Host %q does not exist", hostName) return nil, fmt.Errorf("Host %q does not exist", hostName)
} }
h, err := loadHost(store, hostName) h, err := loadHost(store, hostName)
@ -131,9 +126,10 @@ func getFirstArgHost(c *cli.Context) *host.Host {
// the host reliably we're definitely not going to be able to // the host reliably we're definitely not going to be able to
// do anything else interesting, but also this premature exit // do anything else interesting, but also this premature exit
// feels wrong to me. Let's revisit it later. // feels wrong to me. Let's revisit it later.
fatalf("Error trying to get host %q: %s", hostName, err) return nil, fmt.Errorf("Error trying to get host %q: %s", hostName, err)
} }
return h
return h, nil
} }
func getHostsFromContext(c *cli.Context) ([]*host.Host, error) { func getHostsFromContext(c *cli.Context) ([]*host.Host, error) {
@ -155,13 +151,13 @@ var Commands = []cli.Command{
{ {
Name: "active", Name: "active",
Usage: "Print which machine is active", Usage: "Print which machine is active",
Action: cmdActive, Action: fatalOnError(cmdActive),
}, },
{ {
Name: "config", Name: "config",
Usage: "Print the connection config for machine", Usage: "Print the connection config for machine",
Description: "Argument is a machine name.", Description: "Argument is a machine name.",
Action: cmdConfig, Action: fatalOnError(cmdConfig),
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "swarm", Name: "swarm",
@ -173,14 +169,14 @@ var Commands = []cli.Command{
Flags: sharedCreateFlags, Flags: sharedCreateFlags,
Name: "create", Name: "create",
Usage: "Create a machine.\n\nSpecify a driver with --driver to include the create flags for that driver in the help text.", Usage: "Create a machine.\n\nSpecify a driver with --driver to include the create flags for that driver in the help text.",
Action: cmdCreateOuter, Action: fatalOnError(cmdCreateOuter),
SkipFlagParsing: true, SkipFlagParsing: true,
}, },
{ {
Name: "env", Name: "env",
Usage: "Display the commands to set up the environment for the Docker client", Usage: "Display the commands to set up the environment for the Docker client",
Description: "Argument is a machine name.", Description: "Argument is a machine name.",
Action: cmdEnv, Action: fatalOnError(cmdEnv),
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "swarm", Name: "swarm",
@ -204,7 +200,7 @@ var Commands = []cli.Command{
Name: "inspect", Name: "inspect",
Usage: "Inspect information about a machine", Usage: "Inspect information about a machine",
Description: "Argument is a machine name.", Description: "Argument is a machine name.",
Action: cmdInspect, Action: fatalOnError(cmdInspect),
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "format, f", Name: "format, f",
@ -217,13 +213,13 @@ var Commands = []cli.Command{
Name: "ip", Name: "ip",
Usage: "Get the IP address of a machine", Usage: "Get the IP address of a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdIp, Action: fatalOnError(cmdIp),
}, },
{ {
Name: "kill", Name: "kill",
Usage: "Kill a machine", Usage: "Kill a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdKill, Action: fatalOnError(cmdKill),
}, },
{ {
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -239,13 +235,13 @@ var Commands = []cli.Command{
}, },
Name: "ls", Name: "ls",
Usage: "List machines", Usage: "List machines",
Action: cmdLs, Action: fatalOnError(cmdLs),
}, },
{ {
Name: "regenerate-certs", Name: "regenerate-certs",
Usage: "Regenerate TLS Certificates for a machine", Usage: "Regenerate TLS Certificates for a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdRegenerateCerts, Action: fatalOnError(cmdRegenerateCerts),
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "force, f", Name: "force, f",
@ -257,7 +253,7 @@ var Commands = []cli.Command{
Name: "restart", Name: "restart",
Usage: "Restart a machine", Usage: "Restart a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdRestart, Action: fatalOnError(cmdRestart),
}, },
{ {
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -269,20 +265,20 @@ var Commands = []cli.Command{
Name: "rm", Name: "rm",
Usage: "Remove a machine", Usage: "Remove a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdRm, Action: fatalOnError(cmdRm),
}, },
{ {
Name: "ssh", Name: "ssh",
Usage: "Log into or run a command on a machine with SSH.", Usage: "Log into or run a command on a machine with SSH.",
Description: "Arguments are [machine-name] [command]", Description: "Arguments are [machine-name] [command]",
Action: cmdSsh, Action: fatalOnError(cmdSsh),
SkipFlagParsing: true, SkipFlagParsing: true,
}, },
{ {
Name: "scp", Name: "scp",
Usage: "Copy files between machines", Usage: "Copy files between machines",
Description: "Arguments are [machine:][path] [machine:][path].", Description: "Arguments are [machine:][path] [machine:][path].",
Action: cmdScp, Action: fatalOnError(cmdScp),
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "recursive, r", Name: "recursive, r",
@ -294,31 +290,31 @@ var Commands = []cli.Command{
Name: "start", Name: "start",
Usage: "Start a machine", Usage: "Start a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdStart, Action: fatalOnError(cmdStart),
}, },
{ {
Name: "status", Name: "status",
Usage: "Get the status of a machine", Usage: "Get the status of a machine",
Description: "Argument is a machine name.", Description: "Argument is a machine name.",
Action: cmdStatus, Action: fatalOnError(cmdStatus),
}, },
{ {
Name: "stop", Name: "stop",
Usage: "Stop a machine", Usage: "Stop a machine",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdStop, Action: fatalOnError(cmdStop),
}, },
{ {
Name: "upgrade", Name: "upgrade",
Usage: "Upgrade a machine to the latest version of Docker", Usage: "Upgrade a machine to the latest version of Docker",
Description: "Argument(s) are one or more machine names.", Description: "Argument(s) are one or more machine names.",
Action: cmdUpgrade, Action: fatalOnError(cmdUpgrade),
}, },
{ {
Name: "url", Name: "url",
Usage: "Get the URL of a machine", Usage: "Get the URL of a machine",
Description: "Argument is a machine name.", Description: "Argument is a machine name.",
Action: cmdUrl, Action: fatalOnError(cmdUrl),
}, },
} }

View File

@ -14,26 +14,31 @@ import (
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
) )
func cmdConfig(c *cli.Context) { func cmdConfig(c *cli.Context) error {
// Ensure that log messages always go to stderr when this command is // Ensure that log messages always go to stderr when this command is
// being run (it is intended to be run in a subshell) // being run (it is intended to be run in a subshell)
log.SetOutWriter(os.Stderr) log.SetOutWriter(os.Stderr)
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
fatal(ErrExpectedOneMachine) return ErrExpectedOneMachine
} }
h := getFirstArgHost(c) host, err := getFirstArgHost(c)
dockerHost, authOptions, err := runConnectionBoilerplate(h, c)
if err != nil { if err != nil {
fatalf("Error running connection boilerplate: %s", err) return err
}
dockerHost, authOptions, err := runConnectionBoilerplate(host, c)
if err != nil {
return fmt.Errorf("Error running connection boilerplate: %s", err)
} }
log.Debug(dockerHost) log.Debug(dockerHost)
fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s", fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s",
authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost) authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost)
return nil
} }
func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthOptions, error) { func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthOptions, error) {
@ -44,7 +49,7 @@ func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthO
return "", &auth.AuthOptions{}, fmt.Errorf("Error trying to get host state: %s", err) return "", &auth.AuthOptions{}, fmt.Errorf("Error trying to get host state: %s", err)
} }
if hostState != state.Running { if hostState != state.Running {
return "", &auth.AuthOptions{}, fmt.Errorf("%s is not running. Please start it in order to use the connection settings.", h.Name) return "", &auth.AuthOptions{}, fmt.Errorf("%s is not running. Please start it in order to use the connection settings", h.Name)
} }
dockerHost, err := h.Driver.GetURL() dockerHost, err := h.Driver.GetURL()
@ -101,7 +106,7 @@ func parseSwarm(hostUrl string, h *host.Host) (string, error) {
swarmOptions := h.HostOptions.SwarmOptions swarmOptions := h.HostOptions.SwarmOptions
if !swarmOptions.Master { if !swarmOptions.Master {
return "", fmt.Errorf("Error: %s is not a swarm master. The --swarm flag is intended for use with swarm masters.", h.Name) return "", fmt.Errorf("Error: %s is not a swarm master. The --swarm flag is intended for use with swarm masters", h.Name)
} }
u, err := url.Parse(swarmOptions.Host) u, err := url.Parse(swarmOptions.Host)

View File

@ -10,6 +10,8 @@ import (
"sort" "sort"
"strings" "strings"
"errors"
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
"github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine"
@ -25,6 +27,10 @@ import (
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/swarm"
) )
var (
errNoMachineName = errors.New("Error: No machine name specified")
)
var ( var (
sharedCreateFlags = []cli.Flag{ sharedCreateFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
@ -111,9 +117,9 @@ var (
} }
) )
func cmdCreateInner(c *cli.Context) { func cmdCreateInner(c *cli.Context) error {
if len(c.Args()) > 1 { if len(c.Args()) > 1 {
fatalf("Invalid command line. Found extra arguments %v", c.Args()[1:]) return fmt.Errorf("Invalid command line. Found extra arguments %v", c.Args()[1:])
} }
name := c.Args().First() name := c.Args().First()
@ -130,16 +136,16 @@ func cmdCreateInner(c *cli.Context) {
if name == "" { if name == "" {
cli.ShowCommandHelp(c, "create") cli.ShowCommandHelp(c, "create")
fatal("Error: No machine name specified.") return errNoMachineName
} }
validName := host.ValidateHostName(name) validName := host.ValidateHostName(name)
if !validName { if !validName {
fatal("Error creating machine: ", mcnerror.ErrInvalidHostname) return fmt.Errorf("Error creating machine: %s", mcnerror.ErrInvalidHostname)
} }
if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil { if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil {
fatalf("Error parsing swarm discovery: %s", err) return fmt.Errorf("Error parsing swarm discovery: %s", err)
} }
// TODO: Fix hacky JSON solution // TODO: Fix hacky JSON solution
@ -148,17 +154,17 @@ func cmdCreateInner(c *cli.Context) {
StorePath: c.GlobalString("storage-path"), StorePath: c.GlobalString("storage-path"),
}) })
if err != nil { if err != nil {
fatalf("Error attempting to marshal bare driver data: %s", err) return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
} }
driver, err := newPluginDriver(driverName, bareDriverData) driver, err := newPluginDriver(driverName, bareDriverData)
if err != nil { if err != nil {
fatalf("Error loading driver %q: %s", driverName, err) return fmt.Errorf("Error loading driver %q: %s", driverName, err)
} }
h, err := store.NewHost(driver) h, err := store.NewHost(driver)
if err != nil { if err != nil {
fatalf("Error getting new host: %s", err) return fmt.Errorf("Error getting new host: %s", err)
} }
h.HostOptions = &host.HostOptions{ h.HostOptions = &host.HostOptions{
@ -196,12 +202,12 @@ func cmdCreateInner(c *cli.Context) {
exists, err := store.Exists(h.Name) exists, err := store.Exists(h.Name)
if err != nil { if err != nil {
fatalf("Error checking if host exists: %s", err) return fmt.Errorf("Error checking if host exists: %s", err)
} }
if exists { if exists {
fatal(mcnerror.ErrHostAlreadyExists{ return mcnerror.ErrHostAlreadyExists{
Name: h.Name, Name: h.Name,
}) }
} }
// driverOpts is the actual data we send over the wire to set the // driverOpts is the actual data we send over the wire to set the
@ -211,18 +217,20 @@ func cmdCreateInner(c *cli.Context) {
driverOpts := getDriverOpts(c, mcnFlags) driverOpts := getDriverOpts(c, mcnFlags)
if err := h.Driver.SetConfigFromFlags(driverOpts); err != nil { if err := h.Driver.SetConfigFromFlags(driverOpts); err != nil {
fatalf("Error setting machine configuration from flags provided: %s", err) return fmt.Errorf("Error setting machine configuration from flags provided: %s", err)
} }
if err := libmachine.Create(store, h); err != nil { if err := libmachine.Create(store, h); err != nil {
fatalf("Error creating machine: %s", err) return fmt.Errorf("Error creating machine: %s", err)
} }
if err := saveHost(store, h); err != nil { if err := saveHost(store, h); err != nil {
fatalf("Error attempting to save store: %s", err) return fmt.Errorf("Error attempting to save store: %s", err)
} }
log.Infof("To see how to connect Docker to this machine, run: %s", fmt.Sprintf("%s env %s", os.Args[0], name)) log.Infof("To see how to connect Docker to this machine, run: %s", fmt.Sprintf("%s env %s", os.Args[0], name))
return nil
} }
// The following function is needed because the CLI acrobatics that we're doing // The following function is needed because the CLI acrobatics that we're doing
@ -261,13 +269,13 @@ func flagHackLookup(flagName string) string {
return "" return ""
} }
func cmdCreateOuter(c *cli.Context) { func cmdCreateOuter(c *cli.Context) error {
driverName := flagHackLookup("--driver") driverName := flagHackLookup("--driver")
// We didn't recognize the driver name. // We didn't recognize the driver name.
if driverName == "" { if driverName == "" {
cli.ShowCommandHelp(c, "create") cli.ShowCommandHelp(c, "create")
return return nil // ?
} }
name := c.Args().First() name := c.Args().First()
@ -277,12 +285,12 @@ func cmdCreateOuter(c *cli.Context) {
MachineName: name, MachineName: name,
}) })
if err != nil { if err != nil {
fatalf("Error attempting to marshal bare driver data: %s", err) return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
} }
driver, err := newPluginDriver(driverName, bareDriverData) driver, err := newPluginDriver(driverName, bareDriverData)
if err != nil { if err != nil {
fatalf("Error loading driver %q: %s", driverName, err) return fmt.Errorf("Error loading driver %q: %s", driverName, err)
} }
// TODO: So much flag manipulation and voodoo here, it seems to be // TODO: So much flag manipulation and voodoo here, it seems to be
@ -296,8 +304,9 @@ func cmdCreateOuter(c *cli.Context) {
// on the requested driver. // on the requested driver.
cliFlags, err := convertMcnFlagsToCliFlags(mcnFlags) cliFlags, err := convertMcnFlagsToCliFlags(mcnFlags)
if err != nil { if err != nil {
fatalf("Error trying to convert provided driver flags to cli flags: %s", err) return fmt.Errorf("Error trying to convert provided driver flags to cli flags: %s", err)
} }
for i := range c.App.Commands { for i := range c.App.Commands {
cmd := &c.App.Commands[i] cmd := &c.App.Commands[i]
if cmd.HasName("create") { if cmd.HasName("create") {
@ -306,12 +315,10 @@ func cmdCreateOuter(c *cli.Context) {
} }
if err := driver.Close(); err != nil { if err := driver.Close(); err != nil {
fatal(err) return err
} }
if err := c.App.Run(os.Args); err != nil { return c.App.Run(os.Args)
fatal(err)
}
} }
func getDriverOpts(c *cli.Context, mcnflags []mcnflag.Flag) drivers.DriverOptions { func getDriverOpts(c *cli.Context, mcnflags []mcnflag.Flag) drivers.DriverOptions {
@ -401,7 +408,7 @@ func convertMcnFlagsToCliFlags(mcnFlags []mcnflag.Flag) ([]cli.Flag, error) {
func addDriverFlagsToCommand(cliFlags []cli.Flag, cmd *cli.Command) *cli.Command { func addDriverFlagsToCommand(cliFlags []cli.Flag, cmd *cli.Command) *cli.Command {
cmd.Flags = append(sharedCreateFlags, cliFlags...) cmd.Flags = append(sharedCreateFlags, cliFlags...)
cmd.SkipFlagParsing = false cmd.SkipFlagParsing = false
cmd.Action = cmdCreateInner cmd.Action = fatalOnError(cmdCreateInner)
sort.Sort(ByFlagName(cmd.Flags)) sort.Sort(ByFlagName(cmd.Flags))
return cmd return cmd

View File

@ -18,7 +18,7 @@ const (
) )
var ( var (
improperEnvArgsError = errors.New("Error: Expected either one machine name, or -u flag to unset the variables in the arguments.") errImproperEnvArgs = errors.New("Error: Expected either one machine name, or -u flag to unset the variables in the arguments")
) )
type ShellConfig struct { type ShellConfig struct {
@ -34,27 +34,30 @@ type ShellConfig struct {
NoProxyValue string NoProxyValue string
} }
func cmdEnv(c *cli.Context) { func cmdEnv(c *cli.Context) error {
// Ensure that log messages always go to stderr when this command is // Ensure that log messages always go to stderr when this command is
// being run (it is intended to be run in a subshell) // being run (it is intended to be run in a subshell)
log.SetOutWriter(os.Stderr) log.SetOutWriter(os.Stderr)
if len(c.Args()) != 1 && !c.Bool("unset") { if len(c.Args()) != 1 && !c.Bool("unset") {
fatal(improperEnvArgsError) return errImproperEnvArgs
} }
h := getFirstArgHost(c) host, err := getFirstArgHost(c)
dockerHost, _, err := runConnectionBoilerplate(h, c)
if err != nil { if err != nil {
fatalf("Error running connection boilerplate: %s", err) return err
}
dockerHost, _, err := runConnectionBoilerplate(host, c)
if err != nil {
return fmt.Errorf("Error running connection boilerplate: %s", err)
} }
userShell := c.String("shell") userShell := c.String("shell")
if userShell == "" { if userShell == "" {
shell, err := detectShell() shell, err := detectShell()
if err != nil { if err != nil {
fatal(err) return err
} }
userShell = shell userShell = shell
} }
@ -64,17 +67,17 @@ func cmdEnv(c *cli.Context) {
usageHint := generateUsageHint(userShell, os.Args) usageHint := generateUsageHint(userShell, os.Args)
shellCfg := &ShellConfig{ shellCfg := &ShellConfig{
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), h.Name), DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), host.Name),
DockerHost: dockerHost, DockerHost: dockerHost,
DockerTLSVerify: "1", DockerTLSVerify: "1",
UsageHint: usageHint, UsageHint: usageHint,
MachineName: h.Name, MachineName: host.Name,
} }
if c.Bool("no-proxy") { if c.Bool("no-proxy") {
ip, err := h.Driver.GetIP() ip, err := host.Driver.GetIP()
if err != nil { if err != nil {
fatalf("Error getting host IP: %s", err) return fmt.Errorf("Error getting host IP: %s", err)
} }
// first check for an existing lower case no_proxy var // first check for an existing lower case no_proxy var
@ -128,13 +131,10 @@ func cmdEnv(c *cli.Context) {
tmpl, err := t.Parse(envTmpl) tmpl, err := t.Parse(envTmpl)
if err != nil { if err != nil {
fatal(err) return err
} }
if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { return tmpl.Execute(os.Stdout, shellCfg)
fatal(err)
}
return
} }
switch userShell { switch userShell {
@ -158,12 +158,10 @@ func cmdEnv(c *cli.Context) {
tmpl, err := t.Parse(envTmpl) tmpl, err := t.Parse(envTmpl)
if err != nil { if err != nil {
fatal(err) return err
} }
if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { return tmpl.Execute(os.Stdout, shellCfg)
fatal(err)
}
} }
func generateUsageHint(userShell string, args []string) string { func generateUsageHint(userShell string, args []string) string {

View File

@ -20,10 +20,15 @@ var funcMap = template.FuncMap{
}, },
} }
func cmdInspect(c *cli.Context) { func cmdInspect(c *cli.Context) error {
if len(c.Args()) == 0 { if len(c.Args()) == 0 {
cli.ShowCommandHelp(c, "inspect") cli.ShowCommandHelp(c, "inspect")
fatal("You must specify a machine name") return ErrExpectedOneMachine
}
host, err := getFirstArgHost(c)
if err != nil {
return err
} }
tmplString := c.String("format") tmplString := c.String("format")
@ -31,28 +36,32 @@ func cmdInspect(c *cli.Context) {
var tmpl *template.Template var tmpl *template.Template
var err error var err error
if tmpl, err = template.New("").Funcs(funcMap).Parse(tmplString); err != nil { if tmpl, err = template.New("").Funcs(funcMap).Parse(tmplString); err != nil {
fatalf("Template parsing error: %v\n", err) return fmt.Errorf("Template parsing error: %v\n", err)
} }
jsonHost, err := json.Marshal(getFirstArgHost(c)) jsonHost, err := json.Marshal(host)
if err != nil { if err != nil {
fatal(err) return err
} }
obj := make(map[string]interface{}) obj := make(map[string]interface{})
if err := json.Unmarshal(jsonHost, &obj); err != nil { if err := json.Unmarshal(jsonHost, &obj); err != nil {
fatal(err) return err
} }
if err := tmpl.Execute(os.Stdout, obj); err != nil { if err := tmpl.Execute(os.Stdout, obj); err != nil {
fatal(err) return err
} }
os.Stdout.Write([]byte{'\n'}) os.Stdout.Write([]byte{'\n'})
} else { } else {
prettyJSON, err := json.MarshalIndent(getFirstArgHost(c), "", " ") prettyJSON, err := json.MarshalIndent(host, "", " ")
if err != nil { if err != nil {
fatal(err) return err
} }
fmt.Println(string(prettyJSON)) fmt.Println(string(prettyJSON))
} }
return nil
} }

View File

@ -2,8 +2,6 @@ package commands
import "github.com/docker/machine/cli" import "github.com/docker/machine/cli"
func cmdIp(c *cli.Context) { func cmdIp(c *cli.Context) error {
if err := runActionWithContext("ip", c); err != nil { return runActionWithContext("ip", c)
fatal(err)
}
} }

View File

@ -2,8 +2,6 @@ package commands
import "github.com/docker/machine/cli" import "github.com/docker/machine/cli"
func cmdKill(c *cli.Context) { func cmdKill(c *cli.Context) error {
if err := runActionWithContext("kill", c); err != nil { return runActionWithContext("kill", c)
fatal(err)
}
} }

View File

@ -40,17 +40,17 @@ type HostListItem struct {
SwarmOptions *swarm.SwarmOptions SwarmOptions *swarm.SwarmOptions
} }
func cmdLs(c *cli.Context) { func cmdLs(c *cli.Context) error {
quiet := c.Bool("quiet") quiet := c.Bool("quiet")
filters, err := parseFilters(c.StringSlice("filter")) filters, err := parseFilters(c.StringSlice("filter"))
if err != nil { if err != nil {
fatal(err) return err
} }
store := getStore(c) store := getStore(c)
hostList, err := listHosts(store) hostList, err := listHosts(store)
if err != nil { if err != nil {
fatal(err) return err
} }
hostList = filterHosts(hostList, filters) hostList = filterHosts(hostList, filters)
@ -60,7 +60,7 @@ func cmdLs(c *cli.Context) {
for _, host := range hostList { for _, host := range hostList {
fmt.Println(host.Name) fmt.Println(host.Name)
} }
return return nil
} }
swarmMasters := make(map[string]string) swarmMasters := make(map[string]string)
@ -103,6 +103,8 @@ func cmdLs(c *cli.Context) {
} }
w.Flush() w.Flush()
return nil
} }
func parseFilters(filters []string) (FilterOptions, error) { func parseFilters(filters []string) (FilterOptions, error) {
@ -218,7 +220,8 @@ func matchesName(host *host.Host, names []string) bool {
for _, n := range names { for _, n := range names {
r, err := regexp.Compile(n) r, err := regexp.Compile(n)
if err != nil { if err != nil {
fatal(err) // TODO: remove that call to Fatal
log.Fatal(err)
} }
if r.MatchString(host.Driver.GetMachineName()) { if r.MatchString(host.Driver.GetMachineName()) {
return true return true

View File

@ -5,12 +5,19 @@ import (
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRegenerateCerts(c *cli.Context) { func cmdRegenerateCerts(c *cli.Context) error {
force := c.Bool("force") if !c.Bool("force") {
if force || confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.") { ok, err := confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.")
log.Infof("Regenerating TLS certificates") if err != nil {
if err := runActionWithContext("configureAuth", c); err != nil { return err
fatal(err) }
if !ok {
return nil
} }
} }
log.Infof("Regenerating TLS certificates")
return runActionWithContext("configureAuth", c)
} }

View File

@ -6,9 +6,12 @@ import (
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
) )
func cmdRestart(c *cli.Context) { func cmdRestart(c *cli.Context) error {
if err := runActionWithContext("restart", c); err != nil { if err := runActionWithContext("restart", c); err != nil {
fatal(err) return err
} }
log.Info("Restarted machines may have new IP addresses. You may need to re-run the `docker-machine env` command.") log.Info("Restarted machines may have new IP addresses. You may need to re-run the `docker-machine env` command.")
return nil
} }

View File

@ -1,14 +1,17 @@
package commands package commands
import ( import (
"errors"
"fmt"
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRm(c *cli.Context) { func cmdRm(c *cli.Context) error {
if len(c.Args()) == 0 { if len(c.Args()) == 0 {
cli.ShowCommandHelp(c, "rm") cli.ShowCommandHelp(c, "rm")
fatal("You must specify a machine name") return errors.New("You must specify a machine name")
} }
force := c.Bool("force") force := c.Bool("force")
@ -17,8 +20,9 @@ func cmdRm(c *cli.Context) {
for _, hostName := range c.Args() { for _, hostName := range c.Args() {
h, err := loadHost(store, hostName) h, err := loadHost(store, hostName)
if err != nil { if err != nil {
fatalf("Error removing host %q: %s", hostName, err) return fmt.Errorf("Error removing host %q: %s", hostName, err)
} }
if err := h.Driver.Remove(); err != nil { if err := h.Driver.Remove(); err != nil {
if !force { if !force {
log.Errorf("Provider error removing machine %q: %s", hostName, err) log.Errorf("Provider error removing machine %q: %s", hostName, err)
@ -32,4 +36,6 @@ func cmdRm(c *cli.Context) {
log.Infof("Successfully removed %s", hostName) log.Infof("Successfully removed %s", hostName)
} }
} }
return nil
} }

View File

@ -14,7 +14,8 @@ import (
) )
var ( var (
ErrMalformedInput = fmt.Errorf("The input was malformed") errMalformedInput = errors.New("The input was malformed")
errWrongNumberArguments = errors.New("Improper number of arguments")
) )
var ( var (
@ -64,7 +65,7 @@ func getInfoForScpArg(hostAndPath string, store persist.Store) (*host.Host, stri
return host, path, args, nil return host, path, args, nil
} }
return nil, "", nil, ErrMalformedInput return nil, "", nil, errMalformedInput
} }
func generateLocationArg(host *host.Host, path string) (string, error) { func generateLocationArg(host *host.Host, path string) (string, error) {
@ -120,19 +121,17 @@ func runCmdWithStdIo(cmd exec.Cmd) error {
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fatal(err) return cmd.Run()
}
return nil
} }
func cmdScp(c *cli.Context) { func cmdScp(c *cli.Context) error {
hostLoader = &ScpHostLoader{} hostLoader = &ScpHostLoader{}
args := c.Args() args := c.Args()
if len(args) != 2 { if len(args) != 2 {
cli.ShowCommandHelp(c, "scp") cli.ShowCommandHelp(c, "scp")
fatal("Improper number of arguments.") return errWrongNumberArguments
} }
// TODO: Check that "-3" flag is available in user's version of scp. // TODO: Check that "-3" flag is available in user's version of scp.
@ -147,12 +146,11 @@ func cmdScp(c *cli.Context) {
dest := args[1] dest := args[1]
store := getStore(c) store := getStore(c)
cmd, err := getScpCmd(src, dest, sshArgs, store)
cmd, err := getScpCmd(src, dest, sshArgs, store)
if err != nil { if err != nil {
fatal(err) return err
}
if err := runCmdWithStdIo(*cmd); err != nil {
fatal(err)
} }
return runCmdWithStdIo(*cmd)
} }

View File

@ -196,7 +196,7 @@ func TestGetInfoForScpArg(t *testing.T) {
} }
host, path, opts, err = getInfoForScpArg("foo:bar:widget", store) host, path, opts, err = getInfoForScpArg("foo:bar:widget", store)
if err != ErrMalformedInput { if err != errMalformedInput {
t.Fatalf("Didn't get back an error when we were expecting it for malformed args") t.Fatalf("Didn't get back an error when we were expecting it for malformed args")
} }
} }

View File

@ -1,39 +1,37 @@
package commands package commands
import ( import (
"fmt"
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
) )
func cmdSsh(c *cli.Context) { func cmdSsh(c *cli.Context) error {
args := c.Args() name := c.Args().First()
name := args.First()
if name == "" { if name == "" {
fatal("Error: Please specify a machine name.") return ErrExpectedOneMachine
} }
store := getStore(c) store := getStore(c)
host, err := loadHost(store, name) host, err := loadHost(store, name)
if err != nil { if err != nil {
fatal(err) return err
} }
currentState, err := host.Driver.GetState() currentState, err := host.Driver.GetState()
if err != nil { if err != nil {
fatal(err) return err
} }
if currentState != state.Running { if currentState != state.Running {
fatalf("Error: Cannot run SSH command: Host %q is not running", host.Name) return fmt.Errorf("Error: Cannot run SSH command: Host %q is not running", host.Name)
} }
client, err := host.CreateSSHClient() client, err := host.CreateSSHClient()
if err != nil { if err != nil {
fatal(err) return err
} }
if err := client.Shell(c.Args().Tail()...); err != nil { return client.Shell(c.Args().Tail()...)
fatal(err)
}
} }

View File

@ -6,9 +6,12 @@ import (
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
) )
func cmdStart(c *cli.Context) { func cmdStart(c *cli.Context) error {
if err := runActionWithContext("start", c); err != nil { if err := runActionWithContext("start", c); err != nil {
fatal(err) return err
} }
log.Info("Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.") log.Info("Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.")
return nil
} }

View File

@ -5,16 +5,22 @@ import (
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdStatus(c *cli.Context) { func cmdStatus(c *cli.Context) error {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
fatal(ErrExpectedOneMachine) return ErrExpectedOneMachine
}
host, err := getFirstArgHost(c)
if err != nil {
return err
} }
host := getFirstArgHost(c)
currentState, err := host.Driver.GetState() currentState, err := host.Driver.GetState()
if err != nil { if err != nil {
log.Errorf("error getting state for host %s: %s", host.Name, err) log.Errorf("error getting state for host %s: %s", host.Name, err)
} }
log.Info(currentState) log.Info(currentState)
return nil
} }

View File

@ -2,8 +2,6 @@ package commands
import "github.com/docker/machine/cli" import "github.com/docker/machine/cli"
func cmdStop(c *cli.Context) { func cmdStop(c *cli.Context) error {
if err := runActionWithContext("stop", c); err != nil { return runActionWithContext("stop", c)
fatal(err)
}
} }

View File

@ -2,8 +2,6 @@ package commands
import "github.com/docker/machine/cli" import "github.com/docker/machine/cli"
func cmdUpgrade(c *cli.Context) { func cmdUpgrade(c *cli.Context) error {
if err := runActionWithContext("upgrade", c); err != nil { return runActionWithContext("upgrade", c)
fatal(err)
}
} }

View File

@ -6,14 +6,22 @@ import (
"github.com/docker/machine/cli" "github.com/docker/machine/cli"
) )
func cmdUrl(c *cli.Context) { func cmdUrl(c *cli.Context) error {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
fatal(ErrExpectedOneMachine) return ErrExpectedOneMachine
} }
url, err := getFirstArgHost(c).GetURL()
host, err := getFirstArgHost(c)
if err != nil { if err != nil {
fatal(err) return err
}
url, err := host.GetURL()
if err != nil {
return err
} }
fmt.Println(url) fmt.Println(url)
return nil
} }

View File

@ -8,11 +8,11 @@ load ${BASE_TEST_DIR}/helpers.bash
[[ ${lines[0]} == "Driver \"bogus\" not found. Do you have the plugin binary accessible in your PATH?" ]] [[ ${lines[0]} == "Driver \"bogus\" not found. Do you have the plugin binary accessible in your PATH?" ]]
} }
@test "none: create with no name fails 'machine create -d none \" \"'" { @test "none: create with no name fails 'machine create -d none'" {
run machine create -d none run machine create -d none
last=$((${#lines[@]} - 1)) last=$((${#lines[@]} - 1))
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
[[ ${lines[$last]} == "Error: No machine name specified." ]] [[ ${lines[$last]} == "Error: No machine name specified" ]]
} }
@test "none: create with invalid name fails 'machine create -d none --url none ∞'" { @test "none: create with invalid name fails 'machine create -d none --url none ∞'" {

View File

@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash
@test "inspect: show error in case of no args" { @test "inspect: show error in case of no args" {
run machine inspect run machine inspect
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
[[ ${output} == *"must specify a machine name"* ]] [[ ${output} == *"Expected one machine name as an argument"* ]]
} }

View File

@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash
@test "status: show error in case of no args" { @test "status: show error in case of no args" {
run machine status run machine status
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
[[ ${output} == *"Expected one machine name as an argument."* ]] [[ ${output} == *"Expected one machine name as an argument"* ]]
} }

View File

@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash
@test "url: show error in case of no args" { @test "url: show error in case of no args" {
run machine url run machine url
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
[[ ${output} == *"Expected one machine name as an argument."* ]] [[ ${output} == *"Expected one machine name as an argument"* ]]
} }