diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl index 80d6e9097b40..e6451ae5420c 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl @@ -1381,6 +1381,13 @@ API (for read pools, effective_availability_type may differ from availability_ty Required: true, Description: `The name of the instance from which the point in time should be restored.`, }, + {{- if ne $.TargetVersionName "ga" }} + "source_project": { + Type: schema.TypeString, + Optional: true, + Description: `The project ID of the source project`, + }, + {{- end }} "point_in_time": { Type: schema.TypeString, Optional: true, @@ -1561,7 +1568,14 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{}) ReplicaConfiguration: expandReplicaConfiguration(d.Get("replica_configuration").([]interface{})), } - cloneContext, cloneSource := expandCloneContext(d.Get("clone").([]interface{})) + cloneContext, cloneSourceInstance := expandCloneContext(d.Get("clone").([]interface{})) + + {{- if ne $.TargetVersionName "ga" }} + cloneSourceProject := expandCloneSourceProject(d.Get("clone").([]interface{})) + if cloneSourceProject == "" { + cloneSourceProject = project + } + {{- end }} pointInTimeRestoreContext := expandPointInTimeRestoreContext(d.Get("point_in_time_restore_context").([]interface{})) if valueI, ok := d.GetOk("settings.0.auto_upgrade_enabled"); ok && !(valueI.(bool)) { @@ -1623,8 +1637,19 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{}) RetryFunc: func() (operr error) { if cloneContext != nil { cloneContext.DestinationInstanceName = name + {{- if ne $.TargetVersionName "ga" }} + // if cloneSourceProject isn't equal to the project, then it's a cross project clone request. + if (cloneSourceProject != project) { + cloneContext.DestinationProject = project + cloneContext.DestinationNetwork = network + } + {{- end }} clodeReq := sqladmin.InstancesCloneRequest{CloneContext: cloneContext} - op, operr = config.NewSqlAdminClient(userAgent).Instances.Clone(project, cloneSource, &clodeReq).Do() + {{- if ne $.TargetVersionName "ga" }} + op, operr = config.NewSqlAdminClient(userAgent).Instances.Clone(cloneSourceProject, cloneSourceInstance, &clodeReq).Do() + {{- else }} + op, operr = config.NewSqlAdminClient(userAgent).Instances.Clone(project, cloneSourceInstance, &clodeReq).Do() + {{- end }} } else if pointInTimeRestoreContext != nil { parent := fmt.Sprintf("projects/%s", project) op, operr = config.NewSqlAdminClient(userAgent).Instances.PointInTimeRestore(parent, pointInTimeRestoreContext).Do() @@ -1872,6 +1897,16 @@ func expandCloneContext(configured []interface{}) (*sqladmin.CloneContext, strin }, _cloneConfiguration["source_instance_name"].(string) } +{{- if ne $.TargetVersionName "ga" }} +func expandCloneSourceProject(configured []interface{}) (string) { + if len(configured) == 0 || configured[0] == nil { + return "" + } + _cloneConfiguration := configured[0].(map[string]interface{}) + return _cloneConfiguration["source_project"].(string) +} +{{- end }} + func expandMaintenanceWindow(configured []interface{}) *sqladmin.MaintenanceWindow { if len(configured) == 0 || configured[0] == nil { return nil diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_meta.yaml.tmpl b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_meta.yaml.tmpl index bbf41275c115..3e03bf55dd0e 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_meta.yaml.tmpl +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_meta.yaml.tmpl @@ -13,6 +13,9 @@ fields: - field: 'clone.preferred_zone' - field: 'clone.source_instance_deletion_time' - field: 'clone.source_instance_name' + {{- if ne $.TargetVersionName "ga" }} + - field: 'clone.source_project' + {{- end }} - api_field: 'connectionName' - api_field: 'databaseVersion' - field: 'deletion_protection' diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go.tmpl b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go.tmpl index 534c5fadb7cf..e6853e911927 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go.tmpl +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go.tmpl @@ -1,3 +1,4 @@ + package sql_test import ( @@ -1718,6 +1719,49 @@ func TestAccSqlDatabaseInstance_basicClone(t *testing.T) { }) } +{{- if ne $.TargetVersionName "ga" }} +func TestAccSqlDatabaseInstance_crossProjectClone(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "orgId": envvar.GetTestOrgFromEnv(t), + "billingAccount": envvar.GetTestBillingAccountFromEnv(t), + "cloneSourceProject": envvar.GetTestProjectFromEnv(), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccSqlDatabaseInstance_crossProjectClone(context), + }, + { + ResourceName: "google_sql_database_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccSqlDatabaseInstanceImportStateIdFunc("google_sql_database_instance.instance"), + ImportStateVerifyIgnore: []string{"deletion_protection", "clone"}, + }, + }, + }) +} +{{- end }} + +func testAccSqlDatabaseInstanceImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["project"], rs.Primary.Attributes["name"]), nil + } +} + func TestAccSqlDatabaseInstance_cloneWithSettings(t *testing.T) { // Sqladmin client acctest.SkipIfVcr(t) @@ -8252,6 +8296,113 @@ data "google_sql_backup_run" "backup" { `, context) } +func testAccSqlDatabaseInstance_crossProjectClone(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_project" "project" { + provider = google-beta + name = "tf-test-cpc-%{random_suffix}" + project_id = "tf-test-cpc-%{random_suffix}" + org_id = "%{orgId}" + billing_account = "%{billingAccount}" + deletion_policy = "DELETE" +} + +resource "time_sleep" "wait_60_seconds" { + create_duration = "60s" + depends_on = [google_project.project] +} + +resource "google_project_service" "compute" { + provider = google-beta + project = google_project.project.project_id + service = "compute.googleapis.com" + depends_on = [time_sleep.wait_60_seconds] +} + +resource "google_project_service" "servicenetworking" { + provider = google-beta + project = google_project.project.project_id + service = "servicenetworking.googleapis.com" + depends_on = [google_project_service.compute] +} + +resource "time_sleep" "wait_300_seconds" { + create_duration = "300s" + depends_on = [google_project_service.servicenetworking] +} + +resource "google_compute_network" "sql_network" { + provider = google-beta + name = "sql-network" + project = google_project.project.project_id + depends_on = [time_sleep.wait_300_seconds] +} + +resource "google_compute_global_address" "sql_range" { + provider = google-beta + name = "sql-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.sql_network.id + project = google_project.project.project_id +} + +resource "google_service_networking_connection" "sql_vpc_connection" { + provider = google-beta + network = google_compute_network.sql_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.sql_range.name] + depends_on = [google_project_service.servicenetworking] + deletion_policy = "ABANDON" +} + +resource "google_sql_database_instance" "instance" { + provider = google-beta + name = "tf-test-cpc-%{random_suffix}" + database_version = "POSTGRES_11" + region = "us-central1" + project = google_project.project.project_id + + settings { + tier = "db-custom-2-3840" + edition = "ENTERPRISE" + ip_configuration { + private_network = google_compute_network.sql_network.self_link + } + backup_configuration { + enabled = true + point_in_time_recovery_enabled = true + } + } + + clone { + source_project = "%{cloneSourceProject}" + source_instance_name = google_sql_database_instance.source_instance.name + } + + deletion_protection = false + + // Ignore changes, since the most recent backup may change during the test + lifecycle{ + ignore_changes = [clone[0].point_in_time] + } + depends_on = [google_service_networking_connection.sql_vpc_connection] +} + +resource "google_sql_database_instance" "source_instance" { + provider = google-beta + name = "tf-test-source-%{random_suffix}" + database_version = "POSTGRES_11" + region = "us-central1" + deletion_protection = false + settings { + tier = "db-g1-small" + } +} +`, context) +} + func testAccSqlDatabaseInstance_cloneWithSettings(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_sql_database_instance" "instance" { @@ -9031,4 +9182,4 @@ func testGoogleSqlDatabaseInstanceCheckNodeCount(t *testing.T, instance string, return nil } -} +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/r/sql_database_instance.html.markdown b/mmv1/third_party/terraform/website/docs/r/sql_database_instance.html.markdown index 557eab484b72..fe0297b64716 100644 --- a/mmv1/third_party/terraform/website/docs/r/sql_database_instance.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/sql_database_instance.html.markdown @@ -715,6 +715,7 @@ The optional `point_in_time_restore_context` block supports: The optional `clone` block supports: * `source_instance_name` - (Required) Name of the source instance which will be cloned. +* `source_project` - (Optional) Id of source project where source instances exits, required for cross project clone scenario. * `point_in_time` - (Optional) The timestamp of the point in time that should be restored.