github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/modelcmd/credentials.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelcmd 5 6 import ( 7 "io/ioutil" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/utils" 12 13 "github.com/juju/juju/cloud" 14 "github.com/juju/juju/environs" 15 "github.com/juju/juju/jujuclient" 16 ) 17 18 var ( 19 // ErrMultipleCredentials is the error returned by DetectCredential 20 // if more than one credential is detected. 21 ErrMultipleCredentials = errors.New("more than one credential detected") 22 ) 23 24 // GetCredentialsParams contains parameters for the GetCredentials function. 25 type GetCredentialsParams struct { 26 // Cloud is the cloud definition. 27 Cloud cloud.Cloud 28 29 // CloudName is the name of the cloud for which credentials are being 30 // obtained. 31 CloudName string 32 33 // CloudRegion is the name of the region that the user has specified. 34 // If this is empty, then GetCredentials will determine the default 35 // region, and return that. The default region is the one set by the 36 // user in credentials.yaml, or if there is none set, the first region 37 // in the cloud's list. 38 CloudRegion string 39 40 // CredentialName is the name of the credential to get. 41 CredentialName string 42 } 43 44 // GetCredentials returns a curated set of credential values for a given cloud. 45 // The credential key values are read from the credentials store and the provider 46 // finalises the values to resolve things like json files. 47 // If region is not specified, the default credential region is used. 48 func GetCredentials( 49 ctx *cmd.Context, 50 store jujuclient.CredentialGetter, 51 args GetCredentialsParams, 52 ) (_ *cloud.Credential, chosenCredentialName, regionName string, _ error) { 53 54 credential, credentialName, defaultRegion, err := credentialByName( 55 store, args.CloudName, args.CredentialName, 56 ) 57 if err != nil { 58 return nil, "", "", errors.Trace(err) 59 } 60 61 regionName = args.CloudRegion 62 if regionName == "" { 63 regionName = defaultRegion 64 if regionName == "" && len(args.Cloud.Regions) > 0 { 65 // No region was specified, use the first region 66 // in the list. 67 regionName = args.Cloud.Regions[0].Name 68 } 69 } 70 71 cloudEndpoint := args.Cloud.Endpoint 72 cloudIdentityEndpoint := args.Cloud.IdentityEndpoint 73 if regionName != "" { 74 region, err := cloud.RegionByName(args.Cloud.Regions, regionName) 75 if err != nil { 76 return nil, "", "", errors.Trace(err) 77 } 78 cloudEndpoint = region.Endpoint 79 cloudIdentityEndpoint = region.IdentityEndpoint 80 } 81 82 readFile := func(f string) ([]byte, error) { 83 f, err := utils.NormalizePath(f) 84 if err != nil { 85 return nil, errors.Trace(err) 86 } 87 return ioutil.ReadFile(f) 88 } 89 90 // Finalize credential against schemas supported by the provider. 91 provider, err := environs.Provider(args.Cloud.Type) 92 if err != nil { 93 return nil, "", "", errors.Trace(err) 94 } 95 96 credential, err = cloud.FinalizeCredential( 97 *credential, provider.CredentialSchemas(), readFile, 98 ) 99 if err != nil { 100 return nil, "", "", errors.Annotatef( 101 err, "finalizing %q credential for cloud %q", 102 credentialName, args.CloudName, 103 ) 104 } 105 106 credential, err = provider.FinalizeCredential( 107 ctx, environs.FinalizeCredentialParams{ 108 Credential: *credential, 109 CloudEndpoint: cloudEndpoint, 110 CloudIdentityEndpoint: cloudIdentityEndpoint, 111 }, 112 ) 113 if err != nil { 114 return nil, "", "", errors.Annotatef( 115 err, "finalizing %q credential for cloud %q", 116 credentialName, args.CloudName, 117 ) 118 } 119 120 return credential, credentialName, regionName, nil 121 } 122 123 // credentialByName returns the credential and default region to use for the 124 // specified cloud, optionally specifying a credential name. If no credential 125 // name is specified, then use the default credential for the cloud if one has 126 // been specified. The credential name is returned also, in case the default 127 // credential is used. If there is only one credential, it is implicitly the 128 // default. 129 // 130 // If there exists no matching credentials, an error satisfying 131 // errors.IsNotFound will be returned. 132 func credentialByName( 133 store jujuclient.CredentialGetter, cloudName, credentialName string, 134 ) (_ *cloud.Credential, credentialNameUsed string, defaultRegion string, _ error) { 135 136 cloudCredentials, err := store.CredentialForCloud(cloudName) 137 if err != nil { 138 return nil, "", "", errors.Annotate(err, "loading credentials") 139 } 140 if credentialName == "" { 141 credentialName = cloudCredentials.DefaultCredential 142 if credentialName == "" { 143 // No credential specified, but there's more than one. 144 if len(cloudCredentials.AuthCredentials) > 1 { 145 return nil, "", "", ErrMultipleCredentials 146 } 147 // No credential specified, so use the default for the cloud. 148 for credentialName = range cloudCredentials.AuthCredentials { 149 } 150 } 151 } 152 credential, ok := cloudCredentials.AuthCredentials[credentialName] 153 if !ok { 154 return nil, "", "", errors.NotFoundf( 155 "%q credential for cloud %q", credentialName, cloudName, 156 ) 157 } 158 return &credential, credentialName, cloudCredentials.DefaultRegion, nil 159 } 160 161 // DetectCredential detects credentials for the specified cloud type, and, if 162 // exactly one is detected, returns it. 163 // 164 // If no credentials are detected, an error satisfying errors.IsNotFound will 165 // be returned. If more than one credential is detected, ErrMultipleCredentials 166 // will be returned. 167 func DetectCredential(cloudName, cloudType string) (*cloud.CloudCredential, error) { 168 provider, err := environs.Provider(cloudType) 169 if err != nil { 170 return nil, errors.Trace(err) 171 } 172 detected, err := provider.DetectCredentials() 173 if err != nil { 174 return nil, errors.Annotatef( 175 err, "detecting credentials for %q cloud provider", cloudName, 176 ) 177 } 178 logger.Tracef("provider detected credentials: %v", detected) 179 if len(detected.AuthCredentials) == 0 { 180 return nil, errors.NotFoundf("credentials for cloud %q", cloudName) 181 } 182 if len(detected.AuthCredentials) > 1 { 183 return nil, ErrMultipleCredentials 184 } 185 return detected, nil 186 }