github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/errors"
    10  	"github.com/juju/utils"
    11  
    12  	"github.com/juju/juju/cloud"
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/jujuclient"
    15  )
    16  
    17  var (
    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")
    21  )
    22  
    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.
    27  func GetCredentials(
    28  	store jujuclient.CredentialGetter, region, credentialName, cloudName, cloudType string,
    29  ) (_ *cloud.Credential, chosenCredentialName, regionName string, _ error) {
    30  
    31  	credential, credentialName, defaultRegion, err := credentialByName(
    32  		store, cloudName, credentialName,
    33  	)
    34  	if err != nil {
    35  		return nil, "", "", errors.Trace(err)
    36  	}
    37  
    38  	regionName = region
    39  	if regionName == "" {
    40  		regionName = defaultRegion
    41  	}
    42  
    43  	readFile := func(f string) ([]byte, error) {
    44  		f, err := utils.NormalizePath(f)
    45  		if err != nil {
    46  			return nil, errors.Trace(err)
    47  		}
    48  		return ioutil.ReadFile(f)
    49  	}
    50  
    51  	// Finalize credential against schemas supported by the provider.
    52  	provider, err := environs.Provider(cloudType)
    53  	if err != nil {
    54  		return nil, "", "", errors.Trace(err)
    55  	}
    56  
    57  	credential, err = cloud.FinalizeCredential(
    58  		*credential, provider.CredentialSchemas(), readFile,
    59  	)
    60  	if err != nil {
    61  		return nil, "", "", errors.Annotatef(
    62  			err, "validating %q credential for cloud %q",
    63  			credentialName, cloudName,
    64  		)
    65  	}
    66  	return credential, credentialName, regionName, nil
    67  }
    68  
    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
    74  // default.
    75  //
    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) {
    81  
    82  	cloudCredentials, err := store.CredentialForCloud(cloudName)
    83  	if err != nil {
    84  		return nil, "", "", errors.Annotate(err, "loading credentials")
    85  	}
    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 {
    91  			}
    92  		}
    93  	}
    94  	credential, ok := cloudCredentials.AuthCredentials[credentialName]
    95  	if !ok {
    96  		return nil, "", "", errors.NotFoundf(
    97  			"%q credential for cloud %q", credentialName, cloudName,
    98  		)
    99  	}
   100  	return &credential, credentialName, cloudCredentials.DefaultRegion, nil
   101  }
   102  
   103  // DetectCredential detects credentials for the specified cloud type, and, if
   104  // exactly one is detected, returns it.
   105  //
   106  // If no credentials are detected, an error satisfying errors.IsNotFound will
   107  // be returned. If more than one credential is detected, ErrMultipleCredentials
   108  // will be returned.
   109  func DetectCredential(cloudName, cloudType string) (*cloud.CloudCredential, error) {
   110  	provider, err := environs.Provider(cloudType)
   111  	if err != nil {
   112  		return nil, errors.Trace(err)
   113  	}
   114  	detected, err := provider.DetectCredentials()
   115  	if err != nil {
   116  		return nil, errors.Annotatef(
   117  			err, "detecting credentials for %q cloud provider", cloudName,
   118  		)
   119  	}
   120  	logger.Tracef("provider detected credentials: %v", detected)
   121  	if len(detected.AuthCredentials) == 0 {
   122  		return nil, errors.NotFoundf("credentials for cloud %q", cloudName)
   123  	}
   124  	if len(detected.AuthCredentials) > 1 {
   125  		return nil, ErrMultipleCredentials
   126  	}
   127  	return detected, nil
   128  }