github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "launchpad.net/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 // ErrNoControllerSpecified 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 ErrNoControllerSpecified = errors.New(`no controller specified 23 24 There is no current controller. Please use "juju switch" to 25 set the current controller/model, or create a new controller 26 using "juju bootstrap". 27 `) 28 29 // ErrNoAccountSpecified is returned by commands that operate on a 30 // controller if there is no current account associated with the 31 // controller. 32 ErrNoAccountSpecified = errors.New("no account specified") 33 ) 34 35 // ControllerCommand is intended to be a base for all commands 36 // that need to operate on controllers as opposed to models. 37 type ControllerCommand interface { 38 CommandBase 39 40 // SetClientStore is called prior to the wrapped command's Init method 41 // with the default controller store. It may also be called to override the 42 // default controller store for testing. 43 SetClientStore(jujuclient.ClientStore) 44 45 // ClientStore returns the controller store that the command is 46 // associated with. 47 ClientStore() jujuclient.ClientStore 48 49 // SetControllerName is called prior to the wrapped command's Init method with 50 // the active controller name. The controller name is guaranteed to be non-empty 51 // at entry of Init. It records the current model name in the 52 // ControllerCommandBase. 53 SetControllerName(controllerName string) error 54 55 // ControllerName returns the name of the controller or model used to 56 // determine that API end point. 57 ControllerName() string 58 59 // SetAPIOpener allows the replacement of the default API opener, 60 // which ends up calling NewAPIRoot 61 SetAPIOpener(opener APIOpener) 62 } 63 64 // ControllerCommandBase is a convenience type for embedding in commands 65 // that wish to implement ControllerCommand. 66 type ControllerCommandBase struct { 67 JujuCommandBase 68 69 store jujuclient.ClientStore 70 controllerName string 71 accountName string 72 73 // opener is the strategy used to open the API connection. 74 opener APIOpener 75 } 76 77 // SetClientStore implements the ControllerCommand interface. 78 func (c *ControllerCommandBase) SetClientStore(store jujuclient.ClientStore) { 79 c.store = store 80 } 81 82 // ClientStore implements the ControllerCommand interface. 83 func (c *ControllerCommandBase) ClientStore() jujuclient.ClientStore { 84 return c.store 85 } 86 87 // SetControllerName implements the ControllerCommand interface. 88 func (c *ControllerCommandBase) SetControllerName(controllerName string) error { 89 actualControllerName, err := ResolveControllerName(c.ClientStore(), controllerName) 90 if err != nil { 91 return errors.Trace(err) 92 } 93 controllerName = actualControllerName 94 95 accountName, err := c.store.CurrentAccount(controllerName) 96 if err != nil && !errors.IsNotFound(err) { 97 return errors.Trace(err) 98 } 99 c.controllerName = controllerName 100 c.accountName = accountName 101 return nil 102 } 103 104 // ControllerName implements the ControllerCommand interface. 105 func (c *ControllerCommandBase) ControllerName() string { 106 return c.controllerName 107 } 108 109 // AccountName implements the ControllerCommand interface. 110 func (c *ControllerCommandBase) AccountName() string { 111 return c.accountName 112 } 113 114 // SetAPIOpener specifies the strategy used by the command to open 115 // the API connection. 116 func (c *ControllerCommandBase) SetAPIOpener(opener APIOpener) { 117 c.opener = opener 118 } 119 120 // NewModelManagerAPIClient returns an API client for the 121 // ModelManager on the current controller using the current credentials. 122 func (c *ControllerCommandBase) NewModelManagerAPIClient() (*modelmanager.Client, error) { 123 root, err := c.NewAPIRoot() 124 if err != nil { 125 return nil, errors.Trace(err) 126 } 127 return modelmanager.NewClient(root), nil 128 } 129 130 // NewControllerAPIClient returns an API client for the Controller on 131 // the current controller using the current credentials. 132 func (c *ControllerCommandBase) NewControllerAPIClient() (*controller.Client, error) { 133 root, err := c.NewAPIRoot() 134 if err != nil { 135 return nil, errors.Trace(err) 136 } 137 return controller.NewClient(root), nil 138 } 139 140 // NewUserManagerAPIClient returns an API client for the UserManager on the 141 // current controller using the current credentials. 142 func (c *ControllerCommandBase) NewUserManagerAPIClient() (*usermanager.Client, error) { 143 root, err := c.NewAPIRoot() 144 if err != nil { 145 return nil, errors.Trace(err) 146 } 147 return usermanager.NewClient(root), nil 148 } 149 150 // NewAPIRoot returns a restricted API for the current controller using the current 151 // credentials. Only the UserManager and ModelManager may be accessed 152 // through this API connection. 153 func (c *ControllerCommandBase) NewAPIRoot() (api.Connection, error) { 154 if c.controllerName == "" { 155 return nil, errors.Trace(ErrNoControllerSpecified) 156 } 157 if c.accountName == "" { 158 return nil, errors.Trace(ErrNoAccountSpecified) 159 } 160 opener := c.opener 161 if opener == nil { 162 opener = OpenFunc(c.JujuCommandBase.NewAPIRoot) 163 } 164 return opener.Open(c.store, c.controllerName, c.accountName, "") 165 } 166 167 // ModelUUIDs returns the model UUIDs for the given model names. 168 func (c *ControllerCommandBase) ModelUUIDs(modelNames []string) ([]string, error) { 169 var result []string 170 store := c.ClientStore() 171 controllerName := c.ControllerName() 172 accountName := c.AccountName() 173 for _, modelName := range modelNames { 174 model, err := store.ModelByName(controllerName, accountName, modelName) 175 if errors.IsNotFound(err) { 176 // The model isn't known locally, so query the models available in the controller. 177 logger.Infof("model %q not cached locally, refreshing models from controller", modelName) 178 if err := c.RefreshModels(store, controllerName, accountName); err != nil { 179 return nil, errors.Annotatef(err, "refreshing model %q", modelName) 180 } 181 model, err = store.ModelByName(controllerName, accountName, modelName) 182 } 183 if err != nil { 184 return nil, errors.Annotatef(err, "model %q not found", modelName) 185 } 186 result = append(result, model.ModelUUID) 187 } 188 return result, nil 189 } 190 191 // WrapControllerOption sets various parameters of the 192 // ControllerCommand wrapper. 193 type WrapControllerOption func(*sysCommandWrapper) 194 195 // ControllerSkipFlags instructs the wrapper to skip -c 196 // and --controller flag definition. 197 func ControllerSkipFlags(w *sysCommandWrapper) { 198 w.setFlags = false 199 } 200 201 // ControllerSkipDefault instructs the wrapper not to 202 // use the default controller name. 203 func ControllerSkipDefault(w *sysCommandWrapper) { 204 w.useDefaultControllerName = false 205 } 206 207 // ControllerAPIOpener instructs the underlying controller command to use a 208 // different APIOpener strategy. 209 func ControllerAPIOpener(opener APIOpener) WrapControllerOption { 210 return func(w *sysCommandWrapper) { 211 w.ControllerCommand.SetAPIOpener(opener) 212 } 213 } 214 215 // WrapController wraps the specified ControllerCommand, returning a Command 216 // that proxies to each of the ControllerCommand methods. 217 func WrapController(c ControllerCommand, options ...WrapControllerOption) cmd.Command { 218 wrapper := &sysCommandWrapper{ 219 ControllerCommand: c, 220 setFlags: true, 221 useDefaultControllerName: true, 222 } 223 for _, option := range options { 224 option(wrapper) 225 } 226 return WrapBase(wrapper) 227 } 228 229 type sysCommandWrapper struct { 230 ControllerCommand 231 setFlags bool 232 useDefaultControllerName bool 233 controllerName string 234 } 235 236 // SetFlags implements Command.SetFlags, then calls the wrapped command's SetFlags. 237 func (w *sysCommandWrapper) SetFlags(f *gnuflag.FlagSet) { 238 if w.setFlags { 239 f.StringVar(&w.controllerName, "c", "", "Controller to operate in") 240 f.StringVar(&w.controllerName, "controller", "", "") 241 } 242 w.ControllerCommand.SetFlags(f) 243 } 244 245 func (w *sysCommandWrapper) getDefaultControllerName() (string, error) { 246 if currentController, err := ReadCurrentController(); err != nil { 247 return "", errors.Trace(err) 248 } else if currentController != "" { 249 return currentController, nil 250 } 251 return "", errors.Trace(ErrNoControllerSpecified) 252 } 253 254 // Init implements Command.Init, then calls the wrapped command's Init. 255 func (w *sysCommandWrapper) Init(args []string) error { 256 store := w.ClientStore() 257 if store == nil { 258 store = jujuclient.NewFileClientStore() 259 w.SetClientStore(store) 260 } 261 if w.setFlags { 262 if w.controllerName == "" && w.useDefaultControllerName { 263 name, err := w.getDefaultControllerName() 264 if err != nil { 265 return errors.Trace(err) 266 } 267 w.controllerName = name 268 } 269 if w.controllerName == "" && !w.useDefaultControllerName { 270 return ErrNoControllerSpecified 271 } 272 } 273 if w.controllerName != "" { 274 if err := w.SetControllerName(w.controllerName); err != nil { 275 return errors.Trace(err) 276 } 277 } 278 return w.ControllerCommand.Init(args) 279 } 280 281 // ResolveControllerName returns the canonical name of a controller given 282 // an unambiguous identifier for that controller. 283 // Locally created controllers (i.e. those whose names begin with "local.") 284 // may be identified with or without the "local." prefix if there exists no 285 // other controller in the store with the same unprefixed name. 286 func ResolveControllerName(store jujuclient.ControllerStore, controllerName string) (string, error) { 287 _, err := store.ControllerByName(controllerName) 288 if err == nil { 289 return controllerName, nil 290 } 291 if !errors.IsNotFound(err) { 292 return "", err 293 } 294 var secondErr error 295 localName := "local." + controllerName 296 _, secondErr = store.ControllerByName(localName) 297 // If fallback name not found, return the original error. 298 if errors.IsNotFound(secondErr) { 299 return "", err 300 } 301 return localName, secondErr 302 }