You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

752 lines
26 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package utilities
import (
"context"
"fmt"
"strconv"
"time"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/rg"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/rg/models"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/status"
)
func CreateRequestResourceRG(ctx context.Context, plan *models.ResourceRGModel) (rg.CreateRequest, diag.Diagnostics) {
tflog.Info(ctx, "Start CreateRequestResourceRG", map[string]any{
"name": plan.Name.ValueString(),
"account_id": plan.AccountID.ValueInt64(),
"gid": plan.GID.ValueInt64(),
})
// set up required parameters in resource group create request
createReq := rg.CreateRequest{
Name: plan.Name.ValueString(),
AccountID: uint64(plan.AccountID.ValueInt64()),
GID: uint64(plan.GID.ValueInt64()),
MaxCPUCapacity: -1,
MaxVDiskCapacity: -1,
MaxMemoryCapacity: -1,
MaxNetworkPeerTransfer: -1,
MaxNumPublicIP: -1,
}
// set up resource limits optional parameters
if !plan.ResourceLimits.IsUnknown() {
var resourceLimits models.ResourceLimitsModel
diags := plan.ResourceLimits.As(ctx, &resourceLimits, basetypes.ObjectAsOptions{})
if diags.HasError() {
tflog.Error(ctx, "CreateRequestResourceRG: cannot populate ResourceLimits with plan.ResourceLimits object element")
return createReq, diags
}
if !resourceLimits.CUM.IsUnknown() {
createReq.MaxMemoryCapacity = int64(resourceLimits.CUM.ValueFloat64())
}
if !resourceLimits.CUDM.IsUnknown() {
createReq.MaxVDiskCapacity = int64(resourceLimits.CUDM.ValueFloat64())
}
if !resourceLimits.CUC.IsUnknown() {
createReq.MaxCPUCapacity = int64(resourceLimits.CUC.ValueFloat64())
}
if !resourceLimits.CUI.IsUnknown() {
createReq.MaxNumPublicIP = int64(resourceLimits.CUI.ValueFloat64())
}
if !resourceLimits.CUNP.IsUnknown() {
createReq.MaxNetworkPeerTransfer = int64(resourceLimits.CUNP.ValueFloat64())
}
}
// set up defNet, owner, ipcidr, description, extNetId, extIp, registerComputes, uniqPools optional parameters
if !plan.Owner.IsNull() {
createReq.Owner = plan.Owner.ValueString()
}
if plan.DefNetType.IsNull() {
createReq.DefNet = "PRIVATE" // default value
} else {
createReq.DefNet = plan.DefNetType.ValueString()
}
if !plan.IPCIDR.IsNull() {
//createReq.IPCIDR = plan.IPCIDR.ValueString()
}
if !plan.Description.IsNull() {
createReq.Description = plan.Description.ValueString()
}
if plan.ExtNetID.IsNull() {
createReq.ExtNetID = 0 // default value 0
} else {
createReq.ExtNetID = uint64(plan.ExtNetID.ValueInt64())
}
if !plan.ExtIP.IsNull() {
createReq.ExtIP = plan.ExtIP.ValueString()
}
if plan.RegisterComputes.IsNull() {
createReq.RegisterComputes = false // default value
} else {
createReq.RegisterComputes = plan.RegisterComputes.ValueBool()
}
if !plan.UniqPools.IsUnknown() {
uniqPools := make([]string, 0, len(plan.UniqPools.Elements()))
diags := plan.UniqPools.ElementsAs(ctx, &uniqPools, true)
if diags.HasError() {
tflog.Error(ctx, "CreateRequestResourceRG: cannot populate UniqPools with plan.UniqPools object element")
return createReq, diags
}
createReq.UniqPools = uniqPools
}
return createReq, nil
}
// RestoreRG performs resource group Restore request. Returns error in case of failures.
func RestoreRG(ctx context.Context, rgId uint64, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
restoreReq := rg.RestoreRequest{RGID: rgId}
tflog.Info(ctx, "utilityRestoreRG: before calling CloudBroker().RG().Restore", map[string]any{"rgId": rgId, "req": restoreReq})
res, err := c.CloudBroker().RG().Restore(ctx, restoreReq)
if err != nil {
diags.AddError(
"RestoreRG: cannot restore resource group",
err.Error(),
)
return diags
}
tflog.Info(ctx, "utilityRestoreRG: response from CloudBroker().RG().Restore", map[string]any{"rg_id": rgId, "response": res})
return nil
}
// EnableRG performs resource group Enable request
func EnableRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) error {
enableReq := rg.EnableRequest{RGID: rgId}
tflog.Info(ctx, "utilityEnableRG: before calling CloudBroker().RG().Enable", map[string]any{"rg_id": rgId, "req": enableReq})
res, err := c.CloudBroker().RG().Enable(ctx, enableReq)
tflog.Info(ctx, "utilityEnableRG: response from CloudBroker().RG().Enable", map[string]any{"rg_id": rgId, "response": res})
return err
}
// DisableRG performs resource group Disable request
func DisableRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) error {
disableReq := rg.DisableRequest{RGID: rgId}
tflog.Info(ctx, "utilityDisableRG: before calling CloudBroker().RG().Disable", map[string]any{"rg_id": rgId, "req": disableReq})
res, err := c.CloudBroker().RG().Disable(ctx, disableReq)
tflog.Info(ctx, "utilityDisableRG: response from CloudBroker().RG().Disable", map[string]any{"rg_id": rgId, "response": res})
return err
}
// UpdateRG compares plan and state for resource group fields name, description, quota, register_computes.
// If any changes are detected, Update request is performed. If not, no update is performed.
func UpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
var updateNeeded bool
var diags diag.Diagnostics
updateReq := rg.UpdateRequest{
RGID: rgId,
}
if !plan.Name.Equal(state.Name) {
updateReq.Name = plan.Name.ValueString()
tflog.Info(ctx, "utilityUpdateRG: new name specified", map[string]any{
"rg_id": plan.Id.ValueString(),
"name_plan": plan.Name.ValueString(),
"name_state": state.Name.ValueString()})
updateNeeded = true
}
if !plan.Description.Equal(state.Description) {
updateReq.Description = plan.Description.ValueString()
tflog.Info(ctx, "utilityUpdateRG: new description specified", map[string]any{
"rg_id": plan.Id.ValueString(),
"description_plan": plan.Description.ValueString(),
"description_state": state.Description.ValueString()})
updateNeeded = true
}
if !plan.RegisterComputes.Equal(state.RegisterComputes) {
if plan.RegisterComputes.IsNull() {
updateReq.RegisterComputes = false // default value
} else {
updateReq.RegisterComputes = plan.RegisterComputes.ValueBool()
}
tflog.Info(ctx, "utilityUpdateRG: new register_computes specified", map[string]any{
"rg_id": plan.Id.ValueString(),
"register_computes_plan": plan.RegisterComputes.ValueBool(),
"register_computes_state": state.RegisterComputes.ValueBool()})
updateNeeded = true
}
var updResLimitsNeeded bool
var resLimitsPlan, resLimitsState models.ResourceLimitsModel
if !plan.ResourceLimits.IsUnknown() {
diags = plan.ResourceLimits.As(ctx, &resLimitsPlan, basetypes.ObjectAsOptions{})
if diags.HasError() {
return diags
}
}
if !state.ResourceLimits.IsNull() {
diags = state.ResourceLimits.As(ctx, &resLimitsState, basetypes.ObjectAsOptions{})
if diags.HasError() {
return diags
}
}
if !plan.UniqPools.IsUnknown() {
if !plan.UniqPools.Equal(state.UniqPools) {
uniqPools := make([]string, 0, len(plan.UniqPools.Elements()))
diags := plan.UniqPools.ElementsAs(ctx, &uniqPools, true)
if diags.HasError() {
tflog.Error(ctx, "UpdateResourceRG: cannot populate UniqPools with plan.UniqPools object element")
return diags
}
updateReq.UniqPools = uniqPools
}
}
if !plan.ResourceLimits.IsUnknown() {
if !resLimitsPlan.CUM.Equal(resLimitsState.CUM) {
updateReq.MaxMemoryCapacity = int64(resLimitsPlan.CUM.ValueFloat64())
updResLimitsNeeded = true
}
if !resLimitsPlan.CUDM.Equal(resLimitsState.CUDM) {
updateReq.MaxVDiskCapacity = int64(resLimitsPlan.CUDM.ValueFloat64())
updResLimitsNeeded = true
}
if !resLimitsPlan.CUC.Equal(resLimitsState.CUC) {
updateReq.MaxCPUCapacity = int64(resLimitsPlan.CUC.ValueFloat64())
updResLimitsNeeded = true
}
if !resLimitsPlan.CUNP.Equal(resLimitsState.CUNP) {
updateReq.MaxNetworkPeerTransfer = int64(resLimitsPlan.CUNP.ValueFloat64())
updResLimitsNeeded = true
}
if !resLimitsPlan.CUI.Equal(resLimitsState.CUI) {
updateReq.MaxNumPublicIP = int64(resLimitsPlan.CUI.ValueFloat64())
updResLimitsNeeded = true
}
}
if updResLimitsNeeded {
tflog.Info(ctx, "utilityUpdateRG: new resource limits specified", map[string]any{
"rg_id": plan.Id.ValueString()})
updateNeeded = true
}
if updateNeeded {
tflog.Info(ctx, "utilityUpdateRG: before calling CloudBroker().RG().Update", map[string]any{"rg_id": plan.Id.ValueString(), "req": updateReq})
res, err := c.CloudBroker().RG().Update(ctx, updateReq)
tflog.Info(ctx, "utilityUpdateRG: response from CloudBroker().RG().Update", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
if err != nil {
diags.AddError("can not update RG", err.Error())
return diags
}
}
if !updateNeeded {
tflog.Info(ctx, "utilityUpdateRG: call for CloudBroker().RG().Update was not needed", map[string]any{"rg_id": plan.Id.ValueString()})
}
return nil
}
// EnableDisableCreateRG performs Enable request is enable is true, and Disable request otherwise.
// In case of failure returns warnings.
func EnableDisableCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
var enable bool
if plan.Enable.IsNull() {
enable = true // default value
} else {
enable = plan.Enable.ValueBool()
}
tflog.Info(ctx, "EnableDisableCreateRG: resource group to be enabled/disabled", map[string]any{
"rg_id": rgId,
"enable": enable})
if enable {
err := EnableRG(ctx, rgId, plan, c)
if err != nil {
diags.AddWarning(
"EnableDisableCreateRG: cannot enable rg",
err.Error(),
)
return diags
}
}
if !enable {
err := DisableRG(ctx, rgId, plan, c)
if err != nil {
diags.AddWarning(
"EnableDisableCreateRG: cannot disable rg",
err.Error(),
)
return diags
}
}
tflog.Info(ctx, "EnableDisableCreateRG: resource group is successfully enabled/disabled", map[string]any{"rg_id": rgId, "enable": enable})
return nil
}
// EnableDisableUpdateRG performs Enable request is enable is true, and Disable request otherwise.
// In case of failure returns errors.
func EnableDisableUpdateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
var enable bool
if plan.Enable.IsNull() {
enable = true // default value
} else {
enable = plan.Enable.ValueBool()
}
tflog.Info(ctx, "EnableDisableUpdateRG: resource group to be enabled/disabled", map[string]any{
"rg_id": rgId,
"enable": enable})
if enable {
err := EnableRG(ctx, rgId, plan, c)
if err != nil {
diags.AddError(
"EnableDisableUpdateRG: cannot enable rg",
err.Error(),
)
return diags
}
}
if !enable {
err := DisableRG(ctx, rgId, plan, c)
if err != nil {
diags.AddError(
"EnableDisableUpdateRG: cannot disable rg",
err.Error(),
)
return diags
}
}
tflog.Info(ctx, "EnableDisableUpdateRG: resource group is successfully enabled/disabled", map[string]any{"rg_id": rgId, "enable": enable})
return nil
}
// AccessUpdateRG compares plan and state for resource group field access.
// If changes are detected, AccessRevoke request is performed for each deleted access user and AccessGrant request is
// performed for each added access user. If no changes are detected, no requests performed.
// Returns errors in case of failures.
func AccessUpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
itemsAccessPlan := make([]models.AccessModel, 0, len(plan.Access.Elements()))
diags = plan.Access.ElementsAs(ctx, &itemsAccessPlan, false)
if diags.HasError() {
tflog.Error(ctx, "AccessUpdateRG: cannot populate itemsAccess with plan.Access List elements")
return diags
}
itemsAccessState := make([]models.AccessModel, 0, len(state.Access.Elements()))
diags = state.Access.ElementsAs(ctx, &itemsAccessState, false)
if diags.HasError() {
tflog.Error(ctx, "AccessUpdateRG: cannot populate itemsAccess with state.Access List elements")
return diags
}
// define accesses to be revoked and revoke them
var deletedAccess []models.AccessModel
for _, accessStateElem := range itemsAccessState {
if !accessStateElem.Contains(itemsAccessPlan) {
deletedAccess = append(deletedAccess, accessStateElem)
}
}
if len(deletedAccess) == 0 {
tflog.Info(ctx, "AccessUpdateRG: no access needs to be revoked", map[string]any{
"rg_id": plan.Id.ValueString()})
}
if len(deletedAccess) > 0 {
tflog.Info(ctx, "AccessUpdateRG: access needs to be revoked", map[string]any{
"rg_id": plan.Id.ValueString(),
"deleted_access": deletedAccess})
for _, deletedAccessItem := range deletedAccess {
revokeReq := rg.AccessRevokeRequest{
RGID: rgId,
User: deletedAccessItem.User.ValueString(),
}
tflog.Info(ctx, "AccessUpdateRG: before calling CloudBroker().RG().AccessRevoke", map[string]any{"rg_id": plan.Id.ValueString(), "req": revokeReq})
res, err := c.CloudBroker().RG().AccessRevoke(ctx, revokeReq)
tflog.Info(ctx, "AccessUpdateRG: response from CloudBroker().RG().AccessRevoke", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
if err != nil {
diags.AddError(
"AccessUpdateRG: cannot revoke access for rg",
err.Error())
}
}
}
// define accesses to be granted and grant them
var addedAccess []models.AccessModel
for _, accessPlanElem := range itemsAccessPlan {
if !accessPlanElem.Contains(itemsAccessState) {
addedAccess = append(addedAccess, accessPlanElem)
}
}
if len(addedAccess) == 0 {
tflog.Info(ctx, "AccessUpdateRG: no access need to be granted", map[string]any{
"rg_id": plan.Id.ValueString()})
}
if len(addedAccess) > 0 {
tflog.Info(ctx, "AccessUpdateRG: access needs to be granted", map[string]any{
"rg_id": plan.Id.ValueString(),
"added_access": addedAccess})
for _, addedAccessItem := range addedAccess {
grantReq := rg.AccessGrantRequest{
RGID: rgId,
User: addedAccessItem.User.ValueString(),
Right: addedAccessItem.Right.ValueString(),
}
tflog.Info(ctx, "AccessUpdateRG: before calling CloudBroker().RG().AccessGrant", map[string]any{"rg_id": plan.Id.ValueString(), "req": grantReq})
res, err := c.CloudBroker().RG().AccessGrant(ctx, grantReq)
tflog.Info(ctx, "AccessUpdateRG: response from CloudBroker().RG().AccessGrant", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
if err != nil {
diags.AddError(
"AccessUpdateRG: cannot grant access for rg",
err.Error())
}
}
}
return diags
}
// AccessCreateRG grants access to users specified in access field for created resource.
// In case of failure returns warnings.
func AccessCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
if len(plan.Access.Elements()) != 0 {
tflog.Info(ctx, "AccessCreateRG: access needs to be granted", map[string]any{
"rg_id": rgId,
"access_plan": plan.Access.Elements()})
itemsAccessPlan := make([]models.AccessModel, 0, len(plan.Access.Elements()))
diagsItem := plan.Access.ElementsAs(ctx, &itemsAccessPlan, false)
if diagsItem.HasError() {
tflog.Warn(ctx, "cannot populate itemsAccess with plan.Access List elements")
diags.AddWarning(fmt.Sprintf("AccessCreateRG: Unable to get access info for RG %d", rgId),
"cannot populate itemsAccess with plan.Access List elements",
)
return diags
}
for _, addedAccessItem := range itemsAccessPlan {
grantReq := rg.AccessGrantRequest{
RGID: rgId,
User: addedAccessItem.User.ValueString(),
Right: addedAccessItem.Right.ValueString(),
}
tflog.Info(ctx, "AccessCreateRG: before calling CloudBroker().RG().AccessGrant", map[string]any{
"rg_id": rgId,
"req": grantReq})
res, err := c.CloudBroker().RG().AccessGrant(ctx, grantReq)
if err != nil {
diags.AddWarning("AccessCreateRG: Unable to grant access for RG",
err.Error())
}
tflog.Info(ctx, "AccessCreateRG: response from CloudBroker().RG().AccessGrant", map[string]any{
"rg_id": rgId,
"response": res})
}
}
if len(plan.Access.Elements()) == 0 {
tflog.Info(ctx, "AccessCreateRG: no access need to be granted", map[string]any{
"rg_id": rgId,
"access_plan": plan.Access.Elements()})
}
return diags
}
// SetDefNetUpdateRG compares plan and state for resource group update field def_net.
// If any changes are detected, SetDefNet request is performed. If not, no SetDefNet is performed.
// Returns error in case of failures.
func SetDefNetUpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
var setDefNetNeeded bool
setDefNetReq := rg.SetDefNetRequest{
RGID: rgId,
}
var itemDefNetPlan, itemDefNetState models.DefNetModel
if !plan.DefNet.IsNull() {
diags.Append(plan.DefNet.As(ctx, &itemDefNetPlan, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
tflog.Error(ctx, "SetDefNetUpdateRG: cannot populate defNet with plan.DefNet object element")
return diags
}
}
if !state.DefNet.IsNull() {
diags.Append(state.DefNet.As(ctx, &itemDefNetState, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
tflog.Error(ctx, "SetDefNetUpdateRG: cannot populate defNet with state.DefNet object element")
return diags
}
}
if !plan.DefNet.IsNull() && !state.DefNet.IsNull() {
if !itemDefNetPlan.NetId.Equal(itemDefNetState.NetId) {
setDefNetNeeded = true
}
if !itemDefNetPlan.NetType.Equal(itemDefNetState.NetType) {
setDefNetNeeded = true
}
} else if !plan.DefNet.IsNull() {
setDefNetNeeded = true
}
if setDefNetNeeded {
tflog.Info(ctx, "utilitySetDefNetUpdateRG: new def_net specified", map[string]any{
"rg_id": plan.Id.ValueString(),
"def_net_plan": plan.DefNet,
"def_net_state": state.DefNet})
setDefNetReq.NetType = itemDefNetPlan.NetType.ValueString()
if itemDefNetPlan.NetId.IsNull() {
setDefNetReq.NetID = 0 // default value
} else {
setDefNetReq.NetID = uint64(itemDefNetPlan.NetId.ValueInt64())
}
tflog.Info(ctx, "utilitySetDefNetUpdateRG: before calling CloudBroker().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "req": setDefNetReq})
res, err := c.CloudBroker().RG().SetDefNet(ctx, setDefNetReq)
if err != nil {
diags.AddError(
"SetDefNetUpdateRG: can not set defNet for rg",
err.Error())
return diags
}
tflog.Info(ctx, "utilitySetDefNetUpdateRG: response from CloudBroker().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
}
if !setDefNetNeeded {
tflog.Info(ctx, "utilitySetDefNetUpdateRG: call for CloudBroker().RG().SetDefNet was not needed", map[string]any{
"rg_id": plan.Id.ValueString(),
"def_net_plan": plan.DefNet,
"def_net_state": state.DefNet})
}
return nil
}
// SetDefNetCreateRG performs SetDefNet request if def_net field is not empty. Otherwise, no SetDefNet request is performed.
// In case of failure returns warnings.
func SetDefNetCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
setDefNetReq := rg.SetDefNetRequest{RGID: rgId}
var itemDefNetPlan models.DefNetModel
if !plan.DefNet.IsNull() {
tflog.Info(ctx, "SetDefNetCreateRG: new def_net specified", map[string]any{
"rg_id": rgId,
"def_net_plan": plan.DefNet})
diagItem := plan.DefNet.As(ctx, &itemDefNetPlan, basetypes.ObjectAsOptions{})
if diagItem.HasError() {
diags.AddWarning(
fmt.Sprintf("SetDefNetCreateRG: Unable to setDefNet for RG %d", rgId),
"cannot populate defNet with plan.DefNet object element for rg",
)
return diags
}
setDefNetReq.NetType = itemDefNetPlan.NetType.ValueString()
if itemDefNetPlan.NetId.IsNull() {
setDefNetReq.NetID = 0 // default value
} else {
setDefNetReq.NetID = uint64(itemDefNetPlan.NetId.ValueInt64())
}
tflog.Info(ctx, "SetDefNetCreateRG: before calling CloudBroker().RG().SetDefNet", map[string]any{"rg_id": rgId, "req": setDefNetReq})
res, err := c.CloudBroker().RG().SetDefNet(ctx, setDefNetReq)
tflog.Info(ctx, "SetDefNetCreateRG: response from CloudBroker().RG().SetDefNet", map[string]any{"rg_id": rgId, "response": res})
if err != nil {
diags.AddWarning(
"SetDefNetCreateRG: Unable to setDefNet for RG",
err.Error(),
)
return diags
}
}
if plan.DefNet.IsNull() {
tflog.Info(ctx, "SetDefNetCreateRG: call for CloudBroker().RG().SetDefNet was not needed", map[string]any{
"rg_id": rgId,
"def_net_plan": plan.DefNet})
}
return nil
}
// UpdateComputeFeature performs СomputeFeatureUpdate request if compute_feature field is not empty and need update.
// In case of failure returns error.
func UpdateComputeFeature(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
req := rg.UpdateComputeFeaturesRequest{RGID: rgId}
computeFeatures := make([]string, 0, len(plan.ComputeFeatures.Elements()))
diags = plan.ComputeFeatures.ElementsAs(ctx, &computeFeatures, true)
if diags.HasError() {
tflog.Error(ctx, "UpdateComputeFeature: cannot populate ComputeFeature with plan.ComputeFeatures object element")
return diags
}
req.ComputeFeatures = computeFeatures
tflog.Info(ctx, "UpdateComputeFeature: before call CloudBroker().LB().UpdateComputeFeatures", map[string]any{"req": req})
_, err := c.CloudBroker().RG().UpdateComputeFeatures(ctx, req)
if err != nil {
diags.AddError("UpdateComputeFeature: unable to update compute features", err.Error())
return diags
}
tflog.Info(ctx, "UpdateComputeFeature: compute features updated")
return nil
}
// UpdateCpuAllocationParameter performs setCpuAllocationParameter request if cpu_allocation_parameter field is not empty and need update.
// In case of failure returns error.
func UpdateCpuAllocationParameter(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
req := rg.SetCPUAllocationParameterRequest{
RGID: rgId,
StrictLoose: plan.CPUAllocationParameter.ValueString(),
}
tflog.Info(ctx, "UpdateCpuAllocationParameter: before call CloudBroker().LB().SetCPUAllocationParameter", map[string]any{"req": req})
_, err := c.CloudBroker().RG().SetCPUAllocationParameter(ctx, req)
if err != nil {
diags.AddError("UpdateCpuAllocationParameter: unable to update cpu allocation parameter", err.Error())
return diags
}
tflog.Info(ctx, "UpdateCpuAllocationParameter: cpu allocation parameter updated")
return nil
}
// UpdateCpuAllocationRatio performs setCpuAllocationRatio request if ratio field is not empty and need update.
// In case of failure returns error.
func UpdateCpuAllocationRatio(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
req := rg.SetCPUAllocationRatioRequest{
RGID: rgId,
Ratio: plan.CPUAllocationRatio.ValueFloat64(),
}
tflog.Info(ctx, "UpdateCpuAllocationRatio: before call CloudBroker().LB().SetCPUAllocationRatio", map[string]any{"req": req})
_, err := c.CloudBroker().RG().SetCPUAllocationRatio(ctx, req)
if err != nil {
diags.AddError("UpdateCpuAllocationRatio: unable to update cpu allocation ratio", err.Error())
return diags
}
tflog.Info(ctx, "UpdateCpuAllocationRatio: cpu allocation ratio updated")
return nil
}
// RGReadStatus loads rg resource by ids id, gets it current status. Performs restore and enable if needed for
// Deleted status.
// In case of failure returns errors.
func RGReadStatus(ctx context.Context, state *models.ResourceRGModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "RGReadStatus: Read status rg with ID", map[string]any{"rg_id": state.Id.ValueString()})
diags := diag.Diagnostics{}
rgId, err := strconv.ParseUint(state.Id.ValueString(), 10, 64)
if err != nil {
diags.AddError("RGReadStatus: Cannot parse resource group ID from state", err.Error())
return diags
}
recordRG, err := RGCheckPresence(ctx, rgId, c)
if err != nil {
diags.AddError("RGReadStatus: Unable to Read RG before status check", err.Error())
return diags
}
tflog.Info(ctx, "RGReadStatus: resource group values before status check", map[string]any{
"rg_id": recordRG.ID,
"updated_recordRG": recordRG})
// check resource status
switch recordRG.Status {
case status.Modeled:
diags.AddError(
"RG is in status Modeled",
"please, contact support for more information",
)
return diags
case status.Deleted:
tflog.Info(ctx, "RGReadStatus: resource group with status.Deleted is being checked", map[string]any{
"rg_id": recordRG.ID,
"status": recordRG.Status})
// restore and enable resource group in case it is required
if state.Restore.IsNull() || state.Restore.ValueBool() { // default true or user set-up true
diags.Append(RestoreRG(ctx, rgId, c)...)
if diags.HasError() {
tflog.Error(ctx, "RGReadStatus: cannot restore rg")
return diags
}
tflog.Info(ctx, "RGReadStatus: resource group restored successfully", map[string]any{"rg_id": recordRG.ID})
state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))
if state.Enable.IsNull() || state.Enable.ValueBool() { // default true or user set-up true
err := EnableRG(ctx, rgId, state, c)
if err != nil {
diags.AddError(
"RGReadStatus: Unable to Enable RG",
err.Error(),
)
return diags
}
tflog.Info(ctx, "RGReadStatus: resource group enabled successfully", map[string]any{"rg_id": recordRG.ID})
}
}
case status.Destroyed:
diags.AddError(
"RGReadStatus: RG is in status Destroyed",
fmt.Sprintf("the resource with rg_id %d cannot be read because it has been destroyed", recordRG.ID),
)
return diags
}
return nil
}