Make google_storage_default_object_acl authoritative. (#2345)

<!-- This change is generated by MagicModules. -->
/cc @rileykarson
This commit is contained in:
The Magician 2018-10-26 16:01:39 -07:00 committed by Nathan McKinley
parent 12369626c8
commit bf296e55bf
3 changed files with 98 additions and 135 deletions

View File

@ -2,61 +2,69 @@ package google
import ( import (
"fmt" "fmt"
"log"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/storage/v1" "google.golang.org/api/storage/v1"
) )
func resourceStorageDefaultObjectAcl() *schema.Resource { func resourceStorageDefaultObjectAcl() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceStorageDefaultObjectAclCreate, Create: resourceStorageDefaultObjectAclCreateUpdate,
Read: resourceStorageDefaultObjectAclRead, Read: resourceStorageDefaultObjectAclRead,
Update: resourceStorageDefaultObjectAclUpdate, Update: resourceStorageDefaultObjectAclCreateUpdate,
Delete: resourceStorageDefaultObjectAclDelete, Delete: resourceStorageDefaultObjectAclDelete,
CustomizeDiff: resourceStorageRoleEntityCustomizeDiff,
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"bucket": &schema.Schema{ "bucket": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true, ForceNew: true,
}, },
"role_entity": &schema.Schema{ "role_entity": {
Type: schema.TypeList, Type: schema.TypeSet,
Optional: true, Optional: true,
Computed: true, Computed: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{
MinItems: 1, Type: schema.TypeString,
ValidateFunc: validateRoleEntityPair,
},
}, },
}, },
} }
} }
func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interface{}) error { func resourceStorageDefaultObjectAclCreateUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
bucket := d.Get("bucket").(string) bucket := d.Get("bucket").(string)
roleEntity := d.Get("role_entity").([]interface{}) defaultObjectAcl := []*storage.ObjectAccessControl{}
for _, v := range d.Get("role_entity").(*schema.Set).List() {
for _, v := range roleEntity { pair := getValidatedRoleEntityPair(v.(string))
pair, err := getRoleEntityPair(v.(string)) defaultObjectAcl = append(defaultObjectAcl, &storage.ObjectAccessControl{
ObjectAccessControl := &storage.ObjectAccessControl{
Role: pair.Role, Role: pair.Role,
Entity: pair.Entity, Entity: pair.Entity,
} })
}
log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket %s", pair.Role, pair.Entity, bucket) res, err := config.clientStorage.Buckets.Get(bucket).Do()
if err != nil {
_, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do() return fmt.Errorf("Error reading bucket %s: %v", bucket, err)
}
// Even with ForceSendFields the empty array wasn't working. Luckily, this is the same thing
if len(defaultObjectAcl) == 0 {
_, err = config.clientStorage.Buckets.Update(bucket, res).IfMetagenerationMatch(res.Metageneration).PredefinedDefaultObjectAcl("private").Do()
if err != nil { if err != nil {
return fmt.Errorf("Error setting Default Object ACL for %s on bucket %s: %v", pair.Entity, bucket, err) return fmt.Errorf("Error updating default object acl to empty for bucket %s: %v", bucket, err)
}
} else {
res.DefaultObjectAcl = defaultObjectAcl
_, err = config.clientStorage.Buckets.Update(bucket, res).IfMetagenerationMatch(res.Metageneration).Do()
if err != nil {
return fmt.Errorf("Error updating default object acl for bucket %s: %v", bucket, err)
} }
} }
d.SetId(bucket)
return resourceStorageDefaultObjectAclRead(d, meta) return resourceStorageDefaultObjectAclRead(d, meta)
} }
@ -64,123 +72,39 @@ func resourceStorageDefaultObjectAclRead(d *schema.ResourceData, meta interface{
config := meta.(*Config) config := meta.(*Config)
bucket := d.Get("bucket").(string) bucket := d.Get("bucket").(string)
res, err := config.clientStorage.Buckets.Get(bucket).Projection("full").Do()
roleEntities := make([]interface{}, 0)
reLocal := d.Get("role_entity").([]interface{})
reLocalMap := make(map[string]string)
for _, v := range reLocal {
res, err := getRoleEntityPair(v.(string))
if err != nil {
return fmt.Errorf(
"Old state has malformed Role/Entity pair: %v", err)
}
reLocalMap[res.Entity] = res.Role
}
res, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do()
if err != nil { if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Storage Default Object ACL for bucket %q", d.Get("bucket").(string))) return handleNotFoundError(err, d, fmt.Sprintf("Default Storage Object ACL for Bucket %q", d.Get("bucket").(string)))
} }
for _, v := range res.Items { var roleEntities []string
role := v.Role for _, roleEntity := range res.DefaultObjectAcl {
entity := v.Entity role := roleEntity.Role
// We only store updates to the locally defined access controls entity := roleEntity.Entity
if _, in := reLocalMap[entity]; in { roleEntities = append(roleEntities, fmt.Sprintf("%s:%s", role, entity))
roleEntities = append(roleEntities, fmt.Sprintf("%s:%s", role, entity))
log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity)
}
} }
d.Set("role_entity", roleEntities) err = d.Set("role_entity", roleEntities)
if err != nil {
return err
}
d.SetId(bucket)
return nil return nil
} }
func resourceStorageDefaultObjectAclUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
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))
if err != nil {
return fmt.Errorf(
"Old state has malformed Role/Entity pair: %v", err)
}
oldReMap[res.Entity] = res.Role
}
for _, v := range newRe {
pair, err := getRoleEntityPair(v.(string))
ObjectAccessControl := &storage.ObjectAccessControl{
Role: pair.Role,
Entity: pair.Entity,
}
// 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()
}
// 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)
}
}
for entity := range oldReMap {
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)
}
func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interface{}) error { func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
bucket := d.Get("bucket").(string) bucket := d.Get("bucket").(string)
res, err := config.clientStorage.Buckets.Get(bucket).Do()
if err != nil {
return fmt.Errorf("Error reading bucket %s: %v", bucket, err)
}
reLocal := d.Get("role_entity").([]interface{}) _, err = config.clientStorage.Buckets.Update(bucket, res).IfMetagenerationMatch(res.Metageneration).PredefinedDefaultObjectAcl("private").Do()
for _, v := range reLocal { if err != nil {
res, err := getRoleEntityPair(v.(string)) return fmt.Errorf("Error deleting (updating to private) default object acl for bucket %s: %v", bucket, err)
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 return nil

View File

@ -12,7 +12,6 @@ func TestAccStorageDefaultObjectAcl_basic(t *testing.T) {
t.Parallel() t.Parallel()
bucketName := testBucketName() bucketName := testBucketName()
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders, Providers: testAccProviders,
@ -29,6 +28,22 @@ func TestAccStorageDefaultObjectAcl_basic(t *testing.T) {
}) })
} }
func TestAccStorageDefaultObjectAcl_noRoleEntity(t *testing.T) {
t.Parallel()
bucketName := testBucketName()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccStorageDefaultObjectAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageDefaultObjectsAclNoRoleEntity(bucketName),
},
},
})
}
func TestAccStorageDefaultObjectAcl_upgrade(t *testing.T) { func TestAccStorageDefaultObjectAcl_upgrade(t *testing.T) {
t.Parallel() t.Parallel()
@ -178,6 +193,19 @@ func testAccCheckGoogleStorageDefaultObjectAclDelete(bucket, roleEntityS string)
} }
} }
func testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntity1, roleEntity2 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, roleEntity1, roleEntity2)
}
func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntity string) string { func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntity string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "google_storage_bucket" "bucket" { resource "google_storage_bucket" "bucket" {
@ -191,7 +219,7 @@ resource "google_storage_default_object_acl" "acl" {
`, bucketName, roleEntity) `, bucketName, roleEntity)
} }
func testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntity1, roleEntity2 string) string { func testGoogleStorageDefaultObjectsAclNoRoleEntity(bucketName string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "google_storage_bucket" "bucket" { resource "google_storage_bucket" "bucket" {
name = "%s" name = "%s"
@ -199,9 +227,9 @@ resource "google_storage_bucket" "bucket" {
resource "google_storage_default_object_acl" "acl" { resource "google_storage_default_object_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}" bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"] role_entity = []
} }
`, bucketName, roleEntity1, roleEntity2) `, bucketName)
} }
func testGoogleStorageDefaultObjectAclUnordered(bucketName string) string { func testGoogleStorageDefaultObjectAclUnordered(bucketName string) string {

View File

@ -3,12 +3,19 @@ layout: "google"
page_title: "Google: google_storage_default_object_acl" 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: |- description: |-
Creates a new default object ACL in Google Cloud Storage. Authoritatively manages the default object ACLs for a Google Cloud Storage bucket
--- ---
# google\_storage\_default\_object\_acl # google\_storage\_default\_object\_acl
Creates a new default object ACL in Google Cloud Storage service (GCS). For more information see Authoritatively manages the default object ACLs for a Google Cloud Storage bucket
without managing the bucket itself.
-> Note that for each object, its creator will have the `"OWNER"` role in addition
to the default ACL that has been defined.
For more information see
[the official documentation](https://cloud.google.com/storage/docs/access-control/lists) [the official documentation](https://cloud.google.com/storage/docs/access-control/lists)
and and
[API](https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls). [API](https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls).
@ -36,7 +43,11 @@ resource "google_storage_default_object_acl" "image-store-default-acl" {
* `bucket` - (Required) The name of the bucket it applies to. * `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. ---
* `role_entity` - (Optional) 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.
Omitting the field is the same as providing an empty list.
## Attributes Reference ## Attributes Reference