Skip to content

Commit c71e9c0

Browse files
authored
Adding in automated backup config to memorystore (#13403)
1 parent 2674ce0 commit c71e9c0

File tree

5 files changed

+343
-1
lines changed

5 files changed

+343
-1
lines changed

mmv1/products/memorystore/Instance.yaml

+31
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,37 @@ properties:
193193
description:
194194
"Output only. Target node type for the instance."
195195
output: true
196+
- name: 'automatedBackupConfig'
197+
type: NestedObject
198+
description: "The automated backup config for a instance."
199+
custom_flatten: 'templates/terraform/custom_flatten/memorystore_instance_automated_backup_config.go.tmpl'
200+
custom_expand: 'templates/terraform/custom_expand/memorystore_instance_automated_backup_config.go.tmpl'
201+
properties:
202+
- name: 'fixedFrequencySchedule'
203+
type: NestedObject
204+
description: "Trigger automated backups at a fixed frequency."
205+
required: true
206+
properties:
207+
- name: 'startTime'
208+
type: NestedObject
209+
description: |
210+
The start time of every automated backup in UTC.
211+
It must be set to the start of an hour. This field is required.
212+
required: true
213+
properties:
214+
- name: 'hours'
215+
type: Integer
216+
description: |
217+
Hours of a day in 24 hour format. Must be greater than or equal to 0 and typically must be less than or equal to 23.
218+
An API may choose to allow the value "24:00:00" for scenarios like business closing time.
219+
required: true
220+
- name: 'retention'
221+
type: String
222+
description: |
223+
How long to keep automated backups before the backups are deleted.
224+
The value should be between 1 day and 365 days. If not specified, the default value is 35 days.
225+
A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". The default_value is "3024000s"
226+
required: true
196227
- name: 'uid'
197228
type: String
198229
description: "Output only. System assigned, unique identifier for the instance. "
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
{{/*
2+
The license inside this block applies to this file
3+
Copyright 2025 Google Inc.
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/ -}}
13+
func expandMemorystoreInstanceAutomatedBackupConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
14+
l := v.([]interface{})
15+
16+
// The automated_backup_config block is not specified, so automatedBackupMode should be DISABLED
17+
transformed := make(map[string]interface{})
18+
if len(d.Get("automated_backup_config").([]interface{})) < 1 {
19+
transformed["automatedBackupMode"] = "DISABLED"
20+
return transformed, nil
21+
}
22+
if len(l) == 0 || l[0] == nil {
23+
return nil, nil
24+
}
25+
raw := l[0]
26+
original := raw.(map[string]interface{})
27+
28+
// The automated_backup_config block is specified, so automatedBackupMode should be ENABLED
29+
transformed["automatedBackupMode"] = "ENABLED"
30+
transformedFixedFrequencySchedule, err := expandMemorystoreInstanceAutomatedBackupConfigFixedFrequencySchedule(original["fixed_frequency_schedule"], d, config)
31+
if err != nil {
32+
return nil, err
33+
} else if val := reflect.ValueOf(transformedFixedFrequencySchedule); val.IsValid() && !tpgresource.IsEmptyValue(val) {
34+
transformed["fixedFrequencySchedule"] = transformedFixedFrequencySchedule
35+
}
36+
37+
transformedRetention, err := expandMemorystoreInstanceAutomatedBackupConfigRetention(original["retention"], d, config)
38+
if err != nil {
39+
return nil, err
40+
} else if val := reflect.ValueOf(transformedRetention); val.IsValid() && !tpgresource.IsEmptyValue(val) {
41+
transformed["retention"] = transformedRetention
42+
}
43+
44+
return transformed, nil
45+
}
46+
47+
func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencySchedule(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
48+
l := v.([]interface{})
49+
if len(l) == 0 || l[0] == nil {
50+
return nil, nil
51+
}
52+
raw := l[0]
53+
original := raw.(map[string]interface{})
54+
transformed := make(map[string]interface{})
55+
56+
transformedStartTime, err := expand{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTime(original["start_time"], d, config)
57+
if err != nil {
58+
return nil, err
59+
} else if val := reflect.ValueOf(transformedStartTime); val.IsValid() && !tpgresource.IsEmptyValue(val) {
60+
transformed["startTime"] = transformedStartTime
61+
}
62+
63+
return transformed, nil
64+
}
65+
66+
func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTime(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
67+
l := v.([]interface{})
68+
if len(l) == 0 || l[0] == nil {
69+
return nil, nil
70+
}
71+
raw := l[0]
72+
original := raw.(map[string]interface{})
73+
transformed := make(map[string]interface{})
74+
75+
transformedHours, err := expand{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTimeHours(original["hours"], d, config)
76+
if err != nil {
77+
return nil, err
78+
} else if val := reflect.ValueOf(transformedHours); val.IsValid() && !tpgresource.IsEmptyValue(val) {
79+
transformed["hours"] = transformedHours
80+
}
81+
82+
return transformed, nil
83+
}
84+
85+
func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTimeHours(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
86+
return v, nil
87+
}
88+
89+
func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}Retention(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
90+
return v, nil
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{{/*
2+
The license inside this block applies to this file
3+
Copyright 2025 Google Inc.
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
13+
*/ -}}
14+
func flattenMemorystoreInstanceAutomatedBackupConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
15+
if v == nil {
16+
return nil
17+
}
18+
original := v.(map[string]interface{})
19+
if len(original) == 0 {
20+
return nil
21+
}
22+
transformed := make(map[string]interface{})
23+
// if automated_backup_config is not defined
24+
25+
if original["automatedBackupMode"] == "DISABLED" {
26+
return nil
27+
}
28+
29+
transformed["fixed_frequency_schedule"] =
30+
flattenMemorystoreInstanceAutomatedBackupConfigFixedFrequencySchedule(original["fixedFrequencySchedule"], d, config)
31+
transformed["retention"] =
32+
flattenMemorystoreInstanceAutomatedBackupConfigRetention(original["retention"], d, config)
33+
return []interface{}{transformed}
34+
}
35+
func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencySchedule(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
36+
if v == nil {
37+
return nil
38+
}
39+
original := v.(map[string]interface{})
40+
if len(original) == 0 {
41+
return nil
42+
}
43+
44+
transformed := make(map[string]interface{})
45+
transformed["start_time"] =
46+
flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTime(original["startTime"], d, config)
47+
return []interface{}{transformed}
48+
}
49+
func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
50+
if v == nil {
51+
return nil
52+
}
53+
original := v.(map[string]interface{})
54+
if len(original) == 0 {
55+
return nil
56+
}
57+
58+
transformed := make(map[string]interface{})
59+
transformed["hours"] =
60+
flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTimeHours(original["hours"], d, config)
61+
return []interface{}{transformed}
62+
}
63+
func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}FixedFrequencyScheduleStartTimeHours(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
64+
// Handles the string fixed64 format
65+
if strVal, ok := v.(string); ok {
66+
if intVal, err := tpgresource.StringToFixed64(strVal); err == nil {
67+
return intVal
68+
}
69+
}
70+
71+
// number values are represented as float64
72+
if floatVal, ok := v.(float64); ok {
73+
intVal := int(floatVal)
74+
return intVal
75+
}
76+
77+
return v // let terraform core handle it otherwise
78+
}
79+
80+
func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}Retention(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
81+
82+
return v
83+
}

