From 36c48290edb0b19366c991600fdb7d52738095ff Mon Sep 17 00:00:00 2001 From: Zhenhua Li Date: Wed, 25 Sep 2024 14:10:14 -0700 Subject: [PATCH] Add support for AlloyDB Cluster Major Version Upgrade --- mmv1/products/alloydb/Cluster.yaml | 3 +- .../pre_update/alloydb_cluster.go.tmpl | 34 +++++++ .../alloydb/resource_alloydb_cluster_test.go | 97 +++++++++++++++++-- 3 files changed, 127 insertions(+), 7 deletions(-) diff --git a/mmv1/products/alloydb/Cluster.yaml b/mmv1/products/alloydb/Cluster.yaml index ad5967241524..bc3ac954e488 100644 --- a/mmv1/products/alloydb/Cluster.yaml +++ b/mmv1/products/alloydb/Cluster.yaml @@ -262,7 +262,8 @@ properties: - name: 'databaseVersion' type: String description: | - The database engine major version. This is an optional field and it's populated at the Cluster creation time. This field cannot be changed after cluster creation. + The database engine major version. This is an optional field and it's populated at the Cluster creation time. + Note: Changing this field to a higer version results in upgrading the AlloyDB cluster which is an irreversible change. default_from_api: true - name: 'pscConfig' type: NestedObject diff --git a/mmv1/templates/terraform/pre_update/alloydb_cluster.go.tmpl b/mmv1/templates/terraform/pre_update/alloydb_cluster.go.tmpl index dcbd075fb019..1829997b0644 100644 --- a/mmv1/templates/terraform/pre_update/alloydb_cluster.go.tmpl +++ b/mmv1/templates/terraform/pre_update/alloydb_cluster.go.tmpl @@ -1,3 +1,37 @@ +// Implementation for cluster upgrade +if d.HasChange("database_version") && !tpgresource.IsEmptyValue(reflect.ValueOf(d.Get("database_version"))) { + upgradeUrl := strings.Split(url, "?updateMask")[0] + ":upgrade" + patchObj := make(map[string]interface{}) + patchObj["version"] = obj["databaseVersion"] + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + upgradeClusterTimeout := 58 * time.Hour + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: upgradeUrl, + UserAgent: userAgent, + Body: patchObj, + Timeout: upgradeClusterTimeout, + }) + if err != nil { + return fmt.Errorf("Error upgrading cluster: %v", err) + } + + err = AlloydbOperationWaitTime( + config, res, project, "Upgrading cluster", userAgent, + upgradeClusterTimeout) + + if err != nil { + return fmt.Errorf("Error waiting to upgrade cluster: %s", err) + } + + log.Printf("[DEBUG] Finished upgrading cluster %q: %#v", d.Id(), res) +} + // Restrict setting secondary_config if cluster_type is PRIMARY if d.Get("cluster_type") == "PRIMARY" && !tpgresource.IsEmptyValue(reflect.ValueOf(d.Get("secondary_config"))) { return fmt.Errorf("Can not set secondary config for primary cluster.") diff --git a/mmv1/third_party/terraform/services/alloydb/resource_alloydb_cluster_test.go b/mmv1/third_party/terraform/services/alloydb/resource_alloydb_cluster_test.go index 5982f83a2f4b..7f305e3be9e0 100644 --- a/mmv1/third_party/terraform/services/alloydb/resource_alloydb_cluster_test.go +++ b/mmv1/third_party/terraform/services/alloydb/resource_alloydb_cluster_test.go @@ -56,21 +56,106 @@ resource "google_alloydb_cluster" "default" { network_config { network = "projects/${data.google_project.project.number}/global/networks/${google_compute_network.default.name}" } +} + +data "google_project" "project" { +} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-cluster%{random_suffix}" +} +`, context) +} + +func TestAccAlloydbCluster_upgrade(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "alloydb-instance-upgrade-1"), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbCluster_beforeUpgrade(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"initial_user", "cluster_id", "location", "labels", "terraform_labels"}, + }, + { + Config: testAccAlloydbCluster_afterUpgrade(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"initial_user", "cluster_id", "location", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccAlloydbCluster_beforeUpgrade(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network_config { + network = data.google_compute_network.default.id + } labels = { - foo = "bar" + test = "tf-test-alloydb-cluster%{random_suffix}" } + database_version = "POSTGRES_14" +} - lifecycle { - prevent_destroy = true +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "tf-test-alloydb-instance%{random_suffix}" + instance_type = "PRIMARY" + + machine_config { + cpu_count = 8 } } -data "google_project" "project" { +data "google_compute_network" "default" { + name = "%{network_name}" +} +`, context) } -resource "google_compute_network" "default" { - name = "tf-test-alloydb-cluster%{random_suffix}" +func testAccAlloydbCluster_afterUpgrade(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network_config { + network = data.google_compute_network.default.id + } + database_version = "POSTGRES_15" +} + +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "tf-test-alloydb-instance%{random_suffix}" + instance_type = "PRIMARY" + + machine_config { + cpu_count = 4 + } +} + +data "google_compute_network" "default" { + name = "%{network_name}" } `, context) }