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 }