github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/cloud/showcredential.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloud 5 6 import ( 7 "github.com/juju/cmd" 8 "github.com/juju/errors" 9 "github.com/juju/gnuflag" 10 11 apicloud "github.com/juju/juju/api/cloud" 12 "github.com/juju/juju/apiserver/params" 13 jujucmd "github.com/juju/juju/cmd" 14 "github.com/juju/juju/cmd/modelcmd" 15 "github.com/juju/juju/jujuclient" 16 ) 17 18 type showCredentialCommand struct { 19 modelcmd.CommandBase 20 store jujuclient.ClientStore 21 22 out cmd.Output 23 24 newAPIFunc func() (CredentialContentAPI, error) 25 26 CloudName string 27 CredentialName string 28 29 ShowSecrets bool 30 } 31 32 // NewShowCredentialCommand returns a command to show information about 33 // credentials stored on the controller. 34 func NewShowCredentialCommand() cmd.Command { 35 cmd := &showCredentialCommand{ 36 store: jujuclient.NewFileClientStore(), 37 } 38 cmd.newAPIFunc = func() (CredentialContentAPI, error) { 39 return cmd.NewCredentialAPI() 40 } 41 return modelcmd.WrapBase(cmd) 42 } 43 44 func (c *showCredentialCommand) SetFlags(f *gnuflag.FlagSet) { 45 c.CommandBase.SetFlags(f) 46 // We only support yaml for display purposes. 47 c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ 48 "yaml": cmd.FormatYaml, 49 }) 50 f.BoolVar(&c.ShowSecrets, "show-secrets", false, "Display credential secret attributes") 51 } 52 53 func (c *showCredentialCommand) Init(args []string) error { 54 switch len(args) { 55 case 0: 56 // will get all credentials stored on the controller for this user. 57 break 58 case 1: 59 return errors.New("both cloud and credential name are needed") 60 case 2: 61 c.CloudName = args[0] 62 c.CredentialName = args[1] 63 default: 64 return errors.New("only cloud and credential names are supported") 65 } 66 return nil 67 } 68 69 func (c *showCredentialCommand) Info() *cmd.Info { 70 return jujucmd.Info(&cmd.Info{ 71 Name: "show-credential", 72 Args: "[<cloud name> <credential name>]", 73 Purpose: "Shows credential information on a controller.", 74 Doc: showCredentialDoc, 75 Aliases: []string{"show-credentials"}, 76 }) 77 } 78 79 func (c *showCredentialCommand) Run(ctxt *cmd.Context) error { 80 client, err := c.newAPIFunc() 81 if err != nil { 82 return err 83 } 84 defer client.Close() 85 86 if client.BestAPIVersion() < 2 { 87 ctxt.Infof("credential content lookup is not supported by this version of Juju") 88 return nil 89 } 90 contents, err := client.CredentialContents(c.CloudName, c.CredentialName, c.ShowSecrets) 91 if err != nil { 92 ctxt.Infof("Getting credential content failed with: %v", err) 93 return err 94 } 95 return c.parseContents(ctxt, contents) 96 } 97 98 type CredentialContentAPI interface { 99 CredentialContents(cloud, credential string, withSecrets bool) ([]params.CredentialContentResult, error) 100 BestAPIVersion() int 101 Close() error 102 } 103 104 func (c *showCredentialCommand) NewCredentialAPI() (CredentialContentAPI, error) { 105 currentController, err := c.store.CurrentController() 106 if err != nil { 107 if errors.IsNotFound(err) { 108 return nil, errors.New("there is no active controller") 109 } 110 return nil, errors.Trace(err) 111 } 112 api, err := c.NewAPIRoot(c.store, currentController, "") 113 if err != nil { 114 return nil, errors.Annotate(err, "opening API connection") 115 } 116 return apicloud.NewClient(api), nil 117 } 118 119 type CredentialContent struct { 120 AuthType string `yaml:"auth-type"` 121 Attributes map[string]string `yaml:",inline"` 122 } 123 124 type CredentialDetails struct { 125 Content CredentialContent `yaml:"content"` 126 Models map[string]string `yaml:"models"` 127 } 128 129 type NamedCredentials map[string]CredentialDetails 130 131 type CloudCredentials map[string]NamedCredentials 132 133 type ControllerCredentials struct { 134 All CloudCredentials `yaml:"controller-credentials"` 135 } 136 137 func (c *showCredentialCommand) parseContents(ctxt *cmd.Context, in []params.CredentialContentResult) error { 138 if len(in) == 0 { 139 ctxt.Infof("No credential to display") 140 return nil 141 } 142 143 out := CloudCredentials{} 144 for _, result := range in { 145 if result.Error != nil { 146 ctxt.Infof("%v", result.Error) 147 continue 148 } 149 150 info := result.Result 151 _, ok := out[info.Content.Cloud] 152 if !ok { 153 out[info.Content.Cloud] = NamedCredentials{} 154 //cloudGroup = out[info.Content.Cloud] 155 } 156 157 models := make(map[string]string, len(info.Models)) 158 for _, m := range info.Models { 159 ownerAccess := m.Access 160 if ownerAccess == "" { 161 ownerAccess = "no access" 162 } 163 models[m.Model] = ownerAccess 164 } 165 out[info.Content.Cloud][info.Content.Name] = CredentialDetails{ 166 Content: CredentialContent{ 167 AuthType: info.Content.AuthType, 168 Attributes: info.Content.Attributes, 169 }, 170 Models: models, 171 } 172 } 173 return c.out.Write(ctxt, ControllerCredentials{out}) 174 } 175 176 var showCredentialDoc = ` 177 This command displays information about credential(s) stored on the controller 178 for this user. 179 180 To see the contents of a specific credential, supply its cloud and name. 181 To see all credentials stored for you, supply no arguments. 182 183 To see secrets, content attributes marked as hidden, use --show-secrets option. 184 185 To see locally stored credentials, use "juju credentials' command. 186 187 Examples: 188 189 juju show-credential google my-admin-credential 190 juju show-credentials 191 juju show-credentials --show-secrets 192 193 See also: 194 credentials 195 `