Skip to content

fix(es): [125657314]fix node_info_list update #3442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/3442.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/tencentcloud_elasticsearch_instance: fix node_info_list update
```
223 changes: 190 additions & 33 deletions tencentcloud/services/es/resource_tc_elasticsearch_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func ResourceTencentCloudElasticsearchInstance() *schema.Resource {
Description: "License type. Valid values are `oss`, `basic` and `platinum`. The default value is `platinum`.",
},
"node_info_list": {
Type: schema.TypeList,
Type: schema.TypeSet,
Required: true,
MinItems: 1,
Description: "Node information list, which is used to describe the specification information of various types of nodes in the cluster, such as node type, node quantity, node specification, disk type, and disk size.",
Expand Down Expand Up @@ -346,6 +346,19 @@ func ResourceTencentCloudElasticsearchInstance() *schema.Resource {
Description: "Instance creation time.",
},
},
CustomizeDiff: func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
nodeInfos := d.Get("node_info_list").(*schema.Set).List()
typeMap := map[string]bool{}
for _, v := range nodeInfos {
m := v.(map[string]interface{})
t := m["type"].(string)
if typeMap[t] {
return fmt.Errorf("duplicate node type '%s' is not allowed in node_info_list", t)
}
typeMap[t] = true
}
return nil
},
}
}

Expand Down Expand Up @@ -428,7 +441,7 @@ func resourceTencentCloudElasticsearchInstanceCreate(d *schema.ResourceData, met
}

if v, ok := d.GetOk("node_info_list"); ok {
infos := v.([]interface{})
infos := v.(*schema.Set).List()
request.NodeInfoList = make([]*es.NodeInfo, 0, len(infos))
for _, item := range infos {
value := item.(map[string]interface{})
Expand Down Expand Up @@ -995,39 +1008,155 @@ func resourceTencentCloudElasticsearchInstanceUpdate(d *schema.ResourceData, met
}

if d.HasChange("node_info_list") {
nodeInfos := d.Get("node_info_list").([]interface{})
nodeInfoList := make([]*es.NodeInfo, 0, len(nodeInfos))
for _, d := range nodeInfos {
value := d.(map[string]interface{})
nodeType := value["node_type"].(string)
diskSize := uint64(value["disk_size"].(int))
nodeNum := uint64(value["node_num"].(int))
types := value["type"].(string)
diskType := value["disk_type"].(string)
encrypt := value["encrypt"].(bool)
dataDisk := es.NodeInfo{
NodeType: &nodeType,
DiskSize: &diskSize,
NodeNum: &nodeNum,
Type: &types,
DiskType: &diskType,
DiskEncrypt: helper.BoolToInt64Pointer(encrypt),
o, n := d.GetChange("node_info_list")
oldNodeMap := make(map[string]map[string]interface{})
newNodesMap := make(map[string]map[string]interface{})
for _, node := range o.(*schema.Set).List() {
nodeMap := node.(map[string]interface{})
oldNodeMap[nodeMap["type"].(string)] = nodeMap
}
for _, node := range n.(*schema.Set).List() {
nodeMap := node.(map[string]interface{})
newNodesMap[nodeMap["type"].(string)] = nodeMap
}

typeList := []string{"hotData", "warmData", "dedicatedMaster"}
for _, t := range typeList {
old := oldNodeMap[t]
new := newNodesMap[t]
baseNodeList := make([]interface{}, 0)
for k, v := range oldNodeMap {
if k == t {
continue
}
baseNodeList = append(baseNodeList, v)
}
nodeInfoList = append(nodeInfoList, &dataDisk)
}
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, nodeInfoList, nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)

if old == nil && new == nil {
// 没有该类型节点配置
continue
} else if old == nil {
// 新增
baseNodeList = append(baseNodeList, new)
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, convertToNodeInfos(baseNodeList), nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
}
} else if new == nil {
// 删除
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, convertToNodeInfos(baseNodeList), nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
}
} else {
// 磁盘类型不支持修改
fields := []string{"disk_type", "encrypt", "type"}
for _, field := range fields {
if old[field] != new[field] {
return fmt.Errorf("%s not support change", field)
}
}
// 修改一种节点的个数
var isUpdateNodeNum bool
if old["node_num"].(int) != new["node_num"].(int) {
changeESNodes := convertToNodeInfos(baseNodeList)
thisNode := convertToNodeInfo(old)
thisNode.NodeNum = helper.IntUint64(new["node_num"].(int))
changeESNodes = append(changeESNodes, thisNode)
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, changeESNodes, nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
}
isUpdateNodeNum = true
}

var isUpdateNodeType bool
// 修改一种节点的节点规格
if old["node_type"].(string) != new["node_type"].(string) {
changeESNodes := convertToNodeInfos(baseNodeList)
thisNode := convertToNodeInfo(old)
thisNode.NodeType = helper.String(new["node_type"].(string))
if isUpdateNodeNum {
thisNode.NodeNum = helper.IntUint64(new["node_num"].(int))
}
changeESNodes = append(changeESNodes, thisNode)
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, changeESNodes, nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
}
isUpdateNodeType = true
}
// 修改一种节点的磁盘大小
if old["disk_size"].(int) != new["disk_size"].(int) {
changeESNodes := convertToNodeInfos(baseNodeList)
thisNode := convertToNodeInfo(old)
thisNode.NodeType = helper.String(new["node_type"].(string))
thisNode.DiskSize = helper.IntUint64(new["disk_size"].(int))
if isUpdateNodeNum {
thisNode.NodeNum = helper.IntUint64(new["node_num"].(int))
}
if isUpdateNodeType {
thisNode.NodeType = helper.String(new["node_type"].(string))
}
changeESNodes = append(changeESNodes, thisNode)
err := resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
errRet := elasticsearchService.UpdateInstance(ctx, instanceId, "", "", "", "", "", 0, changeESNodes, nil, nil, nil, nil)
if errRet != nil {
return tccommon.RetryError(errRet)
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
}
}
}
return nil
})
if err != nil {
return err
}
err = tencentCloudElasticsearchInstanceUpgradeWaiting(ctx, &elasticsearchService, instanceId)
if err != nil {
return err
// 更新oldNodeMap中的值
oldNodeMap[t] = new
}
}

Expand Down Expand Up @@ -1251,3 +1380,31 @@ func tencentCloudElasticsearchInstanceUpgradeWaiting(ctx context.Context, servic
return nil
})
}

func convertToNodeInfo(n interface{}) *es.NodeInfo {
value := n.(map[string]interface{})
nodeType := value["node_type"].(string)
diskSize := uint64(value["disk_size"].(int))
nodeNum := uint64(value["node_num"].(int))
types := value["type"].(string)
diskType := value["disk_type"].(string)
encrypt := value["encrypt"].(bool)
nodeInfo := &es.NodeInfo{
NodeType: &nodeType,
DiskSize: &diskSize,
NodeNum: &nodeNum,
Type: &types,
DiskType: &diskType,
DiskEncrypt: helper.BoolToInt64Pointer(encrypt),
}
return nodeInfo
}

func convertToNodeInfos(nodeInfos []interface{}) []*es.NodeInfo {
nodeInfoList := make([]*es.NodeInfo, 0, len(nodeInfos))
for _, n := range nodeInfos {
nodeInfo := convertToNodeInfo(n)
nodeInfoList = append(nodeInfoList, nodeInfo)
}
return nodeInfoList
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,30 @@ func TestAccTencentCloudElasticsearchInstanceResource_https(t *testing.T) {
})
}

func TestAccTencentCloudElasticsearchInstanceResource_nodeInfoList(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { tcacctest.AccPreCheck(t) },
Providers: tcacctest.AccProviders,
CheckDestroy: testAccCheckElasticsearchInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccElasticsearchInstanceNodeInfoList,
Check: resource.ComposeTestCheckFunc(
testAccCheckElasticsearchInstanceExists("tencentcloud_elasticsearch_instance.es_node_info_list"),
resource.TestCheckResourceAttr("tencentcloud_elasticsearch_instance.es_node_info_list", "node_info_list.#", "1"),
),
},
{
Config: testAccElasticsearchInstanceNodeInfoListUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckElasticsearchInstanceExists("tencentcloud_elasticsearch_instance.es_node_info_list"),
resource.TestCheckResourceAttr("tencentcloud_elasticsearch_instance.es_node_info_list", "node_info_list.#", "2"),
),
},
},
})
}

func testAccCheckElasticsearchInstanceDestroy(s *terraform.State) error {
logId := tccommon.GetLogId(tccommon.ContextNil)
ctx := context.WithValue(context.TODO(), tccommon.LogIdKey, logId)
Expand Down Expand Up @@ -573,3 +597,76 @@ resource "tencentcloud_elasticsearch_instance" "es_kibana" {
}
}
`

