Implemented bucket & object ACLs, as well as documentation and tests

This commit is contained in:
Lars Wander 2015-09-16 14:46:46 -04:00
parent 6bd40a7bf4
commit d15acb042c
12 changed files with 1198 additions and 15 deletions

View File

@ -54,7 +54,9 @@ func Provider() terraform.ResourceProvider {
"google_dns_record_set": resourceDnsRecordSet(),
"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
"google_storage_bucket": resourceStorageBucket(),
"google_storage_bucket_acl": resourceStorageBucketAcl(),
"google_storage_bucket_object": resourceStorageBucketObject(),
"google_storage_object_acl": resourceStorageObjectAcl(),
},
ConfigureFunc: providerConfigure,

View File

@ -24,10 +24,10 @@ func resourceStorageBucket() *schema.Resource {
ForceNew: true,
},
"predefined_acl": &schema.Schema{
Type: schema.TypeString,
Default: "projectPrivate",
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Deprecated: "Please use resource \"storage_bucket_acl.predefined_acl\" instead.",
Optional: true,
ForceNew: true,
},
"location": &schema.Schema{
Type: schema.TypeString,
@ -69,7 +69,6 @@ func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error
// 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.
@ -95,7 +94,12 @@ func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error
}
}
res, err := config.clientStorage.Buckets.Insert(config.Project, sb).PredefinedAcl(acl).Do()
call := config.clientStorage.Buckets.Insert(config.Project, sb)
if v, ok := d.GetOk("predefined_acl"); ok {
call = call.PredefinedAcl(v.(string))
}
res, err := call.Do()
if err != nil {
fmt.Printf("Error creating bucket %s: %v", bucket, err)

View File

@ -0,0 +1,292 @@
package google
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/storage/v1"
)
func resourceStorageBucketAcl() *schema.Resource {
return &schema.Resource{
Create: resourceStorageBucketAclCreate,
Read: resourceStorageBucketAclRead,
Update: resourceStorageBucketAclUpdate,
Delete: resourceStorageBucketAclDelete,
Schema: map[string]*schema.Schema{
"bucket": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"predefined_acl": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"role_entity": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"default_acl": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
}
}
type RoleEntity struct {
Role string
Entity string
}
func getBucketAclId(bucket string) string {
return bucket + "-acl"
}
func getRoleEntityPair(role_entity string) (*RoleEntity, error) {
split := strings.Split(role_entity, ":")
if len(split) != 2 {
return nil, fmt.Errorf("Error, each role entity pair must be " +
"formatted as ROLE:entity")
}
return &RoleEntity{Role: split[0], Entity: split[1]}, nil
}
func resourceStorageBucketAclCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
predefined_acl := ""
default_acl := ""
role_entity := make([]interface{}, 0)
if v, ok := d.GetOk("predefined_acl"); ok {
predefined_acl = v.(string)
}
if v, ok := d.GetOk("role_entity"); ok {
role_entity = v.([]interface{})
}
if v, ok := d.GetOk("default_acl"); ok {
default_acl = v.(string)
}
if len(predefined_acl) > 0 {
if len(role_entity) > 0 {
return fmt.Errorf("Error, you cannot specify both " +
"\"predefined_acl\" and \"role_entity\"");
}
res, err := config.clientStorage.Buckets.Get(bucket).Do()
if err != nil {
return fmt.Errorf("Error reading bucket %s: %v", bucket, err)
}
res, err = config.clientStorage.Buckets.Update(bucket,
res).PredefinedAcl(predefined_acl).Do()
if err != nil {
return fmt.Errorf("Error updating bucket %s: %v", bucket, err)
}
return resourceStorageBucketAclRead(d, meta);
} else if len(role_entity) > 0 {
for _, v := range(role_entity) {
pair, err := getRoleEntityPair(v.(string))
bucketAccessControl := &storage.BucketAccessControl{
Role: pair.Role,
Entity: pair.Entity,
}
log.Printf("[DEBUG]: storing re %s-%s", pair.Role, pair.Entity)
_, err = config.clientStorage.BucketAccessControls.Insert(bucket, bucketAccessControl).Do()
if err != nil {
return fmt.Errorf("Error updating ACL for bucket %s: %v", bucket, err)
}
}
return resourceStorageBucketAclRead(d, meta);
}
if len(default_acl) > 0 {
res, err := config.clientStorage.Buckets.Get(bucket).Do()
if err != nil {
return fmt.Errorf("Error reading bucket %s: %v", bucket, err)
}
res, err = config.clientStorage.Buckets.Update(bucket,
res).PredefinedDefaultObjectAcl(default_acl).Do()
if err != nil {
return fmt.Errorf("Error updating bucket %s: %v", bucket, err)
}
return resourceStorageBucketAclRead(d, meta);
}
return nil
}
func resourceStorageBucketAclRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
// Predefined ACLs cannot easily be parsed once they have been processed
// by the GCP server
if _, ok := d.GetOk("predefined_acl"); !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.BucketAccessControls.List(bucket).Do()
if err != nil {
return err
}
for _, v := range(res.Items) {
log.Printf("[DEBUG]: examining re %s-%s", v.Role, v.Entity)
// We only store updates to the locally defined access controls
if _, in := re_local_map[v.Entity]; in {
role_entity = append(role_entity, fmt.Sprintf("%s:%s", v.Role, v.Entity))
log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity)
}
}
d.Set("role_entity", role_entity)
}
d.SetId(getBucketAclId(bucket))
return nil
}
func resourceStorageBucketAclUpdate(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, new_re := o.([]interface{}), 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))
bucketAccessControl := &storage.BucketAccessControl{
Role: pair.Role,
Entity: pair.Entity,
}
// If the old state is missing this entity, it needs to
// be created. Otherwise it is updated
if _, ok := old_re_map[pair.Entity]; ok {
_, err = config.clientStorage.BucketAccessControls.Update(
bucket, pair.Entity, bucketAccessControl).Do()
} else {
_, err = config.clientStorage.BucketAccessControls.Insert(
bucket, bucketAccessControl).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 ACL for bucket %s: %v", bucket, err)
}
}
for entity, _ := range(old_re_map) {
log.Printf("[DEBUG]: removing entity %s", entity)
err := config.clientStorage.BucketAccessControls.Delete(bucket, entity).Do()
if err != nil {
return fmt.Errorf("Error updating ACL for bucket %s: %v", bucket, err)
}
}
return resourceStorageBucketAclRead(d, meta);
}
if d.HasChange("default_acl") {
default_acl := d.Get("default_acl").(string)
res, err := config.clientStorage.Buckets.Get(bucket).Do()
if err != nil {
return fmt.Errorf("Error reading bucket %s: %v", bucket, err)
}
res, err = config.clientStorage.Buckets.Update(bucket,
res).PredefinedDefaultObjectAcl(default_acl).Do()
if err != nil {
return fmt.Errorf("Error updating bucket %s: %v", bucket, err)
}
return resourceStorageBucketAclRead(d, meta);
}
return nil
}
func resourceStorageBucketAclDelete(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.BucketAccessControls.Delete(bucket, res.Entity).Do()
if err != nil {
return fmt.Errorf("Error deleting entity %s ACL: %s", res.Entity, err)
}
}
return nil
}

