github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_storage_bucket_acl.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/hashicorp/terraform/helper/schema" 9 10 "google.golang.org/api/googleapi" 11 "google.golang.org/api/storage/v1" 12 ) 13 14 func resourceStorageBucketAcl() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceStorageBucketAclCreate, 17 Read: resourceStorageBucketAclRead, 18 Update: resourceStorageBucketAclUpdate, 19 Delete: resourceStorageBucketAclDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "bucket": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 28 "default_acl": &schema.Schema{ 29 Type: schema.TypeString, 30 Optional: true, 31 }, 32 33 "predefined_acl": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 ForceNew: true, 37 }, 38 39 "role_entity": &schema.Schema{ 40 Type: schema.TypeList, 41 Optional: true, 42 Elem: &schema.Schema{Type: schema.TypeString}, 43 }, 44 }, 45 } 46 } 47 48 type RoleEntity struct { 49 Role string 50 Entity string 51 } 52 53 func getBucketAclId(bucket string) string { 54 return bucket + "-acl" 55 } 56 57 func getRoleEntityPair(role_entity string) (*RoleEntity, error) { 58 split := strings.Split(role_entity, ":") 59 if len(split) != 2 { 60 return nil, fmt.Errorf("Error, each role entity pair must be " + 61 "formatted as ROLE:entity") 62 } 63 64 return &RoleEntity{Role: split[0], Entity: split[1]}, nil 65 } 66 67 func resourceStorageBucketAclCreate(d *schema.ResourceData, meta interface{}) error { 68 config := meta.(*Config) 69 70 bucket := d.Get("bucket").(string) 71 predefined_acl := "" 72 default_acl := "" 73 role_entity := make([]interface{}, 0) 74 75 if v, ok := d.GetOk("predefined_acl"); ok { 76 predefined_acl = v.(string) 77 } 78 79 if v, ok := d.GetOk("role_entity"); ok { 80 role_entity = v.([]interface{}) 81 } 82 83 if v, ok := d.GetOk("default_acl"); ok { 84 default_acl = v.(string) 85 } 86 87 if len(predefined_acl) > 0 { 88 if len(role_entity) > 0 { 89 return fmt.Errorf("Error, you cannot specify both " + 90 "\"predefined_acl\" and \"role_entity\"") 91 } 92 93 res, err := config.clientStorage.Buckets.Get(bucket).Do() 94 95 if err != nil { 96 return fmt.Errorf("Error reading bucket %s: %v", bucket, err) 97 } 98 99 res, err = config.clientStorage.Buckets.Update(bucket, 100 res).PredefinedAcl(predefined_acl).Do() 101 102 if err != nil { 103 return fmt.Errorf("Error updating bucket %s: %v", bucket, err) 104 } 105 106 return resourceStorageBucketAclRead(d, meta) 107 } else if len(role_entity) > 0 { 108 for _, v := range role_entity { 109 pair, err := getRoleEntityPair(v.(string)) 110 111 bucketAccessControl := &storage.BucketAccessControl{ 112 Role: pair.Role, 113 Entity: pair.Entity, 114 } 115 116 log.Printf("[DEBUG]: storing re %s-%s", pair.Role, pair.Entity) 117 118 _, err = config.clientStorage.BucketAccessControls.Insert(bucket, bucketAccessControl).Do() 119 120 if err != nil { 121 return fmt.Errorf("Error updating ACL for bucket %s: %v", bucket, err) 122 } 123 } 124 125 return resourceStorageBucketAclRead(d, meta) 126 } 127 128 if len(default_acl) > 0 { 129 res, err := config.clientStorage.Buckets.Get(bucket).Do() 130 131 if err != nil { 132 return fmt.Errorf("Error reading bucket %s: %v", bucket, err) 133 } 134 135 res, err = config.clientStorage.Buckets.Update(bucket, 136 res).PredefinedDefaultObjectAcl(default_acl).Do() 137 138 if err != nil { 139 return fmt.Errorf("Error updating bucket %s: %v", bucket, err) 140 } 141 142 return resourceStorageBucketAclRead(d, meta) 143 } 144 145 return nil 146 } 147 148 func resourceStorageBucketAclRead(d *schema.ResourceData, meta interface{}) error { 149 config := meta.(*Config) 150 151 bucket := d.Get("bucket").(string) 152 153 // Predefined ACLs cannot easily be parsed once they have been processed 154 // by the GCP server 155 if _, ok := d.GetOk("predefined_acl"); !ok { 156 role_entity := make([]interface{}, 0) 157 re_local := d.Get("role_entity").([]interface{}) 158 re_local_map := make(map[string]string) 159 for _, v := range re_local { 160 res, err := getRoleEntityPair(v.(string)) 161 162 if err != nil { 163 return fmt.Errorf( 164 "Old state has malformed Role/Entity pair: %v", err) 165 } 166 167 re_local_map[res.Entity] = res.Role 168 } 169 170 res, err := config.clientStorage.BucketAccessControls.List(bucket).Do() 171 172 if err != nil { 173 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 174 log.Printf("[WARN] Removing Bucket ACL for bucket %q because it's gone", d.Get("bucket").(string)) 175 // The resource doesn't exist anymore 176 d.SetId("") 177 178 return nil 179 } 180 181 return err 182 } 183 184 for _, v := range res.Items { 185 log.Printf("[DEBUG]: examining re %s-%s", v.Role, v.Entity) 186 // We only store updates to the locally defined access controls 187 if _, in := re_local_map[v.Entity]; in { 188 role_entity = append(role_entity, fmt.Sprintf("%s:%s", v.Role, v.Entity)) 189 log.Printf("[DEBUG]: saving re %s-%s", v.Role, v.Entity) 190 } 191 } 192 193 d.Set("role_entity", role_entity) 194 } 195 196 d.SetId(getBucketAclId(bucket)) 197 return nil 198 } 199 200 func resourceStorageBucketAclUpdate(d *schema.ResourceData, meta interface{}) error { 201 config := meta.(*Config) 202 203 bucket := d.Get("bucket").(string) 204 205 if d.HasChange("role_entity") { 206 o, n := d.GetChange("role_entity") 207 old_re, new_re := o.([]interface{}), n.([]interface{}) 208 209 old_re_map := make(map[string]string) 210 for _, v := range old_re { 211 res, err := getRoleEntityPair(v.(string)) 212 213 if err != nil { 214 return fmt.Errorf( 215 "Old state has malformed Role/Entity pair: %v", err) 216 } 217 218 old_re_map[res.Entity] = res.Role 219 } 220 221 for _, v := range new_re { 222 pair, err := getRoleEntityPair(v.(string)) 223 224 bucketAccessControl := &storage.BucketAccessControl{ 225 Role: pair.Role, 226 Entity: pair.Entity, 227 } 228 229 // If the old state is missing this entity, it needs to 230 // be created. Otherwise it is updated 231 if _, ok := old_re_map[pair.Entity]; ok { 232 _, err = config.clientStorage.BucketAccessControls.Update( 233 bucket, pair.Entity, bucketAccessControl).Do() 234 } else { 235 _, err = config.clientStorage.BucketAccessControls.Insert( 236 bucket, bucketAccessControl).Do() 237 } 238 239 // Now we only store the keys that have to be removed 240 delete(old_re_map, pair.Entity) 241 242 if err != nil { 243 return fmt.Errorf("Error updating ACL for bucket %s: %v", bucket, err) 244 } 245 } 246 247 for entity, _ := range old_re_map { 248 log.Printf("[DEBUG]: removing entity %s", entity) 249 err := config.clientStorage.BucketAccessControls.Delete(bucket, entity).Do() 250 251 if err != nil { 252 return fmt.Errorf("Error updating ACL for bucket %s: %v", bucket, err) 253 } 254 } 255 256 return resourceStorageBucketAclRead(d, meta) 257 } 258 259 if d.HasChange("default_acl") { 260 default_acl := d.Get("default_acl").(string) 261 262 res, err := config.clientStorage.Buckets.Get(bucket).Do() 263 264 if err != nil { 265 return fmt.Errorf("Error reading bucket %s: %v", bucket, err) 266 } 267 268 res, err = config.clientStorage.Buckets.Update(bucket, 269 res).PredefinedDefaultObjectAcl(default_acl).Do() 270 271 if err != nil { 272 return fmt.Errorf("Error updating bucket %s: %v", bucket, err) 273 } 274 275 return resourceStorageBucketAclRead(d, meta) 276 } 277 278 return nil 279 } 280 281 func resourceStorageBucketAclDelete(d *schema.ResourceData, meta interface{}) error { 282 config := meta.(*Config) 283 284 bucket := d.Get("bucket").(string) 285 286 re_local := d.Get("role_entity").([]interface{}) 287 for _, v := range re_local { 288 res, err := getRoleEntityPair(v.(string)) 289 if err != nil { 290 return err 291 } 292 293 log.Printf("[DEBUG]: removing entity %s", res.Entity) 294 295 err = config.clientStorage.BucketAccessControls.Delete(bucket, res.Entity).Do() 296 297 if err != nil { 298 return fmt.Errorf("Error deleting entity %s ACL: %s", res.Entity, err) 299 } 300 } 301 302 return nil 303 }