const testAccElasticsearchInstanceNodeInfoList = tcacctest.DefaultEsVariables + `
resource "tencentcloud_elasticsearch_instance" "es_node_info_list" {
instance_name = "tf-ci-test-node"
availability_zone = var.availability_zone
version = "7.10.1"
vpc_id = var.vpc_id
subnet_id = var.subnet_id
password = "Test1234"
license_type = "basic"
basic_security_type = 2
public_access = "OPEN"
protocol = "https"
es_acl {
white_list = [
"127.0.0.2"
]
}
es_public_acl {
white_ip_list = [
"127.0.0.2"
]
}

node_info_list {
node_num = 2
node_type = "ES.S1.MEDIUM4"
disk_size = 50
type = "hotData"
disk_type = "CLOUD_SSD"
}
}
`

const testAccElasticsearchInstanceNodeInfoListUpdate = tcacctest.DefaultEsVariables + `
resource "tencentcloud_elasticsearch_instance" "es_node_info_list" {
instance_name = "tf-ci-test-node"
availability_zone = var.availability_zone
version = "7.10.1"
vpc_id = var.vpc_id
subnet_id = var.subnet_id
password = "Test1234"
license_type = "basic"
basic_security_type = 2
public_access = "OPEN"
protocol = "https"
es_acl {
white_list = [
"127.0.0.2"
]
}
es_public_acl {
white_ip_list = [
"127.0.0.2"
]
}

node_info_list {
node_num = 3
node_type = "ES.S1.MEDIUM8"
disk_size = 100
type = "hotData"
disk_type = "CLOUD_SSD"
}
node_info_list {
node_num = 3
node_type = "ES.S1.MEDIUM8"
disk_type = "CLOUD_SSD"
type = "dedicatedMaster"
disk_size = 50
}
}
`
2 changes: 1 addition & 1 deletion website/docs/r/elasticsearch_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ resource "tencentcloud_elasticsearch_instance" "example_multi_zone" {

The following arguments are supported:

* `node_info_list` - (Required, List) Node information list, which is used to describe the specification information of various types of nodes in the cluster, such as node type, node quantity, node specification, disk type, and disk size.
* `node_info_list` - (Required, Set) Node information list, which is used to describe the specification information of various types of nodes in the cluster, such as node type, node quantity, node specification, disk type, and disk size.
* `password` - (Required, String) Password to an instance, the password needs to be 8 to 16 characters, including at least two items ([a-z,A-Z], [0-9] and [-!@#$%&^*+=_:;,.?] special symbols.
* `version` - (Required, String) Version of the instance. Valid values are `5.6.4`, `6.4.3`, `6.8.2`, `7.5.1` and `7.10.1`.
* `vpc_id` - (Required, String, ForceNew) The ID of a VPC network.
Expand Down
Loading