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