mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-05 17:52:38 +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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/logging"
|
||||
"github.com/hashicorp/terraform/helper/pathorcontents"
|
||||
|
@ -14,8 +12,7 @@ import (
|
|||
"github.com/terraform-providers/terraform-provider-google/version"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
googleoauth "golang.org/x/oauth2/google"
|
||||
appengine "google.golang.org/api/appengine/v1"
|
||||
"google.golang.org/api/bigquery/v2"
|
||||
"google.golang.org/api/cloudbilling/v1"
|
||||
|
@ -53,6 +50,7 @@ import (
|
|||
// provider.
|
||||
type Config struct {
|
||||
Credentials string
|
||||
AccessToken string
|
||||
Project string
|
||||
Region string
|
||||
Zone string
|
||||
|
@ -98,7 +96,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",
|
||||
|
@ -106,55 +103,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...)
|
||||
tokenSource, err := c.getTokenSource(clientScopes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokenSource, err = google.DefaultTokenSource(context.Background(), 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()
|
||||
|
@ -165,8 +120,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 {
|
||||
|
@ -391,17 +344,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)
|
||||
|
||||
return dec.Decode(result)
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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
|
||||
|
@ -28,6 +30,12 @@ func Provider() terraform.ResourceProvider {
|
|||
ValidateFunc: validateCredentials,
|
||||
},
|
||||
|
||||
"access_token": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ConflictsWith: []string{"credentials"},
|
||||
},
|
||||
|
||||
"project": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -244,14 +252,19 @@ 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),
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -268,10 +281,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
|
||||
|
|
|
@ -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 != "" {
|
||||
|
|
Loading…
Reference in New Issue
Block a user