diff --git a/commands/active.go b/commands/active.go index 3e7ac50f38..30544d069c 100644 --- a/commands/active.go +++ b/commands/active.go @@ -12,20 +12,27 @@ import ( "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 { - fatal("Error: Too many arguments given.") + return errTooManyArguments } store := getStore(c) + host, err := getActiveHost(store) if err != nil { - fatalf("Error getting active host: %s", err) + return fmt.Errorf("Error getting active host: %s", err) } if host != nil { fmt.Println(host.Name) } + + return nil } func getActiveHost(store persist.Store) (*host.Host, error) { diff --git a/commands/commands.go b/commands/commands.go index 9508f41038..833c13f5e0 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -19,8 +19,8 @@ import ( var ( ErrUnknownShell = errors.New("Error: Unknown shell") - 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.") + 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") ) func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClientDriver, error) { @@ -32,29 +32,25 @@ func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClient return d, nil } -func fatal(args ...interface{}) { - log.Fatal(args...) +func fatalOnError(command func(context *cli.Context) error) func(context *cli.Context) { + return func(context *cli.Context) { + if err := command(context); err != nil { + log.Fatal(err) + } + } } -func fatalf(fmtString string, args ...interface{}) { - log.Fatalf(fmtString, args...) -} - -func confirmInput(msg string) bool { +func confirmInput(msg string) (bool, error) { fmt.Printf("%s (y/n): ", msg) + var resp string _, err := fmt.Scanln(&resp) - if err != nil { - fatal(err) + return false, err } - if strings.Index(strings.ToLower(resp), "y") == 0 { - return true - - } - - return false + confirmed := strings.Index(strings.ToLower(resp), "y") == 0 + return confirmed, nil } func getStore(c *cli.Context) persist.Store { @@ -112,17 +108,16 @@ func saveHost(store persist.Store, h *host.Host) error { return nil } -func getFirstArgHost(c *cli.Context) *host.Host { +func getFirstArgHost(c *cli.Context) (*host.Host, error) { store := getStore(c) hostName := c.Args().First() exists, err := store.Exists(hostName) 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 { - fatalf("Host %q does not exist", hostName) + return nil, fmt.Errorf("Host %q does not exist", 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 // do anything else interesting, but also this premature exit // 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) { @@ -155,13 +151,13 @@ var Commands = []cli.Command{ { Name: "active", Usage: "Print which machine is active", - Action: cmdActive, + Action: fatalOnError(cmdActive), }, { Name: "config", Usage: "Print the connection config for machine", Description: "Argument is a machine name.", - Action: cmdConfig, + Action: fatalOnError(cmdConfig), Flags: []cli.Flag{ cli.BoolFlag{ Name: "swarm", @@ -173,14 +169,14 @@ var Commands = []cli.Command{ Flags: sharedCreateFlags, Name: "create", 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, }, { Name: "env", Usage: "Display the commands to set up the environment for the Docker client", Description: "Argument is a machine name.", - Action: cmdEnv, + Action: fatalOnError(cmdEnv), Flags: []cli.Flag{ cli.BoolFlag{ Name: "swarm", @@ -204,7 +200,7 @@ var Commands = []cli.Command{ Name: "inspect", Usage: "Inspect information about a machine", Description: "Argument is a machine name.", - Action: cmdInspect, + Action: fatalOnError(cmdInspect), Flags: []cli.Flag{ cli.StringFlag{ Name: "format, f", @@ -217,13 +213,13 @@ var Commands = []cli.Command{ Name: "ip", Usage: "Get the IP address of a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdIp, + Action: fatalOnError(cmdIp), }, { Name: "kill", Usage: "Kill a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdKill, + Action: fatalOnError(cmdKill), }, { Flags: []cli.Flag{ @@ -239,13 +235,13 @@ var Commands = []cli.Command{ }, Name: "ls", Usage: "List machines", - Action: cmdLs, + Action: fatalOnError(cmdLs), }, { Name: "regenerate-certs", Usage: "Regenerate TLS Certificates for a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdRegenerateCerts, + Action: fatalOnError(cmdRegenerateCerts), Flags: []cli.Flag{ cli.BoolFlag{ Name: "force, f", @@ -257,7 +253,7 @@ var Commands = []cli.Command{ Name: "restart", Usage: "Restart a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdRestart, + Action: fatalOnError(cmdRestart), }, { Flags: []cli.Flag{ @@ -269,20 +265,20 @@ var Commands = []cli.Command{ Name: "rm", Usage: "Remove a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdRm, + Action: fatalOnError(cmdRm), }, { Name: "ssh", Usage: "Log into or run a command on a machine with SSH.", Description: "Arguments are [machine-name] [command]", - Action: cmdSsh, + Action: fatalOnError(cmdSsh), SkipFlagParsing: true, }, { Name: "scp", Usage: "Copy files between machines", Description: "Arguments are [machine:][path] [machine:][path].", - Action: cmdScp, + Action: fatalOnError(cmdScp), Flags: []cli.Flag{ cli.BoolFlag{ Name: "recursive, r", @@ -294,31 +290,31 @@ var Commands = []cli.Command{ Name: "start", Usage: "Start a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdStart, + Action: fatalOnError(cmdStart), }, { Name: "status", Usage: "Get the status of a machine", Description: "Argument is a machine name.", - Action: cmdStatus, + Action: fatalOnError(cmdStatus), }, { Name: "stop", Usage: "Stop a machine", Description: "Argument(s) are one or more machine names.", - Action: cmdStop, + Action: fatalOnError(cmdStop), }, { Name: "upgrade", Usage: "Upgrade a machine to the latest version of Docker", Description: "Argument(s) are one or more machine names.", - Action: cmdUpgrade, + Action: fatalOnError(cmdUpgrade), }, { Name: "url", Usage: "Get the URL of a machine", Description: "Argument is a machine name.", - Action: cmdUrl, + Action: fatalOnError(cmdUrl), }, } diff --git a/commands/config.go b/commands/config.go index bc3441acb7..311b40b796 100644 --- a/commands/config.go +++ b/commands/config.go @@ -14,26 +14,31 @@ import ( "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 // being run (it is intended to be run in a subshell) log.SetOutWriter(os.Stderr) if len(c.Args()) != 1 { - fatal(ErrExpectedOneMachine) + return ErrExpectedOneMachine } - h := getFirstArgHost(c) - - dockerHost, authOptions, err := runConnectionBoilerplate(h, c) + host, err := getFirstArgHost(c) 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) fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s", authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost) + + return nil } 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) } 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() @@ -101,7 +106,7 @@ func parseSwarm(hostUrl string, h *host.Host) (string, error) { swarmOptions := h.HostOptions.SwarmOptions 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) diff --git a/commands/create.go b/commands/create.go index 1caa2050b8..5edfcebfd7 100644 --- a/commands/create.go +++ b/commands/create.go @@ -10,6 +10,8 @@ import ( "sort" "strings" + "errors" + "github.com/docker/machine/cli" "github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/libmachine" @@ -25,6 +27,10 @@ import ( "github.com/docker/machine/libmachine/swarm" ) +var ( + errNoMachineName = errors.New("Error: No machine name specified") +) + var ( sharedCreateFlags = []cli.Flag{ cli.StringFlag{ @@ -111,9 +117,9 @@ var ( } ) -func cmdCreateInner(c *cli.Context) { +func cmdCreateInner(c *cli.Context) error { 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() @@ -130,16 +136,16 @@ func cmdCreateInner(c *cli.Context) { if name == "" { cli.ShowCommandHelp(c, "create") - fatal("Error: No machine name specified.") + return errNoMachineName } validName := host.ValidateHostName(name) 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 { - fatalf("Error parsing swarm discovery: %s", err) + return fmt.Errorf("Error parsing swarm discovery: %s", err) } // TODO: Fix hacky JSON solution @@ -148,17 +154,17 @@ func cmdCreateInner(c *cli.Context) { StorePath: c.GlobalString("storage-path"), }) 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) 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) if err != nil { - fatalf("Error getting new host: %s", err) + return fmt.Errorf("Error getting new host: %s", err) } h.HostOptions = &host.HostOptions{ @@ -196,12 +202,12 @@ func cmdCreateInner(c *cli.Context) { exists, err := store.Exists(h.Name) if err != nil { - fatalf("Error checking if host exists: %s", err) + return fmt.Errorf("Error checking if host exists: %s", err) } if exists { - fatal(mcnerror.ErrHostAlreadyExists{ + return mcnerror.ErrHostAlreadyExists{ Name: h.Name, - }) + } } // 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) 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 { - fatalf("Error creating machine: %s", err) + return fmt.Errorf("Error creating machine: %s", err) } 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)) + + return nil } // The following function is needed because the CLI acrobatics that we're doing @@ -261,13 +269,13 @@ func flagHackLookup(flagName string) string { return "" } -func cmdCreateOuter(c *cli.Context) { +func cmdCreateOuter(c *cli.Context) error { driverName := flagHackLookup("--driver") // We didn't recognize the driver name. if driverName == "" { cli.ShowCommandHelp(c, "create") - return + return nil // ? } name := c.Args().First() @@ -277,12 +285,12 @@ func cmdCreateOuter(c *cli.Context) { MachineName: name, }) 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) 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 @@ -296,8 +304,9 @@ func cmdCreateOuter(c *cli.Context) { // on the requested driver. cliFlags, err := convertMcnFlagsToCliFlags(mcnFlags) 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 { cmd := &c.App.Commands[i] if cmd.HasName("create") { @@ -306,12 +315,10 @@ func cmdCreateOuter(c *cli.Context) { } if err := driver.Close(); err != nil { - fatal(err) + return err } - if err := c.App.Run(os.Args); err != nil { - fatal(err) - } + return c.App.Run(os.Args) } 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 { cmd.Flags = append(sharedCreateFlags, cliFlags...) cmd.SkipFlagParsing = false - cmd.Action = cmdCreateInner + cmd.Action = fatalOnError(cmdCreateInner) sort.Sort(ByFlagName(cmd.Flags)) return cmd diff --git a/commands/env.go b/commands/env.go index 2bdc30f32e..d27c616c33 100644 --- a/commands/env.go +++ b/commands/env.go @@ -18,7 +18,7 @@ const ( ) 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 { @@ -34,27 +34,30 @@ type ShellConfig struct { 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 // being run (it is intended to be run in a subshell) log.SetOutWriter(os.Stderr) if len(c.Args()) != 1 && !c.Bool("unset") { - fatal(improperEnvArgsError) + return errImproperEnvArgs } - h := getFirstArgHost(c) - - dockerHost, _, err := runConnectionBoilerplate(h, c) + host, err := getFirstArgHost(c) 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") if userShell == "" { shell, err := detectShell() if err != nil { - fatal(err) + return err } userShell = shell } @@ -64,17 +67,17 @@ func cmdEnv(c *cli.Context) { usageHint := generateUsageHint(userShell, os.Args) shellCfg := &ShellConfig{ - DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), h.Name), + DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), host.Name), DockerHost: dockerHost, DockerTLSVerify: "1", UsageHint: usageHint, - MachineName: h.Name, + MachineName: host.Name, } if c.Bool("no-proxy") { - ip, err := h.Driver.GetIP() + ip, err := host.Driver.GetIP() 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 @@ -128,13 +131,10 @@ func cmdEnv(c *cli.Context) { tmpl, err := t.Parse(envTmpl) if err != nil { - fatal(err) + return err } - if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { - fatal(err) - } - return + return tmpl.Execute(os.Stdout, shellCfg) } switch userShell { @@ -158,12 +158,10 @@ func cmdEnv(c *cli.Context) { tmpl, err := t.Parse(envTmpl) if err != nil { - fatal(err) + return err } - if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { - fatal(err) - } + return tmpl.Execute(os.Stdout, shellCfg) } func generateUsageHint(userShell string, args []string) string { diff --git a/commands/inspect.go b/commands/inspect.go index aecf6ccfab..d3b30bee37 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -20,10 +20,15 @@ var funcMap = template.FuncMap{ }, } -func cmdInspect(c *cli.Context) { +func cmdInspect(c *cli.Context) error { if len(c.Args()) == 0 { 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") @@ -31,28 +36,32 @@ func cmdInspect(c *cli.Context) { var tmpl *template.Template var err error 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 { - fatal(err) + return err } + obj := make(map[string]interface{}) if err := json.Unmarshal(jsonHost, &obj); err != nil { - fatal(err) + return err } if err := tmpl.Execute(os.Stdout, obj); err != nil { - fatal(err) + return err } + os.Stdout.Write([]byte{'\n'}) } else { - prettyJSON, err := json.MarshalIndent(getFirstArgHost(c), "", " ") + prettyJSON, err := json.MarshalIndent(host, "", " ") if err != nil { - fatal(err) + return err } fmt.Println(string(prettyJSON)) } + + return nil } diff --git a/commands/ip.go b/commands/ip.go index 3a68d5686b..8eb4b9cd06 100644 --- a/commands/ip.go +++ b/commands/ip.go @@ -2,8 +2,6 @@ package commands import "github.com/docker/machine/cli" -func cmdIp(c *cli.Context) { - if err := runActionWithContext("ip", c); err != nil { - fatal(err) - } +func cmdIp(c *cli.Context) error { + return runActionWithContext("ip", c) } diff --git a/commands/kill.go b/commands/kill.go index 1529771000..57c29041e2 100644 --- a/commands/kill.go +++ b/commands/kill.go @@ -2,8 +2,6 @@ package commands import "github.com/docker/machine/cli" -func cmdKill(c *cli.Context) { - if err := runActionWithContext("kill", c); err != nil { - fatal(err) - } +func cmdKill(c *cli.Context) error { + return runActionWithContext("kill", c) } diff --git a/commands/ls.go b/commands/ls.go index db0b97e429..5da1e06992 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -40,17 +40,17 @@ type HostListItem struct { SwarmOptions *swarm.SwarmOptions } -func cmdLs(c *cli.Context) { +func cmdLs(c *cli.Context) error { quiet := c.Bool("quiet") filters, err := parseFilters(c.StringSlice("filter")) if err != nil { - fatal(err) + return err } store := getStore(c) hostList, err := listHosts(store) if err != nil { - fatal(err) + return err } hostList = filterHosts(hostList, filters) @@ -60,7 +60,7 @@ func cmdLs(c *cli.Context) { for _, host := range hostList { fmt.Println(host.Name) } - return + return nil } swarmMasters := make(map[string]string) @@ -103,6 +103,8 @@ func cmdLs(c *cli.Context) { } w.Flush() + + return nil } func parseFilters(filters []string) (FilterOptions, error) { @@ -218,7 +220,8 @@ func matchesName(host *host.Host, names []string) bool { for _, n := range names { r, err := regexp.Compile(n) if err != nil { - fatal(err) + // TODO: remove that call to Fatal + log.Fatal(err) } if r.MatchString(host.Driver.GetMachineName()) { return true diff --git a/commands/regeneratecerts.go b/commands/regeneratecerts.go index 0392137f40..4e55a93f62 100644 --- a/commands/regeneratecerts.go +++ b/commands/regeneratecerts.go @@ -5,12 +5,19 @@ import ( "github.com/docker/machine/libmachine/log" ) -func cmdRegenerateCerts(c *cli.Context) { - force := c.Bool("force") - if force || confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.") { - log.Infof("Regenerating TLS certificates") - if err := runActionWithContext("configureAuth", c); err != nil { - fatal(err) +func cmdRegenerateCerts(c *cli.Context) error { + if !c.Bool("force") { + ok, err := confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.") + if err != nil { + return err + } + + if !ok { + return nil } } + + log.Infof("Regenerating TLS certificates") + + return runActionWithContext("configureAuth", c) } diff --git a/commands/restart.go b/commands/restart.go index a6687db874..f1fbb2a2f2 100644 --- a/commands/restart.go +++ b/commands/restart.go @@ -6,9 +6,12 @@ import ( "github.com/docker/machine/cli" ) -func cmdRestart(c *cli.Context) { +func cmdRestart(c *cli.Context) error { 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.") + + return nil } diff --git a/commands/rm.go b/commands/rm.go index 71b0a74ce9..718f2fbf55 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -1,14 +1,17 @@ package commands import ( + "errors" + "fmt" + "github.com/docker/machine/cli" "github.com/docker/machine/libmachine/log" ) -func cmdRm(c *cli.Context) { +func cmdRm(c *cli.Context) error { if len(c.Args()) == 0 { cli.ShowCommandHelp(c, "rm") - fatal("You must specify a machine name") + return errors.New("You must specify a machine name") } force := c.Bool("force") @@ -17,8 +20,9 @@ func cmdRm(c *cli.Context) { for _, hostName := range c.Args() { h, err := loadHost(store, hostName) 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 !force { 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) } } + + return nil } diff --git a/commands/scp.go b/commands/scp.go index 599dd126a2..93b3259971 100644 --- a/commands/scp.go +++ b/commands/scp.go @@ -14,7 +14,8 @@ import ( ) var ( - ErrMalformedInput = fmt.Errorf("The input was malformed") + errMalformedInput = errors.New("The input was malformed") + errWrongNumberArguments = errors.New("Improper number of arguments") ) var ( @@ -64,7 +65,7 @@ func getInfoForScpArg(hostAndPath string, store persist.Store) (*host.Host, stri return host, path, args, nil } - return nil, "", nil, ErrMalformedInput + return nil, "", nil, errMalformedInput } func generateLocationArg(host *host.Host, path string) (string, error) { @@ -120,19 +121,17 @@ func runCmdWithStdIo(cmd exec.Cmd) error { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - fatal(err) - } - return nil + + return cmd.Run() } -func cmdScp(c *cli.Context) { +func cmdScp(c *cli.Context) error { hostLoader = &ScpHostLoader{} args := c.Args() if len(args) != 2 { cli.ShowCommandHelp(c, "scp") - fatal("Improper number of arguments.") + return errWrongNumberArguments } // 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] store := getStore(c) - cmd, err := getScpCmd(src, dest, sshArgs, store) + cmd, err := getScpCmd(src, dest, sshArgs, store) if err != nil { - fatal(err) - } - if err := runCmdWithStdIo(*cmd); err != nil { - fatal(err) + return err } + + return runCmdWithStdIo(*cmd) } diff --git a/commands/scp_test.go b/commands/scp_test.go index 70333ab2d4..540642323e 100644 --- a/commands/scp_test.go +++ b/commands/scp_test.go @@ -196,7 +196,7 @@ func TestGetInfoForScpArg(t *testing.T) { } 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") } } diff --git a/commands/ssh.go b/commands/ssh.go index b3fd03cb12..6d33992a4e 100644 --- a/commands/ssh.go +++ b/commands/ssh.go @@ -1,39 +1,37 @@ package commands import ( + "fmt" + "github.com/docker/machine/cli" "github.com/docker/machine/libmachine/state" ) -func cmdSsh(c *cli.Context) { - args := c.Args() - name := args.First() - +func cmdSsh(c *cli.Context) error { + name := c.Args().First() if name == "" { - fatal("Error: Please specify a machine name.") + return ErrExpectedOneMachine } store := getStore(c) host, err := loadHost(store, name) if err != nil { - fatal(err) + return err } currentState, err := host.Driver.GetState() if err != nil { - fatal(err) + return err } 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() if err != nil { - fatal(err) + return err } - if err := client.Shell(c.Args().Tail()...); err != nil { - fatal(err) - } + return client.Shell(c.Args().Tail()...) } diff --git a/commands/start.go b/commands/start.go index 7245e3fbed..036707a970 100644 --- a/commands/start.go +++ b/commands/start.go @@ -6,9 +6,12 @@ import ( "github.com/docker/machine/cli" ) -func cmdStart(c *cli.Context) { +func cmdStart(c *cli.Context) error { 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.") + + return nil } diff --git a/commands/status.go b/commands/status.go index 48fd9e45e5..d87722062e 100644 --- a/commands/status.go +++ b/commands/status.go @@ -5,16 +5,22 @@ import ( "github.com/docker/machine/libmachine/log" ) -func cmdStatus(c *cli.Context) { +func cmdStatus(c *cli.Context) error { 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() if err != nil { log.Errorf("error getting state for host %s: %s", host.Name, err) } log.Info(currentState) + + return nil } diff --git a/commands/stop.go b/commands/stop.go index 2a1f2b0bd7..69920e5400 100644 --- a/commands/stop.go +++ b/commands/stop.go @@ -2,8 +2,6 @@ package commands import "github.com/docker/machine/cli" -func cmdStop(c *cli.Context) { - if err := runActionWithContext("stop", c); err != nil { - fatal(err) - } +func cmdStop(c *cli.Context) error { + return runActionWithContext("stop", c) } diff --git a/commands/upgrade.go b/commands/upgrade.go index 5fb0d38d18..89327357d0 100644 --- a/commands/upgrade.go +++ b/commands/upgrade.go @@ -2,8 +2,6 @@ package commands import "github.com/docker/machine/cli" -func cmdUpgrade(c *cli.Context) { - if err := runActionWithContext("upgrade", c); err != nil { - fatal(err) - } +func cmdUpgrade(c *cli.Context) error { + return runActionWithContext("upgrade", c) } diff --git a/commands/url.go b/commands/url.go index a7154cc3cd..40c21515dc 100644 --- a/commands/url.go +++ b/commands/url.go @@ -6,14 +6,22 @@ import ( "github.com/docker/machine/cli" ) -func cmdUrl(c *cli.Context) { +func cmdUrl(c *cli.Context) error { if len(c.Args()) != 1 { - fatal(ErrExpectedOneMachine) + return ErrExpectedOneMachine } - url, err := getFirstArgHost(c).GetURL() + + host, err := getFirstArgHost(c) if err != nil { - fatal(err) + return err + } + + url, err := host.GetURL() + if err != nil { + return err } fmt.Println(url) + + return nil } diff --git a/test/integration/cli/create-rm.bats b/test/integration/cli/create-rm.bats index 37a07134a6..73f18184ae 100644 --- a/test/integration/cli/create-rm.bats +++ b/test/integration/cli/create-rm.bats @@ -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?" ]] } -@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 last=$((${#lines[@]} - 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 ∞'" { diff --git a/test/integration/cli/inspect.bats b/test/integration/cli/inspect.bats index 5777c25824..09c03ec130 100644 --- a/test/integration/cli/inspect.bats +++ b/test/integration/cli/inspect.bats @@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash @test "inspect: show error in case of no args" { run machine inspect [ "$status" -eq 1 ] - [[ ${output} == *"must specify a machine name"* ]] + [[ ${output} == *"Expected one machine name as an argument"* ]] } diff --git a/test/integration/cli/status.bats b/test/integration/cli/status.bats index c9c664880d..6d52b73699 100644 --- a/test/integration/cli/status.bats +++ b/test/integration/cli/status.bats @@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash @test "status: show error in case of no args" { run machine status [ "$status" -eq 1 ] - [[ ${output} == *"Expected one machine name as an argument."* ]] + [[ ${output} == *"Expected one machine name as an argument"* ]] } diff --git a/test/integration/cli/url.bats b/test/integration/cli/url.bats index 1cd6690495..75ec1d3f7e 100644 --- a/test/integration/cli/url.bats +++ b/test/integration/cli/url.bats @@ -5,5 +5,5 @@ load ${BASE_TEST_DIR}/helpers.bash @test "url: show error in case of no args" { run machine url [ "$status" -eq 1 ] - [[ ${output} == *"Expected one machine name as an argument."* ]] + [[ ${output} == *"Expected one machine name as an argument"* ]] }