github.com/mgood/deis@v1.0.2-0.20141120022609-9a185b756e7d/deisctl/deisctl.go (about)

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