Skip to content

Commit

Permalink
fix(CLOUDDEV-670): Fix update bare-metal instance
Browse files Browse the repository at this point in the history
- Bare-metal recreate when image_id or flavor_id is changed
- Error message when changing keypair_name
- Validate network_id, subnet_id, port_id on a bare metal change

fix(CLOUDDEV-1265): Fix update subnet

- Recreate subnet when changing CIDR
  • Loading branch information
Aleksandr Semenov committed Feb 3, 2025
1 parent 3a3c23a commit f6b4778
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 21 deletions.
85 changes: 65 additions & 20 deletions edgecenter/resource_edgecenter_baremetal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

"github.com/Edge-Center/edgecentercloud-go/edgecenter/instance/v1/types"
edgecloudV2 "github.com/Edge-Center/edgecentercloud-go/v2"
utilV2 "github.com/Edge-Center/edgecentercloud-go/v2/util"
)

const (
BmInstanceDeletingTimeout int = 1200
BmInstanceCreatingTimeout int = 3600
BmInstancePoint = "bminstances"
)

var (
Expand Down Expand Up @@ -84,6 +85,7 @@ func resourceBmInstance() *schema.Resource {
"flavor_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"interface": {
Type: schema.TypeList,
Expand All @@ -107,22 +109,25 @@ func resourceBmInstance() *schema.Resource {
Description: "Order of attaching interface. Trunk interface always attached first, fields affect only on creation",
},
"network_id": {
Type: schema.TypeString,
Description: "required if type is 'subnet' or 'any_subnet'",
Optional: true,
Computed: true,
Type: schema.TypeString,
Description: "required if type is 'subnet' or 'any_subnet'",
Optional: true,
Computed: true,
ValidateFunc: validation.IsUUID,
},
"subnet_id": {
Type: schema.TypeString,
Description: "required if type is 'subnet'",
Optional: true,
Computed: true,
Type: schema.TypeString,
Description: "required if type is 'subnet'",
Optional: true,
Computed: true,
ValidateFunc: validation.IsUUID,
},
"port_id": {
Type: schema.TypeString,
Computed: true,
Description: "required if type is 'reserved_fixed_ip'",
Optional: true,
Type: schema.TypeString,
Computed: true,
Description: "required if type is 'reserved_fixed_ip'",
Optional: true,
ValidateFunc: validation.IsUUID,
},
// nested map is not supported, in this case, you do not need to use the list for the map
"fip_source": {
Expand Down Expand Up @@ -162,6 +167,7 @@ func resourceBmInstance() *schema.Resource {
"image_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ExactlyOneOf: []string{
"image_id",
"apptemplate_id",
Expand Down Expand Up @@ -371,9 +377,6 @@ func resourceBmInstanceCreate(ctx context.Context, d *schema.ResourceData, m int

instanceID := taskResult.Instances[0]
log.Printf("[DEBUG] Baremetal Instance id (%s)", instanceID)
if err != nil {
return diag.FromErr(err)
}

d.SetId(instanceID)
resourceBmInstanceRead(ctx, d, m)
Expand All @@ -400,7 +403,7 @@ func resourceBmInstanceRead(ctx context.Context, d *schema.ResourceData, m inter

instance, resp, err := clientV2.Instances.Get(ctx, instanceID)
if err != nil {
if resp.StatusCode == http.StatusNotFound {
if resp != nil && resp.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing instance %s because resource doesn't exist anymore", d.Id())
d.SetId("")
return nil
Expand Down Expand Up @@ -431,9 +434,6 @@ func resourceBmInstanceRead(ctx context.Context, d *schema.ResourceData, m inter

ifs := d.Get("interface").([]interface{})
orderedInterfacesMap := extractInstanceInterfaceToListRead(ifs)
if err != nil {
return diag.FromErr(err)
}

var interfacesList []interface{}
for _, iFace := range interfacesListAPI {
Expand Down Expand Up @@ -633,6 +633,44 @@ func resourceBmInstanceUpdate(ctx context.Context, d *schema.ResourceData, m int
ifsOld := ifsOldRaw.([]interface{})
ifsNew := ifsNewRaw.([]interface{})

// Get interface settings from config
ifsRawConfig := d.GetRawConfig().GetAttr("interface")
ifs := ifsRawConfig.AsValueSlice()

// Validate interface settings from config.
// This is necessary because the fields ‘subnet_id’, ‘network_id’, ‘port_id’ have computed options enabled.
for _, i := range ifs {
ifsMap := i.AsValueMap()
ifsType := ifsMap["type"].AsString()

switch ifsType {
case types.ExternalInterfaceType.String():
continue
case types.AnySubnetInterfaceType.String():
if !ifsMap["subnet_id"].IsNull() {
return diag.Errorf("prohibit the use of any subnet with interface type \"%s\"", ifsType)
}
case types.ReservedFixedIPType.String():
if !ifsMap["subnet_id"].IsNull() || !ifsMap["network_id"].IsNull() {
return diag.Errorf("prohibit the use of any subnet or network with interface type \"%s\"", ifsType)
}
case types.SubnetInterfaceType.String():
if !ifsMap["network_id"].IsNull() {
networkID := ifsMap["network_id"].AsString()
subnetID := ifsMap["subnet_id"].AsString()

subnet, _, err := clientV2.Subnetworks.Get(ctx, subnetID)
if err != nil {
return diag.Errorf("cannot get subnet with ID: %s. Error: %s", subnetID, err.Error())
}

if subnet.NetworkID != networkID {
return diag.Errorf("subnet with ID: \"%s\" does not belong to the network with ID: \"%s\"", subnetID, networkID)
}
}
}
}

for _, i := range ifsOld {
iface := i.(map[string]interface{})
if isInterfaceContains(iface, ifsNew) {
Expand Down Expand Up @@ -683,6 +721,13 @@ func resourceBmInstanceUpdate(ctx context.Context, d *schema.ResourceData, m int
}
}

if d.HasChange("keypair_name") {
oldKN, _ := d.GetChange("keypair_name")
d.Set("keypair_name", oldKN.(string))

return diag.Errorf("changing keypair name for bare-metal instance is prohibited")
}

d.Set("last_updated", time.Now().Format(time.RFC850))
log.Println("[DEBUG] Finish Instance updating")

Expand Down
1 change: 1 addition & 0 deletions edgecenter/resource_edgecenter_subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func resourceSubnet() *schema.Resource {
"cidr": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Represents the IP address range of the subnet.",
},
"network_id": {
Expand Down
8 changes: 7 additions & 1 deletion edgecenter/utils_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,15 +337,21 @@ func extractVolumesMapV2(volumes []interface{}) ([]edgecloudV2.InstanceVolumeCre
func isInterfaceContains(verifiable map[string]interface{}, ifsSet []interface{}) bool {
verifiableType := verifiable["type"].(string)
verifiableSubnetID, _ := verifiable["subnet_id"].(string)
verifiableNetworkID, _ := verifiable["network_id"].(string)
for _, e := range ifsSet {
i := e.(map[string]interface{})
iType := i["type"].(string)
subnetID, _ := i["subnet_id"].(string)
networkID, _ := i["network_id"].(string)
if iType == types.ExternalInterfaceType.String() && verifiableType == types.ExternalInterfaceType.String() {
return true
}

if iType == verifiableType && subnetID == verifiableSubnetID {
if iType == types.SubnetInterfaceType.String() && verifiableType == types.SubnetInterfaceType.String() && subnetID == verifiableSubnetID {
return true
}

if iType == types.AnySubnetInterfaceType.String() && verifiableType == types.AnySubnetInterfaceType.String() && networkID == verifiableNetworkID {
return true
}
}
Expand Down

0 comments on commit f6b4778

Please sign in to comment.