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