github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/google/resource_storage_bucket.go (about) 1 package google 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/resource" 10 "github.com/hashicorp/terraform/helper/schema" 11 12 "google.golang.org/api/googleapi" 13 "google.golang.org/api/storage/v1" 14 ) 15 16 func resourceStorageBucket() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceStorageBucketCreate, 19 Read: resourceStorageBucketRead, 20 Update: resourceStorageBucketUpdate, 21 Delete: resourceStorageBucketDelete, 22 Importer: &schema.ResourceImporter{ 23 State: resourceStorageBucketStateImporter, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "force_destroy": &schema.Schema{ 34 Type: schema.TypeBool, 35 Optional: true, 36 Default: false, 37 }, 38 39 "location": &schema.Schema{ 40 Type: schema.TypeString, 41 Default: "US", 42 Optional: true, 43 ForceNew: true, 44 }, 45 46 "predefined_acl": &schema.Schema{ 47 Type: schema.TypeString, 48 Deprecated: "Please use resource \"storage_bucket_acl.predefined_acl\" instead.", 49 Optional: true, 50 ForceNew: true, 51 }, 52 53 "project": &schema.Schema{ 54 Type: schema.TypeString, 55 Optional: true, 56 ForceNew: true, 57 }, 58 59 "self_link": &schema.Schema{ 60 Type: schema.TypeString, 61 Computed: true, 62 }, 63 64 "url": &schema.Schema{ 65 Type: schema.TypeString, 66 Computed: true, 67 }, 68 69 "storage_class": &schema.Schema{ 70 Type: schema.TypeString, 71 Optional: true, 72 Default: "STANDARD", 73 ForceNew: true, 74 }, 75 76 "website": &schema.Schema{ 77 Type: schema.TypeList, 78 Optional: true, 79 Elem: &schema.Resource{ 80 Schema: map[string]*schema.Schema{ 81 "main_page_suffix": &schema.Schema{ 82 Type: schema.TypeString, 83 Optional: true, 84 }, 85 "not_found_page": &schema.Schema{ 86 Type: schema.TypeString, 87 Optional: true, 88 }, 89 }, 90 }, 91 }, 92 }, 93 } 94 } 95 96 func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error { 97 config := meta.(*Config) 98 99 project, err := getProject(d, config) 100 if err != nil { 101 return err 102 } 103 104 // Get the bucket and acl 105 bucket := d.Get("name").(string) 106 location := d.Get("location").(string) 107 108 // Create a bucket, setting the acl, location and name. 109 sb := &storage.Bucket{Name: bucket, Location: location} 110 111 if v, ok := d.GetOk("storage_class"); ok { 112 sb.StorageClass = v.(string) 113 } 114 115 if v, ok := d.GetOk("website"); ok { 116 websites := v.([]interface{}) 117 118 if len(websites) > 1 { 119 return fmt.Errorf("At most one website block is allowed") 120 } 121 122 sb.Website = &storage.BucketWebsite{} 123 124 website := websites[0].(map[string]interface{}) 125 126 if v, ok := website["not_found_page"]; ok { 127 sb.Website.NotFoundPage = v.(string) 128 } 129 130 if v, ok := website["main_page_suffix"]; ok { 131 sb.Website.MainPageSuffix = v.(string) 132 } 133 } 134 135 var res *storage.Bucket 136 137 err = resource.Retry(1*time.Minute, func() *resource.RetryError { 138 call := config.clientStorage.Buckets.Insert(project, sb) 139 if v, ok := d.GetOk("predefined_acl"); ok { 140 call = call.PredefinedAcl(v.(string)) 141 } 142 143 res, err = call.Do() 144 if err == nil { 145 return nil 146 } 147 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 429 { 148 return resource.RetryableError(gerr) 149 } 150 return resource.NonRetryableError(err) 151 }) 152 153 if err != nil { 154 fmt.Printf("Error creating bucket %s: %v", bucket, err) 155 return err 156 } 157 158 log.Printf("[DEBUG] Created bucket %v at location %v\n\n", res.Name, res.SelfLink) 159 160 d.SetId(res.Id) 161 return resourceStorageBucketRead(d, meta) 162 } 163 164 func resourceStorageBucketUpdate(d *schema.ResourceData, meta interface{}) error { 165 config := meta.(*Config) 166 167 sb := &storage.Bucket{} 168 169 if d.HasChange("website") { 170 if v, ok := d.GetOk("website"); ok { 171 websites := v.([]interface{}) 172 173 if len(websites) > 1 { 174 return fmt.Errorf("At most one website block is allowed") 175 } 176 177 // Setting fields to "" to be explicit that the PATCH call will 178 // delete this field. 179 if len(websites) == 0 { 180 sb.Website.NotFoundPage = "" 181 sb.Website.MainPageSuffix = "" 182 } else { 183 website := websites[0].(map[string]interface{}) 184 sb.Website = &storage.BucketWebsite{} 185 if v, ok := website["not_found_page"]; ok { 186 sb.Website.NotFoundPage = v.(string) 187 } else { 188 sb.Website.NotFoundPage = "" 189 } 190 191 if v, ok := website["main_page_suffix"]; ok { 192 sb.Website.MainPageSuffix = v.(string) 193 } else { 194 sb.Website.MainPageSuffix = "" 195 } 196 } 197 } 198 } 199 200 res, err := config.clientStorage.Buckets.Patch(d.Get("name").(string), sb).Do() 201 202 if err != nil { 203 return err 204 } 205 206 log.Printf("[DEBUG] Patched bucket %v at location %v\n\n", res.Name, res.SelfLink) 207 208 // Assign the bucket ID as the resource ID 209 d.Set("self_link", res.SelfLink) 210 d.SetId(res.Id) 211 212 return nil 213 } 214 215 func resourceStorageBucketRead(d *schema.ResourceData, meta interface{}) error { 216 config := meta.(*Config) 217 218 // Get the bucket and acl 219 bucket := d.Get("name").(string) 220 res, err := config.clientStorage.Buckets.Get(bucket).Do() 221 222 if err != nil { 223 return handleNotFoundError(err, d, fmt.Sprintf("Storage Bucket %q", d.Get("name").(string))) 224 } 225 226 log.Printf("[DEBUG] Read bucket %v at location %v\n\n", res.Name, res.SelfLink) 227 228 // Update the bucket ID according to the resource ID 229 d.Set("self_link", res.SelfLink) 230 d.Set("url", fmt.Sprintf("gs://%s", bucket)) 231 d.Set("storage_class", res.StorageClass) 232 d.Set("location", res.Location) 233 d.SetId(res.Id) 234 return nil 235 } 236 237 func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error { 238 config := meta.(*Config) 239 240 // Get the bucket 241 bucket := d.Get("name").(string) 242 243 for { 244 res, err := config.clientStorage.Objects.List(bucket).Do() 245 if err != nil { 246 fmt.Printf("Error Objects.List failed: %v", err) 247 return err 248 } 249 250 if len(res.Items) != 0 { 251 if d.Get("force_destroy").(bool) { 252 // purge the bucket... 253 log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n") 254 255 for _, object := range res.Items { 256 log.Printf("[DEBUG] Found %s", object.Name) 257 if err := config.clientStorage.Objects.Delete(bucket, object.Name).Do(); err != nil { 258 log.Fatalf("Error trying to delete object: %s %s\n\n", object.Name, err) 259 } else { 260 log.Printf("Object deleted: %s \n\n", object.Name) 261 } 262 } 263 264 } else { 265 delete_err := errors.New("Error trying to delete a bucket containing objects without `force_destroy` set to true") 266 log.Printf("Error! %s : %s\n\n", bucket, delete_err) 267 return delete_err 268 } 269 } else { 270 break // 0 items, bucket empty 271 } 272 } 273 274 // remove empty bucket 275 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 276 err := config.clientStorage.Buckets.Delete(bucket).Do() 277 if err == nil { 278 return nil 279 } 280 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 429 { 281 return resource.RetryableError(gerr) 282 } 283 return resource.NonRetryableError(err) 284 }) 285 if err != nil { 286 fmt.Printf("Error deleting bucket %s: %v\n\n", bucket, err) 287 return err 288 } 289 log.Printf("[DEBUG] Deleted bucket %v\n\n", bucket) 290 291 return nil 292 } 293 294 func resourceStorageBucketStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 295 d.Set("name", d.Id()) 296 return []*schema.ResourceData{d}, nil 297 }