Skip to content

Commit d768791

Browse files
modular-magicianrileykarson
authored andcommitted
Pub/Sub Geo Restriction (#4131)
Signed-off-by: Modular Magician <[email protected]>
1 parent 202d226 commit d768791

4 files changed

+169
-1
lines changed

google/resource_pubsub_topic.go

+75
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ func resourcePubsubTopic() *schema.Resource {
5858
Optional: true,
5959
Elem: &schema.Schema{Type: schema.TypeString},
6060
},
61+
"message_storage_policy": {
62+
Type: schema.TypeList,
63+
Optional: true,
64+
MaxItems: 1,
65+
Elem: &schema.Resource{
66+
Schema: map[string]*schema.Schema{
67+
"allowed_persistence_regions": {
68+
Type: schema.TypeList,
69+
Required: true,
70+
Elem: &schema.Schema{
71+
Type: schema.TypeString,
72+
},
73+
},
74+
},
75+
},
76+
},
6177
"project": {
6278
Type: schema.TypeString,
6379
Optional: true,
@@ -90,6 +106,12 @@ func resourcePubsubTopicCreate(d *schema.ResourceData, meta interface{}) error {
90106
} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
91107
obj["labels"] = labelsProp
92108
}
109+
messageStoragePolicyProp, err := expandPubsubTopicMessageStoragePolicy(d.Get("message_storage_policy"), d, config)
110+
if err != nil {
111+
return err
112+
} else if v, ok := d.GetOkExists("message_storage_policy"); !isEmptyValue(reflect.ValueOf(messageStoragePolicyProp)) && (ok || !reflect.DeepEqual(v, messageStoragePolicyProp)) {
113+
obj["messageStoragePolicy"] = messageStoragePolicyProp
114+
}
93115

94116
obj, err = resourcePubsubTopicEncoder(d, meta, obj)
95117
if err != nil {
@@ -149,6 +171,9 @@ func resourcePubsubTopicRead(d *schema.ResourceData, meta interface{}) error {
149171
if err := d.Set("labels", flattenPubsubTopicLabels(res["labels"], d)); err != nil {
150172
return fmt.Errorf("Error reading Topic: %s", err)
151173
}
174+
if err := d.Set("message_storage_policy", flattenPubsubTopicMessageStoragePolicy(res["messageStoragePolicy"], d)); err != nil {
175+
return fmt.Errorf("Error reading Topic: %s", err)
176+
}
152177

153178
return nil
154179
}
@@ -163,6 +188,12 @@ func resourcePubsubTopicUpdate(d *schema.ResourceData, meta interface{}) error {
163188
} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
164189
obj["labels"] = labelsProp
165190
}
191+
messageStoragePolicyProp, err := expandPubsubTopicMessageStoragePolicy(d.Get("message_storage_policy"), d, config)
192+
if err != nil {
193+
return err
194+
} else if v, ok := d.GetOkExists("message_storage_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, messageStoragePolicyProp)) {
195+
obj["messageStoragePolicy"] = messageStoragePolicyProp
196+
}
166197

167198
obj, err = resourcePubsubTopicUpdateEncoder(d, meta, obj)
168199
if err != nil {
@@ -180,6 +211,10 @@ func resourcePubsubTopicUpdate(d *schema.ResourceData, meta interface{}) error {
180211
if d.HasChange("labels") {
181212
updateMask = append(updateMask, "labels")
182213
}
214+
215+
if d.HasChange("message_storage_policy") {
216+
updateMask = append(updateMask, "messageStoragePolicy")
217+
}
183218
// updateMask is a URL parameter but not present in the schema, so replaceVars
184219
// won't set it
185220
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
@@ -249,6 +284,23 @@ func flattenPubsubTopicLabels(v interface{}, d *schema.ResourceData) interface{}
249284
return v
250285
}
251286

287+
func flattenPubsubTopicMessageStoragePolicy(v interface{}, d *schema.ResourceData) interface{} {
288+
if v == nil {
289+
return nil
290+
}
291+
original := v.(map[string]interface{})
292+
if len(original) == 0 {
293+
return nil
294+
}
295+
transformed := make(map[string]interface{})
296+
transformed["allowed_persistence_regions"] =
297+
flattenPubsubTopicMessageStoragePolicyAllowedPersistenceRegions(original["allowedPersistenceRegions"], d)
298+
return []interface{}{transformed}
299+
}
300+
func flattenPubsubTopicMessageStoragePolicyAllowedPersistenceRegions(v interface{}, d *schema.ResourceData) interface{} {
301+
return v
302+
}
303+
252304
func expandPubsubTopicName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
253305
return GetResourceNameFromSelfLink(v.(string)), nil
254306
}
@@ -268,6 +320,29 @@ func expandPubsubTopicLabels(v interface{}, d TerraformResourceData, config *Con
268320
return m, nil
269321
}
270322

323+
func expandPubsubTopicMessageStoragePolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
324+
l := v.([]interface{})
325+
if len(l) == 0 || l[0] == nil {
326+
return nil, nil
327+
}
328+
raw := l[0]
329+
original := raw.(map[string]interface{})
330+
transformed := make(map[string]interface{})
331+
332+
transformedAllowedPersistenceRegions, err := expandPubsubTopicMessageStoragePolicyAllowedPersistenceRegions(original["allowed_persistence_regions"], d, config)
333+
if err != nil {
334+
return nil, err
335+
} else if val := reflect.ValueOf(transformedAllowedPersistenceRegions); val.IsValid() && !isEmptyValue(val) {
336+
transformed["allowedPersistenceRegions"] = transformedAllowedPersistenceRegions
337+
}
338+
339+
return transformed, nil
340+
}
341+
342+
func expandPubsubTopicMessageStoragePolicyAllowedPersistenceRegions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
343+
return v, nil
344+
}
345+
271346
func resourcePubsubTopicEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
272347
delete(obj, "name")
273348
return obj, nil

