From df251f17c93cef5bb694e5a955b9c795046d46d1 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Tue, 12 May 2015 14:58:10 -0500 Subject: [PATCH 1/8] Strip 'sdk' suffix from methods; it's a remnant --- resource_compute_http_health_check_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resource_compute_http_health_check_test.go b/resource_compute_http_health_check_test.go index d071b5ae..c37c770b 100644 --- a/resource_compute_http_health_check_test.go +++ b/resource_compute_http_health_check_test.go @@ -116,7 +116,7 @@ func testAccCheckComputeHttpHealthCheckExists(n string, healthCheck *compute.Htt func testAccCheckComputeHttpHealthCheckRequestPath(path string, healthCheck *compute.HttpHealthCheck) resource.TestCheckFunc { return func(s *terraform.State) error { if healthCheck.RequestPath != path { - return fmt.Errorf("RequestPath doesn't match: expected %d, got %d", path, healthCheck.RequestPath) + return fmt.Errorf("RequestPath doesn't match: expected %s, got %s", path, healthCheck.RequestPath) } return nil From a4170297a3caa9c25d49d8d40c0fd19d56b283d5 Mon Sep 17 00:00:00 2001 From: Dan Hilton Date: Thu, 21 May 2015 18:28:27 +0100 Subject: [PATCH 2/8] provider/google: Add support for Google Compute Stogare buckets. Configure Google Compute Storage buckets using: * name (compulsory attribute) * predefined_acl (optional, default: `projectPrivate`) * location (optional, default: `US`) * force_destroy (optional, default: `false`) Currently supporting only `predefined_acl`s. Bucket attribute updates happen via re-creation. force_destroy will cause bucket objects to be purged, enabling bucket destruction. --- config.go | 12 +- provider.go | 1 + resource_storage_bucket.go | 144 ++++++++++++++++++++ resource_storage_bucket_test.go | 231 ++++++++++++++++++++++++++++++++ 4 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 resource_storage_bucket.go create mode 100644 resource_storage_bucket_test.go diff --git a/config.go b/config.go index de97df60..25348bbf 100644 --- a/config.go +++ b/config.go @@ -15,6 +15,7 @@ import ( "golang.org/x/oauth2/jwt" "google.golang.org/api/compute/v1" "google.golang.org/api/dns/v1" + "google.golang.org/api/storage/v1" ) // Config is the configuration structure used to instantiate the Google @@ -25,7 +26,8 @@ type Config struct { Region string clientCompute *compute.Service - clientDns *dns.Service + clientDns *dns.Service + clientStorage *storage.Service } func (c *Config) loadAndValidate() error { @@ -55,6 +57,7 @@ func (c *Config) loadAndValidate() error { clientScopes := []string{ "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/ndev.clouddns.readwrite", + "https://www.googleapis.com/auth/devstorage.full_control", } // Get the token for use in our requests @@ -114,6 +117,13 @@ func (c *Config) loadAndValidate() error { } c.clientDns.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Storage Client...") + c.clientStorage, err = storage.New(client) + if err != nil { + return err + } + c.clientStorage.UserAgent = userAgent + return nil } diff --git a/provider.go b/provider.go index 09687a77..1554d915 100644 --- a/provider.go +++ b/provider.go @@ -41,6 +41,7 @@ func Provider() terraform.ResourceProvider { "google_compute_target_pool": resourceComputeTargetPool(), "google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_record_set": resourceDnsRecordSet(), + "google_storage_bucket": resourceStorageBucket(), }, ConfigureFunc: providerConfigure, diff --git a/resource_storage_bucket.go b/resource_storage_bucket.go new file mode 100644 index 00000000..59370720 --- /dev/null +++ b/resource_storage_bucket.go @@ -0,0 +1,144 @@ +package google + +import ( + "errors" + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "google.golang.org/api/storage/v1" +) + +func resourceStorageBucket() *schema.Resource { + return &schema.Resource{ + Create: resourceStorageBucketCreate, + Read: resourceStorageBucketRead, + Update: resourceStorageBucketUpdate, + Delete: resourceStorageBucketDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "predefined_acl": &schema.Schema{ + Type: schema.TypeString, + Default: "projectPrivate", + Optional: true, + ForceNew: true, + }, + "location": &schema.Schema{ + Type: schema.TypeString, + Default: "US", + Optional: true, + ForceNew: true, + }, + "force_destroy": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + } +} + +func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Get the bucket and acl + bucket := d.Get("name").(string) + acl := d.Get("predefined_acl").(string) + location := d.Get("location").(string) + + // Create a bucket, setting the acl, location and name. + sb := &storage.Bucket{Name: bucket, Location: location} + res, err := config.clientStorage.Buckets.Insert(config.Project, sb).PredefinedAcl(acl).Do() + + if err != nil { + fmt.Printf("Error creating bucket %s: %v", bucket, err) + return err + } + + log.Printf("[DEBUG] Created bucket %v at location %v\n\n", res.Name, res.SelfLink) + + // Assign the bucket ID as the resource ID + d.SetId(res.Id) + + return nil +} + +func resourceStorageBucketUpdate(d *schema.ResourceData, meta interface{}) error { + // Only thing you can currently change is force_delete (all other properties have ForceNew) + // which is just terraform object state change, so nothing to do here + return nil +} + +func resourceStorageBucketRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Get the bucket and acl + bucket := d.Get("name").(string) + res, err := config.clientStorage.Buckets.Get(bucket).Do() + + if err != nil { + fmt.Printf("Error reading bucket %s: %v", bucket, err) + return err + } + + log.Printf("[DEBUG] Read bucket %v at location %v\n\n", res.Name, res.SelfLink) + + // Update the bucket ID according to the resource ID + d.SetId(res.Id) + + return nil +} + +func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Get the bucket + bucket := d.Get("name").(string) + + for { + res, err := config.clientStorage.Objects.List(bucket).Do() + if err != nil { + fmt.Printf("Error Objects.List failed: %v", err) + return err + } + + if len(res.Items) != 0 { + if d.Get("force_destroy").(bool) { + // purge the bucket... + log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n") + + for _, object := range res.Items { + log.Printf("[DEBUG] Found %s", object.Name) + if err := config.clientStorage.Objects.Delete(bucket, object.Name).Do(); err != nil { + log.Fatalf("Error trying to delete object: %s %s\n\n", object.Name, err) + } else { + log.Printf("Object deleted: %s \n\n", object.Name) + } + } + + } else { + delete_err := errors.New("Error trying to delete a bucket containing objects without `force_destroy` set to true") + log.Printf("Error! %s : %s\n\n", bucket, delete_err) + return delete_err + } + } else { + break // 0 items, bucket empty + } + } + + // remove empty bucket + err := config.clientStorage.Buckets.Delete(bucket).Do() + if err != nil { + fmt.Printf("Error deleting bucket %s: %v\n\n", bucket, err) + return err + } + log.Printf("[DEBUG] Deleted bucket %v\n\n", bucket) + + return nil +} diff --git a/resource_storage_bucket_test.go b/resource_storage_bucket_test.go new file mode 100644 index 00000000..e33cd5cb --- /dev/null +++ b/resource_storage_bucket_test.go @@ -0,0 +1,231 @@ +package google + +import ( + "fmt" + "math/rand" + "bytes" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "google.golang.org/api/googleapi" + storage "google.golang.org/api/storage/v1" +) + +func TestAccStorageDefaults(t *testing.T) { + var bucketName string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsReaderDefaults, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketExists( + "google_storage_bucket.bucket", &bucketName), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "predefined_acl", "projectPrivate"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "US"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "false"), + ), + }, + }, + }) +} + +func TestAccStorageCustomAttributes(t *testing.T) { + var bucketName string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsReaderCustomAttributes, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketExists( + "google_storage_bucket.bucket", &bucketName), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "predefined_acl", "publicReadWrite"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + ), + }, + }, + }) +} + +func TestAccStorageBucketUpdate(t *testing.T) { + var bucketName string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsReaderDefaults, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketExists( + "google_storage_bucket.bucket", &bucketName), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "predefined_acl", "projectPrivate"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "US"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "false"), + ), + }, + resource.TestStep{ + Config: testGoogleStorageBucketsReaderCustomAttributes, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketExists( + "google_storage_bucket.bucket", &bucketName), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "predefined_acl", "publicReadWrite"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + ), + }, + }, + }) +} + +func TestAccStorageForceDestroy(t *testing.T) { + var bucketName string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsReaderCustomAttributes, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketExists( + "google_storage_bucket.bucket", &bucketName), + ), + }, + resource.TestStep{ + Config: testGoogleStorageBucketsReaderCustomAttributes, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketPutItem(&bucketName), + ), + }, + resource.TestStep{ + Config: "", + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStorageBucketMissing(&bucketName), + ), + }, + }, + }) +} + +func testAccCheckCloudStorageBucketExists(n string, bucketName *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Project_ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Id != rs.Primary.ID { + return fmt.Errorf("Bucket not found") + } + + *bucketName = found.Name + return nil + } +} + +func testAccCheckCloudStorageBucketPutItem(bucketName *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + data := bytes.NewBufferString("test") + dataReader := bytes.NewReader(data.Bytes()) + object := &storage.Object{Name: "bucketDestroyTestFile"} + + // This needs to use Media(io.Reader) call, otherwise it does not go to /upload API and fails + if res, err := config.clientStorage.Objects.Insert(*bucketName, object).Media(dataReader).Do(); err == nil { + fmt.Printf("Created object %v at location %v\n\n", res.Name, res.SelfLink) + } else { + return fmt.Errorf("Objects.Insert failed: %v", err) + } + + return nil + } +} + +func testAccCheckCloudStorageBucketMissing(bucketName *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.Buckets.Get(*bucketName).Do() + if err == nil { + return fmt.Errorf("Found %s", *bucketName) + } + + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + return nil + } else { + return err + } + } +} + +func testAccGoogleStorageDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket" { + continue + } + + _, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Bucket still exists") + } + } + + return nil +} + +var randInt = rand.New(rand.NewSource(time.Now().UnixNano())).Int() + +var testGoogleStorageBucketsReaderDefaults = fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "tf-test-bucket-%d" +} +`, randInt) + +var testGoogleStorageBucketsReaderCustomAttributes = fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "tf-test-bucket-%d" + predefined_acl = "publicReadWrite" + location = "EU" + force_destroy = "true" +} +`, randInt) From e39d629ba6830eef5b5f385c1069aeb7a20fc9dd Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Sun, 7 Jun 2015 18:18:14 -0500 Subject: [PATCH 3/8] acc tests: ensure each resource has a _basic test Helpful for breadth first acc test sweeps `-run '_basic$'` --- resource_storage_bucket_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resource_storage_bucket_test.go b/resource_storage_bucket_test.go index e33cd5cb..a7b59c61 100644 --- a/resource_storage_bucket_test.go +++ b/resource_storage_bucket_test.go @@ -1,9 +1,9 @@ package google import ( + "bytes" "fmt" "math/rand" - "bytes" "testing" "time" @@ -14,7 +14,7 @@ import ( storage "google.golang.org/api/storage/v1" ) -func TestAccStorageDefaults(t *testing.T) { +func TestAccStorage_basic(t *testing.T) { var bucketName string resource.Test(t, resource.TestCase{ From aca09d1cc721168b7996f622240ae4b11c8aa236 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Wed, 10 Jun 2015 00:14:13 -0400 Subject: [PATCH 4/8] Add beta compute client --- config.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config.go b/config.go index 25348bbf..0640690e 100644 --- a/config.go +++ b/config.go @@ -14,6 +14,7 @@ import ( "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" "google.golang.org/api/compute/v1" + computeBeta "google.golang.org/api/compute/v0.beta" "google.golang.org/api/dns/v1" "google.golang.org/api/storage/v1" ) @@ -26,6 +27,7 @@ type Config struct { Region string clientCompute *compute.Service + clientComputeBeta *computeBeta.Service clientDns *dns.Service clientStorage *storage.Service } @@ -110,6 +112,13 @@ func (c *Config) loadAndValidate() error { } c.clientCompute.UserAgent = userAgent + log.Printf("[INFO] Instantiating Beta GCE client...") + c.clientComputeBeta, err = computeBeta.New(client) + if err != nil { + return err + } + c.clientComputeBeta.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Cloud DNS client...") c.clientDns, err = dns.New(client) if err != nil { From 14ff9f2912f4c7a3550c7dd509241e6cb406724a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 23 Jun 2015 22:31:24 -0700 Subject: [PATCH 5/8] fmt --- config.go | 8 ++--- dns_change.go | 6 ++-- resource_compute_instance.go | 14 ++++----- resource_compute_instance_migrate.go | 3 +- resource_compute_instance_migrate_test.go | 14 ++++----- resource_dns_managed_zone.go | 24 +++++++-------- resource_dns_record_set.go | 37 +++++++++++------------ 7 files changed, 52 insertions(+), 54 deletions(-) diff --git a/config.go b/config.go index 0640690e..dda16a03 100644 --- a/config.go +++ b/config.go @@ -13,8 +13,8 @@ import ( "golang.org/x/oauth2" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" - "google.golang.org/api/compute/v1" computeBeta "google.golang.org/api/compute/v0.beta" + "google.golang.org/api/compute/v1" "google.golang.org/api/dns/v1" "google.golang.org/api/storage/v1" ) @@ -26,10 +26,10 @@ type Config struct { Project string Region string - clientCompute *compute.Service + clientCompute *compute.Service clientComputeBeta *computeBeta.Service - clientDns *dns.Service - clientStorage *storage.Service + clientDns *dns.Service + clientStorage *storage.Service } func (c *Config) loadAndValidate() error { diff --git a/dns_change.go b/dns_change.go index d8cb73cd..a1facdd9 100644 --- a/dns_change.go +++ b/dns_change.go @@ -7,9 +7,9 @@ import ( ) type DnsChangeWaiter struct { - Service *dns.Service - Change *dns.Change - Project string + Service *dns.Service + Change *dns.Change + Project string ManagedZone string } diff --git a/resource_compute_instance.go b/resource_compute_instance.go index 956f2745..380aac8d 100644 --- a/resource_compute_instance.go +++ b/resource_compute_instance.go @@ -101,7 +101,7 @@ func resourceComputeInstance() *schema.Resource { }, "device_name": &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeString, Optional: true, }, }, @@ -148,9 +148,9 @@ func resourceComputeInstance() *schema.Resource { }, "network": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - ForceNew: true, + Type: schema.TypeList, + Optional: true, + ForceNew: true, Deprecated: "Please use network_interface", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -229,7 +229,7 @@ func resourceComputeInstance() *schema.Resource { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: stringHashcode, + Set: stringHashcode, }, "metadata_fingerprint": &schema.Schema{ @@ -327,7 +327,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err disk.Source = diskData.SelfLink } else { // Create a new disk - disk.InitializeParams = &compute.AttachedDiskInitializeParams{ } + disk.InitializeParams = &compute.AttachedDiskInitializeParams{} } if v, ok := d.GetOk(prefix + ".scratch"); ok { @@ -367,7 +367,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err disk.InitializeParams.DiskSizeGb = int64(diskSizeGb) } - if v, ok := d.GetOk(prefix + ".device_name"); ok { + if v, ok := d.GetOk(prefix + ".device_name"); ok { disk.DeviceName = v.(string) } diff --git a/resource_compute_instance_migrate.go b/resource_compute_instance_migrate.go index 749839ad..05dc6b57 100644 --- a/resource_compute_instance_migrate.go +++ b/resource_compute_instance_migrate.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/terraform" ) @@ -127,7 +127,6 @@ func migrateStateV1toV2(is *terraform.InstanceState) (*terraform.InstanceState, delete(is.Attributes, k) } - for service_acct_index, newScopes := range newScopesMap { for _, newScope := range newScopes { hash := hashcode.String(canonicalizeServiceScope(newScope)) diff --git a/resource_compute_instance_migrate_test.go b/resource_compute_instance_migrate_test.go index 9272761c..7f9857e4 100644 --- a/resource_compute_instance_migrate_test.go +++ b/resource_compute_instance_migrate_test.go @@ -30,8 +30,8 @@ func TestComputeInstanceMigrateState(t *testing.T) { "change scope from list to set": { StateVersion: 1, Attributes: map[string]string{ - "service_account.#": "1", - "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", + "service_account.#": "1", + "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", "service_account.0.scopes.#": "4", "service_account.0.scopes.0": "https://www.googleapis.com/auth/compute", "service_account.0.scopes.1": "https://www.googleapis.com/auth/datastore", @@ -39,12 +39,12 @@ func TestComputeInstanceMigrateState(t *testing.T) { "service_account.0.scopes.3": "https://www.googleapis.com/auth/logging.write", }, Expected: map[string]string{ - "service_account.#": "1", - "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", - "service_account.0.scopes.#": "4", + "service_account.#": "1", + "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", + "service_account.0.scopes.#": "4", "service_account.0.scopes.1693978638": "https://www.googleapis.com/auth/devstorage.full_control", - "service_account.0.scopes.172152165": "https://www.googleapis.com/auth/logging.write", - "service_account.0.scopes.299962681": "https://www.googleapis.com/auth/compute", + "service_account.0.scopes.172152165": "https://www.googleapis.com/auth/logging.write", + "service_account.0.scopes.299962681": "https://www.googleapis.com/auth/compute", "service_account.0.scopes.3435931483": "https://www.googleapis.com/auth/datastore", }, }, diff --git a/resource_dns_managed_zone.go b/resource_dns_managed_zone.go index 0e10d3b0..7253297e 100644 --- a/resource_dns_managed_zone.go +++ b/resource_dns_managed_zone.go @@ -50,18 +50,18 @@ func resourceDnsManagedZone() *schema.Resource { func resourceDnsManagedZoneCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - // Build the parameter - zone := &dns.ManagedZone{ - Name: d.Get("name").(string), + // Build the parameter + zone := &dns.ManagedZone{ + Name: d.Get("name").(string), DnsName: d.Get("dns_name").(string), - } - // Optional things - if v, ok := d.GetOk("description"); ok { - zone.Description = v.(string) - } - if v, ok := d.GetOk("dns_name"); ok { - zone.DnsName = v.(string) - } + } + // Optional things + if v, ok := d.GetOk("description"); ok { + zone.Description = v.(string) + } + if v, ok := d.GetOk("dns_name"); ok { + zone.DnsName = v.(string) + } log.Printf("[DEBUG] DNS ManagedZone create request: %#v", zone) zone, err := config.clientDns.ManagedZones.Create(config.Project, zone).Do() @@ -90,7 +90,7 @@ func resourceDnsManagedZoneRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error reading DNS ManagedZone: %#v", err) } - d.Set("name_servers", zone.NameServers) + d.Set("name_servers", zone.NameServers) return nil } diff --git a/resource_dns_record_set.go b/resource_dns_record_set.go index 795d4998..05fa547f 100644 --- a/resource_dns_record_set.go +++ b/resource_dns_record_set.go @@ -61,17 +61,17 @@ func resourceDnsRecordSetCreate(d *schema.ResourceData, meta interface{}) error // Build the change chg := &dns.Change{ - Additions: []*dns.ResourceRecordSet { - &dns.ResourceRecordSet { - Name: d.Get("name").(string), - Type: d.Get("type").(string), - Ttl: int64(d.Get("ttl").(int)), + Additions: []*dns.ResourceRecordSet{ + &dns.ResourceRecordSet{ + Name: d.Get("name").(string), + Type: d.Get("type").(string), + Ttl: int64(d.Get("ttl").(int)), Rrdatas: make([]string, rrdatasCount), }, }, } - for i := 0; i < rrdatasCount ; i++ { + for i := 0; i < rrdatasCount; i++ { rrdata := fmt.Sprintf("rrdatas.%d", i) chg.Additions[0].Rrdatas[i] = d.Get(rrdata).(string) } @@ -85,9 +85,9 @@ func resourceDnsRecordSetCreate(d *schema.ResourceData, meta interface{}) error d.SetId(chg.Id) w := &DnsChangeWaiter{ - Service: config.clientDns, - Change: chg, - Project: config.Project, + Service: config.clientDns, + Change: chg, + Project: config.Project, ManagedZone: zone, } state := w.Conf() @@ -126,7 +126,6 @@ func resourceDnsRecordSetRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Only expected 1 record set, got %d", len(resp.Rrsets)) } - d.Set("ttl", resp.Rrsets[0].Ttl) d.Set("rrdatas", resp.Rrsets[0].Rrdatas) @@ -142,17 +141,17 @@ func resourceDnsRecordSetDelete(d *schema.ResourceData, meta interface{}) error // Build the change chg := &dns.Change{ - Deletions: []*dns.ResourceRecordSet { - &dns.ResourceRecordSet { - Name: d.Get("name").(string), - Type: d.Get("type").(string), - Ttl: int64(d.Get("ttl").(int)), + Deletions: []*dns.ResourceRecordSet{ + &dns.ResourceRecordSet{ + Name: d.Get("name").(string), + Type: d.Get("type").(string), + Ttl: int64(d.Get("ttl").(int)), Rrdatas: make([]string, rrdatasCount), }, }, } - for i := 0; i < rrdatasCount ; i++ { + for i := 0; i < rrdatasCount; i++ { rrdata := fmt.Sprintf("rrdatas.%d", i) chg.Deletions[0].Rrdatas[i] = d.Get(rrdata).(string) } @@ -163,9 +162,9 @@ func resourceDnsRecordSetDelete(d *schema.ResourceData, meta interface{}) error } w := &DnsChangeWaiter{ - Service: config.clientDns, - Change: chg, - Project: config.Project, + Service: config.clientDns, + Change: chg, + Project: config.Project, ManagedZone: zone, } state := w.Conf() From a4037a0f61d11ae4752daae5ef3114a663acfc39 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Wed, 1 Jul 2015 21:24:34 -0400 Subject: [PATCH 6/8] Add ForceNew metadata_startup_script field --- resource_compute_instance.go | 33 +++++++++++++++++++++++---- resource_compute_instance_template.go | 6 ++++- resource_compute_instance_test.go | 4 ++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/resource_compute_instance.go b/resource_compute_instance.go index 380aac8d..8233815b 100644 --- a/resource_compute_instance.go +++ b/resource_compute_instance.go @@ -191,6 +191,12 @@ func resourceComputeInstance() *schema.Resource { ForceNew: true, }, + "metadata_startup_script": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "metadata": &schema.Schema{ Type: schema.TypeMap, Optional: true, @@ -469,13 +475,18 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err serviceAccounts = append(serviceAccounts, serviceAccount) } + metadata, err := resourceInstanceMetadata(d) + if err != nil { + return fmt.Errorf("Error creating metadata: %s", err) + } + // Create the instance information instance := compute.Instance{ CanIpForward: d.Get("can_ip_forward").(bool), Description: d.Get("description").(string), Disks: disks, MachineType: machineType.SelfLink, - Metadata: resourceInstanceMetadata(d), + Metadata: metadata, Name: d.Get("name").(string), NetworkInterfaces: networkInterfaces, Tags: resourceInstanceTags(d), @@ -662,7 +673,10 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // If the Metadata has changed, then update that. if d.HasChange("metadata") { - metadata := resourceInstanceMetadata(d) + metadata, err := resourceInstanceMetadata(d) + if err != nil { + return fmt.Errorf("Error updating metadata: %s", err) + } op, err := config.clientCompute.Instances.SetMetadata( config.Project, zone, d.Id(), metadata).Do() if err != nil { @@ -781,9 +795,18 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err return nil } -func resourceInstanceMetadata(d *schema.ResourceData) *compute.Metadata { +func resourceInstanceMetadata(d *schema.ResourceData) (*compute.Metadata, error) { m := &compute.Metadata{} - if mdMap := d.Get("metadata").(map[string]interface{}); len(mdMap) > 0 { + mdMap := d.Get("metadata").(map[string]interface{}) + _, mapScriptExists := mdMap["startup-script"] + dScript, dScriptExists := d.GetOk("metadata_startup_script") + if mapScriptExists && dScriptExists { + return nil, fmt.Errorf("Not allowed to have both metadata_startup_script and metadata.startup-script") + } + if dScriptExists { + mdMap["startup-script"] = dScript + } + if len(mdMap) > 0 { m.Items = make([]*compute.MetadataItems, 0, len(mdMap)) for key, val := range mdMap { m.Items = append(m.Items, &compute.MetadataItems{ @@ -797,7 +820,7 @@ func resourceInstanceMetadata(d *schema.ResourceData) *compute.Metadata { m.Fingerprint = d.Get("metadata_fingerprint").(string) } - return m + return m, nil } func resourceInstanceTags(d *schema.ResourceData) *compute.Tags { diff --git a/resource_compute_instance_template.go b/resource_compute_instance_template.go index f1d2f9bc..4069da10 100644 --- a/resource_compute_instance_template.go +++ b/resource_compute_instance_template.go @@ -331,7 +331,11 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac instanceProperties.Description = d.Get("instance_description").(string) instanceProperties.MachineType = d.Get("machine_type").(string) instanceProperties.Disks = buildDisks(d, meta) - instanceProperties.Metadata = resourceInstanceMetadata(d) + metadata, err := resourceInstanceMetadata(d) + if err != nil { + return err + } + instanceProperties.Metadata = metadata err, networks := buildNetworks(d, meta) if err != nil { return err diff --git a/resource_compute_instance_test.go b/resource_compute_instance_test.go index 70d0c5f2..3ae487a1 100644 --- a/resource_compute_instance_test.go +++ b/resource_compute_instance_test.go @@ -476,10 +476,10 @@ resource "google_compute_instance" "foobar" { metadata { foo = "bar" - } - metadata { baz = "qux" } + + metadata_startup_script = "echo Hello" }` const testAccComputeInstance_basic2 = ` From c6db486ab826afdc2d3bd6edbf5908d5c6b9e6d8 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 5 Jul 2015 18:39:01 +0200 Subject: [PATCH 7/8] Add new resource - google_container_cluster --- config.go | 10 + provider.go | 1 + resource_container_cluster.go | 445 +++++++++++++++++++++++++++++ resource_container_cluster_test.go | 85 ++++++ 4 files changed, 541 insertions(+) create mode 100644 resource_container_cluster.go create mode 100644 resource_container_cluster_test.go diff --git a/config.go b/config.go index dda16a03..905e56d4 100644 --- a/config.go +++ b/config.go @@ -15,6 +15,7 @@ import ( "golang.org/x/oauth2/jwt" computeBeta "google.golang.org/api/compute/v0.beta" "google.golang.org/api/compute/v1" + "google.golang.org/api/container/v1" "google.golang.org/api/dns/v1" "google.golang.org/api/storage/v1" ) @@ -28,6 +29,7 @@ type Config struct { clientCompute *compute.Service clientComputeBeta *computeBeta.Service + clientContainer *container.Service clientDns *dns.Service clientStorage *storage.Service } @@ -58,6 +60,7 @@ func (c *Config) loadAndValidate() error { clientScopes := []string{ "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/ndev.clouddns.readwrite", "https://www.googleapis.com/auth/devstorage.full_control", } @@ -119,6 +122,13 @@ func (c *Config) loadAndValidate() error { } c.clientComputeBeta.UserAgent = userAgent + log.Printf("[INFO] Instantiating GKE client...") + c.clientContainer, err = container.New(client) + if err != nil { + return err + } + c.clientContainer.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Cloud DNS client...") c.clientDns, err = dns.New(client) if err != nil { diff --git a/provider.go b/provider.go index 1554d915..b19d9fce 100644 --- a/provider.go +++ b/provider.go @@ -39,6 +39,7 @@ func Provider() terraform.ResourceProvider { "google_compute_network": resourceComputeNetwork(), "google_compute_route": resourceComputeRoute(), "google_compute_target_pool": resourceComputeTargetPool(), + "google_container_cluster": resourceContainerCluster(), "google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_record_set": resourceDnsRecordSet(), "google_storage_bucket": resourceStorageBucket(), diff --git a/resource_container_cluster.go b/resource_container_cluster.go new file mode 100644 index 00000000..be957381 --- /dev/null +++ b/resource_container_cluster.go @@ -0,0 +1,445 @@ +package google + +import ( + "fmt" + "log" + "net" + "regexp" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/container/v1" +) + +func resourceContainerCluster() *schema.Resource { + return &schema.Resource{ + Create: resourceContainerClusterCreate, + Read: resourceContainerClusterRead, + Update: resourceContainerClusterUpdate, + Delete: resourceContainerClusterDelete, + + Schema: map[string]*schema.Schema{ + "zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "node_version": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "cluster_ipv4_cidr": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + + if err != nil || ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR", k)) + } + return + }, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "endpoint": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "logging_service": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "monitoring_service": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "master_auth": &schema.Schema{ + Type: schema.TypeList, + Required: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_certificate": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "client_key": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "cluster_ca_certificate": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 40 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 40 characters", k)) + } + if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q can only contain lowercase letters, numbers and hyphens", k)) + } + if !regexp.MustCompile("^[a-z]").MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must start with a letter", k)) + } + if !regexp.MustCompile("[a-z0-9]$").MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must end with a number or a letter", k)) + } + return + }, + }, + + "network": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "default", + ForceNew: true, + }, + + "node_config": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "machine_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "disk_size_gb": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + + if value < 10 { + errors = append(errors, fmt.Errorf( + "%q cannot be less than 10", k)) + } + return + }, + }, + + "oauth_scopes": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + }, + }, + + "initial_node_count": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "instance_group_urls": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + zoneName := d.Get("zone").(string) + clusterName := d.Get("name").(string) + + masterAuths := d.Get("master_auth").([]interface{}) + if len(masterAuths) > 1 { + return fmt.Errorf("Cannot specify more than one master_auth.") + } + masterAuth := masterAuths[0].(map[string]interface{}) + + cluster := &container.Cluster{ + MasterAuth: &container.MasterAuth{ + Password: masterAuth["password"].(string), + Username: masterAuth["username"].(string), + }, + Name: clusterName, + InitialNodeCount: int64(d.Get("initial_node_count").(int)), + } + + if v, ok := d.GetOk("cluster_ipv4_cidr"); ok { + cluster.ClusterIpv4Cidr = v.(string) + } + + if v, ok := d.GetOk("description"); ok { + cluster.Description = v.(string) + } + + if v, ok := d.GetOk("logging_service"); ok { + cluster.LoggingService = v.(string) + } + + if v, ok := d.GetOk("monitoring_service"); ok { + cluster.MonitoringService = v.(string) + } + + if v, ok := d.GetOk("network"); ok { + cluster.Network = v.(string) + } + + if v, ok := d.GetOk("node_config"); ok { + nodeConfigs := v.([]interface{}) + if len(nodeConfigs) > 1 { + return fmt.Errorf("Cannot specify more than one node_config.") + } + nodeConfig := nodeConfigs[0].(map[string]interface{}) + + cluster.NodeConfig = &container.NodeConfig{} + + if v, ok = nodeConfig["machine_type"]; ok { + cluster.NodeConfig.MachineType = v.(string) + } + + if v, ok = nodeConfig["disk_size_gb"]; ok { + cluster.NodeConfig.DiskSizeGb = v.(int64) + } + + if v, ok := nodeConfig["oauth_scopes"]; ok { + scopesList := v.([]interface{}) + scopes := []string{} + for _, v := range scopesList { + scopes = append(scopes, v.(string)) + } + + cluster.NodeConfig.OauthScopes = scopes + } + } + + req := &container.CreateClusterRequest{ + Cluster: cluster, + } + + op, err := config.clientContainer.Projects.Zones.Clusters.Create( + config.Project, zoneName, req).Do() + if err != nil { + return err + } + + // Wait until it's created + wait := resource.StateChangeConf{ + Pending: []string{"PENDING", "RUNNING"}, + Target: "DONE", + Timeout: 30 * time.Minute, + MinTimeout: 3 * time.Second, + Refresh: func() (interface{}, string, error) { + resp, err := config.clientContainer.Projects.Zones.Operations.Get( + config.Project, zoneName, op.Name).Do() + log.Printf("[DEBUG] Progress of creating GKE cluster %s: %s", + clusterName, resp.Status) + return resp, resp.Status, err + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s has been created", clusterName) + + d.SetId(clusterName) + + return resourceContainerClusterRead(d, meta) +} + +func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + zoneName := d.Get("zone").(string) + + cluster, err := config.clientContainer.Projects.Zones.Clusters.Get( + config.Project, zoneName, d.Get("name").(string)).Do() + if err != nil { + return err + } + + d.Set("name", cluster.Name) + d.Set("zone", cluster.Zone) + d.Set("endpoint", cluster.Endpoint) + + masterAuth := []map[string]interface{}{ + map[string]interface{}{ + "username": cluster.MasterAuth.Username, + "password": cluster.MasterAuth.Password, + "client_certificate": cluster.MasterAuth.ClientCertificate, + "client_key": cluster.MasterAuth.ClientKey, + "cluster_ca_certificate": cluster.MasterAuth.ClusterCaCertificate, + }, + } + d.Set("master_auth", masterAuth) + + d.Set("initial_node_count", cluster.InitialNodeCount) + d.Set("node_version", cluster.CurrentNodeVersion) + d.Set("cluster_ipv4_cidr", cluster.ClusterIpv4Cidr) + d.Set("description", cluster.Description) + d.Set("logging_service", cluster.LoggingService) + d.Set("monitoring_service", cluster.MonitoringService) + d.Set("network", cluster.Network) + d.Set("node_config", flattenClusterNodeConfig(cluster.NodeConfig)) + d.Set("instance_group_urls", cluster.InstanceGroupUrls) + + return nil +} + +func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + zoneName := d.Get("zone").(string) + clusterName := d.Get("name").(string) + desiredNodeVersion := d.Get("node_version").(string) + + req := &container.UpdateClusterRequest{ + Update: &container.ClusterUpdate{ + DesiredNodeVersion: desiredNodeVersion, + }, + } + op, err := config.clientContainer.Projects.Zones.Clusters.Update( + config.Project, zoneName, clusterName, req).Do() + if err != nil { + return err + } + + // Wait until it's updated + wait := resource.StateChangeConf{ + Pending: []string{"PENDING", "RUNNING"}, + Target: "DONE", + Timeout: 10 * time.Minute, + MinTimeout: 2 * time.Second, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if GKE cluster %s is updated", clusterName) + resp, err := config.clientContainer.Projects.Zones.Operations.Get( + config.Project, zoneName, op.Name).Do() + log.Printf("[DEBUG] Progress of updating GKE cluster %s: %s", + clusterName, resp.Status) + return resp, resp.Status, err + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s has been updated to %s", d.Id(), + desiredNodeVersion) + + return resourceContainerClusterRead(d, meta) +} + +func resourceContainerClusterDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + zoneName := d.Get("zone").(string) + clusterName := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting GKE cluster %s", d.Get("name").(string)) + op, err := config.clientContainer.Projects.Zones.Clusters.Delete( + config.Project, zoneName, clusterName).Do() + if err != nil { + return err + } + + // Wait until it's deleted + wait := resource.StateChangeConf{ + Pending: []string{"PENDING", "RUNNING"}, + Target: "DONE", + Timeout: 10 * time.Minute, + MinTimeout: 3 * time.Second, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if GKE cluster %s is deleted", clusterName) + resp, err := config.clientContainer.Projects.Zones.Operations.Get( + config.Project, zoneName, op.Name).Do() + log.Printf("[DEBUG] Progress of deleting GKE cluster %s: %s", + clusterName, resp.Status) + return resp, resp.Status, err + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s has been deleted", d.Id()) + + d.SetId("") + + return nil +} + +func flattenClusterNodeConfig(c *container.NodeConfig) []map[string]interface{} { + config := []map[string]interface{}{ + map[string]interface{}{ + "machine_type": c.MachineType, + "disk_size_gb": c.DiskSizeGb, + }, + } + + if len(c.OauthScopes) > 0 { + config[0]["oauth_scopes"] = c.OauthScopes + } + + return config +} diff --git a/resource_container_cluster_test.go b/resource_container_cluster_test.go new file mode 100644 index 00000000..daced551 --- /dev/null +++ b/resource_container_cluster_test.go @@ -0,0 +1,85 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccContainerCluster_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccContainerCluster_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerClusterExists( + "google_container_cluster.primary"), + ), + }, + }, + }) +} + +func testAccCheckContainerClusterDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_container_cluster" { + continue + } + + attributes := rs.Primary.Attributes + _, err := config.clientContainer.Projects.Zones.Clusters.Get( + config.Project, attributes["zone"], attributes["name"]).Do() + if err == nil { + return fmt.Errorf("Cluster still exists") + } + } + + return nil +} + +func testAccCheckContainerClusterExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + attributes := rs.Primary.Attributes + found, err := config.clientContainer.Projects.Zones.Clusters.Get( + config.Project, attributes["zone"], attributes["name"]).Do() + if err != nil { + return err + } + + if found.Name != attributes["name"] { + return fmt.Errorf("Cluster not found") + } + + return nil + } +} + +const testAccContainerCluster_basic = ` +resource "google_container_cluster" "primary" { + name = "terraform-foo-bar-test" + zone = "us-central1-a" + initial_node_count = 3 + + master_auth { + username = "mr.yoda" + password = "adoy.rm" + } +}` From 6ffbac43d7fea9b47ce2da62841a66b37719d5f3 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Thu, 16 Jul 2015 18:39:25 +0200 Subject: [PATCH 8/8] Fixing the build... The v0.beta is removed, so I also removed it from here. Strangely enough I cannot find any code that actually used it other then in being instantiated in the provider config func. --- config.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/config.go b/config.go index 905e56d4..8803868f 100644 --- a/config.go +++ b/config.go @@ -13,7 +13,6 @@ import ( "golang.org/x/oauth2" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" - computeBeta "google.golang.org/api/compute/v0.beta" "google.golang.org/api/compute/v1" "google.golang.org/api/container/v1" "google.golang.org/api/dns/v1" @@ -27,11 +26,10 @@ type Config struct { Project string Region string - clientCompute *compute.Service - clientComputeBeta *computeBeta.Service - clientContainer *container.Service - clientDns *dns.Service - clientStorage *storage.Service + clientCompute *compute.Service + clientContainer *container.Service + clientDns *dns.Service + clientStorage *storage.Service } func (c *Config) loadAndValidate() error { @@ -115,13 +113,6 @@ func (c *Config) loadAndValidate() error { } c.clientCompute.UserAgent = userAgent - log.Printf("[INFO] Instantiating Beta GCE client...") - c.clientComputeBeta, err = computeBeta.New(client) - if err != nil { - return err - } - c.clientComputeBeta.UserAgent = userAgent - log.Printf("[INFO] Instantiating GKE client...") c.clientContainer, err = container.New(client) if err != nil {