diff --git a/docs/resources/gaussdb_mysql_proxy.md b/docs/resources/gaussdb_mysql_proxy.md
index 8b93d3ffb0..60d632b184 100644
--- a/docs/resources/gaussdb_mysql_proxy.md
+++ b/docs/resources/gaussdb_mysql_proxy.md
@@ -2,23 +2,22 @@
subcategory: "GaussDB(for MySQL)"
layout: "huaweicloud"
page_title: "HuaweiCloud: huaweicloud_gaussdb_mysql_proxy"
-description: ""
+description: |-
+ Manages GaussDB mysql proxy resource within HuaweiCloud.
---
# huaweicloud_gaussdb_mysql_proxy
-GaussDB mysql proxy management within HuaweiCould.
+Manages GaussDB mysql proxy resource within HuaweiCloud.
## Example Usage
-### create a proxy
-
```hcl
variable "instance_id" {}
-resource "huaweicloud_gaussdb_mysql_proxy" "proxy_1" {
+resource "huaweicloud_gaussdb_mysql_proxy" "test" {
instance_id = var.instance_id
- flavor = "gaussdb.proxy.xlarge.arm.2"
+ flavor = "gaussdb.proxy.xlarge.x86.2"
node_num = 3
}
```
@@ -30,34 +29,126 @@ The following arguments are supported:
* `region` - (Optional, String, ForceNew) The region in which to create the GaussDB mysql proxy resource. If omitted,
the provider-level region will be used. Changing this creates a new resource.
-* `instance_id` - (Required, String, ForceNew) Specifies the instance ID of the proxy.
- Changing this parameter will create a new resource.
+* `instance_id` - (Required, String, ForceNew) Specifies the ID of the GaussDB MySQL instance. Changing this parameter
+ will create a new resource.
-* `flavor` - (Required, String, ForceNew) Specifies the flavor of the proxy.
- Changing this parameter will create a new resource.
+* `flavor` - (Required, String, ForceNew) Specifies the flavor of the proxy. Changing this parameter will create a new
+ resource.
* `node_num` - (Required, Int) Specifies the node count of the proxy.
+* `proxy_name` - (Optional, String) Specifies the name of the proxy. The name consists of **4** to **64** characters and
+ starts with a letter. It is case-sensitive and can contain only letters, digits, hyphens (-), and underscores (_).
+
+* `proxy_mode` - (Optional, String, ForceNew) Specifies the type of the proxy. Changing this creates a new resource.
+ Value options:
+ + **readwrite**: read and write.
+ + **readonly**: read-only.
+
+ Defaults to **readwrite**.
+
+* `route_mode` - (Optional, Int, ForceNew) Specifies the routing policy of the proxy. Changing this creates a new
+ resource. Value options:
+ + **0**: weighted load balancing.
+ + **1**: load balancing (The primary node does not process read requests).
+ + **2**: load balancing (The primary node processes read requests).
+
+* `subnet_id` - (Optional, String, ForceNew) Specifies the network ID of a subnet. Changing this creates a new resource.
+
+* `master_node_weight` - (Optional, List) Specifies the read weight of the master node.
+ The [master_node_weight](#node_weight_struct) structure is documented below.
+
+* `readonly_nodes_weight` - (Optional, List) Specifies the read weight of the read-only node.
+ The [readonly_nodes_weight](#node_weight_struct) structure is documented below.
+
+* `new_node_auto_add_status` - (Optional, String) Specifies whether new nodes are automatically associate with proxy.
+ Value options:
+ + **ON**: New nodes are automatically associate with proxy.
+ + **OFF**: New nodes are not automatically associate with proxy.
+
+ -> **NOTE:** To configure this parameter, contact customer service.
+
+* `new_node_weight` - (Optional, Int) Specifies the read weight of the new node.
+ + If `route_mode` is **0** and `new_node_auto_add_status` is **ON**, the value of this parameter ranges from **0** to **1000**.
+ + If `route_mode` is not **0** and `new_node_auto_add_status` is **OFF**, this parameter is unavailable.
+
+* `port` - (Optional, Int) Specifies the port of the proxy.
+
+
+The `master_node_weight` and `readonly_nodes_weight` block supports:
+
+* `id` - (Required, String) Specifies the ID of the node.
+
+* `weight` - (Required, Int) Specifies the weight assigned to the node.
+ + If `route_mode` is **0**, the value is **0** to **1000**.
+ + If `route_mode` is **1**, the value for the primary node is **0** and the value for read replicas is **0** or **1**.
+ + If `route_mode` is **2**, the value for the primary node is **1** and the value for read replicas is **0** or **1**.
+
## Attribute Reference
In addition to all arguments above, the following attributes are exported:
-* `id` - Indicates the resource ID in UUID format.
+* `id` - Indicates the resource ID.
+
* `address` - Indicates the address of the proxy.
-* `port` - Indicates the port of the proxy.
+
+* `nodes` - Indicates the node information of the proxy.
+ The [nodes](#nodes_struct) structure is documented below.
+
+
+The `nodes` block supports:
+
+* `id` - Indicates the proxy node ID.
+
+* `status` - Indicates the proxy node status. The values can be:
+ + **ACTIVE**: The node is available.
+ + **ABNORMAL**: The node is abnormal.
+ + **FAILED**: The node fails.
+ + **DELETED**: The node has been deleted.
+
+* `name` - Indicates the proxy node name.
+
+* `role` - Indicates the proxy node role. The values can be:
+ + **master**: primary node.
+ + **slave**: read replica.
+
+* `az_code` - Indicates the proxy node AZ.
+
+* `frozen_flag` - Indicates whether the proxy node is frozen. The values can be:
+ + **0**: unfrozen.
+ + **1**: frozen.
+ + **2**: deleted after being frozen.
## Timeouts
This resource provides the following timeouts configuration options:
* `create` - Default is 30 minutes.
-* `update` - Default is 10 minutes.
+* `update` - Default is 30 minutes.
* `delete` - Default is 10 minutes.
## Import
-GaussDB instance can be imported using the instance `id`, e.g.
+The GaussDB MySQL proxy can be imported using the `instance_id` and `id` separated by a slash, e.g.
+```bash
+$ terraform import huaweicloud_gaussdb_mysql_proxy.test /
```
-$ terraform import huaweicloud_gaussdb_mysql_proxy.proxy_1 ee678f40-ce8e-4d0c-8221-38dead426f06
+
+Note that the imported state may not be identical to your resource definition, due to the attribute missing from the
+API response. The missing attribute is: `new_node_weight`, `proxy_mode`, `readonly_nodes_weight`. It is generally
+recommended running `terraform plan` after importing a GaussDB MySQL proxy. You can then decide if changes should be
+applied to the GaussDB MySQL proxy, or the resource definition should be updated to align with the GaussDB MySQL proxy.
+Also you can ignore changes as below.
+
+```hcl
+resource "huaweicloud_gaussdb_mysql_proxy" "test" {
+ ...
+
+ lifecycle {
+ ignore_changes = [
+ new_node_weight, proxy_mode, readonly_nodes_weight,
+ ]
+ }
+}
```
diff --git a/huaweicloud/services/acceptance/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy_test.go b/huaweicloud/services/acceptance/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy_test.go
index 2cdd010022..ac74c7135a 100644
--- a/huaweicloud/services/acceptance/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy_test.go
+++ b/huaweicloud/services/acceptance/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy_test.go
@@ -1,31 +1,72 @@
package gaussdb
import (
+ "encoding/json"
"fmt"
+ "strings"
"testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+ "github.com/chnsz/golangsdk"
"github.com/chnsz/golangsdk/openstack/taurusdb/v3/instances"
+ "github.com/chnsz/golangsdk/pagination"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance/common"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
)
-func getResourceProxy(conf *config.Config, state *terraform.ResourceState) (interface{}, error) {
- client, err := conf.GaussdbV3Client(acceptance.HW_REGION_NAME)
+func getResourceProxy(cfg *config.Config, state *terraform.ResourceState) (interface{}, error) {
+ region := acceptance.HW_REGION_NAME
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxies"
+ product = "gaussdb"
+ )
+ client, err := cfg.NewServiceClient(product, region)
+ if err != nil {
+ return nil, fmt.Errorf("error creating GaussDB client: %s", err)
+ }
+
+ listPath := client.Endpoint + httpUrl
+ listPath = strings.ReplaceAll(listPath, "{project_id}", client.ProjectID)
+ listPath = strings.ReplaceAll(listPath, "{instance_id}", state.Primary.Attributes["instance_id"])
+
+ listMysqlDatabasesResp, err := pagination.ListAllItems(
+ client,
+ "offset",
+ listPath,
+ &pagination.QueryOpts{MarkerField: ""})
+
if err != nil {
- return nil, fmt.Errorf("Error creating HuaweiCloud GaussDB client: %s", err)
+ return nil, err
}
- return instances.GetProxy(client, state.Primary.ID).Extract()
+ listRespJson, err := json.Marshal(listMysqlDatabasesResp)
+ if err != nil {
+ return nil, err
+ }
+ var listRespBody interface{}
+ err = json.Unmarshal(listRespJson, &listRespBody)
+ if err != nil {
+ return nil, err
+ }
+
+ searchExpression := fmt.Sprintf("proxy_list[?proxy.pool_id=='%s']|[0]", state.Primary.ID)
+ proxy := utils.PathSearch(searchExpression, listRespBody, nil)
+ if proxy == nil {
+ return nil, golangsdk.ErrDefault404{}
+ }
+
+ return proxy, nil
}
-func TestAccGaussDBProxy_basic(t *testing.T) {
+func TestAccGaussDBMySQLProxy_basic(t *testing.T) {
var proxy instances.Proxy
rName := acceptance.RandomAccResourceName()
+ updateName := acceptance.RandomAccResourceName()
resourceName := "huaweicloud_gaussdb_mysql_proxy.test"
rc := acceptance.InitResourceCheck(
@@ -43,7 +84,45 @@ func TestAccGaussDBProxy_basic(t *testing.T) {
Config: testAccMysqlProxy_basic(rName),
Check: resource.ComposeTestCheckFunc(
rc.CheckResourceExists(),
- resource.TestCheckResourceAttr(resourceName, "flavor", "gaussdb.proxy.xlarge.arm.2"),
+ resource.TestCheckResourceAttrPair(resourceName, "instance_id",
+ "huaweicloud_gaussdb_mysql_instance.test", "id"),
+ resource.TestCheckResourceAttrPair(resourceName, "flavor",
+ "data.huaweicloud_gaussdb_mysql_proxy_flavors.test", "flavor_groups.0.flavors.0.spec_code"),
+ resource.TestCheckResourceAttr(resourceName, "node_num", "2"),
+ resource.TestCheckResourceAttr(resourceName, "proxy_name", rName),
+ resource.TestCheckResourceAttr(resourceName, "route_mode", "1"),
+ resource.TestCheckResourceAttrPair(resourceName, "subnet_id",
+ "huaweicloud_vpc_subnet.test", "id"),
+ resource.TestCheckResourceAttr(resourceName, "new_node_auto_add_status", "OFF"),
+ resource.TestCheckResourceAttr(resourceName, "port", "3339"),
+ resource.TestCheckResourceAttr(resourceName, "master_node_weight.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "readonly_nodes_weight.#", "1"),
+ ),
+ },
+ {
+ Config: testAccMysqlProxy_basic_update(rName, updateName),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttrPair(resourceName, "instance_id",
+ "huaweicloud_gaussdb_mysql_instance.test", "id"),
+ resource.TestCheckResourceAttrPair(resourceName, "flavor",
+ "data.huaweicloud_gaussdb_mysql_proxy_flavors.test", "flavor_groups.0.flavors.1.spec_code"),
+ resource.TestCheckResourceAttr(resourceName, "node_num", "4"),
+ resource.TestCheckResourceAttr(resourceName, "proxy_name", updateName),
+ resource.TestCheckResourceAttr(resourceName, "route_mode", "1"),
+ resource.TestCheckResourceAttrPair(resourceName, "subnet_id",
+ "huaweicloud_vpc_subnet.test", "id"),
+ resource.TestCheckResourceAttr(resourceName, "new_node_auto_add_status", "ON"),
+ resource.TestCheckResourceAttr(resourceName, "new_node_weight", "20"),
+ resource.TestCheckResourceAttr(resourceName, "port", "3338"),
+ resource.TestCheckResourceAttr(resourceName, "master_node_weight.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "readonly_nodes_weight.#", "2"),
+ ),
+ },
+ {
+ Config: testAccMysqlProxy_basic_reduce_node(rName, updateName),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
resource.TestCheckResourceAttr(resourceName, "node_num", "3"),
),
},
@@ -51,38 +130,156 @@ func TestAccGaussDBProxy_basic(t *testing.T) {
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
+ ImportStateIdFunc: testGaussDBMysqlProxyResourceImportState(resourceName),
ImportStateVerifyIgnore: []string{
- "instance_id",
- "flavor",
- "node_num",
+ "new_node_weight",
+ "proxy_mode",
+ "readonly_nodes_weight",
},
},
},
})
}
-func testAccMysqlProxy_basic(rName string) string {
+func testAccMysqlProxy_base(rName string) string {
return fmt.Sprintf(`
%s
data "huaweicloud_availability_zones" "test" {}
+data "huaweicloud_gaussdb_mysql_flavors" "test" {
+ engine = "gaussdb-mysql"
+ version = "8.0"
+ availability_zone_mode = "multi"
+}
+
resource "huaweicloud_gaussdb_mysql_instance" "test" {
- name = "%s"
- password = "Test@12345678"
- flavor = "gaussdb.mysql.2xlarge.x86.4"
- vpc_id = huaweicloud_vpc.test.id
- subnet_id = huaweicloud_vpc_subnet.test.id
+ name = "%s"
+ password = "Test@12345678"
+ flavor = data.huaweicloud_gaussdb_mysql_flavors.test.flavors[0].name
+ vpc_id = huaweicloud_vpc.test.id
+ subnet_id = huaweicloud_vpc_subnet.test.id
+ security_group_id = huaweicloud_networking_secgroup.test.id
+ enterprise_project_id = "0"
+ master_availability_zone = data.huaweicloud_availability_zones.test.names[0]
+ availability_zone_mode = "multi"
+ read_replicas = 4
+}
- security_group_id = huaweicloud_networking_secgroup.test.id
+data "huaweicloud_gaussdb_mysql_proxy_flavors" "test" {
+ instance_id = huaweicloud_gaussdb_mysql_instance.test.id
+}
- enterprise_project_id = "0"
+locals{
+ sort_nodes = tolist(values({ for node in huaweicloud_gaussdb_mysql_instance.test.nodes : node.name => node }))
}
+`, common.TestBaseNetwork(rName), rName)
+}
+
+func testAccMysqlProxy_basic(rName string) string {
+ return fmt.Sprintf(`
+%[1]s
resource "huaweicloud_gaussdb_mysql_proxy" "test" {
- instance_id = huaweicloud_gaussdb_mysql_instance.test.id
- flavor = "gaussdb.proxy.xlarge.arm.2"
- node_num = 3
+ instance_id = huaweicloud_gaussdb_mysql_instance.test.id
+ flavor = data.huaweicloud_gaussdb_mysql_proxy_flavors.test.flavor_groups[0].flavors[0].spec_code
+ node_num = 2
+ proxy_name = "%[2]s"
+ proxy_mode = "readwrite"
+ route_mode = 1
+ subnet_id = huaweicloud_vpc_subnet.test.id
+ new_node_auto_add_status = "OFF"
+ new_node_weight = 20
+ port = 3339
+
+ master_node_weight {
+ id = local.sort_nodes[0].id
+ weight = 20
+ }
+
+ readonly_nodes_weight {
+ id = local.sort_nodes[1].id
+ weight = 30
+ }
}
-`, common.TestBaseNetwork(rName), rName)
+`, testAccMysqlProxy_base(rName), rName)
+}
+
+func testAccMysqlProxy_basic_update(rName, updateName string) string {
+ return fmt.Sprintf(`
+%[1]s
+
+resource "huaweicloud_gaussdb_mysql_proxy" "test" {
+ instance_id = huaweicloud_gaussdb_mysql_instance.test.id
+ flavor = data.huaweicloud_gaussdb_mysql_proxy_flavors.test.flavor_groups[0].flavors[1].spec_code
+ node_num = 4
+ proxy_name = "%[2]s"
+ proxy_mode = "readwrite"
+ route_mode = 1
+ subnet_id = huaweicloud_vpc_subnet.test.id
+ new_node_auto_add_status = "ON"
+ new_node_weight = 20
+ port = 3338
+
+ master_node_weight {
+ id = local.sort_nodes[0].id
+ weight = 10
+ }
+
+ readonly_nodes_weight {
+ id = local.sort_nodes[2].id
+ weight = 20
+ }
+
+ readonly_nodes_weight {
+ id = local.sort_nodes[3].id
+ weight = 30
+ }
+}
+`, testAccMysqlProxy_base(rName), updateName)
+}
+
+func testAccMysqlProxy_basic_reduce_node(rName, updateName string) string {
+ return fmt.Sprintf(`
+%[1]s
+
+resource "huaweicloud_gaussdb_mysql_proxy" "test" {
+ instance_id = huaweicloud_gaussdb_mysql_instance.test.id
+ flavor = data.huaweicloud_gaussdb_mysql_proxy_flavors.test.flavor_groups[0].flavors[1].spec_code
+ node_num = 3
+ proxy_name = "%[2]s"
+ proxy_mode = "readwrite"
+ route_mode = 1
+ subnet_id = huaweicloud_vpc_subnet.test.id
+ new_node_auto_add_status = "ON"
+ new_node_weight = 20
+ port = 3338
+
+ master_node_weight {
+ id = local.sort_nodes[0].id
+ weight = 10
+ }
+
+ readonly_nodes_weight {
+ id = local.sort_nodes[2].id
+ weight = 20
+ }
+
+ readonly_nodes_weight {
+ id = local.sort_nodes[3].id
+ weight = 30
+ }
+}
+`, testAccMysqlProxy_base(rName), updateName)
+}
+
+func testGaussDBMysqlProxyResourceImportState(name string) resource.ImportStateIdFunc {
+ return func(s *terraform.State) (string, error) {
+ rs, ok := s.RootModule().Resources[name]
+ if !ok {
+ return "", fmt.Errorf("resource (%s) not found: %s", name, rs)
+ }
+ instanceID := rs.Primary.Attributes["instance_id"]
+ return fmt.Sprintf("%s/%s", instanceID, rs.Primary.ID), nil
+ }
}
diff --git a/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_eip_associate.go b/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_eip_associate.go
index ee3c2a02f5..c1c2da2393 100644
--- a/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_eip_associate.go
+++ b/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_eip_associate.go
@@ -3,14 +3,12 @@ package gaussdb
import (
"context"
"encoding/json"
- "fmt"
"net/http"
"strings"
"time"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/jmespath/go-jmespath"
@@ -159,7 +157,8 @@ func resourceGaussMysqlEipAssociateRead(_ context.Context, d *schema.ResourceDat
getResp, err := client.Request("GET", getPath, &getOpt)
if err != nil {
- return common.CheckDeletedDiag(d, parseMysqlProxyError(err), "error retrieving EIP associated with GaussDB MySQL")
+ return common.CheckDeletedDiag(d, parseMysqlProxyEipAssociateError(err), "error retrieving EIP associated "+
+ "with GaussDB MySQL")
}
getRespBody, err := utils.FlattenResponse(getResp)
@@ -249,22 +248,7 @@ func resourceGaussMysqlEipAssociateDelete(ctx context.Context, d *schema.Resourc
return nil
}
-func checkGaussDBMySQLProxyJobFinish(ctx context.Context, client *golangsdk.ServiceClient, jobID string,
- timeout time.Duration) error {
- stateConf := &resource.StateChangeConf{
- Pending: []string{"Pending", "Running"},
- Target: []string{"Completed"},
- Refresh: gaussDBMysqlDatabaseStatusRefreshFunc(client, jobID),
- Timeout: timeout,
- PollInterval: 2 * time.Second,
- }
- if _, err := stateConf.WaitForStateContext(ctx); err != nil {
- return fmt.Errorf("error waiting for job(%s) to be completed: %s ", jobID, err)
- }
- return nil
-}
-
-func parseMysqlProxyError(err error) error {
+func parseMysqlProxyEipAssociateError(err error) error {
if errCode, ok := err.(golangsdk.ErrDefault400); ok {
var apiError interface{}
if jsonErr := json.Unmarshal(errCode.Body, &apiError); jsonErr != nil {
diff --git a/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy.go b/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy.go
index 62e1786ef0..e25e4aa28c 100644
--- a/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy.go
+++ b/huaweicloud/services/gaussdb/resource_huaweicloud_gaussdb_mysql_proxy.go
@@ -2,19 +2,35 @@ package gaussdb
import (
"context"
+ "encoding/json"
+ "fmt"
+ "strings"
"time"
+ "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/jmespath/go-jmespath"
- "github.com/chnsz/golangsdk/openstack/taurusdb/v3/instances"
+ "github.com/chnsz/golangsdk"
+ "github.com/chnsz/golangsdk/pagination"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
)
// @API GaussDBforMySQL POST /v3/{project_id}/instances/{instance_id}/proxy
// @API GaussDBforMySQL GET /v3/{project_id}/jobs
+// @API GaussDBforMySQL PUT /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/port
+// @API GaussDBforMySQL PUT /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/flavor
// @API GaussDBforMySQL POST /v3/{project_id}/instances/{instance_id}/proxy/enlarge
+// @API GaussDBforMySQL PUT /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/reduce
+// @API GaussDBforMySQL PUT /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/rename
+// @API GaussDBforMySQL PUT /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/weight
+// @API GaussDBforMySQL POST /v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/new-node-auto-add
+// @API GaussDBforMySQL GET /v3/{project_id}/instances/{instance_id}/proxies
// @API GaussDBforMySQL DELETE /v3/{project_id}/instances/{instance_id}/proxy
func ResourceGaussDBProxy() *schema.Resource {
return &schema.Resource{
@@ -23,16 +39,16 @@ func ResourceGaussDBProxy() *schema.Resource {
UpdateContext: resourceGaussDBProxyUpdate,
DeleteContext: resourceGaussDBProxyDelete,
Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
+ StateContext: resourceGaussDBMySQLProxyImportState,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
- Update: schema.DefaultTimeout(10 * time.Minute),
+ Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
- Schema: map[string]*schema.Schema{ // request and response parameters
+ Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Optional: true,
@@ -47,52 +63,341 @@ func ResourceGaussDBProxy() *schema.Resource {
"flavor": {
Type: schema.TypeString,
Required: true,
- ForceNew: true,
},
"node_num": {
Type: schema.TypeInt,
Required: true,
},
- "address": {
+ "proxy_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "proxy_mode": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ },
+ "route_mode": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "subnet_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "master_node_weight": {
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ MaxItems: 1,
+ Elem: gaussDBMysqlProxyNodeWeightSchema(),
+ },
+ "readonly_nodes_weight": {
+ Type: schema.TypeSet,
+ Optional: true,
+ Computed: true,
+ Elem: gaussDBMysqlProxyNodeWeightSchema(),
+ },
+ "new_node_auto_add_status": {
Type: schema.TypeString,
+ Optional: true,
Computed: true,
},
+ "new_node_weight": {
+ Type: schema.TypeInt,
+ Optional: true,
+ RequiredWith: []string{"new_node_auto_add_status"},
+ },
"port": {
Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "address": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "nodes": {
+ Type: schema.TypeList,
Computed: true,
+ Elem: gaussDBMysqlProxyNodeSchema(),
},
},
}
}
+func gaussDBMysqlProxyNodeWeightSchema() *schema.Resource {
+ sc := schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "weight": {
+ Type: schema.TypeInt,
+ Required: true,
+ },
+ },
+ }
+ return &sc
+}
+
+func gaussDBMysqlProxyNodeSchema() *schema.Resource {
+ sc := schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "az_code": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "frozen_flag": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ },
+ }
+ return &sc
+}
+
func resourceGaussDBProxyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
- client, err := cfg.GaussdbV3Client(cfg.GetRegion(d))
+ region := cfg.GetRegion(d)
+
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy"
+ product = "gaussdb"
+ )
+ client, err := cfg.NewServiceClient(product, region)
if err != nil {
- return diag.Errorf("error creating GaussDB client: %s ", err)
+ return diag.Errorf("error creating GaussDB client: %s", err)
}
- createOpts := instances.ProxyOpts{
- Flavor: d.Get("flavor").(string),
- NodeNum: d.Get("node_num").(int),
+ createPath := client.Endpoint + httpUrl
+ createPath = strings.ReplaceAll(createPath, "{project_id}", client.ProjectID)
+ createPath = strings.ReplaceAll(createPath, "{instance_id}", d.Get("instance_id").(string))
+
+ createOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ createOpt.JSONBody = utils.RemoveNil(buildCreateGaussDBMysqlProxyBodyParams(d))
+ createResp, err := client.Request("POST", createPath, &createOpt)
+ if err != nil {
+ return diag.Errorf("error creating GaussDB MySQL proxy: %s", err)
}
- instanceId := d.Get("instance_id").(string)
- n, err := instances.EnableProxy(client, instanceId, createOpts).ExtractJobResponse()
+ createRespBody, err := utils.FlattenResponse(createResp)
if err != nil {
- return diag.Errorf("error creating gaussdb_mysql_proxy: %s", err)
+ return diag.FromErr(err)
+ }
+
+ jobId := utils.PathSearch("job_id", createRespBody, nil)
+ if jobId == nil {
+ return diag.Errorf("error creating GaussDB MySQL proxy: job_id is not found in API response")
}
- d.SetId(instanceId)
- if err := instances.WaitForJobSuccess(client, int(d.Timeout(schema.TimeoutCreate)/time.Second), n.JobID); err != nil {
- return diag.Errorf("error waiting for gaussdb_mysql_proxy job: %s", err)
+ searchExpression := "proxy_list[?proxy.status=='ENABLING PROXY']|[0].proxy.pool_id"
+ proxyId, err := getGaussDBProxy(client, d.Get("instance_id").(string), searchExpression)
+ if err != nil {
+ return diag.Errorf("error retrieving GaussDB MySQL proxy: %s", err)
+ }
+ d.SetId(proxyId.(string))
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutCreate))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if _, ok := d.GetOk("port"); ok {
+ err = updateGaussDBMySQLPort(ctx, d, client)
+ if err != nil {
+ return diag.FromErr(err)
+ }
}
return resourceGaussDBProxyRead(ctx, d, meta)
}
-func resourceGaussDBProxyRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
- return nil
+func buildCreateGaussDBMysqlProxyBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "flavor_ref": d.Get("flavor"),
+ "node_num": d.Get("node_num"),
+ "proxy_name": utils.ValueIgnoreEmpty(d.Get("proxy_name")),
+ "proxy_mode": utils.ValueIgnoreEmpty(d.Get("proxy_mode")),
+ "route_mode": utils.ValueIgnoreEmpty(d.Get("route_mode")),
+ "nodes_read_weight": buildCreateGaussDBMySQLProxyNodesReadWeightBody(d),
+ "subnet_id": utils.ValueIgnoreEmpty(d.Get("subnet_id")),
+ "new_node_auto_add_status": utils.ValueIgnoreEmpty(d.Get("new_node_auto_add_status")),
+ "new_node_weight": utils.ValueIgnoreEmpty(d.Get("new_node_weight")),
+ }
+ return bodyParams
+}
+
+func buildCreateGaussDBMySQLProxyNodesReadWeightBody(d *schema.ResourceData) []map[string]interface{} {
+ masterNodeRawParams := d.Get("master_node_weight").([]interface{})
+ readonlyNodesRawParams := d.Get("readonly_nodes_weight").(*schema.Set).List()
+ length := len(masterNodeRawParams) + len(readonlyNodesRawParams)
+ if length == 0 {
+ return nil
+ }
+ rst := make([]map[string]interface{}, 0, length)
+ for _, v := range masterNodeRawParams {
+ raw := v.(map[string]interface{})
+ rst = append(rst, map[string]interface{}{
+ "id": raw["id"],
+ "weight": raw["weight"],
+ })
+ }
+ for _, v := range readonlyNodesRawParams {
+ raw := v.(map[string]interface{})
+ rst = append(rst, map[string]interface{}{
+ "id": raw["id"],
+ "weight": raw["weight"],
+ })
+ }
+ return rst
+}
+
+func resourceGaussDBProxyRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cfg := meta.(*config.Config)
+ region := cfg.GetRegion(d)
+
+ var (
+ product = "gaussdb"
+ )
+ client, err := cfg.NewServiceClient(product, region)
+ if err != nil {
+ return diag.Errorf("error creating GaussDB client: %s", err)
+ }
+ searchExpression := fmt.Sprintf("proxy_list[?proxy.pool_id=='%s']|[0]", d.Id())
+ proxy, err := getGaussDBProxy(client, d.Get("instance_id").(string), searchExpression)
+ if err != nil {
+ return common.CheckDeletedDiag(d, parseMysqlProxyError(err), "error retrieving GaussDB MySQL proxy")
+ }
+
+ mErr := multierror.Append(
+ d.Set("region", region),
+ d.Set("flavor", utils.PathSearch("proxy.flavor_ref", proxy, nil)),
+ d.Set("node_num", utils.PathSearch("proxy.node_num", proxy, nil)),
+ d.Set("proxy_name", utils.PathSearch("proxy.name", proxy, nil)),
+ d.Set("route_mode", utils.PathSearch("proxy.route_mode", proxy, nil)),
+ d.Set("subnet_id", utils.PathSearch("proxy.subnet_id", proxy, nil)),
+ d.Set("master_node_weight", flattenGaussDBProxyResponseBodyMasterNodeWeight(proxy)),
+ d.Set("readonly_nodes_weight", flattenGaussDBProxyResponseBodyReadonlyNodesWeight(proxy, d)),
+ d.Set("new_node_auto_add_status", utils.PathSearch("proxy.new_node_auto_add_status", proxy, nil)),
+ d.Set("port", utils.PathSearch("proxy.port", proxy, nil)),
+ d.Set("address", utils.PathSearch("proxy.address", proxy, nil)),
+ d.Set("nodes", flattenGaussDBProxyResponseBodyNodes(proxy)),
+ )
+
+ return diag.FromErr(mErr.ErrorOrNil())
+}
+
+func flattenGaussDBProxyResponseBodyMasterNodeWeight(proxy interface{}) []interface{} {
+ rst := make([]interface{}, 0, 1)
+ rst = append(rst, map[string]interface{}{
+ "id": utils.PathSearch("master_node.id", proxy, nil),
+ "weight": utils.PathSearch("master_node.weight", proxy, nil),
+ })
+ return rst
+}
+
+func flattenGaussDBProxyResponseBodyReadonlyNodesWeight(proxy interface{}, d *schema.ResourceData) []interface{} {
+ readonlyNodesJson := utils.PathSearch("readonly_nodes", proxy, make([]interface{}, 0))
+ readonlyNodeArray := readonlyNodesJson.([]interface{})
+ if len(readonlyNodeArray) < 1 {
+ return nil
+ }
+ readonlyNodesWeightRaw := d.Get("readonly_nodes_weight").(*schema.Set).List()
+ readonlyNodesRawMap := make(map[string]bool)
+ for _, v := range readonlyNodesWeightRaw {
+ readonlyNodesRawMap[v.(map[string]interface{})["id"].(string)] = true
+ }
+ rst := make([]interface{}, 0, len(readonlyNodesWeightRaw))
+ for _, v := range readonlyNodeArray {
+ id := utils.PathSearch("id", v, "").(string)
+ if readonlyNodesRawMap[id] {
+ rst = append(rst, map[string]interface{}{
+ "id": id,
+ "weight": utils.PathSearch("weight", v, nil),
+ })
+ }
+ }
+ return rst
+}
+
+func flattenGaussDBProxyResponseBodyNodes(proxy interface{}) []interface{} {
+ nodesJson := utils.PathSearch("proxy.nodes", proxy, make([]interface{}, 0))
+ nodeArray := nodesJson.([]interface{})
+ if len(nodeArray) < 1 {
+ return nil
+ }
+ rst := make([]interface{}, 0, len(nodeArray))
+ for _, v := range nodeArray {
+ rst = append(rst, map[string]interface{}{
+ "id": utils.PathSearch("id", v, nil),
+ "status": utils.PathSearch("status", v, nil),
+ "name": utils.PathSearch("name", v, nil),
+ "role": utils.PathSearch("role", v, nil),
+ "az_code": utils.PathSearch("az_code", v, nil),
+ "frozen_flag": utils.PathSearch("frozen_flag", v, nil),
+ })
+ }
+ return rst
+}
+
+func getGaussDBProxy(client *golangsdk.ServiceClient, instanceId, expression string) (interface{}, error) {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxies"
+ )
+
+ listPath := client.Endpoint + httpUrl
+ listPath = strings.ReplaceAll(listPath, "{project_id}", client.ProjectID)
+ listPath = strings.ReplaceAll(listPath, "{instance_id}", instanceId)
+
+ listMysqlDatabasesResp, err := pagination.ListAllItems(
+ client,
+ "offset",
+ listPath,
+ &pagination.QueryOpts{MarkerField: ""})
+
+ if err != nil {
+ return nil, err
+ }
+
+ listRespJson, err := json.Marshal(listMysqlDatabasesResp)
+ if err != nil {
+ return nil, err
+ }
+ var listRespBody interface{}
+ err = json.Unmarshal(listRespJson, &listRespBody)
+ if err != nil {
+ return nil, err
+ }
+ proxy := utils.PathSearch(expression, listRespBody, nil)
+ if proxy == nil {
+ return nil, golangsdk.ErrDefault404{}
+ }
+ return proxy, nil
}
func resourceGaussDBProxyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -102,45 +407,490 @@ func resourceGaussDBProxyUpdate(ctx context.Context, d *schema.ResourceData, met
return diag.Errorf("error creating GaussDB client: %s ", err)
}
+ if d.HasChange("flavor") {
+ err = updateGaussDBMySQLFlavor(ctx, d, client)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ }
+
if d.HasChange("node_num") {
- oldnum, newnum := d.GetChange("node_num")
- if newnum.(int) < oldnum.(int) {
- return diag.Errorf("error updating gaussdb_mysql_proxy %s: new node num should be greater than old num", d.Id())
+ oldNum, newNum := d.GetChange("node_num")
+ if oldNum.(int) < newNum.(int) {
+ err = enlargeGaussDBMySQLProxyNumber(ctx, d, client, newNum.(int)-oldNum.(int))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ } else {
+ err = reduceGaussDBMySQLProxyNumber(ctx, d, client, oldNum.(int)-newNum.(int))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ }
+ }
+
+ if d.HasChange("proxy_name") {
+ err = updateGaussDBMySQLProxyName(d, client)
+ if err != nil {
+ return diag.FromErr(err)
}
+ }
- enlargeSize := newnum.(int) - oldnum.(int)
- enlargeProxyOpts := instances.EnlargeProxyOpts{
- NodeNum: enlargeSize,
+ if d.HasChange("port") {
+ err = updateGaussDBMySQLPort(ctx, d, client)
+ if err != nil {
+ return diag.FromErr(err)
}
+ }
- lp, err := instances.EnlargeProxy(client, d.Id(), enlargeProxyOpts).ExtractJobResponse()
+ if d.HasChanges("master_node_weight", "readonly_nodes_weight") {
+ err = updateGaussDBMySQLNodesWeight(ctx, d, client)
if err != nil {
- return diag.Errorf("error enlarging gaussdb_mysql_proxy: %s", err)
+ return diag.FromErr(err)
}
+ }
- if err = instances.WaitForJobSuccess(client, int(d.Timeout(schema.TimeoutUpdate)/time.Second), lp.JobID); err != nil {
- return diag.Errorf("error waiting for gaussdb_mysql_proxy job: %s", err)
+ if d.HasChanges("new_node_auto_add_status", "new_node_weight") {
+ err = updateGaussDBMySQLNewNode(d, client)
+ if err != nil {
+ return diag.FromErr(err)
}
}
return resourceGaussDBProxyRead(ctx, d, meta)
}
-func resourceGaussDBProxyDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+func updateGaussDBMySQLFlavor(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/flavor"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildUpdateGaussDBMySQLFlavorBodyParams(d))
+
+ updateResp, err := client.Request("PUT", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error updating GaussDB MySQL proxy flavor: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ jobId := utils.PathSearch("job_id", updateRespBody, nil)
+ if jobId == nil {
+ return fmt.Errorf("error updating GaussDB MySQL proxy flavor: job_id is not found in API response")
+ }
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutUpdate))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func buildUpdateGaussDBMySQLFlavorBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "flavor_ref": d.Get("flavor"),
+ }
+ return bodyParams
+}
+
+func enlargeGaussDBMySQLProxyNumber(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient,
+ number int) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/enlarge"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildEnlargeGaussDBMySQLProxyNumberBodyParams(d, number))
+
+ updateResp, err := client.Request("POST", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error enlarging GaussDB MySQL proxy number: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ jobId := utils.PathSearch("job_id", updateRespBody, nil)
+ if jobId == nil {
+ return fmt.Errorf("error enlarging GaussDB MySQL proxy number: job_id is not found in API response")
+ }
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutUpdate))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func buildEnlargeGaussDBMySQLProxyNumberBodyParams(d *schema.ResourceData, number int) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "node_num": number,
+ "proxy_id": d.Id(),
+ }
+ return bodyParams
+}
+
+func reduceGaussDBMySQLProxyNumber(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient,
+ number int) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/reduce"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildReduceGaussDBMySQLProxyNumberBodyParams(number))
+
+ updateResp, err := client.Request("PUT", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error reducing GaussDB MySQL proxy number: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ jobId := utils.PathSearch("job_id", updateRespBody, nil)
+ if jobId == nil {
+ return fmt.Errorf("error reducing GaussDB MySQL proxy number: job_id is not found in API response")
+ }
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutUpdate))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func buildReduceGaussDBMySQLProxyNumberBodyParams(number int) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "node_num": number,
+ }
+ return bodyParams
+}
+
+func updateGaussDBMySQLProxyName(d *schema.ResourceData, client *golangsdk.ServiceClient) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/rename"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildUpdateGaussDBMySQLProxyNameBodyParams(d))
+
+ updateResp, err := client.Request("PUT", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error updating GaussDB MySQL proxy name: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ result := utils.PathSearch("result", updateRespBody, "").(string)
+ if result != "success" {
+ return fmt.Errorf("error updating GaussDB MySQL proxy name: result is: %s", result)
+ }
+
+ return nil
+}
+
+func buildUpdateGaussDBMySQLProxyNameBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "alias": d.Get("proxy_name"),
+ }
+ return bodyParams
+}
+
+func updateGaussDBMySQLPort(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/port"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildUpdateGaussDBMySQLPortBodyParams(d))
+
+ updateResp, err := client.Request("PUT", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error updating GaussDB MySQL proxy port: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ jobId := utils.PathSearch("job_id", updateRespBody, nil)
+ if jobId == nil {
+ // if the port is not change, then the job_id is null
+ return nil
+ }
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutUpdate))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func buildUpdateGaussDBMySQLPortBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "port": d.Get("port"),
+ }
+ return bodyParams
+}
+
+func updateGaussDBMySQLNodesWeight(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/weight"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = buildUpdateGaussDBMySQLNodesWeightBodyParams(d)
+
+ updateResp, err := client.Request("PUT", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error updating GaussDB MySQL nodes weight associated with proxy: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ jobId := utils.PathSearch("job_id", updateRespBody, nil)
+ if jobId == nil {
+ return fmt.Errorf("error updating GaussDB MySQL nodes weight associated with proxy: job_id is not " +
+ "found in API response")
+ }
+
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutUpdate))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func buildUpdateGaussDBMySQLNodesWeightBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := make(map[string]interface{})
+ if len(d.Get("master_node_weight").([]interface{})) > 0 {
+ masterNodeWeight := d.Get("master_node_weight").([]interface{})[0].(map[string]interface{})["weight"]
+ bodyParams["master_weight"] = masterNodeWeight
+ }
+ if len(d.Get("readonly_nodes_weight").(*schema.Set).List()) > 0 {
+ readonlyNodesRawParams := d.Get("readonly_nodes_weight").(*schema.Set).List()
+ readonlyNodesWeightParam := make([]map[string]interface{}, len(readonlyNodesRawParams))
+ for i, v := range readonlyNodesRawParams {
+ raw := v.(map[string]interface{})
+ readonlyNodesWeightParam[i] = map[string]interface{}{
+ "id": raw["id"],
+ "weight": raw["weight"],
+ }
+ }
+ bodyParams["readonly_nodes"] = readonlyNodesWeightParam
+ }
+ return bodyParams
+}
+
+func updateGaussDBMySQLNewNode(d *schema.ResourceData, client *golangsdk.ServiceClient) error {
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy/{proxy_id}/new-node-auto-add"
+ )
+ updatePath := client.Endpoint + httpUrl
+ updatePath = strings.ReplaceAll(updatePath, "{project_id}", client.ProjectID)
+ updatePath = strings.ReplaceAll(updatePath, "{instance_id}", d.Get("instance_id").(string))
+ updatePath = strings.ReplaceAll(updatePath, "{proxy_id}", d.Id())
+
+ updateOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ updateOpt.JSONBody = utils.RemoveNil(buildUpdateGaussDBMySQLNewNodeBodyParams(d))
+
+ updateResp, err := client.Request("POST", updatePath, &updateOpt)
+ if err != nil {
+ return fmt.Errorf("error updating GaussDB MySQL new node automatically associate with proxy: %s", err)
+ }
+
+ updateRespBody, err := utils.FlattenResponse(updateResp)
+ if err != nil {
+ return err
+ }
+
+ result := utils.PathSearch("result", updateRespBody, "").(string)
+ if result != "success" {
+ return fmt.Errorf("error updating GaussDB MySQL new node automatically associate with proxy: result is: %s",
+ result)
+ }
+
+ return nil
+}
+
+func buildUpdateGaussDBMySQLNewNodeBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "switch_status": d.Get("new_node_auto_add_status"),
+ "weight": utils.ValueIgnoreEmpty(d.Get("new_node_weight")),
+ }
+ return bodyParams
+}
+
+func resourceGaussDBProxyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
- client, err := cfg.GaussdbV3Client(cfg.GetRegion(d))
+ region := cfg.GetRegion(d)
+
+ var (
+ httpUrl = "v3/{project_id}/instances/{instance_id}/proxy"
+ product = "gaussdb"
+ )
+ client, err := cfg.NewServiceClient(product, region)
if err != nil {
- return diag.Errorf("error creating GaussDB client: %s ", err)
+ return diag.Errorf("error creating GaussDB client: %s", err)
+ }
+
+ deletePath := client.Endpoint + httpUrl
+ deletePath = strings.ReplaceAll(deletePath, "{project_id}", client.ProjectID)
+ deletePath = strings.ReplaceAll(deletePath, "{instance_id}", d.Get("instance_id").(string))
+
+ deleteOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+ deleteOpt.JSONBody = utils.RemoveNil(buildDeleteGaussDBMySQLProxyBodyParams(d))
+
+ deleteResp, err := client.Request("DELETE", deletePath, &deleteOpt)
+ if err != nil {
+ return common.CheckDeletedDiag(d, parseMysqlProxyError(err), "error deleting GaussDB MySQL proxy")
+ }
+
+ deleteRespBody, err := utils.FlattenResponse(deleteResp)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ jobId := utils.PathSearch("job_id", deleteRespBody, nil)
+ if jobId == nil {
+ return diag.Errorf("error deleting GaussDB MySQL proxy: job_id is not found in API response")
}
- dp, err := instances.DeleteProxy(client, d.Id()).ExtractJobResponse()
+ err = checkGaussDBMySQLProxyJobFinish(ctx, client, jobId.(string), d.Timeout(schema.TimeoutDelete))
if err != nil {
- return diag.Errorf("error deleting gaussdb_mysql_proxy: %s", err)
+ return diag.FromErr(err)
}
- if err = instances.WaitForJobSuccess(client, int(d.Timeout(schema.TimeoutDelete)/time.Second), dp.JobID); err != nil {
- return diag.Errorf("error waiting for gaussdb_mysql_proxy job: %s", err)
+ return nil
+}
+
+func buildDeleteGaussDBMySQLProxyBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "proxy_ids": []string{d.Id()},
}
+ return bodyParams
+}
+func checkGaussDBMySQLProxyJobFinish(ctx context.Context, client *golangsdk.ServiceClient, jobID string,
+ timeout time.Duration) error {
+ stateConf := &resource.StateChangeConf{
+ Pending: []string{"Pending", "Running"},
+ Target: []string{"Completed"},
+ Refresh: gaussDBMysqlDatabaseStatusRefreshFunc(client, jobID),
+ Timeout: timeout,
+ PollInterval: 10 * time.Second,
+ }
+ if _, err := stateConf.WaitForStateContext(ctx); err != nil {
+ return fmt.Errorf("error waiting for GaussDB MySQL proxy job (%s) to be completed: %s ", jobID, err)
+ }
return nil
}
+
+func parseMysqlProxyError(err error) error {
+ if errCode, ok := err.(golangsdk.ErrDefault400); ok {
+ var apiError interface{}
+ if jsonErr := json.Unmarshal(errCode.Body, &apiError); jsonErr != nil {
+ return err
+ }
+
+ errorCode, errorCodeErr := jmespath.Search("error_code", apiError)
+ if errorCodeErr != nil {
+ return err
+ }
+
+ if errorCode == "DBS.201028" {
+ return golangsdk.ErrDefault404(errCode)
+ }
+ }
+ if errCode, ok := err.(golangsdk.ErrUnexpectedResponseCode); ok && errCode.Actual == 409 {
+ var apiError interface{}
+ if jsonErr := json.Unmarshal(errCode.Body, &apiError); jsonErr != nil {
+ return err
+ }
+
+ errorCode, errorCodeErr := jmespath.Search("error_code", apiError)
+ if errorCodeErr != nil {
+ return err
+ }
+
+ if errorCode == "DBS.200932" {
+ return golangsdk.ErrDefault404{}
+ }
+ }
+ return err
+}
+
+func resourceGaussDBMySQLProxyImportState(_ context.Context, d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData,
+ error) {
+ parts := strings.Split(d.Id(), "/")
+
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("invalid format specified for import ID, must be /")
+ }
+
+ d.SetId(parts[1])
+ mErr := multierror.Append(nil,
+ d.Set("instance_id", parts[0]),
+ )
+
+ return []*schema.ResourceData{d}, mErr.ErrorOrNil()
+}