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 }