Skip to content

Commit 293810e

Browse files
Add support for managed_server_ca to google_redis_cluster resource (#14172) (#23223)
[upstream:9a878056ebd7c8ce44ed7cb281d2af3cc3d1f7dd] Signed-off-by: Modular Magician <[email protected]>
1 parent 9a58ca2 commit 293810e

File tree

5 files changed

+240
-0
lines changed

5 files changed

+240
-0
lines changed

.changelog/14172.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
redis: added `managed_server_ca` to `google_redis_cluster` resource
3+
```

google/services/redis/resource_redis_cluster.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,32 @@ resolution and up to nine fractional digits.`,
625625
},
626626
},
627627
},
628+
"managed_server_ca": {
629+
Type: schema.TypeList,
630+
Computed: true,
631+
Description: `Cluster's Certificate Authority. This field will only be populated if Redis Cluster's transit_encryption_mode is TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION`,
632+
Elem: &schema.Resource{
633+
Schema: map[string]*schema.Schema{
634+
"ca_certs": {
635+
Type: schema.TypeList,
636+
Computed: true,
637+
Description: `The PEM encoded CA certificate chains for redis managed server authentication`,
638+
Elem: &schema.Resource{
639+
Schema: map[string]*schema.Schema{
640+
"certificates": {
641+
Type: schema.TypeList,
642+
Computed: true,
643+
Description: `The certificates that form the CA chain, from leaf to root order`,
644+
Elem: &schema.Schema{
645+
Type: schema.TypeString,
646+
},
647+
},
648+
},
649+
},
650+
},
651+
},
652+
},
653+
},
628654
"precise_size_gb": {
629655
Type: schema.TypeFloat,
630656
Computed: true,
@@ -942,6 +968,18 @@ func resourceRedisClusterRead(d *schema.ResourceData, meta interface{}) error {
942968
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("RedisCluster %q", d.Id()))
943969
}
944970

971+
res, err = resourceRedisClusterDecoder(d, meta, res)
972+
if err != nil {
973+
return err
974+
}
975+
976+
if res == nil {
977+
// Decoding the object has resulted in it being gone. It may be marked deleted
978+
log.Printf("[DEBUG] Removing RedisCluster because it no longer exists.")
979+
d.SetId("")
980+
return nil
981+
}
982+
945983
if err := d.Set("project", project); err != nil {
946984
return fmt.Errorf("Error reading Cluster: %s", err)
947985
}
@@ -1026,6 +1064,9 @@ func resourceRedisClusterRead(d *schema.ResourceData, meta interface{}) error {
10261064
if err := d.Set("kms_key", flattenRedisClusterKmsKey(res["kmsKey"], d, config)); err != nil {
10271065
return fmt.Errorf("Error reading Cluster: %s", err)
10281066
}
1067+
if err := d.Set("managed_server_ca", flattenRedisClusterManagedServerCa(res["managedServerCa"], d, config)); err != nil {
1068+
return fmt.Errorf("Error reading Cluster: %s", err)
1069+
}
10291070

10301071
return nil
10311072
}
@@ -2034,6 +2075,41 @@ func flattenRedisClusterKmsKey(v interface{}, d *schema.ResourceData, config *tr
20342075
return v
20352076
}
20362077

