diff --git a/mmv1/products/redis/Cluster.yaml b/mmv1/products/redis/Cluster.yaml index 9a841e1ee0cf..d4b315e99816 100644 --- a/mmv1/products/redis/Cluster.yaml +++ b/mmv1/products/redis/Cluster.yaml @@ -21,6 +21,10 @@ references: api: 'https://cloud.google.com/memorystore/docs/cluster/reference/rest/v1/projects.locations.clusters' docs: note: | + For [Multiple VPC Networking](https://cloud.google.com/memorystore/docs/cluster/about-multiple-vpc-networking) if you want to use + [User-registered PSC Connections](https://cloud.google.com/memorystore/docs/cluster/about-multiple-vpc-networking#psc_connection_types), + then please use `google_redis_cluster_user_created_connections` resource. + 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 **Cross Region Replication** @@ -263,6 +267,7 @@ properties: type: NestedObject description: Immutable. Zone distribution config for Memorystore Redis cluster. immutable: true + default_from_api: true properties: - name: 'mode' type: Enum @@ -283,7 +288,6 @@ properties: Required. Each PscConfig configures the consumer network where two network addresses will be designated to the cluster for client access. Currently, only one PscConfig is supported. - required: true ignore_read: true item_type: type: NestedObject @@ -687,3 +691,25 @@ properties: description: | The last time cross cluster replication config was updated. output: true + - name: 'pscServiceAttachments' + type: Array + description: Service attachment details to configure Psc connections. + output: true + item_type: + type: NestedObject + description: | + Configuration of a service attachment of the cluster, for creating PSC connections. + properties: + - name: 'serviceAttachment' + type: String + output: true + description: | + Service attachment URI which your self-created PscConnection should use as + - name: 'connectionType' + type: Enum + output: true + enum_values: + - 'CONNECTION_TYPE_READER' + - 'CONNECTION_TYPE_PRIMARY' + - 'CONNECTION_TYPE_DISCOVERY' + description: Type of a PSC connection targeting this service attachment. diff --git a/mmv1/products/redis/ClusterUserCreatedConnections.yaml b/mmv1/products/redis/ClusterUserCreatedConnections.yaml new file mode 100644 index 000000000000..9811862f085d --- /dev/null +++ b/mmv1/products/redis/ClusterUserCreatedConnections.yaml @@ -0,0 +1,167 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: 'ClusterUserCreatedConnections' +api_resource_type_kind: Cluster +description: | + Manages user created connections for Redis cluster +docs: + note: | + If you remove a connections item from the resource, the corresponding forwarding rule will no longer be functioning. + If the corresponding forwarding rule is represented in your terraform configuration it is recommended to delete that + `google_compute_forwarding_rule` resource at the same time. +references: + guides: + api: 'https://cloud.google.com/memorystore/docs/cluster/reference/rest/v1/projects.locations.clusters' +id_format: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}' +base_url: 'projects/{{project}}/locations/{{region}}/clusters' +self_link: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}' +create_url: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}?updateMask=cluster_endpoints' +create_verb: PATCH +update_verb: 'PATCH' +update_url: 'projects/{{project}}/locations/{{region}}/clusters/{{name}}?updateMask=cluster_endpoints' +update_mask: true +custom_code: + custom_delete: 'templates/terraform/custom_delete/redis_cluster_user_created_connections.go.tmpl' +timeouts: + insert_minutes: 60 + update_minutes: 120 + delete_minutes: 30 +autogen_async: true +async: + actions: ['create', 'delete', 'update'] + type: 'OpAsync' + operation: + base_url: '{{op_id}}' + result: + resource_inside_response: false +examples: + - name: 'redis_cluster_user_created_connections' + primary_resource_id: 'cluster-user-conn' + vars: + cluster_name: 'cluster-user-conn' + network1_name: 'net1' + subnet_network1_name: 'subnet-net1' + ip1_network1_name: 'ip1-net1' + ip2_network1_name: 'ip2-net1' + forwarding_rule1_network1_name: 'fwd1-net1' + forwarding_rule2_network1_name: 'fwd2-net1' + network2_name: 'network2' + subnet_network2_name: 'subnet-net2' + ip1_network2_name: 'ip1-net2' + ip2_network2_name: 'ip2-net2' + forwarding_rule1_network2_name: 'fwd1-net2' + forwarding_rule2_network2_name: 'fwd2-net2' + - name: 'redis_cluster_user_and_auto_created_connections' + primary_resource_id: 'cluster-user-auto-conn' + vars: + cluster_name: 'cluster-user-auto-conn' + network1_name: 'net1' + subnet_network1_name: 'subnet-net1' + policy_name: 'scpolicy' + network2_name: 'network2' + subnet_network2_name: 'subnet-net2' + ip1_network2_name: 'ip1-net2' + ip2_network2_name: 'ip2-net2' + forwarding_rule1_network2_name: 'fwd1-net2' + forwarding_rule2_network2_name: 'fwd2-net2' +parameters: + - name: 'name' + type: String + description: | + The name of the Redis cluster these endpoints should be added to. + required: true + url_param_only: true + - name: 'region' + type: String + description: | + The name of the region of the Redis cluster these endpoints should be added to. + url_param_only: true + required: true +properties: + - name: 'clusterEndpoints' + type: Array + description: "A list of cluster endpoints" + item_type: + type: NestedObject + description: | + ClusterEndpoint consists of PSC connections that are created + as a group in each VPC network for accessing the cluster. In each group, + there shall be one connection for each service attachment in the cluster. + properties: + - name: connections + type: Array + item_type: + type: NestedObject + name: 'connections' + properties: + - name: 'pscConnection' + type: NestedObject + description: | + Detailed information of a PSC connection that is created by the customer + who owns the cluster. + properties: + - name: 'pscConnectionId' + type: String + description: + "The PSC connection id of the forwarding rule connected + to the\nservice attachment." + required: true + - name: 'address' + type: String + description: + "The IP allocated on the consumer network for the + PSC forwarding rule. " + required: true + - name: 'forwardingRule' + type: String + description: "The URI of the consumer side forwarding rule.\nFormat:\nprojects/{project}/regions/{region}/forwardingRules/{forwarding_rule} " + required: true + - name: 'projectId' + type: String + description: + "The consumer project_id where the forwarding rule is + created from. " + default_from_api: true + - name: 'network' + type: String + description: + "The consumer network where the IP address resides, in + the form of\nprojects/{project_id}/global/networks/{network_id}. " + required: true + - name: 'serviceAttachment' + type: String + description: + "The service attachment which is the target of the PSC connection, in the form of + projects/{project-id}/regions/{region}/serviceAttachments/{service-attachment-id}." + required: true + - name: 'pscConnectionStatus' + type: Enum + description: + "Output Only. The status of the PSC connection: whether a connection exists and ACTIVE or it no longer exists. + \n Possible values:\n ACTIVE \n NOT_FOUND" + output: true + enum_values: + - 'ACTIVE' + - 'NOT_FOUND' + - name: 'connectionType' + type: Enum + description: + "Output Only. Type of a PSC Connection. + \n Possible values:\n CONNECTION_TYPE_DISCOVERY \n CONNECTION_TYPE_PRIMARY \n CONNECTION_TYPE_READER" + output: true + enum_values: + - 'CONNECTION_TYPE_READER' + - 'CONNECTION_TYPE_PRIMARY' + - 'CONNECTION_TYPE_DISCOVERY' diff --git a/mmv1/templates/terraform/custom_delete/redis_cluster_user_created_connections.go.tmpl b/mmv1/templates/terraform/custom_delete/redis_cluster_user_created_connections.go.tmpl new file mode 100644 index 000000000000..94d42379e41f --- /dev/null +++ b/mmv1/templates/terraform/custom_delete/redis_cluster_user_created_connections.go.tmpl @@ -0,0 +1,65 @@ + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ClusterUserCreatedConnections: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + // not setting clusterEndpoints in obj + + + url, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}RedisBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}region{{"}}"}}/clusters/{{"{{"}}name{{"}}"}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating ClusterUserCreatedConnections %q: %#v", d.Id(), obj) + headers := make(http.Header) + + updateMask := []string{} + updateMask = append(updateMask, "clusterEndpoints") + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + obj["async_cluster_endpoints_deletion_enabled"] = true + + // if updateMask is empty we are not updating anything so skip the post + if len(updateMask) > 0 { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + Headers: headers, + }) + + if err != nil { + return fmt.Errorf("Error updating ClusterUserCreatedConnections %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating ClusterUserCreatedConnections %q: %#v", d.Id(), res) + } + + err = RedisOperationWaitTime( + config, res, project, "Updating ClusterUserCreatedConnections", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + } + + return resourceRedisClusterUserCreatedConnectionsRead(d, meta) \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/redis_cluster_user_and_auto_created_connections.tf.tmpl b/mmv1/templates/terraform/examples/redis_cluster_user_and_auto_created_connections.tf.tmpl new file mode 100644 index 000000000000..d45de4bcce46 --- /dev/null +++ b/mmv1/templates/terraform/examples/redis_cluster_user_and_auto_created_connections.tf.tmpl @@ -0,0 +1,108 @@ +resource "google_redis_cluster_user_created_connections" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "cluster_name"}}" + region = "us-central1" + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network2.psc_connection_id + address = google_compute_address.ip1_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network2.psc_connection_id + address = google_compute_address.ip2_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment + } + } + } +} + +resource "google_compute_forwarding_rule" "forwarding_rule1_network2" { + name = "{{index $.Vars "forwarding_rule1_network2_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip1_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment +} + +resource "google_compute_forwarding_rule" "forwarding_rule2_network2" { + name = "{{index $.Vars "forwarding_rule2_network2_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip2_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment +} + +resource "google_compute_address" "ip1_network2" { + name = "{{index $.Vars "ip1_network2_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_address" "ip2_network2" { + name = "{{index $.Vars "ip2_network2_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_subnetwork" "subnet_network2" { + name = "{{index $.Vars "subnet_network2_name"}}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network2.id +} + +resource "google_compute_network" "network2" { + name = "{{index $.Vars "network2_name"}}" + auto_create_subnetworks = false +} + +// redis cluster without endpoint +resource "google_redis_cluster" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "cluster_name"}}" + shard_count = 3 + region = "us-central1" + replica_count = 0 + deletion_protection_enabled = false + psc_configs { + network = google_compute_network.network1.id + } + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "{{index $.Vars "policy_name"}}" + location = "us-central1" + service_class = "gcp-memorystore-redis" + description = "my basic service connection policy" + network = google_compute_network.network1.id + psc_config { + subnetworks = [google_compute_subnetwork.subnet_network1.id] + } +} + +resource "google_compute_subnetwork" "subnet_network1" { + name = "{{index $.Vars "subnet_network1_name"}}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network1.id +} + +resource "google_compute_network" "network1" { + name = "{{index $.Vars "network1_name"}}" + auto_create_subnetworks = false +} diff --git a/mmv1/templates/terraform/examples/redis_cluster_user_created_connections.tf.tmpl b/mmv1/templates/terraform/examples/redis_cluster_user_created_connections.tf.tmpl new file mode 100644 index 000000000000..5230e12604db --- /dev/null +++ b/mmv1/templates/terraform/examples/redis_cluster_user_created_connections.tf.tmpl @@ -0,0 +1,149 @@ +resource "google_redis_cluster_user_created_connections" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "cluster_name"}}" + region = "us-central1" + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network1.psc_connection_id + address = google_compute_address.ip1_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network1.id + network = google_compute_network.network1.id + project_id = data.google_project.project.project_id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network1.psc_connection_id + address = google_compute_address.ip2_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network1.id + network = google_compute_network.network1.id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment + } + } + } + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network2.psc_connection_id + address = google_compute_address.ip1_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network2.psc_connection_id + address = google_compute_address.ip2_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment + } + } + } +} + +resource "google_compute_forwarding_rule" "forwarding_rule1_network1" { + name = "{{index $.Vars "forwarding_rule1_network1_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip1_network1.id + load_balancing_scheme = "" + network = google_compute_network.network1.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment +} + +resource "google_compute_forwarding_rule" "forwarding_rule2_network1" { + name = "{{index $.Vars "forwarding_rule2_network1_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip2_network1.id + load_balancing_scheme = "" + network = google_compute_network.network1.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment +} + +resource "google_compute_address" "ip1_network1" { + name = "{{index $.Vars "ip1_network1_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network1.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_address" "ip2_network1" { + name = "{{index $.Vars "ip2_network1_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network1.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_subnetwork" "subnet_network1" { + name = "{{index $.Vars "subnet_network1_name"}}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network1.id +} + +resource "google_compute_network" "network1" { + name = "{{index $.Vars "network1_name"}}" + auto_create_subnetworks = false +} + +resource "google_compute_forwarding_rule" "forwarding_rule1_network2" { + name = "{{index $.Vars "forwarding_rule1_network2_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip1_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[0].service_attachment +} + +resource "google_compute_forwarding_rule" "forwarding_rule2_network2" { + name = "{{index $.Vars "forwarding_rule2_network2_name"}}" + region = "us-central1" + ip_address = google_compute_address.ip2_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.{{$.PrimaryResourceId}}.psc_service_attachments[1].service_attachment +} + +resource "google_compute_address" "ip1_network2" { + name = "{{index $.Vars "ip1_network2_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_address" "ip2_network2" { + name = "{{index $.Vars "ip2_network2_name"}}" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" +} + +resource "google_compute_subnetwork" "subnet_network2" { + name = "{{index $.Vars "subnet_network2_name"}}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network2.id +} + +resource "google_compute_network" "network2" { + name = "{{index $.Vars "network2_name"}}" + auto_create_subnetworks = false +} + +// redis cluster without endpoint +resource "google_redis_cluster" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "cluster_name"}}" + shard_count = 3 + region = "us-central1" + replica_count = 0 + deletion_protection_enabled = false +} + +data "google_project" "project" { +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/redis/resource_redis_cluster_test.go.tmpl b/mmv1/third_party/terraform/services/redis/resource_redis_cluster_test.go.tmpl index 31a8759a32d2..3042df527a02 100644 --- a/mmv1/third_party/terraform/services/redis/resource_redis_cluster_test.go.tmpl +++ b/mmv1/third_party/terraform/services/redis/resource_redis_cluster_test.go.tmpl @@ -349,6 +349,75 @@ func TestAccRedisCluster_switchoverAndDetachSecondary(t *testing.T) { }) } +// Validate that cluster endpoints are updated for the cluster +func TestAccRedisCluster_updateClusterEndpoints(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckRedisClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create cluster with no user created connections + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: true, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 0}), + }, + { + ResourceName: "google_redis_cluster.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"psc_configs"}, + }, + { + // create cluster with one user created connection + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: true, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 1}), + }, + { + ResourceName: "google_redis_cluster.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"psc_configs"}, + }, + { + // update cluster with 2 endpoints + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: true, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 2}), + }, + { + ResourceName: "google_redis_cluster.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"psc_configs"}, + }, + { + // update cluster with 1 endpoint + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: true, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 1}), + }, + { + ResourceName: "google_redis_cluster.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"psc_configs"}, + }, + { + // update cluster with 0 endpoints + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: true, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 0}), + }, + { + ResourceName: "google_redis_cluster.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"psc_configs"}, + }, + { + // clean up the resource + Config: createOrUpdateRedisCluster(&ClusterParams{name: name, replicaCount: 0, shardCount: 3, deletionProtectionEnabled: false, zoneDistributionMode: "MULTI_ZONE", maintenanceDay: "MONDAY", maintenanceHours: 1, maintenanceMinutes: 0, maintenanceSeconds: 0, maintenanceNanos: 0, userEndpointCount: 0}), + }, + }, + }) +} + type ClusterParams struct { name string replicaCount int @@ -367,6 +436,242 @@ type ClusterParams struct { shouldCreateSecondary bool secondaryClusterName string ccrRole string + userEndpointCount int +} + +func createRedisClusterEndpoints(params *ClusterParams) string { + if params.userEndpointCount == 2 { + return createRedisClusterEndpointsWithTwoUserCreatedConnections(params) + } else if params.userEndpointCount == 1 { + return createRedisClusterEndpointsWithOneUserCreatedConnections(params) + } + return `` +} + +func createRedisClusterEndpointsWithOneUserCreatedConnections(params *ClusterParams) string { + return fmt.Sprintf(` + resource "google_redis_cluster_user_created_connections" "default" { + provider = google-beta + name = "%s" + region = "us-central1" + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network1.psc_connection_id + address = google_compute_address.ip1_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network1.id + network = google_compute_network.network1.id + project_id = data.google_project.project.project_id + service_attachment = google_redis_cluster.test.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network1.psc_connection_id + address = google_compute_address.ip2_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network1.id + network = google_compute_network.network1.id + service_attachment = google_redis_cluster.test.psc_service_attachments[1].service_attachment + } + } + } + } + data "google_project" "project" { + provider = google-beta + } + %s + `, + params.name, + createRedisClusterUserCreatedConnection1(params), + ) + +} + +func createRedisClusterEndpointsWithTwoUserCreatedConnections(params *ClusterParams) string { + return fmt.Sprintf(` + resource "google_redis_cluster_user_created_connections" "default" { + provider = google-beta + name = "%s" + region = "us-central1" + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network1.psc_connection_id + address = google_compute_address.ip1_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network1.id + network = google_compute_network.network1.id + project_id = data.google_project.project.project_id + service_attachment = google_redis_cluster.test.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network1.psc_connection_id + address = google_compute_address.ip2_network1.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network1.id + network = google_compute_network.network1.id + service_attachment = google_redis_cluster.test.psc_service_attachments[1].service_attachment + } + } + } + cluster_endpoints { + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule1_network2.psc_connection_id + address = google_compute_address.ip1_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule1_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.test.psc_service_attachments[0].service_attachment + } + } + connections { + psc_connection { + psc_connection_id = google_compute_forwarding_rule.forwarding_rule2_network2.psc_connection_id + address = google_compute_address.ip2_network2.address + forwarding_rule = google_compute_forwarding_rule.forwarding_rule2_network2.id + network = google_compute_network.network2.id + service_attachment = google_redis_cluster.test.psc_service_attachments[1].service_attachment + } + } + } + } + data "google_project" "project" { + provider = google-beta + } + %s + %s + `, + params.name, + createRedisClusterUserCreatedConnection1(params), + createRedisClusterUserCreatedConnection2(params), + ) +} + +func createRedisClusterUserCreatedConnection1(params *ClusterParams) string { + return fmt.Sprintf(` + resource "google_compute_forwarding_rule" "forwarding_rule1_network1" { + provider = google-beta + name = "%s" + region = "us-central1" + ip_address = google_compute_address.ip1_network1.id + load_balancing_scheme = "" + network = google_compute_network.network1.id + target = google_redis_cluster.test.psc_service_attachments[0].service_attachment + } + + resource "google_compute_forwarding_rule" "forwarding_rule2_network1" { + provider = google-beta + name = "%s" + region = "us-central1" + ip_address = google_compute_address.ip2_network1.id + load_balancing_scheme = "" + network = google_compute_network.network1.id + target = google_redis_cluster.test.psc_service_attachments[1].service_attachment + } + + resource "google_compute_address" "ip1_network1" { + provider = google-beta + name = "%s" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network1.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" + } + + resource "google_compute_address" "ip2_network1" { + provider = google-beta + name = "%s" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network1.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" + } + + resource "google_compute_subnetwork" "subnet_network1" { + provider = google-beta + name = "%s" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network1.id + } + + resource "google_compute_network" "network1" { + provider = google-beta + name = "%s" + auto_create_subnetworks = false + } + `, + params.name+"-11", // fwd-rule1-net1 + params.name+"-12", // fwd-rule2-net1 + params.name+"-11", // ip1-net1 + params.name+"-12", // ip2-net1 + params.name+"-1", // subnet-net1 + params.name+"-1", // net1 + ) +} + +func createRedisClusterUserCreatedConnection2(params *ClusterParams) string { + return fmt.Sprintf(` + resource "google_compute_forwarding_rule" "forwarding_rule1_network2" { + provider = google-beta + name = "%s" + region = "us-central1" + ip_address = google_compute_address.ip1_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.test.psc_service_attachments[0].service_attachment + } + + resource "google_compute_forwarding_rule" "forwarding_rule2_network2" { + provider = google-beta + name = "%s" + region = "us-central1" + ip_address = google_compute_address.ip2_network2.id + load_balancing_scheme = "" + network = google_compute_network.network2.id + target = google_redis_cluster.test.psc_service_attachments[1].service_attachment + } + + resource "google_compute_address" "ip1_network2" { + provider = google-beta + name = "%s" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" + } + + resource "google_compute_address" "ip2_network2" { + provider = google-beta + name = "%s" + region = "us-central1" + subnetwork = google_compute_subnetwork.subnet_network2.id + address_type = "INTERNAL" + purpose = "GCE_ENDPOINT" + } + + resource "google_compute_subnetwork" "subnet_network2" { + provider = google-beta + name = "%s" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.network2.id + } + + resource "google_compute_network" "network2" { + provider = google-beta + name = "%s" + auto_create_subnetworks = false + } + `, + params.name+"-21", // fwd-rule1-net2 + params.name+"-22", // fwd-rule2-net2 + params.name+"-21", // ip1-net2 + params.name+"-22", // ip2-net2 + params.name+"-2", // subnet-net2 + params.name+"-2", // net2 + ) + } func createOrUpdateRedisCluster(params *ClusterParams) string { @@ -376,7 +681,10 @@ func createOrUpdateRedisCluster(params *ClusterParams) string { secClusterResourceBlock = createRedisClusterResourceConfig(params, /*isSecondaryCluster*/true) } + endpointBlock := createRedisClusterEndpoints(params) + return fmt.Sprintf(` + %s %s %s resource "google_network_connectivity_service_connection_policy" "default" { @@ -405,6 +713,7 @@ func createOrUpdateRedisCluster(params *ClusterParams) string { auto_create_subnetworks = false } `, + endpointBlock, clusterResourceBlock, secClusterResourceBlock, params.name,