Skip to content

Commit fdad859

Browse files
Support Swithcover for MySQL and PostgreSQL (#12646) (#9044)
[upstream:06d96101ab13b27dc9a9f4a46d8f854dbebecb40] Signed-off-by: Modular Magician <[email protected]>
1 parent ea2c798 commit fdad859

6 files changed

+1197
-22
lines changed

.changelog/12646.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:enhancement
2+
sql: added `replication_cluster` field to `google_sql_database_instance` resource
3+
```
4+
```release-note:enhancement
5+
sql: added support of switchover for MySQL and PostgreSQL in `google_sql_database_instance` resource
6+
```

google-beta/services/sql/data_source_sql_database_instances.go

+17
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ func flattenDatasourceGoogleDatabaseInstancesList(fetchedInstances []*sqladmin.D
155155
}
156156

157157
instance["replica_configuration"] = flattenReplicaConfigurationforDataSource(rawInstance.ReplicaConfiguration)
158+
instance["replication_cluster"] = flattenReplicationClusterForDataSource(rawInstance.ReplicationCluster)
158159

159160
ipAddresses := flattenIpAddresses(rawInstance.IpAddresses)
160161
instance["ip_address"] = ipAddresses
@@ -200,3 +201,19 @@ func flattenReplicaConfigurationforDataSource(replicaConfiguration *sqladmin.Rep
200201

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

google-beta/services/sql/resource_sql_database_instance.go

+53-12
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,27 @@ is set to true. Defaults to ZONAL.`,
927927
},
928928
Description: `The replicas of the instance.`,
929929
},
930+
"replication_cluster": {
931+
Type: schema.TypeList,
932+
Computed: true,
933+
Optional: true,
934+
MaxItems: 1,
935+
Elem: &schema.Resource{
936+
Schema: map[string]*schema.Schema{
937+
"failover_dr_replica_name": {
938+
Type: schema.TypeString,
939+
Optional: true,
940+
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.`),
941+
},
942+
"dr_replica": {
943+
Type: schema.TypeBool,
944+
Computed: true,
945+
Description: `Read-only field that indicates whether the replica is a DR replica.`,
946+
},
947+
},
948+
},
949+
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.",
950+
},
930951
"server_ca_cert": {
931952
Type: schema.TypeList,
932953
Computed: true,
@@ -1721,6 +1742,11 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
17211742
if err := d.Set("replica_names", instance.ReplicaNames); err != nil {
17221743
return fmt.Errorf("Error setting replica_names: %w", err)
17231744
}
1745+
1746+
// We always set replication_cluster because it is computed+optional.
1747+
if err := d.Set("replication_cluster", flattenReplicationCluster(instance.ReplicationCluster, d)); err != nil {
1748+
return fmt.Errorf("Error setting replication_cluster: %w", err)
1749+
}
17241750
ipAddresses := flattenIpAddresses(instance.IpAddresses)
17251751
if err := d.Set("ip_address", ipAddresses); err != nil {
17261752
log.Printf("[WARN] Failed to set SQL Database Instance IP Addresses")
@@ -1983,7 +2009,7 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
19832009
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.IsSqlOperationInProgressError},
19842010
})
19852011
if err != nil {
1986-
return fmt.Errorf("Error, failed to promote read replica instance as primary stand-alone %s: %s", instance.Name, err)
2012+
return fmt.Errorf("Error, failed to promote read replica instance as primary stand-alone %s: %s", d.Get("name"), err)
19872013
}
19882014
err = SqlAdminOperationWaitTime(config, op, project, "Promote Instance", userAgent, d.Timeout(schema.TimeoutUpdate))
19892015
if err != nil {
@@ -2052,6 +2078,13 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
20522078
instance.DatabaseVersion = databaseVersion
20532079
}
20542080

2081+
failoverDrReplicaName := d.Get("replication_cluster.0.failover_dr_replica_name").(string)
2082+
if failoverDrReplicaName != "" {
2083+
instance.ReplicationCluster = &sqladmin.ReplicationCluster{
2084+
FailoverDrReplicaName: failoverDrReplicaName,
2085+
}
2086+
}
2087+
20552088
err = transport_tpg.Retry(transport_tpg.RetryOptions{
20562089
RetryFunc: func() (rerr error) {
20572090
op, rerr = config.NewSqlAdminClient(userAgent).Instances.Update(project, d.Get("name").(string), instance).Do()
@@ -2379,6 +2412,22 @@ func flattenDatabaseFlags(databaseFlags []*sqladmin.DatabaseFlags) []map[string]
23792412
return flags
23802413
}
23812414

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

@@ -2683,10 +2727,6 @@ func isReplicaPromoteRequested(_ context.Context, oldInstanceType interface{}, n
26832727
// Check if this resource change is the manual update done on old primary after a switchover. If true, no replacement is needed.
26842728
func isSwitchoverFromOldPrimarySide(d *schema.ResourceDiff) bool {
26852729
dbVersion := d.Get("database_version")
2686-
if !strings.HasPrefix(dbVersion.(string), "SQLSERVER") {
2687-
log.Printf("[WARN] Switchover is only supported for SQL Server %q", dbVersion)
2688-
return false
2689-
}
26902730
oldInstanceType, newInstanceType := d.GetChange("instance_type")
26912731
oldReplicaNames, newReplicaNames := d.GetChange("replica_names")
26922732
_, newMasterInstanceName := d.GetChange("master_instance_name")
@@ -2701,11 +2741,12 @@ func isSwitchoverFromOldPrimarySide(d *schema.ResourceDiff) bool {
27012741
newMasterInOldReplicaNames := slices.Contains(oldReplicaNames.([]interface{}), newMasterInstanceName)
27022742
newMasterNotInNewReplicaNames := !slices.Contains(newReplicaNames.([]interface{}), newMasterInstanceName)
27032743
isCascadableReplica := cascadableReplicaFieldExists && cascadableReplica.(bool)
2744+
isSQLServer := strings.HasPrefix(dbVersion.(string), "SQLSERVER")
27042745

27052746
return newMasterInstanceName != nil &&
27062747
instanceTypeChangedFromPrimaryToReplica &&
2707-
newMasterInOldReplicaNames && newMasterNotInNewReplicaNames &&
2708-
isCascadableReplica
2748+
newMasterInOldReplicaNames && newMasterNotInNewReplicaNames && (!isSQLServer ||
2749+
isCascadableReplica)
27092750
}
27102751

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

0 commit comments

Comments
 (0)