From a50b7ffc946db4c75c3a003e31c50447c306feca Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Mon, 3 Jun 2024 21:17:39 +0000 Subject: [PATCH] Adding zone distribution mode in the Cluster resource for Memorystore Redis cluster (#10458) [upstream:40e5c97e051e9b4ac75cc1704c1e5a92eaa7bad5] Signed-off-by: Modular Magician --- .../services/redis/resource_redis_cluster.go | 90 +++++++++++++++++++ .../resource_redis_cluster_generated_test.go | 76 ++++++++++++++++ website/docs/r/redis_cluster.html.markdown | 72 +++++++++++++++ 3 files changed, 238 insertions(+) diff --git a/google/services/redis/resource_redis_cluster.go b/google/services/redis/resource_redis_cluster.go index 4709d1c8f4c..5d3a833df7a 100644 --- a/google/services/redis/resource_redis_cluster.go +++ b/google/services/redis/resource_redis_cluster.go @@ -133,6 +133,30 @@ https://cloud.google.com/memorystore/docs/cluster/supported-instance-configurati If not provided, encryption is disabled for the cluster. Default value: "TRANSIT_ENCRYPTION_MODE_DISABLED" Possible values: ["TRANSIT_ENCRYPTION_MODE_UNSPECIFIED", "TRANSIT_ENCRYPTION_MODE_DISABLED", "TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION"]`, Default: "TRANSIT_ENCRYPTION_MODE_DISABLED", }, + "zone_distribution_config": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `Immutable. Zone distribution config for Memorystore Redis cluster.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: verify.ValidateEnum([]string{"MULTI_ZONE", "SINGLE_ZONE", ""}), + Description: `Immutable. The mode for zone distribution for Memorystore Redis cluster. +If not provided, MULTI_ZONE will be used as default Possible values: ["MULTI_ZONE", "SINGLE_ZONE"]`, + }, + "zone": { + Type: schema.TypeString, + Optional: true, + Description: `Immutable. The zone for single zone Memorystore Redis cluster.`, + }, + }, + }, + }, "create_time": { Type: schema.TypeString, Computed: true, @@ -299,6 +323,12 @@ func resourceRedisClusterCreate(d *schema.ResourceData, meta interface{}) error } else if v, ok := d.GetOkExists("node_type"); !tpgresource.IsEmptyValue(reflect.ValueOf(nodeTypeProp)) && (ok || !reflect.DeepEqual(v, nodeTypeProp)) { obj["nodeType"] = nodeTypeProp } + zoneDistributionConfigProp, err := expandRedisClusterZoneDistributionConfig(d.Get("zone_distribution_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("zone_distribution_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(zoneDistributionConfigProp)) && (ok || !reflect.DeepEqual(v, zoneDistributionConfigProp)) { + obj["zoneDistributionConfig"] = zoneDistributionConfigProp + } pscConfigsProp, err := expandRedisClusterPscConfigs(d.Get("psc_configs"), d, config) if err != nil { return err @@ -448,6 +478,9 @@ func resourceRedisClusterRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("node_type", flattenRedisClusterNodeType(res["nodeType"], d, config)); err != nil { return fmt.Errorf("Error reading Cluster: %s", err) } + if err := d.Set("zone_distribution_config", flattenRedisClusterZoneDistributionConfig(res["zoneDistributionConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Cluster: %s", err) + } if err := d.Set("discovery_endpoints", flattenRedisClusterDiscoveryEndpoints(res["discoveryEndpoints"], d, config)); err != nil { return fmt.Errorf("Error reading Cluster: %s", err) } @@ -685,6 +718,29 @@ func flattenRedisClusterNodeType(v interface{}, d *schema.ResourceData, config * return v } +func flattenRedisClusterZoneDistributionConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["mode"] = + flattenRedisClusterZoneDistributionConfigMode(original["mode"], d, config) + transformed["zone"] = + flattenRedisClusterZoneDistributionConfigZone(original["zone"], d, config) + return []interface{}{transformed} +} +func flattenRedisClusterZoneDistributionConfigMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenRedisClusterZoneDistributionConfigZone(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenRedisClusterDiscoveryEndpoints(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { if v == nil { return v @@ -918,6 +974,40 @@ func expandRedisClusterNodeType(v interface{}, d tpgresource.TerraformResourceDa return v, nil } +func expandRedisClusterZoneDistributionConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedMode, err := expandRedisClusterZoneDistributionConfigMode(original["mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMode); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["mode"] = transformedMode + } + + transformedZone, err := expandRedisClusterZoneDistributionConfigZone(original["zone"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedZone); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["zone"] = transformedZone + } + + return transformed, nil +} + +func expandRedisClusterZoneDistributionConfigMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandRedisClusterZoneDistributionConfigZone(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandRedisClusterPscConfigs(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { l := v.([]interface{}) req := make([]interface{}, 0, len(l)) diff --git a/google/services/redis/resource_redis_cluster_generated_test.go b/google/services/redis/resource_redis_cluster_generated_test.go index 47e72ebdcc3..6205a9322c7 100644 --- a/google/services/redis/resource_redis_cluster_generated_test.go +++ b/google/services/redis/resource_redis_cluster_generated_test.go @@ -72,6 +72,82 @@ resource "google_redis_cluster" "cluster-ha" { redis_configs = { maxmemory-policy = "volatile-ttl" } + zone_distribution_config { + mode = "MULTI_ZONE" + } + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + + lifecycle { + prevent_destroy = %{prevent_destroy} + } +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "mypolicy%{random_suffix}" + location = "us-central1" + service_class = "gcp-memorystore-redis" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "mysubnet%{random_suffix}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "mynetwork%{random_suffix}" + auto_create_subnetworks = false +} +`, context) +} + +func TestAccRedisCluster_redisClusterHaSingleZoneExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "prevent_destroy": false, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckRedisClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccRedisCluster_redisClusterHaSingleZoneExample(context), + }, + { + ResourceName: "google_redis_cluster.cluster-ha-single-zone", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "psc_configs", "region"}, + }, + }, + }) +} + +func testAccRedisCluster_redisClusterHaSingleZoneExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_redis_cluster" "cluster-ha-single-zone" { + name = "tf-test-ha-cluster-single-zone%{random_suffix}" + shard_count = 3 + psc_configs { + network = google_compute_network.producer_net.id + } + region = "us-central1" + zone_distribution_config { + mode = "SINGLE_ZONE" + zone = "us-central1-f" + } depends_on = [ google_network_connectivity_service_connection_policy.default ] diff --git a/website/docs/r/redis_cluster.html.markdown b/website/docs/r/redis_cluster.html.markdown index a19070d44b6..16659ff5343 100644 --- a/website/docs/r/redis_cluster.html.markdown +++ b/website/docs/r/redis_cluster.html.markdown @@ -51,6 +51,61 @@ resource "google_redis_cluster" "cluster-ha" { redis_configs = { maxmemory-policy = "volatile-ttl" } + zone_distribution_config { + mode = "MULTI_ZONE" + } + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + + lifecycle { + prevent_destroy = true + } +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "mypolicy" + location = "us-central1" + service_class = "gcp-memorystore-redis" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "mysubnet" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "mynetwork" + auto_create_subnetworks = false +} +``` + +## Example Usage - Redis Cluster Ha Single Zone + + +```hcl +resource "google_redis_cluster" "cluster-ha-single-zone" { + name = "ha-cluster-single-zone" + shard_count = 3 + psc_configs { + network = google_compute_network.producer_net.id + } + region = "us-central1" + zone_distribution_config { + mode = "SINGLE_ZONE" + zone = "us-central1-f" + } depends_on = [ google_network_connectivity_service_connection_policy.default ] @@ -136,6 +191,11 @@ The following arguments are supported: If not provided, REDIS_HIGHMEM_MEDIUM will be used as default Possible values are: `REDIS_SHARED_CORE_NANO`, `REDIS_HIGHMEM_MEDIUM`, `REDIS_HIGHMEM_XLARGE`, `REDIS_STANDARD_SMALL`. +* `zone_distribution_config` - + (Optional) + Immutable. Zone distribution config for Memorystore Redis cluster. + Structure is [documented below](#nested_zone_distribution_config). + * `replica_count` - (Optional) Optional. The number of replica nodes per shard. @@ -154,6 +214,18 @@ The following arguments are supported: If it is not provided, the provider project is used. +The `zone_distribution_config` block supports: + +* `mode` - + (Optional) + Immutable. The mode for zone distribution for Memorystore Redis cluster. + If not provided, MULTI_ZONE will be used as default + Possible values are: `MULTI_ZONE`, `SINGLE_ZONE`. + +* `zone` - + (Optional) + Immutable. The zone for single zone Memorystore Redis cluster. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: