Skip to content

Commit d53d378

Browse files
Added custom_placement_config field in google_storage_bucket resource (#6619) (#4752)
Signed-off-by: Modular Magician <[email protected]> Signed-off-by: Modular Magician <[email protected]>
1 parent 3d9d650 commit d53d378

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

.changelog/6619.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
storage: added `custom_placement_config` field to `google_storage_bucket` resource to support custom dual-region GCS buckets
3+
```

google-beta/resource_storage_bucket.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,27 @@ func resourceStorageBucket() *schema.Resource {
365365
Computed: true,
366366
Description: `Enables uniform bucket-level access on a bucket.`,
367367
},
368+
"custom_placement_config": {
369+
Type: schema.TypeList,
370+
Optional: true,
371+
MaxItems: 1,
372+
Elem: &schema.Resource{
373+
Schema: map[string]*schema.Schema{
374+
"data_locations": {
375+
Type: schema.TypeSet,
376+
Required: true,
377+
ForceNew: true,
378+
MaxItems: 2,
379+
MinItems: 2,
380+
Elem: &schema.Schema{
381+
Type: schema.TypeString,
382+
},
383+
Description: `The list of individual regions that comprise a dual-region bucket. See the docs for a list of acceptable regions. Note: If any of the data_locations changes, it will recreate the bucket.`,
384+
},
385+
},
386+
},
387+
Description: `The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated a single or multi-region, the parameters are empty.`,
388+
},
368389
"public_access_prevention": {
369390
Type: schema.TypeString,
370391
Optional: true,
@@ -488,6 +509,10 @@ func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error
488509
}
489510
}
490511

512+
if v, ok := d.GetOk("custom_placement_config"); ok {
513+
sb.CustomPlacementConfig = expandBucketCustomPlacementConfig(v.([]interface{}))
514+
}
515+
491516
var res *storage.Bucket
492517

493518
err = retry(func() error {
@@ -900,6 +925,42 @@ func flattenBucketEncryption(enc *storage.BucketEncryption) []map[string]interfa
900925
return encryption
901926
}
902927

928+
func expandBucketCustomPlacementConfig(configured interface{}) *storage.BucketCustomPlacementConfig {
929+
cfcs := configured.([]interface{})
930+
if len(cfcs) == 0 || cfcs[0] == nil {
931+
return nil
932+
}
933+
cfc := cfcs[0].(map[string]interface{})
934+
bucketcfc := &storage.BucketCustomPlacementConfig{
935+
DataLocations: expandBucketDataLocations(cfc["data_locations"]),
936+
}
937+
return bucketcfc
938+
}
939+
940+
func flattenBucketCustomPlacementConfig(cfc *storage.BucketCustomPlacementConfig) []map[string]interface{} {
941+
customPlacementConfig := make([]map[string]interface{}, 0, 1)
942+
943+
if cfc == nil {
944+
return customPlacementConfig
945+
}
946+
947+
customPlacementConfig = append(customPlacementConfig, map[string]interface{}{
948+
"data_locations": cfc.DataLocations,
949+
})
950+
951+
return customPlacementConfig
952+
}
953+
954+
func expandBucketDataLocations(configured interface{}) []string {
955+
l := configured.(*schema.Set).List()
956+
957+
req := make([]string, 0, len(l))
958+
for _, raw := range l {
959+
req = append(req, raw.(string))
960+
}
961+
return req
962+
}
963+
903964
func expandBucketLogging(configured interface{}) *storage.BucketLogging {
904965
loggings := configured.([]interface{})
905966
if len(loggings) == 0 {
@@ -1440,6 +1501,9 @@ func setStorageBucket(d *schema.ResourceData, config *Config, res *storage.Bucke
14401501
if err := d.Set("retention_policy", flattenBucketRetentionPolicy(res.RetentionPolicy)); err != nil {
14411502
return fmt.Errorf("Error setting retention_policy: %s", err)
14421503
}
1504+
if err := d.Set("custom_placement_config", flattenBucketCustomPlacementConfig(res.CustomPlacementConfig)); err != nil {
1505+
return fmt.Errorf("Error setting custom_placement_config: %s", err)
1506+
}
14431507

14441508
if res.IamConfiguration != nil && res.IamConfiguration.UniformBucketLevelAccess != nil {
14451509
if err := d.Set("uniform_bucket_level_access", res.IamConfiguration.UniformBucketLevelAccess.Enabled); err != nil {

google-beta/resource_storage_bucket_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,29 @@ func TestAccStorageBucket_lowercaseLocation(t *testing.T) {
103103
})
104104
}
105105

106+
func TestAccStorageBucket_dualLocation(t *testing.T) {
107+
t.Parallel()
108+
109+
bucketName := testBucketName(t)
110+
111+
vcrTest(t, resource.TestCase{
112+
PreCheck: func() { testAccPreCheck(t) },
113+
Providers: testAccProviders,
114+
CheckDestroy: testAccStorageBucketDestroyProducer(t),
115+
Steps: []resource.TestStep{
116+
{
117+
Config: testAccStorageBucket_dualLocation(bucketName),
118+
},
119+
{
120+
ResourceName: "google_storage_bucket.bucket",
121+
ImportState: true,
122+
ImportStateVerify: true,
123+
ImportStateVerifyIgnore: []string{"force_destroy"},
124+
},
125+
},
126+
})
127+
}
128+
106129
func TestAccStorageBucket_customAttributes(t *testing.T) {
107130
t.Parallel()
108131

@@ -1309,6 +1332,19 @@ resource "google_storage_bucket" "bucket" {
13091332
`, bucketName)
13101333
}
13111334

