github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/controller/listcontrollers.go (about) 1 // Copyright 2015,2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller 5 6 import ( 7 "fmt" 8 "strings" 9 "sync" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/gnuflag" 14 "gopkg.in/juju/names.v2" 15 16 "github.com/juju/juju/api/base" 17 "github.com/juju/juju/api/controller" 18 jujucmd "github.com/juju/juju/cmd" 19 "github.com/juju/juju/cmd/modelcmd" 20 "github.com/juju/juju/core/status" 21 "github.com/juju/juju/environs/bootstrap" 22 "github.com/juju/juju/jujuclient" 23 ) 24 25 var helpControllersSummary = ` 26 Lists all controllers.`[1:] 27 28 var helpControllersDetails = ` 29 The output format may be selected with the '--format' option. In the 30 default tabular output, the current controller is marked with an asterisk. 31 32 Examples: 33 juju controllers 34 juju controllers --format json --output ~/tmp/controllers.json 35 36 See also: 37 models 38 show-controller`[1:] 39 40 // NewListControllersCommand returns a command to list registered controllers. 41 func NewListControllersCommand() cmd.Command { 42 cmd := &listControllersCommand{ 43 store: jujuclient.NewFileClientStore(), 44 } 45 return modelcmd.WrapBase(cmd) 46 } 47 48 // Info implements Command.Info 49 func (c *listControllersCommand) Info() *cmd.Info { 50 return jujucmd.Info(&cmd.Info{ 51 Name: "controllers", 52 Purpose: helpControllersSummary, 53 Doc: helpControllersDetails, 54 Aliases: []string{"list-controllers"}, 55 }) 56 } 57 58 // SetFlags implements Command.SetFlags. 59 func (c *listControllersCommand) SetFlags(f *gnuflag.FlagSet) { 60 c.CommandBase.SetFlags(f) 61 f.BoolVar(&c.refresh, "refresh", false, "Connect to each controller to download the latest details") 62 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 63 "yaml": cmd.FormatYaml, 64 "json": cmd.FormatJson, 65 "tabular": c.formatControllersListTabular, 66 }) 67 } 68 69 func (c *listControllersCommand) getAPI(controllerName string) (ControllerAccessAPI, error) { 70 if c.api != nil { 71 return c.api(controllerName), nil 72 } 73 api, err := c.NewAPIRoot(c.store, controllerName, "") 74 if err != nil { 75 return nil, errors.Annotate(err, "opening API connection") 76 } 77 return controller.NewClient(api), nil 78 } 79 80 // Run implements Command.Run 81 func (c *listControllersCommand) Run(ctx *cmd.Context) error { 82 controllers, err := c.store.AllControllers() 83 if err != nil { 84 return errors.Annotate(err, "failed to list controllers") 85 } 86 if len(controllers) == 0 && c.out.Name() == "tabular" { 87 return errors.Trace(modelcmd.ErrNoControllersDefined) 88 } 89 if c.refresh && len(controllers) > 0 { 90 var wg sync.WaitGroup 91 wg.Add(len(controllers)) 92 for controllerName := range controllers { 93 name := controllerName 94 go func() { 95 defer wg.Done() 96 client, err := c.getAPI(name) 97 if err != nil { 98 fmt.Fprintf(ctx.GetStderr(), "error connecting to api for %q: %v\n", name, err) 99 return 100 } 101 defer client.Close() 102 if err := c.refreshControllerDetails(client, name); err != nil { 103 fmt.Fprintf(ctx.GetStderr(), "error updating cached details for %q: %v\n", name, err) 104 } 105 }() 106 } 107 wg.Wait() 108 // Reload controller details 109 controllers, err = c.store.AllControllers() 110 if err != nil { 111 return errors.Annotate(err, "failed to list controllers") 112 } 113 } 114 details, errs := c.convertControllerDetails(controllers) 115 if len(errs) > 0 { 116 fmt.Fprintln(ctx.Stderr, strings.Join(errs, "\n")) 117 } 118 currentController, err := c.store.CurrentController() 119 if errors.IsNotFound(err) { 120 currentController = "" 121 } else if err != nil { 122 return errors.Annotate(err, "getting current controller") 123 } 124 controllerSet := ControllerSet{ 125 Controllers: details, 126 CurrentController: currentController, 127 } 128 return c.out.Write(ctx, controllerSet) 129 } 130 131 func (c *listControllersCommand) refreshControllerDetails(client ControllerAccessAPI, controllerName string) error { 132 // First, get all the models the user can see, and their details. 133 allModels, err := client.AllModels() 134 if err != nil { 135 return err 136 } 137 // Update client store. 138 if err := c.SetControllerModels(c.store, controllerName, allModels); err != nil { 139 return errors.Trace(err) 140 } 141 142 var controllerModelUUID string 143 modelTags := make([]names.ModelTag, len(allModels)) 144 for i, m := range allModels { 145 modelTags[i] = names.NewModelTag(m.UUID) 146 if m.Name == bootstrap.ControllerModelName { 147 controllerModelUUID = m.UUID 148 } 149 } 150 modelStatus, err := client.ModelStatus(modelTags...) 151 if err != nil { 152 return err 153 } 154 155 c.mu.Lock() 156 defer c.mu.Unlock() 157 // Use the model information to update the cached controller details. 158 details, err := c.store.ControllerByName(controllerName) 159 if err != nil { 160 return err 161 } 162 163 machineCount := 0 164 for _, s := range modelStatus { 165 if s.Error != nil { 166 if errors.IsNotFound(s.Error) { 167 // This most likely occurred because a model was 168 // destroyed half-way through the call. 169 continue 170 } 171 return errors.Trace(s.Error) 172 } 173 machineCount += s.TotalMachineCount 174 } 175 details.MachineCount = &machineCount 176 details.ActiveControllerMachineCount, details.ControllerMachineCount = ControllerMachineCounts(controllerModelUUID, modelStatus) 177 return c.store.UpdateController(controllerName, *details) 178 } 179 180 func ControllerMachineCounts(controllerModelUUID string, modelStatusResults []base.ModelStatus) (activeCount, totalCount int) { 181 for _, s := range modelStatusResults { 182 if s.Error != nil { 183 // This most likely occurred because a model was 184 // destroyed half-way through the call. 185 continue 186 } 187 if s.UUID != controllerModelUUID { 188 continue 189 } 190 for _, m := range s.Machines { 191 if !m.WantsVote { 192 continue 193 } 194 totalCount++ 195 if m.Status != string(status.Down) && m.HasVote { 196 activeCount++ 197 } 198 } 199 } 200 return activeCount, totalCount 201 } 202 203 type listControllersCommand struct { 204 modelcmd.CommandBase 205 206 out cmd.Output 207 store jujuclient.ClientStore 208 api func(controllerName string) ControllerAccessAPI 209 refresh bool 210 mu sync.Mutex 211 }