google/resource_pubsub_topic_generated_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,45 @@ resource "google_pubsub_topic" "example" {
6060
`, context)
6161
}
6262

63+
func TestAccPubsubTopic_pubsubTopicGeoRestrictedExample(t *testing.T) {
64+
t.Parallel()
65+
66+
context := map[string]interface{}{
67+
"random_suffix": acctest.RandString(10),
68+
}
69+
70+
resource.Test(t, resource.TestCase{
71+
PreCheck: func() { testAccPreCheck(t) },
72+
Providers: testAccProviders,
73+
CheckDestroy: testAccCheckPubsubTopicDestroy,
74+
Steps: []resource.TestStep{
75+
{
76+
Config: testAccPubsubTopic_pubsubTopicGeoRestrictedExample(context),
77+
},
78+
{
79+
ResourceName: "google_pubsub_topic.example",
80+
ImportState: true,
81+
ImportStateVerify: true,
82+
},
83+
},
84+
})
85+
}
86+
87+
func testAccPubsubTopic_pubsubTopicGeoRestrictedExample(context map[string]interface{}) string {
88+
return Nprintf(`
89+
resource "google_pubsub_topic" "example" {
90+
name = "example-topic%{random_suffix}"
91+
92+
message_storage_policy {
93+
allowed_persistence_regions = [
94+
"europe-west3",
95+
]
96+
}
97+
98+
}
99+
`, context)
100+
}
101+
63102
func testAccCheckPubsubTopicDestroy(s *terraform.State) error {
64103
for name, rs := range s.RootModule().Resources {
65104
if rs.Type != "google_pubsub_topic" {

google/resource_pubsub_topic_test.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func TestAccPubsubTopic_update(t *testing.T) {
2828
ImportStateVerify: true,
2929
},
3030
{
31-
Config: testAccPubsubTopic_update(topic, "wibble", "wobble"),
31+
Config: testAccPubsubTopic_updateWithRegion(topic, "wibble", "wobble", "us-central1"),
3232
},
3333
{
3434
ResourceName: "google_pubsub_topic.foo",
@@ -74,6 +74,23 @@ resource "google_pubsub_topic" "foo" {
7474
`, topic, key, value)
7575
}
7676

77+
func testAccPubsubTopic_updateWithRegion(topic, key, value, region string) string {
78+
return fmt.Sprintf(`
79+
resource "google_pubsub_topic" "foo" {
80+
name = "%s"
81+
labels = {
82+
%s = "%s"
83+
}
84+
85+
message_storage_policy {
86+
allowed_persistence_regions = [
87+
"%s",
88+
]
89+
}
90+
}
91+
`, topic, key, value, region)
92+
}
93+
7794
func testAccPubsubTopic_cmek(pid, topicName, kmsKey string) string {
7895
return fmt.Sprintf(`
7996
data "google_project" "project" {

website/docs/r/pubsub_topic.html.markdown

+37
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,26 @@ resource "google_kms_key_ring" "key_ring" {
6666
location = "global"
6767
}
6868
```
69+
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
70+
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.jpy.wang%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=pubsub_topic_geo_restricted&cloudshell_image=gcr.io%2Fgraphite-cloud-shell-images%2Fterraform%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
71+
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
72+
</a>
73+
</div>
74+
## Example Usage - Pubsub Topic Geo Restricted
75+
76+
77+
```hcl
78+
resource "google_pubsub_topic" "example" {
79+
name = "example-topic"
80+
81+
message_storage_policy {
82+
allowed_persistence_regions = [
83+
"europe-west3",
84+
]
85+
}
86+
87+
}
88+
```
6989

7090
## Argument Reference
7191

@@ -92,10 +112,27 @@ The following arguments are supported:
92112
(Optional)
93113
A set of key/value label pairs to assign to this Topic.
94114

115+
* `message_storage_policy` -
116+
(Optional)
117+
Policy constraining the set of Google Cloud Platform regions where
118+
messages published to the topic may be stored. If not present, then no
119+
constraints are in effect. Structure is documented below.
120+
95121
* `project` - (Optional) The ID of the project in which the resource belongs.
96122
If it is not provided, the provider project is used.
97123

98124

125+
The `message_storage_policy` block supports:
126+
127+
* `allowed_persistence_regions` -
128+
(Required)
129+
A list of IDs of GCP regions where messages that are published to
130+
the topic may be persisted in storage. Messages published by
131+
publishers running in non-allowed GCP regions (or running outside
132+
of GCP altogether) will be routed for storage in one of the
133+
allowed regions. An empty list means that no regions are allowed,
134+
and is not a valid configuration.
135+
99136

100137
## Timeouts
101138

0 commit comments

Comments
 (0)