View File

@ -0,0 +1,232 @@
package google
import (
"fmt"
"testing"
"math/rand"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
//"google.golang.org/api/storage/v1"
)
var roleEntityBasic1 = "OWNER:user-omeemail@gmail.com"
var roleEntityBasic2 = "READER:user-anotheremail@gmail.com"
var roleEntityBasic3_owner = "OWNER:user-yetanotheremail@gmail.com"
var roleEntityBasic3_reader = "READER:user-yetanotheremail@gmail.com"
var testAclBucketName = fmt.Sprintf("%s-%d", "tf-test-acl-bucket", rand.New(rand.NewSource(time.Now().UnixNano())).Int())
func TestAccGoogleStorageBucketAcl_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageBucketAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageBucketsAclBasic1,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic1),
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic2),
),
},
},
})
}
func TestAccGoogleStorageBucketAcl_upgrade(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageBucketAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageBucketsAclBasic1,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic1),
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic2),
),
},
resource.TestStep{
Config: testGoogleStorageBucketsAclBasic2,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic2),
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic3_owner),
),
},
resource.TestStep{
Config: testGoogleStorageBucketsAclBasicDelete,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic1),
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic2),
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic3_owner),
),
},
},
})
}
func TestAccGoogleStorageBucketAcl_downgrade(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageBucketAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageBucketsAclBasic2,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic2),
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic3_owner),
),
},
resource.TestStep{
Config: testGoogleStorageBucketsAclBasic3,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic2),
testAccCheckGoogleStorageBucketAcl(testAclBucketName, roleEntityBasic3_reader),
),
},
resource.TestStep{
Config: testGoogleStorageBucketsAclBasicDelete,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic1),
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic2),
testAccCheckGoogleStorageBucketAclDelete(testAclBucketName, roleEntityBasic3_owner),
),
},
},
})
}
func TestAccGoogleStorageBucketAcl_predefined(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageBucketAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageBucketsAclPredefined,
},
},
})
}
func testAccCheckGoogleStorageBucketAclDelete(bucket, roleEntityS string) resource.TestCheckFunc {
return func(s *terraform.State) error {
roleEntity, _ := getRoleEntityPair(roleEntityS)
config := testAccProvider.Meta().(*Config)
_, err := config.clientStorage.BucketAccessControls.Get(bucket, roleEntity.Entity).Do()
if err != nil {
return nil
}
return fmt.Errorf("Error, entity %s still exists", roleEntity.Entity)
}
}
func testAccCheckGoogleStorageBucketAcl(bucket, roleEntityS string) resource.TestCheckFunc {
return func(s *terraform.State) error {
roleEntity, _ := getRoleEntityPair(roleEntityS)
config := testAccProvider.Meta().(*Config)
res, err := config.clientStorage.BucketAccessControls.Get(bucket, roleEntity.Entity).Do()
if err != nil {
return fmt.Errorf("Error retrieving contents of 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 testAccGoogleStorageBucketAclDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_storage_bucket_acl" {
continue
}
bucket := rs.Primary.Attributes["bucket"]
_, err := config.clientStorage.BucketAccessControls.List(bucket).Do()
if err == nil {
return fmt.Errorf("Acl for bucket %s still exists", bucket)
}
}
return nil
}
var testGoogleStorageBucketsAclBasic1 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, roleEntityBasic1, roleEntityBasic2)
var testGoogleStorageBucketsAclBasic2 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, roleEntityBasic2, roleEntityBasic3_owner)
var testGoogleStorageBucketsAclBasicDelete = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}"
role_entity = []
}
`, testAclBucketName)
var testGoogleStorageBucketsAclBasic3 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, roleEntityBasic2, roleEntityBasic3_reader)
var testGoogleStorageBucketsAclPredefined = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_acl" "acl" {
bucket = "${google_storage_bucket.bucket.name}"
predefined_acl = "projectPrivate"
default_acl = "projectPrivate"
}
`, testAclBucketName)