1335+
func testAccStorageBucket_dualLocation(bucketName string) string {
1336+
return fmt.Sprintf(`
1337+
resource "google_storage_bucket" "bucket" {
1338+
name = "%s"
1339+
location = "ASIA"
1340+
force_destroy = true
1341+
custom_placement_config {
1342+
data_locations = ["ASIA-EAST1", "ASIA-SOUTHEAST1"]
1343+
}
1344+
}
1345+
`, bucketName)
1346+
}
1347+
13121348
func testAccStorageBucket_customAttributes(bucketName string) string {
13131349
return fmt.Sprintf(`
13141350
resource "google_storage_bucket" "bucket" {

website/docs/r/storage_bucket.html.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ The following arguments are supported:
101101

102102
* `uniform_bucket_level_access` - (Optional, Default: false) Enables [Uniform bucket-level access](https://cloud.google.com/storage/docs/uniform-bucket-level-access) access to a bucket.
103103

104+
* `custom_placement_config` - (Optional) The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated a single or multi-region, the parameters are empty. Structure is [documented below](#nested_custom_placement_config).
105+
104106
<a name="nested_lifecycle_rule"></a>The `lifecycle_rule` block supports:
105107

106108
* `action` - (Required) The Lifecycle Rule's action configuration. A single block of this type is supported. Structure is [documented below](#nested_action).
@@ -189,6 +191,10 @@ The following arguments are supported:
189191
state of the project.
190192
You should take care for race conditions when the same Terraform manages IAM policy on the Cloud KMS crypto key. See the data source page for more details.
191193

194+
<a name="nested_custom_placement_config"></a>The `custom_placement_config` block supports:
195+
196+
* `data_locations` - (Required) The list of individual regions that comprise a dual-region bucket. See [Cloud Storage bucket locations](https://cloud.google.com/storage/docs/dual-regions#availability) for a list of acceptable regions. **Note**: If any of the data_locations changes, it will [recreate the bucket](https://cloud.google.com/storage/docs/locations#key-concepts).
197+
192198
## Attributes Reference
193199

194200
In addition to the arguments listed above, the following computed attributes are

0 commit comments

Comments
 (0)