This commit is contained in:
asteam
2024-08-23 16:55:50 +03:00
parent 6f40af6a5f
commit 003e4d656e
524 changed files with 43376 additions and 432 deletions

View File

@@ -0,0 +1,21 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
)
func AccountAuditsListDataSourceCheckPresence(ctx context.Context, accountId uint64, c *decort.DecortClient) (*account.ListAudits, error) {
tflog.Info(ctx, fmt.Sprintf("AccountAuditsListDataSourceCheckPresence: Get info about account audits with account ID - %v", accountId))
auditsList, err := c.CloudBroker().Account().Audits(ctx, account.AuditsRequest{AccountID: accountId})
if err != nil {
return nil, fmt.Errorf("cannot get info about account audits with error: %w", err)
}
return &auditsList, err
}

View File

@@ -0,0 +1,21 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
)
func AccountAvailableTemplatesListDataSourceCheckPresence(ctx context.Context, accountId uint64, c *decort.DecortClient) ([]uint64, error) {
tflog.Info(ctx, fmt.Sprintf("AccountAvailableTemplatesListDataSourceCheckPresence: Get info about templates with account ID - %v", accountId))
templatesList, err := c.CloudBroker().Account().ListAvailableTemplates(ctx, account.ListAvailableTemplatesRequest{AccountID: accountId})
if err != nil {
return nil, fmt.Errorf("cannot get info about templated with error: %w", err)
}
return templatesList, err
}

View File

@@ -0,0 +1,61 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
)
func AccountComputesListDataSourceCheckPresence(ctx context.Context, plan *models.ListComputesModel, c *decort.DecortClient) (*account.ListComputes, error) {
tflog.Info(ctx, "AccountComputesListDataSourceCheckPresence: Get info about list accounts")
req := account.ListComputesRequest{
AccountID: uint64(plan.AccountID.ValueInt64()),
}
if !plan.ComputeID.IsNull() {
req.ComputeID = uint64(plan.ComputeID.ValueInt64())
}
if !plan.Name.IsNull() {
req.Name = plan.Name.ValueString()
}
if !plan.RGName.IsNull() {
req.RGName = plan.RGName.ValueString()
}
if !plan.RGID.IsNull() {
req.RGID = uint64(plan.RGID.ValueInt64())
}
if !plan.TechStatus.IsNull() {
req.TechStatus = plan.TechStatus.ValueString()
}
if !plan.IpAddress.IsNull() {
req.IPAddress = plan.IpAddress.ValueString()
}
if !plan.ExtNetName.IsNull() {
req.ExtNetName = plan.ExtNetName.ValueString()
}
if !plan.ExtNetID.IsNull() {
req.ExtNetID = uint64(plan.ExtNetID.ValueInt64())
}
if !plan.Page.IsNull() {
req.Page = uint64(plan.Page.ValueInt64())
}
if !plan.Size.IsNull() {
req.Size = uint64(plan.Size.ValueInt64())
}
if !plan.SortBy.IsNull() {
req.SortBy = plan.SortBy.ValueString()
}
tflog.Info(ctx, "AccountComputesListDataSourceCheckPresence: before call CloudBroker().Account().List", map[string]any{"req": req})
listComputes, err := c.CloudBroker().Account().ListComputes(ctx, req)
if err != nil {
return nil, fmt.Errorf("cannot get list computes with error: %w", err)
}
tflog.Info(ctx, "AccountComputesListDataSourceCheckPresence: response from CloudBroker().Account().List")
return listComputes, err
}

View File

@@ -0,0 +1,51 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
)
func AccountDisksListCheckPresence(ctx context.Context, plan *models.DataSourceAccountDisksListModel, c *decort.DecortClient) (*account.ListDisks, error) {
tflog.Info(ctx, "AccountDisksListCheckPresence: Get info about account disks list")
disksListReq := account.ListDisksRequest{
AccountID: uint64(plan.AccountID.ValueInt64()),
}
if !plan.DiskID.IsNull() {
disksListReq.DiskID = uint64(plan.DiskID.ValueInt64())
}
if !plan.Name.IsNull() {
disksListReq.Name = plan.Name.ValueString()
}
if !plan.DiskMaxSize.IsNull() {
disksListReq.DiskMaxSize = uint64(plan.DiskMaxSize.ValueInt64())
}
if !plan.Type.IsNull() {
disksListReq.Type = plan.Type.ValueString()
}
if !plan.Page.IsNull() {
disksListReq.Page = uint64(plan.Page.ValueInt64())
}
if !plan.Size.IsNull() {
disksListReq.Size = uint64(plan.Size.ValueInt64())
}
if !plan.SortBy.IsNull() {
disksListReq.SortBy = plan.SortBy.ValueString()
}
tflog.Info(ctx, "AccountDisksListCheckPresence: before call CloudBroker().Account().ListDisks", map[string]any{"req": disksListReq})
disksList, err := c.CloudBroker().Account().ListDisks(ctx, disksListReq)
if err != nil {
return nil, fmt.Errorf("cannot get info about account disks list with error: %w", err)
}
tflog.Info(ctx, "AccountDisksListCheckPresence: response from CloudBroker().Account().ListDisks")
return disksList, err
}

