diff --git a/builtin/providers/google/config.go b/builtin/providers/google/config.go index 54c115b4b..edb7add16 100644 --- a/builtin/providers/google/config.go +++ b/builtin/providers/google/config.go @@ -17,17 +17,26 @@ const clientScopes string = "https://www.googleapis.com/auth/compute" // Config is the configuration structure used to instantiate the Google // provider. type Config struct { - AccountFile string - ClientSecretsFile string - Project string - Region string + AccountFile string + Project string + Region string clientCompute *compute.Service } func (c *Config) loadAndValidate() error { var account accountFile - var secrets clientSecretsFile + + // TODO: validation that it isn't blank + if c.AccountFile == "" { + c.AccountFile = os.Getenv("GOOGLE_ACCOUNT_FILE") + } + if c.Project == "" { + c.Project = os.Getenv("GOOGLE_PROJECT") + } + if c.Region == "" { + c.Region = os.Getenv("GOOGLE_REGION") + } if err := loadJSON(&account, c.AccountFile); err != nil { return fmt.Errorf( @@ -36,24 +45,15 @@ func (c *Config) loadAndValidate() error { err) } - if err := loadJSON(&secrets, c.ClientSecretsFile); err != nil { - return fmt.Errorf( - "Error loading client secrets file '%s': %s", - c.ClientSecretsFile, - 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)) - log.Printf("[INFO] -- Token URL: %s", secrets.Web.TokenURI) jwtTok := jwt.NewToken( account.ClientEmail, clientScopes, []byte(account.PrivateKey)) - jwtTok.ClaimSet.Aud = secrets.Web.TokenURI token, err := jwtTok.Assert(new(http.Client)) if err != nil { return fmt.Errorf("Error retrieving auth token: %s", err) @@ -64,8 +64,6 @@ func (c *Config) loadAndValidate() error { Config: &oauth.Config{ ClientId: account.ClientId, Scope: clientScopes, - TokenURL: secrets.Web.TokenURI, - AuthURL: secrets.Web.AuthURI, }, Token: token, } @@ -87,16 +85,6 @@ type accountFile struct { ClientId string `json:"client_id"` } -// clientSecretsFile represents the structure of the client secrets JSON file. -type clientSecretsFile struct { - Web struct { - AuthURI string `json:"auth_uri"` - ClientEmail string `json:"client_email"` - ClientId string `json:"client_id"` - TokenURI string `json:"token_uri"` - } -} - func loadJSON(result interface{}, path string) error { f, err := os.Open(path) if err != nil { diff --git a/builtin/providers/google/config_test.go b/builtin/providers/google/config_test.go index 25d424cda..b4ee58520 100644 --- a/builtin/providers/google/config_test.go +++ b/builtin/providers/google/config_test.go @@ -22,20 +22,3 @@ func TestConfigLoadJSON_account(t *testing.T) { t.Fatalf("bad: %#v", actual) } } - -func TestConfigLoadJSON_client(t *testing.T) { - var actual clientSecretsFile - if err := loadJSON(&actual, "./test-fixtures/fake_client.json"); err != nil { - t.Fatalf("err: %s", err) - } - - var expected clientSecretsFile - expected.Web.AuthURI = "https://accounts.google.com/o/oauth2/auth" - expected.Web.ClientEmail = "foo@developer.gserviceaccount.com" - expected.Web.ClientId = "foo.apps.googleusercontent.com" - expected.Web.TokenURI = "https://accounts.google.com/o/oauth2/token" - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} diff --git a/builtin/providers/google/provider.go b/builtin/providers/google/provider.go index b487513f1..3a16dc0a0 100644 --- a/builtin/providers/google/provider.go +++ b/builtin/providers/google/provider.go @@ -15,12 +15,6 @@ func Provider() terraform.ResourceProvider { DefaultFunc: schema.EnvDefaultFunc("GOOGLE_ACCOUNT_FILE", nil), }, - "client_secrets_file": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("GOOGLE_CLIENT_FILE", nil), - }, - "project": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -49,10 +43,9 @@ func Provider() terraform.ResourceProvider { func providerConfigure(d *schema.ResourceData) (interface{}, error) { config := Config{ - AccountFile: d.Get("account_file").(string), - ClientSecretsFile: d.Get("client_secrets_file").(string), - Project: d.Get("project").(string), - Region: d.Get("region").(string), + AccountFile: d.Get("account_file").(string), + Project: d.Get("project").(string), + Region: d.Get("region").(string), } if err := config.loadAndValidate(); err != nil { diff --git a/builtin/providers/google/provider_test.go b/builtin/providers/google/provider_test.go index d5a32be33..2275e188f 100644 --- a/builtin/providers/google/provider_test.go +++ b/builtin/providers/google/provider_test.go @@ -33,10 +33,6 @@ func testAccPreCheck(t *testing.T) { t.Fatal("GOOGLE_ACCOUNT_FILE must be set for acceptance tests") } - if v := os.Getenv("GOOGLE_CLIENT_FILE"); v == "" { - t.Fatal("GOOGLE_CLIENT_FILE must be set for acceptance tests") - } - if v := os.Getenv("GOOGLE_PROJECT"); v == "" { t.Fatal("GOOGLE_PROJECT must be set for acceptance tests") } diff --git a/builtin/providers/google/resource_compute_instance.go b/builtin/providers/google/resource_compute_instance.go index ca3120a0d..98e9faf95 100644 --- a/builtin/providers/google/resource_compute_instance.go +++ b/builtin/providers/google/resource_compute_instance.go @@ -137,11 +137,11 @@ func resourceComputeInstance() *schema.Resource { }, "scopes": &schema.Schema{ - Type: schema.TypeList, - Required: true, - ForceNew: true, - Elem: &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeList, + Required: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, StateFunc: func(v interface{}) string { return canonicalizeServiceScope(v.(string)) }, @@ -294,11 +294,11 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err scopesCount := d.Get(prefix + ".scopes.#").(int) scopes := make([]string, 0, scopesCount) for j := 0; j < scopesCount; j++ { - scope := d.Get(fmt.Sprintf(prefix + ".scopes.%d", j)).(string) + scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) scopes = append(scopes, canonicalizeServiceScope(scope)) } - serviceAccount := &compute.ServiceAccount { + serviceAccount := &compute.ServiceAccount{ Email: "default", Scopes: scopes, } @@ -378,8 +378,8 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error // Set the service accounts for i, serviceAccount := range instance.ServiceAccounts { prefix := fmt.Sprintf("service_account.%d", i) - d.Set(prefix + ".email", serviceAccount.Email) - d.Set(prefix + ".scopes.#", len(serviceAccount.Scopes)) + d.Set(prefix+".email", serviceAccount.Email) + d.Set(prefix+".scopes.#", len(serviceAccount.Scopes)) for j, scope := range serviceAccount.Scopes { d.Set(fmt.Sprintf("%s.scopes.%d", prefix, j), scope) } @@ -534,14 +534,19 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err func resourceInstanceMetadata(d *schema.ResourceData) *compute.Metadata { var metadata *compute.Metadata - if v := d.Get("metadata").([]interface{}); len(v) > 0 { + if metadataList := d.Get("metadata").([]interface{}); len(metadataList) > 0 { m := new(compute.Metadata) - m.Items = make([]*compute.MetadataItems, 0, len(v)) - for _, v := range v { - for k, v := range v.(map[string]interface{}) { + m.Items = make([]*compute.MetadataItems, 0, len(metadataList)) + for _, metadataMap := range metadataList { + for key, val := range metadataMap.(map[string]interface{}) { + // TODO: fix https://github.com/hashicorp/terraform/issues/883 + // and remove this workaround <3 phinze + if key == "#" { + continue + } m.Items = append(m.Items, &compute.MetadataItems{ - Key: k, - Value: v.(string), + Key: key, + Value: val.(string), }) } } diff --git a/website/source/docs/providers/google/index.html.markdown b/website/source/docs/providers/google/index.html.markdown index 6eafafbcd..3014125cf 100644 --- a/website/source/docs/providers/google/index.html.markdown +++ b/website/source/docs/providers/google/index.html.markdown @@ -20,7 +20,6 @@ Use the navigation to the left to read about the available resources. # Configure the Google Cloud provider provider "google" { account_file = "account.json" - client_secrets_file = "client_secrets.json" project = "my-gce-project" region = "us-central1" } @@ -39,33 +38,23 @@ The following keys can be used to configure the provider. your account credentials, downloaded from Google Cloud Console. More details on retrieving this file are below. -* `client_secrets_file` - (Required) Path to the JSON file containing - the secrets for your account, downloaded from Google Cloud Console. - More details on retrieving this file are below. - * `project` - (Required) The name of the project to apply any resources to. * `region` - (Required) The region to operate under. -## Authentication JSON Files +## Authentication JSON File -Authenticating with Google Cloud services requires two separate JSON -files: one which we call the _account file_ and the _client secrets file_. +Authenticating with Google Cloud services requires a JSON +file which we call the _account file_. -Both of these files are downloaded directly from the +This file is downloaded directly from the [Google Developers Console](https://console.developers.google.com). To make -the process more straightforwarded, it is documented here. +the process more straightforwarded, it is documented here: 1. Log into the [Google Developers Console](https://console.developers.google.com) and select a project. 2. Under the "APIs & Auth" section, click "Credentials." -3. Create a new OAuth client ID and select "Installed application" as the - type of account. Once created, click the "Download JSON" button underneath - the account. The file should start with "client\_secret". This is your _client - secrets file_. - -4. Create a new OAuth client ID and select "Service account" as the type - of account. Once created, a JSON file should be downloaded. This is your - _account file_. +3. Create a new OAuth client ID and select "Service account" as the type + of account. Once created, and after a P12 key is downloaded, a JSON file should be downloaded. This is your _account file_.