github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "os" 9 "path/filepath" 10 "runtime" 11 12 "github.com/juju/errors" 13 "github.com/juju/utils" 14 15 "github.com/juju/juju/cloud" 16 "github.com/juju/juju/provider/gce/google" 17 ) 18 19 type environProviderCredentials struct{} 20 21 // CredentialSchemas is part of the environs.ProviderCredentials interface. 22 func (environProviderCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema { 23 return map[cloud.AuthType]cloud.CredentialSchema{ 24 cloud.OAuth2AuthType: { 25 { 26 "client-id", cloud.CredentialAttr{Description: "client ID"}, 27 }, { 28 "client-email", cloud.CredentialAttr{Description: "client e-mail address"}, 29 }, { 30 "private-key", 31 cloud.CredentialAttr{ 32 Description: "client secret", 33 Hidden: true, 34 }, 35 }, { 36 "project-id", cloud.CredentialAttr{Description: "project ID"}, 37 }, 38 }, 39 cloud.JSONFileAuthType: { 40 { 41 "file", cloud.CredentialAttr{ 42 Description: "path to the .json file containing your Google Compute Engine project credentials", 43 FilePath: true, 44 }, 45 }, 46 }, 47 } 48 } 49 50 // DetectCredentials is part of the environs.ProviderCredentials interface. 51 func (environProviderCredentials) DetectCredentials() (*cloud.CloudCredential, error) { 52 // Google recommends credentials in a json file: 53 // 1. whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable. 54 // 2. whose location is known to the gcloud command-line tool. 55 // On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. 56 // On other systems, $HOME/.config/gcloud/application_default_credentials.json. 57 58 validatePath := func(possbleFilePath string) string { 59 if possbleFilePath == "" { 60 return "" 61 } 62 fi, err := os.Stat(possbleFilePath) 63 if err != nil || fi.IsDir() { 64 return "" 65 } 66 return possbleFilePath 67 } 68 69 possbleFilePath := validatePath(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")) 70 if possbleFilePath == "" { 71 possbleFilePath = validatePath(wellKnownCredentialsFile()) 72 } 73 if possbleFilePath == "" { 74 return nil, errors.NotFoundf("gce credentials") 75 } 76 parsedCred, err := parseJSONAuthFile(possbleFilePath) 77 if err != nil { 78 return nil, errors.Annotatef(err, "invalid json credential file %s", possbleFilePath) 79 } 80 81 user, err := utils.LocalUsername() 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 cred := cloud.NewCredential(cloud.JSONFileAuthType, map[string]string{ 86 "file": possbleFilePath, 87 }) 88 credName := parsedCred.Attributes()["client-email"] 89 if credName == "" { 90 credName = parsedCred.Attributes()["client-id"] 91 } 92 cred.Label = fmt.Sprintf("google credential %q", credName) 93 return &cloud.CloudCredential{ 94 DefaultRegion: os.Getenv("CLOUDSDK_COMPUTE_REGION"), 95 AuthCredentials: map[string]cloud.Credential{ 96 user: cred, 97 }}, nil 98 } 99 100 func wellKnownCredentialsFile() string { 101 const f = "application_default_credentials.json" 102 if runtime.GOOS == "windows" { 103 return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) 104 } 105 return filepath.Join(utils.Home(), ".config", "gcloud", f) 106 } 107 108 // parseJSONAuthFile parses the file with the given path, and extracts 109 // the OAuth2 credentials within. 110 func parseJSONAuthFile(filename string) (cloud.Credential, error) { 111 authFile, err := os.Open(filename) 112 if err != nil { 113 return cloud.Credential{}, errors.Trace(err) 114 } 115 defer authFile.Close() 116 creds, err := google.ParseJSONKey(authFile) 117 if err != nil { 118 return cloud.Credential{}, errors.Trace(err) 119 } 120 return cloud.NewCredential(cloud.OAuth2AuthType, map[string]string{ 121 "project-id": creds.ProjectID, 122 "client-id": creds.ClientID, 123 "client-email": creds.ClientEmail, 124 "private-key": string(creds.PrivateKey), 125 }), nil 126 }