docs/commands.go

1188 lines
28 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
"github.com/skarademir/naturalsort"
"github.com/docker/machine/drivers"
_ "github.com/docker/machine/drivers/amazonec2"
_ "github.com/docker/machine/drivers/azure"
_ "github.com/docker/machine/drivers/digitalocean"
_ "github.com/docker/machine/drivers/google"
_ "github.com/docker/machine/drivers/hyperv"
_ "github.com/docker/machine/drivers/none"
_ "github.com/docker/machine/drivers/openstack"
_ "github.com/docker/machine/drivers/rackspace"
_ "github.com/docker/machine/drivers/softlayer"
_ "github.com/docker/machine/drivers/virtualbox"
_ "github.com/docker/machine/drivers/vmwarefusion"
_ "github.com/docker/machine/drivers/vmwarevcloudair"
_ "github.com/docker/machine/drivers/vmwarevsphere"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/state"
"github.com/docker/machine/utils"
)
type machineConfig struct {
machineName string
machineDir string
machineUrl string
clientKeyPath string
serverCertPath string
clientCertPath string
caCertPath string
caKeyPath string
serverKeyPath string
AuthOptions auth.AuthOptions
SwarmOptions swarm.SwarmOptions
}
type hostListItem struct {
Name string
Active bool
DriverName string
State state.State
URL string
SwarmOptions swarm.SwarmOptions
}
func sortHostListItemsByName(items []hostListItem) {
m := make(map[string]hostListItem, len(items))
s := make([]string, len(items))
for i, v := range items {
name := strings.ToLower(v.Name)
m[name] = v
s[i] = name
}
sort.Sort(naturalsort.NaturalSort(s))
for i, v := range s {
items[i] = m[v]
}
}
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 newMcn(store libmachine.Store) (*libmachine.Machine, error) {
return libmachine.New(store)
}
func getMachineDir(rootPath string) string {
return filepath.Join(rootPath, "machines")
}
func getDefaultStore(rootPath, caCertPath, privateKeyPath string) (libmachine.Store, error) {
return libmachine.NewFilestore(
rootPath,
caCertPath,
privateKeyPath,
), nil
}
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
}
var Commands = []cli.Command{
{
Name: "active",
Usage: "Get or set the active machine",
Action: cmdActive,
},
{
Flags: append(
drivers.GetCreateFlags(),
cli.StringFlag{
Name: "driver, d",
Usage: fmt.Sprintf(
"Driver to create machine with. Available drivers: %s",
strings.Join(drivers.GetDriverNames(), ", "),
),
Value: "none",
},
cli.BoolFlag{
Name: "swarm",
Usage: "Configure Machine with Swarm",
},
cli.BoolFlag{
Name: "swarm-master",
Usage: "Configure Machine to be a Swarm master",
},
cli.StringFlag{
Name: "swarm-discovery",
Usage: "Discovery service to use with Swarm",
Value: "",
},
cli.StringFlag{
Name: "swarm-host",
Usage: "ip/socket to listen on for Swarm master",
Value: "tcp://0.0.0.0:3376",
},
cli.StringFlag{
Name: "swarm-addr",
Usage: "addr to advertise for Swarm (default: detect and use the machine IP)",
Value: "",
},
),
Name: "create",
Usage: "Create a machine",
Action: cmdCreate,
},
{
Name: "config",
Usage: "Print the connection config for machine",
Description: "Argument is a machine name. Will use the active machine if none is provided.",
Action: cmdConfig,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "swarm",
Usage: "Display the Swarm config instead of the Docker daemon",
},
},
},
{
Name: "inspect",
Usage: "Inspect information about a machine",
Description: "Argument is a machine name. Will use the active machine if none is provided.",
Action: cmdInspect,
},
{
Name: "ip",
Usage: "Get the IP address of a machine",
Description: "Argument is a machine name. Will use the active machine if none is provided.",
Action: cmdIp,
},
{
Name: "kill",
Usage: "Kill a machine",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdKill,
},
{
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "Enable quiet mode",
},
},
Name: "ls",
Usage: "List machines",
Action: cmdLs,
},
{
Name: "regenerate-certs",
Usage: "Regenerate TLS Certificates for a machine",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdRegenerateCerts,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "Force rebuild and do not prompt",
},
},
},
{
Name: "restart",
Usage: "Restart a machine",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdRestart,
},
{
Flags: []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "Remove local configuration even if machine cannot be removed",
},
},
Name: "rm",
Usage: "Remove a machine",
Description: "Argument(s) are one or more machine names.",
Action: cmdRm,
},
{
Name: "env",
Usage: "Display the commands to set up the environment for the Docker client",
Description: "Argument is a machine name. Will use the active machine if none is provided.",
Action: cmdEnv,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "swarm",
Usage: "Display the Swarm config instead of the Docker daemon",
},
cli.BoolFlag{
Name: "unset, u",
Usage: "Unset variables instead of setting them",
},
},
},
{
Name: "ssh",
Usage: "Log into or run a command on a machine with SSH",
Description: "Arguments are [machine-name] command - Will use the active machine if none is provided.",
Action: cmdSsh,
},
{
Name: "start",
Usage: "Start a machine",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdStart,
},
{
Name: "stop",
Usage: "Stop a machine",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdStop,
},
{
Name: "upgrade",
Usage: "Upgrade a machine to the latest version of Docker",
Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.",
Action: cmdUpgrade,
},
{
Name: "url",
Usage: "Get the URL of a machine",
Description: "Argument is a machine name. Will use the active machine if none is provided.",
Action: cmdUrl,
},
}
func cmdActive(c *cli.Context) {
name := c.Args().First()
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)
}
if name == "" {
host, err := mcn.GetActive()
if err != nil {
log.Fatalf("error getting active host: %v", err)
}
if host != nil {
fmt.Println(host.Name)
}
} else if name != "" {
host, err := mcn.Get(name)
if err != nil {
log.Fatalf("error loading host: %v", err)
}
if err := mcn.SetActive(host); err != nil {
log.Fatalf("error setting active host: %v", err)
}
} else {
cli.ShowCommandHelp(c, "active")
}
}
func cmdCreate(c *cli.Context) {
driver := c.String("driver")
name := c.Args().First()
if name == "" {
cli.ShowCommandHelp(c, "create")
log.Fatal("You must specify a machine name")
}
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)
}
mcn, err := newMcn(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{},
SwarmOptions: &swarm.SwarmOptions{
IsSwarm: c.Bool("swarm"),
Master: c.Bool("swarm-master"),
Discovery: c.String("swarm-discovery"),
Address: c.String("swarm-addr"),
Host: c.String("swarm-host"),
},
}
host, err := mcn.Create(name, driver, hostOptions, c)
if err != nil {
log.Errorf("Error creating machine: %s", err)
log.Warn("You will want to check the provider to make sure the machine and associated resources were properly removed.")
log.Fatal("Error creating machine")
}
if err := mcn.SetActive(host); err != nil {
log.Fatalf("error setting active host: %v", err)
}
info := ""
userShell := filepath.Base(os.Getenv("SHELL"))
switch userShell {
case "fish":
info = fmt.Sprintf("%s env %s | source", c.App.Name, name)
default:
info = fmt.Sprintf(`eval "$(%s env %s)"`, c.App.Name, name)
}
log.Infof("%q has been created and is now the active machine.", name)
if info != "" {
log.Infof("To point your Docker client at it, run this in your shell: %s", info)
}
}
func cmdConfig(c *cli.Context) {
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", 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",
cfg.caCertPath, cfg.clientCertPath, cfg.clientKeyPath, dockerHost)
}
func cmdInspect(c *cli.Context) {
prettyJSON, err := json.MarshalIndent(getHost(c), "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(prettyJSON))
}
func cmdIp(c *cli.Context) {
ip, err := getHost(c).Driver.GetIP()
if err != nil {
log.Fatal(err)
}
fmt.Println(ip)
}
func cmdLs(c *cli.Context) {
quiet := c.Bool("quiet")
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)
}
hostList, err := mcn.List()
if err != nil {
log.Fatal(err)
}
w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
if !quiet {
fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL\tSWARM")
}
items := []hostListItem{}
hostListItems := make(chan hostListItem)
swarmMasters := make(map[string]string)
swarmInfo := make(map[string]string)
for _, host := range hostList {
swarmOptions := host.HostOptions.SwarmOptions
if !quiet {
if swarmOptions.Master {
swarmMasters[swarmOptions.Discovery] = host.Name
}
if swarmOptions.Discovery != "" {
swarmInfo[host.Name] = swarmOptions.Discovery
}
go getHostState(*host, defaultStore, hostListItems)
} else {
fmt.Fprintf(w, "%s\n", host.Name)
}
}
if !quiet {
for i := 0; i < len(hostList); i++ {
items = append(items, <-hostListItems)
}
}
close(hostListItems)
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 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 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
}
}
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.")
}
log.Print("The machine was successfully removed.")
}
func cmdEnv(c *cli.Context) {
userShell := filepath.Base(os.Getenv("SHELL"))
if c.Bool("unset") {
switch userShell {
case "fish":
fmt.Printf("set -e DOCKER_TLS_VERIFY;\nset -e DOCKER_CERT_PATH;\nset -e DOCKER_HOST;\n")
default:
fmt.Println("unset DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_HOST")
}
return
}
cfg, err := getMachineConfig(c)
if err != nil {
log.Fatal(err)
}
if cfg.machineUrl == "" {
log.Fatalf("%s is not running. Please start this with docker-machine start %s", cfg.machineName, 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)
}
}
}
usageHint := generateUsageHint(c.Args().First(), userShell)
switch userShell {
case "fish":
fmt.Printf("set -x DOCKER_TLS_VERIFY 1;\nset -x DOCKER_CERT_PATH %q;\nset -x DOCKER_HOST %s;\n\n%s\n",
cfg.machineDir, dockerHost, usageHint)
default:
fmt.Printf("export DOCKER_TLS_VERIFY=1\nexport DOCKER_CERT_PATH=%q\nexport DOCKER_HOST=%s\n\n%s\n",
cfg.machineDir, dockerHost, usageHint)
}
}
func generateUsageHint(machineName string, userShell string) string {
cmd := ""
switch userShell {
case "fish":
if machineName != "" {
cmd = fmt.Sprintf("eval (docker-machine env %s)", machineName)
} else {
cmd = "eval (docker-machine env)"
}
default:
if machineName != "" {
cmd = fmt.Sprintf("eval \"$(docker-machine env %s)\"", machineName)
} else {
cmd = "eval \"$(docker-machine env)\""
}
}
return fmt.Sprintf("# Run this command to configure your shell: %s\n", cmd)
}
func cmdSsh(c *cli.Context) {
var (
err error
sshCmd *exec.Cmd
)
name := c.Args().First()
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)
}
if name == "" {
host, err := mcn.GetActive()
if err != nil {
log.Fatalf("unable to get active host: %v", err)
}
if host == nil {
log.Fatalf("There is no active host. Please set it with %s active <machine name>.", c.App.Name)
}
name = host.Name
}
host, err := mcn.Get(name)
if err != nil {
log.Fatal(err)
}
_, err = host.GetURL()
if err != nil {
if err == drivers.ErrHostIsNotRunning {
log.Fatalf("%s is not running. Please start this with docker-machine start %s", host.Name, host.Name)
} else {
log.Fatalf("Unexpected error getting machine url: %s", err)
}
}
if len(c.Args()) <= 1 {
sshCmd, err = host.GetSSHCommand()
} else {
sshCmd, err = host.GetSSHCommand(c.Args()[1:]...)
}
if err != nil {
log.Fatal(err)
}
sshCmd.Stdin = os.Stdin
sshCmd.Stdout = os.Stdout
sshCmd.Stderr = os.Stderr
if err := sshCmd.Run(); err != nil {
log.Fatal(err)
}
}
// machineCommand maps the command name to the corresponding machine command.
// We run commands concurrently and communicate back an error if there was one.
func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- error) {
commands := map[string](func() error){
"configureAuth": host.ConfigureAuth,
"start": host.Start,
"stop": host.Stop,
"restart": host.Restart,
"kill": host.Kill,
"upgrade": host.Upgrade,
}
log.Debugf("command=%s machine=%s", actionName, host.Name)
if err := commands[actionName](); err != nil {
errorChan <- err
return
}
errorChan <- nil
}
// runActionForeachMachine will run the command across multiple machines
func runActionForeachMachine(actionName string, machines []*libmachine.Host) {
var (
numConcurrentActions = 0
serialMachines = []*libmachine.Host{}
errorChan = make(chan error)
)
for _, machine := range machines {
// Virtualbox is temperamental about doing things concurrently,
// so we schedule the actions in a "queue" to be executed serially
// after the concurrent actions are scheduled.
switch machine.DriverName {
case "virtualbox":
machine := machine
serialMachines = append(serialMachines, machine)
default:
numConcurrentActions++
go machineCommand(actionName, machine, errorChan)
}
}
// While the concurrent actions are running,
// do the serial actions. As the name implies,
// these run one at a time.
for _, machine := range serialMachines {
serialChan := make(chan error)
go machineCommand(actionName, machine, serialChan)
if err := <-serialChan; err != nil {
log.Errorln(err)
}
close(serialChan)
}
// TODO: We should probably only do 5-10 of these
// at a time, since otherwise cloud providers might
// rate limit us.
for i := 0; i < numConcurrentActions; i++ {
if err := <-errorChan; err != nil {
log.Errorln(err)
}
}
close(errorChan)
}
func runActionWithContext(actionName string, c *cli.Context) error {
machines, err := getHosts(c)
if err != nil {
return err
}
// No args specified, so use active.
if len(machines) == 0 {
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)
}
activeHost, err := mcn.GetActive()
if err != nil {
log.Fatalf("Unable to get active host: %v", err)
}
if activeHost == nil {
log.Fatalf("There is no active host. Please set it with %s active <machine name>.", c.App.Name)
}
machines = []*libmachine.Host{activeHost}
}
runActionForeachMachine(actionName, machines)
return nil
}
func cmdStart(c *cli.Context) {
if err := runActionWithContext("start", c); err != nil {
log.Fatal(err)
}
}
func cmdStop(c *cli.Context) {
if err := runActionWithContext("stop", c); err != nil {
log.Fatal(err)
}
}
func cmdRestart(c *cli.Context) {
if err := runActionWithContext("restart", c); err != nil {
log.Fatal(err)
}
}
func cmdKill(c *cli.Context) {
if err := runActionWithContext("kill", c); err != nil {
log.Fatal(err)
}
}
func cmdUpgrade(c *cli.Context) {
if err := runActionWithContext("upgrade", c); err != nil {
log.Fatal(err)
}
}
func cmdUrl(c *cli.Context) {
url, err := getHost(c).GetURL()
if err != nil {
log.Fatal(err)
}
fmt.Println(url)
}
func cmdNotFound(c *cli.Context, command string) {
log.Fatalf(
"%s: '%s' is not a %s command. See '%s --help'.",
c.App.Name,
command,
c.App.Name,
c.App.Name,
)
}
func getHosts(c *cli.Context) ([]*libmachine.Host, error) {
machines := []*libmachine.Host{}
for _, n := range c.Args() {
machine, err := loadMachine(n, c)
if err != nil {
return nil, err
}
machines = append(machines, machine)
}
return machines, nil
}
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)
}
mcn, err := newMcn(defaultStore)
if err != nil {
log.Fatal(err)
}
host, err := mcn.Get(name)
if err != nil {
return nil, err
}
return host, 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)
}
mcn, err := newMcn(defaultStore)
if err != nil {
log.Fatal(err)
}
if name == "" {
host, err := mcn.GetActive()
if err != nil {
log.Fatalf("unable to get active host: %v", err)
}
if host == nil {
log.Fatal("unable to get active host, active file not found")
}
return host
}
host, err := mcn.Get(name)
if err != nil {
log.Fatalf("unable to load host: %v", err)
}
return host
}
func getHostState(host libmachine.Host, store libmachine.Store, hostListItems chan<- hostListItem) {
currentState, err := host.Driver.GetState()
if err != nil {
log.Errorf("error getting state for host %s: %s", host.Name, err)
}
url, err := host.GetURL()
if err != nil {
if err == drivers.ErrHostIsNotRunning {
url = ""
} else {
log.Errorf("error getting URL for host %s: %s", host.Name, err)
}
}
isActive, err := store.IsActive(&host)
if err != nil {
log.Debugf("error determining whether host %q is active: %s",
host.Name, err)
}
hostListItems <- hostListItem{
Name: host.Name,
Active: isActive,
DriverName: host.Driver.DriverName(),
State: currentState,
URL: url,
SwarmOptions: *host.HostOptions.SwarmOptions,
}
}
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)
}
mcn, err := newMcn(defaultStore)
if err != nil {
log.Fatal(err)
}
var machine *libmachine.Host
if name == "" {
m, err := mcn.GetActive()
if err != nil {
log.Fatalf("error getting active host: %v", err)
}
if m == nil {
return nil, fmt.Errorf("There is no active host")
}
machine = m
} else {
m, err := mcn.Get(name)
if err != nil {
return nil, fmt.Errorf("Error loading machine config: %s", err)
}
machine = m
}
machineDir := filepath.Join(utils.GetMachineDir(), machine.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 := machine.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: *machine.HostOptions.AuthOptions,
SwarmOptions: *machine.HostOptions.SwarmOptions,
}, nil
}
// getCertPaths returns the cert paths
// 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 getCertPathInfo(c *cli.Context) libmachine.CertPathInfo {
// setup cert paths
caCertPath := c.GlobalString("tls-ca-cert")
caKeyPath := c.GlobalString("tls-ca-key")
clientCertPath := c.GlobalString("tls-client-cert")
clientKeyPath := c.GlobalString("tls-client-key")
if caCertPath == "" {
caCertPath = filepath.Join(utils.GetMachineCertDir(), "ca.pem")
}
if caKeyPath == "" {
caKeyPath = filepath.Join(utils.GetMachineCertDir(), "ca-key.pem")
}
if clientCertPath == "" {
clientCertPath = filepath.Join(utils.GetMachineCertDir(), "cert.pem")
}
if clientKeyPath == "" {
clientKeyPath = filepath.Join(utils.GetMachineCertDir(), "key.pem")
}
return libmachine.CertPathInfo{
CaCertPath: caCertPath,
CaKeyPath: caKeyPath,
ClientCertPath: clientCertPath,
ClientKeyPath: clientKeyPath,
}
}