View File

@@ -0,0 +1,55 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
)
func AccountFlipgroupsListCheckPresence(ctx context.Context, plan *models.DataSourceAccountFlipgroupsListModel, c *decort.DecortClient) (*account.ListFLIPGroups, error) {
tflog.Info(ctx, "AccountFlipgroupsListCheckPresence: Get info about account flipgroups list")
flipgroupsListReq := account.ListFLIPGroupsRequest{AccountID: uint64(plan.AccountID.ValueInt64())}
if !plan.Name.IsNull() {
flipgroupsListReq.Name = plan.Name.ValueString()
}
if !plan.VINSID.IsNull() {
flipgroupsListReq.VINSID = uint64(plan.VINSID.ValueInt64())
}
if !plan.VINSName.IsNull() {
flipgroupsListReq.VINSName = plan.VINSName.ValueString()
}
if !plan.ExtNetID.IsNull() {
flipgroupsListReq.ExtNetID = uint64(plan.ExtNetID.ValueInt64())
}
if !plan.ByIP.IsNull() {
flipgroupsListReq.ByIP = plan.ByIP.ValueString()
}
if !plan.FLIPGroupID.IsNull() {
flipgroupsListReq.FLIPGroupID = uint64(plan.FLIPGroupID.ValueInt64())
}
if !plan.SortBy.IsNull() {
flipgroupsListReq.SortBy = plan.SortBy.ValueString()
}
if !plan.Page.IsNull() {
flipgroupsListReq.Page = uint64(plan.Page.ValueInt64())
}
if !plan.Size.IsNull() {
flipgroupsListReq.Size = uint64(plan.Size.ValueInt64())
}
tflog.Info(ctx, "AccountListCheckPresence: before call CloudBroker().Account().ListFLIPGroups", map[string]any{"req": flipgroupsListReq})
flipgroupsList, err := c.CloudBroker().Account().ListFLIPGroups(ctx, flipgroupsListReq)
if err != nil {
return nil, fmt.Errorf("cannot get info about account flipgroups list with error: %w", err)
}
tflog.Info(ctx, "AccountListCheckPresence: response from CloudBroker().Account().ListFLIPGroups")
return flipgroupsList, err
}

View File

@@ -0,0 +1,24 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
)
func AccountGetResourceConsumptionDataSourceCheckPresence(ctx context.Context, accountId uint64, c *decort.DecortClient) (*account.RecordResourceConsumption, error) {
tflog.Info(ctx, fmt.Sprintf("AccountGetResourceConsumptionDataSourceCheckPresence: Get info about account with ID - %v", accountId))
record, err := c.CloudBroker().Account().GetResourceConsumption(ctx, account.GetResourceConsumptionRequest{AccountID: accountId})
if err != nil {
return nil, fmt.Errorf("cannot get info about resource with error: %w", err)
}
tflog.Info(ctx, "AccountGetResourceConsumptionDataSourceCheckPresence: response from CloudBroker().Account().GetResourceConsumption",
map[string]any{"account_id": accountId, "response": record})
return record, err
}

View File

@@ -0,0 +1,46 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
)
func AccountListDeletedCheckPresence(ctx context.Context, plan *models.DataSourceAccountListDeletedModel, c *decort.DecortClient) (*account.ListAccounts, error) {
tflog.Info(ctx, "AccountListDeletedCheckPresence: Get info about account list deleted")
accListDelReq := account.ListDeletedRequest{}
if !plan.ByID.IsNull() {
accListDelReq.ByID = uint64(plan.ByID.ValueInt64())
}
if !plan.Name.IsNull() {
accListDelReq.Name = plan.Name.ValueString()
}
if !plan.ACL.IsNull() {
accListDelReq.ACL = plan.ACL.ValueString()
}
if !plan.Page.IsNull() {
accListDelReq.Page = uint64(plan.Page.ValueInt64())
}
if !plan.Size.IsNull() {
accListDelReq.Size = uint64(plan.Size.ValueInt64())
}
if !plan.SortBy.IsNull() {
accListDelReq.SortBy = plan.SortBy.ValueString()
}
tflog.Info(ctx, "AccountListDeletedCheckPresence: before call CloudBroker().Account().ListDeleted", map[string]any{"req": accListDelReq})
accListDel, err := c.CloudBroker().Account().ListDeleted(ctx, accListDelReq)
if err != nil {
return nil, fmt.Errorf("cannot get info about account with error: %w", err)
}
tflog.Info(ctx, "AccountListDeletedCheckPresence: response from CloudBroker().Account().ListDeleted")
return accListDel, err
}

