1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
10
"github.com/juju/utils"
12
"github.com/juju/juju/cloud"
13
"github.com/juju/juju/environs"
14
"github.com/juju/juju/jujuclient"
18
// ErrMultipleCredentials is the error returned by DetectCredential
19
// if more than one credential is detected.
20
ErrMultipleCredentials = errors.New("more than one credential detected")
23
// GetCredentials returns a curated set of credential values for a given cloud.
24
// The credential key values are read from the credentials store and the provider
25
// finalises the values to resolve things like json files.
26
// If region is not specified, the default credential region is used.
28
store jujuclient.CredentialGetter, region, credentialName, cloudName, cloudType string,
29
) (_ *cloud.Credential, chosenCredentialName, regionName string, _ error) {
31
credential, credentialName, defaultRegion, err := credentialByName(
32
store, cloudName, credentialName,
35
return nil, "", "", errors.Trace(err)
40
regionName = defaultRegion
43
readFile := func(f string) ([]byte, error) {
44
f, err := utils.NormalizePath(f)
46
return nil, errors.Trace(err)
48
return ioutil.ReadFile(f)
51
// Finalize credential against schemas supported by the provider.
52
provider, err := environs.Provider(cloudType)
54
return nil, "", "", errors.Trace(err)
57
credential, err = cloud.FinalizeCredential(
58
*credential, provider.CredentialSchemas(), readFile,
61
return nil, "", "", errors.Annotatef(
62
err, "validating %q credential for cloud %q",
63
credentialName, cloudName,
66
return credential, credentialName, regionName, nil
69
// credentialByName returns the credential and default region to use for the
70
// specified cloud, optionally specifying a credential name. If no credential
71
// name is specified, then use the default credential for the cloud if one has
72
// been specified. The credential name is returned also, in case the default
73
// credential is used. If there is only one credential, it is implicitly the
76
// If there exists no matching credentials, an error satisfying
77
// errors.IsNotFound will be returned.
78
func credentialByName(
79
store jujuclient.CredentialGetter, cloudName, credentialName string,
80
) (_ *cloud.Credential, credentialNameUsed string, defaultRegion string, _ error) {
82
cloudCredentials, err := store.CredentialForCloud(cloudName)
84
return nil, "", "", errors.Annotate(err, "loading credentials")
86
if credentialName == "" {
87
// No credential specified, so use the default for the cloud.
88
credentialName = cloudCredentials.DefaultCredential
89
if credentialName == "" && len(cloudCredentials.AuthCredentials) == 1 {
90
for credentialName = range cloudCredentials.AuthCredentials {
94
credential, ok := cloudCredentials.AuthCredentials[credentialName]
96
return nil, "", "", errors.NotFoundf(
97
"%q credential for cloud %q", credentialName, cloudName,
100
return &credential, credentialName, cloudCredentials.DefaultRegion, nil
103
// DetectCredential detects credentials for the specified cloud type, and, if
104
// exactly one is detected, returns it.
106
// If no credentials are detected, an error satisfying errors.IsNotFound will
107
// be returned. If more than one credential is detected, ErrMultipleCredentials
109
func DetectCredential(cloudName, cloudType string) (*cloud.CloudCredential, error) {
110
provider, err := environs.Provider(cloudType)
112
return nil, errors.Trace(err)
114
detected, err := provider.DetectCredentials()
116
return nil, errors.Annotatef(
117
err, "detecting credentials for %q cloud provider", cloudName,
120
logger.Tracef("provider detected credentials: %v", detected)
121
if len(detected.AuthCredentials) == 0 {
122
return nil, errors.NotFoundf("credentials for cloud %q", cloudName)
124
if len(detected.AuthCredentials) > 1 {
125
return nil, ErrMultipleCredentials