From 376d1d6083bba1ba6a87c1461acd1f926bd6a909 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 25 Aug 2014 12:55:08 -0700 Subject: [PATCH] providers/google: google_compute_address --- config.go | 8 ++ operation.go | 64 +++++++++++ provider.go | 15 ++- resource_compute_address.go | 107 ++++++++++++++++++ ...est.go => resource_compute_address_test.go | 9 +- 5 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 operation.go create mode 100644 resource_compute_address.go rename resource_compute_instance_test.go => resource_compute_address_test.go (94%) diff --git a/config.go b/config.go index d83f0f1c..91f8992a 100644 --- a/config.go +++ b/config.go @@ -19,6 +19,8 @@ const clientScopes string = "https://www.googleapis.com/auth/compute" type Config struct { AccountFile string ClientSecretsFile string + Project string + Region string clientCompute *compute.Service } @@ -34,6 +36,12 @@ func (c *Config) loadAndValidate() error { if c.ClientSecretsFile == "" { c.ClientSecretsFile = os.Getenv("GOOGLE_CLIENT_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( diff --git a/operation.go b/operation.go new file mode 100644 index 00000000..59c6839a --- /dev/null +++ b/operation.go @@ -0,0 +1,64 @@ +package google + +import ( + "fmt" + + "code.google.com/p/google-api-go-client/compute/v1" + "github.com/hashicorp/terraform/helper/resource" +) + +// OperationWaitType is an enum specifying what type of operation +// we're waiting on. +type OperationWaitType byte + +const ( + OperationWaitInvalid OperationWaitType = iota + OperationWaitGlobal + OperationWaitRegion + OperationWaitZone +) + +type OperationWaiter struct { + Service *compute.Service + Op *compute.Operation + Project string + Region string + Zone string + Type OperationWaitType +} + +func (w *OperationWaiter) RefreshFunc() resource.StateRefreshFunc { + return func() (interface{}, string, error) { + var op *compute.Operation + var err error + + switch w.Type { + case OperationWaitGlobal: + op, err = w.Service.GlobalOperations.Get( + w.Project, w.Op.Name).Do() + case OperationWaitRegion: + op, err = w.Service.RegionOperations.Get( + w.Project, w.Region, w.Op.Name).Do() + case OperationWaitZone: + op, err = w.Service.ZoneOperations.Get( + w.Project, w.Zone, w.Op.Name).Do() + default: + return nil, "bad-type", fmt.Errorf( + "Invalid wait type: %#v", w.Type) + } + + if err != nil { + return nil, "", err + } + + return op, op.Status, nil + } +} + +func (w *OperationWaiter) Conf() *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{"PENDING", "RUNNING"}, + Target: "DONE", + Refresh: w.RefreshFunc(), + } +} diff --git a/provider.go b/provider.go index 71ef37d8..84e6c17e 100644 --- a/provider.go +++ b/provider.go @@ -17,9 +17,20 @@ func Provider() *schema.Provider { Type: schema.TypeString, Required: true, }, + + "project": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, }, ResourcesMap: map[string]*schema.Resource{ + "google_compute_address": resourceComputeAddress(), "google_compute_instance": resourceComputeInstance(), }, @@ -31,11 +42,13 @@ 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), } if err := config.loadAndValidate(); err != nil { return nil, err } - return nil, nil + return &config, nil } diff --git a/resource_compute_address.go b/resource_compute_address.go new file mode 100644 index 00000000..b0abdb83 --- /dev/null +++ b/resource_compute_address.go @@ -0,0 +1,107 @@ +package google + +import ( + "fmt" + "log" + "time" + + "code.google.com/p/google-api-go-client/compute/v1" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceComputeAddress() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeAddressCreate, + Read: resourceComputeAddressRead, + Delete: resourceComputeAddressDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Build the address parameter + addr := &compute.Address{Name: d.Get("name").(string)} + log.Printf("[DEBUG] Address insert request: %#v", addr) + op, err := config.clientCompute.Addresses.Insert( + config.Project, config.Region, addr).Do() + if err != nil { + return fmt.Errorf("Error creating address: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(addr.Name) + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Project: config.Project, + Region: config.Region, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + if _, err := state.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for address to create: %s", err) + } + + return resourceComputeAddressRead(d, meta) +} + +func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + addr, err := config.clientCompute.Addresses.Get( + config.Project, config.Region, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error reading address: %s", err) + } + + d.Set("address", addr.Address) + + return nil +} + +func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Delete the address + op, err := config.clientCompute.Addresses.Delete( + config.Project, config.Region, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting address: %s", err) + } + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Project: config.Project, + Region: config.Region, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + if _, err := state.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for address to delete: %s", err) + } + + d.SetId("") + return nil +} diff --git a/resource_compute_instance_test.go b/resource_compute_address_test.go similarity index 94% rename from resource_compute_instance_test.go rename to resource_compute_address_test.go index 067512ec..b042e558 100644 --- a/resource_compute_instance_test.go +++ b/resource_compute_address_test.go @@ -6,14 +6,14 @@ import ( "github.com/hashicorp/terraform/helper/resource" ) -func TestAccComputeInstance_basic(t *testing.T) { +func TestAccComputeAddress_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, + Config: testAccComputeAddress_basic, /* Check: resource.ComposeTestCheckFunc( testAccCheckHerokuAppExists("heroku_app.foobar", &app), @@ -156,6 +156,7 @@ func testAccCheckHerokuAppExists(n string, app *heroku.App) resource.TestCheckFu } */ -const testAccComputeInstance_basic = ` -resource "google_compute_instance" "foobar" { +const testAccComputeAddress_basic = ` +resource "google_compute_address" "foobar" { + name = "terraform-test" }`