mmv1/templates/terraform/encoders/memorystore_instance.go.tmpl

+7
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,12 @@ for _, raw := range l {
2323

2424
req = append(req, connectionReq)
2525
}
26+
2627
obj["pscAutoConnections"] = req
28+
// if the automated_backup_config is not defined, automatedBackupMode needs to be passed and set to DISABLED in the expand
29+
if obj["automatedBackupConfig"] == nil {
30+
config := meta.(*transport_tpg.Config)
31+
automatedBackupConfigProp, _ := expandMemorystoreInstanceAutomatedBackupConfig(d.Get("automated_backup_config"), d, config)
32+
obj["automatedBackupConfig"] = automatedBackupConfigProp
33+
}
2734
return obj, nil

mmv1/third_party/terraform/services/memorystore/resource_memorystore_instance_test.go

+131-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,137 @@ func TestAccMemorystoreInstance_updateReplicaCount(t *testing.T) {
4646
})
4747
}
4848

49-
// Validate that shard count is updated for the instance
49+
func TestAccMemorystoreInstance_automatedBackupConfig(t *testing.T) {
50+
t.Parallel()
51+
52+
context := map[string]interface{}{
53+
"random_suffix": acctest.RandString(t, 10),
54+
}
55+
56+
acctest.VcrTest(t, resource.TestCase{
57+
PreCheck: func() { acctest.AccTestPreCheck(t) },
58+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
59+
CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t),
60+
Steps: []resource.TestStep{
61+
{
62+
Config: testAccMemorystoreInstance_automatedBackupConfig(context),
63+
},
64+
{
65+
ResourceName: "google_memorystore_instance.test_abc",
66+
ImportState: true,
67+
ImportStateVerify: true,
68+
},
69+
{
70+
Config: testAccMemorystoreInstance_automatedBackupConfigWithout(context),
71+
},
72+
{
73+
ResourceName: "google_memorystore_instance.test_abc",
74+
ImportState: true,
75+
ImportStateVerify: true,
76+
},
77+
},
78+
})
79+
}
80+
81+
func testAccMemorystoreInstance_automatedBackupConfig(context map[string]interface{}) string {
82+
return acctest.Nprintf(`
83+
// Primary instance
84+
resource "google_memorystore_instance" "test_abc" {
85+
instance_id = "tf-test-instance-abc-%{random_suffix}"
86+
shard_count = 1
87+
location = "us-central1"
88+
replica_count = 0
89+
node_type = "SHARED_CORE_NANO"
90+
deletion_protection_enabled = false
91+
desired_psc_auto_connections {
92+
network = google_compute_network.primary_producer_net.id
93+
project_id = data.google_project.project.project_id
94+
}
95+
automated_backup_config {
96+
retention = "259200s"
97+
fixed_frequency_schedule {
98+
start_time {
99+
hours = 20
100+
}
101+
}
102+
}
103+
depends_on = [ google_network_connectivity_service_connection_policy.primary_policy ]
104+
}
105+
106+
resource "google_network_connectivity_service_connection_policy" "primary_policy" {
107+
name = "tf-test-abc-policy-%{random_suffix}"
108+
location = "us-central1"
109+
service_class = "gcp-memorystore"
110+
description = "my basic service connection policy"
111+
network = google_compute_network.primary_producer_net.id
112+
psc_config {
113+
subnetworks = [google_compute_subnetwork.primary_producer_subnet.id]
114+
}
115+
}
116+
117+
resource "google_compute_subnetwork" "primary_producer_subnet" {
118+
name = "tf-test-abc-%{random_suffix}"
119+
ip_cidr_range = "10.0.4.0/29"
120+
region = "us-central1"
121+
network = google_compute_network.primary_producer_net.id
122+
}
123+
124+
resource "google_compute_network" "primary_producer_net" {
125+
name = "tf-test-abc-net-%{random_suffix}"
126+
auto_create_subnetworks = false
127+
}
128+
129+
data "google_project" "project" {
130+
}
131+
`, context)
132+
}
133+
134+
func testAccMemorystoreInstance_automatedBackupConfigWithout(context map[string]interface{}) string {
135+
return acctest.Nprintf(`
136+
// Primary instance
137+
resource "google_memorystore_instance" "test_abc" {
138+
instance_id = "tf-test-instance-abc-%{random_suffix}"
139+
shard_count = 1
140+
location = "us-central1"
141+
replica_count = 0
142+
node_type = "SHARED_CORE_NANO"
143+
deletion_protection_enabled = false
144+
desired_psc_auto_connections {
145+
network = google_compute_network.primary_producer_net.id
146+
project_id = data.google_project.project.project_id
147+
}
148+
depends_on = [ google_network_connectivity_service_connection_policy.primary_policy ]
149+
}
150+
151+
resource "google_network_connectivity_service_connection_policy" "primary_policy" {
152+
name = "tf-test-abc-policy-%{random_suffix}"
153+
location = "us-central1"
154+
service_class = "gcp-memorystore"
155+
description = "my basic service connection policy"
156+
network = google_compute_network.primary_producer_net.id
157+
psc_config {
158+
subnetworks = [google_compute_subnetwork.primary_producer_subnet.id]
159+
}
160+
}
161+
162+
resource "google_compute_subnetwork" "primary_producer_subnet" {
163+
name = "tf-test-abc-%{random_suffix}"
164+
ip_cidr_range = "10.0.4.0/29"
165+
region = "us-central1"
166+
network = google_compute_network.primary_producer_net.id
167+
}
168+
169+
resource "google_compute_network" "primary_producer_net" {
170+
name = "tf-test-abc-net-%{random_suffix}"
171+
auto_create_subnetworks = false
172+
}
173+
174+
data "google_project" "project" {
175+
}
176+
`, context)
177+
}
178+
179+
// Validate that shard count is updated for the cluster
50180
func TestAccMemorystoreInstance_updateShardCount(t *testing.T) {
51181
t.Parallel()
52182

0 commit comments

Comments
 (0)