github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/cloud/listcredentials.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloud 5 6 import ( 7 "bytes" 8 "fmt" 9 "sort" 10 "strings" 11 "text/tabwriter" 12 13 "github.com/juju/cmd" 14 "github.com/juju/errors" 15 "launchpad.net/gnuflag" 16 17 jujucloud "github.com/juju/juju/cloud" 18 "github.com/juju/juju/cmd/juju/common" 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/jujuclient" 21 ) 22 23 var usageListCredentialsSummary = ` 24 Lists credentials for a cloud.`[1:] 25 26 var usageListCredentialsDetails = ` 27 Credentials are used with `[1:] + "`juju bootstrap`" + ` and ` + "`juju create-model`" + `. 28 An arbitrary "credential name" is used to represent credentials, which are 29 added either via ` + "`juju add-credential` or `juju autoload-credentials`" + `. 30 Note that there can be multiple sets of credentials and thus multiple 31 names. 32 Actual authentication material is exposed with the '--show-secrets' 33 option. 34 A controller and subsequently created models can be created with a 35 different set of credentials but any action taken within the model (e.g.: 36 ` + "`juju deploy`; `juju add-unit`" + `) applies the set used to create the model. 37 Recall that when a controller is created a 'default' model is also 38 created. 39 Credentials denoted with an asterisk '*' are currently set as the default 40 for the given cloud. 41 42 Examples: 43 juju list-credentials 44 juju list-credentials aws 45 juju list-credentials --format yaml --show-secrets 46 47 See also: 48 add-credential 49 remove-credential 50 set-default-credential 51 autoload-credentials` 52 53 type listCredentialsCommand struct { 54 cmd.CommandBase 55 out cmd.Output 56 cloudName string 57 showSecrets bool 58 59 store jujuclient.CredentialGetter 60 personalCloudsFunc func() (map[string]jujucloud.Cloud, error) 61 cloudByNameFunc func(string) (*jujucloud.Cloud, error) 62 } 63 64 type credentialsMap struct { 65 Credentials map[string]jujucloud.CloudCredential `yaml:"credentials" json:"credentials"` 66 } 67 68 // NewListCredentialsCommand returns a command to list cloud credentials. 69 func NewListCredentialsCommand() cmd.Command { 70 return &listCredentialsCommand{ 71 store: jujuclient.NewFileCredentialStore(), 72 cloudByNameFunc: jujucloud.CloudByName, 73 } 74 } 75 76 func (c *listCredentialsCommand) Info() *cmd.Info { 77 return &cmd.Info{ 78 Name: "list-credentials", 79 Args: "[<cloud name>]", 80 Purpose: usageListCredentialsSummary, 81 Doc: usageListCredentialsDetails, 82 } 83 } 84 85 func (c *listCredentialsCommand) SetFlags(f *gnuflag.FlagSet) { 86 f.BoolVar(&c.showSecrets, "show-secrets", false, "Show secrets") 87 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 88 "yaml": cmd.FormatYaml, 89 "json": cmd.FormatJson, 90 "tabular": formatCredentialsTabular, 91 }) 92 } 93 94 func (c *listCredentialsCommand) Init(args []string) error { 95 cloudName, err := cmd.ZeroOrOneArgs(args) 96 if err != nil { 97 return errors.Trace(err) 98 } 99 c.cloudName = cloudName 100 return nil 101 } 102 103 func (c *listCredentialsCommand) personalClouds() (map[string]jujucloud.Cloud, error) { 104 if c.personalCloudsFunc == nil { 105 return jujucloud.PersonalCloudMetadata() 106 } 107 return c.personalCloudsFunc() 108 } 109 110 // displayCloudName returns the provided cloud name prefixed 111 // with "local:" if it is a local cloud. 112 func displayCloudName(cloudName string, personalCloudNames []string) string { 113 for _, name := range personalCloudNames { 114 if cloudName == name { 115 return localPrefix + cloudName 116 } 117 } 118 return cloudName 119 } 120 121 func (c *listCredentialsCommand) Run(ctxt *cmd.Context) error { 122 var credentials map[string]jujucloud.CloudCredential 123 credentials, err := c.store.AllCredentials() 124 if err != nil && !errors.IsNotFound(err) { 125 return err 126 } 127 if c.cloudName != "" { 128 for cloudName := range credentials { 129 if cloudName != c.cloudName { 130 delete(credentials, cloudName) 131 } 132 } 133 } 134 135 // Find local cloud names. 136 personalClouds, err := c.personalClouds() 137 if err != nil { 138 return err 139 } 140 var personalCloudNames []string 141 for name := range personalClouds { 142 personalCloudNames = append(personalCloudNames, name) 143 } 144 145 displayCredentials := make(map[string]jujucloud.CloudCredential) 146 for cloudName, cred := range credentials { 147 if !c.showSecrets { 148 if err := c.removeSecrets(cloudName, &cred); err != nil { 149 return errors.Annotatef(err, "removing secrets from credentials for cloud %v", cloudName) 150 } 151 } 152 displayCredentials[displayCloudName(cloudName, personalCloudNames)] = cred 153 } 154 return c.out.Write(ctxt, credentialsMap{displayCredentials}) 155 } 156 157 func (c *listCredentialsCommand) removeSecrets(cloudName string, cloudCred *jujucloud.CloudCredential) error { 158 cloud, err := common.CloudOrProvider(cloudName, c.cloudByNameFunc) 159 if err != nil { 160 return err 161 } 162 provider, err := environs.Provider(cloud.Type) 163 if err != nil { 164 return err 165 } 166 schemas := provider.CredentialSchemas() 167 for name, cred := range cloudCred.AuthCredentials { 168 sanitisedCred, err := jujucloud.RemoveSecrets(cred, schemas) 169 if err != nil { 170 return err 171 } 172 cloudCred.AuthCredentials[name] = *sanitisedCred 173 } 174 return nil 175 } 176 177 // formatCredentialsTabular returns a tabular summary of cloud information. 178 func formatCredentialsTabular(value interface{}) ([]byte, error) { 179 credentials, ok := value.(credentialsMap) 180 if !ok { 181 return nil, errors.Errorf("expected value of type %T, got %T", credentials, value) 182 } 183 184 // For tabular we'll sort alphabetically by cloud, and then by credential name. 185 var cloudNames []string 186 for name := range credentials.Credentials { 187 cloudNames = append(cloudNames, name) 188 } 189 sort.Strings(cloudNames) 190 191 var out bytes.Buffer 192 const ( 193 // To format things into columns. 194 minwidth = 0 195 tabwidth = 1 196 padding = 2 197 padchar = ' ' 198 flags = 0 199 ) 200 tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags) 201 p := func(values ...string) { 202 text := strings.Join(values, "\t") 203 fmt.Fprintln(tw, text) 204 } 205 p("CLOUD\tCREDENTIALS") 206 for _, cloudName := range cloudNames { 207 var haveDefault bool 208 var credentialNames []string 209 credentials := credentials.Credentials[cloudName] 210 for credentialName := range credentials.AuthCredentials { 211 if credentialName == credentials.DefaultCredential { 212 credentialNames = append([]string{credentialName + "*"}, credentialNames...) 213 haveDefault = true 214 } else { 215 credentialNames = append(credentialNames, credentialName) 216 } 217 } 218 if haveDefault { 219 sort.Strings(credentialNames[1:]) 220 } else { 221 sort.Strings(credentialNames) 222 } 223 p(cloudName, strings.Join(credentialNames, ", ")) 224 } 225 tw.Flush() 226 227 return out.Bytes(), nil 228 }