provider/google: Add CORS support for google_storage_bucket. (#14695)
This commit is contained in:
parent
4e6dcb3a3e
commit
32d47b384f
|
@ -89,6 +89,40 @@ func resourceStorageBucket() *schema.Resource {
|
|||
},
|
||||
},
|
||||
},
|
||||
|
||||
"cors": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"origin": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
"method": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
"response_header": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
"max_age_seconds": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +166,10 @@ func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("cors"); ok {
|
||||
sb.Cors = expandCors(v.([]interface{}))
|
||||
}
|
||||
|
||||
var res *storage.Bucket
|
||||
|
||||
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
|
||||
|
@ -197,6 +235,10 @@ func resourceStorageBucketUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
}
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("cors"); ok {
|
||||
sb.Cors = expandCors(v.([]interface{}))
|
||||
}
|
||||
|
||||
res, err := config.clientStorage.Buckets.Patch(d.Get("name").(string), sb).Do()
|
||||
|
||||
if err != nil {
|
||||
|
@ -230,6 +272,7 @@ func resourceStorageBucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
d.Set("url", fmt.Sprintf("gs://%s", bucket))
|
||||
d.Set("storage_class", res.StorageClass)
|
||||
d.Set("location", res.Location)
|
||||
d.Set("cors", flattenCors(res.Cors))
|
||||
d.SetId(res.Id)
|
||||
return nil
|
||||
}
|
||||
|
@ -295,3 +338,43 @@ func resourceStorageBucketStateImporter(d *schema.ResourceData, meta interface{}
|
|||
d.Set("name", d.Id())
|
||||
return []*schema.ResourceData{d}, nil
|
||||
}
|
||||
|
||||
func expandCors(configured []interface{}) []*storage.BucketCors {
|
||||
corsRules := make([]*storage.BucketCors, 0, len(configured))
|
||||
for _, raw := range configured {
|
||||
data := raw.(map[string]interface{})
|
||||
corsRule := storage.BucketCors{
|
||||
Origin: convertSchemaArrayToStringArray(data["origin"].([]interface{})),
|
||||
Method: convertSchemaArrayToStringArray(data["method"].([]interface{})),
|
||||
ResponseHeader: convertSchemaArrayToStringArray(data["response_header"].([]interface{})),
|
||||
MaxAgeSeconds: int64(data["max_age_seconds"].(int)),
|
||||
}
|
||||
|
||||
corsRules = append(corsRules, &corsRule)
|
||||
}
|
||||
return corsRules
|
||||
}
|
||||
|
||||
func convertSchemaArrayToStringArray(input []interface{}) []string {
|
||||
output := make([]string, 0, len(input))
|
||||
for _, val := range input {
|
||||
output = append(output, val.(string))
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func flattenCors(corsRules []*storage.BucketCors) []map[string]interface{} {
|
||||
corsRulesSchema := make([]map[string]interface{}, 0, len(corsRules))
|
||||
for _, corsRule := range corsRules {
|
||||
data := map[string]interface{}{
|
||||
"origin": corsRule.Origin,
|
||||
"method": corsRule.Method,
|
||||
"response_header": corsRule.ResponseHeader,
|
||||
"max_age_seconds": corsRule.MaxAgeSeconds,
|
||||
}
|
||||
|
||||
corsRulesSchema = append(corsRulesSchema, data)
|
||||
}
|
||||
return corsRulesSchema
|
||||
}
|
||||
|
|
|
@ -173,6 +173,76 @@ func TestAccStorageBucket_forceDestroy(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccStorageBucket_cors(t *testing.T) {
|
||||
var bucket storage.Bucket
|
||||
bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccStorageBucketDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsCors(bucketName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStorageBucketExists(
|
||||
"google_storage_bucket.bucket", bucketName, &bucket),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if len(bucket.Cors) != 2 {
|
||||
t.Errorf("Expected # of cors elements to be 2, got %d", len(bucket.Cors))
|
||||
}
|
||||
|
||||
firstArr := bucket.Cors[0]
|
||||
if firstArr.MaxAgeSeconds != 10 {
|
||||
t.Errorf("Expected first block's MaxAgeSeconds to be 10, got %d", firstArr.MaxAgeSeconds)
|
||||
}
|
||||
|
||||
for i, v := range []string{"abc", "def"} {
|
||||
if firstArr.Origin[i] != v {
|
||||
t.Errorf("Expected value in first block origin to be to be %v, got %v", v, firstArr.Origin[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range []string{"a1a"} {
|
||||
if firstArr.Method[i] != v {
|
||||
t.Errorf("Expected value in first block method to be to be %v, got %v", v, firstArr.Method[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range []string{"123", "456", "789"} {
|
||||
if firstArr.ResponseHeader[i] != v {
|
||||
t.Errorf("Expected value in first block response headerto be to be %v, got %v", v, firstArr.ResponseHeader[i])
|
||||
}
|
||||
}
|
||||
|
||||
secondArr := bucket.Cors[1]
|
||||
if secondArr.MaxAgeSeconds != 5 {
|
||||
t.Errorf("Expected second block's MaxAgeSeconds to be 5, got %d", secondArr.MaxAgeSeconds)
|
||||
}
|
||||
|
||||
for i, v := range []string{"ghi", "jkl"} {
|
||||
if secondArr.Origin[i] != v {
|
||||
t.Errorf("Expected value in second block origin to be to be %v, got %v", v, secondArr.Origin[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range []string{"z9z"} {
|
||||
if secondArr.Method[i] != v {
|
||||
t.Errorf("Expected value in second block method to be to be %v, got %v", v, secondArr.Method[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range []string{"000"} {
|
||||
if secondArr.ResponseHeader[i] != v {
|
||||
t.Errorf("Expected value in second block response headerto be to be %v, got %v", v, secondArr.ResponseHeader[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckStorageBucketExists(n string, bucketName string, bucket *storage.Bucket) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -289,3 +359,24 @@ resource "google_storage_bucket" "bucket" {
|
|||
}
|
||||
`, bucketName, storageClass, locationBlock)
|
||||
}
|
||||
|
||||
func testGoogleStorageBucketsCors(bucketName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_storage_bucket" "bucket" {
|
||||
name = "%s"
|
||||
cors {
|
||||
origin = ["abc", "def"]
|
||||
method = ["a1a"]
|
||||
response_header = ["123", "456", "789"]
|
||||
max_age_seconds = 10
|
||||
}
|
||||
|
||||
cors {
|
||||
origin = ["ghi", "jkl"]
|
||||
method = ["z9z"]
|
||||
response_header = ["000"]
|
||||
max_age_seconds = 5
|
||||
}
|
||||
}
|
||||
`, bucketName)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ description: |-
|
|||
|
||||
# google\_storage\_bucket
|
||||
|
||||
Creates a new bucket in Google cloud storage service(GCS). Currently, it will not change location nor ACL once a bucket has been created with Terraform. For more information see [the official documentation](https://cloud.google.com/storage/docs/overview) and [API](https://cloud.google.com/storage/docs/json_api).
|
||||
Creates a new bucket in Google cloud storage service(GCS). Currently, it will not change location nor ACL once a bucket has been created with Terraform. For more information see [the official documentation](https://cloud.google.com/storage/docs/overview) and [API](https://cloud.google.com/storage/docs/json_api/v1/buckets).
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
@ -50,15 +50,27 @@ to `google_storage_bucket_acl.predefined_acl`.
|
|||
|
||||
* `storage_class` - (Optional) The [Storage Class](https://cloud.google.com/storage/docs/storage-classes) of the new bucket. Supported values include: `MULTI_REGIONAL`, `REGIONAL`, `NEARLINE`, `COLDLINE`.
|
||||
|
||||
* `website` - (Optional) Configuration if the bucket acts as a website.
|
||||
* `website` - (Optional) Configuration if the bucket acts as a website. Structure is documented below.
|
||||
|
||||
The optional `website` block supports:
|
||||
* `cors` - (Optional) The bucket's [Cross-Origin Resource Sharing (CORS)](https://www.w3.org/TR/cors/) configuration. Multiple blocks of this type are permitted. Structure is documented below.
|
||||
|
||||
The `website` block supports:
|
||||
|
||||
* `main_page_suffix` - (Optional) Behaves as the bucket's directory index where
|
||||
missing objects are treated as potential directories.
|
||||
|
||||
* `not_found_page` - (Optional) The custom object to return when a requested
|
||||
resource is not found.
|
||||
|
||||
The `cors` block supports:
|
||||
|
||||
* `origin` - (Optional) The list of [Origins](https://tools.ietf.org/html/rfc6454) eligible to receive CORS response headers. Note: "*" is permitted in the list of origins, and means "any Origin".
|
||||
|
||||
* `method` - (Optional) The list of HTTP methods on which to include CORS response headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list of methods, and means "any method".
|
||||
|
||||
* `response_header` - (Optional) The list of HTTP headers other than the [simple response headers](https://www.w3.org/TR/cors/#simple-response-header) to give permission for the user-agent to share across domains.
|
||||
|
||||
* `max_age_seconds` - (Optional) The value, in seconds, to return in the [Access-Control-Max-Age header](https://www.w3.org/TR/cors/#access-control-max-age-response-header) used in preflight responses.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
|
|
Loading…
Reference in New Issue