Skip to content

Commit

Permalink
feat(GaussDB): gaussdb opengauss instance support update flavor (#6021)
Browse files Browse the repository at this point in the history
  • Loading branch information
houpeng80 authored Dec 13, 2024
1 parent 5907d60 commit eb9541e
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 19 deletions.
9 changes: 4 additions & 5 deletions docs/resources/gaussdb_opengauss_instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ The following arguments are supported:
The value must be `4` to `64` characters in length and start with a letter. It is case-sensitive and can contain only
letters, digits, hyphens (-), and underscores (_).

* `flavor` - (Required, String, ForceNew) Specifies the instance specifications. Please reference the API docs for valid
options. Changing this parameter will create a new resource.
* `flavor` - (Required, String) Specifies the instance specifications.

* `password` - (Required, String) Specifies the database password. The value must be `8` to `32` characters in length,
including uppercase and lowercase letters, digits, and special characters, such as **~!@#%^*-_=+?**. You are advised
Expand Down Expand Up @@ -293,7 +292,7 @@ The `nodes` block contains:
This resource provides the following timeouts configuration options:

* `create` - Default is 120 minutes.
* `update` - Default is 90 minutes.
* `update` - Default is 150 minutes.
* `delete` - Default is 45 minutes.

## Import
Expand All @@ -317,8 +316,8 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
lifecycle {
ignore_changes = [
password, ha.0.mode, ha.0.instance_mode, configuration_id, disk_encryption_id, enable_force_switch,
enable_single_float_ip, period_unit, period, auto_renew,
password, configuration_id, disk_encryption_id, enable_force_switch, enable_single_float_ip, period_unit, period,
auto_renew,
]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,13 @@ func TestAccOpenGaussInstance_basic(t *testing.T) {
resource.TestCheckResourceAttrPair(resourceName, "subnet_id", "huaweicloud_vpc_subnet.test", "id"),
resource.TestCheckResourceAttrPair(resourceName, "security_group_id",
"huaweicloud_networking_secgroup.test", "id"),
resource.TestCheckResourceAttr(resourceName, "flavor", "gaussdb.opengauss.ee.dn.m6.2xlarge.8.in"),
resource.TestCheckResourceAttrPair(resourceName, "flavor",
"data.huaweicloud_gaussdb_opengauss_flavors.test", "flavors.0.spec_code"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "password", password),
resource.TestCheckResourceAttr(resourceName, "ha.0.mode", "enterprise"),
resource.TestCheckResourceAttr(resourceName, "ha.0.replication_mode", "sync"),
resource.TestCheckResourceAttr(resourceName, "ha.0.consistency", "strong"),
resource.TestCheckResourceAttr(resourceName, "ha.0.consistency", "eventual"),
resource.TestCheckResourceAttr(resourceName, "volume.0.type", "ULTRAHIGH"),
resource.TestCheckResourceAttr(resourceName, "volume.0.size", "40"),
resource.TestCheckResourceAttr(resourceName, "sharding_num", "1"),
Expand All @@ -105,6 +106,8 @@ func TestAccOpenGaussInstance_basic(t *testing.T) {
Config: testAccOpenGaussInstance_update(rName, newPassword, 3),
Check: resource.ComposeTestCheckFunc(
rc.CheckResourceExists(),
resource.TestCheckResourceAttrPair(resourceName, "flavor",
"data.huaweicloud_gaussdb_opengauss_flavors.test", "flavors.1.spec_code"),
resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("%s-update", rName)),
resource.TestCheckResourceAttr(resourceName, "password", newPassword),
resource.TestCheckResourceAttr(resourceName, "sharding_num", "2"),
Expand Down Expand Up @@ -151,7 +154,8 @@ func TestAccOpenGaussInstance_haModeCentralized(t *testing.T) {
resource.TestCheckResourceAttrPair(resourceName, "subnet_id", "huaweicloud_vpc_subnet.test", "id"),
resource.TestCheckResourceAttrPair(resourceName, "security_group_id",
"huaweicloud_networking_secgroup.test", "id"),
resource.TestCheckResourceAttr(resourceName, "flavor", "gaussdb.bs.s6.xlarge.x864.ha"),
resource.TestCheckResourceAttrPair(resourceName, "flavor",
"data.huaweicloud_gaussdb_opengauss_flavors.test", "flavors.0.spec_code"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "ha.0.mode", "centralization_standard"),
resource.TestCheckResourceAttr(resourceName, "ha.0.replication_mode", "sync"),
Expand All @@ -165,6 +169,8 @@ func TestAccOpenGaussInstance_haModeCentralized(t *testing.T) {
Config: testAccOpenGaussInstance_haModeCentralizedUpdate(rName, newPassword),
Check: resource.ComposeTestCheckFunc(
rc.CheckResourceExists(),
resource.TestCheckResourceAttrPair(resourceName, "flavor",
"data.huaweicloud_gaussdb_opengauss_flavors.test", "flavors.1.spec_code"),
resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("%s-update", rName)),
resource.TestCheckResourceAttr(resourceName, "volume.0.size", "80"),
resource.TestCheckResourceAttr(resourceName, "backup_strategy.0.start_time", "08:00-09:00"),
Expand Down Expand Up @@ -205,6 +211,11 @@ func testAccOpenGaussInstance_basic(rName, password string, replicaNum int) stri
return fmt.Sprintf(`
%[1]s
data "huaweicloud_gaussdb_opengauss_flavors" "test" {
version = "8.201"
ha_mode = "enterprise"
}
resource "huaweicloud_gaussdb_opengauss_instance" "test" {
depends_on = [
huaweicloud_networking_secgroup_rule.in_v4_tcp_opengauss,
Expand All @@ -215,7 +226,7 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
subnet_id = huaweicloud_vpc_subnet.test.id
security_group_id = huaweicloud_networking_secgroup.test.id
flavor = "gaussdb.bs.s6.xlarge.x864.ha"
flavor = data.huaweicloud_gaussdb_opengauss_flavors.test.flavors[0].spec_code
name = "%[2]s"
password = "%[3]s"
sharding_num = 1
Expand All @@ -228,10 +239,10 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
enterprise_project_id = "%[5]s"
ha {
mode = "centralization_standard"
mode = "enterprise"
replication_mode = "sync"
consistency = "eventual"
instance_mode = "basic"
instance_mode = "enterprise"
}
volume {
Expand All @@ -251,6 +262,11 @@ func testAccOpenGaussInstance_update(rName, password string, replicaNum int) str
return fmt.Sprintf(`
%[1]s
data "huaweicloud_gaussdb_opengauss_flavors" "test" {
version = "8.201"
ha_mode = "enterprise"
}
resource "huaweicloud_gaussdb_opengauss_instance" "test" {
depends_on = [
huaweicloud_networking_secgroup_rule.in_v4_tcp_opengauss,
Expand All @@ -261,7 +277,7 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
subnet_id = huaweicloud_vpc_subnet.test.id
security_group_id = huaweicloud_networking_secgroup.test.id
flavor = "gaussdb.bs.s6.xlarge.x864.ha"
flavor = data.huaweicloud_gaussdb_opengauss_flavors.test.flavors[1].spec_code
name = "%[2]s-update"
password = "%[3]s"
sharding_num = 2
Expand All @@ -274,10 +290,10 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
enterprise_project_id = "%[5]s"
ha {
mode = "centralization_standard"
mode = "enterprise"
replication_mode = "sync"
consistency = "eventual"
instance_mode = "basic"
instance_mode = "enterprise"
}
volume {
Expand All @@ -297,6 +313,11 @@ func testAccOpenGaussInstance_haModeCentralized(rName, password string) string {
return fmt.Sprintf(`
%[1]s
data "huaweicloud_gaussdb_opengauss_flavors" "test" {
version = "8.201"
ha_mode = "centralization_standard"
}
resource "huaweicloud_gaussdb_opengauss_instance" "test" {
depends_on = [
huaweicloud_networking_secgroup_rule.in_v4_tcp_opengauss,
Expand All @@ -306,7 +327,7 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
subnet_id = huaweicloud_vpc_subnet.test.id
security_group_id = huaweicloud_networking_secgroup.test.id
flavor = "gaussdb.bs.s6.xlarge.x864.ha"
flavor = data.huaweicloud_gaussdb_opengauss_flavors.test.flavors[0].spec_code
name = "%[2]s"
password = "%[3]s"
replica_num = 3
Expand Down Expand Up @@ -340,6 +361,11 @@ func testAccOpenGaussInstance_haModeCentralizedUpdate(rName, password string) st
return fmt.Sprintf(`
%[1]s
data "huaweicloud_gaussdb_opengauss_flavors" "test" {
version = "8.201"
ha_mode = "centralization_standard"
}
resource "huaweicloud_gaussdb_opengauss_instance" "test" {
depends_on = [
huaweicloud_networking_secgroup_rule.in_v4_tcp_opengauss,
Expand All @@ -350,7 +376,7 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" {
subnet_id = huaweicloud_vpc_subnet.test.id
security_group_id = huaweicloud_networking_secgroup.test.id
flavor = "gaussdb.bs.s6.xlarge.x864.ha"
flavor = data.huaweicloud_gaussdb_opengauss_flavors.test.flavors[1].spec_code
name = "%[2]s-update"
password = "%[3]s"
replica_num = 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -32,6 +33,7 @@ const (
// @API GaussDB POST /v3/{project_id}/instances/{instance_id}/password
// @API GaussDB POST /v3/{project_id}/instances/{instance_id}/action
// @API GaussDB PUT /v3/{project_id}/instances/{instance_id}/backups/policy
// @API GaussDB PUT /v3/{project_id}/instance/{instance_id}/flavor
// @API GaussDB DELETE /v3/{project_id}/instances/{instance_id}
// @API BSS GET /v2/orders/customer-orders/details/{order_id}
// @API BSS POST /v2/orders/suscriptions/resources/query
Expand All @@ -51,7 +53,7 @@ func ResourceOpenGaussInstance() *schema.Resource {

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(120 * time.Minute),
Update: schema.DefaultTimeout(90 * time.Minute),
Update: schema.DefaultTimeout(150 * time.Minute),
Delete: schema.DefaultTimeout(45 * time.Minute),
},
CustomizeDiff: func(_ context.Context, d *schema.ResourceDiff, v interface{}) error {
Expand All @@ -75,7 +77,6 @@ func ResourceOpenGaussInstance() *schema.Resource {
"flavor": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"availability_zone": {
Type: schema.TypeString,
Expand Down Expand Up @@ -619,7 +620,7 @@ func resourceOpenGaussInstanceRead(_ context.Context, d *schema.ResourceData, me
func flattenGaussDBOpenGaussResponseBodyHa(instance interface{}) []interface{} {
rst := []interface{}{
map[string]interface{}{
"mode": utils.PathSearch("type", instance, nil),
"mode": strings.ToLower(utils.PathSearch("type", instance, "").(string)),
"replication_mode": utils.PathSearch("ha.replication_mode", instance, nil),
"consistency": utils.PathSearch("ha.consistency", instance, nil),
"instance_mode": utils.PathSearch("instance_mode", instance, nil),
Expand Down Expand Up @@ -775,6 +776,12 @@ func resourceOpenGaussInstanceUpdate(ctx context.Context, d *schema.ResourceData
}
}

if d.HasChange("flavor") {
if err = updateInstanceFlavor(ctx, d, client, bssClient); err != nil {
return diag.FromErr(err)
}
}

if d.HasChange("auto_renew") {
if err = common.UpdateAutoRenew(bssClient, d.Get("auto_renew").(string), d.Id()); err != nil {
return diag.Errorf("error updating the auto-renew of the instance (%s): %s", d.Id(), err)
Expand Down Expand Up @@ -1046,6 +1053,87 @@ func buildUpdateInstanceBackupStrategyBodyParams(d *schema.ResourceData) map[str
return bodyParams
}

func updateInstanceFlavor(ctx context.Context, d *schema.ResourceData, client, bssClient *golangsdk.ServiceClient) error {
var (
httpUrl = "v3/{project_id}/instance/{instance_id}/flavor"
)

updatePath := client.Endpoint + httpUrl
updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Id())

updateOpt := golangsdk.RequestOpts{
KeepResponseBody: true,
}
updateOpt.JSONBody = utils.RemoveNil(buildUpdateInstanceFlavorBodyParams(d))
retryFunc := func() (interface{}, bool, error) {
res, err := client.Request("PUT", updatePath, &updateOpt)
retry, err := handleMultiOperationsError(err)
return res, retry, err
}
r, err := common.RetryContextWithWaitForState(&common.RetryContextWithWaitForStateParam{
Ctx: ctx,
RetryFunc: retryFunc,
WaitFunc: instanceStateRefreshFunc(client, d.Id()),
WaitTarget: []string{"ACTIVE"},
Timeout: d.Timeout(schema.TimeoutUpdate),
DelayTimeout: 10 * time.Second,
PollInterval: 10 * time.Second,
})
if err != nil {
return fmt.Errorf("error updating GaussDB OpenGauss instance (%s) flavor: %s", d.Id(), err)
}

updateRespBody, err := utils.FlattenResponse(r.(*http.Response))
if err != nil {
return err
}

if v, ok := d.GetOk("charging_mode"); ok && v.(string) == "prePaid" {
orderId := utils.PathSearch("order_id", updateRespBody, nil)
if orderId == nil {
return fmt.Errorf("error updating GaussDB OpenGauss instance flavor: order_id is not found in API response")
}
// wait for order success
err = common.WaitOrderComplete(ctx, bssClient, orderId.(string), d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}
} else {
jobId := utils.PathSearch("job_id", updateRespBody, nil)
if jobId == nil {
return fmt.Errorf("error updating GaussDB OpenGauss instance flavor: job_id is not found in API response")
}
err = checkGaussDBOpenGaussJobFinish(ctx, client, jobId.(string), 180, d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}
}

stateConf := &resource.StateChangeConf{
Pending: []string{"MODIFYING", "BACKING UP"},
Target: []string{"ACTIVE"},
Refresh: instanceStateRefreshFunc(client, d.Id()),
Timeout: d.Timeout(schema.TimeoutUpdate),
PollInterval: 10 * time.Second,
}

_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return fmt.Errorf("error waiting for instance (%s) to become ready: %s", d.Id(), err)
}

return nil
}

func buildUpdateInstanceFlavorBodyParams(d *schema.ResourceData) map[string]interface{} {
bodyParams := map[string]interface{}{
"flavor_ref": d.Get("flavor"),
"is_auto_pay": true,
}
return bodyParams
}

func resourceOpenGaussInstanceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)
Expand Down

0 comments on commit eb9541e

Please sign in to comment.