View File

@@ -0,0 +1,24 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
)
func AccountGetResourceConsumptionListDataSourceCheckPresence(ctx context.Context, c *decort.DecortClient) (*account.ListResources, error) {
tflog.Info(ctx, "AccountGetResourceConsumptionListDataSourceCheckPresence: Get info about account resource consumption list")
record, err := c.CloudBroker().Account().ListResourceConsumption(ctx)
if err != nil {
return nil, fmt.Errorf("cannot get info about resource with error: %w", err)
}
tflog.Info(ctx, "AccountGetResourceConsumptionListDataSourceCheckPresence: response from CloudBroker().Account().ListResourceConsumption",
map[string]any{"response": record})
return record, err
}

View File

@@ -0,0 +1,52 @@
package utilities
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
)
func AccountRGListCheckPresence(ctx context.Context, plan *models.DataSourceAccountRGListModel, c *decort.DecortClient) (*account.ListRG, error) {
tflog.Info(ctx, "AccountRGListCheckPresence: Get info about account rg list")
rgListReq := account.ListRGRequest{AccountID: uint64(plan.AccountID.ValueInt64())}
if !plan.RGID.IsNull() {
rgListReq.RGID = uint64(plan.RGID.ValueInt64())
}
if !plan.VinsID.IsNull() {
rgListReq.VINSID = uint64(plan.VinsID.ValueInt64())
}
if !plan.VMID.IsNull() {
rgListReq.VMID = uint64(plan.VMID.ValueInt64())
}
if !plan.Name.IsNull() {
rgListReq.Name = plan.Name.ValueString()
}
if !plan.Status.IsNull() {
rgListReq.Status = plan.Status.ValueString()
}
if !plan.Page.IsNull() {
rgListReq.Page = uint64(plan.Page.ValueInt64())
}
if !plan.Size.IsNull() {
rgListReq.Size = uint64(plan.Size.ValueInt64())
}
if !plan.SortBy.IsNull() {
rgListReq.SortBy = plan.SortBy.ValueString()
}
tflog.Info(ctx, "AccountRGListCheckPresence: before call CloudBroker().Account().ListRG", map[string]any{"req": rgListReq})
rgList, err := c.CloudBroker().Account().ListRG(ctx, rgListReq)
if err != nil {
return nil, fmt.Errorf("cannot get info about account with error: %w", err)
}
tflog.Info(ctx, "AccountRGListCheckPresence: response from CloudBroker().Account().ListRG")
return rgList, err
}

View File

