func cmdScp(c *cli.Context) { args := c.Args() if len(args) != 2 { cli.ShowCommandHelp(c, "scp") log.Fatal("Improper number of arguments.") } // TODO: Check that "-3" flag is available in user's version of scp. // It is on every system I've checked, but the manual mentioned it's "newer" sshArgs := append(baseSSHArgs, "-3") if c.Bool("recursive") { sshArgs = append(sshArgs, "-r") } src := args[0] dest := args[1] provider := getDefaultProvider(c) cmd, err := getScpCmd(src, dest, sshArgs, *provider) if err != nil { log.Fatal(err) } if err := runCmdWithStdIo(*cmd); err != nil { log.Fatal(err) } }
func cmdInspect(c *cli.Context) { tmplString := c.String("format") if tmplString != "" { var tmpl *template.Template var err error if tmpl, err = template.New("").Funcs(funcMap).Parse(tmplString); err != nil { log.Fatalf("Template parsing error: %v\n", err) } jsonHost, err := json.Marshal(getHost(c)) if err != nil { log.Fatal(err) } obj := make(map[string]interface{}) if err := json.Unmarshal(jsonHost, &obj); err != nil { log.Fatal(err) } if err := tmpl.Execute(os.Stderr, obj); err != nil { log.Fatal(err) } os.Stderr.Write([]byte{'\n'}) } else { prettyJSON, err := json.MarshalIndent(getHost(c), "", " ") if err != nil { log.Fatal(err) } fmt.Println(string(prettyJSON)) } }
func cmdActive(c *cli.Context) { if len(c.Args()) > 0 { log.Fatal("Error: Too many arguments given.") } certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } mcn, err := newMcn(defaultStore) if err != nil { log.Fatal(err) } host, err := mcn.GetActive() if err != nil { log.Fatalf("Error getting active host: %s", err) } if host != nil { fmt.Println(host.Name) } }
func setupCertificates(caCertPath, caKeyPath, clientCertPath, clientKeyPath string) error { org := utils.GetUsername() bits := 2048 if _, err := os.Stat(utils.GetMachineCertDir()); err != nil { if os.IsNotExist(err) { if err := os.MkdirAll(utils.GetMachineCertDir(), 0700); err != nil { log.Fatalf("Error creating machine config dir: %s", err) } } else { log.Fatal(err) } } if _, err := os.Stat(caCertPath); os.IsNotExist(err) { log.Infof("Creating CA: %s", caCertPath) // check if the key path exists; if so, error if _, err := os.Stat(caKeyPath); err == nil { log.Fatalf("The CA key already exists. Please remove it or specify a different key/cert.") } if err := utils.GenerateCACertificate(caCertPath, caKeyPath, org, bits); err != nil { log.Infof("Error generating CA certificate: %s", err) } } if _, err := os.Stat(clientCertPath); os.IsNotExist(err) { log.Infof("Creating client certificate: %s", clientCertPath) if _, err := os.Stat(utils.GetMachineCertDir()); err != nil { if os.IsNotExist(err) { if err := os.Mkdir(utils.GetMachineCertDir(), 0700); err != nil { log.Fatalf("Error creating machine client cert dir: %s", err) } } else { log.Fatal(err) } } // check if the key path exists; if so, error if _, err := os.Stat(clientKeyPath); err == nil { log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.") } if err := utils.GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caKeyPath, org, bits); err != nil { log.Fatalf("Error generating client certificate: %s", err) } } return nil }
func getMachineConfig(c *cli.Context) (*machineConfig, error) { name := c.Args().First() certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } m, err := provider.Get(name) if err != nil { return nil, err } machineDir := filepath.Join(utils.GetMachineDir(), m.Name) caCert := filepath.Join(machineDir, "ca.pem") caKey := filepath.Join(utils.GetMachineCertDir(), "ca-key.pem") clientCert := filepath.Join(machineDir, "cert.pem") clientKey := filepath.Join(machineDir, "key.pem") serverCert := filepath.Join(machineDir, "server.pem") serverKey := filepath.Join(machineDir, "server-key.pem") machineUrl, err := m.GetURL() if err != nil { if err == drivers.ErrHostIsNotRunning { machineUrl = "" } else { return nil, fmt.Errorf("Unexpected error getting machine url: %s", err) } } return &machineConfig{ machineName: name, machineDir: machineDir, machineUrl: machineUrl, clientKeyPath: clientKey, clientCertPath: clientCert, serverCertPath: serverCert, caKeyPath: caKey, caCertPath: caCert, serverKeyPath: serverKey, AuthOptions: *m.HostOptions.AuthOptions, SwarmOptions: *m.HostOptions.SwarmOptions, }, nil }
func cmdUrl(c *cli.Context) { url, err := getHost(c).GetURL() if err != nil { log.Fatal(err) } fmt.Println(url) }
func DumpVal(vals ...interface{}) { for _, val := range vals { prettyJSON, err := json.MarshalIndent(val, "", " ") if err != nil { log.Fatal(err) } log.Debug(string(prettyJSON)) } }
func getDefaultProvider(c *cli.Context) *libmachine.Provider { certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } return provider }
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 { log.Fatal(err) } } }
func getDefaultMcn(c *cli.Context) *libmachine.Machine { certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } mcn, err := newMcn(defaultStore) if err != nil { log.Fatal(err) } return mcn }
func runCmdWithStdIo(cmd exec.Cmd) error { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } return nil }
func runActionWithContext(actionName string, c *cli.Context) error { machines, err := getHosts(c) if err != nil { return err } if len(machines) == 0 { log.Fatal(ErrNoMachineSpecified) } runActionForeachMachine(actionName, machines) return nil }
func getHost(c *cli.Context) *libmachine.Host { name := c.Args().First() defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), c.GlobalString("tls-ca-cert"), c.GlobalString("tls-ca-key"), ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } host, err := provider.Get(name) if err != nil { log.Fatalf("unable to load host: %v", err) } return host }
func loadMachine(name string, c *cli.Context) (*libmachine.Host, error) { certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } host, err := provider.Get(name) if err != nil { return nil, err } return host, nil }
func cmdSsh(c *cli.Context) { args := c.Args() name := args.First() if name == "" { log.Fatal("Error: Please specify a machine name.") } certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } host, err := provider.Get(name) if err != nil { log.Fatal(err) } currentState, err := host.Driver.GetState() if err != nil { log.Fatal(err) } if currentState != state.Running { log.Fatalf("Error: Cannot run SSH command: Host %q is not running", host.Name) } if len(c.Args()) == 1 { err := host.CreateSSHShell() if err != nil { log.Fatal(err) } } else { output, err := host.RunSSHCommand(strings.Join(c.Args().Tail(), " ")) if err != nil { log.Fatal(err) } fmt.Print(output) } }
func cmdRm(c *cli.Context) { if len(c.Args()) == 0 { cli.ShowCommandHelp(c, "rm") log.Fatal("You must specify a machine name") } force := c.Bool("force") isError := false certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } mcn, err := newMcn(defaultStore) if err != nil { log.Fatal(err) } for _, host := range c.Args() { if err := mcn.Remove(host, force); err != nil { log.Errorf("Error removing machine %s: %s", host, err) isError = true } else { log.Infof("Successfully removed %s", host) } } if isError { log.Fatal("There was an error removing a machine. To force remove it, pass the -f option. Warning: this might leave it running on the provider.") } }
func matchesName(host *libmachine.Host, names []string) bool { if len(names) == 0 { return true } for _, n := range names { r, err := regexp.Compile(n) if err != nil { log.Fatal(err) } if r.MatchString(host.Driver.GetMachineName()) { return true } } return false }
func confirmInput(msg string) bool { fmt.Printf("%s (y/n): ", msg) var resp string _, err := fmt.Scanln(&resp) if err != nil { log.Fatal(err) } if strings.Index(strings.ToLower(resp), "y") == 0 { return true } return false }
func (h *Host) Upgrade() error { machineState, err := h.Driver.GetState() if err != nil { return err } if machineState != state.Running { log.Fatal(errMachineMustBeRunningForUpgrade) } provisioner, err := provision.DetectProvisioner(h.Driver) if err != nil { return err } if err := provisioner.Package("docker", pkgaction.Upgrade); err != nil { return err } if err := provisioner.Service("docker", pkgaction.Restart); err != nil { return err } return nil }
func cmdKill(c *cli.Context) { if err := runActionWithContext("kill", c); err != nil { log.Fatal(err) } }
func cmdRestart(c *cli.Context) { if err := runActionWithContext("restart", c); err != nil { log.Fatal(err) } }
func cmdConfig(c *cli.Context) { if len(c.Args()) != 1 { log.Fatal(ErrExpectedOneMachine) } cfg, err := getMachineConfig(c) if err != nil { log.Fatal(err) } dockerHost, err := getHost(c).Driver.GetURL() if err != nil { log.Fatal(err) } if c.Bool("swarm") { if !cfg.SwarmOptions.Master { log.Fatalf("%s is not a swarm master", cfg.machineName) } u, err := url.Parse(cfg.SwarmOptions.Host) if err != nil { log.Fatal(err) } parts := strings.Split(u.Host, ":") swarmPort := parts[1] // get IP of machine to replace in case swarm host is 0.0.0.0 mUrl, err := url.Parse(dockerHost) if err != nil { log.Fatal(err) } mParts := strings.Split(mUrl.Host, ":") machineIp := mParts[0] dockerHost = fmt.Sprintf("tcp://%s:%s\n", machineIp, swarmPort) } log.Debug(dockerHost) u, err := url.Parse(cfg.machineUrl) if err != nil { log.Fatal(err) } if u.Scheme != "unix" { // validate cert and regenerate if needed valid, err := utils.ValidateCertificate( u.Host, cfg.caCertPath, cfg.serverCertPath, cfg.serverKeyPath, ) if err != nil { log.Fatal(err) } if !valid { log.Debugf("invalid certs detected; regenerating for %s", u.Host) if err := runActionWithContext("configureAuth", c); err != nil { log.Fatal(err) } } } fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s\n", cfg.caCertPath, cfg.clientCertPath, cfg.clientKeyPath, dockerHost) }
func cmdSsh(c *cli.Context) { args := c.Args() name := args.First() cmd := "" if name == "" { log.Fatal("Error: Please specify a machine name.") } certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } mcn, err := newMcn(defaultStore) if err != nil { log.Fatal(err) } host, err := mcn.Get(name) if err != nil { log.Fatal(err) } currentState, err := host.Driver.GetState() if err != nil { log.Fatal(err) } if currentState != state.Running { log.Fatalf("Error: Cannot run SSH command: Host %q is not running", host.Name) } // Loop through the arguments and parse out a command which relies on // flags if it exists, for instance an invocation of the form // `docker-machine ssh dev -- df -h` would mandate this, otherwise we // will accidentally trigger the codegangsta/cli help text because it // thinks we are trying to specify codegangsta flags. // // TODO: I thought codegangsta/cli supported the flag parsing // terminator manually, which would mitigate the need for this kind of // hack. We should investigate. for i, arg := range args { if arg == "--" { cmd = strings.Join(args[i+1:], " ") break } } // It is possible that the user has specified an appended command which // does not rely on the flag parsing terminator, such as // `docker-machine ssh dev ls`, so this block accounts for that case. if len(cmd) == 0 { cmd = strings.Join(args[1:], " ") } if len(c.Args()) == 1 { err := host.CreateSSHShell() if err != nil { log.Fatal(err) } } else { output, err := host.RunSSHCommand(cmd) if err != nil { log.Fatal(err) } fmt.Print(output) } }
func cmdEnv(c *cli.Context) { if len(c.Args()) != 1 && !c.Bool("unset") { log.Fatal(improperEnvArgsError) } userShell := c.String("shell") if userShell == "" { shell, err := detectShell() if err != nil { log.Fatal(err) } userShell = shell } t := template.New("envConfig") usageHint := generateUsageHint(c.App.Name, c.Args().First(), userShell) shellCfg := ShellConfig{ DockerCertPath: "", DockerHost: "", DockerTLSVerify: "", MachineName: "", } // unset vars if c.Bool("unset") { switch userShell { case "fish": shellCfg.Prefix = "set -e " shellCfg.Delimiter = "" shellCfg.Suffix = ";\n" case "powershell": shellCfg.Prefix = "Remove-Item Env:\\\\" shellCfg.Delimiter = "" shellCfg.Suffix = "\n" case "cmd": // since there is no way to unset vars in cmd just reset to empty shellCfg.DockerCertPath = "" shellCfg.DockerHost = "" shellCfg.DockerTLSVerify = "" shellCfg.Prefix = "set " shellCfg.Delimiter = "=" shellCfg.Suffix = "\n" default: shellCfg.Prefix = "unset " shellCfg.Delimiter = " " shellCfg.Suffix = "\n" } tmpl, err := t.Parse(envTmpl) if err != nil { log.Fatal(err) } if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { log.Fatal(err) } return } cfg, err := getMachineConfig(c) if err != nil { log.Fatal(err) } if cfg.machineUrl == "" { log.Fatalf("%s is not running. Please start this with %s start %s", cfg.machineName, c.App.Name, cfg.machineName) } dockerHost := cfg.machineUrl if c.Bool("swarm") { if !cfg.SwarmOptions.Master { log.Fatalf("%s is not a swarm master", cfg.machineName) } u, err := url.Parse(cfg.SwarmOptions.Host) if err != nil { log.Fatal(err) } parts := strings.Split(u.Host, ":") swarmPort := parts[1] // get IP of machine to replace in case swarm host is 0.0.0.0 mUrl, err := url.Parse(cfg.machineUrl) if err != nil { log.Fatal(err) } mParts := strings.Split(mUrl.Host, ":") machineIp := mParts[0] dockerHost = fmt.Sprintf("tcp://%s:%s", machineIp, swarmPort) } u, err := url.Parse(cfg.machineUrl) if err != nil { log.Fatal(err) } if u.Scheme != "unix" { // validate cert and regenerate if needed valid, err := utils.ValidateCertificate( u.Host, cfg.caCertPath, cfg.serverCertPath, cfg.serverKeyPath, ) if err != nil { log.Fatal(err) } if !valid { log.Debugf("invalid certs detected; regenerating for %s", u.Host) if err := runActionWithContext("configureAuth", c); err != nil { log.Fatal(err) } } } shellCfg = ShellConfig{ DockerCertPath: cfg.machineDir, DockerHost: dockerHost, DockerTLSVerify: "1", UsageHint: usageHint, MachineName: cfg.machineName, } switch userShell { case "fish": shellCfg.Prefix = "set -x " shellCfg.Suffix = "\";\n" shellCfg.Delimiter = " \"" case "powershell": shellCfg.Prefix = "$Env:" shellCfg.Suffix = "\"\n" shellCfg.Delimiter = " = \"" case "cmd": shellCfg.Prefix = "set " shellCfg.Suffix = "\n" shellCfg.Delimiter = "=" default: shellCfg.Prefix = "export " shellCfg.Suffix = "\"\n" shellCfg.Delimiter = "=\"" } tmpl, err := t.Parse(envTmpl) if err != nil { log.Fatal(err) } if err := tmpl.Execute(os.Stdout, shellCfg); err != nil { log.Fatal(err) } }
func cmdRestart(c *cli.Context) { if err := runActionWithContext("restart", c); err != nil { log.Fatal(err) } log.Info("Restarted machines may have new IP addresses. You may need to re-run the `docker-machine env` command.") }
func cmdCreate(c *cli.Context) { var ( err error ) driver := c.String("driver") name := c.Args().First() // TODO: Not really a fan of "none" as the default driver... if driver != "none" { c.App.Commands, err = trimDriverFlags(driver, c.App.Commands) if err != nil { log.Fatal(err) } } if name == "" { cli.ShowCommandHelp(c, "create") log.Fatal("You must specify a machine name") } if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil { log.Fatalf("Error parsing swarm discovery: %s", err) } certInfo := getCertPathInfo(c) if err := setupCertificates( certInfo.CaCertPath, certInfo.CaKeyPath, certInfo.ClientCertPath, certInfo.ClientKeyPath); err != nil { log.Fatalf("Error generating certificates: %s", err) } defaultStore, err := getDefaultStore( c.GlobalString("storage-path"), certInfo.CaCertPath, certInfo.CaKeyPath, ) if err != nil { log.Fatal(err) } provider, err := newProvider(defaultStore) if err != nil { log.Fatal(err) } hostOptions := &libmachine.HostOptions{ AuthOptions: &auth.AuthOptions{ CaCertPath: certInfo.CaCertPath, PrivateKeyPath: certInfo.CaKeyPath, ClientCertPath: certInfo.ClientCertPath, ClientKeyPath: certInfo.ClientKeyPath, ServerCertPath: filepath.Join(utils.GetMachineDir(), name, "server.pem"), ServerKeyPath: filepath.Join(utils.GetMachineDir(), name, "server-key.pem"), }, EngineOptions: &engine.EngineOptions{ ArbitraryFlags: c.StringSlice("engine-opt"), Env: c.StringSlice("engine-env"), InsecureRegistry: c.StringSlice("engine-insecure-registry"), Labels: c.StringSlice("engine-label"), RegistryMirror: c.StringSlice("engine-registry-mirror"), StorageDriver: c.String("engine-storage-driver"), TlsVerify: true, InstallURL: c.String("engine-install-url"), }, SwarmOptions: &swarm.SwarmOptions{ IsSwarm: c.Bool("swarm"), Image: c.String("swarm-image"), Master: c.Bool("swarm-master"), Discovery: c.String("swarm-discovery"), Address: c.String("swarm-addr"), Host: c.String("swarm-host"), Strategy: c.String("swarm-strategy"), ArbitraryFlags: c.StringSlice("swarm-opt"), }, } _, err = provider.Create(name, driver, hostOptions, c) if err != nil { log.Errorf("Error creating machine: %s", err) log.Fatal("You will want to check the provider to make sure the machine and associated resources were properly removed.") } info := fmt.Sprintf("%s env %s", c.App.Name, name) log.Infof("To see how to connect Docker to this machine, run: %s", info) }
func cmdUpgrade(c *cli.Context) { if err := runActionWithContext("upgrade", c); err != nil { log.Fatal(err) } }
func cmdLs(c *cli.Context) { quiet := c.Bool("quiet") filters, err := parseFilters(c.StringSlice("filter")) if err != nil { log.Fatal(err) } provider := getDefaultProvider(c) hostList, err := provider.List() if err != nil { log.Fatal(err) } hostList = filterHosts(hostList, filters) // Just print out the names if we're being quiet if quiet { for _, host := range hostList { fmt.Println(host.Name) } return } swarmMasters := make(map[string]string) swarmInfo := make(map[string]string) w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL\tSWARM") for _, host := range hostList { swarmOptions := host.HostOptions.SwarmOptions if swarmOptions.Master { swarmMasters[swarmOptions.Discovery] = host.Name } if swarmOptions.Discovery != "" { swarmInfo[host.Name] = swarmOptions.Discovery } } items := libmachine.GetHostListItems(hostList) sortHostListItemsByName(items) for _, item := range items { activeString := "" if item.Active { activeString = "*" } swarmInfo := "" if item.SwarmOptions.Discovery != "" { swarmInfo = swarmMasters[item.SwarmOptions.Discovery] if item.SwarmOptions.Master { swarmInfo = fmt.Sprintf("%s (master)", swarmInfo) } } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", item.Name, activeString, item.DriverName, item.State, item.URL, swarmInfo) } w.Flush() }
func cmdStop(c *cli.Context) { if err := runActionWithContext("stop", c); err != nil { log.Fatal(err) } }