Skip to content

Commit 9f21b56

Browse files
ishashchukdanawillow
authored andcommitted
Add google_storage_default_object_acl resource (#992)
* Storage Default Object ACL resource * Fixed the doc * Renamed the resource id. Log change * Complying with go vet * Changes for review * link to default object acl docs in sidebar
1 parent e4ae7bd commit 9f21b56

5 files changed

+420
-0
lines changed

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func Provider() terraform.ResourceProvider {
192192
"google_storage_bucket_iam_member": ResourceIamMember(IamStorageBucketSchema, NewStorageBucketIamUpdater),
193193
"google_storage_bucket_object": resourceStorageBucketObject(),
194194
"google_storage_object_acl": resourceStorageObjectAcl(),
195+
"google_storage_default_object_acl": resourceStorageDefaultObjectAcl(),
195196
},
196197

197198
ConfigureFunc: providerConfigure,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform/helper/schema"
8+
"google.golang.org/api/storage/v1"
9+
)
10+
11+
func resourceStorageDefaultObjectAcl() *schema.Resource {
12+
return &schema.Resource{
13+
Create: resourceStorageDefaultObjectAclCreate,
14+
Read: resourceStorageDefaultObjectAclRead,
15+
Update: resourceStorageDefaultObjectAclUpdate,
16+
Delete: resourceStorageDefaultObjectAclDelete,
17+
18+
Schema: map[string]*schema.Schema{
19+
"bucket": &schema.Schema{
20+
Type: schema.TypeString,
21+
Required: true,
22+
ForceNew: true,
23+
},
24+
25+
"role_entity": &schema.Schema{
26+
Type: schema.TypeList,
27+
Required: true,
28+
Elem: &schema.Schema{Type: schema.TypeString},
29+
MinItems: 1,
30+
},
31+
},
32+
}
33+
}
34+
35+
func resourceStorageDefaultObjectAclCreate(d *schema.ResourceData, meta interface{}) error {
36+
config := meta.(*Config)
37+
38+
bucket := d.Get("bucket").(string)
39+
roleEntity := d.Get("role_entity").([]interface{})
40+
41+
for _, v := range roleEntity {
42+
pair, err := getRoleEntityPair(v.(string))
43+
44+
ObjectAccessControl := &storage.ObjectAccessControl{
45+
Role: pair.Role,
46+
Entity: pair.Entity,
47+
}
48+
49+
log.Printf("[DEBUG]: setting role = %s, entity = %s on bucket %s", pair.Role, pair.Entity, bucket)
50+
51+
_, err = config.clientStorage.DefaultObjectAccessControls.Insert(bucket, ObjectAccessControl).Do()
52+
53+
if err != nil {
54+
return fmt.Errorf("Error setting Default Object ACL for %s on bucket %s: %v", pair.Entity, bucket, err)
55+
}
56+
}
57+
d.SetId(bucket)
58+
return resourceStorageDefaultObjectAclRead(d, meta)
59+
}
60+
61+
func resourceStorageDefaultObjectAclRead(d *schema.ResourceData, meta interface{}) error {
62+
config := meta.(*Config)
63+
64+
bucket := d.Get("bucket").(string)
65+
66+
roleEntities := make([]interface{}, 0)
67+
reLocal := d.Get("role_entity").([]interface{})
68+
reLocalMap := make(map[string]string)
69+
for _, v := range reLocal {
70+
res, err := getRoleEntityPair(v.(string))
71+
72+
if err != nil {
73+
return fmt.Errorf(
74+
"Old state has malformed Role/Entity pair: %v", err)
75+
}
76+
77+
reLocalMap[res.Entity] = res.Role
78+
}
79+
80+
res, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do()
81+
82+
if err != nil {
83+
return handleNotFoundError(err, d, fmt.Sprintf("Storage Default Object ACL for bucket %q", d.Get("bucket").(string)))
84+
}
85+
86+
for _, v := range res.Items {
87+
role := v.Role
88+
entity := v.Entity
89+
// We only store updates to the locally defined access controls
90+
if _, in := reLocalMap[entity]; in {
91+
roleEntities = append(roleEntities, fmt.Sprintf("%s:%s", role, entity))
92+
log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity)
93+
}
94+
}
95+
96+
d.Set("role_entity", roleEntities)
97+
98+
return nil
99+
}
100+
101+
func resourceStorageDefaultObjectAclUpdate(d *schema.ResourceData, meta interface{}) error {
102+
config := meta.(*Config)
103+
104+
bucket := d.Get("bucket").(string)
105+
106+
if !d.HasChange("role_entity") {
107+
return nil
108+
}
109+
o, n := d.GetChange("role_entity")
110+
oldRe := o.([]interface{})
111+
newRe := n.([]interface{})
112+
113+
oldReMap := make(map[string]string)
114+
for _, v := range oldRe {
115+
res, err := getRoleEntityPair(v.(string))
116+
117+
if err != nil {
118+
return fmt.Errorf(
119+
"Old state has malformed Role/Entity pair: %v", err)
120+
}
121+
122+
oldReMap[res.Entity] = res.Role
123+
}
124+
125+
for _, v := range newRe {
126+
pair, err := getRoleEntityPair(v.(string))
127+
128+
ObjectAccessControl := &storage.ObjectAccessControl{
129+
Role: pair.Role,
130+
Entity: pair.Entity,
131+
}
132+
133+
// If the old state is present for the entity, it is updated
134+
// If the old state is missing, it is inserted
135+
if _, ok := oldReMap[pair.Entity]; ok {
136+
_, err = config.clientStorage.DefaultObjectAccessControls.Update(
137+
bucket, pair.Entity, ObjectAccessControl).Do()
138+
} else {
139+
_, err = config.clientStorage.DefaultObjectAccessControls.Insert(
140+
bucket, ObjectAccessControl).Do()
141+
}
142+
143+
// Now we only store the keys that have to be removed
144+
delete(oldReMap, pair.Entity)
145+
146+
if err != nil {
147+
return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err)
148+
}
149+
}
150+
151+
for entity := range oldReMap {
152+
log.Printf("[DEBUG]: removing entity %s", entity)
153+
err := config.clientStorage.DefaultObjectAccessControls.Delete(bucket, entity).Do()
154+
155+
if err != nil {
156+
return fmt.Errorf("Error updating Storage Default Object ACL for bucket %s: %v", bucket, err)
157+
}
158+
}
159+
160+
return resourceStorageDefaultObjectAclRead(d, meta)
161+
}
162+
163+
func resourceStorageDefaultObjectAclDelete(d *schema.ResourceData, meta interface{}) error {
164+
config := meta.(*Config)
165+
166+
bucket := d.Get("bucket").(string)
167+
168+
reLocal := d.Get("role_entity").([]interface{})
169+
for _, v := range reLocal {
170+
res, err := getRoleEntityPair(v.(string))
171+
if err != nil {
172+
return err
173+
}
174+
175+
log.Printf("[DEBUG]: removing entity %s", res.Entity)
176+
177+
err = config.clientStorage.DefaultObjectAccessControls.Delete(bucket, res.Entity).Do()
178+
179+
if err != nil {
180+
return fmt.Errorf("Error deleting entity %s ACL: %s", res.Entity, err)
181+
}
182+
}
183+
184+
return nil
185+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform/helper/resource"
8+
"github.com/hashicorp/terraform/terraform"
9+
)
10+
11+
func TestAccGoogleStorageDefaultObjectAcl_basic(t *testing.T) {
12+
t.Parallel()
13+
14+
bucketName := testBucketName()
15+
16+
resource.Test(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Providers: testAccProviders,
19+
CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy,
20+
Steps: []resource.TestStep{
21+
resource.TestStep{
22+
Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2),
23+
Check: resource.ComposeTestCheckFunc(
24+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1),
25+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2),
26+
),
27+
},
28+
},
29+
})
30+
}
31+
32+
func TestAccGoogleStorageDefaultObjectAcl_upgrade(t *testing.T) {
33+
t.Parallel()
34+
35+
bucketName := testBucketName()
36+
37+
resource.Test(t, resource.TestCase{
38+
PreCheck: func() { testAccPreCheck(t) },
39+
Providers: testAccProviders,
40+
CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy,
41+
Steps: []resource.TestStep{
42+
resource.TestStep{
43+
Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2),
44+
Check: resource.ComposeTestCheckFunc(
45+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1),
46+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2),
47+
),
48+
},
49+
50+
resource.TestStep{
51+
Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner),
52+
Check: resource.ComposeTestCheckFunc(
53+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2),
54+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner),
55+
),
56+
},
57+
58+
resource.TestStep{
59+
Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1),
60+
Check: resource.ComposeTestCheckFunc(
61+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1),
62+
testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2),
63+
testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader),
64+
),
65+
},
66+
},
67+
})
68+
}
69+
70+
func TestAccGoogleStorageDefaultObjectAcl_downgrade(t *testing.T) {
71+
t.Parallel()
72+
73+
bucketName := testBucketName()
74+
75+
resource.Test(t, resource.TestCase{
76+
PreCheck: func() { testAccPreCheck(t) },
77+
Providers: testAccProviders,
78+
CheckDestroy: testAccGoogleStorageDefaultObjectAclDestroy,
79+
Steps: []resource.TestStep{
80+
resource.TestStep{
81+
Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner),
82+
Check: resource.ComposeTestCheckFunc(
83+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2),
84+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner),
85+
),
86+
},
87+
88+
resource.TestStep{
89+
Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_reader),
90+
Check: resource.ComposeTestCheckFunc(
91+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2),
92+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_reader),
93+
),
94+
},
95+
96+
resource.TestStep{
97+
Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1),
98+
Check: resource.ComposeTestCheckFunc(
99+
testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1),
100+
testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2),
101+
testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader),
102+
),
103+
},
104+
},
105+
})
106+
}
107+
108+
func testAccCheckGoogleStorageDefaultObjectAcl(bucket, roleEntityS string) resource.TestCheckFunc {
109+
return func(s *terraform.State) error {
110+
roleEntity, _ := getRoleEntityPair(roleEntityS)
111+
config := testAccProvider.Meta().(*Config)
112+
113+
res, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket,
114+
roleEntity.Entity).Do()
115+
116+
if err != nil {
117+
return fmt.Errorf("Error retrieving contents of storage default Acl for bucket %s: %s", bucket, err)
118+
}
119+
120+
if res.Role != roleEntity.Role {
121+
return fmt.Errorf("Error, Role mismatch %s != %s", res.Role, roleEntity.Role)
122+
}
123+
124+
return nil
125+
}
126+
}
127+
128+
func testAccGoogleStorageDefaultObjectAclDestroy(s *terraform.State) error {
129+
config := testAccProvider.Meta().(*Config)
130+
131+
for _, rs := range s.RootModule().Resources {
132+
133+
if rs.Type != "google_storage_default_object_acl" {
134+
continue
135+
}
136+
137+
bucket := rs.Primary.Attributes["bucket"]
138+
139+
_, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do()
140+
if err == nil {
141+
return fmt.Errorf("Default Storage Object Acl for bucket %s still exists", bucket)
142+
}
143+
}
144+
return nil
145+
}
146+
147+
func testAccCheckGoogleStorageDefaultObjectAclDelete(bucket, roleEntityS string) resource.TestCheckFunc {
148+
return func(s *terraform.State) error {
149+
roleEntity, _ := getRoleEntityPair(roleEntityS)
150+
config := testAccProvider.Meta().(*Config)
151+
152+
_, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket, roleEntity.Entity).Do()
153+
154+
if err != nil {
155+
return nil
156+
}
157+
158+
return fmt.Errorf("Error, Object Default Acl Entity still exists %s for bucket %s",
159+
roleEntity.Entity, bucket)
160+
}
161+
}
162+
163+
func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntity string) string {
164+
return fmt.Sprintf(`
165+
resource "google_storage_bucket" "bucket" {
166+
name = "%s"
167+
}
168+
169+
resource "google_storage_default_object_acl" "acl" {
170+
bucket = "${google_storage_bucket.bucket.name}"
171+
role_entity = ["%s"]
172+
}
173+
`, bucketName, roleEntity)
174+
}
175+
176+
func testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntity1, roleEntity2 string) string {
177+
return fmt.Sprintf(`
178+
resource "google_storage_bucket" "bucket" {
179+
name = "%s"
180+
}
181+
182+
resource "google_storage_default_object_acl" "acl" {
183+
bucket = "${google_storage_bucket.bucket.name}"
184+
role_entity = ["%s", "%s"]
185+
}
186+
`, bucketName, roleEntity1, roleEntity2)
187+
}

0 commit comments

Comments
 (0)