From e7bc2c0c21bb3a0002eca1002c32999afe099305 Mon Sep 17 00:00:00 2001 From: Shuya Ma <87669292+shuyama1@users.noreply.github.com> Date: Tue, 8 Feb 2022 13:01:59 -0800 Subject: [PATCH] Support Cloud SQL Address Range Picker for Clones and Read replicas (#5664) --- mmv1/third_party/terraform/go.mod.erb | 2 +- mmv1/third_party/terraform/go.sum | 8 +- .../resource_sql_database_instance.go.erb | 9 +- ...resource_sql_database_instance_test.go.erb | 198 +++++++++++++++++- .../r/sql_database_instance.html.markdown | 2 + 5 files changed, 214 insertions(+), 5 deletions(-) diff --git a/mmv1/third_party/terraform/go.mod.erb b/mmv1/third_party/terraform/go.mod.erb index cf04e12fb886..87eb6cf00e56 100644 --- a/mmv1/third_party/terraform/go.mod.erb +++ b/mmv1/third_party/terraform/go.mod.erb @@ -26,7 +26,7 @@ require ( golang.org/x/mod v0.5.0 // indirect golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 - google.golang.org/api v0.65.0 + google.golang.org/api v0.66.0 google.golang.org/grpc v1.40.1 ) diff --git a/mmv1/third_party/terraform/go.sum b/mmv1/third_party/terraform/go.sum index aa3ea0060ea8..fc8c8f757b55 100644 --- a/mmv1/third_party/terraform/go.sum +++ b/mmv1/third_party/terraform/go.sum @@ -1153,6 +1153,8 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1311,6 +1313,8 @@ google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tD google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= google.golang.org/api v0.65.0 h1:MTW9c+LIBAbwoS1Gb+YV7NjFBt2f7GtAS5hIzh2NjgQ= google.golang.org/api v0.65.0/go.mod h1:ArYhxgGadlWmqO1IqVujw6Cs8IdD33bTmzKo2Sh+cbg= +google.golang.org/api v0.66.0 h1:CbGy4LEiXCVCiNEDFgGpWOVwsDT7E2Qej1ZvN1P7KPg= +google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1394,6 +1398,8 @@ google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998 h1:g/x+MYjJYDEP3OBCYYmwIbt4x6k3gryb+ohyOR7PXfI= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 h1:aCsSLXylHWFno0r4S3joLpiaWayvqd2Mn4iSvx4WZZc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1495,4 +1501,4 @@ rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= \ No newline at end of file diff --git a/mmv1/third_party/terraform/resources/resource_sql_database_instance.go.erb b/mmv1/third_party/terraform/resources/resource_sql_database_instance.go.erb index dd752a0a3b39..1c2c338b8c8d 100644 --- a/mmv1/third_party/terraform/resources/resource_sql_database_instance.go.erb +++ b/mmv1/third_party/terraform/resources/resource_sql_database_instance.go.erb @@ -311,7 +311,6 @@ settings.backup_configuration.binary_log_enabled are both set to true.`, "allocated_ip_range": { Type: schema.TypeString, Optional: true, - ForceNew: true, AtLeastOneOf: ipConfigurationKeys, Description: `The name of the allocated ip range for the private ip CloudSQL instance. For example: "google-managed-services-default". If set, the instance ip will be created in the allocated range. The range name must comply with RFC 1035. Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])?.`, }, @@ -694,6 +693,11 @@ settings.backup_configuration.binary_log_enabled are both set to true.`, DiffSuppressFunc: timestampDiffSuppress(time.RFC3339Nano), Description: `The timestamp of the point in time that should be restored.`, }, + "allocated_ip_range": { + Type: schema.TypeString, + Optional: true, + Description: `The name of the allocated ip range for the private ip CloudSQL instance. For example: "google-managed-services-default". If set, the cloned instance ip will be created in the allocated range. The range name must comply with [RFC 1035](https://tools.ietf.org/html/rfc1035). Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])?.`, + }, }, }, }, @@ -1005,7 +1009,8 @@ func expandCloneContext(configured []interface{}) (*sqladmin.CloneContext, strin _cloneConfiguration := configured[0].(map[string]interface{}) return &sqladmin.CloneContext{ - PointInTime: _cloneConfiguration["point_in_time"].(string), + PointInTime: _cloneConfiguration["point_in_time"].(string), + AllocatedIpRange: _cloneConfiguration["allocated_ip_range"].(string), }, _cloneConfiguration["source_instance_name"].(string) } diff --git a/mmv1/third_party/terraform/tests/resource_sql_database_instance_test.go.erb b/mmv1/third_party/terraform/tests/resource_sql_database_instance_test.go.erb index 73fa5e281900..15eef48a263b 100644 --- a/mmv1/third_party/terraform/tests/resource_sql_database_instance_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_sql_database_instance_test.go.erb @@ -697,6 +697,8 @@ func TestAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRange(t *testi databaseName := "tf-test-" + randString(t, 10) addressName := "tf-test-" + randString(t, 10) networkName := BootstrapSharedTestNetwork(t, "sql-instance-private-allocated-ip-range") + addressName_update := "tf-test-" + randString(t, 10) + "update" + networkName_update := BootstrapSharedTestNetwork(t, "sql-instance-private-allocated-ip-range-update") vcrTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -712,10 +714,80 @@ func TestAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRange(t *testi ImportStateVerify: true, ImportStateVerifyIgnore: []string{"deletion_protection"}, }, + { + Config: testAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRange(databaseName, networkName_update, addressName_update), + }, + { + ResourceName: "google_sql_database_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, }, }) } +func TestAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeReplica(t *testing.T) { + t.Parallel() + + databaseName := "tf-test-" + randString(t, 10) + addressName := "tf-test-" + randString(t, 10) + networkName := BootstrapSharedTestNetwork(t, "sql-instance-private-replica") + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeReplica(databaseName, networkName, addressName), + }, + { + ResourceName: "google_sql_database_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + { + ResourceName: "google_sql_database_instance.replica1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: ignoredReplicaConfigurationFields, + }, + }, + }) +} + +func TestAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeClone(t *testing.T) { + t.Parallel() + + databaseName := "tf-test-" + randString(t, 10) + addressName := "tf-test-" + randString(t, 10) + networkName := BootstrapSharedTestNetwork(t, "sql-instance-private-clone") + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeClone(databaseName, networkName, addressName), + }, + { + ResourceName: "google_sql_database_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + { + ResourceName: "google_sql_database_instance.clone1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection", "clone"}, + }, + }, + }) +} func TestAccSqlDatabaseInstance_createFromBackup(t *testing.T) { // Sqladmin client @@ -1097,7 +1169,7 @@ resource "google_compute_global_address" "foobar" { name = "%s" purpose = "VPC_PEERING" address_type = "INTERNAL" - prefix_length = 24 + prefix_length = 16 network = data.google_compute_network.servicenet.self_link } @@ -1125,6 +1197,130 @@ resource "google_sql_database_instance" "instance" { `, networkName, addressRangeName, databaseName) } + +func testAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeReplica(databaseName, networkName, addressRangeName string) string { + return fmt.Sprintf(` +data "google_compute_network" "servicenet" { + name = "%s" +} + +resource "google_compute_global_address" "foobar" { + name = "%s" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = data.google_compute_network.servicenet.self_link +} + +resource "google_service_networking_connection" "foobar" { + network = data.google_compute_network.servicenet.self_link + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.foobar.name] +} + +resource "google_sql_database_instance" "instance" { + depends_on = [google_service_networking_connection.foobar] + name = "%s" + region = "us-central1" + database_version = "MYSQL_5_7" + deletion_protection = false + settings { + tier = "db-f1-micro" + ip_configuration { + ipv4_enabled = "false" + private_network = data.google_compute_network.servicenet.self_link + } + backup_configuration { + enabled = true + start_time = "00:00" + binary_log_enabled = true + } + } +} +resource "google_sql_database_instance" "replica1" { + depends_on = [google_service_networking_connection.foobar] + name = "%s-replica1" + region = "us-central1" + database_version = "MYSQL_5_7" + deletion_protection = false + settings { + tier = "db-f1-micro" + ip_configuration { + ipv4_enabled = "false" + private_network = data.google_compute_network.servicenet.self_link + allocated_ip_range = google_compute_global_address.foobar.name + } + } + + master_instance_name = google_sql_database_instance.instance.name + + replica_configuration { + connect_retry_interval = 100 + master_heartbeat_period = 10000 + password = "password" + username = "username" + ssl_cipher = "ALL" + verify_server_certificate = false + } +} +`, networkName, addressRangeName, databaseName, databaseName) +} + +func testAccSqlDatabaseInstance_withPrivateNetwork_withAllocatedIpRangeClone(databaseName, networkName, addressRangeName string) string { + return fmt.Sprintf(` +data "google_compute_network" "servicenet" { + name = "%s" +} + +resource "google_compute_global_address" "foobar" { + name = "%s" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = data.google_compute_network.servicenet.self_link +} + +resource "google_service_networking_connection" "foobar" { + network = data.google_compute_network.servicenet.self_link + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.foobar.name] +} + +resource "google_sql_database_instance" "instance" { + depends_on = [google_service_networking_connection.foobar] + name = "%s" + region = "us-central1" + database_version = "MYSQL_5_7" + deletion_protection = false + settings { + tier = "db-f1-micro" + ip_configuration { + ipv4_enabled = "false" + private_network = data.google_compute_network.servicenet.self_link + } + backup_configuration { + enabled = true + start_time = "00:00" + binary_log_enabled = true + } + } +} + +resource "google_sql_database_instance" "clone1" { + name = "%s-clone1" + region = "us-central1" + database_version = "MYSQL_5_7" + deletion_protection = false + + clone { + source_instance_name = google_sql_database_instance.instance.name + allocated_ip_range = google_compute_global_address.foobar.name + } + +} +`, networkName, addressRangeName, databaseName, databaseName) +} + var testGoogleSqlDatabaseInstance_settings = ` resource "google_sql_database_instance" "instance" { name = "%s" 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 decf00ab1bcd..d32e5661042f 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 @@ -379,6 +379,8 @@ The optional `clone` block supports: A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z". +* `allocated_ip_range` - (Optional) The name of the allocated ip range for the private ip CloudSQL instance. For example: "google-managed-services-default". If set, the cloned instance ip will be created in the allocated range. The range name must comply with [RFC 1035](https://tools.ietf.org/html/rfc1035). Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])?. + The optional `restore_backup_context` block supports: **NOTE:** Restoring from a backup is an imperative action and not recommended via Terraform. Adding or modifying this block during resource creation/update will trigger the restore action after the resource is created/updated.