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 }