github.com/meteor/terraform@v0.6.15-0.20210412225145-79ec4bc057c6/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 return handleNotFoundError(err, d, fmt.Sprintf("Storage Bucket %q", d.Get("name").(string))) 219 } 220 221 log.Printf("[DEBUG] Read bucket %v at location %v\n\n", res.Name, res.SelfLink) 222 223 // Update the bucket ID according to the resource ID 224 d.Set("self_link", res.SelfLink) 225 d.SetId(res.Id) 226 227 return nil 228 } 229 230 func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error { 231 config := meta.(*Config) 232 233 // Get the bucket 234 bucket := d.Get("name").(string) 235 236 for { 237 res, err := config.clientStorage.Objects.List(bucket).Do() 238 if err != nil { 239 fmt.Printf("Error Objects.List failed: %v", err) 240 return err 241 } 242 243 if len(res.Items) != 0 { 244 if d.Get("force_destroy").(bool) { 245 // purge the bucket... 246 log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n") 247 248 for _, object := range res.Items { 249 log.Printf("[DEBUG] Found %s", object.Name) 250 if err := config.clientStorage.Objects.Delete(bucket, object.Name).Do(); err != nil { 251 log.Fatalf("Error trying to delete object: %s %s\n\n", object.Name, err) 252 } else { 253 log.Printf("Object deleted: %s \n\n", object.Name) 254 } 255 } 256 257 } else { 258 delete_err := errors.New("Error trying to delete a bucket containing objects without `force_destroy` set to true") 259 log.Printf("Error! %s : %s\n\n", bucket, delete_err) 260 return delete_err 261 } 262 } else { 263 break // 0 items, bucket empty 264 } 265 } 266 267 // remove empty bucket 268 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 269 err := config.clientStorage.Buckets.Delete(bucket).Do() 270 if err == nil { 271 return nil 272 } 273 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 429 { 274 return resource.RetryableError(gerr) 275 } 276 return resource.NonRetryableError(err) 277 }) 278 if err != nil { 279 fmt.Printf("Error deleting bucket %s: %v\n\n", bucket, err) 280 return err 281 } 282 log.Printf("[DEBUG] Deleted bucket %v\n\n", bucket) 283 284 return nil 285 }