@@ -0,0 +1,725 @@
package utilities
import (
"context"
"fmt"
"strconv"
"github.com/hashicorp/terraform-plugin-framework/diag"
"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/account"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/account/models"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/ic"
"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/status"
)
// AccountResourceCheckPresence checks if account with accountId exists
func AccountResourceCheckPresence(ctx context.Context, accountId uint64, c *decort.DecortClient) (*account.RecordAccount, error) {
tflog.Info(ctx, fmt.Sprintf("AccountResourceCheckPresence: Get info about resource with ID - %v", accountId))
accountRecord, err := c.CloudBroker().Account().Get(ctx, account.GetRequest{AccountID: accountId})
if err != nil {
return nil, fmt.Errorf("AccountResourceCheckPresence: cannot get info about resource with error: %w", err)
}
tflog.Info(ctx, "AccountResourceCheckPresence: response from CloudBroker().Account().Get", map[string]any{"account_id": accountId, "response": accountRecord})
return accountRecord, err
}
// AccountReadStatus loads account resource by its id, gets it current status. Performs restore and enable if needed for
// Deleted status.
// In case of failure returns errors.
func AccountReadStatus(ctx context.Context, state *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "AccountReadStatus: Read status resource with ID", map[string]any{"account_id": state.Id.ValueString()})
diags := diag.Diagnostics{}
accountId, err := strconv.ParseUint(state.Id.ValueString(), 10, 64)
if err != nil {
diags.AddError("AccountReadStatus: Cannot parse resource ID from state", err.Error())
return diags
}
recordAccount, err := AccountResourceCheckPresence(ctx, accountId, c)
if err != nil {
diags.AddError("AccountReadStatus: Unable to Read account before status check", err.Error())
return diags
}
// check resource status
switch recordAccount.Status {
case status.Disabled:
tflog.Info(ctx, "The account is in status Disabled, troubles may occur with update. Please, enable account first.")
case status.Deleted:
restore := state.Restore.ValueBool()
if state.Restore.IsNull() {
restore = true
} // default true
if restore {
// attempt to restore account
tflog.Info(ctx, "AccountReadStatus: account with status.Deleted is being read, attempt to restore it", map[string]any{
"account_id": accountId,
"status": recordAccount.Status})
diags.Append(RestoreAccount(ctx, accountId, c)...)
if diags.HasError() {
tflog.Error(ctx, "AccountReadStatus: cannot restore account")
return diags
}
tflog.Info(ctx, "AccountReadStatus: account restored successfully", map[string]any{"account_id": accountId})
} else {
tflog.Info(ctx, "AccountReadStatus: account is i status Deleted but restore is not specified")
}
case status.Destroyed:
diags.AddError(
"AccountReadStatus: Account is in status Destroyed",
fmt.Sprintf("the resource with account_id %d cannot be read or updated because it has been destroyed", accountId),
)
return diags
case status.Destroying:
diags.AddError(
"AccountReadStatus: Account is in progress with status Destroying",
fmt.Sprintf("the resource with account_id %d cannot be read or updated because it is currently being destroyed", accountId),
)
return diags
}
return nil
}
// RestoreAccount performs account Restore request.
// Returns error in case of failures.
func RestoreAccount(ctx context.Context, accountId uint64, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
restoreReq := account.RestoreRequest{
AccountID: accountId,
}
tflog.Info(ctx, "RestoreAccount: before calling CloudBroker().Account().Restore", map[string]any{"account_id": accountId, "req": restoreReq})
res, err := c.CloudBroker().Account().Restore(ctx, restoreReq)
if err != nil {
diags.AddError(
"RestoreAccount: cannot restore account",
err.Error(),
)
return diags
}
tflog.Info(ctx, "RestoreAccount: response from CloudBroker().Account().Restore", map[string]any{"account_id": accountId, "response": res})
return nil
}
// EnableDisableAccount performs account Enable/Disable request.
// Returns error in case of failures.
func EnableDisableAccount(ctx context.Context, accountId uint64, enable bool, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start EnableDisableAccount", map[string]any{"account_id": accountId})
diags := diag.Diagnostics{}
if enable {
tflog.Info(ctx, "EnableDisableAccount: before calling CloudBroker().Account().Enable", map[string]any{"account_id": accountId})
res, err := c.CloudBroker().Account().Enable(ctx, account.EnableRequest{AccountID: accountId})
if err != nil {
diags.AddError(
"EnableDisableAccount: cannot enable account",
err.Error(),
)
return diags
}
tflog.Info(ctx, "EnableDisableAccount: response from CloudBroker().Account().Enable", map[string]any{"account_id": accountId, "response": res})
return nil
}
tflog.Info(ctx, "EnableDisableAccount: before calling CloudBroker().Account().Disable", map[string]any{"account_id": accountId})
res, err := c.CloudBroker().Account().Disable(ctx, account.DisableRequest{AccountID: accountId})
if err != nil {
diags.AddError(
"EnableDisableAccount: cannot disable account",
err.Error(),
)
return diags
}
tflog.Info(ctx, "EnableDisableAccount: response from CloudBroker().Account().Disable", map[string]any{"account_id": accountId, "response": res})
return nil
}
func UtilityAccountCreate(ctx context.Context, plan *models.ResourceAccountModel, c *decort.DecortClient) (diag.Diagnostics, *uint64) {
tflog.Info(ctx, "Start UtilityAccountCreate", map[string]any{"account_id": plan.AccountID})
diags := diag.Diagnostics{}
req := account.CreateRequest{
Name: plan.AccountName.ValueString(),
Username: plan.Username.ValueString(),
}
if !plan.EmailAddress.IsUnknown() {
req.EmailAddress = plan.EmailAddress.ValueString()
}
if !plan.SendAccessEmails.IsUnknown() {
req.SendAccessEmails = plan.SendAccessEmails.ValueBool()
}
if !plan.UniqPools.IsUnknown() {
var uniqPools []string
diags.Append(plan.UniqPools.ElementsAs(ctx, &uniqPools, true)...)
if diags.HasError() {
tflog.Error(ctx, "UtilityAccountCreate: cannot populate UniqPools with plan.UniqPools object element")
return diags, nil
}
req.UniqPools = uniqPools
}
if !plan.ResourceLimits.IsUnknown() {
var resourceLimitsPlan models.ResourceLimitsInAccountResourceModel
diags.Append(plan.ResourceLimits.As(ctx, &resourceLimitsPlan, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
tflog.Error(ctx, "UtilityAccountCreate: cannot populate ResourceLimits with plan.ResourceLimits object element")
return diags, nil
}
if resourceLimitsPlan.CUM.ValueFloat64() == 0 {
req.MaxMemoryCapacity = -1
} else {
req.MaxMemoryCapacity = int64(resourceLimitsPlan.CUM.ValueFloat64())
}
if resourceLimitsPlan.CUD.ValueFloat64() == 0 {
req.MaxVDiskCapacity = -1
} else {
req.MaxVDiskCapacity = int64(resourceLimitsPlan.CUD.ValueFloat64())
}
if resourceLimitsPlan.CUC.ValueFloat64() == 0 {
req.MaxCPUCapacity = -1
} else {
req.MaxCPUCapacity = int64(resourceLimitsPlan.CUC.ValueFloat64())
}
if resourceLimitsPlan.CUI.ValueFloat64() == 0 {
req.MaxNumPublicIP = -1
} else {
req.MaxNumPublicIP = int64(resourceLimitsPlan.CUI.ValueFloat64())
}
if resourceLimitsPlan.CUNP.ValueFloat64() == 0 {
req.MaxNetworkPeerTransfer = -1
} else {
req.MaxNetworkPeerTransfer = int64(resourceLimitsPlan.CUNP.ValueFloat64())
}
if resourceLimitsPlan.GPUUnits.ValueFloat64() == 0 {
req.GPUUnits = -1
} else {
req.GPUUnits = int64(resourceLimitsPlan.GPUUnits.ValueFloat64())
}
}
accountId, err := c.CloudBroker().Account().Create(ctx, req)
if err != nil {
diags.AddError("UtilityAccountCreate: Unable to create account",
err.Error())
return diags, nil
}
if !plan.Users.IsUnknown() {
usersPlan := make([]models.UsersModel, 0, len(plan.Users.Elements()))
diagsI := plan.Users.ElementsAs(ctx, &usersPlan, true)
if diagsI.HasError() {
tflog.Error(ctx, "UtilityAccountCreate: cannot populate usersPlan with plan.Users list elements")
diags.AddWarning("UtilityAccountCreate: cannot populate usersPlan with plan.Users list elements",
fmt.Sprintf("%v", diagsI))
return diags, nil
}
for _, v := range usersPlan {
req := account.AddUserRequest{
AccountID: accountId,
Username: v.UserID.ValueString(),
AccessType: v.AccessType.ValueString(),
}
_, err := c.CloudBroker().Account().AddUser(ctx, req)
if err != nil {
diags.AddWarning("UtilityAccountCreate: Unable to add users",
err.Error())
return diags, nil
}
}
}
if !plan.CPUAllocationParameter.IsUnknown() {
req := account.SetCPUAllocationParameterRequest{
AccountID: accountId,
StrictLoose: plan.CPUAllocationParameter.ValueString(),
}
_, err := c.CloudBroker().Account().SetCPUAllocationParameter(ctx, req)
if err != nil {
diags.AddWarning("UtilityAccountCreate: Unable to set CPUAllocationParameter ",
err.Error())
return diags, nil
}
}
if !plan.CPUAllocationRatio.IsUnknown() {
req := account.SetCPUAllocationRatioRequest{
AccountID: accountId,
Ratio: plan.CPUAllocationRatio.ValueFloat64(),
}
_, err := c.CloudBroker().Account().SetCPUAllocationRatio(ctx, req)
if err != nil {
diags.AddWarning("UtilityAccountCreate: Unable to set CPUAllocationRatio ",
err.Error())
return diags, nil
}
}
if !plan.Enable.IsUnknown() && !plan.Enable.ValueBool() {
_, err := c.CloudBroker().Account().Disable(ctx, account.DisableRequest{
AccountID: accountId,
})
if err != nil {
diags.AddWarning("UtilityAccountCreate: Unable to disable account",
err.Error())
return diags, nil
}
}
if !plan.AvailableTemplates.IsUnknown() {
diagsI := UtilityAccountAvailiableTemplatesUpdate(ctx, plan, plan, true, c)
if diagsI.HasError() {
tflog.Error(ctx, "UtilityAccountCreate: error with utilityAccountAvailiableTemplatesUpdate")
diags.AddWarning("UtilityAccountCreate: cannot populate usersPlan with plan.Users list elements", fmt.Sprintf("%v", diagsI))
return diags, nil
}
}
tflog.Info(ctx, "End UtilityAccountCreate", map[string]any{"account_id": plan.AccountID.ValueInt64()})
return diags, &accountId
}
// UpdateAccount updates disk data: account_name, resource_limits, send_access_emails.
// Returns error in case of failures.
func UpdateAccount(ctx context.Context, accountId uint64, plan, state *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start UpdateAccount", map[string]any{"account_id": accountId})
var diags diag.Diagnostics
var updateNeeded bool
updateReq := account.UpdateRequest{
AccountID: accountId,
}
// check if account_name was changed
if !plan.AccountName.Equal(state.AccountName) {
updateReq.Name = plan.AccountName.ValueString()
updateNeeded = true
}
// check if resource_limits were changed
if !plan.ResourceLimits.Equal(state.ResourceLimits) && !plan.ResourceLimits.IsUnknown() {
tflog.Info(ctx, "UpdateAccount: new ResourceLimits specified", map[string]any{"account_id": accountId})
var resourceLimitsPlan models.ResourceLimitsInAccountResourceModel
diags.Append(plan.ResourceLimits.As(ctx, &resourceLimitsPlan, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
tflog.Error(ctx, "UpdateAccount: cannot populate ResourceLimits with plan.ResourceLimits object element")
return diags
}
if resourceLimitsPlan.CUM.ValueFloat64() == 0 {
updateReq.MaxMemoryCapacity = -1
} else {
updateReq.MaxMemoryCapacity = int64(resourceLimitsPlan.CUM.ValueFloat64())
}
if resourceLimitsPlan.CUD.ValueFloat64() == 0 {
updateReq.MaxVDiskCapacity = -1
} else {
updateReq.MaxVDiskCapacity = int64(resourceLimitsPlan.CUD.ValueFloat64())
}
if resourceLimitsPlan.CUC.ValueFloat64() == 0 {
updateReq.MaxCPUCapacity = -1
} else {
updateReq.MaxCPUCapacity = int64(resourceLimitsPlan.CUC.ValueFloat64())
}
if resourceLimitsPlan.CUI.ValueFloat64() == 0 {
updateReq.MaxNumPublicIP = -1
} else {
updateReq.MaxNumPublicIP = int64(resourceLimitsPlan.CUI.ValueFloat64())
}
if resourceLimitsPlan.CUNP.ValueFloat64() == 0 {
updateReq.MaxNetworkPeerTransfer = -1
} else {
updateReq.MaxNetworkPeerTransfer = int64(resourceLimitsPlan.CUNP.ValueFloat64())
}
if resourceLimitsPlan.GPUUnits.ValueFloat64() == 0 {
updateReq.GPUUnits = -1
} else {
updateReq.GPUUnits = int64(resourceLimitsPlan.GPUUnits.ValueFloat64())
}
updateNeeded = true
}
// check if send_access_emails was changed
if !plan.SendAccessEmails.Equal(state.SendAccessEmails) && !plan.SendAccessEmails.IsNull() {
updateReq.SendAccessEmails = plan.SendAccessEmails.ValueBool()
updateNeeded = true
}
if !updateNeeded {
tflog.Info(ctx, "UpdateAccount: no general account update is needed because neither account_name, nor resource_limits, nor send_access_emails were changed.", map[string]any{
"account_id": plan.Id.ValueString(),
})
return nil
}
// perform account update
tflog.Info(ctx, "UpdateAccount: before calling CloudBroker().Account().Update", map[string]any{
"account_id": accountId,
"req": updateReq,
})
res, err := c.CloudBroker().Account().Update(ctx, updateReq)
if err != nil {
diags.AddError("UpdateAccount: Unable to update account",
err.Error())
return diags
}
tflog.Info(ctx, "UpdateAccount: response from CloudBroker().Account().Update", map[string]any{
"account_id": accountId,
"response": res})
return nil
}
// AddDeleteUsersAccount adds/deletes users to/from account.
// In case of failure returns errors.
func AddDeleteUsersAccount(ctx context.Context, accountId uint64, plan, state *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start AddDeleteUsersAccount: new users specified", map[string]any{"account_id": accountId})
diags := diag.Diagnostics{}
usersPlan := make([]models.UsersModel, 0, len(plan.Users.Elements()))
diags.Append(plan.Users.ElementsAs(ctx, &usersPlan, true)...)
if diags.HasError() {
tflog.Error(ctx, "AddDeleteUsersAccount: cannot populate usersPlan with plan.Users list elements")
return diags
}
usersState := make([]models.UsersModel, 0, len(state.Users.Elements()))
diags.Append(state.Users.ElementsAs(ctx, &usersState, true)...)
if diags.HasError() {
tflog.Error(ctx, "AddDeleteUsersAccount: cannot populate usersState with state.Users list elements")
return diags
}
// define users to be deleted, added and updated
var deletedUsers, addedUsers, updatedUsers []models.UsersModel
for _, user := range usersState {
if !containsUser(usersPlan, user) {
deletedUsers = append(deletedUsers, user)
}
}
for _, user := range usersPlan {
if !containsUser(usersState, user) {
addedUsers = append(addedUsers, user)
} else if isChangedUser(usersState, user) {
updatedUsers = append(updatedUsers, user)
}
}
// delete users
if len(deletedUsers) == 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: no users need to be deleted", map[string]any{"account_id": accountId})
}
if len(deletedUsers) > 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: users need to be deleted", map[string]any{
"accountId": accountId,
"deletedUsers": deletedUsers})
for _, user := range deletedUsers {
delUserReq := account.DeleteUserRequest{
AccountID: accountId,
UserName: user.UserID.ValueString(),
RecursiveDelete: user.RecursiveDelete.ValueBool(), // default false
}
tflog.Info(ctx, "AddDeleteUsersAccount: before calling CloudBroker().Account().DeleteUser", map[string]any{"account_id": accountId, "req": delUserReq})
res, err := c.CloudBroker().Account().DeleteUser(ctx, delUserReq)
tflog.Info(ctx, "AddDeleteUsersAccount: response from CloudBroker().Account().DeleteUser", map[string]any{"account_id": accountId, "response": res})
if err != nil {
diags.AddError(
"AddDeleteUsersAccount: can not delete user from account",
err.Error())
}
}
}
// add users
if len(addedUsers) == 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: no users needs to be added", map[string]any{"account_id": accountId})
}
if len(addedUsers) > 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: users need to be added", map[string]any{"account_id": accountId})
for _, user := range addedUsers {
addUserReq := account.AddUserRequest{
AccountID: accountId,
Username: user.UserID.ValueString(),
AccessType: user.AccessType.ValueString(),
}
tflog.Info(ctx, "AddDeleteUsersAccount: before calling CloudBroker().Account().AddUser", map[string]any{
"account_id": accountId,
"addUserReq": addUserReq})
res, err := c.CloudBroker().Account().AddUser(ctx, addUserReq)
if err != nil {
diags.AddError("AddDeleteUsersAccount: Unable to add users to account",
err.Error())
}
tflog.Info(ctx, "AddDeleteUsersAccount: response from CloudBroker().Account().AddUser", map[string]any{
"account_id": accountId,
"response": res})
}
}
// update users
if len(updatedUsers) == 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: no users needs to be updated", map[string]any{"account_id": accountId})
}
if len(updatedUsers) > 0 {
tflog.Info(ctx, "AddDeleteUsersAccount: users need to be updated", map[string]any{"account_id": accountId})
for _, user := range updatedUsers {
updUserReq := account.UpdateUserRequest{
AccountID: accountId,
UserID: user.UserID.ValueString(),
AccessType: user.AccessType.ValueString(),
}
tflog.Info(ctx, "AddDeleteUsersAccount: before calling CloudBroker().Account().UpdateUser", map[string]any{
"account_id": accountId,
"updatedUsers": updatedUsers})
res, err := c.CloudBroker().Account().UpdateUser(ctx, updUserReq)
if err != nil {
diags.AddError("AddDeleteUsersAccount: Unable to update users",
err.Error())
}
tflog.Info(ctx, "AddDeleteUsersAccount: response from CloudBroker().Account().UpdateUser", map[string]any{
"account_id": accountId,
"response": res})
}
}
return diags
}
func containsUser(users []models.UsersModel, target models.UsersModel) bool {
for _, user := range users {
if target.UserID == user.UserID {
return true
}
}
return false
}
func isChangedUser(users []models.UsersModel, target models.UsersModel) bool {
for _, user := range users {
if user.UserID.Equal(target.UserID) && !user.AccessType.Equal(target.AccessType) {
return true
}
}
return false
}
func UtilityAccountCPUParameterUpdate(ctx context.Context, accountID uint64, plan *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start utilityAccountCPUParameterUpdate", map[string]any{"account_id": plan.AccountID})
diags := diag.Diagnostics{}
_, err := c.CloudBroker().Account().SetCPUAllocationParameter(ctx, account.SetCPUAllocationParameterRequest{
AccountID: accountID,
StrictLoose: plan.CPUAllocationParameter.ValueString(),
})
if err != nil {
diags.AddError("utilityAccountCPUParameterUpdate: Unable to update CPUAllocationParameter",
err.Error())
return diags
}
return diags
}
func UtilityAccountCPURatioUpdate(ctx context.Context, accountID uint64, plan *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start utilityAccountCPURatioUpdate", map[string]any{"account_id": plan.AccountID})
diags := diag.Diagnostics{}
_, err := c.CloudBroker().Account().SetCPUAllocationRatio(ctx, account.SetCPUAllocationRatioRequest{
AccountID: accountID,
Ratio: plan.CPUAllocationRatio.ValueFloat64(),
})
if err != nil {
diags.AddError("utilityAccountCPURatioUpdate: Unable to update CPUAllocationRatio",
err.Error())
return diags
}
return diags
}
func UtilityAccountAvailiableTemplatesUpdate(ctx context.Context, state, plan *models.ResourceAccountModel, afterCreate bool, c *decort.DecortClient) diag.Diagnostics {
tflog.Info(ctx, "Start utilityAccountAvailiableTemplatesUpdate", map[string]any{"account_id": plan.AccountID})
diags := diag.Diagnostics{}
if afterCreate {
imageIds := make([]uint64, 0, len(plan.AvailableTemplates.Elements()))
diags.Append(plan.AvailableTemplates.ElementsAs(ctx, &imageIds, true)...)
if diags.HasError() {
tflog.Error(ctx, "UpdateAccount: cannot populate AvailableTemplates with plan.AvailableTemplates object element")
return diags
}
if len(imageIds) == 0 {
diags.AddError(
"you have not been granted access to any images",
"len(imageIds) == 0",
)
return diags
}
tflog.Error(ctx, "you have not been granted access to any images")
if err := ic.ExistImages(ctx, imageIds, c); err != nil {
diags.AddError(
fmt.Sprintf("can not grant access for available templates: %s", err.Error()),
err.Error(),
)
return diags
}
tflog.Error(ctx, fmt.Sprint(imageIds))
req := account.GrantAccessTemplatesRequest{
AccountID: uint64(state.AccountID.ValueInt64()),
ImageIDs: imageIds,
}
_, err := c.CloudBroker().Account().GrantAccessTemplates(ctx, req)
if err != nil {
diags.AddError(
fmt.Sprintf("can not grant access for available templates: %s", err.Error()),
err.Error(),
)
return diags
}
return diags
}
var oldSet, newSet []int
diags.Append(plan.AvailableTemplates.ElementsAs(ctx, &newSet, true)...)
diags.Append(state.AvailableTemplates.ElementsAs(ctx, &oldSet, true)...)
if diags.HasError() {
tflog.Error(ctx, "UtilityAccountCreate: cannot populate newSet or oldSet with AvailableTemplates")
return diags
}
revokeAT := setDifference(oldSet, newSet)
if len(revokeAT) > 0 {
imageIds := make([]uint64, 0, len(revokeAT))
for _, imageId := range revokeAT {
imageIds = append(imageIds, imageId)
}
if err := ic.ExistImages(ctx, imageIds, c); err != nil {
diags.AddError(fmt.Sprintf("can not grant access for available templates: %s", err), err.Error())
return diags
}
req := account.RevokeAccessTemplatesRequest{
AccountID: uint64(state.AccountID.ValueInt64()),
ImageIDs: imageIds,
}
_, err := c.CloudBroker().Account().RevokeAccessTemplates(ctx, req)
if err != nil {
diags.AddError("UtilityAccountCreate: error with RevokeAccessTemplates", err.Error())
return diags
}
}
addedAT := setDifference(newSet, oldSet)
if len(addedAT) > 0 {
imageIds := make([]uint64, 0, len(addedAT))
imageIds = append(imageIds, addedAT...)
if err := ic.ExistImages(ctx, imageIds, c); err != nil {
diags.AddError(fmt.Sprintf("can grant access for available templates: %s", err.Error()), err.Error())
return diags
}
req := account.GrantAccessTemplatesRequest{
AccountID: uint64(plan.AccountID.ValueInt64()),
ImageIDs: imageIds,
}
_, err := c.CloudBroker().Account().GrantAccessTemplates(ctx, req)
if err != nil {
diags.AddError("UtilityAccountCreate: error with GrantAccessTemplates", err.Error())
return diags
}
}
return diags
}
func setDifference(set, check []int) []uint64 {
mapCheck := make(map[int]struct{})
for _, id := range check {
mapCheck[id] = struct{}{}
}
var diff []uint64
for _, id := range set {
if _, ok := mapCheck[id]; !ok {
diff = append(diff, uint64(id))
}
}
return diff
}
func UtilityAccountComputeFeaturesUpdate(ctx context.Context, accountID uint64, plan *models.ResourceAccountModel, c *decort.DecortClient) diag.Diagnostics {
diags := diag.Diagnostics{}
var compFeatures []string
diags.Append(plan.ComputeFeatures.ElementsAs(ctx, &compFeatures, true)...)
if diags.HasError() {
tflog.Error(ctx, "utilityAccountComputeFeaturesUpdate: cannot populate compFeatures with plan.ComputeFeatures object element")
return diags
}
req := account.UpdateComputeFeaturesRequest{
AccountID: accountID,
ComputeFeatures: compFeatures,
}
_, err := c.CloudBroker().Account().UpdateComputeFeatures(ctx, req)
if err != nil {
diags.AddError("utilityAccountComputeFeaturesUpdate: error with CloudBroker().Account().UpdateComputeFeatures", err.Error())
return diags
}
return diags
}