Skip to content

Commit 06d9610

Browse files
authored
Support Swithcover for MySQL and PostgreSQL (#12646)
1 parent 7d808d9 commit 06d9610

5 files changed

+1191
-22
lines changed

mmv1/third_party/terraform/services/sql/data_source_sql_database_instances.go

+17
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func flattenDatasourceGoogleDatabaseInstancesList(fetchedInstances []*sqladmin.D
153153
}
154154

155155
instance["replica_configuration"] = flattenReplicaConfigurationforDataSource(rawInstance.ReplicaConfiguration)
156+
instance["replication_cluster"] = flattenReplicationClusterForDataSource(rawInstance.ReplicationCluster)
156157

157158
ipAddresses := flattenIpAddresses(rawInstance.IpAddresses)
158159
instance["ip_address"] = ipAddresses
@@ -198,3 +199,19 @@ func flattenReplicaConfigurationforDataSource(replicaConfiguration *sqladmin.Rep
198199

199200
return rc
200201
}
202+
203+
// flattenReplicationClusterForDataSource converts cloud SQL backend ReplicationCluster (proto) to
204+
// terraform replication_cluster. We explicitly allow the case when ReplicationCluster
205+
// is nil since replication_cluster is computed+optional.
206+
func flattenReplicationClusterForDataSource(replicationCluster *sqladmin.ReplicationCluster) []map[string]interface{} {
207+
data := make(map[string]interface{})
208+
data["failover_dr_replica_name"] = ""
209+
if replicationCluster != nil && replicationCluster.FailoverDrReplicaName != "" {
210+
data["failover_dr_replica_name"] = replicationCluster.FailoverDrReplicaName
211+
}
212+
data["dr_replica"] = false
213+
if replicationCluster != nil {
214+
data["dr_replica"] = replicationCluster.DrReplica
215+
}
216+
return []map[string]interface{}{data}
217+
}

mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl

+53-12
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,27 @@ is set to true. Defaults to ZONAL.`,
925925
},
926926
Description: `The replicas of the instance.`,
927927
},
928+
"replication_cluster": {
929+
Type: schema.TypeList,
930+
Computed: true,
931+
Optional: true,
932+
MaxItems: 1,
933+
Elem: &schema.Resource{
934+
Schema: map[string]*schema.Schema{
935+
"failover_dr_replica_name": {
936+
Type: schema.TypeString,
937+
Optional: true,
938+
Description: fmt.Sprintf(`If the instance is a primary instance, then this field identifies the disaster recovery (DR) replica. The standard format of this field is "your-project:your-instance". You can also set this field to "your-instance", but cloud SQL backend will convert it to the aforementioned standard format.`),
939+
},
940+
"dr_replica": {
941+
Type: schema.TypeBool,
942+
Computed: true,
943+
Description: `Read-only field that indicates whether the replica is a DR replica.`,
944+
},
945+
},
946+
},
947+
Description: "A primary instance and disaster recovery replica pair. Applicable to MySQL and PostgreSQL. This field can be set only after both the primary and replica are created.",
948+
},
928949
"server_ca_cert": {
929950
Type: schema.TypeList,
930951
Computed: true,
@@ -1719,6 +1740,11 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
17191740
if err := d.Set("replica_names", instance.ReplicaNames); err != nil {
17201741
return fmt.Errorf("Error setting replica_names: %w", err)
17211742
}
1743+
1744+
// We always set replication_cluster because it is computed+optional.
1745+
if err := d.Set("replication_cluster", flattenReplicationCluster(instance.ReplicationCluster, d)); err != nil {
1746+
return fmt.Errorf("Error setting replication_cluster: %w", err)
1747+
}
17221748
ipAddresses := flattenIpAddresses(instance.IpAddresses)
17231749
if err := d.Set("ip_address", ipAddresses); err != nil {
17241750
log.Printf("[WARN] Failed to set SQL Database Instance IP Addresses")
@@ -1981,7 +2007,7 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
19812007
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.IsSqlOperationInProgressError},
19822008
})
19832009
if err != nil {
1984-
return fmt.Errorf("Error, failed to promote read replica instance as primary stand-alone %s: %s", instance.Name, err)
2010+
return fmt.Errorf("Error, failed to promote read replica instance as primary stand-alone %s: %s", d.Get("name"), err)
19852011
}
19862012
err = SqlAdminOperationWaitTime(config, op, project, "Promote Instance", userAgent, d.Timeout(schema.TimeoutUpdate))
19872013
if err != nil {
@@ -2050,6 +2076,13 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
20502076
instance.DatabaseVersion = databaseVersion
20512077
}
20522078

2079+
failoverDrReplicaName := d.Get("replication_cluster.0.failover_dr_replica_name").(string)
2080+
if failoverDrReplicaName != "" {
2081+
instance.ReplicationCluster = &sqladmin.ReplicationCluster{
2082+
FailoverDrReplicaName: failoverDrReplicaName,
2083+
}
2084+
}
2085+
20532086
err = transport_tpg.Retry(transport_tpg.RetryOptions{
20542087
RetryFunc: func() (rerr error) {
20552088
op, rerr = config.NewSqlAdminClient(userAgent).Instances.Update(project, d.Get("name").(string), instance).Do()
@@ -2377,6 +2410,22 @@ func flattenDatabaseFlags(databaseFlags []*sqladmin.DatabaseFlags) []map[string]
23772410
return flags
23782411
}
23792412

2413+
// flattenReplicationCluster converts cloud SQL backend ReplicationCluster (proto) to
2414+
// terraform replication_cluster. We explicitly allow the case when ReplicationCluster
2415+
// is nil since replication_cluster is computed+optional.
2416+
func flattenReplicationCluster(replicationCluster *sqladmin.ReplicationCluster, d *schema.ResourceData) []map[string]interface{} {
2417+
data := make(map[string]interface{})
2418+
data["failover_dr_replica_name"] = ""
2419+
if replicationCluster != nil && replicationCluster.FailoverDrReplicaName != "" {
2420+
data["failover_dr_replica_name"] = replicationCluster.FailoverDrReplicaName
2421+
}
2422+
data["dr_replica"] = false
2423+
if replicationCluster != nil {
2424+
data["dr_replica"] = replicationCluster.DrReplica
2425+
}
2426+
return []map[string]interface{}{data}
2427+
}
2428+
23802429
func flattenIpConfiguration(ipConfiguration *sqladmin.IpConfiguration, d *schema.ResourceData) interface{} {
23812430
data := map[string]interface{}{
23822431
"ipv4_enabled": ipConfiguration.Ipv4Enabled,
@@ -2659,11 +2708,6 @@ func isSwitchoverRequested(d *schema.ResourceData) bool {
26592708
if !slices.Contains(newReplicaNames.([]interface{}), originalPrimaryName) {
26602709
return false
26612710
}
2662-
dbVersion := d.Get("database_version")
2663-
if !strings.HasPrefix(dbVersion.(string), "SQLSERVER") {
2664-
log.Printf("[WARN] Switchover is only supported for SQL Server %q", dbVersion)
2665-
return false
2666-
}
26672711
return true
26682712
}
26692713

@@ -2681,10 +2725,6 @@ func isReplicaPromoteRequested(_ context.Context, oldInstanceType interface{}, n
26812725
// Check if this resource change is the manual update done on old primary after a switchover. If true, no replacement is needed.
26822726
func isSwitchoverFromOldPrimarySide(d *schema.ResourceDiff) bool {
26832727
dbVersion := d.Get("database_version")
2684-
if !strings.HasPrefix(dbVersion.(string), "SQLSERVER") {
2685-
log.Printf("[WARN] Switchover is only supported for SQL Server %q", dbVersion)
2686-
return false
2687-
}
26882728
oldInstanceType, newInstanceType := d.GetChange("instance_type")
26892729
oldReplicaNames, newReplicaNames := d.GetChange("replica_names")
26902730
_, newMasterInstanceName := d.GetChange("master_instance_name")
@@ -2699,11 +2739,12 @@ func isSwitchoverFromOldPrimarySide(d *schema.ResourceDiff) bool {
26992739
newMasterInOldReplicaNames := slices.Contains(oldReplicaNames.([]interface{}), newMasterInstanceName)
27002740
newMasterNotInNewReplicaNames := !slices.Contains(newReplicaNames.([]interface{}), newMasterInstanceName)
27012741
isCascadableReplica := cascadableReplicaFieldExists && cascadableReplica.(bool)
2742+
isSQLServer := strings.HasPrefix(dbVersion.(string), "SQLSERVER")
27022743

27032744
return newMasterInstanceName != nil &&
27042745
instanceTypeChangedFromPrimaryToReplica &&
2705-
newMasterInOldReplicaNames && newMasterNotInNewReplicaNames &&
2706-
isCascadableReplica
2746+
newMasterInOldReplicaNames && newMasterNotInNewReplicaNames && (!isSQLServer ||
2747+
isCascadableReplica)
27072748
}
27082749

27092750
func checkPromoteConfigurations(d *schema.ResourceData) error {

0 commit comments

Comments
 (0)