github.com/chasestarr/deis@v1.13.5-0.20170519182049-1d9e59fbdbfc/client/deis.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"strings"
     8  	"syscall"
     9  
    10  	"github.com/deis/deis/client/parser"
    11  	docopt "github.com/docopt/docopt-go"
    12  )
    13  
    14  const extensionPrefix = "deis-"
    15  
    16  // main exits with the return value of Command(os.Args[1:]), deferring all logic to
    17  // a func we can test.
    18  func main() {
    19  	os.Exit(Command(os.Args[1:]))
    20  }
    21  
    22  // Command routes deis commands to their proper parser.
    23  func Command(argv []string) int {
    24  	usage := `
    25  The Deis command-line client issues API calls to a Deis controller.
    26  
    27  Usage: deis <command> [<args>...]
    28  
    29  Option flags::
    30  
    31    -h --help     display help information
    32    -v --version  display client version
    33  
    34  Auth commands::
    35  
    36    register      register a new user with a controller
    37    login         login to a controller
    38    logout        logout from the current controller
    39  
    40  Subcommands, use 'deis help [subcommand]' to learn more::
    41  
    42    apps          manage applications used to provide services
    43    ps            manage processes inside an app container
    44    config        manage environment variables that define app config
    45    domains       manage and assign domain names to your applications
    46    builds        manage builds created using 'git push'
    47    limits        manage resource limits for your application
    48    tags          manage tags for application containers
    49    releases      manage releases of an application
    50    certs         manage SSL endpoints for an app
    51  
    52    keys          manage ssh keys used for 'git push' deployments
    53    perms         manage permissions for applications
    54    git           manage git for applications
    55    users         manage users
    56    version       display client version
    57  
    58  Shortcut commands, use 'deis shortcuts' to see all::
    59  
    60    create        create a new application
    61    scale         scale processes by type (web=2, worker=1)
    62    info          view information about the current app
    63    open          open a URL to the app in a browser
    64    logs          view aggregated log info for the app
    65    run           run a command in an ephemeral app container
    66    destroy       destroy an application
    67    pull          imports an image and deploys as a new release
    68  
    69  Use 'git push deis master' to deploy to an application.
    70  `
    71  	// Reorganize some command line flags and commands.
    72  	command, argv := parseArgs(argv)
    73  	// Give docopt an optional final false arg so it doesn't call os.Exit().
    74  	_, err := docopt.Parse(usage, []string{command}, false, "", true, false)
    75  
    76  	if err != nil {
    77  		fmt.Fprintln(os.Stderr, err)
    78  		return 1
    79  	}
    80  
    81  	if len(argv) == 0 {
    82  		fmt.Fprintln(os.Stderr, "Usage: deis <command> [<args>...]")
    83  		return 1
    84  	}
    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 "auth":
    90  		err = parser.Auth(argv)
    91  	case "ps":
    92  		err = parser.Ps(argv)
    93  	case "apps":
    94  		err = parser.Apps(argv)
    95  	case "config":
    96  		err = parser.Config(argv)
    97  	case "domains":
    98  		err = parser.Domains(argv)
    99  	case "builds":
   100  		err = parser.Builds(argv)
   101  	case "limits":
   102  		err = parser.Limits(argv)
   103  	case "tags":
   104  		err = parser.Tags(argv)
   105  	case "releases":
   106  		err = parser.Releases(argv)
   107  	case "certs":
   108  		err = parser.Certs(argv)
   109  	case "keys":
   110  		err = parser.Keys(argv)
   111  	case "perms":
   112  		err = parser.Perms(argv)
   113  	case "git":
   114  		err = parser.Git(argv)
   115  	case "users":
   116  		err = parser.Users(argv)
   117  	case "version":
   118  		err = parser.Version(argv)
   119  	case "help":
   120  		fmt.Print(usage)
   121  		return 0
   122  	default:
   123  		env := os.Environ()
   124  
   125  		binary, err := exec.LookPath(extensionPrefix + command)
   126  		if err != nil {
   127  			parser.PrintUsage()
   128  			return 1
   129  		}
   130  
   131  		cmdArgv := prepareCmdArgs(command, argv)
   132  
   133  		err = syscall.Exec(binary, cmdArgv, env)
   134  		if err != nil {
   135  			parser.PrintUsage()
   136  			return 1
   137  		}
   138  	}
   139  	if err != nil {
   140  		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
   141  		return 1
   142  	}
   143  	return 0
   144  }
   145  
   146  // parseArgs returns the provided args with "--help" as the last arg if need be,
   147  // expands shortcuts and formats commands to be properly routed.
   148  func parseArgs(argv []string) (string, []string) {
   149  	if len(argv) == 1 {
   150  		if argv[0] == "--help" || argv[0] == "-h" {
   151  			// rearrange "deis --help" as "deis help"
   152  			argv[0] = "help"
   153  		} else if argv[0] == "--version" || argv[0] == "-v" {
   154  			// rearrange "deis --version" as "deis version"
   155  			argv[0] = "version"
   156  		}
   157  	}
   158  
   159  	if len(argv) >= 2 {
   160  		// Rearrange "deis help <command>" to "deis <command> --help".
   161  		if argv[0] == "help" || argv[0] == "--help" || argv[0] == "-h" {
   162  			argv = append(argv[1:], "--help")
   163  		}
   164  	}
   165  
   166  	if len(argv) > 0 {
   167  		argv[0] = replaceShortcut(argv[0])
   168  
   169  		index := strings.Index(argv[0], ":")
   170  
   171  		if index != -1 {
   172  			command := argv[0]
   173  			return command[:index], argv
   174  		}
   175  
   176  		return argv[0], argv
   177  	}
   178  
   179  	return "", argv
   180  }
   181  
   182  // split original command and pass its first element in arguments
   183  func prepareCmdArgs(command string, argv []string) []string {
   184  	cmdArgv := []string{extensionPrefix + command}
   185  	cmdSplit := strings.Split(argv[0], command+":")
   186  
   187  	if len(cmdSplit) > 1 {
   188  		cmdArgv = append(cmdArgv, cmdSplit[1])
   189  	}
   190  
   191  	return append(cmdArgv, argv[1:]...)
   192  }
   193  
   194  func replaceShortcut(command string) string {
   195  	shortcuts := map[string]string{
   196  		"create":         "apps:create",
   197  		"destroy":        "apps:destroy",
   198  		"info":           "apps:info",
   199  		"login":          "auth:login",
   200  		"logout":         "auth:logout",
   201  		"logs":           "apps:logs",
   202  		"open":           "apps:open",
   203  		"passwd":         "auth:passwd",
   204  		"pull":           "builds:create",
   205  		"register":       "auth:register",
   206  		"rollback":       "releases:rollback",
   207  		"run":            "apps:run",
   208  		"scale":          "ps:scale",
   209  		"sharing":        "perms:list",
   210  		"sharing:list":   "perms:list",
   211  		"sharing:add":    "perms:create",
   212  		"sharing:remove": "perms:delete",
   213  		"whoami":         "auth:whoami",
   214  	}
   215  
   216  	expandedCommand := shortcuts[command]
   217  	if expandedCommand == "" {
   218  		return command
   219  	}
   220  
   221  	return expandedCommand
   222  }