github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/controller/listmodels.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller 5 6 import ( 7 "bytes" 8 "fmt" 9 "text/tabwriter" 10 "time" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 "launchpad.net/gnuflag" 16 17 "github.com/juju/juju/api/base" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/cmd/juju/common" 20 "github.com/juju/juju/cmd/modelcmd" 21 ) 22 23 // NewListModelsCommand returns a command to list models. 24 func NewListModelsCommand() cmd.Command { 25 return modelcmd.WrapController(&modelsCommand{}) 26 } 27 28 // modelsCommand returns the list of all the models the 29 // current user can access on the current controller. 30 type modelsCommand struct { 31 modelcmd.ControllerCommandBase 32 out cmd.Output 33 all bool 34 user string 35 listUUID bool 36 exactTime bool 37 modelAPI ModelManagerAPI 38 sysAPI ModelsSysAPI 39 } 40 41 var listModelsDoc = ` 42 The models listed here are either models you have created yourself, or 43 models which have been shared with you. Default values for user and 44 controller are, respectively, the current user and the current controller. 45 The active model is denoted by an asterisk. 46 47 Examples: 48 49 juju list-models 50 juju list-models --user bob 51 52 See also: create-model 53 share-model 54 unshare-model 55 ` 56 57 // ModelManagerAPI defines the methods on the model manager API that 58 // the models command calls. 59 type ModelManagerAPI interface { 60 Close() error 61 ListModels(user string) ([]base.UserModel, error) 62 ModelInfo([]names.ModelTag) ([]params.ModelInfoResult, error) 63 } 64 65 // ModelsSysAPI defines the methods on the controller manager API that the 66 // list models command calls. 67 type ModelsSysAPI interface { 68 Close() error 69 AllModels() ([]base.UserModel, error) 70 } 71 72 // Info implements Command.Info 73 func (c *modelsCommand) Info() *cmd.Info { 74 return &cmd.Info{ 75 Name: "list-models", 76 Purpose: "Lists models a user can access on a controller.", 77 Doc: listModelsDoc, 78 } 79 } 80 81 func (c *modelsCommand) getModelManagerAPI() (ModelManagerAPI, error) { 82 if c.modelAPI != nil { 83 return c.modelAPI, nil 84 } 85 return c.NewModelManagerAPIClient() 86 } 87 88 func (c *modelsCommand) getSysAPI() (ModelsSysAPI, error) { 89 if c.sysAPI != nil { 90 return c.sysAPI, nil 91 } 92 return c.NewControllerAPIClient() 93 } 94 95 // SetFlags implements Command.SetFlags. 96 func (c *modelsCommand) SetFlags(f *gnuflag.FlagSet) { 97 f.StringVar(&c.user, "user", "", "The user to list models for (administrative users only)") 98 f.BoolVar(&c.all, "all", false, "Lists all models, regardless of user accessibility (administrative users only)") 99 f.BoolVar(&c.listUUID, "uuid", false, "Display UUID for models") 100 f.BoolVar(&c.exactTime, "exact-time", false, "Use full timestamps") 101 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 102 "yaml": cmd.FormatYaml, 103 "json": cmd.FormatJson, 104 "tabular": c.formatTabular, 105 }) 106 } 107 108 // ModelSet contains the set of models known to the client, 109 // and UUID of the current model. 110 type ModelSet struct { 111 Models []common.ModelInfo `yaml:"models" json:"models"` 112 CurrentModel string `yaml:"current-model,omitempty" json:"current-model,omitempty"` 113 } 114 115 // Run implements Command.Run 116 func (c *modelsCommand) Run(ctx *cmd.Context) error { 117 if c.user == "" { 118 accountDetails, err := c.ClientStore().AccountByName( 119 c.ControllerName(), c.AccountName(), 120 ) 121 if err != nil { 122 return err 123 } 124 c.user = accountDetails.User 125 } 126 127 // First get a list of the models. 128 var models []base.UserModel 129 var err error 130 if c.all { 131 models, err = c.getAllModels() 132 } else { 133 models, err = c.getUserModels() 134 } 135 if err != nil { 136 return errors.Annotate(err, "cannot list models") 137 } 138 139 // And now get the full details of the models. 140 paramsModelInfo, err := c.getModelInfo(models) 141 if err != nil { 142 return errors.Annotate(err, "cannot get model details") 143 } 144 145 now := time.Now() 146 modelInfo := make([]common.ModelInfo, 0, len(models)) 147 for _, info := range paramsModelInfo { 148 model, err := common.ModelInfoFromParams(info, now) 149 if err != nil { 150 return errors.Trace(err) 151 } 152 modelInfo = append(modelInfo, model) 153 } 154 155 modelSet := ModelSet{Models: modelInfo} 156 current, err := c.ClientStore().CurrentModel(c.ControllerName(), c.AccountName()) 157 if err != nil && !errors.IsNotFound(err) { 158 return err 159 } 160 modelSet.CurrentModel = current 161 if err := c.out.Write(ctx, modelSet); err != nil { 162 return err 163 } 164 165 if len(models) == 0 && c.out.Name() == "tabular" { 166 // When the output is tabular, we inform the user when there 167 // are no models available, and tell them how to go about 168 // creating or granting access to them. 169 fmt.Fprintf(ctx.Stderr, "\n%s\n\n", errNoModels.Error()) 170 } 171 return nil 172 } 173 174 func (c *modelsCommand) getModelInfo(userModels []base.UserModel) ([]params.ModelInfo, error) { 175 client, err := c.getModelManagerAPI() 176 if err != nil { 177 return nil, errors.Trace(err) 178 } 179 defer client.Close() 180 181 tags := make([]names.ModelTag, len(userModels)) 182 for i, m := range userModels { 183 tags[i] = names.NewModelTag(m.UUID) 184 } 185 results, err := client.ModelInfo(tags) 186 if err != nil { 187 return nil, errors.Trace(err) 188 } 189 190 info := make([]params.ModelInfo, len(tags)) 191 for i, result := range results { 192 if result.Error != nil { 193 if params.IsCodeUnauthorized(result.Error) { 194 // If we get this, then the model was removed 195 // between the initial listing and the call 196 // to query its details. 197 continue 198 } 199 return nil, errors.Annotatef( 200 result.Error, "getting model %s (%q) info", 201 userModels[i].UUID, userModels[i].Name, 202 ) 203 } 204 info[i] = *result.Result 205 } 206 return info, nil 207 } 208 209 func (c *modelsCommand) getAllModels() ([]base.UserModel, error) { 210 client, err := c.getSysAPI() 211 if err != nil { 212 return nil, errors.Trace(err) 213 } 214 defer client.Close() 215 return client.AllModels() 216 } 217 218 func (c *modelsCommand) getUserModels() ([]base.UserModel, error) { 219 client, err := c.getModelManagerAPI() 220 if err != nil { 221 return nil, errors.Trace(err) 222 } 223 defer client.Close() 224 return client.ListModels(c.user) 225 } 226 227 // formatTabular takes an interface{} to adhere to the cmd.Formatter interface 228 func (c *modelsCommand) formatTabular(value interface{}) ([]byte, error) { 229 modelSet, ok := value.(ModelSet) 230 if !ok { 231 return nil, errors.Errorf("expected value of type %T, got %T", modelSet, value) 232 } 233 var out bytes.Buffer 234 const ( 235 // To format things into columns. 236 minwidth = 0 237 tabwidth = 1 238 padding = 2 239 padchar = ' ' 240 flags = 0 241 ) 242 tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags) 243 fmt.Fprintf(tw, "NAME") 244 if c.listUUID { 245 fmt.Fprintf(tw, "\tMODEL UUID") 246 } 247 fmt.Fprintf(tw, "\tOWNER\tSTATUS\tLAST CONNECTION\n") 248 for _, model := range modelSet.Models { 249 name := model.Name 250 if name == modelSet.CurrentModel { 251 name += "*" 252 } 253 fmt.Fprintf(tw, "%s", name) 254 if c.listUUID { 255 fmt.Fprintf(tw, "\t%s", model.UUID) 256 } 257 user := names.NewUserTag(c.user).Canonical() 258 lastConnection := model.Users[user].LastConnection 259 if lastConnection == "" { 260 lastConnection = "never connected" 261 } 262 fmt.Fprintf(tw, "\t%s\t%s\t%s\n", model.Owner, model.Status.Current, lastConnection) 263 } 264 tw.Flush() 265 return out.Bytes(), nil 266 }