mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-03 08:42:39 +00:00
providers/google: first pass
This commit is contained in:
commit
80e2023e6b
115
config.go
Normal file
115
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
config_test.go
Normal file
41
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
provider.go
Normal file
41
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
provider_test.go
Normal file
39
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
resource_compute_instance.go
Normal file
17
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
resource_compute_instance_test.go
Normal file
161
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
test-fixtures/fake_account.json
Normal file
7
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
test-fixtures/fake_client.json
Normal file
11
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