From ba3c60c6f377fe8297a5d6bc19c6ca79c211c05d Mon Sep 17 00:00:00 2001 From: Irena Shashchuk Date: Thu, 18 Jan 2018 14:43:01 -0500 Subject: [PATCH 1/6] Storage Default Object ACL resource --- google/provider.go | 1 + google/resource_storage_default_object_acl.go | 199 ++++++++++++++++ ...esource_storage_default_object_acl_test.go | 213 ++++++++++++++++++ .../storage_default_object_acl.html.markdown | 43 ++++ 4 files changed, 456 insertions(+) create mode 100644 google/resource_storage_default_object_acl.go create mode 100644 google/resource_storage_default_object_acl_test.go create mode 100644 website/docs/r/storage_default_object_acl.html.markdown diff --git a/google/provider.go b/google/provider.go index 8c9693257e6..d045f71c4ee 100644 --- a/google/provider.go +++ b/google/provider.go @@ -155,6 +155,7 @@ func Provider() terraform.ResourceProvider { "google_storage_bucket_acl": resourceStorageBucketAcl(), "google_storage_bucket_object": resourceStorageBucketObject(), "google_storage_object_acl": resourceStorageObjectAcl(), + "google_storage_default_object_acl": resourceStorageDefaultObjectAcl(), }, ConfigureFunc: providerConfigure, diff --git a/google/resource_storage_default_object_acl.go b/google/resource_storage_default_object_acl.go new file mode 100644 index 00000000000..4e214b3ce6f --- /dev/null +++ b/google/resource_storage_default_object_acl.go @@ -0,0 +1,199 @@ +package google + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/storage/v1" +) + +func resourceStorageDefaultObjectAcl() *schema.Resource { + return &schema.Resource{ + Create: resourceStorageDefaultObjectAclCreate, + Read: resourceStorageDefaultObjectAclRead, + Update: resourceStorageDefaultObjectAclUpdate, + Delete: resourceStorageDefaultObjectAclDelete, + + Schema: map[string]*schema.Schema{ + "bucket": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "role_entity": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func getDefaultObjectAclId(bucket string) string { + return bucket + "-default-acl" +} + +func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + bucket := d.Get("bucket").(string) + role_entity := make([]interface{}, 0) + + if v, ok := d.GetOk("role_entity"); ok { + role_entity = v.([]interface{}) + } + + if len(role_entity) > 0 { + for _, v := range role_entity { + pair, err := getRoleEntityPair(v.(string)) + + ObjectAccessControl := &storage.ObjectAccessControl{ + Role: pair.Role, + Entity: pair.Entity, + } + + log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket", pair.Role, pair.Entity, bucket) + + _, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() + + if err != nil { + return fmt.Errorf("Error setting Default Object ACL for bucket %s: %v", bucket, err) + } + } + + return resourceStorageDefaultObjectAclRead(d, meta) + } + return nil +} + +func resourceStorageDefaultObjectAclRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + bucket := d.Get("bucket").(string) + + if _, ok := d.GetOk("role_entity"); ok { + role_entity := make([]interface{}, 0) + re_local := d.Get("role_entity").([]interface{}) + re_local_map := make(map[string]string) + for _, v := range re_local { + res, err := getRoleEntityPair(v.(string)) + + if err != nil { + return fmt.Errorf( + "Old state has malformed Role/Entity pair: %v", err) + } + + re_local_map[res.Entity] = res.Role + } + + res, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do() + + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("Storage Default Object ACL for bucket %q", d.Get("bucket").(string))) + } + + for _, v := range res.Items { + role := v.Role + entity := v.Entity + // We only store updates to the locally defined access controls + if _, in := re_local_map[entity]; in { + role_entity = append(role_entity, fmt.Sprintf("%s:%s", role, entity)) + log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity) + } + } + + d.Set("role_entity", role_entity) + } + + d.SetId(getDefaultObjectAclId(bucket)) + return nil +} + +func resourceStorageDefaultObjectAclUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + bucket := d.Get("bucket").(string) + + if d.HasChange("role_entity") { + o, n := d.GetChange("role_entity") + old_re := o.([]interface{}) + new_re := n.([]interface{}) + + old_re_map := make(map[string]string) + for _, v := range old_re { + res, err := getRoleEntityPair(v.(string)) + + if err != nil { + return fmt.Errorf( + "Old state has malformed Role/Entity pair: %v", err) + } + + old_re_map[res.Entity] = res.Role + } + + for _, v := range new_re { + pair, err := getRoleEntityPair(v.(string)) + + ObjectAccessControl := &storage.ObjectAccessControl{ + Role: pair.Role, + Entity: pair.Entity, + } + + // If the old state is missing for this entity, it needs to + // be created. Otherwise it is updated + if _, ok := old_re_map[pair.Entity]; ok { + _, err = config.clientStorage.DefaultObjectAccessControls.Update( + bucket, pair.Entity, ObjectAccessControl).Do() + } else { + _, err = config.clientStorage.DefaultObjectAccessControls.Insert( + bucket, ObjectAccessControl).Do() + } + + // Now we only store the keys that have to be removed + delete(old_re_map, pair.Entity) + + if err != nil { + return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) + } + } + + for entity, _ := range old_re_map { + log.Printf("[DEBUG]: removing entity %s", entity) + err := config.clientStorage.DefaultObjectAccessControls.Delete(bucket, entity).Do() + + if err != nil { + return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) + } + } + + return resourceStorageDefaultObjectAclRead(d, meta) + } + + return nil +} + +func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + bucket := d.Get("bucket").(string) + + re_local := d.Get("role_entity").([]interface{}) + for _, v := range re_local { + res, err := getRoleEntityPair(v.(string)) + if err != nil { + return err + } + + log.Printf("[DEBUG]: removing entity %s", res.Entity) + + err = config.clientStorage.DefaultObjectAccessControls.Delete(bucket, res.Entity).Do() + + if err != nil { + return fmt.Errorf("Error deleting entity %s ACL: %s", res.Entity, err) + } + } + + return nil +} diff --git a/google/resource_storage_default_object_acl_test.go b/google/resource_storage_default_object_acl_test.go new file mode 100644 index 00000000000..5af8e5cc479 --- /dev/null +++ b/google/resource_storage_default_object_acl_test.go @@ -0,0 +1,213 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccGoogleStorageDefaultObjectAcl_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic1(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + ), + }, + }, + }) +} + +func TestAccGoogleStorageDefaultObjectAcl_upgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic1(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic2(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +func TestAccGoogleStorageDefaultObjectAcl_downgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic2(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic3(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_reader), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +func testAccCheckGoogleStorageDefaultObjectAcl(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + res, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket, + roleEntity.Entity).Do() + + if err != nil { + return fmt.Errorf("Error retrieving contents of storage default Acl for bucket %s: %s", bucket, err) + } + + if res.Role != roleEntity.Role { + return fmt.Errorf("Error, Role mismatch %s != %s", res.Role, roleEntity.Role) + } + + return nil + } +} + +func testAccGoogleStorageDefaultObjectAclDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + + if rs.Type != "google_storage_default_object_acl" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + + _, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do() + if err == nil { + return fmt.Errorf("Default Storage Object Acl for bucket %s still exists", bucket) + } + } + return nil +} + +func testAccCheckGoogleStorageDefaultObjectAclDelete(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket, roleEntity.Entity).Do() + + if err != nil { + return nil + } + + return fmt.Errorf("Error, Object Default Acl Entity still exists %s for bucket %s", + roleEntity.Entity, bucket) + } +} + +func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = [] +} +`, bucketName) +} + +func testGoogleStorageDefaultObjectsAclBasic1(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, roleEntityBasic1, roleEntityBasic2) +} + +func testGoogleStorageDefaultObjectsAclBasic2(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, roleEntityBasic2, roleEntityBasic3_owner) +} + +func testGoogleStorageDefaultObjectsAclBasic3(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, roleEntityBasic2, roleEntityBasic3_reader) +} diff --git a/website/docs/r/storage_default_object_acl.html.markdown b/website/docs/r/storage_default_object_acl.html.markdown new file mode 100644 index 00000000000..d40f4b0d292 --- /dev/null +++ b/website/docs/r/storage_default_object_acl.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "google" +page_title: "Google: google_storage_default_object_acl" +sidebar_current: "docs-google-storage-default_object-acl" +description: |- + Creates a new default object ACL in Google Cloud Storage. +--- + +# google\_storage\_object\_acl + +Creates a new default object ACL in Google cloud storage service (GCS). For more information see +[the official documentation](https://cloud.google.com/storage/docs/access-control/lists) +and +[API](https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls). + +## Example Usage + +Example creating a default object ACL on a bucket with one owner, and one reader. + +```hcl +resource "google_storage_bucket" "image-store" { + name = "image-store-bucket" + location = "EU" +} + +resource "google_storage_default_object_acl" "image-store-default-acl" { + bucket = "${google_storage_bucket.image-store.name}" + role_entity = [ + "OWNER:user-my.email@gmail.com", + "READER:group-mygroup", + ] +} +``` + +## Argument Reference + +* `bucket` - (Required) The name of the bucket it applies to. + +* `role_entity` - (Required) List of role/entity pairs in the form `ROLE:entity`. See [GCS Object ACL documentation](https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls) for more details. + +## Attributes Reference + +Only the arguments listed above are exposed as attributes. From 19912ecf334e2d5590dfa80c12d98071dec19999 Mon Sep 17 00:00:00 2001 From: Irena Shashchuk Date: Fri, 19 Jan 2018 10:49:42 -0500 Subject: [PATCH 2/6] Fixed the doc --- website/docs/r/storage_default_object_acl.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/storage_default_object_acl.html.markdown b/website/docs/r/storage_default_object_acl.html.markdown index d40f4b0d292..6dbd25c5843 100644 --- a/website/docs/r/storage_default_object_acl.html.markdown +++ b/website/docs/r/storage_default_object_acl.html.markdown @@ -6,7 +6,7 @@ description: |- Creates a new default object ACL in Google Cloud Storage. --- -# google\_storage\_object\_acl +# google\_storage\_default\_object\_acl Creates a new default object ACL in Google cloud storage service (GCS). For more information see [the official documentation](https://cloud.google.com/storage/docs/access-control/lists) From 6810e53b8997f696e509fbbcb9da60d57c02b321 Mon Sep 17 00:00:00 2001 From: Irena Shashchuk Date: Fri, 19 Jan 2018 16:28:50 -0500 Subject: [PATCH 3/6] Renamed the resource id. Log change --- google/resource_storage_default_object_acl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/resource_storage_default_object_acl.go b/google/resource_storage_default_object_acl.go index 4e214b3ce6f..1dda438953f 100644 --- a/google/resource_storage_default_object_acl.go +++ b/google/resource_storage_default_object_acl.go @@ -32,7 +32,7 @@ func resourceStorageDefaultObjectAcl() *schema.Resource { } func getDefaultObjectAclId(bucket string) string { - return bucket + "-default-acl" + return bucket + "-default-object-acl" } func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interface{}) error { @@ -59,7 +59,7 @@ func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interfac _, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() if err != nil { - return fmt.Errorf("Error setting Default Object ACL for bucket %s: %v", bucket, err) + return fmt.Errorf("Error setting Default Object ACL for %s on bucket %s: %v", pair.Entity, bucket, err) } } From e7cebf76c65c109d7502a5f3f95c1a96e1a0a1b4 Mon Sep 17 00:00:00 2001 From: Irena Shashchuk Date: Mon, 22 Jan 2018 11:40:22 -0500 Subject: [PATCH 4/6] Complying with go vet --- google/resource_storage_default_object_acl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/resource_storage_default_object_acl.go b/google/resource_storage_default_object_acl.go index 1dda438953f..7bcd3389271 100644 --- a/google/resource_storage_default_object_acl.go +++ b/google/resource_storage_default_object_acl.go @@ -54,7 +54,7 @@ func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interfac Entity: pair.Entity, } - log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket", pair.Role, pair.Entity, bucket) + log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket %s", pair.Role, pair.Entity, bucket) _, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() From 5e3538153ead3d43a170af4a8c8a9c8670cf9ce6 Mon Sep 17 00:00:00 2001 From: Irena Shashchuk Date: Thu, 25 Jan 2018 10:28:03 -0500 Subject: [PATCH 5/6] Changes for review --- google/resource_storage_default_object_acl.go | 180 ++++++++---------- ...esource_storage_default_object_acl_test.go | 54 ++---- .../storage_default_object_acl.html.markdown | 2 +- 3 files changed, 98 insertions(+), 138 deletions(-) diff --git a/google/resource_storage_default_object_acl.go b/google/resource_storage_default_object_acl.go index 7bcd3389271..8e86a4aea94 100644 --- a/google/resource_storage_default_object_acl.go +++ b/google/resource_storage_default_object_acl.go @@ -26,46 +26,36 @@ func resourceStorageDefaultObjectAcl() *schema.Resource { Type: schema.TypeList, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, }, }, } } -func getDefaultObjectAclId(bucket string) string { - return bucket + "-default-object-acl" -} - func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) bucket := d.Get("bucket").(string) - role_entity := make([]interface{}, 0) - - if v, ok := d.GetOk("role_entity"); ok { - role_entity = v.([]interface{}) - } + roleEntity := d.Get("role_entity").([]interface{}) - if len(role_entity) > 0 { - for _, v := range role_entity { - pair, err := getRoleEntityPair(v.(string)) + for _, v := range roleEntity { + pair, err := getRoleEntityPair(v.(string)) - ObjectAccessControl := &storage.ObjectAccessControl{ - Role: pair.Role, - Entity: pair.Entity, - } + ObjectAccessControl := &storage.ObjectAccessControl{ + Role: pair.Role, + Entity: pair.Entity, + } - log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket %s", pair.Role, pair.Entity, bucket) + log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket %s", pair.Role, pair.Entity, bucket) - _, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() + _, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() - if err != nil { - return fmt.Errorf("Error setting Default Object ACL for %s on bucket %s: %v", pair.Entity, bucket, err) - } + if err != nil { + return fmt.Errorf("Error setting Default Object ACL for %s on bucket %s: %v", pair.Entity, bucket, err) } - - return resourceStorageDefaultObjectAclRead(d, meta) } - return nil + d.SetId(bucket) + return resourceStorageDefaultObjectAclRead(d, meta) } func resourceStorageDefaultObjectAclRead(d *schema.ResourceData, meta interface{}) error { @@ -73,41 +63,38 @@ func resourceStorageDefaultObjectAclRead(d *schema.ResourceData, meta interface{ bucket := d.Get("bucket").(string) - if _, ok := d.GetOk("role_entity"); ok { - role_entity := make([]interface{}, 0) - re_local := d.Get("role_entity").([]interface{}) - re_local_map := make(map[string]string) - for _, v := range re_local { - res, err := getRoleEntityPair(v.(string)) - - if err != nil { - return fmt.Errorf( - "Old state has malformed Role/Entity pair: %v", err) - } + roleEntities := make([]interface{}, 0) + reLocal := d.Get("role_entity").([]interface{}) + reLocalMap := make(map[string]string) + for _, v := range reLocal { + res, err := getRoleEntityPair(v.(string)) - re_local_map[res.Entity] = res.Role + if err != nil { + return fmt.Errorf( + "Old state has malformed Role/Entity pair: %v", err) } - res, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do() + reLocalMap[res.Entity] = res.Role + } - if err != nil { - return handleNotFoundError(err, d, fmt.Sprintf("Storage Default Object ACL for bucket %q", d.Get("bucket").(string))) - } + res, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do() - for _, v := range res.Items { - role := v.Role - entity := v.Entity - // We only store updates to the locally defined access controls - if _, in := re_local_map[entity]; in { - role_entity = append(role_entity, fmt.Sprintf("%s:%s", role, entity)) - log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity) - } - } + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("Storage Default Object ACL for bucket %q", d.Get("bucket").(string))) + } - d.Set("role_entity", role_entity) + for _, v := range res.Items { + role := v.Role + entity := v.Entity + // We only store updates to the locally defined access controls + if _, in := reLocalMap[entity]; in { + roleEntities = append(roleEntities, fmt.Sprintf("%s:%s", role, entity)) + log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity) + } } - d.SetId(getDefaultObjectAclId(bucket)) + d.Set("role_entity", roleEntities) + return nil } @@ -116,62 +103,61 @@ func resourceStorageDefaultObjectAclUpdate(d *schema.ResourceData, meta interfac bucket := d.Get("bucket").(string) - if d.HasChange("role_entity") { - o, n := d.GetChange("role_entity") - old_re := o.([]interface{}) - new_re := n.([]interface{}) + if !d.HasChange("role_entity") { + return nil + } + o, n := d.GetChange("role_entity") + oldRe := o.([]interface{}) + newRe := n.([]interface{}) + + oldReMap := make(map[string]string) + for _, v := range oldRe { + res, err := getRoleEntityPair(v.(string)) - old_re_map := make(map[string]string) - for _, v := range old_re { - res, err := getRoleEntityPair(v.(string)) + if err != nil { + return fmt.Errorf( + "Old state has malformed Role/Entity pair: %v", err) + } - if err != nil { - return fmt.Errorf( - "Old state has malformed Role/Entity pair: %v", err) - } + oldReMap[res.Entity] = res.Role + } - old_re_map[res.Entity] = res.Role + for _, v := range newRe { + pair, err := getRoleEntityPair(v.(string)) + + ObjectAccessControl := &storage.ObjectAccessControl{ + Role: pair.Role, + Entity: pair.Entity, } - for _, v := range new_re { - pair, err := getRoleEntityPair(v.(string)) - - ObjectAccessControl := &storage.ObjectAccessControl{ - Role: pair.Role, - Entity: pair.Entity, - } - - // If the old state is missing for this entity, it needs to - // be created. Otherwise it is updated - if _, ok := old_re_map[pair.Entity]; ok { - _, err = config.clientStorage.DefaultObjectAccessControls.Update( - bucket, pair.Entity, ObjectAccessControl).Do() - } else { - _, err = config.clientStorage.DefaultObjectAccessControls.Insert( - bucket, ObjectAccessControl).Do() - } - - // Now we only store the keys that have to be removed - delete(old_re_map, pair.Entity) - - if err != nil { - return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) - } + // If the old state is present for the entity, it is updated + // If the old state is missing, it is inserted + if _, ok := oldReMap[pair.Entity]; ok { + _, err = config.clientStorage.DefaultObjectAccessControls.Update( + bucket, pair.Entity, ObjectAccessControl).Do() + } else { + _, err = config.clientStorage.DefaultObjectAccessControls.Insert( + bucket, ObjectAccessControl).Do() } - for entity, _ := range old_re_map { - log.Printf("[DEBUG]: removing entity %s", entity) - err := config.clientStorage.DefaultObjectAccessControls.Delete(bucket, entity).Do() + // Now we only store the keys that have to be removed + delete(oldReMap, pair.Entity) - if err != nil { - return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) - } + if err != nil { + return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) } + } + + for entity := range oldReMap { + log.Printf("[DEBUG]: removing entity %s", entity) + err := config.clientStorage.DefaultObjectAccessControls.Delete(bucket, entity).Do() - return resourceStorageDefaultObjectAclRead(d, meta) + if err != nil { + return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err) + } } - return nil + return resourceStorageDefaultObjectAclRead(d, meta) } func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interface{}) error { @@ -179,8 +165,8 @@ func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interfac bucket := d.Get("bucket").(string) - re_local := d.Get("role_entity").([]interface{}) - for _, v := range re_local { + reLocal := d.Get("role_entity").([]interface{}) + for _, v := range reLocal { res, err := getRoleEntityPair(v.(string)) if err != nil { return err diff --git a/google/resource_storage_default_object_acl_test.go b/google/resource_storage_default_object_acl_test.go index 5af8e5cc479..957c5395698 100644 --- a/google/resource_storage_default_object_acl_test.go +++ b/google/resource_storage_default_object_acl_test.go @@ -19,7 +19,7 @@ func TestAccGoogleStorageDefaultObjectAcl_basic(t *testing.T) { CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasic1(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2), Check: resource.ComposeTestCheckFunc( testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), @@ -40,7 +40,7 @@ func TestAccGoogleStorageDefaultObjectAcl_upgrade(t *testing.T) { CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasic1(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2), Check: resource.ComposeTestCheckFunc( testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), @@ -48,7 +48,7 @@ func TestAccGoogleStorageDefaultObjectAcl_upgrade(t *testing.T) { }, resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasic2(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner), Check: resource.ComposeTestCheckFunc( testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), @@ -56,9 +56,9 @@ func TestAccGoogleStorageDefaultObjectAcl_upgrade(t *testing.T) { }, resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1), Check: resource.ComposeTestCheckFunc( - testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), ), @@ -78,7 +78,7 @@ func TestAccGoogleStorageDefaultObjectAcl_downgrade(t *testing.T) { CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasic2(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner), Check: resource.ComposeTestCheckFunc( testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), @@ -86,7 +86,7 @@ func TestAccGoogleStorageDefaultObjectAcl_downgrade(t *testing.T) { }, resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasic3(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_reader), Check: resource.ComposeTestCheckFunc( testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_reader), @@ -94,9 +94,9 @@ func TestAccGoogleStorageDefaultObjectAcl_downgrade(t *testing.T) { }, resource.TestStep{ - Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName), + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1), Check: resource.ComposeTestCheckFunc( - testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), ), @@ -160,20 +160,7 @@ func testAccCheckGoogleStorageDefaultObjectAclDelete(bucket, roleEntityS string) } } -func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName string) string { - return fmt.Sprintf(` -resource "google_storage_bucket" "bucket" { - name = "%s" -} - -resource "google_storage_default_object_acl" "acl" { - bucket = "${google_storage_bucket.bucket.name}" - role_entity = [] -} -`, bucketName) -} - -func testGoogleStorageDefaultObjectsAclBasic1(bucketName string) string { +func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntity string) string { return fmt.Sprintf(` resource "google_storage_bucket" "bucket" { name = "%s" @@ -181,25 +168,12 @@ resource "google_storage_bucket" "bucket" { resource "google_storage_default_object_acl" "acl" { bucket = "${google_storage_bucket.bucket.name}" - role_entity = ["%s", "%s"] -} -`, bucketName, roleEntityBasic1, roleEntityBasic2) -} - -func testGoogleStorageDefaultObjectsAclBasic2(bucketName string) string { - return fmt.Sprintf(` -resource "google_storage_bucket" "bucket" { - name = "%s" -} - -resource "google_storage_default_object_acl" "acl" { - bucket = "${google_storage_bucket.bucket.name}" - role_entity = ["%s", "%s"] + role_entity = ["%s"] } -`, bucketName, roleEntityBasic2, roleEntityBasic3_owner) +`, bucketName, roleEntity) } -func testGoogleStorageDefaultObjectsAclBasic3(bucketName string) string { +func testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntity1, roleEntity2 string) string { return fmt.Sprintf(` resource "google_storage_bucket" "bucket" { name = "%s" @@ -209,5 +183,5 @@ resource "google_storage_default_object_acl" "acl" { bucket = "${google_storage_bucket.bucket.name}" role_entity = ["%s", "%s"] } -`, bucketName, roleEntityBasic2, roleEntityBasic3_reader) +`, bucketName, roleEntity1, roleEntity2) } diff --git a/website/docs/r/storage_default_object_acl.html.markdown b/website/docs/r/storage_default_object_acl.html.markdown index 6dbd25c5843..f778c2e3b97 100644 --- a/website/docs/r/storage_default_object_acl.html.markdown +++ b/website/docs/r/storage_default_object_acl.html.markdown @@ -8,7 +8,7 @@ description: |- # google\_storage\_default\_object\_acl -Creates a new default object ACL in Google cloud storage service (GCS). For more information see +Creates a new default object ACL in Google Cloud Storage service (GCS). For more information see [the official documentation](https://cloud.google.com/storage/docs/access-control/lists) and [API](https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls). From a89bc5d83cd8e8230bf5816fd7068d85154f5fea Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 25 Jan 2018 11:25:07 -0800 Subject: [PATCH 6/6] link to default object acl docs in sidebar --- website/docs/r/storage_default_object_acl.html.markdown | 2 +- website/google.erb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/website/docs/r/storage_default_object_acl.html.markdown b/website/docs/r/storage_default_object_acl.html.markdown index f778c2e3b97..96f05e616d1 100644 --- a/website/docs/r/storage_default_object_acl.html.markdown +++ b/website/docs/r/storage_default_object_acl.html.markdown @@ -1,7 +1,7 @@ --- layout: "google" page_title: "Google: google_storage_default_object_acl" -sidebar_current: "docs-google-storage-default_object-acl" +sidebar_current: "docs-google-storage-default-object-acl" description: |- Creates a new default object ACL in Google Cloud Storage. --- diff --git a/website/google.erb b/website/google.erb index 3a28109be3f..99590557069 100644 --- a/website/google.erb +++ b/website/google.erb @@ -522,6 +522,10 @@ google_storage_bucket_object + > + google_storage_default_object_acl + + > google_storage_object_acl