mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
providers/google: first pass
This commit is contained in:
parent
56cf1e6faa
commit
f346964fca
115
builtin/providers/google/config.go
Normal file
115
builtin/providers/google/config.go
Normal file
@ -0,0 +1,115 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"code.google.com/p/goauth2/oauth"
|
||||
"code.google.com/p/goauth2/oauth/jwt"
|
||||
"code.google.com/p/google-api-go-client/compute/v1"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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.ClientSecretsFile == "" {
|
||||
c.ClientSecretsFile = os.Getenv("GOOGLE_CLIENT_FILE")
|
||||
}
|
||||
|
||||
if err := loadJSON(&account, c.AccountFile); err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error loading account file '%s': %s",
|
||||
c.AccountFile,
|
||||
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)
|
||||
}
|
||||
|
||||
// Instantiate the transport to communicate to Google
|
||||
transport := &oauth.Transport{
|
||||
Config: &oauth.Config{
|
||||
ClientId: account.ClientId,
|
||||
Scope: clientScopes,
|
||||
TokenURL: secrets.Web.TokenURI,
|
||||
AuthURL: secrets.Web.AuthURI,
|
||||
},
|
||||
Token: token,
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Instantiating GCE client...")
|
||||
c.clientCompute, err = compute.New(transport.Client())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
dec := json.NewDecoder(f)
|
||||
return dec.Decode(result)
|
||||
}
|
41
builtin/providers/google/config_test.go
Normal file
41
builtin/providers/google/config_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigLoadJSON_account(t *testing.T) {
|
||||
var actual accountFile
|
||||
if err := loadJSON(&actual, "./test-fixtures/fake_account.json"); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
expected := accountFile{
|
||||
PrivateKeyId: "foo",
|
||||
PrivateKey: "bar",
|
||||
ClientEmail: "foo@bar.com",
|
||||
ClientId: "id@foo.com",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
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)
|
||||
}
|
||||
}
|
41
builtin/providers/google/provider.go
Normal file
41
builtin/providers/google/provider.go
Normal file
@ -0,0 +1,41 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// Provider returns a terraform.ResourceProvider.
|
||||
func Provider() *schema.Provider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"account_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"client_secrets_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"google_compute_instance": resourceComputeInstance(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
}
|
||||
}
|
||||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
AccountFile: d.Get("account_file").(string),
|
||||
ClientSecretsFile: d.Get("client_secrets_file").(string),
|
||||
}
|
||||
|
||||
if err := config.loadAndValidate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
39
builtin/providers/google/provider_test.go
Normal file
39
builtin/providers/google/provider_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider()
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"google": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = Provider()
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("GOOGLE_ACCOUNT_FILE"); v == "" {
|
||||
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")
|
||||
}
|
||||
}
|
17
builtin/providers/google/resource_compute_instance.go
Normal file
17
builtin/providers/google/resource_compute_instance.go
Normal file
@ -0,0 +1,17 @@
|
||||
package google
|
||||
|
||||
import(
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceComputeInstance() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceComputeInstanceCreate,
|
||||
|
||||
Schema: map[string]*schema.Schema{},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
return nil
|
||||
}
|
161
builtin/providers/google/resource_compute_instance_test.go
Normal file
161
builtin/providers/google/resource_compute_instance_test.go
Normal file
@ -0,0 +1,161 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccComputeInstance_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
//CheckDestroy: testAccCheckHerokuAppDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccComputeInstance_basic,
|
||||
/*
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckHerokuAppExists("heroku_app.foobar", &app),
|
||||
testAccCheckHerokuAppAttributes(&app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"heroku_app.foobar", "name", "terraform-test-app"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"heroku_app.foobar", "config_vars.0.FOO", "bar"),
|
||||
),
|
||||
*/
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
func testAccCheckHerokuAppDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*heroku.Client)
|
||||
|
||||
for _, rs := range s.Resources {
|
||||
if rs.Type != "heroku_app" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := client.AppInfo(rs.ID)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("App still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckHerokuAppAttributes(app *heroku.App) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*heroku.Client)
|
||||
|
||||
if app.Region.Name != "us" {
|
||||
return fmt.Errorf("Bad region: %s", app.Region.Name)
|
||||
}
|
||||
|
||||
if app.Stack.Name != "cedar" {
|
||||
return fmt.Errorf("Bad stack: %s", app.Stack.Name)
|
||||
}
|
||||
|
||||
if app.Name != "terraform-test-app" {
|
||||
return fmt.Errorf("Bad name: %s", app.Name)
|
||||
}
|
||||
|
||||
vars, err := client.ConfigVarInfo(app.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if vars["FOO"] != "bar" {
|
||||
return fmt.Errorf("Bad config vars: %v", vars)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHerokuAppAttributesUpdated(app *heroku.App) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*heroku.Client)
|
||||
|
||||
if app.Name != "terraform-test-renamed" {
|
||||
return fmt.Errorf("Bad name: %s", app.Name)
|
||||
}
|
||||
|
||||
vars, err := client.ConfigVarInfo(app.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure we kept the old one
|
||||
if vars["FOO"] != "bing" {
|
||||
return fmt.Errorf("Bad config vars: %v", vars)
|
||||
}
|
||||
|
||||
if vars["BAZ"] != "bar" {
|
||||
return fmt.Errorf("Bad config vars: %v", vars)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHerokuAppAttributesNoVars(app *heroku.App) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*heroku.Client)
|
||||
|
||||
if app.Name != "terraform-test-app" {
|
||||
return fmt.Errorf("Bad name: %s", app.Name)
|
||||
}
|
||||
|
||||
vars, err := client.ConfigVarInfo(app.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(vars) != 0 {
|
||||
return fmt.Errorf("vars exist: %v", vars)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHerokuAppExists(n string, app *heroku.App) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.ID == "" {
|
||||
return fmt.Errorf("No App Name is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*heroku.Client)
|
||||
|
||||
foundApp, err := client.AppInfo(rs.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if foundApp.Name != rs.ID {
|
||||
return fmt.Errorf("App not found")
|
||||
}
|
||||
|
||||
*app = *foundApp
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const testAccComputeInstance_basic = `
|
||||
resource "google_compute_instance" "foobar" {
|
||||
}`
|
7
builtin/providers/google/test-fixtures/fake_account.json
Normal file
7
builtin/providers/google/test-fixtures/fake_account.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"private_key_id": "foo",
|
||||
"private_key": "bar",
|
||||
"client_email": "foo@bar.com",
|
||||
"client_id": "id@foo.com",
|
||||
"type": "service_account"
|
||||
}
|
11
builtin/providers/google/test-fixtures/fake_client.json
Normal file
11
builtin/providers/google/test-fixtures/fake_client.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"web": {
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"client_secret": "foo",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"client_email": "foo@developer.gserviceaccount.com",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/foo@developer.gserviceaccount.com",
|
||||
"client_id": "foo.apps.googleusercontent.com",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user