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" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/rg" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/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()), } // set up quota optional parameters if !plan.Quota.IsUnknown() { var quota models.QuotaModel diags := plan.Quota.As(ctx, "a, basetypes.ObjectAsOptions{}) if diags.HasError() { return createReq, diags } if quota.CPU.IsUnknown() { createReq.MaxCPUCapacity = -1 // default value -1 } else { createReq.MaxCPUCapacity = quota.CPU.ValueInt64() } if quota.Disk.IsUnknown() { createReq.MaxVDiskCapacity = -1 // default value -1 } else { createReq.MaxVDiskCapacity = quota.Disk.ValueInt64() } if quota.Ram.IsUnknown() { createReq.MaxMemoryCapacity = -1 // default value -1 } else { createReq.MaxMemoryCapacity = quota.Ram.ValueInt64() } if quota.ExtTraffic.IsUnknown() { createReq.MaxNetworkPeerTransfer = -1 // default value -1 } else { createReq.MaxNetworkPeerTransfer = quota.ExtTraffic.ValueInt64() } if quota.ExtIps.IsUnknown() { createReq.MaxNumPublicIP = -1 // default value -1 } else { createReq.MaxNumPublicIP = quota.ExtIps.ValueInt64() } } else { createReq.MaxCPUCapacity = -1 createReq.MaxVDiskCapacity = -1 createReq.MaxMemoryCapacity = -1 createReq.MaxNetworkPeerTransfer = -1 createReq.MaxNumPublicIP = -1 } // set up defNet, owner, ipcidr, description, extNetId, extIp, registerComputes optional parameters if !plan.DefNetType.IsNull() { createReq.DefNet = plan.DefNetType.ValueString() } if !plan.Owner.IsNull() { createReq.Owner = plan.Owner.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() } return createReq, nil } func RGCheckPresence(ctx context.Context, rgId uint64, c *client.Client) (*rg.RecordResourceGroup, error) { tflog.Info(ctx, fmt.Sprintf("Get info about resource group with ID - %v", rgId)) recordRG, err := c.CloudAPI().RG().Get(ctx, rg.GetRequest{RGID: rgId}) if err != nil { return nil, fmt.Errorf("cannot get info about resource group with error: %w", err) } tflog.Info(ctx, "UtilityRGCheckPresence resourceRG: response from CloudAPI().RG().Get", map[string]any{"rg_id": rgId, "response": recordRG}) return recordRG, err } // RestoreRG performs resource group Restore request. Returns error in case of failures. func RestoreRG(ctx context.Context, rgId uint64, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} restoreReq := rg.RestoreRequest{RGID: rgId} tflog.Info(ctx, "utilityRestoreRG: before calling CloudAPI().RG().Restore", map[string]any{"rgId": rgId, "req": restoreReq}) res, err := c.CloudAPI().RG().Restore(ctx, restoreReq) if err != nil { diags.AddError( "RestoreRG: cannot restore resource group", err.Error(), ) return diags } tflog.Info(ctx, "utilityRestoreRG: response from CloudAPI().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 *client.Client) error { enableReq := rg.EnableRequest{RGID: rgId} tflog.Info(ctx, "utilityEnableRG: before calling CloudAPI().RG().Enable", map[string]any{"rg_id": rgId, "req": enableReq}) res, err := c.CloudAPI().RG().Enable(ctx, enableReq) tflog.Info(ctx, "utilityEnableRG: response from CloudAPI().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 *client.Client) error { disableReq := rg.DisableRequest{RGID: rgId} tflog.Info(ctx, "utilityDisableRG: before calling CloudAPI().RG().Disable", map[string]any{"rg_id": rgId, "req": disableReq}) res, err := c.CloudAPI().RG().Disable(ctx, disableReq) tflog.Info(ctx, "utilityDisableRG: response from CloudAPI().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 *client.Client) 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 } if !plan.UniqPools.Equal(state.UniqPools) { if plan.UniqPools.IsNull() || len(plan.UniqPools.Elements()) == 0 { updateReq.ClearUniqPools = true } else { uPoolsList := make([]string, 0, len(plan.UniqPools.Elements())) diags.Append(plan.UniqPools.ElementsAs(ctx, &uPoolsList, true)...) if diags.HasError() { tflog.Error(ctx, "utilityUpdateRG: cannot populate result with plan.UniqPools object element") return diags } updateReq.UniqPools = uPoolsList } 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 updQuotaNeeded bool var quotaPlan, quotaState models.QuotaModel if !plan.Quota.IsNull() { diags = plan.Quota.As(ctx, "aPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}) if diags.HasError() { return diags } } if !state.Quota.IsNull() { diags = state.Quota.As(ctx, "aState, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}) if diags.HasError() { return diags } } if !plan.Quota.IsNull() && !state.Quota.IsNull() { if !quotaPlan.CPU.Equal(quotaState.CPU) { updateReq.MaxCPUCapacity = quotaPlan.CPU.ValueInt64() updQuotaNeeded = true } if !quotaPlan.Disk.Equal(quotaState.Disk) { updateReq.MaxVDiskCapacity = quotaPlan.Disk.ValueInt64() updQuotaNeeded = true } if !quotaPlan.Ram.Equal(quotaState.Ram) { updateReq.MaxMemoryCapacity = quotaPlan.Ram.ValueInt64() updQuotaNeeded = true } if !quotaPlan.ExtTraffic.Equal(quotaState.ExtTraffic) { updateReq.MaxNetworkPeerTransfer = quotaPlan.ExtTraffic.ValueInt64() updQuotaNeeded = true } if !quotaPlan.ExtIps.Equal(quotaState.ExtIps) { updateReq.MaxNumPublicIP = quotaPlan.ExtIps.ValueInt64() updQuotaNeeded = true } } if state.Quota.IsNull() { if !quotaPlan.CPU.IsNull() { updateReq.MaxCPUCapacity = quotaPlan.CPU.ValueInt64() } if !quotaPlan.Disk.IsNull() { updateReq.MaxVDiskCapacity = quotaPlan.Disk.ValueInt64() } if !quotaPlan.Ram.IsNull() { updateReq.MaxMemoryCapacity = quotaPlan.Ram.ValueInt64() } if !quotaPlan.ExtTraffic.IsNull() { updateReq.MaxNetworkPeerTransfer = quotaPlan.ExtTraffic.ValueInt64() } if !quotaPlan.ExtIps.IsNull() { updateReq.MaxNumPublicIP = quotaPlan.ExtIps.ValueInt64() } updQuotaNeeded = true } // plan.Quota.IsNull() == true: we do not change quota in case it used to be set and then deleted if updQuotaNeeded { tflog.Info(ctx, "utilityUpdateRG: new quota specified", map[string]any{ "rg_id": plan.Id.ValueString()}) updateNeeded = true } if updateNeeded { tflog.Info(ctx, "utilityUpdateRG: before calling CloudAPI().RG().Update", map[string]any{"rg_id": plan.Id.ValueString(), "req": updateReq}) res, err := c.CloudAPI().RG().Update(ctx, updateReq) tflog.Info(ctx, "utilityUpdateRG: response from CloudAPI().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 CloudAPI().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 *client.Client) 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 *client.Client) 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 *client.Client) 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 CloudAPI().RG().AccessRevoke", map[string]any{"rg_id": plan.Id.ValueString(), "req": revokeReq}) res, err := c.CloudAPI().RG().AccessRevoke(ctx, revokeReq) tflog.Info(ctx, "AccessUpdateRG: response from CloudAPI().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 CloudAPI().RG().AccessGrant", map[string]any{"rg_id": plan.Id.ValueString(), "req": grantReq}) res, err := c.CloudAPI().RG().AccessGrant(ctx, grantReq) tflog.Info(ctx, "AccessUpdateRG: response from CloudAPI().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 *client.Client) 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 CloudAPI().RG().AccessGrant", map[string]any{ "rg_id": rgId, "req": grantReq}) res, err := c.CloudAPI().RG().AccessGrant(ctx, grantReq) if err != nil { diags.AddWarning("AccessCreateRG: Unable to grant access for RG", err.Error()) } tflog.Info(ctx, "AccessCreateRG: response from CloudAPI().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 *client.Client) 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 { 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}) if plan.DefNet.IsNull() { removeReq := rg.RemoveDefNetRequest{RGID: uint64(state.RGID.ValueInt64())} res, err := c.CloudAPI().RG().RemoveDefNet(ctx, removeReq) if err != nil { diags.AddError( "SetDefNetUpdateRG: can not remove defNet for rg", err.Error()) return diags } tflog.Info(ctx, "utilitySetDefNetUpdateRG: response from CloudAPI().RG().RemoveDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "response": res}) } else { 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 CloudAPI().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "req": setDefNetReq}) res, err := c.CloudAPI().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 CloudAPI().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "response": res}) } } if !setDefNetNeeded { tflog.Info(ctx, "utilitySetDefNetUpdateRG: call for CloudAPI().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 *client.Client) 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 CloudAPI().RG().SetDefNet", map[string]any{"rg_id": rgId, "req": setDefNetReq}) res, err := c.CloudAPI().RG().SetDefNet(ctx, setDefNetReq) tflog.Info(ctx, "SetDefNetCreateRG: response from CloudAPI().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 CloudAPI().RG().SetDefNet was not needed", map[string]any{ "rg_id": rgId, "def_net_plan": plan.DefNet}) } 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 *client.Client) 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 }