github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/deisctl/deisctl.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/deis/deis/deisctl/backend/fleet"
    12  	"github.com/deis/deis/deisctl/client"
    13  	"github.com/deis/deis/pkg/prettyprint"
    14  	"github.com/deis/deis/version"
    15  
    16  	docopt "github.com/docopt/docopt-go"
    17  )
    18  
    19  // main exits with the return value of Command(os.Args[1:]), deferring all logic to
    20  // a func we can test.
    21  func main() {
    22  	os.Exit(Command(nil))
    23  }
    24  
    25  // Command executes the given deisctl command line.
    26  func Command(argv []string) int {
    27  	deisctlMotd := prettyprint.DeisIfy("Deis Control Utility")
    28  	usage := deisctlMotd + `
    29  Usage: deisctl [options] <command> [<args>...]
    30  
    31  Commands, use "deisctl help <command>" to learn more:
    32    install           install components, or the entire platform
    33    uninstall         uninstall components
    34    list              list installed components
    35    start             start components
    36    stop              stop components
    37    restart           stop, then start components
    38    status            view status of components
    39    scale             grow or shrink the number of routers, registries or store gateways
    40    journal           print the log output of a component
    41    config            set platform or component values
    42    refresh-units     refresh unit files from GitHub
    43    ssh               open an interactive shell on a machine in the cluster
    44    dock              open an interactive shell on a container in the cluster
    45    help              show the help screen for a command
    46    upgrade-prep      prepare a running cluster for upgrade
    47    upgrade-takeover  allow an upgrade to gracefully takeover a running cluster
    48    rolling-restart   perform a rolling restart of a Deis component (currently only router is supported)
    49  
    50  Options:
    51    -h --help                   show this help screen
    52    --endpoint=<url>            etcd endpoint for fleet [default: http://127.0.0.1:4001]
    53    --etcd-cafile=<path>        etcd CA file authentication [default: ]
    54    --etcd-certfile=<path>      etcd cert file authentication [default: ]
    55    --etcd-key-prefix=<path>    keyspace for fleet data in etcd [default: /_coreos.com/fleet/]
    56    --etcd-keyfile=<path>       etcd key file authentication [default: ]
    57    --known-hosts-file=<path>   where to store remote fingerprints [default: ~/.ssh/known_hosts]
    58    --request-timeout=<secs>    seconds before a request is considered failed [default: 10.0]
    59    --ssh-timeout=<secs>        seconds before SSH connection is considered failed [default: 10.0]
    60    --strict-host-key-checking  verify SSH host keys [default: true]
    61    --tunnel=<host>             SSH tunnel for communication with fleet and etcd [default: ]
    62    --version                   print the version of deisctl
    63  `
    64  	// pre-parse command-line arguments
    65  	argv, helpFlag := parseArgs(argv)
    66  	// give docopt an optional final false arg so it doesn't call os.Exit()
    67  	args, err := docopt.Parse(usage, argv, false, version.Version, true, false)
    68  
    69  	if err != nil && err.Error() != "" {
    70  		fmt.Println(err)
    71  		return 1
    72  	}
    73  
    74  	if len(args) == 0 {
    75  		return 0
    76  	}
    77  
    78  	command := args["<command>"]
    79  	setTunnel := true
    80  	// "--help" and "refresh-units" doesn't need SSH tunneling
    81  	if helpFlag || command == "refresh-units" {
    82  		setTunnel = false
    83  	}
    84  	setGlobalFlags(args, setTunnel)
    85  	// clean up the args so subcommands don't need to reparse them
    86  	argv = removeGlobalArgs(argv)
    87  	// construct a client
    88  	c, err := client.NewClient("fleet")
    89  	if err != nil {
    90  		fmt.Printf("Error: %v\n", err)
    91  		return 1
    92  	}
    93  	// Dispatch the command, passing the argv through so subcommands can
    94  	// re-parse it according to their usage strings.
    95  	switch command {
    96  	case "list":
    97  		err = c.List(argv)
    98  	case "scale":
    99  		err = c.Scale(argv)
   100  	case "start":
   101  		err = c.Start(argv)
   102  	case "restart":
   103  		err = c.Restart(argv)
   104  	case "stop":
   105  		err = c.Stop(argv)
   106  	case "status":
   107  		err = c.Status(argv)
   108  	case "journal":
   109  		err = c.Journal(argv)
   110  	case "install":
   111  		err = c.Install(argv)
   112  	case "uninstall":
   113  		err = c.Uninstall(argv)
   114  	case "config":
   115  		err = c.Config(argv)
   116  	case "refresh-units":
   117  		err = c.RefreshUnits(argv)
   118  	case "ssh":
   119  		err = c.SSH(argv)
   120  	case "dock":
   121  		err = c.Dock(argv)
   122  	case "upgrade-prep":
   123  		err = c.UpgradePrep(argv)
   124  	case "upgrade-takeover":
   125  		err = c.UpgradeTakeover(argv)
   126  	case "rolling-restart":
   127  		err = c.RollingRestart(argv)
   128  	case "help":
   129  		fmt.Print(usage)
   130  		return 0
   131  	default:
   132  		fmt.Println(`Found no matching command, try "deisctl help"
   133  Usage: deisctl <command> [<args>...] [options]`)
   134  		return 1
   135  	}
   136  	if err != nil {
   137  		fmt.Printf("Error: %v\n", err)
   138  		return 1
   139  	}
   140  	return 0
   141  }
   142  
   143  // isGlobalArg returns true if a string looks like it is a global deisctl option flag,
   144  // such as "--tunnel".
   145  func isGlobalArg(arg string) bool {
   146  	prefixes := []string{
   147  		"--endpoint=",
   148  		"--etcd-key-prefix=",
   149  		"--etcd-keyfile=",
   150  		"--etcd-certfile=",
   151  		"--etcd-cafile=",
   152  		// "--experimental-api=",
   153  		"--known-hosts-file=",
   154  		"--request-timeout=",
   155  		"--ssh-timeout=",
   156  		"--strict-host-key-checking=",
   157  		"--tunnel=",
   158  	}
   159  	for _, p := range prefixes {
   160  		if strings.HasPrefix(arg, p) {
   161  			return true
   162  		}
   163  	}
   164  	return false
   165  }
   166  
   167  // parseArgs returns the provided args with "--help" as the last arg if need be,
   168  // and a boolean to indicate whether help was requested.
   169  func parseArgs(argv []string) ([]string, bool) {
   170  	if argv == nil {
   171  		argv = os.Args[1:]
   172  	}
   173  
   174  	if len(argv) == 1 {
   175  		// rearrange "deisctl --help" as "deisctl help"
   176  		if argv[0] == "--help" || argv[0] == "-h" {
   177  			argv[0] = "help"
   178  		}
   179  	}
   180  
   181  	if len(argv) >= 2 {
   182  		// rearrange "deisctl help <command>" as "deisctl <command> --help"
   183  		if argv[0] == "help" || argv[0] == "--help" || argv[0] == "-h" {
   184  			argv = append(argv[1:], "--help")
   185  		}
   186  	}
   187  
   188  	helpFlag := false
   189  	for _, a := range argv {
   190  		if a == "help" || a == "--help" || a == "-h" {
   191  			helpFlag = true
   192  			break
   193  		}
   194  	}
   195  
   196  	return argv, helpFlag
   197  }
   198  
   199  // removeGlobalArgs returns the given args without any global option flags, to make
   200  // re-parsing by subcommands easier.
   201  func removeGlobalArgs(argv []string) []string {
   202  	var v []string
   203  	for _, a := range argv {
   204  		if !isGlobalArg(a) {
   205  			v = append(v, a)
   206  		}
   207  	}
   208  	return v
   209  }
   210  
   211  // setGlobalFlags sets fleet provider options based on deisctl global flags.
   212  func setGlobalFlags(args map[string]interface{}, setTunnel bool) {
   213  	fleet.Flags.Endpoint = args["--endpoint"].(string)
   214  	fleet.Flags.EtcdKeyPrefix = args["--etcd-key-prefix"].(string)
   215  	fleet.Flags.EtcdKeyFile = args["--etcd-keyfile"].(string)
   216  	fleet.Flags.EtcdCertFile = args["--etcd-certfile"].(string)
   217  	fleet.Flags.EtcdCAFile = args["--etcd-cafile"].(string)
   218  	//fleet.Flags.UseAPI = args["--experimental-api"].(bool)
   219  	fleet.Flags.KnownHostsFile = args["--known-hosts-file"].(string)
   220  	fleet.Flags.StrictHostKeyChecking = args["--strict-host-key-checking"].(bool)
   221  	timeout, _ := strconv.ParseFloat(args["--request-timeout"].(string), 64)
   222  	fleet.Flags.RequestTimeout = timeout
   223  	sshTimeout, _ := strconv.ParseFloat(args["--ssh-timeout"].(string), 64)
   224  	fleet.Flags.SSHTimeout = sshTimeout
   225  	if setTunnel == true {
   226  		tunnel := args["--tunnel"].(string)
   227  		if tunnel != "" {
   228  			fleet.Flags.Tunnel = tunnel
   229  		} else {
   230  			fleet.Flags.Tunnel = os.Getenv("DEISCTL_TUNNEL")
   231  		}
   232  	}
   233  }