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