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  }