github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  	"github.com/juju/cmd"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/gnuflag"
    10  
    11  	"github.com/juju/juju/api"
    12  	"github.com/juju/juju/api/controller"
    13  	"github.com/juju/juju/api/modelmanager"
    14  	"github.com/juju/juju/api/usermanager"
    15  	"github.com/juju/juju/jujuclient"
    16  )
    17  
    18  var (
    19  	// ErrNoControllersDefined is returned by commands that operate on
    20  	// a controller if there is no current controller, no controller has been
    21  	// explicitly specified, and there is no default controller.
    22  	ErrNoControllersDefined = errors.New(`No controllers registered.
    23  
    24  Please either create a new controller using "juju bootstrap" or connect to
    25  another controller that you have been given access to using "juju register".
    26  `)
    27  	// ErrNoCurrentController is returned by commands that operate on
    28  	// a controller if there is no current controller, no controller has been
    29  	// explicitly specified, and there is no default controller but there are
    30  	// controllers that client knows about.
    31  	ErrNoCurrentController = errors.New(`No selected controller.
    32  
    33  Please use "juju switch" to select a controller.
    34  `)
    35  )
    36  
    37  // ControllerCommand is intended to be a base for all commands
    38  // that need to operate on controllers as opposed to models.
    39  type ControllerCommand interface {
    40  	CommandBase
    41  
    42  	// SetClientStore is called prior to the wrapped command's Init method
    43  	// with the default controller store. It may also be called to override the
    44  	// default controller store for testing.
    45  	SetClientStore(jujuclient.ClientStore)
    46  
    47  	// ClientStore returns the controller store that the command is
    48  	// associated with.
    49  	ClientStore() jujuclient.ClientStore
    50  
    51  	// SetControllerName is called prior to the wrapped command's Init method with
    52  	// the active controller name. The controller name is guaranteed to be non-empty
    53  	// at entry of Init. It records the current model name in the
    54  	// ControllerCommandBase.
    55  	SetControllerName(controllerName string) error
    56  
    57  	// ControllerName returns the name of the controller or model used to
    58  	// determine that API end point.
    59  	ControllerName() string
    60  
    61  	// SetAPIOpener allows the replacement of the default API opener,
    62  	// which ends up calling NewAPIRoot
    63  	SetAPIOpener(opener APIOpener)
    64  }
    65  
    66  // ControllerCommandBase is a convenience type for embedding in commands
    67  // that wish to implement ControllerCommand.
    68  type ControllerCommandBase struct {
    69  	JujuCommandBase
    70  
    71  	store          jujuclient.ClientStore
    72  	controllerName string
    73  
    74  	// opener is the strategy used to open the API connection.
    75  	opener APIOpener
    76  }
    77  
    78  // SetClientStore implements the ControllerCommand interface.
    79  func (c *ControllerCommandBase) SetClientStore(store jujuclient.ClientStore) {
    80  	c.store = store
    81  }
    82  
    83  // ClientStore implements the ControllerCommand interface.
    84  func (c *ControllerCommandBase) ClientStore() jujuclient.ClientStore {
    85  	return c.store
    86  }
    87  
    88  // SetControllerName implements the ControllerCommand interface.
    89  func (c *ControllerCommandBase) SetControllerName(controllerName string) error {
    90  	if _, err := c.ClientStore().ControllerByName(controllerName); err != nil {
    91  		return errors.Trace(err)
    92  	}
    93  	c.controllerName = controllerName
    94  	return nil
    95  }
    96  
    97  // ControllerName implements the ControllerCommand interface.
    98  func (c *ControllerCommandBase) ControllerName() string {
    99  	return c.controllerName
   100  }
   101  
   102  // SetAPIOpener specifies the strategy used by the command to open
   103  // the API connection.
   104  func (c *ControllerCommandBase) SetAPIOpener(opener APIOpener) {
   105  	c.opener = opener
   106  }
   107  
   108  // NewModelManagerAPIClient returns an API client for the
   109  // ModelManager on the current controller using the current credentials.
   110  func (c *ControllerCommandBase) NewModelManagerAPIClient() (*modelmanager.Client, error) {
   111  	root, err := c.NewAPIRoot()
   112  	if err != nil {
   113  		return nil, errors.Trace(err)
   114  	}
   115  	return modelmanager.NewClient(root), nil
   116  }
   117  
   118  // NewControllerAPIClient returns an API client for the Controller on
   119  // the current controller using the current credentials.
   120  func (c *ControllerCommandBase) NewControllerAPIClient() (*controller.Client, error) {
   121  	root, err := c.NewAPIRoot()
   122  	if err != nil {
   123  		return nil, errors.Trace(err)
   124  	}
   125  	return controller.NewClient(root), nil
   126  }
   127  
   128  // NewUserManagerAPIClient returns an API client for the UserManager on the
   129  // current controller using the current credentials.
   130  func (c *ControllerCommandBase) NewUserManagerAPIClient() (*usermanager.Client, error) {
   131  	root, err := c.NewAPIRoot()
   132  	if err != nil {
   133  		return nil, errors.Trace(err)
   134  	}
   135  	return usermanager.NewClient(root), nil
   136  }
   137  
   138  // NewAPIRoot returns a restricted API for the current controller using the current
   139  // credentials.  Only the UserManager and ModelManager may be accessed
   140  // through this API connection.
   141  func (c *ControllerCommandBase) NewAPIRoot() (api.Connection, error) {
   142  	return c.newAPIRoot("")
   143  }
   144  
   145  // NewAPIRoot returns a new connection to the API server for the named model
   146  // in the specified controller.
   147  func (c *ControllerCommandBase) NewModelAPIRoot(modelName string) (api.Connection, error) {
   148  	_, err := c.store.ModelByName(c.controllerName, modelName)
   149  	if err != nil {
   150  		if !errors.IsNotFound(err) {
   151  			return nil, errors.Trace(err)
   152  		}
   153  		// The model isn't known locally, so query the models
   154  		// available in the controller, and cache them locally.
   155  		if err := c.RefreshModels(c.store, c.controllerName); err != nil {
   156  			return nil, errors.Annotate(err, "refreshing models")
   157  		}
   158  	}
   159  	return c.newAPIRoot(modelName)
   160  }
   161  
   162  func (c *ControllerCommandBase) newAPIRoot(modelName string) (api.Connection, error) {
   163  	if c.controllerName == "" {
   164  		controllers, err := c.store.AllControllers()
   165  		if err != nil {
   166  			return nil, errors.Trace(err)
   167  		}
   168  		if len(controllers) == 0 {
   169  			return nil, errors.Trace(ErrNoControllersDefined)
   170  		}
   171  		return nil, errors.Trace(ErrNoCurrentController)
   172  	}
   173  	opener := c.opener
   174  	if opener == nil {
   175  		opener = OpenFunc(c.JujuCommandBase.NewAPIRoot)
   176  	}
   177  	return opener.Open(c.store, c.controllerName, modelName)
   178  }
   179  
   180  // ModelUUIDs returns the model UUIDs for the given model names.
   181  func (c *ControllerCommandBase) ModelUUIDs(modelNames []string) ([]string, error) {
   182  	var result []string
   183  	store := c.ClientStore()
   184  	controllerName := c.ControllerName()
   185  	for _, modelName := range modelNames {
   186  		model, err := store.ModelByName(controllerName, modelName)
   187  		if errors.IsNotFound(err) {
   188  			// The model isn't known locally, so query the models available in the controller.
   189  			logger.Infof("model %q not cached locally, refreshing models from controller", modelName)
   190  			if err := c.RefreshModels(store, controllerName); err != nil {
   191  				return nil, errors.Annotatef(err, "refreshing model %q", modelName)
   192  			}
   193  			model, err = store.ModelByName(controllerName, modelName)
   194  		}
   195  		if err != nil {
   196  			return nil, errors.Annotatef(err, "model %q not found", modelName)
   197  		}
   198  		result = append(result, model.ModelUUID)
   199  	}
   200  	return result, nil
   201  }
   202  
   203  // WrapControllerOption specifies an option to the WrapController function.
   204  type WrapControllerOption func(*sysCommandWrapper)
   205  
   206  // Options for the WrapController call.
   207  var (
   208  	// WrapControllerSkipControllerFlags specifies that the -c
   209  	// and --controller flag flags should not be defined.
   210  	WrapControllerSkipControllerFlags WrapControllerOption = wrapControllerSkipControllerFlags
   211  
   212  	// WrapSkipDefaultModel specifies that no default controller should
   213  	// be used.
   214  	WrapControllerSkipDefaultController WrapControllerOption = wrapControllerSkipDefaultController
   215  )
   216  
   217  func wrapControllerSkipControllerFlags(w *sysCommandWrapper) {
   218  	w.setControllerFlags = false
   219  }
   220  
   221  func wrapControllerSkipDefaultController(w *sysCommandWrapper) {
   222  	w.useDefaultController = false
   223  }
   224  
   225  // WrapControllerAPIOpener specifies that the given APIOpener
   226  // should should be used to open the API connection when
   227  // NewAPIRoot or NewControllerAPIRoot are called.
   228  func WrapControllerAPIOpener(opener APIOpener) WrapControllerOption {
   229  	return func(w *sysCommandWrapper) {
   230  		w.ControllerCommand.SetAPIOpener(opener)
   231  	}
   232  }
   233  
   234  // WrapController wraps the specified ControllerCommand, returning a Command
   235  // that proxies to each of the ControllerCommand methods.
   236  func WrapController(c ControllerCommand, options ...WrapControllerOption) cmd.Command {
   237  	wrapper := &sysCommandWrapper{
   238  		ControllerCommand:    c,
   239  		setControllerFlags:   true,
   240  		useDefaultController: true,
   241  	}
   242  	for _, option := range options {
   243  		option(wrapper)
   244  	}
   245  	return WrapBase(wrapper)
   246  }
   247  
   248  type sysCommandWrapper struct {
   249  	ControllerCommand
   250  	setControllerFlags   bool
   251  	useDefaultController bool
   252  	controllerName       string
   253  }
   254  
   255  // SetFlags implements Command.SetFlags, then calls the wrapped command's SetFlags.
   256  func (w *sysCommandWrapper) SetFlags(f *gnuflag.FlagSet) {
   257  	if w.setControllerFlags {
   258  		f.StringVar(&w.controllerName, "c", "", "Controller to operate in")
   259  		f.StringVar(&w.controllerName, "controller", "", "")
   260  	}
   261  	w.ControllerCommand.SetFlags(f)
   262  }
   263  
   264  // Init implements Command.Init, then calls the wrapped command's Init.
   265  func (w *sysCommandWrapper) Init(args []string) error {
   266  	store := w.ClientStore()
   267  	if store == nil {
   268  		store = jujuclient.NewFileClientStore()
   269  	}
   270  	store = QualifyingClientStore{store}
   271  	w.SetClientStore(store)
   272  
   273  	return w.ControllerCommand.Init(args)
   274  }
   275  
   276  func (w *sysCommandWrapper) Run(ctx *cmd.Context) error {
   277  	if w.setControllerFlags {
   278  		if w.controllerName == "" && w.useDefaultController {
   279  			store := w.ClientStore()
   280  			currentController, err := store.CurrentController()
   281  			if err != nil {
   282  				return translateControllerError(store, err)
   283  			}
   284  			w.controllerName = currentController
   285  		}
   286  		if w.controllerName == "" && !w.useDefaultController {
   287  			return ErrNoControllersDefined
   288  		}
   289  	}
   290  	if w.controllerName != "" {
   291  		if err := w.SetControllerName(w.controllerName); err != nil {
   292  			return translateControllerError(w.ClientStore(), err)
   293  		}
   294  	}
   295  	return w.ControllerCommand.Run(ctx)
   296  }
   297  
   298  func translateControllerError(store jujuclient.ClientStore, err error) error {
   299  	if !errors.IsNotFound(err) {
   300  		return err
   301  	}
   302  	controllers, err2 := store.AllControllers()
   303  	if err2 != nil {
   304  		return err2
   305  	}
   306  	if len(controllers) == 0 {
   307  		return errors.Wrap(err, ErrNoControllersDefined)
   308  	}
   309  	return errors.Wrap(err, ErrNoCurrentController)
   310  }