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 }