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