github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/modelcmd/controller.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelcmd
     5  
     6  import (
     7  	"net/http"
     8  	"os"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/gnuflag"
    13  	"gopkg.in/macaroon-bakery.v2-unstable/httpbakery"
    14  
    15  	"github.com/juju/juju/api"
    16  	"github.com/juju/juju/api/controller"
    17  	"github.com/juju/juju/api/modelmanager"
    18  	"github.com/juju/juju/api/usermanager"
    19  	"github.com/juju/juju/juju/osenv"
    20  	"github.com/juju/juju/jujuclient"
    21  )
    22  
    23  var (
    24  	// ErrNoControllersDefined is returned by commands that operate on
    25  	// a controller if there is no current controller, no controller has been
    26  	// explicitly specified, and there is no default controller.
    27  	ErrNoControllersDefined = errors.New(`No controllers registered.
    28  
    29  Please either create a new controller using "juju bootstrap" or connect to
    30  another controller that you have been given access to using "juju register".
    31  `)
    32  	// ErrNoCurrentController is returned by commands that operate on
    33  	// a controller if there is no current controller, no controller has been
    34  	// explicitly specified, and there is no default controller but there are
    35  	// controllers that client knows about.
    36  	ErrNoCurrentController = errors.New(`No selected controller.
    37  
    38  Please use "juju switch" to select a controller.
    39  `)
    40  )
    41  
    42  // ControllerCommand is intended to be a base for all commands
    43  // that need to operate on controllers as opposed to models.
    44  type ControllerCommand interface {
    45  	Command
    46  
    47  	// SetClientStore is called prior to the wrapped command's Init method
    48  	// with the default controller store. It may also be called to override the
    49  	// default controller store for testing.
    50  	SetClientStore(jujuclient.ClientStore)
    51  
    52  	// ClientStore returns the controller store that the command is
    53  	// associated with.
    54  	ClientStore() jujuclient.ClientStore
    55  
    56  	// SetControllerName sets the name of the current controller.
    57  	SetControllerName(controllerName string, allowDefault bool) error
    58  
    59  	// ControllerName returns the name of the controller
    60  	// that the command should use. It must only be called
    61  	// after Run has been called.
    62  	ControllerName() (string, error)
    63  
    64  	// initModel initializes the controller, resolving an empty
    65  	// controller to the current controller if allowDefault is true.
    66  	initController() error
    67  }
    68  
    69  // ControllerCommandBase is a convenience type for embedding in commands
    70  // that wish to implement ControllerCommand.
    71  type ControllerCommandBase struct {
    72  	CommandBase
    73  
    74  	store jujuclient.ClientStore
    75  
    76  	_controllerName        string
    77  	allowDefaultController bool
    78  
    79  	// doneInitController holds whether initController has been called.
    80  	doneInitController bool
    81  
    82  	// initControllerError holds the result of the initController call.
    83  	initControllerError error
    84  }
    85  
    86  // SetClientStore implements the ControllerCommand interface.
    87  func (c *ControllerCommandBase) SetClientStore(store jujuclient.ClientStore) {
    88  	c.store = store
    89  }
    90  
    91  // ClientStore implements the ControllerCommand interface.
    92  func (c *ControllerCommandBase) ClientStore() jujuclient.ClientStore {
    93  	c.assertRunStarted()
    94  	return c.store
    95  }
    96  
    97  func (c *ControllerCommandBase) initController() error {
    98  	if c.doneInitController {
    99  		return errors.Trace(c.initControllerError)
   100  	}
   101  	c.doneInitController = true
   102  	c.initControllerError = c.initController0()
   103  	return c.initControllerError
   104  }
   105  
   106  func (c *ControllerCommandBase) initController0() error {
   107  	if c._controllerName == "" && !c.allowDefaultController {
   108  		return errors.New("no controller specified")
   109  	}
   110  	if c._controllerName == "" {
   111  		c._controllerName = os.Getenv(osenv.JujuControllerEnvKey)
   112  	}
   113  	store := c.ClientStore()
   114  	if c._controllerName == "" {
   115  		currentController, err := store.CurrentController()
   116  		if err != nil {
   117  			return errors.Trace(translateControllerError(store, err))
   118  		}
   119  		c._controllerName = currentController
   120  	}
   121  	if _, err := store.ControllerByName(c._controllerName); err != nil {
   122  		return errors.Trace(err)
   123  	}
   124  	return nil
   125  }
   126  
   127  // SetControllerName implements ControllerCommand.SetControllerName.
   128  func (c *ControllerCommandBase) SetControllerName(controllerName string, allowDefault bool) error {
   129  	logger.Infof("setting controllerName to %q %v", controllerName, allowDefault)
   130  	c._controllerName = controllerName
   131  	c.allowDefaultController = allowDefault
   132  	if c.runStarted {
   133  		if err := c.initController(); err != nil {
   134  			return errors.Trace(err)
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  // ControllerName implements the ControllerCommand interface.
   141  func (c *ControllerCommandBase) ControllerName() (string, error) {
   142  	c.assertRunStarted()
   143  	if err := c.initController(); err != nil {
   144  		return "", errors.Trace(err)
   145  	}
   146  	return c._controllerName, nil
   147  }
   148  
   149  func (c *ControllerCommandBase) BakeryClient() (*httpbakery.Client, error) {
   150  	controllerName, err := c.ControllerName()
   151  	if err != nil {
   152  		return nil, errors.Trace(err)
   153  	}
   154  	return c.CommandBase.BakeryClient(c.ClientStore(), controllerName)
   155  }
   156  
   157  func (c *ControllerCommandBase) CookieJar() (http.CookieJar, error) {
   158  	controllerName, err := c.ControllerName()
   159  	if err != nil {
   160  		return nil, errors.Trace(err)
   161  	}
   162  	return c.CommandBase.CookieJar(c.ClientStore(), controllerName)
   163  }
   164  
   165  // NewModelManagerAPIClient returns an API client for the
   166  // ModelManager on the current controller using the current credentials.
   167  func (c *ControllerCommandBase) NewModelManagerAPIClient() (*modelmanager.Client, error) {
   168  	root, err := c.NewAPIRoot()
   169  	if err != nil {
   170  		return nil, errors.Trace(err)
   171  	}
   172  	return modelmanager.NewClient(root), nil
   173  }
   174  
   175  // NewControllerAPIClient returns an API client for the Controller on
   176  // the current controller using the current credentials.
   177  func (c *ControllerCommandBase) NewControllerAPIClient() (*controller.Client, error) {
   178  	root, err := c.NewAPIRoot()
   179  	if err != nil {
   180  		return nil, errors.Trace(err)
   181  	}
   182  	return controller.NewClient(root), nil
   183  }
   184  
   185  // NewUserManagerAPIClient returns an API client for the UserManager on the
   186  // current controller using the current credentials.
   187  func (c *ControllerCommandBase) NewUserManagerAPIClient() (*usermanager.Client, error) {
   188  	root, err := c.NewAPIRoot()
   189  	if err != nil {
   190  		return nil, errors.Trace(err)
   191  	}
   192  	return usermanager.NewClient(root), nil
   193  }
   194  
   195  // NewAPIRoot returns a restricted API for the current controller using the current
   196  // credentials.  Only the UserManager and ModelManager may be accessed
   197  // through this API connection.
   198  func (c *ControllerCommandBase) NewAPIRoot() (api.Connection, error) {
   199  	return c.newAPIRoot("")
   200  }
   201  
   202  // NewAPIRoot returns a new connection to the API server for the named model
   203  // in the specified controller.
   204  func (c *ControllerCommandBase) NewModelAPIRoot(modelName string) (api.Connection, error) {
   205  	controllerName, err := c.ControllerName()
   206  	if err != nil {
   207  		return nil, errors.Trace(err)
   208  	}
   209  	_, err = c.store.ModelByName(controllerName, modelName)
   210  	if err != nil {
   211  		if !errors.IsNotFound(err) {
   212  			return nil, errors.Trace(err)
   213  		}
   214  		// The model isn't known locally, so query the models
   215  		// available in the controller, and cache them locally.
   216  		if err := c.RefreshModels(c.store, controllerName); err != nil {
   217  			return nil, errors.Annotate(err, "refreshing models")
   218  		}
   219  	}
   220  	return c.newAPIRoot(modelName)
   221  }
   222  
   223  func (c *ControllerCommandBase) newAPIRoot(modelName string) (api.Connection, error) {
   224  	controllerName, err := c.ControllerName()
   225  	if err != nil {
   226  		return nil, errors.Trace(err)
   227  	}
   228  	return c.CommandBase.NewAPIRoot(c.store, controllerName, modelName)
   229  }
   230  
   231  // ModelUUIDs returns the model UUIDs for the given model names.
   232  func (c *ControllerCommandBase) ModelUUIDs(modelNames []string) ([]string, error) {
   233  	controllerName, err := c.ControllerName()
   234  	if err != nil {
   235  		return nil, errors.Trace(err)
   236  	}
   237  	return c.CommandBase.ModelUUIDs(c.ClientStore(), controllerName, modelNames)
   238  }
   239  
   240  // CurrentAccountDetails returns details of the account associated with
   241  // the current controller.
   242  func (c *ControllerCommandBase) CurrentAccountDetails() (*jujuclient.AccountDetails, error) {
   243  	controllerName, err := c.ControllerName()
   244  	if err != nil {
   245  		return nil, errors.Trace(err)
   246  	}
   247  	return c.ClientStore().AccountDetails(controllerName)
   248  }
   249  
   250  // WrapControllerOption specifies an option to the WrapController function.
   251  type WrapControllerOption func(*controllerCommandWrapper)
   252  
   253  // Options for the WrapController call.
   254  var (
   255  	// WrapControllerSkipControllerFlags specifies that the -c
   256  	// and --controller flag flags should not be defined.
   257  	WrapControllerSkipControllerFlags WrapControllerOption = wrapControllerSkipControllerFlags
   258  
   259  	// WrapSkipDefaultModel specifies that no default controller should
   260  	// be used.
   261  	WrapControllerSkipDefaultController WrapControllerOption = wrapControllerSkipDefaultController
   262  )
   263  
   264  func wrapControllerSkipControllerFlags(w *controllerCommandWrapper) {
   265  	w.setControllerFlags = false
   266  }
   267  
   268  func wrapControllerSkipDefaultController(w *controllerCommandWrapper) {
   269  	w.useDefaultController = false
   270  }
   271  
   272  // WrapController wraps the specified ControllerCommand, returning a Command
   273  // that proxies to each of the ControllerCommand methods.
   274  func WrapController(c ControllerCommand, options ...WrapControllerOption) ControllerCommand {
   275  	wrapper := &controllerCommandWrapper{
   276  		ControllerCommand:    c,
   277  		setControllerFlags:   true,
   278  		useDefaultController: true,
   279  	}
   280  	for _, option := range options {
   281  		option(wrapper)
   282  	}
   283  	// Define a new type so that we can embed the ModelCommand
   284  	// interface one level deeper than cmd.Command, so that
   285  	// we'll get the Command methods from WrapBase
   286  	// and all the ModelCommand methods not in cmd.Command
   287  	// from modelCommandWrapper.
   288  	type embed struct {
   289  		*controllerCommandWrapper
   290  	}
   291  	return struct {
   292  		embed
   293  		cmd.Command
   294  	}{
   295  		Command: WrapBase(wrapper),
   296  		embed:   embed{wrapper},
   297  	}
   298  }
   299  
   300  type controllerCommandWrapper struct {
   301  	ControllerCommand
   302  	setControllerFlags   bool
   303  	useDefaultController bool
   304  	controllerName       string
   305  }
   306  
   307  // wrapped implements wrapper.wrapped.
   308  func (w *controllerCommandWrapper) inner() cmd.Command {
   309  	return w.ControllerCommand
   310  }
   311  
   312  // SetFlags implements Command.SetFlags, then calls the wrapped command's SetFlags.
   313  func (w *controllerCommandWrapper) SetFlags(f *gnuflag.FlagSet) {
   314  	if w.setControllerFlags {
   315  		f.StringVar(&w.controllerName, "c", "", "Controller to operate in")
   316  		f.StringVar(&w.controllerName, "controller", "", "")
   317  	}
   318  	w.ControllerCommand.SetFlags(f)
   319  }
   320  
   321  // Init implements Command.Init, then calls the wrapped command's Init.
   322  func (w *controllerCommandWrapper) Init(args []string) error {
   323  	if w.setControllerFlags {
   324  		if err := w.SetControllerName(w.controllerName, w.useDefaultController); err != nil {
   325  			return errors.Trace(err)
   326  		}
   327  	}
   328  	if err := w.ControllerCommand.Init(args); err != nil {
   329  		return errors.Trace(err)
   330  	}
   331  	return nil
   332  }
   333  
   334  func (w *controllerCommandWrapper) Run(ctx *cmd.Context) error {
   335  	w.setRunStarted()
   336  	store := w.ClientStore()
   337  	if store == nil {
   338  		store = jujuclient.NewFileClientStore()
   339  	}
   340  	store = QualifyingClientStore{store}
   341  	w.SetClientStore(store)
   342  	return w.ControllerCommand.Run(ctx)
   343  }
   344  
   345  func translateControllerError(store jujuclient.ClientStore, err error) error {
   346  	if !errors.IsNotFound(err) {
   347  		return err
   348  	}
   349  	controllers, err2 := store.AllControllers()
   350  	if err2 != nil {
   351  		return err2
   352  	}
   353  	if len(controllers) == 0 {
   354  		return errors.Wrap(err, ErrNoControllersDefined)
   355  	}
   356  	return errors.Wrap(err, ErrNoCurrentController)
   357  }