From 7b2c33e31cc437e6fc5036b02a019ba3d78634db Mon Sep 17 00:00:00 2001 From: Emily Ye Date: Tue, 8 Jan 2019 14:07:48 -0800 Subject: [PATCH 1/2] add access_token parameter, allow for any credential JSON type --- third_party/terraform/utils/config.go.erb | 94 +++++++-------------- third_party/terraform/utils/provider.go.erb | 28 ++++-- 2 files changed, 51 insertions(+), 71 deletions(-) diff --git a/third_party/terraform/utils/config.go.erb b/third_party/terraform/utils/config.go.erb index 831fb16f9383..7e5ccaae97b9 100644 --- a/third_party/terraform/utils/config.go.erb +++ b/third_party/terraform/utils/config.go.erb @@ -3,17 +3,15 @@ package google import ( "context" - "encoding/json" "fmt" "log" "net/http" - "strings" "github.com/hashicorp/terraform/helper/logging" "github.com/hashicorp/terraform/helper/pathorcontents" "golang.org/x/oauth2" - "golang.org/x/oauth2/google" + googleoauth "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" appengine "google.golang.org/api/appengine/v1" <% unless version == 'ga' -%> @@ -58,6 +56,7 @@ import ( // provider. type Config struct { Credentials string + AccessToken string Project string Region string Zone string @@ -109,7 +108,6 @@ type Config struct { } func (c *Config) loadAndValidate() error { - var account accountFile clientScopes := []string{ "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/cloud-platform", @@ -117,55 +115,13 @@ func (c *Config) loadAndValidate() error { "https://www.googleapis.com/auth/devstorage.full_control", } - var client *http.Client - var tokenSource oauth2.TokenSource - - if c.Credentials != "" { - contents, _, err := pathorcontents.Read(c.Credentials) - if err != nil { - return fmt.Errorf("Error loading credentials: %s", err) - } - - // Assume account_file is a JSON string - if err := parseJSON(&account, contents); err != nil { - return fmt.Errorf("Error parsing credentials '%s': %s", contents, err) - } - - // Get the token for use in our requests - log.Printf("[INFO] Requesting Google token...") - log.Printf("[INFO] -- Email: %s", account.ClientEmail) - log.Printf("[INFO] -- Scopes: %s", clientScopes) - log.Printf("[INFO] -- Private Key Length: %d", len(account.PrivateKey)) - - conf := jwt.Config{ - Email: account.ClientEmail, - PrivateKey: []byte(account.PrivateKey), - Scopes: clientScopes, - TokenURL: "https://accounts.google.com/o/oauth2/token", - } - - // Initiate an http.Client. The following GET request will be - // authorized and authenticated on the behalf of - // your service account. - client = conf.Client(context.Background()) - - tokenSource = conf.TokenSource(context.Background()) - } else { - log.Printf("[INFO] Authenticating using DefaultClient") - err := error(nil) - client, err = google.DefaultClient(context.Background(), clientScopes...) - if err != nil { - return err - } - - tokenSource, err = google.DefaultTokenSource(context.Background(), clientScopes...) - if err != nil { - return err - } + tokenSource, err := c.getTokenSource(clientScopes) + if err != nil { + return err } - c.tokenSource = tokenSource + client := oauth2.NewClient(context.Background(), tokenSource) client.Transport = logging.NewTransport("Google", client.Transport) terraformVersion := httpclient.UserAgentString() @@ -180,8 +136,6 @@ func (c *Config) loadAndValidate() error { c.client = client c.userAgent = userAgent - var err error - log.Printf("[INFO] Instantiating GCE client...") c.clientCompute, err = compute.New(client) if err != nil { @@ -424,17 +378,31 @@ func (c *Config) loadAndValidate() error { return nil } -// accountFile represents the structure of the account file JSON file. -type accountFile struct { - PrivateKeyId string `json:"private_key_id"` - PrivateKey string `json:"private_key"` - ClientEmail string `json:"client_email"` - ClientId string `json:"client_id"` -} +func (c *Config) getTokenSource(clientScopes []string) (oauth2.TokenSource, error) { + if c.AccessToken != "" { + log.Printf("[INFO] Using configured Google access token (length %d)", len(c.AccessToken)) + log.Printf("[INFO] -- Scopes: %s", clientScopes) + token := &oauth2.Token{AccessToken: c.AccessToken} + return oauth2.StaticTokenSource(token), nil + } -func parseJSON(result interface{}, contents string) error { - r := strings.NewReader(contents) - dec := json.NewDecoder(r) + if c.Credentials != "" { + contents, _, err := pathorcontents.Read(c.Credentials) + if err != nil { + return nil, fmt.Errorf("Error loading credentials: %s", err) + } + + creds, err := googleoauth.CredentialsFromJSON(context.Background(), []byte(contents), clientScopes...) + if err != nil { + return nil, fmt.Errorf("Unable to parse credentials from '%s': %s", contents, err) + } + + log.Printf("[INFO] Requesting Google token using Credential File %q...", c.Credentials) + log.Printf("[INFO] -- Scopes: %s", clientScopes) + return creds.TokenSource, nil + } - return dec.Decode(result) + log.Printf("[INFO] Authenticating using DefaultClient") + log.Printf("[INFO] -- Scopes: %s", clientScopes) + return googleoauth.DefaultTokenSource(context.Background(), clientScopes...) } diff --git a/third_party/terraform/utils/provider.go.erb b/third_party/terraform/utils/provider.go.erb index 0484d8eb4d6b..272415eb6f91 100644 --- a/third_party/terraform/utils/provider.go.erb +++ b/third_party/terraform/utils/provider.go.erb @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + + googleoauth "golang.org/x/oauth2/google" ) // Global MutexKV @@ -31,6 +33,12 @@ func Provider() terraform.ResourceProvider { ValidateFunc: validateCredentials, }, + "access_token": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"credentials"}, + }, + "project": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -263,12 +271,17 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { } func providerConfigure(d *schema.ResourceData) (interface{}, error) { - credentials := d.Get("credentials").(string) config := Config{ - Credentials: credentials, - Project: d.Get("project").(string), - Region: d.Get("region").(string), - Zone: d.Get("zone").(string), + Project: d.Get("project").(string), + Region: d.Get("region").(string), + Zone: d.Get("zone").(string), + } + + // Add credential source + if v, ok := d.GetOk("access_token"); ok { + config.AccessToken = v.(string) + } else if v, ok := d.GetOk("credentials"); ok { + config.Credentials = v.(string) } if err := config.loadAndValidate(); err != nil { @@ -287,10 +300,9 @@ func validateCredentials(v interface{}, k string) (warnings []string, errors []e if _, err := os.Stat(creds); err == nil { return } - var account accountFile - if err := json.Unmarshal([]byte(creds), &account); err != nil { + if _, err := googleoauth.CredentialsFromJSON(context.Background(), []byte(creds)); err != nil { errors = append(errors, - fmt.Errorf("credentials are not valid JSON '%s': %s", creds, err)) + fmt.Errorf("JSON credentials in %q are not valid: %s", creds, err)) } return From fa127548d6694811faf18ebcb35180dd7606cbe4 Mon Sep 17 00:00:00 2001 From: Emily Ye Date: Tue, 8 Jan 2019 14:31:13 -0800 Subject: [PATCH 2/2] add tests --- third_party/terraform/utils/config_test.go | 15 +++++++++++++++ third_party/terraform/utils/provider_test.go | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/third_party/terraform/utils/config_test.go b/third_party/terraform/utils/config_test.go index 648f93a688df..13b83a2438fa 100644 --- a/third_party/terraform/utils/config_test.go +++ b/third_party/terraform/utils/config_test.go @@ -48,3 +48,18 @@ func TestConfigLoadAndValidate_accountFileJSONInvalid(t *testing.T) { t.Fatalf("expected error, but got nil") } } + +func TestConfigLoadValidate_accessToken(t *testing.T) { + accessToken := getTestAccessTokenFromEnv(t) + + config := Config{ + AccessToken: accessToken, + Project: "my-gce-project", + Region: "us-central1", + } + + err := config.loadAndValidate() + if err != nil { + t.Fatalf("error: %v", err) + } +} diff --git a/third_party/terraform/utils/provider_test.go b/third_party/terraform/utils/provider_test.go index dacfcac583c5..e3ce5fcb441f 100644 --- a/third_party/terraform/utils/provider_test.go +++ b/third_party/terraform/utils/provider_test.go @@ -57,6 +57,10 @@ var billingAccountEnvVars = []string{ "GOOGLE_BILLING_ACCOUNT", } +var accessTokenEnvVars = []string{ + "GOOGLE_OAUTH2_ACCESS_TOKEN", +} + func init() { testAccProvider = Provider().(*schema.Provider) testAccRandomProvider = random.Provider().(*schema.Provider) @@ -202,6 +206,11 @@ func getTestServiceAccountFromEnv(t *testing.T) string { return multiEnvSearch(serviceAccountEnvVars) } +func getTestAccessTokenFromEnv(t *testing.T) string { + skipIfEnvNotSet(t, accessTokenEnvVars...) + return multiEnvSearch(accessTokenEnvVars) +} + func multiEnvSearch(ks []string) string { for _, k := range ks { if v := os.Getenv(k); v != "" {