2078+
func flattenRedisClusterManagedServerCa(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
2079+
if v == nil {
2080+
return nil
2081+
}
2082+
original := v.(map[string]interface{})
2083+
if len(original) == 0 {
2084+
return nil
2085+
}
2086+
transformed := make(map[string]interface{})
2087+
transformed["ca_certs"] =
2088+
flattenRedisClusterManagedServerCaCaCerts(original["caCerts"], d, config)
2089+
return []interface{}{transformed}
2090+
}
2091+
func flattenRedisClusterManagedServerCaCaCerts(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
2092+
if v == nil {
2093+
return v
2094+
}
2095+
l := v.([]interface{})
2096+
transformed := make([]interface{}, 0, len(l))
2097+
for _, raw := range l {
2098+
original := raw.(map[string]interface{})
2099+
if len(original) < 1 {
2100+
// Do not include empty json objects coming back from the api
2101+
continue
2102+
}
2103+
transformed = append(transformed, map[string]interface{}{
2104+
"certificates": flattenRedisClusterManagedServerCaCaCertsCertificates(original["certificates"], d, config),
2105+
})
2106+
}
2107+
return transformed
2108+
}
2109+
func flattenRedisClusterManagedServerCaCaCertsCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
2110+
return v
2111+
}
2112+
20372113
func expandRedisClusterGcsSource(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
20382114
l := v.([]interface{})
20392115
if len(l) == 0 || l[0] == nil {
@@ -2733,3 +2809,54 @@ func resourceRedisClusterEncoder(d *schema.ResourceData, meta interface{}, obj m
27332809

27342810
return obj, nil
27352811
}
2812+
2813+
func resourceRedisClusterDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
2814+
// Such custom code is necessary as the Cluster's certificate authority has to be retrieved via a dedicated
2815+
// getCertificateAuthority API.
2816+
// See https://cloud.google.com/memorystore/docs/cluster/reference/rest/v1/projects.locations.clusters/getCertificateAuthority#http-request
2817+
// for details about this API.
2818+
config := meta.(*transport_tpg.Config)
2819+
2820+
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
2821+
if err != nil {
2822+
return nil, err
2823+
}
2824+
2825+
// Only clusters with TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION mode have certificate authority set
2826+
if v, ok := res["transitEncryptionMode"].(string); !ok || v != "TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION" {
2827+
return res, nil
2828+
}
2829+
2830+
url, err := tpgresource.ReplaceVars(d, config, "{{RedisBasePath}}projects/{{project}}/locations/{{region}}/clusters/{{name}}/certificateAuthority")
2831+
if err != nil {
2832+
return nil, err
2833+
}
2834+
2835+
billingProject := ""
2836+
2837+
project, err := tpgresource.GetProject(d, config)
2838+
if err != nil {
2839+
return nil, fmt.Errorf("Error fetching project for Cluster: %s", err)
2840+
}
2841+
2842+
billingProject = project
2843+
2844+
// err == nil indicates that the billing_project value was found
2845+
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
2846+
billingProject = bp
2847+
}
2848+
2849+
certificateAuthority, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
2850+
Config: config,
2851+
Method: "GET",
2852+
Project: billingProject,
2853+
RawURL: url,
2854+
UserAgent: userAgent,
2855+
})
2856+
if err != nil {
2857+
return nil, fmt.Errorf("Error reading certificateAuthority: %s", err)
2858+
}
2859+
2860+
res["managedServerCa"] = certificateAuthority["managedServerCa"]
2861+
return res, nil
2862+
}

google/services/redis/resource_redis_cluster_generated_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fields:
3838
- field: 'maintenance_schedule.schedule_deadline_time'
3939
- field: 'maintenance_schedule.start_time'
4040
- field: 'managed_backup_source.backup'
41+
- field: 'managed_server_ca.ca_certs.certificates'
4142
- field: 'name'
4243
provider_only: true
4344
- field: 'node_type'

