alicloud/resource_alicloud_eflo_node_group.go (631 lines of code) (raw):
package alicloud
import (
"bytes"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"log"
"strings"
"time"
"github.com/PaesslerAG/jsonpath"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/tidwall/sjson"
)
func resourceAliCloudEfloNodeGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAliCloudEfloNodeGroupCreate,
Read: resourceAliCloudEfloNodeGroupRead,
Update: resourceAliCloudEfloNodeGroupUpdate,
Delete: resourceAliCloudEfloNodeGroupDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(120 * time.Minute),
Delete: schema.DefaultTimeout(5 * time.Minute),
},
Schema: map[string]*schema.Schema{
"az": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"cluster_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"create_time": {
Type: schema.TypeString,
Computed: true,
},
"ignore_failed_node_tasks": {
Type: schema.TypeBool,
Optional: true,
},
"image_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"ip_allocation_policy": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"machine_type_policy": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"bonds": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"subnet": {
Type: schema.TypeString,
Optional: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"machine_type": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"bond_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"bonds": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"subnet": {
Type: schema.TypeString,
Optional: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"bond_default_subnet": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"node_policy": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"bonds": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"subnet": {
Type: schema.TypeString,
Optional: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"node_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
},
"machine_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"node_group_description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"node_group_id": {
Type: schema.TypeString,
Computed: true,
},
"node_group_name": {
Type: schema.TypeString,
Required: true,
},
"nodes": {
Type: schema.TypeSet,
Optional: true,
Set: func(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
if v, ok := m["node_id"]; ok {
buf.WriteString(fmt.Sprint(v))
}
return hashcode.String(buf.String())
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"vpc_id": {
Type: schema.TypeString,
Optional: true,
},
"vswitch_id": {
Type: schema.TypeString,
Optional: true,
},
"hostname": {
Type: schema.TypeString,
Optional: true,
},
"node_id": {
Type: schema.TypeString,
Optional: true,
},
"login_password": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
},
},
},
"user_data": {
Type: schema.TypeString,
Optional: true,
},
"vswitch_zone_id": {
Type: schema.TypeString,
Optional: true,
},
"vpd_subnets": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"zone_id": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
func resourceAliCloudEfloNodeGroupCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
action := "CreateNodeGroup"
var request map[string]interface{}
var response map[string]interface{}
query := make(map[string]interface{})
var err error
request = make(map[string]interface{})
if v, ok := d.GetOk("cluster_id"); ok {
request["ClusterId"] = v
}
request["RegionId"] = client.RegionId
objectDataLocalMap := make(map[string]interface{})
if v, ok := d.GetOk("node_group_description"); ok {
objectDataLocalMap["NodeGroupDescription"] = v
}
if v, ok := d.GetOk("az"); ok {
objectDataLocalMap["Az"] = v
}
if v, ok := d.GetOk("machine_type"); ok {
objectDataLocalMap["MachineType"] = v
}
if v, ok := d.GetOk("image_id"); ok {
objectDataLocalMap["ImageId"] = v
}
if v, ok := d.GetOk("node_group_name"); ok {
objectDataLocalMap["NodeGroupName"] = v
}
objectDataLocalMapJson, err := json.Marshal(objectDataLocalMap)
if err != nil {
return WrapError(err)
}
request["NodeGroup"] = string(objectDataLocalMapJson)
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
response, err = client.RpcPost("eflo-controller", "2022-12-15", action, query, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, "alicloud_eflo_node_group", action, AlibabaCloudSdkGoERROR)
}
d.SetId(fmt.Sprintf("%v:%v", request["ClusterId"], response["NodeGroupId"]))
return resourceAliCloudEfloNodeGroupUpdate(d, meta)
}
func resourceAliCloudEfloNodeGroupRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
efloServiceV2 := EfloServiceV2{client}
objectRaw, err := efloServiceV2.DescribeEfloNodeGroup(d.Id())
if err != nil {
if !d.IsNewResource() && NotFoundError(err) {
log.Printf("[DEBUG] Resource alicloud_eflo_node_group DescribeEfloNodeGroup Failed!!! %s", err)
d.SetId("")
return nil
}
return WrapError(err)
}
d.Set("az", objectRaw["ZoneId"])
d.Set("create_time", objectRaw["CreateTime"])
d.Set("image_id", objectRaw["ImageId"])
d.Set("machine_type", objectRaw["MachineType"])
d.Set("node_group_description", objectRaw["Description"])
d.Set("node_group_name", objectRaw["GroupName"])
d.Set("cluster_id", objectRaw["ClusterId"])
d.Set("node_group_id", objectRaw["GroupId"])
objectRaw, err = efloServiceV2.DescribeNodeGroupListClusterNodes(d.Id())
if err != nil && !NotFoundError(err) {
return WrapError(err)
}
loginPasswordMap := make(map[string]interface{})
if v := d.Get("nodes"); !IsNil(v) {
if v, ok := d.GetOk("nodes"); ok {
localData, err := jsonpath.Get("$", v)
if err != nil {
localData = make([]interface{}, 0)
}
for _, dataLoop := range localData.(*schema.Set).List() {
dataLoopTmp := make(map[string]interface{})
if dataLoop != nil {
dataLoopTmp = dataLoop.(map[string]interface{})
}
loginPasswordMap[dataLoopTmp["node_id"].(string)] = dataLoopTmp["login_password"]
}
}
}
nodesRaw, _ := jsonpath.Get("$.Nodes", objectRaw)
nodesMaps := make([]map[string]interface{}, 0)
if nodesRaw != nil {
for _, nodesChildRaw := range nodesRaw.([]interface{}) {
nodesMap := make(map[string]interface{})
nodesChildRaw := nodesChildRaw.(map[string]interface{})
nodesMap["hostname"] = nodesChildRaw["Hostname"]
nodesMap["node_id"] = nodesChildRaw["NodeId"]
nodesMap["vswitch_id"] = nodesChildRaw["VSwitchId"]
nodesMap["vpc_id"] = nodesChildRaw["VpcId"]
if password, ok := loginPasswordMap[fmt.Sprint(nodesMap["node_id"])]; ok {
nodesMap["login_password"] = password
}
nodesMaps = append(nodesMaps, nodesMap)
}
}
if err := d.Set("nodes", nodesMaps); err != nil {
return err
}
return nil
}
func resourceAliCloudEfloNodeGroupUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
var request map[string]interface{}
var response map[string]interface{}
var query map[string]interface{}
update := false
var err error
parts := strings.Split(d.Id(), ":")
action := "UpdateNodeGroup"
request = make(map[string]interface{})
query = make(map[string]interface{})
request["NodeGroupId"] = parts[1]
request["RegionId"] = client.RegionId
if !d.IsNewResource() && d.HasChange("node_group_name") {
update = true
}
request["NewNodeGroupName"] = d.Get("node_group_name")
if update {
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
response, err = client.RpcPost("eflo-controller", "2022-12-15", action, query, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
}
if d.HasChange("nodes") {
oldEntry, newEntry := d.GetChange("nodes")
oldEntrySet := oldEntry.(*schema.Set)
newEntrySet := newEntry.(*schema.Set)
removed := oldEntrySet.Difference(newEntrySet)
added := newEntrySet.Difference(oldEntrySet)
if removed.Len() > 0 {
parts := strings.Split(d.Id(), ":")
action := "ShrinkCluster"
request = make(map[string]interface{})
query = make(map[string]interface{})
request["ClusterId"] = parts[0]
request["RegionId"] = client.RegionId
objectDataLocalMap := make(map[string]interface{})
localMaps := make([]interface{}, 0)
for _, dataLoop := range removed.List() {
dataLoopTmp := make(map[string]interface{})
if dataLoop != nil {
dataLoopTmp = dataLoop.(map[string]interface{})
}
dataLoopMap := make(map[string]interface{})
dataLoopMap["NodeId"] = dataLoopTmp["node_id"]
localMaps = append(localMaps, dataLoopMap)
}
objectDataLocalMap["Nodes"] = localMaps
if v, ok := d.GetOk("node_group_id"); ok {
objectDataLocalMap["NodeGroupId"] = v
}
NodeGroupsMap := make([]interface{}, 0)
NodeGroupsMap = append(NodeGroupsMap, objectDataLocalMap)
objectDataLocalMapJson, err := json.Marshal(NodeGroupsMap)
if err != nil {
return WrapError(err)
}
request["NodeGroups"] = string(objectDataLocalMapJson)
if v, ok := d.GetOkExists("ignore_failed_node_tasks"); ok {
request["IgnoreFailedNodeTasks"] = v
}
jsonString := convertObjectToJsonString(request)
jsonString, _ = sjson.Set(jsonString, "NodeGroups.0.NodeGroupId", parts[1])
_ = json.Unmarshal([]byte(jsonString), &request)
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
response, err = client.RpcPost("eflo-controller", "2022-12-15", action, query, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
efloServiceV2 := EfloServiceV2{client}
stateConf := BuildStateConf([]string{}, []string{"execution_success"}, d.Timeout(schema.TimeoutUpdate), 10*time.Second, efloServiceV2.DescribeAsyncEfloNodeGroupStateRefreshFunc(d, response, "$.TaskState", []string{}))
if jobDetail, err := stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id(), jobDetail)
}
}
if added.Len() > 0 {
parts := strings.Split(d.Id(), ":")
action := "ExtendCluster"
request = make(map[string]interface{})
query = make(map[string]interface{})
request["ClusterId"] = parts[0]
request["RegionId"] = client.RegionId
objectDataLocalMap := make(map[string]interface{})
localMaps := make([]interface{}, 0)
for _, dataLoop := range added.List() {
dataLoopTmp := make(map[string]interface{})
if dataLoop != nil {
dataLoopTmp = dataLoop.(map[string]interface{})
}
dataLoopMap := make(map[string]interface{})
dataLoopMap["NodeId"] = dataLoopTmp["node_id"]
dataLoopMap["Hostname"] = dataLoopTmp["hostname"]
dataLoopMap["LoginPassword"] = dataLoopTmp["login_password"]
dataLoopMap["VpcId"] = dataLoopTmp["vpc_id"]
dataLoopMap["VSwitchId"] = dataLoopTmp["vswitch_id"]
localMaps = append(localMaps, dataLoopMap)
}
objectDataLocalMap["Nodes"] = localMaps
if v, ok := d.GetOk("node_group_id"); ok {
objectDataLocalMap["NodeGroupId"] = v
}
if v, ok := d.GetOk("user_data"); ok {
objectDataLocalMap["UserData"] = v
}
if v, ok := d.GetOk("zone_id"); ok {
objectDataLocalMap["ZoneId"] = v
}
NodeGroupsMap := make([]interface{}, 0)
NodeGroupsMap = append(NodeGroupsMap, objectDataLocalMap)
objectDataLocalMapJson, err := json.Marshal(NodeGroupsMap)
if err != nil {
return WrapError(err)
}
request["NodeGroups"] = string(objectDataLocalMapJson)
localData1 := d.Get("ip_allocation_policy").([]interface{})
ipAllocationPolicyMapsArray := make([]interface{}, 0)
for _, dataLoop1 := range localData1 {
dataLoop1Tmp := dataLoop1.(map[string]interface{})
dataLoop1Map := make(map[string]interface{})
localData3 := make(map[string]interface{})
bondDefaultSubnet1, _ := jsonpath.Get("$[0].bond_default_subnet", dataLoop1Tmp["bond_policy"])
if bondDefaultSubnet1 != nil && bondDefaultSubnet1 != "" {
localData3["BondDefaultSubnet"] = bondDefaultSubnet1
}
if v, ok := dataLoop1Tmp["bond_policy"]; ok {
localData2, err := jsonpath.Get("$[0].bonds", v)
if err != nil {
localData2 = make([]interface{}, 0)
}
localMaps2 := make([]interface{}, 0)
for _, dataLoop2 := range localData2.([]interface{}) {
dataLoop2Tmp := make(map[string]interface{})
if dataLoop2 != nil {
dataLoop2Tmp = dataLoop2.(map[string]interface{})
}
dataLoop2Map := make(map[string]interface{})
dataLoop2Map["Subnet"] = dataLoop2Tmp["subnet"]
dataLoop2Map["Name"] = dataLoop2Tmp["name"]
localMaps2 = append(localMaps2, dataLoop2Map)
}
localData3["Bonds"] = localMaps2
}
dataLoop1Map["BondPolicy"] = localData3
localMaps3 := make([]interface{}, 0)
localData4 := dataLoop1Tmp["node_policy"]
for _, dataLoop4 := range localData4.([]interface{}) {
dataLoop4Tmp := dataLoop4.(map[string]interface{})
dataLoop4Map := make(map[string]interface{})
dataLoop4Map["NodeId"] = dataLoop4Tmp["node_id"]
localData2, err := jsonpath.Get("$.bonds", dataLoop4Tmp)
if err != nil {
localData2 = make([]interface{}, 0)
}
localMaps2 := make([]interface{}, 0)
for _, dataLoop2 := range localData2.([]interface{}) {
dataLoop2Tmp := make(map[string]interface{})
if dataLoop2 != nil {
dataLoop2Tmp = dataLoop2.(map[string]interface{})
}
dataLoop2Map := make(map[string]interface{})
dataLoop2Map["Subnet"] = dataLoop2Tmp["subnet"]
dataLoop2Map["Name"] = dataLoop2Tmp["name"]
localMaps2 = append(localMaps2, dataLoop2Map)
}
dataLoop4Map["Bonds"] = localMaps2
localMaps3 = append(localMaps3, dataLoop4Map)
}
dataLoop1Map["NodePolicy"] = localMaps3
localMaps5 := make([]interface{}, 0)
localData6 := dataLoop1Tmp["machine_type_policy"]
for _, dataLoop6 := range localData6.([]interface{}) {
dataLoop6Tmp := dataLoop6.(map[string]interface{})
dataLoop6Map := make(map[string]interface{})
dataLoop6Map["MachineType"] = dataLoop6Tmp["machine_type"]
localData2, err := jsonpath.Get("$.bonds", dataLoop6Tmp)
if err != nil {
localData2 = make([]interface{}, 0)
}
localMaps2 := make([]interface{}, 0)
for _, dataLoop2 := range localData2.([]interface{}) {
dataLoop2Tmp := make(map[string]interface{})
if dataLoop2 != nil {
dataLoop2Tmp = dataLoop2.(map[string]interface{})
}
dataLoop2Map := make(map[string]interface{})
dataLoop2Map["Subnet"] = dataLoop2Tmp["subnet"]
dataLoop2Map["Name"] = dataLoop2Tmp["name"]
localMaps2 = append(localMaps2, dataLoop2Map)
}
dataLoop6Map["Bonds"] = localMaps2
localMaps5 = append(localMaps5, dataLoop6Map)
}
dataLoop1Map["MachineTypePolicy"] = localMaps5
ipAllocationPolicyMapsArray = append(ipAllocationPolicyMapsArray, dataLoop1Map)
}
ipAllocationPolicyMapsJson, err := json.Marshal(ipAllocationPolicyMapsArray)
if err != nil {
return WrapError(err)
}
request["IpAllocationPolicy"] = string(ipAllocationPolicyMapsJson)
localData8 := d.Get("vpd_subnets").([]interface{})
vpdSubnetsMapsArray := localData8
vpdSubnetsMapsJson, err := json.Marshal(vpdSubnetsMapsArray)
if err != nil {
return WrapError(err)
}
request["VpdSubnets"] = string(vpdSubnetsMapsJson)
if v, ok := d.GetOkExists("ignore_failed_node_tasks"); ok {
request["IgnoreFailedNodeTasks"] = v
}
if v, ok := d.GetOk("vswitch_zone_id"); ok {
request["VSwitchZoneId"] = v
}
jsonString := convertObjectToJsonString(request)
jsonString, _ = sjson.Set(jsonString, "NodeGroups.0.NodeGroupId", parts[1])
_ = json.Unmarshal([]byte(jsonString), &request)
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
response, err = client.RpcPost("eflo-controller", "2022-12-15", action, query, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
efloServiceV2 := EfloServiceV2{client}
stateConf := BuildStateConf([]string{}, []string{"execution_success"}, d.Timeout(schema.TimeoutUpdate), 10*time.Second, efloServiceV2.DescribeAsyncEfloNodeGroupStateRefreshFunc(d, response, "$.TaskState", []string{}))
if jobDetail, err := stateConf.WaitForState(); err != nil {
return WrapErrorf(err, IdMsg, d.Id(), jobDetail)
}
}
}
return resourceAliCloudEfloNodeGroupRead(d, meta)
}
func resourceAliCloudEfloNodeGroupDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
parts := strings.Split(d.Id(), ":")
action := "DeleteNodeGroup"
var request map[string]interface{}
var response map[string]interface{}
query := make(map[string]interface{})
var err error
request = make(map[string]interface{})
request["ClusterId"] = parts[0]
request["NodeGroupId"] = parts[1]
request["RegionId"] = client.RegionId
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
response, err = client.RpcPost("eflo-controller", "2022-12-15", action, query, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
if NotFoundError(err) {
return nil
}
return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR)
}
return nil
}