github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/gce/credentials.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package gce 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 13 "github.com/juju/errors" 14 "github.com/juju/utils" 15 16 "github.com/juju/juju/cloud" 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/provider/gce/google" 19 ) 20 21 const ( 22 credAttrPrivateKey = "private-key" 23 credAttrClientID = "client-id" 24 credAttrClientEmail = "client-email" 25 credAttrProjectID = "project-id" 26 27 // The contents of the file for "jsonfile" auth-type. 28 credAttrFile = "file" 29 ) 30 31 type environProviderCredentials struct{} 32 33 // CredentialSchemas is part of the environs.ProviderCredentials interface. 34 func (environProviderCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema { 35 return map[cloud.AuthType]cloud.CredentialSchema{ 36 cloud.OAuth2AuthType: {{ 37 Name: credAttrClientID, 38 CredentialAttr: cloud.CredentialAttr{Description: "client ID"}, 39 }, { 40 Name: credAttrClientEmail, 41 CredentialAttr: cloud.CredentialAttr{Description: "client e-mail address"}, 42 }, { 43 Name: credAttrPrivateKey, 44 CredentialAttr: cloud.CredentialAttr{ 45 Description: "client secret", 46 Hidden: true, 47 }, 48 }, { 49 Name: credAttrProjectID, 50 CredentialAttr: cloud.CredentialAttr{Description: "project ID"}, 51 }}, 52 cloud.JSONFileAuthType: {{ 53 Name: credAttrFile, 54 CredentialAttr: cloud.CredentialAttr{ 55 Description: "path to the .json file containing your Google Compute Engine project credentials", 56 FilePath: true, 57 }, 58 }}, 59 } 60 } 61 62 // DetectCredentials is part of the environs.ProviderCredentials interface. 63 func (environProviderCredentials) DetectCredentials() (*cloud.CloudCredential, error) { 64 // Google recommends credentials in a json file: 65 // 1. whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable. 66 // 2. whose location is known to the gcloud command-line tool. 67 // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. 68 // On other systems, $HOME/.config/gcloud/application_default_credentials.json. 69 70 validatePath := func(possibleFilePath string) string { 71 if possibleFilePath == "" { 72 return "" 73 } 74 fi, err := os.Stat(possibleFilePath) 75 if err != nil || fi.IsDir() { 76 return "" 77 } 78 return possibleFilePath 79 } 80 81 possibleFilePath := validatePath(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")) 82 if possibleFilePath == "" { 83 possibleFilePath = validatePath(wellKnownCredentialsFile()) 84 } 85 if possibleFilePath == "" { 86 return nil, errors.NotFoundf("gce credentials") 87 } 88 89 authFile, err := os.Open(possibleFilePath) 90 if err != nil { 91 return nil, errors.Trace(err) 92 } 93 defer authFile.Close() 94 95 parsedCred, err := parseJSONAuthFile(authFile) 96 if err != nil { 97 return nil, errors.Annotatef(err, "invalid json credential file %s", possibleFilePath) 98 } 99 100 user, err := utils.LocalUsername() 101 if err != nil { 102 return nil, errors.Trace(err) 103 } 104 cred := cloud.NewCredential(cloud.JSONFileAuthType, map[string]string{ 105 "file": possibleFilePath, 106 }) 107 credName := parsedCred.Attributes()[credAttrClientEmail] 108 if credName == "" { 109 credName = parsedCred.Attributes()[credAttrClientID] 110 } 111 cred.Label = fmt.Sprintf("google credential %q", credName) 112 return &cloud.CloudCredential{ 113 DefaultRegion: os.Getenv("CLOUDSDK_COMPUTE_REGION"), 114 AuthCredentials: map[string]cloud.Credential{ 115 user: cred, 116 }}, nil 117 } 118 119 func wellKnownCredentialsFile() string { 120 const f = "application_default_credentials.json" 121 if runtime.GOOS == "windows" { 122 return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) 123 } 124 return filepath.Join(utils.Home(), ".config", "gcloud", f) 125 } 126 127 // parseJSONAuthFile parses a file, and extracts the OAuth2 credentials within. 128 func parseJSONAuthFile(r io.Reader) (cloud.Credential, error) { 129 creds, err := google.ParseJSONKey(r) 130 if err != nil { 131 return cloud.Credential{}, errors.Trace(err) 132 } 133 return cloud.NewCredential(cloud.OAuth2AuthType, map[string]string{ 134 credAttrProjectID: creds.ProjectID, 135 credAttrClientID: creds.ClientID, 136 credAttrClientEmail: creds.ClientEmail, 137 credAttrPrivateKey: string(creds.PrivateKey), 138 }), nil 139 } 140 141 // FinalizeCredential is part of the environs.ProviderCredentials interface. 142 func (environProviderCredentials) FinalizeCredential(_ environs.FinalizeCredentialContext, args environs.FinalizeCredentialParams) (*cloud.Credential, error) { 143 return &args.Credential, nil 144 }