View File

@ -34,7 +34,7 @@ func resourceStorageBucketObject() *schema.Resource {
},
"predefined_acl": &schema.Schema{
Type: schema.TypeString,
Default: "projectPrivate",
Deprecated: "Please use resource \"storage_object_acl.predefined_acl\" instead.",
Optional: true,
ForceNew: true,
},
@ -60,7 +60,6 @@ func resourceStorageBucketObjectCreate(d *schema.ResourceData, meta interface{})
bucket := d.Get("bucket").(string)
name := d.Get("name").(string)
source := d.Get("source").(string)
acl := d.Get("predefined_acl").(string)
file, err := os.Open(source)
if err != nil {
@ -73,7 +72,10 @@ func resourceStorageBucketObjectCreate(d *schema.ResourceData, meta interface{})
insertCall := objectsService.Insert(bucket, object)
insertCall.Name(name)
insertCall.Media(file)
insertCall.PredefinedAcl(acl)
if v, ok := d.GetOk("predefined_acl"); ok {
insertCall.PredefinedAcl(v.(string))
}
_, err = insertCall.Do()

View File

@ -0,0 +1,254 @@
package google
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/storage/v1"
)
func resourceStorageObjectAcl() *schema.Resource {
return &schema.Resource{
Create: resourceStorageObjectAclCreate,
Read: resourceStorageObjectAclRead,
Update: resourceStorageObjectAclUpdate,
Delete: resourceStorageObjectAclDelete,
Schema: map[string]*schema.Schema{
"bucket": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"object": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"role_entity": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"predefined_acl": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func getObjectAclId(object string) string {
return object + "-acl"
}
func resourceStorageObjectAclCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
object := d.Get("object").(string)
predefined_acl := ""
role_entity := make([]interface{}, 0)
if v, ok := d.GetOk("predefined_acl"); ok {
predefined_acl = v.(string)
}
if v, ok := d.GetOk("role_entity"); ok {
role_entity = v.([]interface{})
}
if len(predefined_acl) > 0 {
if len(role_entity) > 0 {
return fmt.Errorf("Error, you cannot specify both " +
"\"predefined_acl\" and \"role_entity\"");
}
res, err := config.clientStorage.Objects.Get(bucket, object).Do()
if err != nil {
return fmt.Errorf("Error reading object %s: %v", bucket, err)
}
res, err = config.clientStorage.Objects.Update(bucket,object,
res).PredefinedAcl(predefined_acl).Do()
if err != nil {
return fmt.Errorf("Error updating object %s: %v", bucket, err)
}
return resourceStorageBucketAclRead(d, meta);
} else 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", pair.Role, pair.Entity)
_, err = config.clientStorage.ObjectAccessControls.Insert(bucket,
object, objectAccessControl).Do()
if err != nil {
return fmt.Errorf("Error setting ACL for %s on object %s: %v", pair.Entity, object, err)
}
}
return resourceStorageObjectAclRead(d, meta);
}
return fmt.Errorf("Error, you must specify either " +
"\"predefined_acl\" or \"role_entity\"");
}
func resourceStorageObjectAclRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
object := d.Get("object").(string)
// Predefined ACLs cannot easily be parsed once they have been processed
// by the GCP server
if _, ok := d.GetOk("predefined_acl"); !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.ObjectAccessControls.List(bucket, object).Do()
if err != nil {
return err
}
for _, v := range(res.Items) {
role := ""
entity := ""
for key, val := range (v.(map[string]interface{})) {
if key == "role" {
role = val.(string)
} else if key == "entity" {
entity = val.(string)
}
}
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", role, entity)
}
}
d.Set("role_entity", role_entity)
}
d.SetId(getObjectAclId(object))
return nil
}
func resourceStorageObjectAclUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
object := d.Get("object").(string)
if d.HasChange("role_entity") {
o, n := d.GetChange("role_entity")
old_re, new_re := o.([]interface{}), 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 this entity, it needs to
// be created. Otherwise it is updated
if _, ok := old_re_map[pair.Entity]; ok {
_, err = config.clientStorage.ObjectAccessControls.Update(
bucket, object, pair.Entity, objectAccessControl).Do()
} else {
_, err = config.clientStorage.ObjectAccessControls.Insert(
bucket, object, 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 ACL for object %s: %v", bucket, err)
}
}
for entity, _ := range(old_re_map) {
log.Printf("[DEBUG]: removing entity %s", entity)
err := config.clientStorage.ObjectAccessControls.Delete(bucket, object, entity).Do()
if err != nil {
return fmt.Errorf("Error updating ACL for object %s: %v", bucket, err)
}
}
return resourceStorageObjectAclRead(d, meta);
}
return nil
}
func resourceStorageObjectAclDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
bucket := d.Get("bucket").(string)
object := d.Get("object").(string)
re_local := d.Get("role_entity").([]interface{})
for _, v := range(re_local) {
res, err := getRoleEntityPair(v.(string))
if err != nil {
return err
}
entity := res.Entity
log.Printf("[DEBUG]: removing entity %s", entity)
err = config.clientStorage.ObjectAccessControls.Delete(bucket, object,
entity).Do()
if err != nil {
return fmt.Errorf("Error deleting entity %s ACL: %s",
entity, err)
}
}
return nil
}

