github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/deisctl/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  
     9  	"github.com/deis/deis/deisctl/backend"
    10  	"github.com/deis/deis/deisctl/backend/fleet"
    11  	"github.com/deis/deis/deisctl/cmd"
    12  	"github.com/deis/deis/deisctl/config"
    13  	"github.com/deis/deis/deisctl/config/etcd"
    14  	"github.com/deis/deis/deisctl/units"
    15  
    16  	docopt "github.com/docopt/docopt-go"
    17  )
    18  
    19  // DeisCtlClient manages Deis components, configuration, and related tasks.
    20  type DeisCtlClient interface {
    21  	Config(argv []string) error
    22  	Install(argv []string) error
    23  	Journal(argv []string) error
    24  	List(argv []string) error
    25  	RefreshUnits(argv []string) error
    26  	Restart(argv []string) error
    27  	Scale(argv []string) error
    28  	SSH(argv []string) error
    29  	Start(argv []string) error
    30  	Status(argv []string) error
    31  	Stop(argv []string) error
    32  	Uninstall(argv []string) error
    33  	UpgradePrep(argv []string) error
    34  	UpgradeTakeover(argv []string) error
    35  	RollingRestart(argv []string) error
    36  }
    37  
    38  // Client uses a backend to implement the DeisCtlClient interface.
    39  type Client struct {
    40  	Backend       backend.Backend
    41  	configBackend config.Backend
    42  }
    43  
    44  // NewClient returns a Client using the requested backend.
    45  // The only backend currently supported is "fleet".
    46  func NewClient(requestedBackend string) (*Client, error) {
    47  	var backend backend.Backend
    48  
    49  	cb, err := etcd.NewConfigBackend()
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if requestedBackend == "" {
    55  		requestedBackend = "fleet"
    56  	}
    57  
    58  	switch requestedBackend {
    59  	case "fleet":
    60  		b, err := fleet.NewClient(cb)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  		backend = b
    65  	default:
    66  		return nil, errors.New("invalid backend")
    67  	}
    68  
    69  	return &Client{Backend: backend, configBackend: cb}, nil
    70  }
    71  
    72  // UpgradePrep prepares a running cluster to be upgraded
    73  func (c *Client) UpgradePrep(argv []string) error {
    74  	usage := `Prepare platform for graceful upgrade.
    75  
    76  Usage:
    77    deisctl upgrade-prep [options]
    78  `
    79  	if _, err := docopt.Parse(usage, argv, true, "", false); err != nil {
    80  		return err
    81  	}
    82  
    83  	return cmd.UpgradePrep(c.Backend)
    84  }
    85  
    86  // UpgradeTakeover gracefully restarts a cluster prepared with upgrade-prep
    87  func (c *Client) UpgradeTakeover(argv []string) error {
    88  	usage := `Complete the upgrade of a prepped cluster.
    89  
    90  Usage:
    91    deisctl upgrade-takeover [options]
    92  `
    93  	if _, err := docopt.Parse(usage, argv, true, "", false); err != nil {
    94  		return err
    95  	}
    96  
    97  	return cmd.UpgradeTakeover(c.Backend, c.configBackend)
    98  }
    99  
   100  // RollingRestart attempts a rolling restart of an instance unit
   101  func (c *Client) RollingRestart(argv []string) error {
   102  	usage := `Perform a rolling restart of an instance unit.
   103  
   104  Usage:
   105    deisctl rolling-restart <target>
   106  `
   107  	args, err := docopt.Parse(usage, argv, true, "", false)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	return cmd.RollingRestart(args["<target>"].(string), c.Backend)
   113  }
   114  
   115  // Config gets or sets a configuration value from the cluster.
   116  //
   117  // A configuration value is stored and retrieved from a key/value store (in this case, etcd)
   118  // at /deis/<component>/<config>. Configuration values are typically used for component-level
   119  // configuration, such as enabling TLS for the routers.
   120  func (c *Client) Config(argv []string) error {
   121  	usage := `Gets or sets a configuration value from the cluster.
   122  
   123  A configuration value is stored and retrieved from a key/value store
   124  (in this case, etcd) at /deis/<component>/<config>. Configuration
   125  values are typically used for component-level configuration, such as
   126  enabling TLS for the routers.
   127  
   128  Note: "deisctl config platform set sshPrivateKey=" expects a path
   129  to a private key.
   130  
   131  Usage:
   132    deisctl config <target> get [<key>...]
   133    deisctl config <target> set <key=val>...
   134    deisctl config <target> rm [<key>...]
   135  
   136  Examples:
   137    deisctl config platform set domain=mydomain.com
   138    deisctl config platform set sshPrivateKey=$HOME/.ssh/deis
   139    deisctl config controller get webEnabled
   140    deisctl config controller rm webEnabled
   141  `
   142  	// parse command-line arguments
   143  	args, err := docopt.Parse(usage, argv, true, "", false)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	var action string
   149  	var key []string
   150  
   151  	switch {
   152  	case args["set"] == true:
   153  		action = "set"
   154  		key = args["<key=val>"].([]string)
   155  	case args["rm"] == true:
   156  		action = "rm"
   157  		key = args["<key>"].([]string)
   158  	default:
   159  		action = "get"
   160  		key = args["<key>"].([]string)
   161  	}
   162  
   163  	return cmd.Config(args["<target>"].(string), action, key, c.configBackend)
   164  }
   165  
   166  // Install loads the definitions of components from local unit files.
   167  // After Install, the components will be available to Start.
   168  func (c *Client) Install(argv []string) error {
   169  	usage := fmt.Sprintf(`Loads the definitions of components from local unit files.
   170  
   171  After install, the components will be available to start.
   172  
   173  "deisctl install" looks for unit files in these directories, in this order:
   174  - the $DEISCTL_UNITS environment variable, if set
   175  - $HOME/.deis/units
   176  - /var/lib/deis/units
   177  
   178  Usage:
   179    deisctl install [<target>...] [options]
   180  
   181  Options:
   182    --router-mesh-size=<num>  Number of routers to be loaded when installing the platform [default: %d].
   183  `, cmd.DefaultRouterMeshSize)
   184  	// parse command-line arguments
   185  	args, err := docopt.Parse(usage, argv, true, "", false)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	meshSizeArg, _ := args["--router-mesh-size"].(string)
   191  	parsedValue, err := strconv.ParseUint(meshSizeArg, 0, 8)
   192  	if err != nil || parsedValue < 1 {
   193  		fmt.Print("Error: argument --router-mesh-size: invalid value, make sure the value is an integer between 1 and 255.\n")
   194  		return err
   195  	}
   196  	cmd.RouterMeshSize = uint8(parsedValue)
   197  
   198  	return cmd.Install(args["<target>"].([]string), c.Backend, c.configBackend, cmd.CheckRequiredKeys)
   199  }
   200  
   201  // Journal prints log output for the specified components.
   202  func (c *Client) Journal(argv []string) error {
   203  	usage := `Prints log output for the specified components.
   204  
   205  Usage:
   206    deisctl journal [<target>...] [options]
   207  `
   208  	// parse command-line arguments
   209  	args, err := docopt.Parse(usage, argv, true, "", false)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	return cmd.Journal(args["<target>"].([]string), c.Backend)
   215  }
   216  
   217  // List prints a summary of installed components.
   218  func (c *Client) List(argv []string) error {
   219  	usage := `Prints a list of installed units.
   220  
   221  Usage:
   222    deisctl list [options]
   223  `
   224  	// parse command-line arguments
   225  	if _, err := docopt.Parse(usage, argv, true, "", false); err != nil {
   226  		return err
   227  	}
   228  	return cmd.ListUnits(c.Backend)
   229  }
   230  
   231  // RefreshUnits overwrites local unit files with those requested.
   232  func (c *Client) RefreshUnits(argv []string) error {
   233  	usage := `Overwrites local unit files with those requested.
   234  
   235  Downloading from the Deis project GitHub URL by tag or SHA is the only mechanism
   236  currently supported.
   237  
   238  "deisctl install" looks for unit files in these directories, in this order:
   239  - the $DEISCTL_UNITS environment variable, if set
   240  - $HOME/.deis/units
   241  - /var/lib/deis/units
   242  
   243  Usage:
   244    deisctl refresh-units [-p <target>] [-t <tag>]
   245  
   246  Options:
   247    -p --path=<target>   where to save unit files [default: $HOME/.deis/units]
   248    -t --tag=<tag>       git tag, branch, or SHA to use when downloading unit files
   249                         [default: master]
   250  `
   251  	// parse command-line arguments
   252  	args, err := docopt.Parse(usage, argv, true, "", false)
   253  	if err != nil {
   254  		fmt.Printf("Error: %v\n", err)
   255  		os.Exit(2)
   256  	}
   257  
   258  	return cmd.RefreshUnits(args["--path"].(string), args["--tag"].(string), units.URL)
   259  }
   260  
   261  // Restart stops and then starts components.
   262  func (c *Client) Restart(argv []string) error {
   263  	usage := `Stops and then starts the specified components.
   264  
   265  Usage:
   266    deisctl restart [<target>...] [options]
   267  `
   268  	// parse command-line arguments
   269  	args, err := docopt.Parse(usage, argv, true, "", false)
   270  	if err != nil {
   271  		return err
   272  	}
   273  
   274  	return cmd.Restart(args["<target>"].([]string), c.Backend)
   275  }
   276  
   277  // Scale grows or shrinks the number of running components.
   278  func (c *Client) Scale(argv []string) error {
   279  	usage := `Grows or shrinks the number of running components.
   280  
   281  Currently "router", "registry" and "store-gateway" are the only types that can be scaled.
   282  
   283  Usage:
   284    deisctl scale [<target>...] [options]
   285  `
   286  	// parse command-line arguments
   287  	args, err := docopt.Parse(usage, argv, true, "", false)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	return cmd.Scale(args["<target>"].([]string), c.Backend)
   293  }
   294  
   295  // SSH opens an interactive shell with a machine in the cluster.
   296  func (c *Client) SSH(argv []string) error {
   297  	usage := `Open an interactive shell on a machine in the cluster given a unit or machine id.
   298  
   299  If an optional <command> is provided, that command is run remotely, and the results returned.
   300  
   301  Usage:
   302    deisctl ssh <target> [<command>...]
   303  `
   304  	// parse command-line arguments
   305  	args, err := docopt.Parse(usage, argv, true, "", true)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	target := args["<target>"].(string)
   311  	// handle help explicitly since docopt parsing is relaxed
   312  	if target == "--help" {
   313  		fmt.Println(usage)
   314  		os.Exit(0)
   315  	}
   316  
   317  	var vargs []string
   318  	if v, ok := args["<command>"]; ok {
   319  		vargs = v.([]string)
   320  	}
   321  
   322  	return cmd.SSH(target, vargs, c.Backend)
   323  }
   324  
   325  func (c *Client) Dock(argv []string) error {
   326  	usage := `Connect to the named docker container and run commands on it.
   327  
   328  This is equivalent to running 'docker exec -it <target> <command>'.
   329  
   330  Usage:
   331    deisctl dock <target> [<command>...]
   332  `
   333  	// parse command-line arguments
   334  	args, err := docopt.Parse(usage, argv, true, "", true)
   335  	if err != nil {
   336  		return err
   337  	}
   338  
   339  	target := args["<target>"].(string)
   340  	// handle help explicitly since docopt parsing is relaxed
   341  	if target == "--help" {
   342  		fmt.Println(usage)
   343  		os.Exit(0)
   344  	}
   345  
   346  	var vargs []string
   347  	if v, ok := args["<command>"]; ok {
   348  		vargs = v.([]string)
   349  	}
   350  
   351  	return cmd.Dock(target, vargs, c.Backend)
   352  }
   353  
   354  // Start activates the specified components.
   355  func (c *Client) Start(argv []string) error {
   356  	usage := `Activates the specified components.
   357  
   358  Usage:
   359    deisctl start [<target>...] [options]
   360  `
   361  	// parse command-line arguments
   362  	args, err := docopt.Parse(usage, argv, true, "", false)
   363  	if err != nil {
   364  		return err
   365  	}
   366  
   367  	return cmd.Start(args["<target>"].([]string), c.Backend)
   368  }
   369  
   370  // Status prints the current status of components.
   371  func (c *Client) Status(argv []string) error {
   372  	usage := `Prints the current status of components.
   373  
   374  Usage:
   375    deisctl status [<target>...] [options]
   376  `
   377  	// parse command-line arguments
   378  	args, err := docopt.Parse(usage, argv, true, "", false)
   379  	if err != nil {
   380  		return err
   381  	}
   382  
   383  	return cmd.Status(args["<target>"].([]string), c.Backend)
   384  }
   385  
   386  // Stop deactivates the specified components.
   387  func (c *Client) Stop(argv []string) error {
   388  	usage := `Deactivates the specified components.
   389  
   390  Usage:
   391    deisctl stop [<target>...] [options]
   392  `
   393  	// parse command-line arguments
   394  	args, err := docopt.Parse(usage, argv, true, "", false)
   395  	if err != nil {
   396  		return err
   397  	}
   398  
   399  	return cmd.Stop(args["<target>"].([]string), c.Backend)
   400  }
   401  
   402  // Uninstall unloads the definitions of the specified components.
   403  // After Uninstall, the components will be unavailable until Install is called.
   404  func (c *Client) Uninstall(argv []string) error {
   405  	usage := `Unloads the definitions of the specified components.
   406  
   407  After uninstall, the components will be unavailable until install is called.
   408  
   409  Usage:
   410    deisctl uninstall [<target>...] [options]
   411  `
   412  	// parse command-line arguments
   413  	args, err := docopt.Parse(usage, argv, true, "", false)
   414  	if err != nil {
   415  		return err
   416  	}
   417  
   418  	return cmd.Uninstall(args["<target>"].([]string), c.Backend)
   419  }