google/services/redis/resource_redis_cluster_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,3 +1266,94 @@ func createRedisClusterResourceConfig(params *ClusterParams, isSecondaryCluster
12661266
crossClusterReplicationConfigBlock,
12671267
dependsOnBlock)
12681268
}
1269+
1270+
func TestAccRedisCluster_redisClusterTlsEnabled(t *testing.T) {
1271+
t.Parallel()
1272+
1273+
context := map[string]interface{}{
1274+
"deletion_protection_enabled": false,
1275+
"random_suffix": acctest.RandString(t, 10),
1276+
}
1277+
1278+
acctest.VcrTest(t, resource.TestCase{
1279+
PreCheck: func() { acctest.AccTestPreCheck(t) },
1280+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
1281+
CheckDestroy: testAccCheckRedisClusterDestroyProducer(t),
1282+
Steps: []resource.TestStep{
1283+
{
1284+
Config: testAccRedisCluster_redisClusterTlsEnabled(context),
1285+
Check: resource.ComposeTestCheckFunc(
1286+
resource.TestCheckResourceAttrSet("google_redis_cluster.cluster-tls", "managed_server_ca.0.ca_certs.0.certificates.0"),
1287+
),
1288+
},
1289+
{
1290+
ResourceName: "google_redis_cluster.cluster-tls",
1291+
ImportState: true,
1292+
ImportStateVerify: true,
1293+
ImportStateVerifyIgnore: []string{"gcs_source", "managed_backup_source", "name", "psc_configs", "region"},
1294+
},
1295+
},
1296+
})
1297+
}
1298+
1299+
func testAccRedisCluster_redisClusterTlsEnabled(context map[string]interface{}) string {
1300+
return acctest.Nprintf(`
1301+
resource "google_redis_cluster" "cluster-tls" {
1302+
name = "tf-test-tls-cluster%{random_suffix}"
1303+
shard_count = 3
1304+
psc_configs {
1305+
network = google_compute_network.consumer_net.id
1306+
}
1307+
region = "us-central1"
1308+
replica_count = 1
1309+
node_type = "REDIS_SHARED_CORE_NANO"
1310+
transit_encryption_mode = "TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION"
1311+
authorization_mode = "AUTH_MODE_DISABLED"
1312+
redis_configs = {
1313+
maxmemory-policy = "volatile-ttl"
1314+
}
1315+
deletion_protection_enabled = %{deletion_protection_enabled}
1316+
1317+
zone_distribution_config {
1318+
mode = "MULTI_ZONE"
1319+
}
1320+
maintenance_policy {
1321+
weekly_maintenance_window {
1322+
day = "MONDAY"
1323+
start_time {
1324+
hours = 1
1325+
minutes = 0
1326+
seconds = 0
1327+
nanos = 0
1328+
}
1329+
}
1330+
}
1331+
depends_on = [
1332+
google_network_connectivity_service_connection_policy.default
1333+
]
1334+
}
1335+
1336+
resource "google_network_connectivity_service_connection_policy" "default" {
1337+
name = "tf-test-my-policy%{random_suffix}"
1338+
location = "us-central1"
1339+
service_class = "gcp-memorystore-redis"
1340+
description = "my basic service connection policy"
1341+
network = google_compute_network.consumer_net.id
1342+
psc_config {
1343+
subnetworks = [google_compute_subnetwork.consumer_subnet.id]
1344+
}
1345+
}
1346+
1347+
resource "google_compute_subnetwork" "consumer_subnet" {
1348+
name = "tf-test-my-subnet%{random_suffix}"
1349+
ip_cidr_range = "10.0.0.248/29"
1350+
region = "us-central1"
1351+
network = google_compute_network.consumer_net.id
1352+
}
1353+
1354+
resource "google_compute_network" "consumer_net" {
1355+
name = "tf-test-my-network%{random_suffix}"
1356+
auto_create_subnetworks = false
1357+
}
1358+
`, context)
1359+
}

website/docs/r/redis_cluster.html.markdown

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,10 @@ In addition to the arguments listed above, the following computed attributes are
10141014
Service attachment details to configure Psc connections.
10151015
Structure is [documented below](#nested_psc_service_attachments).
10161016

1017+
* `managed_server_ca` -
1018+
Cluster's Certificate Authority. This field will only be populated if Redis Cluster's transit_encryption_mode is TRANSIT_ENCRYPTION_MODE_SERVER_AUTHENTICATION
1019+
Structure is [documented below](#nested_managed_server_ca).
1020+
10171021

10181022
<a name="nested_discovery_endpoints"></a>The `discovery_endpoints` block contains:
10191023

@@ -1111,6 +1115,20 @@ In addition to the arguments listed above, the following computed attributes are
11111115
(Output)
11121116
Type of a PSC connection targeting this service attachment.
11131117

1118+
<a name="nested_managed_server_ca"></a>The `managed_server_ca` block contains:
1119+
1120+
* `ca_certs` -
1121+
(Output)
1122+
The PEM encoded CA certificate chains for redis managed server authentication
1123+
Structure is [documented below](#nested_managed_server_ca_ca_certs).
1124+
1125+
1126+
<a name="nested_managed_server_ca_ca_certs"></a>The `ca_certs` block contains:
1127+
1128+
* `certificates` -
1129+
(Output)
1130+
The certificates that form the CA chain, from leaf to root order
1131+
11141132
## Timeouts
11151133

11161134
This resource provides the following

0 commit comments

Comments
 (0)