View File

@ -0,0 +1,310 @@
package google
import (
"fmt"
"testing"
"math/rand"
"io/ioutil"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
//"google.golang.org/api/storage/v1"
)
var tfObjectAcl, errObjectAcl = ioutil.TempFile("", "tf-gce-test")
var testAclObjectName = fmt.Sprintf("%s-%d", "tf-test-acl-object",
rand.New(rand.NewSource(time.Now().UnixNano())).Int())
func TestAccGoogleStorageObjectAcl_basic(t *testing.T) {
objectData := []byte("data data data")
ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644)
resource.Test(t, resource.TestCase{
PreCheck: func() {
if errObjectAcl != nil {
panic(errObjectAcl)
}
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageObjectAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageObjectsAclBasic1,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic1),
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic2),
),
},
},
})
}
func TestAccGoogleStorageObjectAcl_upgrade(t *testing.T) {
objectData := []byte("data data data")
ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644)
resource.Test(t, resource.TestCase{
PreCheck: func() {
if errObjectAcl != nil {
panic(errObjectAcl)
}
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageObjectAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageObjectsAclBasic1,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic1),
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic2),
),
},
resource.TestStep{
Config: testGoogleStorageObjectsAclBasic2,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic2),
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic3_owner),
),
},
resource.TestStep{
Config: testGoogleStorageObjectsAclBasicDelete,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic1),
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic2),
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic3_reader),
),
},
},
})
}
func TestAccGoogleStorageObjectAcl_downgrade(t *testing.T) {
objectData := []byte("data data data")
ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644)
resource.Test(t, resource.TestCase{
PreCheck: func() {
if errObjectAcl != nil {
panic(errObjectAcl)
}
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageObjectAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageObjectsAclBasic2,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic2),
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic3_owner),
),
},
resource.TestStep{
Config: testGoogleStorageObjectsAclBasic3,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic2),
testAccCheckGoogleStorageObjectAcl(testAclBucketName,
testAclObjectName, roleEntityBasic3_reader),
),
},
resource.TestStep{
Config: testGoogleStorageObjectsAclBasicDelete,
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic1),
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic2),
testAccCheckGoogleStorageObjectAclDelete(testAclBucketName,
testAclObjectName, roleEntityBasic3_reader),
),
},
},
})
}
func TestAccGoogleStorageObjectAcl_predefined(t *testing.T) {
objectData := []byte("data data data")
ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644)
resource.Test(t, resource.TestCase{
PreCheck: func() {
if errObjectAcl != nil {
panic(errObjectAcl)
}
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccGoogleStorageObjectAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleStorageObjectsAclPredefined,
},
},
})
}
func testAccCheckGoogleStorageObjectAcl(bucket, object, roleEntityS string) resource.TestCheckFunc {
return func(s *terraform.State) error {
roleEntity, _ := getRoleEntityPair(roleEntityS)
config := testAccProvider.Meta().(*Config)
res, err := config.clientStorage.ObjectAccessControls.Get(bucket,
object, roleEntity.Entity).Do()
if err != nil {
return fmt.Errorf("Error retrieving contents of 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 testAccCheckGoogleStorageObjectAclDelete(bucket, object, roleEntityS string) resource.TestCheckFunc {
return func(s *terraform.State) error {
roleEntity, _ := getRoleEntityPair(roleEntityS)
config := testAccProvider.Meta().(*Config)
_, err := config.clientStorage.ObjectAccessControls.Get(bucket,
object, roleEntity.Entity).Do()
if err != nil {
return nil
}
return fmt.Errorf("Error, Entity still exists %s", roleEntity.Entity)
}
}
func testAccGoogleStorageObjectAclDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_storage_bucket_acl" {
continue
}
bucket := rs.Primary.Attributes["bucket"]
object := rs.Primary.Attributes["object"]
_, err := config.clientStorage.ObjectAccessControls.List(bucket, object).Do()
if err == nil {
return fmt.Errorf("Acl for bucket %s still exists", bucket)
}
}
return nil
}
var testGoogleStorageObjectsAclBasicDelete = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_storage_object_acl" "acl" {
object = "${google_storage_bucket_object.object.name}"
bucket = "${google_storage_bucket.bucket.name}"
role_entity = []
}
`, testAclBucketName, testAclObjectName, tfObjectAcl.Name())
var testGoogleStorageObjectsAclBasic1 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_storage_object_acl" "acl" {
object = "${google_storage_bucket_object.object.name}"
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, testAclObjectName, tfObjectAcl.Name(),
roleEntityBasic1, roleEntityBasic2)
var testGoogleStorageObjectsAclBasic2 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_storage_object_acl" "acl" {
object = "${google_storage_bucket_object.object.name}"
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, testAclObjectName, tfObjectAcl.Name(),
roleEntityBasic2, roleEntityBasic3_owner)
var testGoogleStorageObjectsAclBasic3 = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_storage_object_acl" "acl" {
object = "${google_storage_bucket_object.object.name}"
bucket = "${google_storage_bucket.bucket.name}"
role_entity = ["%s", "%s"]
}
`, testAclBucketName, testAclObjectName, tfObjectAcl.Name(),
roleEntityBasic2, roleEntityBasic3_reader)
var testGoogleStorageObjectsAclPredefined = fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "object" {
name = "%s"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_storage_object_acl" "acl" {
object = "${google_storage_bucket_object.object.name}"
bucket = "${google_storage_bucket.bucket.name}"
predefined_acl = "projectPrivate"
}
`, testAclBucketName, testAclObjectName, tfObjectAcl.Name())

View File

@ -17,9 +17,8 @@ Example creating a private bucket in standard storage, in the EU region.
```
resource "google_storage_bucket" "image-store" {
name = "image-store-bucket"
predefined_acl = "projectPrivate"
location = "EU"
name = "image-store-bucket"
location = "EU"
website {
main_page_suffix = "index.html"
not_found_page = "404.html"
@ -33,7 +32,8 @@ resource "google_storage_bucket" "image-store" {
The following arguments are supported:
* `name` - (Required) The name of the bucket.
* `predefined_acl` - (Optional, Default: 'private') The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) to apply.
* `predefined_acl` - (Optional, Deprecated) The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) to apply. Please switch
to `google_storage_bucket_acl.predefined_acl`.
* `location` - (Optional, Default: 'US') The [GCS location](https://cloud.google.com/storage/docs/bucket-locations)
* `force_destroy` - (Optional, Default: false) When deleting a bucket, this boolean option will delete all contained objects. If you try to delete a bucket that contains objects, Terraform will fail that run.

View File

@ -0,0 +1,36 @@
---
layout: "google"
page_title: "Google: google_storage_bucket_acl"
sidebar_current: "docs-google-resource-storage-acl"
description: |-
Creates a new bucket ACL in Google Cloud Storage.
---
# google\_storage\_bucket\_acl
Creates a new bucket ACL in Google cloud storage service(GCS).
## Example Usage
Example creating an ACL on a bucket with one owner, and one reader.
```
resource "google_storage_bucket" "image-store" {
name = "image-store-bucket"
location = "EU"
}
resource "google_storage_bucket_acl" "image-store-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.
* `predefined_acl` - (Optional) The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) to apply. Must be set if both `role_entity` and `default_acl` are not.
* `default_acl` - (Optional) The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) to apply to future buckets. Must be set both `role_entity` and `predefined_acl` are not.
* `role_entity` - (Optional) List of role/entity pairs in the form `ROLE:entity`. See [GCS Bucket ACL documentation](https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls) for more details. Must be set if both `predefined_acl` and `default_acl` are not.

View File

@ -20,7 +20,6 @@ resource "google_storage_bucket_object" "picture" {
name = "butterfly01"
source = "/images/nature/garden-tiger-moth.jpg"
bucket = "image-store"
predefined_acl = "publicRead"
}
```
@ -32,7 +31,8 @@ The following arguments are supported:
* `name` - (Required) The name of the object.
* `bucket` - (Required) The name of the containing bucket.
* `source` - (Required) A path to the data you want to upload.
* `predefined_acl` - (Optional, Default: 'projectPrivate') The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) apply.
* `predefined_acl` - (Optional, Deprecated) The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) apply. Please switch
to `google_storage_object_acl.predefined_acl`.
## Attributes Reference

View File

@ -0,0 +1,43 @@
---
layout: "google"
page_title: "Google: google_storage_object_acl"
sidebar_current: "docs-google-resource-storage-acl"
description: |-
Creates a new object ACL in Google Cloud Storage.
---
# google\_storage\_object\_acl
Creates a new object ACL in Google cloud storage service (GCS)
## Example Usage
Create an object ACL with one owner and one reader.
```
resource "google_storage_bucket" "image-store" {
name = "image-store-bucket"
location = "EU"
}
resource "google_storage_bucket_object" "image" {
name = "image1"
bucket = "${google_storage_bucket.name}"
source = "image1.jpg"
}
resource "google_storage_object_acl" "image-store-acl" {
bucket = "${google_storage_bucket.image_store.name}"
object = "${google_storage_bucket_object.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.
* `object` - (Required) The name of the object it applies to.
* `predefined_acl` - (Optional) The [canned GCS ACL](https://cloud.google.com/storage/docs/access-control#predefined-acl) to apply. Must be set if `role_entity` is not.
* `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. Must be set if `predefined_acl` is not.

View File

@ -93,9 +93,17 @@
<a href="/docs/providers/google/r/storage_bucket.html">google_storage_bucket</a>
</li>
<li<%= sidebar_current("docs-google-resource-storage-bucket-acl") %>>
<a href="/docs/providers/google/r/storage_bucket_acl.html">google_storage_bucket_acl</a>
</li>
<li<%= sidebar_current("docs-google-resource-storage-bucket-object") %>>
<a href="/docs/providers/google/r/storage_bucket_object.html">google_storage_bucket_object</a>
</li>
<li<%= sidebar_current("docs-google-resource-storage-object-acl") %>>
<a href="/docs/providers/google/r/storage_object_acl.html">google_storage_object_acl</a>
</li>
</ul>
</li>
</ul>