mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-04 17:51:11 +00:00
Add OAuth2 access_token and client config as credential options. (#2838)
Signed-off-by: Modular Magician <magic-modules@google.com>
This commit is contained in:
parent
cbfef5e68c
commit
b9cedc68ef
@ -2,11 +2,9 @@ package google
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/logging"
|
"github.com/hashicorp/terraform/helper/logging"
|
||||||
"github.com/hashicorp/terraform/helper/pathorcontents"
|
"github.com/hashicorp/terraform/helper/pathorcontents"
|
||||||
@ -14,8 +12,7 @@ import (
|
|||||||
"github.com/terraform-providers/terraform-provider-google/version"
|
"github.com/terraform-providers/terraform-provider-google/version"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"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"
|
appengine "google.golang.org/api/appengine/v1"
|
||||||
"google.golang.org/api/bigquery/v2"
|
"google.golang.org/api/bigquery/v2"
|
||||||
"google.golang.org/api/cloudbilling/v1"
|
"google.golang.org/api/cloudbilling/v1"
|
||||||
@ -53,6 +50,7 @@ import (
|
|||||||
// provider.
|
// provider.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Credentials string
|
Credentials string
|
||||||
|
AccessToken string
|
||||||
Project string
|
Project string
|
||||||
Region string
|
Region string
|
||||||
Zone string
|
Zone string
|
||||||
@ -98,7 +96,6 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) loadAndValidate() error {
|
func (c *Config) loadAndValidate() error {
|
||||||
var account accountFile
|
|
||||||
clientScopes := []string{
|
clientScopes := []string{
|
||||||
"https://www.googleapis.com/auth/compute",
|
"https://www.googleapis.com/auth/compute",
|
||||||
"https://www.googleapis.com/auth/cloud-platform",
|
"https://www.googleapis.com/auth/cloud-platform",
|
||||||
@ -106,55 +103,13 @@ func (c *Config) loadAndValidate() error {
|
|||||||
"https://www.googleapis.com/auth/devstorage.full_control",
|
"https://www.googleapis.com/auth/devstorage.full_control",
|
||||||
}
|
}
|
||||||
|
|
||||||
var client *http.Client
|
tokenSource, err := c.getTokenSource(clientScopes)
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenSource, err = google.DefaultTokenSource(context.Background(), clientScopes...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.tokenSource = tokenSource
|
c.tokenSource = tokenSource
|
||||||
|
|
||||||
|
client := oauth2.NewClient(context.Background(), tokenSource)
|
||||||
client.Transport = logging.NewTransport("Google", client.Transport)
|
client.Transport = logging.NewTransport("Google", client.Transport)
|
||||||
|
|
||||||
terraformVersion := httpclient.UserAgentString()
|
terraformVersion := httpclient.UserAgentString()
|
||||||
@ -165,8 +120,6 @@ func (c *Config) loadAndValidate() error {
|
|||||||
c.client = client
|
c.client = client
|
||||||
c.userAgent = userAgent
|
c.userAgent = userAgent
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
log.Printf("[INFO] Instantiating GCE client...")
|
log.Printf("[INFO] Instantiating GCE client...")
|
||||||
c.clientCompute, err = compute.New(client)
|
c.clientCompute, err = compute.New(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -391,17 +344,31 @@ func (c *Config) loadAndValidate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountFile represents the structure of the account file JSON file.
|
func (c *Config) getTokenSource(clientScopes []string) (oauth2.TokenSource, error) {
|
||||||
type accountFile struct {
|
if c.AccessToken != "" {
|
||||||
PrivateKeyId string `json:"private_key_id"`
|
log.Printf("[INFO] Using configured Google access token (length %d)", len(c.AccessToken))
|
||||||
PrivateKey string `json:"private_key"`
|
log.Printf("[INFO] -- Scopes: %s", clientScopes)
|
||||||
ClientEmail string `json:"client_email"`
|
token := &oauth2.Token{AccessToken: c.AccessToken}
|
||||||
ClientId string `json:"client_id"`
|
return oauth2.StaticTokenSource(token), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseJSON(result interface{}, contents string) error {
|
if c.Credentials != "" {
|
||||||
r := strings.NewReader(contents)
|
contents, _, err := pathorcontents.Read(c.Credentials)
|
||||||
dec := json.NewDecoder(r)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error loading credentials: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
return dec.Decode(result)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] Authenticating using DefaultClient")
|
||||||
|
log.Printf("[INFO] -- Scopes: %s", clientScopes)
|
||||||
|
return googleoauth.DefaultTokenSource(context.Background(), clientScopes...)
|
||||||
}
|
}
|
||||||
|
@ -48,3 +48,18 @@ func TestConfigLoadAndValidate_accountFileJSONInvalid(t *testing.T) {
|
|||||||
t.Fatalf("expected error, but got nil")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package google
|
package google
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/mutexkv"
|
"github.com/hashicorp/terraform/helper/mutexkv"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
|
googleoauth "golang.org/x/oauth2/google"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global MutexKV
|
// Global MutexKV
|
||||||
@ -28,6 +30,12 @@ func Provider() terraform.ResourceProvider {
|
|||||||
ValidateFunc: validateCredentials,
|
ValidateFunc: validateCredentials,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"access_token": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"credentials"},
|
||||||
|
},
|
||||||
|
|
||||||
"project": {
|
"project": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -244,14 +252,19 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
credentials := d.Get("credentials").(string)
|
|
||||||
config := Config{
|
config := Config{
|
||||||
Credentials: credentials,
|
|
||||||
Project: d.Get("project").(string),
|
Project: d.Get("project").(string),
|
||||||
Region: d.Get("region").(string),
|
Region: d.Get("region").(string),
|
||||||
Zone: d.Get("zone").(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 {
|
if err := config.loadAndValidate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -268,10 +281,9 @@ func validateCredentials(v interface{}, k string) (warnings []string, errors []e
|
|||||||
if _, err := os.Stat(creds); err == nil {
|
if _, err := os.Stat(creds); err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var account accountFile
|
if _, err := googleoauth.CredentialsFromJSON(context.Background(), []byte(creds)); err != nil {
|
||||||
if err := json.Unmarshal([]byte(creds), &account); err != nil {
|
|
||||||
errors = append(errors,
|
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
|
return
|
||||||
|
@ -57,6 +57,10 @@ var billingAccountEnvVars = []string{
|
|||||||
"GOOGLE_BILLING_ACCOUNT",
|
"GOOGLE_BILLING_ACCOUNT",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accessTokenEnvVars = []string{
|
||||||
|
"GOOGLE_OAUTH2_ACCESS_TOKEN",
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
testAccProvider = Provider().(*schema.Provider)
|
testAccProvider = Provider().(*schema.Provider)
|
||||||
testAccRandomProvider = random.Provider().(*schema.Provider)
|
testAccRandomProvider = random.Provider().(*schema.Provider)
|
||||||
@ -202,6 +206,11 @@ func getTestServiceAccountFromEnv(t *testing.T) string {
|
|||||||
return multiEnvSearch(serviceAccountEnvVars)
|
return multiEnvSearch(serviceAccountEnvVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestAccessTokenFromEnv(t *testing.T) string {
|
||||||
|
skipIfEnvNotSet(t, accessTokenEnvVars...)
|
||||||
|
return multiEnvSearch(accessTokenEnvVars)
|
||||||
|
}
|
||||||
|
|
||||||
func multiEnvSearch(ks []string) string {
|
func multiEnvSearch(ks []string) string {
|
||||||
for _, k := range ks {
|
for _, k := range ks {
|
||||||
if v := os.Getenv(k); v != "" {
|
if v := os.Getenv(k); v != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user