|
|
|
|
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
|
|
|
|
|
}
|