Skip to content

Commit 57769ea

Browse files
himanikhNickElliot
authored andcommitted
Redis cluster Multi vpc support (GoogleCloudPlatform#12548)
Co-authored-by: Nick Elliot <[email protected]>
1 parent 695ab82 commit 57769ea

6 files changed

+825
-1
lines changed

mmv1/products/redis/Cluster.yaml

+27-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ references:
2121
api: 'https://cloud.google.com/memorystore/docs/cluster/reference/rest/v1/projects.locations.clusters'
2222
docs:
2323
note: |
24+
For [Multiple VPC Networking](https://cloud.google.com/memorystore/docs/cluster/about-multiple-vpc-networking) if you want to use
25+
[User-registered PSC Connections](https://cloud.google.com/memorystore/docs/cluster/about-multiple-vpc-networking#psc_connection_types),
26+
then please use `google_redis_cluster_user_created_connections` resource.
27+
2428
For [Cross Region Replication](https://cloud.google.com/memorystore/docs/cluster/about-cross-region-replication), please follow the instructions below for performing certain update and failover (switchover and detach) operations
2529
2630
**Cross Region Replication**
@@ -263,6 +267,7 @@ properties:
263267
type: NestedObject
264268
description: Immutable. Zone distribution config for Memorystore Redis cluster.
265269
immutable: true
270+
default_from_api: true
266271
properties:
267272
- name: 'mode'
268273
type: Enum
@@ -283,7 +288,6 @@ properties:
283288
Required. Each PscConfig configures the consumer network where two
284289
network addresses will be designated to the cluster for client access.
285290
Currently, only one PscConfig is supported.
286-
required: true
287291
ignore_read: true
288292
item_type:
289293
type: NestedObject
@@ -687,3 +691,25 @@ properties:
687691
description: |
688692
The last time cross cluster replication config was updated.
689693
output: true
694+
- name: 'pscServiceAttachments'
695+
type: Array
696+
description: Service attachment details to configure Psc connections.
697+
output: true
698+
item_type:
699+
type: NestedObject
700+
description: |
701+
Configuration of a service attachment of the cluster, for creating PSC connections.
702+
properties:
703+
- name: 'serviceAttachment'
704+
type: String
705+
output: true
706+
description: |
707+
Service attachment URI which your self-created PscConnection should use as
708+
- name: 'connectionType'
709+
type: Enum
710+
output: true
711+
enum_values:
712+
- 'CONNECTION_TYPE_READER'
713+
- 'CONNECTION_TYPE_PRIMARY'
714+
- 'CONNECTION_TYPE_DISCOVERY'
715+
description: Type of a PSC connection targeting this service attachment.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Copyright 2024 Google Inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
---
15+
name: 'ClusterUserCreatedConnections'
16+
api_resource_type_kind: Cluster
17+
description: |
18+
Manages user created connections for Redis cluster
19+
docs:
20+
note: |
21+
If you remove a connections item from the resource, the corresponding forwarding rule will no longer be functioning.
22+
If the corresponding forwarding rule is represented in your terraform configuration it is recommended to delete that
23+
`google_compute_forwarding_rule` resource at the same time.
24+
references:
25+
guides:
26+
api: 'https://cloud.google.com/memorystore/docs/cluster/reference/rest/v1/projects.locations.clusters'
27+
id_format: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}'
28+
base_url: 'projects/{{project}}/locations/{{region}}/clusters'
29+
self_link: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}'
30+
create_url: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}?updateMask=cluster_endpoints'
31+
create_verb: PATCH
32+
update_verb: 'PATCH'
33+
update_url: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}?updateMask=cluster_endpoints'
34+
update_mask: true
35+
custom_code:
36+
custom_delete: 'templates/terraform/custom_delete/redis_cluster_user_created_connections.go.tmpl'
37+
timeouts:
38+
insert_minutes: 60
39+
update_minutes: 120
40+
delete_minutes: 30
41+
autogen_async: true
42+
async:
43+
actions: ['create', 'delete', 'update']
44+
type: 'OpAsync'
45+
operation:
46+
base_url: '{{op_id}}'
47+
result:
48+
resource_inside_response: false
49+
examples:
50+
- name: 'redis_cluster_user_created_connections'
51+
primary_resource_id: 'cluster-user-conn'
52+
vars:
53+
cluster_name: 'cluster-user-conn'
54+
network1_name: 'net1'
55+
subnet_network1_name: 'subnet-net1'
56+
ip1_network1_name: 'ip1-net1'
57+
ip2_network1_name: 'ip2-net1'
58+
forwarding_rule1_network1_name: 'fwd1-net1'
59+
forwarding_rule2_network1_name: 'fwd2-net1'
60+
network2_name: 'network2'
61+
subnet_network2_name: 'subnet-net2'
62+
ip1_network2_name: 'ip1-net2'
63+
ip2_network2_name: 'ip2-net2'
64+
forwarding_rule1_network2_name: 'fwd1-net2'
65+
forwarding_rule2_network2_name: 'fwd2-net2'
66+
- name: 'redis_cluster_user_and_auto_created_connections'
67+
primary_resource_id: 'cluster-user-auto-conn'
68+
vars:
69+
cluster_name: 'cluster-user-auto-conn'
70+
network1_name: 'net1'
71+
subnet_network1_name: 'subnet-net1'
72+
policy_name: 'scpolicy'
73+
network2_name: 'network2'
74+
subnet_network2_name: 'subnet-net2'
75+
ip1_network2_name: 'ip1-net2'
76+
ip2_network2_name: 'ip2-net2'
77+
forwarding_rule1_network2_name: 'fwd1-net2'
78+
forwarding_rule2_network2_name: 'fwd2-net2'
79+
parameters:
80+
- name: 'name'
81+
type: String
82+
description: |
83+
The name of the Redis cluster these endpoints should be added to.
84+
required: true
85+
url_param_only: true
86+
- name: 'region'
87+
type: String
88+
description: |
89+
The name of the region of the Redis cluster these endpoints should be added to.
90+
url_param_only: true
91+
required: true
92+
properties:
93+
- name: 'clusterEndpoints'
94+
type: Array
95+
description: "A list of cluster endpoints"
96+
item_type:
97+
type: NestedObject
98+
description: |
99+
ClusterEndpoint consists of PSC connections that are created
100+
as a group in each VPC network for accessing the cluster. In each group,
101+
there shall be one connection for each service attachment in the cluster.
102+
properties:
103+
- name: connections
104+
type: Array
105+
item_type:
106+
type: NestedObject
107+
name: 'connections'
108+
properties:
109+
- name: 'pscConnection'
110+
type: NestedObject
111+
description: |
112+
Detailed information of a PSC connection that is created by the customer
113+
who owns the cluster.
114+
properties:
115+
- name: 'pscConnectionId'
116+
type: String
117+
description:
118+
"The PSC connection id of the forwarding rule connected
119+
to the\nservice attachment."
120+
required: true
121+
- name: 'address'
122+
type: String
123+
description:
124+
"The IP allocated on the consumer network for the
125+
PSC forwarding rule. "
126+
required: true
127+
- name: 'forwardingRule'
128+
type: String
129+
description: "The URI of the consumer side forwarding rule.\nFormat:\nprojects/{project}/regions/{region}/forwardingRules/{forwarding_rule} "
130+
required: true
131+
- name: 'projectId'
132+
type: String
133+
description:
134+
"The consumer project_id where the forwarding rule is
135+
created from. "
136+
default_from_api: true
137+
- name: 'network'
138+
type: String
139+
description:
140+
"The consumer network where the IP address resides, in
141+
the form of\nprojects/{project_id}/global/networks/{network_id}. "
142+
required: true
143+
- name: 'serviceAttachment'
144+
type: String
145+
description:
146+
"The service attachment which is the target of the PSC connection, in the form of
147+
projects/{project-id}/regions/{region}/serviceAttachments/{service-attachment-id}."
148+
required: true
149+
- name: 'pscConnectionStatus'
150+
type: Enum
151+
description:
152+
"Output Only. The status of the PSC connection: whether a connection exists and ACTIVE or it no longer exists.
153+
\n Possible values:\n ACTIVE \n NOT_FOUND"
154+
output: true
155+
enum_values:
156+
- 'ACTIVE'
157+
- 'NOT_FOUND'
158+
- name: 'connectionType'
159+
type: Enum
160+
description:
161+
"Output Only. Type of a PSC Connection.
162+
\n Possible values:\n CONNECTION_TYPE_DISCOVERY \n CONNECTION_TYPE_PRIMARY \n CONNECTION_TYPE_READER"
163+
output: true
164+
enum_values:
165+
- 'CONNECTION_TYPE_READER'
166+
- 'CONNECTION_TYPE_PRIMARY'
167+
- 'CONNECTION_TYPE_DISCOVERY'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
billingProject := ""
2+
3+
project, err := tpgresource.GetProject(d, config)
4+
if err != nil {
5+
return fmt.Errorf("Error fetching project for ClusterUserCreatedConnections: %s", err)
6+
}
7+
billingProject = project
8+
9+
obj := make(map[string]interface{})
10+
// not setting clusterEndpoints in obj
11+
12+
13+
url, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}RedisBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}region{{"}}"}}/clusters/{{"{{"}}name{{"}}"}}")
14+
if err != nil {
15+
return err
16+
}
17+
18+
log.Printf("[DEBUG] Updating ClusterUserCreatedConnections %q: %#v", d.Id(), obj)
19+
headers := make(http.Header)
20+
21+
updateMask := []string{}
22+
updateMask = append(updateMask, "clusterEndpoints")
23+
// updateMask is a URL parameter but not present in the schema, so ReplaceVars
24+
// won't set it
25+
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
26+
if err != nil {
27+
return err
28+
}
29+
30+
// err == nil indicates that the billing_project value was found
31+
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
32+
billingProject = bp
33+
}
34+
35+
obj["async_cluster_endpoints_deletion_enabled"] = true
36+
37+
// if updateMask is empty we are not updating anything so skip the post
38+
if len(updateMask) > 0 {
39+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
40+
Config: config,
41+
Method: "PATCH",
42+
Project: billingProject,
43+
RawURL: url,
44+
UserAgent: userAgent,
45+
Body: obj,
46+
Timeout: d.Timeout(schema.TimeoutUpdate),
47+
Headers: headers,
48+
})
49+
50+
if err != nil {
51+
return fmt.Errorf("Error updating ClusterUserCreatedConnections %q: %s", d.Id(), err)
52+
} else {
53+
log.Printf("[DEBUG] Finished updating ClusterUserCreatedConnections %q: %#v", d.Id(), res)
54+
}
55+
56+
err = RedisOperationWaitTime(
57+
config, res, project, "Updating ClusterUserCreatedConnections", userAgent,
58+
d.Timeout(schema.TimeoutUpdate))
59+
60+
if err != nil {
61+
return err
62+
}
63+
}
64+
65+
return resourceRedisClusterUserCreatedConnectionsRead(d, meta)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
resource "google_redis_cluster_user_created_connections" "{{$.PrimaryResourceId}}" {
2+
name = "{{index $.Vars "cluster_name"}}"
3+
region = "us-central1"
4+
cluster_endpoints {
5+
connections {
6+
psc_connection {
7+
psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network2.psc_connection_id
8+
address = google_compute_address.ip1_network2.address
9+
forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network2.id
10+
network = google_compute_network.network2.id
11+
service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment
12+
}
13+
}
14+
connections {
15+
psc_connection {
16+
psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network2.psc_connection_id
17+
address = google_compute_address.ip2_network2.address
18+
forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network2.id
19+
network = google_compute_network.network2.id
20+
service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment
21+
}
22+
}
23+
}
24+
}
25+
26+
resource "google_compute_forwarding_rule" "forwarding_rule1_network2" {
27+
name = "{{index $.Vars "forwarding_rule1_network2_name"}}"
28+
region = "us-central1"
29+
ip_address = google_compute_address.ip1_network2.id
30+
load_balancing_scheme = ""
31+
network = google_compute_network.network2.id
32+
target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment
33+
}
34+
35+
resource "google_compute_forwarding_rule" "forwarding_rule2_network2" {
36+
name = "{{index $.Vars "forwarding_rule2_network2_name"}}"
37+
region = "us-central1"
38+
ip_address = google_compute_address.ip2_network2.id
39+
load_balancing_scheme = ""
40+
network = google_compute_network.network2.id
41+
target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment
42+
}
43+
44+
resource "google_compute_address" "ip1_network2" {
45+
name = "{{index $.Vars "ip1_network2_name"}}"
46+
region = "us-central1"
47+
subnetwork = google_compute_subnetwork.subnet_network2.id
48+
address_type = "INTERNAL"
49+
purpose = "GCE_ENDPOINT"
50+
}
51+
52+
resource "google_compute_address" "ip2_network2" {
53+
name = "{{index $.Vars "ip2_network2_name"}}"
54+
region = "us-central1"
55+
subnetwork = google_compute_subnetwork.subnet_network2.id
56+
address_type = "INTERNAL"
57+
purpose = "GCE_ENDPOINT"
58+
}
59+
60+
resource "google_compute_subnetwork" "subnet_network2" {
61+
name = "{{index $.Vars "subnet_network2_name"}}"
62+
ip_cidr_range = "10.0.0.248/29"
63+
region = "us-central1"
64+
network = google_compute_network.network2.id
65+
}
66+
67+
resource "google_compute_network" "network2" {
68+
name = "{{index $.Vars "network2_name"}}"
69+
auto_create_subnetworks = false
70+
}
71+
72+
// redis cluster without endpoint
73+
resource "google_redis_cluster" "{{$.PrimaryResourceId}}" {
74+
name = "{{index $.Vars "cluster_name"}}"
75+
shard_count = 3
76+
region = "us-central1"
77+
replica_count = 0
78+
deletion_protection_enabled = false
79+
psc_configs {
80+
network = google_compute_network.network1.id
81+
}
82+
depends_on = [
83+
google_network_connectivity_service_connection_policy.default
84+
]
85+
}
86+
87+
resource "google_network_connectivity_service_connection_policy" "default" {
88+
name = "{{index $.Vars "policy_name"}}"
89+
location = "us-central1"
90+
service_class = "gcp-memorystore-redis"
91+
description = "my basic service connection policy"
92+
network = google_compute_network.network1.id
93+
psc_config {
94+
subnetworks = [google_compute_subnetwork.subnet_network1.id]
95+
}
96+
}
97+
98+
resource "google_compute_subnetwork" "subnet_network1" {
99+
name = "{{index $.Vars "subnet_network1_name"}}"
100+
ip_cidr_range = "10.0.0.248/29"
101+
region = "us-central1"
102+
network = google_compute_network.network1.id
103+
}
104+
105+
resource "google_compute_network" "network1" {
106+
name = "{{index $.Vars "network1_name"}}"
107+
auto_create_subnetworks = false
108+
}

0 commit comments

Comments
 (0)