github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/cloud/list.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloud 5 6 import ( 7 "io" 8 "sort" 9 10 "github.com/juju/ansiterm" 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/gnuflag" 14 "github.com/juju/loggo" 15 16 jujucloud "github.com/juju/juju/cloud" 17 jujucmd "github.com/juju/juju/cmd" 18 "github.com/juju/juju/cmd/juju/common" 19 "github.com/juju/juju/cmd/output" 20 ) 21 22 var logger = loggo.GetLogger("juju.cmd.juju.cloud") 23 24 type listCloudsCommand struct { 25 cmd.CommandBase 26 out cmd.Output 27 } 28 29 // listCloudsDoc is multi-line since we need to use ` to denote 30 // commands for ease in markdown. 31 var listCloudsDoc = "" + 32 "Output includes fundamental properties for each cloud known to the\n" + 33 "current Juju client: name, number of regions, default region, type,\n" + 34 "and description.\n" + 35 "\nThe default output shows public clouds known to Juju out of the box.\n" + 36 "These may change between Juju versions. In addition to these public\n" + 37 "clouds, the 'localhost' cloud (local LXD) is also listed.\n" + 38 "\nThis command's default output format is 'tabular'.\n" + 39 "\nCloud metadata sometimes changes, e.g. AWS adds a new region. Use the\n" + 40 "`update-clouds` command to update the current Juju client accordingly.\n" + 41 "\nUse the `add-cloud` command to add a private cloud to the list of\n" + 42 "clouds known to the current Juju client.\n" + 43 "\nUse the `regions` command to list a cloud's regions. Use the\n" + 44 "`show-cloud` command to get more detail, such as regions and endpoints.\n" + 45 "\nFurther reading: https://docs.jujucharms.com/stable/clouds\n" + listCloudsDocExamples 46 47 var listCloudsDocExamples = ` 48 Examples: 49 50 juju clouds 51 juju clouds --format yaml 52 53 See also: 54 add-cloud 55 regions 56 show-cloud 57 update-clouds 58 ` 59 60 // NewListCloudsCommand returns a command to list cloud information. 61 func NewListCloudsCommand() cmd.Command { 62 return &listCloudsCommand{} 63 } 64 65 func (c *listCloudsCommand) Info() *cmd.Info { 66 return jujucmd.Info(&cmd.Info{ 67 Name: "clouds", 68 Purpose: "Lists all clouds available to Juju.", 69 Doc: listCloudsDoc, 70 Aliases: []string{"list-clouds"}, 71 }) 72 } 73 74 func (c *listCloudsCommand) SetFlags(f *gnuflag.FlagSet) { 75 c.CommandBase.SetFlags(f) 76 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 77 "yaml": cmd.FormatYaml, 78 "json": cmd.FormatJson, 79 "tabular": formatCloudsTabular, 80 }) 81 } 82 83 func (c *listCloudsCommand) Run(ctxt *cmd.Context) error { 84 details, err := listCloudDetails() 85 if err != nil { 86 return err 87 } 88 89 var output interface{} 90 switch c.out.Name() { 91 case "yaml", "json": 92 output = details.all() 93 default: 94 output = details 95 } 96 err = c.out.Write(ctxt, output) 97 if err != nil { 98 return err 99 } 100 return nil 101 } 102 103 type cloudList struct { 104 public map[string]*CloudDetails 105 builtin map[string]*CloudDetails 106 personal map[string]*CloudDetails 107 } 108 109 func newCloudList() *cloudList { 110 return &cloudList{ 111 make(map[string]*CloudDetails), 112 make(map[string]*CloudDetails), 113 make(map[string]*CloudDetails), 114 } 115 } 116 117 func (c *cloudList) all() map[string]*CloudDetails { 118 if len(c.personal) == 0 && len(c.builtin) == 0 && len(c.personal) == 0 { 119 return nil 120 } 121 122 result := make(map[string]*CloudDetails) 123 addAll := func(someClouds map[string]*CloudDetails) { 124 for name, cloud := range someClouds { 125 result[name] = cloud 126 } 127 } 128 129 addAll(c.public) 130 addAll(c.builtin) 131 addAll(c.personal) 132 return result 133 } 134 135 func listCloudDetails() (*cloudList, error) { 136 clouds, _, err := jujucloud.PublicCloudMetadata(jujucloud.JujuPublicCloudsPath()) 137 if err != nil { 138 return nil, err 139 } 140 details := newCloudList() 141 for name, cloud := range clouds { 142 cloudDetails := makeCloudDetails(cloud) 143 details.public[name] = cloudDetails 144 } 145 146 // Add in built in clouds like localhost (lxd). 147 builtinClouds, err := common.BuiltInClouds() 148 if err != nil { 149 return nil, errors.Trace(err) 150 } 151 for name, cloud := range builtinClouds { 152 cloudDetails := makeCloudDetails(cloud) 153 cloudDetails.Source = "built-in" 154 details.builtin[name] = cloudDetails 155 } 156 157 personalClouds, err := jujucloud.PersonalCloudMetadata() 158 if err != nil { 159 return nil, err 160 } 161 for name, cloud := range personalClouds { 162 cloudDetails := makeCloudDetails(cloud) 163 cloudDetails.Source = "local" 164 details.personal[name] = cloudDetails 165 // Delete any built-in or public clouds with same name. 166 delete(details.builtin, name) 167 delete(details.public, name) 168 } 169 170 return details, nil 171 } 172 173 // formatCloudsTabular writes a tabular summary of cloud information. 174 func formatCloudsTabular(writer io.Writer, value interface{}) error { 175 clouds, ok := value.(*cloudList) 176 if !ok { 177 return errors.Errorf("expected value of type %T, got %T", clouds, value) 178 } 179 180 tw := output.TabWriter(writer) 181 w := output.Wrapper{tw} 182 w.Println("Cloud", "Regions", "Default", "Type", "Description") 183 w.SetColumnAlignRight(1) 184 185 cloudNamesSorted := func(someClouds map[string]*CloudDetails) []string { 186 // For tabular we'll sort alphabetically, user clouds last. 187 var names []string 188 for name := range someClouds { 189 names = append(names, name) 190 } 191 sort.Strings(names) 192 return names 193 } 194 195 printClouds := func(someClouds map[string]*CloudDetails, color *ansiterm.Context) { 196 cloudNames := cloudNamesSorted(someClouds) 197 198 for _, name := range cloudNames { 199 info := someClouds[name] 200 defaultRegion := "" 201 if len(info.Regions) > 0 { 202 defaultRegion = info.RegionsMap[info.Regions[0].Key.(string)].Name 203 } 204 description := info.CloudDescription 205 if len(description) > 40 { 206 description = description[:39] 207 } 208 w.PrintColor(color, name) 209 w.Println(len(info.Regions), defaultRegion, info.CloudType, description) 210 } 211 } 212 printClouds(clouds.public, nil) 213 printClouds(clouds.builtin, nil) 214 printClouds(clouds.personal, ansiterm.Foreground(ansiterm.BrightBlue)) 215 216 tw.Flush() 217 return nil 218 }