github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "fmt" 8 "io" 9 "sort" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/gnuflag" 15 16 jujucloud "github.com/juju/juju/cloud" 17 "github.com/juju/juju/cmd/juju/common" 18 "github.com/juju/juju/cmd/output" 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 add-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 credentials 44 juju credentials aws 45 juju 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 // CloudCredential contains attributes used to define credentials for a cloud. 65 type CloudCredential struct { 66 // DefaultCredential is the named credential to use by default. 67 DefaultCredential string `json:"default-credential,omitempty" yaml:"default-credential,omitempty"` 68 69 // DefaultRegion is the cloud region to use by default. 70 DefaultRegion string `json:"default-region,omitempty" yaml:"default-region,omitempty"` 71 72 // Credentials is the collection of all credentials registered by the user for a cloud, keyed on a cloud name. 73 Credentials map[string]Credential `json:"cloud-credentials,omitempty" yaml:",omitempty,inline"` 74 } 75 76 // Credential instances represent cloud credentials. 77 type Credential struct { 78 // AuthType determines authentication type for the credential. 79 AuthType string `json:"auth-type" yaml:"auth-type"` 80 81 // Attributes define details for individual credential. 82 // This collection is provider-specific: each provider is interested in different credential details. 83 Attributes map[string]string `json:"details,omitempty" yaml:",omitempty,inline"` 84 85 // Revoked is true if the credential has been revoked. 86 Revoked bool `json:"revoked,omitempty" yaml:"revoked,omitempty"` 87 88 // Label is optionally set to describe the credentials to a user. 89 Label string `json:"label,omitempty" yaml:"label,omitempty"` 90 } 91 92 type credentialsMap struct { 93 Credentials map[string]CloudCredential `yaml:"credentials" json:"credentials"` 94 } 95 96 // NewListCredentialsCommand returns a command to list cloud credentials. 97 func NewListCredentialsCommand() cmd.Command { 98 return &listCredentialsCommand{ 99 store: jujuclient.NewFileCredentialStore(), 100 cloudByNameFunc: jujucloud.CloudByName, 101 } 102 } 103 104 func (c *listCredentialsCommand) Info() *cmd.Info { 105 return &cmd.Info{ 106 Name: "credentials", 107 Args: "[<cloud name>]", 108 Purpose: usageListCredentialsSummary, 109 Doc: usageListCredentialsDetails, 110 Aliases: []string{"list-credentials"}, 111 } 112 } 113 114 func (c *listCredentialsCommand) SetFlags(f *gnuflag.FlagSet) { 115 c.CommandBase.SetFlags(f) 116 f.BoolVar(&c.showSecrets, "show-secrets", false, "Show secrets") 117 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 118 "yaml": cmd.FormatYaml, 119 "json": cmd.FormatJson, 120 "tabular": formatCredentialsTabular, 121 }) 122 } 123 124 func (c *listCredentialsCommand) Init(args []string) error { 125 cloudName, err := cmd.ZeroOrOneArgs(args) 126 if err != nil { 127 return errors.Trace(err) 128 } 129 c.cloudName = cloudName 130 return nil 131 } 132 133 func (c *listCredentialsCommand) personalClouds() (map[string]jujucloud.Cloud, error) { 134 if c.personalCloudsFunc == nil { 135 return jujucloud.PersonalCloudMetadata() 136 } 137 return c.personalCloudsFunc() 138 } 139 140 func (c *listCredentialsCommand) Run(ctxt *cmd.Context) error { 141 var credentials map[string]jujucloud.CloudCredential 142 credentials, err := c.store.AllCredentials() 143 if err != nil && !errors.IsNotFound(err) { 144 return err 145 } 146 if c.cloudName != "" { 147 for cloudName := range credentials { 148 if cloudName != c.cloudName { 149 delete(credentials, cloudName) 150 } 151 } 152 } 153 154 // Find local cloud names. 155 personalClouds, err := c.personalClouds() 156 if err != nil { 157 return err 158 } 159 var personalCloudNames []string 160 for name := range personalClouds { 161 personalCloudNames = append(personalCloudNames, name) 162 } 163 164 displayCredentials := make(map[string]CloudCredential) 165 for cloudName, cred := range credentials { 166 if !c.showSecrets { 167 if err := c.removeSecrets(cloudName, &cred); err != nil { 168 return errors.Annotatef(err, "removing secrets from credentials for cloud %v", cloudName) 169 } 170 } 171 displayCredential := CloudCredential{ 172 DefaultCredential: cred.DefaultCredential, 173 DefaultRegion: cred.DefaultRegion, 174 } 175 if len(cred.AuthCredentials) != 0 { 176 displayCredential.Credentials = make(map[string]Credential, len(cred.AuthCredentials)) 177 for credName, credDetails := range cred.AuthCredentials { 178 displayCredential.Credentials[credName] = Credential{ 179 string(credDetails.AuthType()), 180 credDetails.Attributes(), 181 credDetails.Revoked, 182 credDetails.Label, 183 } 184 } 185 } 186 displayCredentials[cloudName] = displayCredential 187 } 188 return c.out.Write(ctxt, credentialsMap{displayCredentials}) 189 } 190 191 func (c *listCredentialsCommand) removeSecrets(cloudName string, cloudCred *jujucloud.CloudCredential) error { 192 cloud, err := common.CloudOrProvider(cloudName, c.cloudByNameFunc) 193 if err != nil { 194 return err 195 } 196 provider, err := environs.Provider(cloud.Type) 197 if err != nil { 198 return err 199 } 200 schemas := provider.CredentialSchemas() 201 for name, cred := range cloudCred.AuthCredentials { 202 sanitisedCred, err := jujucloud.RemoveSecrets(cred, schemas) 203 if err != nil { 204 return err 205 } 206 cloudCred.AuthCredentials[name] = *sanitisedCred 207 } 208 return nil 209 } 210 211 // formatCredentialsTabular writes a tabular summary of cloud information. 212 func formatCredentialsTabular(writer io.Writer, value interface{}) error { 213 credentials, ok := value.(credentialsMap) 214 if !ok { 215 return errors.Errorf("expected value of type %T, got %T", credentials, value) 216 } 217 218 if len(credentials.Credentials) == 0 { 219 fmt.Fprintln(writer, "No credentials to display.") 220 return nil 221 } 222 223 // For tabular we'll sort alphabetically by cloud, and then by credential name. 224 var cloudNames []string 225 for name := range credentials.Credentials { 226 cloudNames = append(cloudNames, name) 227 } 228 sort.Strings(cloudNames) 229 230 tw := output.TabWriter(writer) 231 p := func(values ...string) { 232 text := strings.Join(values, "\t") 233 fmt.Fprintln(tw, text) 234 } 235 p("CLOUD\tCREDENTIALS") 236 for _, cloudName := range cloudNames { 237 var haveDefault bool 238 var credentialNames []string 239 credentials := credentials.Credentials[cloudName] 240 for credentialName := range credentials.Credentials { 241 if credentialName == credentials.DefaultCredential { 242 credentialNames = append([]string{credentialName + "*"}, credentialNames...) 243 haveDefault = true 244 } else { 245 credentialNames = append(credentialNames, credentialName) 246 } 247 } 248 if haveDefault { 249 sort.Strings(credentialNames[1:]) 250 } else { 251 sort.Strings(credentialNames) 252 } 253 p(cloudName, strings.Join(credentialNames, ", ")) 254 } 255 tw.Flush() 256 257 return nil 258 }