From 9bad8a69473b6f313fe77dd1501c0c0b6a4adf9a Mon Sep 17 00:00:00 2001 From: KasimBaybikov Date: Mon, 10 Apr 2023 16:56:56 +0300 Subject: [PATCH] 3.6.0 --- CHANGELOG.md | 27 +- Makefile | 2 +- README.md | 2 +- README_EN.md | 2 +- .../cloudapi/account/data_source_account.go | 475 +++++++----------- internal/service/cloudapi/account/flattens.go | 137 +++++ .../cloudapi/account/resource_account.go | 290 ++++------- .../cloudapi/bservice/data_source_bservice.go | 65 +-- .../service/cloudapi/bservice/flattens.go | 66 +++ .../cloudapi/bservice/resource_bservice.go | 148 ++++-- .../bservice/resource_check_input_values.go | 42 ++ .../cloudapi/disks/data_source_disk_list.go | 112 ----- internal/service/cloudapi/disks/flattens.go | 166 ++++++ .../disks/resource_check_input_values.go | 77 +++ .../service/cloudapi/disks/resource_disk.go | 336 +++++++------ .../cloudapi/disks/resource_disk_snapshot.go | 21 +- .../service/cloudapi/disks/utility_disk.go | 10 +- .../cloudapi/image/data_source_image.go | 53 -- internal/service/cloudapi/image/flattens.go | 56 +++ .../image/resource_check_input_values.go | 77 +++ .../service/cloudapi/image/resource_image.go | 119 +++-- internal/service/cloudapi/k8s/api.go | 2 + .../cloudapi/k8s/data_source_k8s_wg.go | 38 +- internal/service/cloudapi/k8s/flattens.go | 8 +- .../k8s/resource_check_input_values.go | 143 ++++++ internal/service/cloudapi/k8s/resource_k8s.go | 163 +++++- .../service/cloudapi/k8s/resource_k8s_wg.go | 69 +-- internal/service/cloudapi/k8s/utility_k8s.go | 12 +- .../service/cloudapi/k8s/utility_k8s_wg.go | 83 ++- .../cloudapi/kvmvm/resource_compute.go | 33 +- .../lb/resource_check_input_values.go | 139 +++++ internal/service/cloudapi/lb/resource_lb.go | 153 +++++- .../cloudapi/lb/resource_lb_backend.go | 26 +- .../cloudapi/lb/resource_lb_backend_server.go | 26 +- .../cloudapi/lb/resource_lb_frontend.go | 11 +- .../cloudapi/lb/resource_lb_frontend_bind.go | 26 +- .../rg/resource_check_input_values.go | 2 + internal/service/cloudapi/rg/resource_rg.go | 130 ++--- .../vins/resource_check_input_values.go | 146 ++++++ .../service/cloudapi/vins/resource_vins.go | 220 +++++++- .../cloudbroker/k8s/resource_k8s_wg.go | 31 +- internal/status/status.go | 58 +++ 42 files changed, 2586 insertions(+), 1216 deletions(-) create mode 100644 internal/service/cloudapi/account/flattens.go create mode 100644 internal/service/cloudapi/bservice/flattens.go create mode 100644 internal/service/cloudapi/bservice/resource_check_input_values.go create mode 100644 internal/service/cloudapi/disks/flattens.go create mode 100644 internal/service/cloudapi/disks/resource_check_input_values.go create mode 100644 internal/service/cloudapi/image/flattens.go create mode 100644 internal/service/cloudapi/image/resource_check_input_values.go create mode 100644 internal/service/cloudapi/k8s/resource_check_input_values.go create mode 100644 internal/service/cloudapi/lb/resource_check_input_values.go create mode 100644 internal/service/cloudapi/vins/resource_check_input_values.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 050fe3e..addebc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,23 @@ -## Version 3.5.2 +## Version 3.6.0 ### Features -- Add new datasource decort_kvmvm_snapshot_usage -- Add the ability to change the size in the 'disks' block in the decort_kvmvm resource. Now when you change 'size' field in the block, the disk size on the platform will also be changed +- Added validation for required fields in the following resources: + - disks + - lb + - image + - bservice + - vins + - k8s + - k8s_wg + - lb_frontend + - lb_frontend_bind + - lb_backed + - lb_backend_server +- Added status handlers in create/update functions (where present) -## Bug Fix -- rule "release" in Makefile don't create the necessary archives -- field "register_computes" in resource decort_resgroup is not used when creating the resource -- removed unused optional fields in datasources +### Bug Fixes +- Fixed state inconsistency in the following resources/data sources: + - data_source_account + - resource_k8s + - resource_k8s_wg + - resource_account diff --git a/Makefile b/Makefile index 78c8b09..391049f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ZIPDIR = ./zip BINARY=${NAME}.exe WORKPATH= ./examples/terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAMESPACE}/${VERSION}/${OS_ARCH} MAINPATH = ./cmd/decort/ -VERSION=3.5.2 +VERSION=3.6.0 #OS_ARCH=darwin_amd64 OS_ARCH=windows_amd64 #OS_ARCH=linux_amd64 diff --git a/README.md b/README.md index 1d99eb7..b184e86 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ terraform init 3. Склонируйте репозиторий с провайдером, выполнив команду: ```bash -git clone https://repository.basistech.ru/BASIS/terraform-provider-decort.git +git clone https://repository.basistech.ru/BASIS/terraform-provider-decort ``` 4. Перейдите в скачанную папку с провайдером и выполните команду diff --git a/README_EN.md b/README_EN.md index 04531ad..f6c342a 100644 --- a/README_EN.md +++ b/README_EN.md @@ -82,7 +82,7 @@ The Provider will automatically install on your computer from the terrafrom regi 3. Clone provider's repo: ```bash -git clone https://repository.basistech.ru/BASIS/terraform-provider-decort.git +git clone https://github.com/rudecs/terraform-provider-decort.git ``` 4. Change directory to clone provider's and execute next command diff --git a/internal/service/cloudapi/account/data_source_account.go b/internal/service/cloudapi/account/data_source_account.go index 3da366b..e796816 100644 --- a/internal/service/cloudapi/account/data_source_account.go +++ b/internal/service/cloudapi/account/data_source_account.go @@ -34,12 +34,11 @@ package account import ( "context" + "strconv" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" - "repository.basistech.ru/BASIS/terraform-provider-decort/internal/flattens" ) func dataSourceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -48,128 +47,213 @@ func dataSourceAccountRead(ctx context.Context, d *schema.ResourceData, m interf return diag.FromErr(err) } - id := uuid.New() - d.SetId(id.String()) - d.Set("dc_location", acc.DCLocation) - d.Set("resources", flattenAccResources(acc.Resources)) - d.Set("ckey", acc.CKey) - d.Set("meta", flattens.FlattenMeta(acc.Meta)) - d.Set("acl", flattenAccAcl(acc.Acl)) - d.Set("company", acc.Company) - d.Set("companyurl", acc.CompanyUrl) - d.Set("created_by", acc.CreatedBy) - d.Set("created_time", acc.CreatedTime) - d.Set("deactivation_time", acc.DeactiovationTime) - d.Set("deleted_by", acc.DeletedBy) - d.Set("deleted_time", acc.DeletedTime) - d.Set("displayname", acc.DisplayName) - d.Set("guid", acc.GUID) - d.Set("account_id", acc.ID) - d.Set("account_name", acc.Name) - d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits)) - d.Set("send_access_emails", acc.SendAccessEmails) - d.Set("service_account", acc.ServiceAccount) - d.Set("status", acc.Status) - d.Set("updated_time", acc.UpdatedTime) - d.Set("version", acc.Version) - d.Set("vins", acc.Vins) - d.Set("vinses", acc.Vinses) - d.Set("computes", flattenAccComputes(acc.Computes)) - d.Set("machines", flattenAccMachines(acc.Machines)) + flattenAccount(d, *acc) + d.SetId(strconv.Itoa(acc.ID)) + return nil } -func flattenAccComputes(acs Computes) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "started": acs.Started, - "stopped": acs.Stopped, +func aclSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "can_be_deleted": { + Type: schema.TypeBool, + Computed: true, + }, + "explicit": { + Type: schema.TypeBool, + Computed: true, + }, + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "right": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "user_group_id": { + Type: schema.TypeString, + Computed: true, + }, } - res = append(res, temp) - return res -} -func flattenAccMachines(ams Machines) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "running": ams.Running, - "halted": ams.Halted, - } - res = append(res, temp) return res } -func flattenAccAcl(acls []AccountAclRecord) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for _, acls := range acls { - temp := map[string]interface{}{ - "can_be_deleted": acls.CanBeDeleted, - "explicit": acls.IsExplicit, - "guid": acls.Guid, - "right": acls.Rights, - "status": acls.Status, - "type": acls.Type, - "user_group_id": acls.UgroupID, - } - res = append(res, temp) +func resourceLimitsSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "cu_c": { + Type: schema.TypeFloat, + Computed: true, + }, + "cu_d": { + Type: schema.TypeFloat, + Computed: true, + }, + "cu_i": { + Type: schema.TypeFloat, + Computed: true, + }, + "cu_m": { + Type: schema.TypeFloat, + Computed: true, + }, + "cu_np": { + Type: schema.TypeFloat, + Computed: true, + }, + "gpu_units": { + Type: schema.TypeFloat, + Computed: true, + }, } + return res } -func flattenRgResourceLimits(rl ResourceLimits) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "cu_c": rl.CUC, - "cu_d": rl.CUD, - "cu_i": rl.CUI, - "cu_m": rl.CUM, - "cu_np": rl.CUNP, - "gpu_units": rl.GpuUnits, +func sepsSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "sep_id": { + Type: schema.TypeString, + Computed: true, + }, + "data_name": { + Type: schema.TypeString, + Computed: true, + }, + "disk_size": { + Type: schema.TypeFloat, + Computed: true, + }, + "disk_size_max": { + Type: schema.TypeInt, + Computed: true, + }, } - res = append(res, temp) return res - } -func flattenAccResources(r Resources) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "current": flattenAccResource(r.Current), - "reserved": flattenAccResource(r.Reserved), +func resourcesSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "current": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu": { + Type: schema.TypeInt, + Computed: true, + }, + "disksize": { + Type: schema.TypeFloat, + Computed: true, + }, + "extips": { + Type: schema.TypeInt, + Computed: true, + }, + "exttraffic": { + Type: schema.TypeInt, + Computed: true, + }, + "gpu": { + Type: schema.TypeInt, + Computed: true, + }, + "ram": { + Type: schema.TypeInt, + Computed: true, + }, + "seps": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: sepsSchemaMake(), + }, + }, + }, + }, + }, + "reserved": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu": { + Type: schema.TypeInt, + Computed: true, + }, + "disksize": { + Type: schema.TypeFloat, + Computed: true, + }, + "extips": { + Type: schema.TypeInt, + Computed: true, + }, + "exttraffic": { + Type: schema.TypeInt, + Computed: true, + }, + "gpu": { + Type: schema.TypeInt, + Computed: true, + }, + "ram": { + Type: schema.TypeInt, + Computed: true, + }, + "seps": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: sepsSchemaMake(), + }, + }, + }, + }, + }, } - res = append(res, temp) + return res } -func flattenAccountSeps(seps map[string]map[string]ResourceSep) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for sepKey, sepVal := range seps { - for dataKey, dataVal := range sepVal { - temp := map[string]interface{}{ - "sep_id": sepKey, - "data_name": dataKey, - "disk_size": dataVal.DiskSize, - "disk_size_max": dataVal.DiskSizeMax, - } - res = append(res, temp) - } +func computesSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "started": { + Type: schema.TypeInt, + Computed: true, + }, + "stopped": { + Type: schema.TypeInt, + Computed: true, + }, } + return res } -func flattenAccResource(r Resource) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "cpu": r.CPU, - "disksize": r.Disksize, - "extips": r.Extips, - "exttraffic": r.Exttraffic, - "gpu": r.GPU, - "ram": r.RAM, - "seps": flattenAccountSeps(r.SEPs), +func machinesSchemaMake() map[string]*schema.Schema { + res := map[string]*schema.Schema{ + "halted": { + Type: schema.TypeInt, + Computed: true, + }, + "running": { + Type: schema.TypeInt, + Computed: true, + }, } - res = append(res, temp) + return res } @@ -188,120 +272,7 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "current": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cpu": { - Type: schema.TypeInt, - Computed: true, - }, - "disksize": { - Type: schema.TypeFloat, - Computed: true, - }, - "extips": { - Type: schema.TypeInt, - Computed: true, - }, - "exttraffic": { - Type: schema.TypeInt, - Computed: true, - }, - "gpu": { - Type: schema.TypeInt, - Computed: true, - }, - "ram": { - Type: schema.TypeInt, - Computed: true, - }, - "seps": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sep_id": { - Type: schema.TypeString, - Computed: true, - }, - "data_name": { - Type: schema.TypeString, - Computed: true, - }, - "disk_size": { - Type: schema.TypeFloat, - Computed: true, - }, - "disk_size_max": { - Type: schema.TypeInt, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - "reserved": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cpu": { - Type: schema.TypeInt, - Computed: true, - }, - "disksize": { - Type: schema.TypeFloat, - Computed: true, - }, - "extips": { - Type: schema.TypeInt, - Computed: true, - }, - "exttraffic": { - Type: schema.TypeInt, - Computed: true, - }, - "gpu": { - Type: schema.TypeInt, - Computed: true, - }, - "ram": { - Type: schema.TypeInt, - Computed: true, - }, - "seps": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sep_id": { - Type: schema.TypeString, - Computed: true, - }, - "data_name": { - Type: schema.TypeString, - Computed: true, - }, - "disk_size": { - Type: schema.TypeFloat, - Computed: true, - }, - "disk_size_max": { - Type: schema.TypeInt, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - }, + Schema: resourcesSchemaMake(), }, }, "ckey": { @@ -319,36 +290,7 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "can_be_deleted": { - Type: schema.TypeBool, - Computed: true, - }, - "explicit": { - Type: schema.TypeBool, - Computed: true, - }, - "guid": { - Type: schema.TypeString, - Computed: true, - }, - "right": { - Type: schema.TypeString, - Computed: true, - }, - "status": { - Type: schema.TypeString, - Computed: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "user_group_id": { - Type: schema.TypeString, - Computed: true, - }, - }, + Schema: aclSchemaMake(), }, }, "company": { @@ -395,32 +337,7 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cu_c": { - Type: schema.TypeFloat, - Computed: true, - }, - "cu_d": { - Type: schema.TypeFloat, - Computed: true, - }, - "cu_i": { - Type: schema.TypeFloat, - Computed: true, - }, - "cu_m": { - Type: schema.TypeFloat, - Computed: true, - }, - "cu_np": { - Type: schema.TypeFloat, - Computed: true, - }, - "gpu_units": { - Type: schema.TypeFloat, - Computed: true, - }, - }, + Schema: resourceLimitsSchemaMake(), }, }, "send_access_emails": { @@ -454,32 +371,14 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "started": { - Type: schema.TypeInt, - Computed: true, - }, - "stopped": { - Type: schema.TypeInt, - Computed: true, - }, - }, + Schema: computesSchemaMake(), }, }, "machines": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "halted": { - Type: schema.TypeInt, - Computed: true, - }, - "running": { - Type: schema.TypeInt, - Computed: true, - }, - }, + Schema: machinesSchemaMake(), }, }, "vinses": { diff --git a/internal/service/cloudapi/account/flattens.go b/internal/service/cloudapi/account/flattens.go new file mode 100644 index 0000000..ee920c0 --- /dev/null +++ b/internal/service/cloudapi/account/flattens.go @@ -0,0 +1,137 @@ +package account + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/flattens" +) + +func flattenAccount(d *schema.ResourceData, acc AccountWithResources) error { + d.Set("dc_location", acc.DCLocation) + d.Set("resources", flattenAccResources(acc.Resources)) + d.Set("ckey", acc.CKey) + d.Set("meta", flattens.FlattenMeta(acc.Meta)) + d.Set("acl", flattenAccAcl(acc.Acl)) + d.Set("company", acc.Company) + d.Set("companyurl", acc.CompanyUrl) + d.Set("created_by", acc.CreatedBy) + d.Set("created_time", acc.CreatedTime) + d.Set("deactivation_time", acc.DeactiovationTime) + d.Set("deleted_by", acc.DeletedBy) + d.Set("deleted_time", acc.DeletedTime) + d.Set("displayname", acc.DisplayName) + d.Set("guid", acc.GUID) + d.Set("account_id", acc.ID) + d.Set("account_name", acc.Name) + d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits)) + d.Set("send_access_emails", acc.SendAccessEmails) + d.Set("service_account", acc.ServiceAccount) + d.Set("status", acc.Status) + d.Set("updated_time", acc.UpdatedTime) + d.Set("version", acc.Version) + d.Set("vins", acc.Vins) + d.Set("vinses", acc.Vinses) + d.Set("computes", flattenAccComputes(acc.Computes)) + d.Set("machines", flattenAccMachines(acc.Machines)) + + if username, ok := d.GetOk("username"); ok { + d.Set("username", username) + } else { + d.Set("username", acc.Acl[0].UgroupID) + } + + return nil +} + +func flattenAccComputes(acs Computes) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "started": acs.Started, + "stopped": acs.Stopped, + } + res = append(res, temp) + return res +} + +func flattenAccMachines(ams Machines) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "running": ams.Running, + "halted": ams.Halted, + } + res = append(res, temp) + return res +} + +func flattenAccAcl(acls []AccountAclRecord) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, acls := range acls { + temp := map[string]interface{}{ + "can_be_deleted": acls.CanBeDeleted, + "explicit": acls.IsExplicit, + "guid": acls.Guid, + "right": acls.Rights, + "status": acls.Status, + "type": acls.Type, + "user_group_id": acls.UgroupID, + } + res = append(res, temp) + } + return res +} + +func flattenRgResourceLimits(rl ResourceLimits) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "cu_c": rl.CUC, + "cu_d": rl.CUD, + "cu_i": rl.CUI, + "cu_m": rl.CUM, + "cu_np": rl.CUNP, + "gpu_units": rl.GpuUnits, + } + res = append(res, temp) + + return res + +} + +func flattenAccResources(r Resources) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "current": flattenAccResource(r.Current), + "reserved": flattenAccResource(r.Reserved), + } + res = append(res, temp) + return res +} + +func flattenAccountSeps(seps map[string]map[string]ResourceSep) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for sepKey, sepVal := range seps { + for dataKey, dataVal := range sepVal { + temp := map[string]interface{}{ + "sep_id": sepKey, + "data_name": dataKey, + "disk_size": dataVal.DiskSize, + "disk_size_max": dataVal.DiskSizeMax, + } + res = append(res, temp) + } + } + return res +} + +func flattenAccResource(r Resource) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "cpu": r.CPU, + "disksize": r.Disksize, + "extips": r.Extips, + "exttraffic": r.Exttraffic, + "gpu": r.GPU, + "ram": r.RAM, + "seps": flattenAccountSeps(r.SEPs), + } + res = append(res, temp) + return res +} diff --git a/internal/service/cloudapi/account/resource_account.go b/internal/service/cloudapi/account/resource_account.go index b3306ad..dd5fddb 100644 --- a/internal/service/cloudapi/account/resource_account.go +++ b/internal/service/cloudapi/account/resource_account.go @@ -43,7 +43,7 @@ import ( log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" - "repository.basistech.ru/BASIS/terraform-provider-decort/internal/flattens" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" ) func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -122,13 +122,10 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interf return diag.FromErr(err) } - d.SetId(accountId) - d.Set("account_id", accountId) + accIdParsed, _ := strconv.Atoi(accountId) - diagnostics := resourceAccountRead(ctx, d, m) - if diagnostics != nil { - return diagnostics - } + d.SetId(accountId) + d.Set("account_id", accIdParsed) urlValues = &url.Values{} @@ -167,44 +164,52 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interf } } - return nil + return resourceAccountRead(ctx, d, m) } func resourceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Debugf("resourceAccountRead") + log.Debugf("resourceAccountRead: called for account with ID: %v", d.Id()) + + c := m.(*controller.ControllerCfg) acc, err := utilityAccountCheckPresence(ctx, d, m) - if acc == nil { + if err != nil { d.SetId("") return diag.FromErr(err) } - d.Set("dc_location", acc.DCLocation) - d.Set("resources", flattenAccResources(acc.Resources)) - d.Set("ckey", acc.CKey) - d.Set("meta", flattens.FlattenMeta(acc.Meta)) - d.Set("acl", flattenAccAcl(acc.Acl)) - d.Set("company", acc.Company) - d.Set("companyurl", acc.CompanyUrl) - d.Set("created_by", acc.CreatedBy) - d.Set("created_time", acc.CreatedTime) - d.Set("deactivation_time", acc.DeactiovationTime) - d.Set("deleted_by", acc.DeletedBy) - d.Set("deleted_time", acc.DeletedTime) - d.Set("displayname", acc.DisplayName) - d.Set("guid", acc.GUID) - d.Set("account_id", acc.ID) - d.Set("account_name", acc.Name) - d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits)) - d.Set("send_access_emails", acc.SendAccessEmails) - d.Set("service_account", acc.ServiceAccount) - d.Set("status", acc.Status) - d.Set("updated_time", acc.UpdatedTime) - d.Set("version", acc.Version) - d.Set("vins", acc.Vins) - d.Set("vinses", acc.Vinses) - d.Set("computes", flattenAccComputes(acc.Computes)) - d.Set("machines", flattenAccMachines(acc.Machines)) + hasChanged := false + + switch acc.Status { + case status.Destroyed: + d.SetId("") + return resourceAccountCreate(ctx, d, m) + case status.Destroying: + return diag.Errorf("The account is in progress with status: %s", acc.Status) + case status.Deleted: + urlValues := &url.Values{} + urlValues.Add("accountId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", accountRestoreAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Disabled: + log.Debugf("The account is in status: %s, troubles may occur with update. Please, enable account first.", acc.Status) + case status.Confirmed: + } + + if hasChanged { + acc, err = utilityAccountCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + + flattenAccount(d, *acc) return nil } @@ -234,11 +239,49 @@ func resourceAccountDelete(ctx context.Context, d *schema.ResourceData, m interf return nil } -func resourceAccountEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceAccountEdit") c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + + acc, err := utilityAccountCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + hasChanged := false + + switch acc.Status { + case status.Destroyed: + d.SetId("") + return resourceAccountCreate(ctx, d, m) + case status.Destroying: + return diag.Errorf("The account is in progress with status: %s", acc.Status) + case status.Deleted: + urlVal := &url.Values{} + urlVal.Add("accountId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", accountRestoreAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Disabled: + log.Debugf("The account is in status: %s, troubles may occur with update. Please, enable account first.", acc.Status) + case status.Confirmed: + } + + if hasChanged { + acc, err = utilityAccountCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + if d.HasChange("enable") { api := accountDisableAPI enable := d.Get("enable").(bool) @@ -424,7 +467,7 @@ func resourceAccountEdit(ctx context.Context, d *schema.ResourceData, m interfac } - return nil + return resourceAccountRead(ctx, d, m) } func isContainsUser(els []interface{}, el interface{}) bool { @@ -553,7 +596,6 @@ func resourceAccountSchemaMake() map[string]*schema.Schema { }, "account_id": { Type: schema.TypeInt, - Optional: true, Computed: true, }, "dc_location": { @@ -564,120 +606,7 @@ func resourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "current": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cpu": { - Type: schema.TypeInt, - Computed: true, - }, - "disksize": { - Type: schema.TypeFloat, - Computed: true, - }, - "extips": { - Type: schema.TypeInt, - Computed: true, - }, - "exttraffic": { - Type: schema.TypeInt, - Computed: true, - }, - "gpu": { - Type: schema.TypeInt, - Computed: true, - }, - "ram": { - Type: schema.TypeInt, - Computed: true, - }, - "seps": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sep_id": { - Type: schema.TypeString, - Computed: true, - }, - "data_name": { - Type: schema.TypeString, - Computed: true, - }, - "disk_size": { - Type: schema.TypeFloat, - Computed: true, - }, - "disk_size_max": { - Type: schema.TypeInt, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - "reserved": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cpu": { - Type: schema.TypeInt, - Computed: true, - }, - "disksize": { - Type: schema.TypeFloat, - Computed: true, - }, - "extips": { - Type: schema.TypeInt, - Computed: true, - }, - "exttraffic": { - Type: schema.TypeInt, - Computed: true, - }, - "gpu": { - Type: schema.TypeInt, - Computed: true, - }, - "ram": { - Type: schema.TypeInt, - Computed: true, - }, - "seps": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sep_id": { - Type: schema.TypeString, - Computed: true, - }, - "data_name": { - Type: schema.TypeString, - Computed: true, - }, - "disk_size": { - Type: schema.TypeFloat, - Computed: true, - }, - "disk_size_max": { - Type: schema.TypeInt, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - }, + Schema: resourcesSchemaMake(), }, }, "ckey": { @@ -695,36 +624,7 @@ func resourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "can_be_deleted": { - Type: schema.TypeBool, - Computed: true, - }, - "explicit": { - Type: schema.TypeBool, - Computed: true, - }, - "guid": { - Type: schema.TypeString, - Computed: true, - }, - "right": { - Type: schema.TypeString, - Computed: true, - }, - "status": { - Type: schema.TypeString, - Computed: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "user_group_id": { - Type: schema.TypeString, - Computed: true, - }, - }, + Schema: aclSchemaMake(), }, }, "company": { @@ -790,32 +690,14 @@ func resourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "started": { - Type: schema.TypeInt, - Computed: true, - }, - "stopped": { - Type: schema.TypeInt, - Computed: true, - }, - }, + Schema: computesSchemaMake(), }, }, "machines": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "halted": { - Type: schema.TypeInt, - Computed: true, - }, - "running": { - Type: schema.TypeInt, - Computed: true, - }, - }, + Schema: machinesSchemaMake(), }, }, "vinses": { @@ -831,7 +713,7 @@ func ResourceAccount() *schema.Resource { CreateContext: resourceAccountCreate, ReadContext: resourceAccountRead, - UpdateContext: resourceAccountEdit, + UpdateContext: resourceAccountUpdate, DeleteContext: resourceAccountDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/bservice/data_source_bservice.go b/internal/service/cloudapi/bservice/data_source_bservice.go index 281d0f5..df684f4 100644 --- a/internal/service/cloudapi/bservice/data_source_bservice.go +++ b/internal/service/cloudapi/bservice/data_source_bservice.go @@ -34,8 +34,8 @@ package bservice import ( "context" + "strconv" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" @@ -47,68 +47,11 @@ func dataSourceBasicServiceRead(ctx context.Context, d *schema.ResourceData, m i return diag.FromErr(err) } - id := uuid.New() - d.SetId(id.String()) - d.Set("account_id", bs.AccountId) - d.Set("account_name", bs.AccountName) - d.Set("base_domain", bs.BaseDomain) - d.Set("computes", flattenBasicServiceComputes(bs.Computes)) - d.Set("cpu_total", bs.CPUTotal) - d.Set("created_by", bs.CreatedBy) - d.Set("created_time", bs.CreatedTime) - d.Set("deleted_by", bs.DeletedBy) - d.Set("deleted_time", bs.DeletedTime) - d.Set("disk_total", bs.DiskTotal) - d.Set("gid", bs.GID) - d.Set("groups", bs.Groups) - d.Set("groups_name", bs.GroupsName) - d.Set("guid", bs.GUID) - d.Set("milestones", bs.Milestones) - d.Set("service_name", bs.Name) - d.Set("parent_srv_id", bs.ParentSrvId) - d.Set("parent_srv_type", bs.ParentSrvType) - d.Set("ram_total", bs.RamTotal) - d.Set("rg_id", bs.RGID) - d.Set("rg_name", bs.RGName) - d.Set("snapshots", flattenBasicServiceSnapshots(bs.Snapshots)) - d.Set("ssh_key", bs.SSHKey) - d.Set("ssh_user", bs.SSHUser) - d.Set("status", bs.Status) - d.Set("tech_status", bs.TechStatus) - d.Set("updated_by", bs.UpdatedBy) - d.Set("updated_time", bs.UpdatedTime) - d.Set("user_managed", bs.UserManaged) - return nil -} + d.SetId(strconv.Itoa(bs.ID)) -func flattenBasicServiceComputes(bscs BasicServiceComputes) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for _, bsc := range bscs { - temp := map[string]interface{}{ - "compgroup_id": bsc.CompGroupId, - "compgroup_name": bsc.CompGroupName, - "compgroup_role": bsc.CompGroupRole, - "id": bsc.ID, - "name": bsc.Name, - } - res = append(res, temp) - } + flattenService(d, bs) - return res -} - -func flattenBasicServiceSnapshots(bsrvss BasicServiceSnapshots) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for _, bsrvs := range bsrvss { - temp := map[string]interface{}{ - "guid": bsrvs.GUID, - "label": bsrvs.Label, - "timestamp": bsrvs.Timestamp, - "valid": bsrvs.Valid, - } - res = append(res, temp) - } - return res + return nil } func dataSourceBasicServiceSchemaMake() map[string]*schema.Schema { diff --git a/internal/service/cloudapi/bservice/flattens.go b/internal/service/cloudapi/bservice/flattens.go new file mode 100644 index 0000000..962be7d --- /dev/null +++ b/internal/service/cloudapi/bservice/flattens.go @@ -0,0 +1,66 @@ +package bservice + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func flattenService(d *schema.ResourceData, bs *BasicServiceExtend) { + d.Set("account_id", bs.AccountId) + d.Set("account_name", bs.AccountName) + d.Set("base_domain", bs.BaseDomain) + d.Set("computes", flattenBasicServiceComputes(bs.Computes)) + d.Set("cpu_total", bs.CPUTotal) + d.Set("created_by", bs.CreatedBy) + d.Set("created_time", bs.CreatedTime) + d.Set("deleted_by", bs.DeletedBy) + d.Set("deleted_time", bs.DeletedTime) + d.Set("disk_total", bs.DiskTotal) + d.Set("gid", bs.GID) + d.Set("groups", bs.Groups) + d.Set("groups_name", bs.GroupsName) + d.Set("guid", bs.GUID) + d.Set("milestones", bs.Milestones) + d.Set("service_name", bs.Name) + d.Set("service_id", bs.ID) + d.Set("parent_srv_id", bs.ParentSrvId) + d.Set("parent_srv_type", bs.ParentSrvType) + d.Set("ram_total", bs.RamTotal) + d.Set("rg_id", bs.RGID) + d.Set("rg_name", bs.RGName) + d.Set("snapshots", flattenBasicServiceSnapshots(bs.Snapshots)) + d.Set("ssh_key", bs.SSHKey) + d.Set("ssh_user", bs.SSHUser) + d.Set("status", bs.Status) + d.Set("tech_status", bs.TechStatus) + d.Set("updated_by", bs.UpdatedBy) + d.Set("updated_time", bs.UpdatedTime) + d.Set("user_managed", bs.UserManaged) +} + +func flattenBasicServiceComputes(bscs BasicServiceComputes) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, bsc := range bscs { + temp := map[string]interface{}{ + "compgroup_id": bsc.CompGroupId, + "compgroup_name": bsc.CompGroupName, + "compgroup_role": bsc.CompGroupRole, + "id": bsc.ID, + "name": bsc.Name, + } + res = append(res, temp) + } + + return res +} + +func flattenBasicServiceSnapshots(bsrvss BasicServiceSnapshots) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, bsrvs := range bsrvss { + temp := map[string]interface{}{ + "guid": bsrvs.GUID, + "label": bsrvs.Label, + "timestamp": bsrvs.Timestamp, + "valid": bsrvs.Valid, + } + res = append(res, temp) + } + return res +} diff --git a/internal/service/cloudapi/bservice/resource_bservice.go b/internal/service/cloudapi/bservice/resource_bservice.go index de2e1a4..97f3795 100644 --- a/internal/service/cloudapi/bservice/resource_bservice.go +++ b/internal/service/cloudapi/bservice/resource_bservice.go @@ -42,6 +42,7 @@ import ( log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" ) func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -50,6 +51,15 @@ func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m i c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceBasicServiceCreate: can't create basic service because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + urlValues.Add("name", d.Get("service_name").(string)) urlValues.Add("rgId", strconv.Itoa(d.Get("rg_id").(int))) @@ -65,8 +75,10 @@ func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m i return diag.FromErr(err) } + serviceIdParsed, _ := strconv.Atoi(serviceId) + d.SetId(serviceId) - d.Set("service_id", serviceId) + d.Set("service_id", serviceIdParsed) diagnostics := resourceBasicServiceRead(ctx, d, m) if diagnostics != nil { @@ -79,42 +91,55 @@ func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m i func resourceBasicServiceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceBasicServiceRead") + c := m.(*controller.ControllerCfg) + bs, err := utilityBasicServiceCheckPresence(ctx, d, m) - if bs == nil { + if err != nil { d.SetId("") return diag.FromErr(err) } - d.Set("account_id", bs.AccountId) - d.Set("account_name", bs.AccountName) - d.Set("base_domain", bs.BaseDomain) - d.Set("computes", flattenBasicServiceComputes(bs.Computes)) - d.Set("cpu_total", bs.CPUTotal) - d.Set("created_by", bs.CreatedBy) - d.Set("created_time", bs.CreatedTime) - d.Set("deleted_by", bs.DeletedBy) - d.Set("deleted_time", bs.DeletedTime) - d.Set("disk_total", bs.DiskTotal) - d.Set("gid", bs.GID) - d.Set("groups", bs.Groups) - d.Set("groups_name", bs.GroupsName) - d.Set("guid", bs.GUID) - d.Set("milestones", bs.Milestones) - d.Set("service_name", bs.Name) - d.Set("service_id", bs.ID) - d.Set("parent_srv_id", bs.ParentSrvId) - d.Set("parent_srv_type", bs.ParentSrvType) - d.Set("ram_total", bs.RamTotal) - d.Set("rg_id", bs.RGID) - d.Set("rg_name", bs.RGName) - d.Set("snapshots", flattenBasicServiceSnapshots(bs.Snapshots)) - d.Set("ssh_key", bs.SSHKey) - d.Set("ssh_user", bs.SSHUser) - d.Set("status", bs.Status) - d.Set("tech_status", bs.TechStatus) - d.Set("updated_by", bs.UpdatedBy) - d.Set("updated_time", bs.UpdatedTime) - d.Set("user_managed", bs.UserManaged) + hasChanged := false + + switch bs.Status { + case status.Modeled: + return diag.Errorf("The basic service is in status: %s, please, contact support for more information", bs.Status) + case status.Created: + case status.Enabled: + case status.Enabling: + case status.Disabled: + log.Debugf("The basic service is in status: %s, troubles can occur with the update. Please, enable bservice first.", bs.Status) + case status.Disabling: + log.Debugf("The basic service is in status: %s, troubles can occur with the update.", bs.Status) + case status.Deleted: + urlVal := &url.Values{} + urlVal.Add("serviceId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", bserviceRestoreAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Deleting: + case status.Destroyed: + d.SetId("") + return resourceBasicServiceCreate(ctx, d, m) + case status.Destroying: + return diag.Errorf("The basic service is in progress with status: %s", bs.Status) + case status.Restoring: + case status.Reconfiguring: + } + + if hasChanged { + bs, err = utilityBasicServiceCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + + flattenService(d, bs) return nil } @@ -144,10 +169,65 @@ func resourceBasicServiceDelete(ctx context.Context, d *schema.ResourceData, m i return nil } -func resourceBasicServiceEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceBasicServiceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceBasicServiceEdit") c := m.(*controller.ControllerCfg) + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceBasicServiceUpdate: can't create basic service because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + + bs, err := utilityBasicServiceCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + hasChanged := false + + switch bs.Status { + case status.Modeled: + return diag.Errorf("The basic service is in status: %s, please, contact support for more information", bs.Status) + case status.Created: + case status.Enabled: + case status.Enabling: + case status.Disabled: + log.Debugf("The basic service is in status: %s, troubles can occur with the update. Please, enable bservice first.", bs.Status) + case status.Disabling: + log.Debugf("The basic service is in status: %s, troubles can occur with the update.", bs.Status) + case status.Deleted: + urlVal := &url.Values{} + urlVal.Add("serviceId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", bserviceRestoreAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Deleting: + case status.Destroyed: + d.SetId("") + return resourceBasicServiceCreate(ctx, d, m) + case status.Destroying: + return diag.Errorf("The basic service is in progress with status: %s", bs.Status) + case status.Restoring: + case status.Reconfiguring: + } + + if hasChanged { + bs, err = utilityBasicServiceCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + urlValues := &url.Values{} if d.HasChange("enable") { api := bserviceDisableAPI @@ -508,7 +588,7 @@ func ResourceBasicService() *schema.Resource { CreateContext: resourceBasicServiceCreate, ReadContext: resourceBasicServiceRead, - UpdateContext: resourceBasicServiceEdit, + UpdateContext: resourceBasicServiceUpdate, DeleteContext: resourceBasicServiceDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/bservice/resource_check_input_values.go b/internal/service/cloudapi/bservice/resource_check_input_values.go new file mode 100644 index 0000000..836d278 --- /dev/null +++ b/internal/service/cloudapi/bservice/resource_check_input_values.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existRGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + rgList := []struct { + ID int `json:"id"` + }{} + + rgListAPI := "/restmachine/cloudapi/rg/list" + + rgListRaw, err := c.DecortAPICall(ctx, "POST", rgListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(rgListRaw), &rgList) + if err != nil { + return false, err + } + + haveRG := false + rgId := d.Get("rg_id").(int) + for _, rg := range rgList { + if rg.ID == rgId { + haveRG = true + break + } + } + + return haveRG, nil +} diff --git a/internal/service/cloudapi/disks/data_source_disk_list.go b/internal/service/cloudapi/disks/data_source_disk_list.go index 810f1fe..3bfee4f 100644 --- a/internal/service/cloudapi/disks/data_source_disk_list.go +++ b/internal/service/cloudapi/disks/data_source_disk_list.go @@ -34,7 +34,6 @@ package disks import ( "context" - "encoding/json" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -42,117 +41,6 @@ import ( "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" ) -func flattenDiskComputes(computes map[string]string) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for computeKey, computeVal := range computes { - temp := map[string]interface{}{ - "compute_id": computeKey, - "compute_name": computeVal, - } - res = append(res, temp) - } - return res -} - -func flattenIOTune(iot IOTune) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - temp := map[string]interface{}{ - "read_bytes_sec": iot.ReadBytesSec, - "read_bytes_sec_max": iot.ReadBytesSecMax, - "read_iops_sec": iot.ReadIopsSec, - "read_iops_sec_max": iot.ReadIopsSecMax, - "size_iops_sec": iot.SizeIopsSec, - "total_bytes_sec": iot.TotalBytesSec, - "total_bytes_sec_max": iot.TotalBytesSecMax, - "total_iops_sec": iot.TotalIopsSec, - "total_iops_sec_max": iot.TotalIopsSecMax, - "write_bytes_sec": iot.WriteBytesSec, - "write_bytes_sec_max": iot.WriteBytesSecMax, - "write_iops_sec": iot.WriteIopsSec, - "write_iops_sec_max": iot.WriteIopsSecMax, - } - - res = append(res, temp) - return res -} - -func flattenDiskList(dl DisksList) []map[string]interface{} { - res := make([]map[string]interface{}, 0) - for _, disk := range dl { - diskAcl, _ := json.Marshal(disk.Acl) - temp := map[string]interface{}{ - "account_id": disk.AccountID, - "account_name": disk.AccountName, - "acl": string(diskAcl), - "computes": flattenDiskComputes(disk.Computes), - "boot_partition": disk.BootPartition, - "created_time": disk.CreatedTime, - "deleted_time": disk.DeletedTime, - "desc": disk.Desc, - "destruction_time": disk.DestructionTime, - "devicename": disk.DeviceName, - "disk_path": disk.DiskPath, - "gid": disk.GridID, - "guid": disk.GUID, - "disk_id": disk.ID, - "image_id": disk.ImageID, - "images": disk.Images, - "iotune": flattenIOTune(disk.IOTune), - "iqn": disk.IQN, - "login": disk.Login, - "machine_id": disk.MachineId, - "machine_name": disk.MachineName, - "milestones": disk.Milestones, - "disk_name": disk.Name, - "order": disk.Order, - "params": disk.Params, - "parent_id": disk.ParentId, - "passwd": disk.Passwd, - "pci_slot": disk.PciSlot, - "pool": disk.Pool, - "present_to": disk.PresentTo, - "purge_attempts": disk.PurgeAttempts, - "purge_time": disk.PurgeTime, - "reality_device_number": disk.RealityDeviceNumber, - "reference_id": disk.ReferenceId, - "res_id": disk.ResID, - "res_name": disk.ResName, - "role": disk.Role, - "sep_id": disk.SepID, - "sep_type": disk.SepType, - "shareable": disk.Shareable, - "size_max": disk.SizeMax, - "size_used": disk.SizeUsed, - "snapshots": flattenDiskSnapshotList(disk.Snapshots), - "status": disk.Status, - "tech_status": disk.TechStatus, - "type": disk.Type, - "vmid": disk.VMID, - } - res = append(res, temp) - } - return res - -} - -func flattenDiskSnapshotList(sl SnapshotList) []interface{} { - res := make([]interface{}, 0) - for _, snapshot := range sl { - temp := map[string]interface{}{ - "guid": snapshot.Guid, - "label": snapshot.Label, - "res_id": snapshot.ResId, - "snap_set_guid": snapshot.SnapSetGuid, - "snap_set_time": snapshot.SnapSetTime, - "timestamp": snapshot.TimeStamp, - } - res = append(res, temp) - } - - return res - -} - func dataSourceDiskListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { diskList, err := utilityDiskListCheckPresence(ctx, d, m, disksListAPI) if err != nil { diff --git a/internal/service/cloudapi/disks/flattens.go b/internal/service/cloudapi/disks/flattens.go new file mode 100644 index 0000000..d8eb8f2 --- /dev/null +++ b/internal/service/cloudapi/disks/flattens.go @@ -0,0 +1,166 @@ +package disks + +import ( + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func flattenDisk(d *schema.ResourceData, disk Disk) { + diskAcl, _ := json.Marshal(disk.Acl) + + d.Set("account_id", disk.AccountID) + d.Set("account_name", disk.AccountName) + d.Set("acl", string(diskAcl)) + d.Set("boot_partition", disk.BootPartition) + d.Set("computes", flattenDiskComputes(disk.Computes)) + d.Set("created_time", disk.CreatedTime) + d.Set("deleted_time", disk.DeletedTime) + d.Set("desc", disk.Desc) + d.Set("destruction_time", disk.DestructionTime) + d.Set("devicename", disk.DeviceName) + d.Set("disk_path", disk.DiskPath) + d.Set("gid", disk.GridID) + d.Set("guid", disk.GUID) + d.Set("disk_id", disk.ID) + d.Set("image_id", disk.ImageID) + d.Set("images", disk.Images) + d.Set("iotune", flattenIOTune(disk.IOTune)) + d.Set("iqn", disk.IQN) + d.Set("login", disk.Login) + d.Set("milestones", disk.Milestones) + d.Set("disk_name", disk.Name) + d.Set("order", disk.Order) + d.Set("params", disk.Params) + d.Set("parent_id", disk.ParentId) + d.Set("passwd", disk.Passwd) + d.Set("pci_slot", disk.PciSlot) + d.Set("pool", disk.Pool) + d.Set("present_to", disk.PresentTo) + d.Set("purge_attempts", disk.PurgeAttempts) + d.Set("purge_time", disk.PurgeTime) + d.Set("reality_device_number", disk.RealityDeviceNumber) + d.Set("reference_id", disk.ReferenceId) + d.Set("res_id", disk.ResID) + d.Set("res_name", disk.ResName) + d.Set("role", disk.Role) + d.Set("sep_id", disk.SepID) + d.Set("sep_type", disk.SepType) + d.Set("size_max", disk.SizeMax) + d.Set("size_used", disk.SizeUsed) + d.Set("shareable", disk.Shareable) + d.Set("snapshots", flattenDiskSnapshotList(disk.Snapshots)) + d.Set("status", disk.Status) + d.Set("tech_status", disk.TechStatus) + d.Set("type", disk.Type) + d.Set("vmid", disk.VMID) +} + +func flattenDiskSnapshotList(sl SnapshotList) []interface{} { + res := make([]interface{}, 0) + for _, snapshot := range sl { + temp := map[string]interface{}{ + "guid": snapshot.Guid, + "label": snapshot.Label, + "res_id": snapshot.ResId, + "snap_set_guid": snapshot.SnapSetGuid, + "snap_set_time": snapshot.SnapSetTime, + "timestamp": snapshot.TimeStamp, + } + res = append(res, temp) + } + + return res +} + +func flattenDiskList(dl DisksList) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, disk := range dl { + diskAcl, _ := json.Marshal(disk.Acl) + temp := map[string]interface{}{ + "account_id": disk.AccountID, + "account_name": disk.AccountName, + "acl": string(diskAcl), + "computes": flattenDiskComputes(disk.Computes), + "boot_partition": disk.BootPartition, + "created_time": disk.CreatedTime, + "deleted_time": disk.DeletedTime, + "desc": disk.Desc, + "destruction_time": disk.DestructionTime, + "devicename": disk.DeviceName, + "disk_path": disk.DiskPath, + "gid": disk.GridID, + "guid": disk.GUID, + "disk_id": disk.ID, + "image_id": disk.ImageID, + "images": disk.Images, + "iotune": flattenIOTune(disk.IOTune), + "iqn": disk.IQN, + "login": disk.Login, + "machine_id": disk.MachineId, + "machine_name": disk.MachineName, + "milestones": disk.Milestones, + "disk_name": disk.Name, + "order": disk.Order, + "params": disk.Params, + "parent_id": disk.ParentId, + "passwd": disk.Passwd, + "pci_slot": disk.PciSlot, + "pool": disk.Pool, + "present_to": disk.PresentTo, + "purge_attempts": disk.PurgeAttempts, + "purge_time": disk.PurgeTime, + "reality_device_number": disk.RealityDeviceNumber, + "reference_id": disk.ReferenceId, + "res_id": disk.ResID, + "res_name": disk.ResName, + "role": disk.Role, + "sep_id": disk.SepID, + "sep_type": disk.SepType, + "shareable": disk.Shareable, + "size_max": disk.SizeMax, + "size_used": disk.SizeUsed, + "snapshots": flattenDiskSnapshotList(disk.Snapshots), + "status": disk.Status, + "tech_status": disk.TechStatus, + "type": disk.Type, + "vmid": disk.VMID, + } + res = append(res, temp) + } + return res +} + +func flattenIOTune(iot IOTune) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "read_bytes_sec": iot.ReadBytesSec, + "read_bytes_sec_max": iot.ReadBytesSecMax, + "read_iops_sec": iot.ReadIopsSec, + "read_iops_sec_max": iot.ReadIopsSecMax, + "size_iops_sec": iot.SizeIopsSec, + "total_bytes_sec": iot.TotalBytesSec, + "total_bytes_sec_max": iot.TotalBytesSecMax, + "total_iops_sec": iot.TotalIopsSec, + "total_iops_sec_max": iot.TotalIopsSecMax, + "write_bytes_sec": iot.WriteBytesSec, + "write_bytes_sec_max": iot.WriteBytesSecMax, + "write_iops_sec": iot.WriteIopsSec, + "write_iops_sec_max": iot.WriteIopsSecMax, + } + + res = append(res, temp) + return res +} + +func flattenDiskComputes(computes map[string]string) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for computeKey, computeVal := range computes { + temp := map[string]interface{}{ + "compute_id": computeKey, + "compute_name": computeVal, + } + res = append(res, temp) + } + return res +} diff --git a/internal/service/cloudapi/disks/resource_check_input_values.go b/internal/service/cloudapi/disks/resource_check_input_values.go new file mode 100644 index 0000000..c328b33 --- /dev/null +++ b/internal/service/cloudapi/disks/resource_check_input_values.go @@ -0,0 +1,77 @@ +package disks + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existAccountID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + accountList := []struct { + ID int `json:"id"` + }{} + + accountListAPI := "/restmachine/cloudapi/account/list" + + accountListRaw, err := c.DecortAPICall(ctx, "POST", accountListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(accountListRaw), &accountList) + if err != nil { + return false, err + } + + haveAccount := false + + myAccount := d.Get("account_id").(int) + for _, account := range accountList { + if account.ID == myAccount { + haveAccount = true + break + } + } + return haveAccount, nil +} + +func existGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + locationList := []struct { + GID int `json:"gid"` + }{} + + locationsListAPI := "/restmachine/cloudapi/locations/list" + + locationListRaw, err := c.DecortAPICall(ctx, "POST", locationsListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(locationListRaw), &locationList) + if err != nil { + return false, err + } + + haveGID := false + + gid := d.Get("gid").(int) + for _, location := range locationList { + if location.GID == gid { + haveGID = true + break + } + } + + return haveGID, nil +} diff --git a/internal/service/cloudapi/disks/resource_disk.go b/internal/service/cloudapi/disks/resource_disk.go index f5d1060..98938b9 100644 --- a/internal/service/cloudapi/disks/resource_disk.go +++ b/internal/service/cloudapi/disks/resource_disk.go @@ -34,17 +34,16 @@ package disks import ( "context" - "encoding/json" "fmt" "net/url" "strconv" "strings" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" - log "github.com/sirupsen/logrus" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -55,10 +54,26 @@ func resourceDiskCreate(ctx context.Context, d *schema.ResourceData, m interface c := m.(*controller.ControllerCfg) urlValues := &url.Values{} - urlValues.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int))) - urlValues.Add("gid", fmt.Sprintf("%d", d.Get("gid").(int))) + haveAccount, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + if !haveAccount { + return diag.Errorf("resourceDiskCreate: can't create Disk because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + if !haveGID { + return diag.Errorf("resourceDiskCreate: can't create Disk because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("gid", strconv.Itoa(d.Get("gid").(int))) urlValues.Add("name", d.Get("disk_name").(string)) - urlValues.Add("size", fmt.Sprintf("%d", d.Get("size_max").(int))) + urlValues.Add("size", strconv.Itoa(d.Get("size_max").(int))) if typeRaw, ok := d.GetOk("type"); ok { urlValues.Add("type", strings.ToUpper(typeRaw.(string))) } else { @@ -85,7 +100,7 @@ func resourceDiskCreate(ctx context.Context, d *schema.ResourceData, m interface urlValues = &url.Values{} - d.SetId(diskId) // update ID of the resource to tell Terraform that the disk resource exists + d.SetId(diskId) if iotuneRaw, ok := d.GetOk("iotune"); ok { iot := iotuneRaw.([]interface{})[0] @@ -136,19 +151,18 @@ func resourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{} warnings := dc.Warnings{} disk, err := utilityDiskCheckPresence(ctx, d, m) - if disk == nil { + if err != nil { d.SetId("") - if err != nil { - return diag.FromErr(err) - } - return nil + return diag.FromErr(err) } hasChangeState := false - if disk.Status == status.Destroyed || disk.Status == status.Purged { + + switch disk.Status { + case status.Destroyed, status.Purged: d.Set("disk_id", 0) return resourceDiskCreate(ctx, d, m) - } else if disk.Status == status.Deleted { + case status.Deleted: hasChangeState = true urlValues.Add("diskId", d.Id()) urlValues.Add("reason", d.Get("reason").(string)) @@ -157,79 +171,84 @@ func resourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{} if err != nil { warnings.Add(err) } + case status.Assigned: + case status.Modeled: + return diag.Errorf("The disk is in status: %s, please, contact support for more information", disk.Status) + case status.Creating: + case status.Created: + case status.Allocated: + case status.Unallocated: } + if hasChangeState { - urlValues = &url.Values{} disk, err = utilityDiskCheckPresence(ctx, d, m) - if disk == nil { + if err != nil { d.SetId("") - if err != nil { - return diag.FromErr(err) - } - return nil + return diag.FromErr(err) } } - diskAcl, _ := json.Marshal(disk.Acl) - - d.Set("account_id", disk.AccountID) - d.Set("account_name", disk.AccountName) - d.Set("acl", string(diskAcl)) - d.Set("boot_partition", disk.BootPartition) - d.Set("computes", flattenDiskComputes(disk.Computes)) - d.Set("created_time", disk.CreatedTime) - d.Set("deleted_time", disk.DeletedTime) - d.Set("desc", disk.Desc) - d.Set("destruction_time", disk.DestructionTime) - d.Set("devicename", disk.DeviceName) - d.Set("disk_path", disk.DiskPath) - d.Set("gid", disk.GridID) - d.Set("guid", disk.GUID) - d.Set("disk_id", disk.ID) - d.Set("image_id", disk.ImageID) - d.Set("images", disk.Images) - d.Set("iotune", flattenIOTune(disk.IOTune)) - d.Set("iqn", disk.IQN) - d.Set("login", disk.Login) - d.Set("milestones", disk.Milestones) - d.Set("disk_name", disk.Name) - d.Set("order", disk.Order) - d.Set("params", disk.Params) - d.Set("parent_id", disk.ParentId) - d.Set("passwd", disk.Passwd) - d.Set("pci_slot", disk.PciSlot) - d.Set("pool", disk.Pool) - d.Set("present_to", disk.PresentTo) - d.Set("purge_attempts", disk.PurgeAttempts) - d.Set("purge_time", disk.PurgeTime) - d.Set("reality_device_number", disk.RealityDeviceNumber) - d.Set("reference_id", disk.ReferenceId) - d.Set("res_id", disk.ResID) - d.Set("res_name", disk.ResName) - d.Set("role", disk.Role) - d.Set("sep_id", disk.SepID) - d.Set("sep_type", disk.SepType) - d.Set("size_max", disk.SizeMax) - d.Set("size_used", disk.SizeUsed) - d.Set("shareable", disk.Shareable) - d.Set("snapshots", flattenDiskSnapshotList(disk.Snapshots)) - d.Set("status", disk.Status) - d.Set("tech_status", disk.TechStatus) - d.Set("type", disk.Type) - d.Set("vmid", disk.VMID) + flattenDisk(d, *disk) return warnings.Get() } func resourceDiskUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { c := m.(*controller.ControllerCfg) + warnings := dc.Warnings{} urlValues := &url.Values{} + + haveAccount, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + if !haveAccount { + return diag.Errorf("resourceDiskUpdate: can't update Disk because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + if !haveGID { + return diag.Errorf("resourceDiskUpdate: can't update Disk because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + disk, err := utilityDiskCheckPresence(ctx, d, m) - if disk == nil { + if err != nil { + return diag.FromErr(err) + } + + hasChangeState := false + + switch disk.Status { + case status.Destroyed, status.Purged: + d.Set("disk_id", 0) + return resourceDiskCreate(ctx, d, m) + case status.Deleted: + hasChangeState = true + urlValues.Add("diskId", d.Id()) + urlValues.Add("reason", d.Get("reason").(string)) + + _, err := c.DecortAPICall(ctx, "POST", disksRestoreAPI, urlValues) if err != nil { + warnings.Add(err) + } + case status.Assigned: + case status.Modeled: + return diag.Errorf("The disk is in status: %s, please, contact support for more information", disk.Status) + case status.Creating: + case status.Created: + case status.Allocated: + case status.Unallocated: + } + + if hasChangeState { + disk, err = utilityDiskCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") return diag.FromErr(err) } - return nil } if d.HasChange("size_max") { @@ -238,7 +257,7 @@ func resourceDiskUpdate(ctx context.Context, d *schema.ResourceData, m interface log.Debugf("resourceDiskUpdate: resizing disk ID %s - %d GB -> %d GB", d.Id(), oldSize.(int), newSize.(int)) urlValues.Add("diskId", d.Id()) - urlValues.Add("size", fmt.Sprintf("%d", newSize.(int))) + urlValues.Add("size", strconv.Itoa(newSize.(int))) _, err := c.DecortAPICall(ctx, "POST", disksResizeAPI, urlValues) if err != nil { return diag.FromErr(err) @@ -310,11 +329,9 @@ func resourceDiskUpdate(ctx context.Context, d *schema.ResourceData, m interface func resourceDiskDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { disk, err := utilityDiskCheckPresence(ctx, d, m) - if disk == nil { - if err != nil { - return diag.FromErr(err) - } - return nil + if err != nil { + d.SetId("") + return diag.FromErr(err) } if disk.Status == status.Destroyed || disk.Status == status.Purged { return nil @@ -363,13 +380,6 @@ func resourceDiskSchemaMake() map[string]*schema.Schema { Computed: true, Description: "Pool for disk location", }, - "present_to": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeInt, - }, - }, "sep_id": { Type: schema.TypeInt, Optional: true, @@ -412,85 +422,6 @@ func resourceDiskSchemaMake() map[string]*schema.Schema { Optional: true, Computed: true, }, - - "disk_id": { - Type: schema.TypeInt, - Computed: true, - Description: "Disk ID. Duplicates the value of the ID parameter", - }, - "account_name": { - Type: schema.TypeString, - Computed: true, - Description: "The name of the subscriber '(account') to whom this disk belongs", - }, - "acl": { - Type: schema.TypeString, - Computed: true, - }, - "boot_partition": { - Type: schema.TypeInt, - Computed: true, - Description: "Number of disk partitions", - }, - "computes": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "compute_id": { - Type: schema.TypeString, - Computed: true, - }, - "compute_name": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - "created_time": { - Type: schema.TypeInt, - Computed: true, - Description: "Created time", - }, - "deleted_time": { - Type: schema.TypeInt, - Computed: true, - Description: "Deleted time", - }, - "destruction_time": { - Type: schema.TypeInt, - Computed: true, - Description: "Time of final deletion", - }, - "devicename": { - Type: schema.TypeString, - Computed: true, - Description: "Name of the device", - }, - "disk_path": { - Type: schema.TypeString, - Computed: true, - Description: "Disk path", - }, - "guid": { - Type: schema.TypeInt, - Computed: true, - Description: "Disk ID on the storage side", - }, - "image_id": { - Type: schema.TypeInt, - Computed: true, - Description: "Image ID", - }, - "images": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "IDs of images using the disk", - }, "iotune": { Type: schema.TypeList, Optional: true, @@ -579,6 +510,91 @@ func resourceDiskSchemaMake() map[string]*schema.Schema { }, }, }, + "present_to": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "disk_id": { + Type: schema.TypeInt, + Computed: true, + Description: "Disk ID. Duplicates the value of the ID parameter", + }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the subscriber '(account') to whom this disk belongs", + }, + "acl": { + Type: schema.TypeString, + Computed: true, + }, + "boot_partition": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of disk partitions", + }, + "computes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compute_id": { + Type: schema.TypeString, + Computed: true, + }, + "compute_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "created_time": { + Type: schema.TypeInt, + Computed: true, + Description: "Created time", + }, + "deleted_time": { + Type: schema.TypeInt, + Computed: true, + Description: "Deleted time", + }, + "destruction_time": { + Type: schema.TypeInt, + Computed: true, + Description: "Time of final deletion", + }, + "devicename": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the device", + }, + "disk_path": { + Type: schema.TypeString, + Computed: true, + Description: "Disk path", + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + Description: "Disk ID on the storage side", + }, + "image_id": { + Type: schema.TypeInt, + Computed: true, + Description: "Image ID", + }, + "images": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "IDs of images using the disk", + }, "iqn": { Type: schema.TypeString, Computed: true, diff --git a/internal/service/cloudapi/disks/resource_disk_snapshot.go b/internal/service/cloudapi/disks/resource_disk_snapshot.go index 233ffd9..54a5357 100644 --- a/internal/service/cloudapi/disks/resource_disk_snapshot.go +++ b/internal/service/cloudapi/disks/resource_disk_snapshot.go @@ -39,21 +39,20 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" - log "github.com/sirupsen/logrus" ) func resourceDiskSnapshotCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { urlValues := &url.Values{} c := m.(*controller.ControllerCfg) + disk, err := utilityDiskCheckPresence(ctx, d, m) - if disk == nil { - if err != nil { - return diag.FromErr(err) - } - return nil + if err != nil { + return diag.FromErr(err) } + snapshots := disk.Snapshots snapshot := Snapshot{} label := d.Get("label").(string) @@ -190,17 +189,17 @@ func resourceDiskSnapshotSchemaMake() map[string]*schema.Schema { Default: false, Description: "Needed in order to make a snapshot rollback", }, - "guid": { - Type: schema.TypeString, - Computed: true, - Description: "ID of the snapshot", - }, "timestamp": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Snapshot time", }, + "guid": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the snapshot", + }, "res_id": { Type: schema.TypeString, Computed: true, diff --git a/internal/service/cloudapi/disks/utility_disk.go b/internal/service/cloudapi/disks/utility_disk.go index 048231c..fd3e063 100644 --- a/internal/service/cloudapi/disks/utility_disk.go +++ b/internal/service/cloudapi/disks/utility_disk.go @@ -38,8 +38,8 @@ import ( "net/url" "strconv" - "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" log "github.com/sirupsen/logrus" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -50,8 +50,12 @@ func utilityDiskCheckPresence(ctx context.Context, d *schema.ResourceData, m int disk := &Disk{} - if d.Get("disk_id").(int) == 0 { - urlValues.Add("diskId", d.Id()) + if d.Get("disk_id") != nil { + if d.Get("disk_id").(int) == 0 { + urlValues.Add("diskId", d.Id()) + } else { + urlValues.Add("diskId", strconv.Itoa(d.Get("disk_id").(int))) + } } else { urlValues.Add("diskId", strconv.Itoa(d.Get("disk_id").(int))) } diff --git a/internal/service/cloudapi/image/data_source_image.go b/internal/service/cloudapi/image/data_source_image.go index e841829..3fa6894 100644 --- a/internal/service/cloudapi/image/data_source_image.go +++ b/internal/service/cloudapi/image/data_source_image.go @@ -41,59 +41,6 @@ import ( "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" ) -func flattenHistory(history []History) []map[string]interface{} { - temp := make([]map[string]interface{}, 0) - for _, item := range history { - t := map[string]interface{}{ - "id": item.Id, - "guid": item.Guid, - "timestamp": item.Timestamp, - } - - temp = append(temp, t) - } - return temp -} - -func flattenImage(d *schema.ResourceData, img *ImageExtend) { - d.Set("unc_path", img.UNCPath) - d.Set("ckey", img.CKey) - d.Set("account_id", img.AccountId) - d.Set("acl", img.Acl) - d.Set("architecture", img.Architecture) - d.Set("boot_type", img.BootType) - d.Set("bootable", img.Bootable) - d.Set("compute_ci_id", img.ComputeCiId) - d.Set("deleted_time", img.DeletedTime) - d.Set("desc", img.Description) - d.Set("drivers", img.Drivers) - d.Set("enabled", img.Enabled) - d.Set("gid", img.GridId) - d.Set("guid", img.GUID) - d.Set("history", flattenHistory(img.History)) - d.Set("hot_resize", img.HotResize) - d.Set("image_id", img.Id) - d.Set("last_modified", img.LastModified) - d.Set("link_to", img.LinkTo) - d.Set("milestones", img.Milestones) - d.Set("image_name", img.Name) - d.Set("password", img.Password) - d.Set("pool_name", img.Pool) - d.Set("provider_name", img.ProviderName) - d.Set("purge_attempts", img.PurgeAttempts) - d.Set("present_to", img.PresentTo) - d.Set("res_id", img.ResId) - d.Set("rescuecd", img.RescueCD) - d.Set("sep_id", img.SepId) - d.Set("shared_with", img.SharedWith) - d.Set("size", img.Size) - d.Set("status", img.Status) - d.Set("tech_status", img.TechStatus) - d.Set("type", img.Type) - d.Set("username", img.Username) - d.Set("version", img.Version) -} - func dataSourceImageRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { image, err := utilityImageCheckPresence(ctx, d, m) if err != nil { diff --git a/internal/service/cloudapi/image/flattens.go b/internal/service/cloudapi/image/flattens.go new file mode 100644 index 0000000..1d6466c --- /dev/null +++ b/internal/service/cloudapi/image/flattens.go @@ -0,0 +1,56 @@ +package image + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func flattenHistory(history []History) []map[string]interface{} { + temp := make([]map[string]interface{}, 0) + for _, item := range history { + t := map[string]interface{}{ + "id": item.Id, + "guid": item.Guid, + "timestamp": item.Timestamp, + } + + temp = append(temp, t) + } + return temp +} + +func flattenImage(d *schema.ResourceData, img *ImageExtend) { + d.Set("unc_path", img.UNCPath) + d.Set("ckey", img.CKey) + d.Set("account_id", img.AccountId) + d.Set("acl", img.Acl) + d.Set("architecture", img.Architecture) + d.Set("boot_type", img.BootType) + d.Set("bootable", img.Bootable) + d.Set("compute_ci_id", img.ComputeCiId) + d.Set("deleted_time", img.DeletedTime) + d.Set("desc", img.Description) + d.Set("drivers", img.Drivers) + d.Set("enabled", img.Enabled) + d.Set("gid", img.GridId) + d.Set("guid", img.GUID) + d.Set("history", flattenHistory(img.History)) + d.Set("hot_resize", img.HotResize) + d.Set("image_id", img.Id) + d.Set("last_modified", img.LastModified) + d.Set("link_to", img.LinkTo) + d.Set("milestones", img.Milestones) + d.Set("image_name", img.Name) + d.Set("password", img.Password) + d.Set("pool_name", img.Pool) + d.Set("provider_name", img.ProviderName) + d.Set("purge_attempts", img.PurgeAttempts) + d.Set("present_to", img.PresentTo) + d.Set("res_id", img.ResId) + d.Set("rescuecd", img.RescueCD) + d.Set("sep_id", img.SepId) + d.Set("shared_with", img.SharedWith) + d.Set("size", img.Size) + d.Set("status", img.Status) + d.Set("tech_status", img.TechStatus) + d.Set("type", img.Type) + d.Set("username", img.Username) + d.Set("version", img.Version) +} diff --git a/internal/service/cloudapi/image/resource_check_input_values.go b/internal/service/cloudapi/image/resource_check_input_values.go new file mode 100644 index 0000000..c88c96a --- /dev/null +++ b/internal/service/cloudapi/image/resource_check_input_values.go @@ -0,0 +1,77 @@ +package image + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existAccountID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + accountList := []struct { + ID int `json:"id"` + }{} + + accountListAPI := "/restmachine/cloudapi/account/list" + + accountListRaw, err := c.DecortAPICall(ctx, "POST", accountListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(accountListRaw), &accountList) + if err != nil { + return false, err + } + + haveAccount := false + + myAccount := d.Get("account_id").(int) + for _, account := range accountList { + if account.ID == myAccount { + haveAccount = true + break + } + } + return haveAccount, nil +} + +func existGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + locationList := []struct { + GID int `json:"gid"` + }{} + + locationsListAPI := "/restmachine/cloudapi/locations/list" + + locationListRaw, err := c.DecortAPICall(ctx, "POST", locationsListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(locationListRaw), &locationList) + if err != nil { + return false, err + } + + haveGID := false + + gid := d.Get("gid").(int) + for _, location := range locationList { + if location.GID == gid { + haveGID = true + break + } + } + + return haveGID, nil +} diff --git a/internal/service/cloudapi/image/resource_image.go b/internal/service/cloudapi/image/resource_image.go index edd574d..7aa2aff 100644 --- a/internal/service/cloudapi/image/resource_image.go +++ b/internal/service/cloudapi/image/resource_image.go @@ -40,14 +40,35 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" - log "github.com/sirupsen/logrus" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" ) func resourceImageCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceImageCreate: called for image %s", d.Get("name").(string)) + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveGID { + return diag.Errorf("resourceImageCreate: can't create Image because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + + if _, ok := d.GetOk("account_id"); ok { + haveAccountID, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveAccountID { + return diag.Errorf("resourceImageCreate: can't create Image because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("name", d.Get("name").(string)) @@ -96,13 +117,7 @@ func resourceImageCreate(ctx context.Context, d *schema.ResourceData, m interfac if architecture, ok := d.GetOk("architecture"); ok { urlValues.Add("architecture", architecture.(string)) } - /* uncomment then OK - imageId, err := c.DecortAPICall(ctx, "POST", imageCreateAPI, urlValues) - if err != nil { - return diag.FromErr(err) - } - */ - //innovation + res, err := c.DecortAPICall(ctx, "POST", imageCreateAPI, urlValues) if err != nil { return diag.FromErr(err) @@ -141,41 +156,17 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, m interface{ return diag.FromErr(err) } - d.Set("unc_path", img.UNCPath) - d.Set("ckey", img.CKey) - d.Set("account_id", img.AccountId) - d.Set("acl", img.Acl) - d.Set("architecture", img.Architecture) - d.Set("boot_type", img.BootType) - d.Set("bootable", img.Bootable) - d.Set("compute_ci_id", img.ComputeCiId) - d.Set("deleted_time", img.DeletedTime) - d.Set("desc", img.Description) - d.Set("drivers", img.Drivers) - d.Set("enabled", img.Enabled) - d.Set("gid", img.GridId) - d.Set("guid", img.GUID) - d.Set("history", flattenHistory(img.History)) - d.Set("hot_resize", img.HotResize) - d.Set("image_id", img.Id) - d.Set("last_modified", img.LastModified) - d.Set("link_to", img.LinkTo) - d.Set("milestones", img.Milestones) - d.Set("image_name", img.Name) - d.Set("password", img.Password) - d.Set("pool_name", img.Pool) - d.Set("provider_name", img.ProviderName) - d.Set("purge_attempts", img.PurgeAttempts) - d.Set("res_id", img.ResId) - d.Set("rescuecd", img.RescueCD) - d.Set("sep_id", img.SepId) - d.Set("shared_with", img.SharedWith) - d.Set("size", img.Size) - d.Set("status", img.Status) - d.Set("tech_status", img.TechStatus) - d.Set("type", img.Type) - d.Set("username", img.Username) - d.Set("version", img.Version) + switch img.Status { + case status.Modeled: + return diag.Errorf("The image is in status: %s, please, contact support for more information", img.Status) + case status.Creating: + case status.Created: + case status.Destroyed, status.Purged: + d.SetId("") + return resourceImageCreate(ctx, d, m) + } + + flattenImage(d, img) return nil } @@ -222,9 +213,47 @@ func resourceImageEditName(ctx context.Context, d *schema.ResourceData, m interf return nil } -func resourceImageEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceImageUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceImageEdit: called for %s, id: %s", d.Get("name").(string), d.Id()) + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveGID { + return diag.Errorf("resourceImageUpdate: can't update Image because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + + if _, ok := d.GetOk("account_id"); ok { + haveAccountID, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveAccountID { + return diag.Errorf("resourceImageUpdate: can't update Image because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + } + + image, err := utilityImageCheckPresence(ctx, d, m) + if image == nil { + if err != nil { + return diag.FromErr(err) + } + return nil + } + + switch image.Status { + case status.Modeled: + return diag.Errorf("The image is in status: %s, please, contact support for more information", image.Status) + case status.Creating: + case status.Created: + case status.Destroyed, status.Purged: + d.SetId("") + return resourceImageCreate(ctx, d, m) + } + if d.HasChange("name") { err := resourceImageEditName(ctx, d, m) if err != nil { @@ -241,7 +270,7 @@ func ResourceImage() *schema.Resource { CreateContext: resourceImageCreate, ReadContext: resourceImageRead, - UpdateContext: resourceImageEdit, + UpdateContext: resourceImageUpdate, DeleteContext: resourceImageDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/k8s/api.go b/internal/service/cloudapi/k8s/api.go index bb53527..86400a6 100644 --- a/internal/service/cloudapi/k8s/api.go +++ b/internal/service/cloudapi/k8s/api.go @@ -39,6 +39,8 @@ const ( K8sDeleteAPI = "/restmachine/cloudapi/k8s/delete" K8sListAPI = "/restmachine/cloudapi/k8s/list" K8sListDeletedAPI = "/restmachine/cloudapi/k8s/listDeleted" + K8sRestoreAPI = "/restmachine/cloudapi/k8s/restore" + K8sEnableAPI = "/restmachine/cloudapi/k8s/enable" K8sWgCreateAPI = "/restmachine/cloudapi/k8s/workersGroupAdd" K8sWgDeleteAPI = "/restmachine/cloudapi/k8s/workersGroupDelete" diff --git a/internal/service/cloudapi/k8s/data_source_k8s_wg.go b/internal/service/cloudapi/k8s/data_source_k8s_wg.go index 5850d32..79ac1bf 100644 --- a/internal/service/cloudapi/k8s/data_source_k8s_wg.go +++ b/internal/service/cloudapi/k8s/data_source_k8s_wg.go @@ -38,51 +38,21 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" - "repository.basistech.ru/BASIS/terraform-provider-decort/internal/service/cloudapi/kvmvm" log "github.com/sirupsen/logrus" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" ) func dataSourceK8sWgRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("dataSourceK8sWgRead: called with k8s id %d", d.Get("k8s_id").(int)) - k8s, err := utilityDataK8sCheckPresence(ctx, d, m) + wg, workersComputeList, err := utilityDataK8sWgCheckPresence(ctx, d, m) if err != nil { return diag.FromErr(err) } - d.SetId(strconv.Itoa(d.Get("wg_id").(int))) - - var id int - if d.Id() != "" { - id, err = strconv.Atoi(d.Id()) - if err != nil { - return diag.FromErr(err) - } - } else { - id = d.Get("wg_id").(int) - } - curWg := K8SGroup{} - for _, wg := range k8s.K8SGroups.Workers { - if wg.ID == uint64(id) { - curWg = wg - break - } - } - if curWg.ID == 0 { - return diag.Errorf("Not found wg with id: %v in k8s cluster: %v", id, k8s.ID) - } - - workersComputeList := make([]kvmvm.ComputeGetResp, 0, 0) - for _, info := range curWg.DetailedInfo { - compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID) - if err != nil { - return diag.FromErr(err) - } - workersComputeList = append(workersComputeList, *compute) - } + d.SetId(strconv.Itoa(d.Get("wg_id").(int))) - flattenWgData(d, curWg, workersComputeList) + flattenWg(d, *wg, workersComputeList) return nil } diff --git a/internal/service/cloudapi/k8s/flattens.go b/internal/service/cloudapi/k8s/flattens.go index 1ff1280..3a8e02c 100644 --- a/internal/service/cloudapi/k8s/flattens.go +++ b/internal/service/cloudapi/k8s/flattens.go @@ -257,9 +257,13 @@ func flattenK8sList(d *schema.ResourceData, k8sItems K8SList) { } func flattenResourceK8s(d *schema.ResourceData, k8s K8SRecord, masters []kvmvm.ComputeGetResp, workers []kvmvm.ComputeGetResp) { + wg_name := k8s.K8SGroups.Workers[0].Name + d.Set("acl", flattenAcl(k8s.ACL)) d.Set("account_id", k8s.AccountID) d.Set("account_name", k8s.AccountName) + d.Set("k8sci_id", k8s.CIID) + d.Set("wg_name", wg_name) d.Set("bservice_id", k8s.BServiceID) d.Set("created_by", k8s.CreatedBy) d.Set("created_time", k8s.CreatedTime) @@ -268,7 +272,9 @@ func flattenResourceK8s(d *schema.ResourceData, k8s K8SRecord, masters []kvmvm.C d.Set("k8s_ci_name", k8s.K8CIName) d.Set("masters", flattenMasterGroup(k8s.K8SGroups.Masters, masters)) d.Set("workers", flattenK8sGroup(k8s.K8SGroups.Workers, workers)) + d.Set("with_lb", k8s.LBID != 0) d.Set("lb_id", k8s.LBID) + d.Set("name", k8s.Name) d.Set("rg_id", k8s.RGID) d.Set("rg_name", k8s.RGName) d.Set("status", k8s.Status) @@ -278,7 +284,7 @@ func flattenResourceK8s(d *schema.ResourceData, k8s K8SRecord, masters []kvmvm.C d.Set("default_wg_id", k8s.K8SGroups.Workers[0].ID) } -func flattenWgData(d *schema.ResourceData, wg K8SGroup, computes []kvmvm.ComputeGetResp) { +func flattenWg(d *schema.ResourceData, wg K8SGroup, computes []kvmvm.ComputeGetResp) { d.Set("annotations", wg.Annotations) d.Set("cpu", wg.CPU) d.Set("detailed_info", flattenDetailedInfo(wg.DetailedInfo, computes)) diff --git a/internal/service/cloudapi/k8s/resource_check_input_values.go b/internal/service/cloudapi/k8s/resource_check_input_values.go new file mode 100644 index 0000000..7dd3656 --- /dev/null +++ b/internal/service/cloudapi/k8s/resource_check_input_values.go @@ -0,0 +1,143 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existK8sID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + k8sList := []struct { + ID int `json:"id"` + }{} + + k8sListAPI := "/restmachine/cloudapi/k8s/list" + + k8sListRaw, err := c.DecortAPICall(ctx, "POST", k8sListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(k8sListRaw), &k8sList) + if err != nil { + return false, err + } + + haveK8s := false + k8sID := d.Get("k8s_id").(int) + for _, k8s := range k8sList { + if k8s.ID == k8sID { + haveK8s = true + break + } + } + + return haveK8s, nil +} + +func existK8sCIID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + k8sciList := []struct { + ID int `json:"id"` + }{} + + k8sciListAPI := "/restmachine/cloudapi/k8ci/list" + + k8sciListRaw, err := c.DecortAPICall(ctx, "POST", k8sciListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(k8sciListRaw), &k8sciList) + if err != nil { + return false, err + } + + haveK8sCI := false + k8sciID := d.Get("k8sci_id").(int) + for _, k8ci := range k8sciList { + if k8ci.ID == k8sciID { + haveK8sCI = true + break + } + } + + return haveK8sCI, nil +} + +func existRGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + rgList := []struct { + ID int `json:"id"` + }{} + + rgListAPI := "/restmachine/cloudapi/rg/list" + + rgListRaw, err := c.DecortAPICall(ctx, "POST", rgListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(rgListRaw), &rgList) + if err != nil { + return false, err + } + + haveRG := false + rgId := d.Get("rg_id").(int) + for _, rg := range rgList { + if rg.ID == rgId { + haveRG = true + break + } + } + + return haveRG, nil +} + +func existExtNetID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + extNetID := d.Get("extnet_id").(int) + + if extNetID == 0 { + return true, nil + } + + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + extNetList := []struct { + ID int `json:"id"` + }{} + + extNetListAPI := "/restmachine/cloudapi/extnet/list" + + extNetListRaw, err := c.DecortAPICall(ctx, "POST", extNetListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(extNetListRaw), &extNetList) + if err != nil { + return false, err + } + + haveExtNet := false + for _, extNet := range extNetList { + if extNet.ID == extNetID { + haveExtNet = true + break + } + } + + return haveExtNet, nil +} diff --git a/internal/service/cloudapi/k8s/resource_k8s.go b/internal/service/cloudapi/k8s/resource_k8s.go index 81b8e61..d4b1c5b 100644 --- a/internal/service/cloudapi/k8s/resource_k8s.go +++ b/internal/service/cloudapi/k8s/resource_k8s.go @@ -43,15 +43,45 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/service/cloudapi/kvmvm" - log "github.com/sirupsen/logrus" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" ) func resourceK8sCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceK8sCreate: called with name %s, rg %d", d.Get("name").(string), d.Get("rg_id").(int)) + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceK8sCreate: can't create k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + + haveK8sciID, err := existK8sCIID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveK8sciID { + return diag.Errorf("resourceK8sCreate: can't create k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int)) + } + + if _, ok := d.GetOk("extnet_id"); ok { + haveExtNetID, err := existExtNetID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveExtNetID { + return diag.Errorf("resourceK8sCreate: can't create k8s cluster because ExtNetID %d is not allowed or does not exist", d.Get("extnet_id").(int)) + } + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("name", d.Get("name").(string)) @@ -158,12 +188,61 @@ func resourceK8sCreate(ctx context.Context, d *schema.ResourceData, m interface{ } func resourceK8sRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - - k8s, err := utilityDataK8sCheckPresence(ctx, d, m) + k8s, err := utilityK8sCheckPresence(ctx, d, m) if err != nil { d.SetId("") return diag.FromErr(err) } + + c := m.(*controller.ControllerCfg) + + hasChanged := false + + switch k8s.Status { + case status.Modeled: + return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", k8s.Status) + case status.Creating: + case status.Created: + case status.Deleting: + case status.Deleted: + urlVal := &url.Values{} + urlVal.Add("k8sId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", K8sRestoreAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + _, err = c.DecortAPICall(ctx, "POST", K8sEnableAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Destroying: + return diag.Errorf("The k8s cluster is in progress with status: %s", k8s.Status) + case status.Destroyed: + d.SetId("") + return resourceK8sCreate(ctx, d, m) + case status.Enabling: + case status.Enabled: + case status.Disabling: + case status.Disabled: + log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", k8s.Status) + case status.Restoring: + } + + if hasChanged { + k8s, err = utilityK8sCheckPresence(ctx, d, m) + if k8s == nil { + d.SetId("") + if err != nil { + return diag.FromErr(err) + } + return nil + } + } + k8sList, err := utilityK8sListCheckPresence(ctx, d, m, K8sListAPI) if err != nil { d.SetId("") @@ -201,7 +280,6 @@ func resourceK8sRead(ctx context.Context, d *schema.ResourceData, m interface{}) flattenResourceK8s(d, *k8s, masterComputeList, workersComputeList) - c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("lbId", strconv.FormatUint(k8s.LBID, 10)) @@ -233,6 +311,76 @@ func resourceK8sUpdate(ctx context.Context, d *schema.ResourceData, m interface{ c := m.(*controller.ControllerCfg) + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + + haveK8sciID, err := existK8sCIID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveK8sciID { + return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int)) + } + + k8s, err := utilityK8sCheckPresence(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + hasChanged := false + + switch k8s.Status { + case status.Modeled: + return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", k8s.Status) + case status.Creating: + case status.Created: + case status.Deleting: + case status.Deleted: + urlVal := &url.Values{} + urlVal.Add("k8sId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", K8sRestoreAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + _, err = c.DecortAPICall(ctx, "POST", K8sEnableAPI, urlVal) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Destroying: + return diag.Errorf("The k8s cluster is in progress with status: %s", k8s.Status) + case status.Destroyed: + d.SetId("") + return resourceK8sCreate(ctx, d, m) + case status.Enabling: + case status.Enabled: + case status.Disabling: + case status.Disabled: + log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", k8s.Status) + case status.Restoring: + } + + if hasChanged { + k8s, err = utilityK8sCheckPresence(ctx, d, m) + if k8s == nil { + d.SetId("") + if err != nil { + return diag.FromErr(err) + } + return nil + } + } + if d.HasChange("name") { urlValues := &url.Values{} urlValues.Add("k8sId", d.Id()) @@ -245,11 +393,6 @@ func resourceK8sUpdate(ctx context.Context, d *schema.ResourceData, m interface{ } if d.HasChange("workers") { - k8s, err := utilityK8sCheckPresence(ctx, d, m) - if err != nil { - return diag.FromErr(err) - } - wg := k8s.K8SGroups.Workers[0] urlValues := &url.Values{} urlValues.Add("k8sId", d.Id()) @@ -321,7 +464,6 @@ func resourceK8sSchemaMake() map[string]*schema.Schema { "wg_name": { Type: schema.TypeString, Required: true, - ForceNew: true, Description: "Name for first worker group created with cluster.", }, "labels": { @@ -369,7 +511,6 @@ func resourceK8sSchemaMake() map[string]*schema.Schema { "with_lb": { Type: schema.TypeBool, Optional: true, - ForceNew: true, Default: true, Description: "Create k8s with load balancer if true.", }, diff --git a/internal/service/cloudapi/k8s/resource_k8s_wg.go b/internal/service/cloudapi/k8s/resource_k8s_wg.go index 1f2f0ed..1a1379d 100644 --- a/internal/service/cloudapi/k8s/resource_k8s_wg.go +++ b/internal/service/cloudapi/k8s/resource_k8s_wg.go @@ -40,15 +40,24 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/service/cloudapi/kvmvm" - log "github.com/sirupsen/logrus" ) func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceK8sWgCreate: called with k8s id %d", d.Get("k8s_id").(int)) + haveK8sID, err := existK8sID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveK8sID { + return diag.Errorf("resourceK8sCreate: can't create k8s cluster because K8sID %d is not allowed or does not exist", d.Get("k8s_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) @@ -56,7 +65,12 @@ func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interfac urlValues.Add("workerNum", strconv.Itoa(d.Get("num").(int))) urlValues.Add("workerCpu", strconv.Itoa(d.Get("cpu").(int))) urlValues.Add("workerRam", strconv.Itoa(d.Get("ram").(int))) - urlValues.Add("workerDisk", strconv.Itoa(d.Get("disk").(int))) + + if d.Get("disk") == nil { + urlValues.Add("workerDisk", strconv.Itoa(0)) + } else { + urlValues.Add("workerDisk", strconv.Itoa(d.Get("disk").(int))) + } resp, err := c.DecortAPICall(ctx, "POST", K8sWgCreateAPI, urlValues) if err != nil { @@ -100,45 +114,34 @@ func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interfac func resourceK8sWgRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceK8sWgRead: called with %v", d.Id()) - k8s, err := utilityDataK8sCheckPresence(ctx, d, m) + wg, err := utilityK8sWgCheckPresence(ctx, d, m) if err != nil { return diag.FromErr(err) } - var id int - if d.Id() != "" { - id, err = strconv.Atoi(strings.Split(d.Id(), "#")[0]) + workersComputeList := make([]kvmvm.ComputeGetResp, 0, 0) + for _, info := range wg.DetailedInfo { + compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID) if err != nil { return diag.FromErr(err) } - } else { - id = d.Get("wg_id").(int) - } - - curWg := K8SGroup{} - for _, wg := range k8s.K8SGroups.Workers { - if wg.ID == uint64(id) { - curWg = wg - break - } - } - if curWg.ID == 0 { - return diag.Errorf("Not found wg with id: %v in k8s cluster: %v", id, k8s.ID) + workersComputeList = append(workersComputeList, *compute) } - workersComputeList := make([]kvmvm.ComputeGetResp, 0, 0) - for _, info := range curWg.DetailedInfo { - compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID) + d.Set("wg_id", wg.ID) + if strings.Contains(d.Id(), "#") { + k8sId, err := strconv.Atoi(strings.Split(d.Id(), "#")[1]) if err != nil { return diag.FromErr(err) } - workersComputeList = append(workersComputeList, *compute) - } + d.Set("k8s_id", k8sId) + } else { + d.Set("k8s_id", d.Get("k8s_id")) + } d.SetId(strings.Split(d.Id(), "#")[0]) - d.Set("k8s_id", k8s.ID) - d.Set("wg_id", curWg.ID) - flattenWgData(d, curWg, workersComputeList) + + flattenWg(d, *wg, workersComputeList) return nil } @@ -148,6 +151,15 @@ func resourceK8sWgUpdate(ctx context.Context, d *schema.ResourceData, m interfac c := m.(*controller.ControllerCfg) + haveK8sID, err := existK8sID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveK8sID { + return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because K8sID %d is not allowed or does not exist", d.Get("k8s_id").(int)) + } + wg, err := utilityK8sWgCheckPresence(ctx, d, m) if err != nil { return diag.FromErr(err) @@ -242,8 +254,7 @@ func resourceK8sWgSchemaMake() map[string]*schema.Schema { "disk": { Type: schema.TypeInt, Optional: true, - ForceNew: true, - Default: 0, + Computed: true, Description: "Worker node boot disk size. If unspecified or 0, size is defined by OS image size.", }, "wg_id": { diff --git a/internal/service/cloudapi/k8s/utility_k8s.go b/internal/service/cloudapi/k8s/utility_k8s.go index cb9cab6..95baf4f 100644 --- a/internal/service/cloudapi/k8s/utility_k8s.go +++ b/internal/service/cloudapi/k8s/utility_k8s.go @@ -37,7 +37,6 @@ import ( "encoding/json" "net/url" "strconv" - "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" @@ -89,15 +88,8 @@ func utilityComputeCheckPresence(ctx context.Context, d *schema.ResourceData, m func utilityDataK8sCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*K8SRecord, error) { c := m.(*controller.ControllerCfg) urlValues := &url.Values{} - if d.Get("k8s_id") != 0 && d.Get("k8s_id") != nil { - urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) - } else if id := d.Id(); id != "" { - if strings.Contains(id, "#") { - urlValues.Add("k8sId", strings.Split(d.Id(), "#")[1]) - } else { - urlValues.Add("k8sId", d.Id()) - } - } + urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) + k8sRaw, err := c.DecortAPICall(ctx, "POST", K8sGetAPI, urlValues) if err != nil { return nil, err diff --git a/internal/service/cloudapi/k8s/utility_k8s_wg.go b/internal/service/cloudapi/k8s/utility_k8s_wg.go index 958528b..53a55eb 100644 --- a/internal/service/cloudapi/k8s/utility_k8s_wg.go +++ b/internal/service/cloudapi/k8s/utility_k8s_wg.go @@ -38,15 +38,82 @@ import ( "fmt" "net/url" "strconv" + "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/service/cloudapi/kvmvm" ) +func utilityDataK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*K8SGroup, []kvmvm.ComputeGetResp, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + k8sId := d.Get("k8s_id").(int) + wgId := d.Get("wg_id").(int) + + urlValues.Add("k8sId", strconv.Itoa(k8sId)) + + k8sRaw, err := c.DecortAPICall(ctx, "POST", K8sGetAPI, urlValues) + if err != nil { + return nil, nil, err + } + + k8s := K8SRecord{} + err = json.Unmarshal([]byte(k8sRaw), &k8s) + if err != nil { + return nil, nil, err + } + + curWg := K8SGroup{} + for _, wg := range k8s.K8SGroups.Workers { + if wg.ID == uint64(wgId) { + curWg = wg + break + } + } + if curWg.ID == 0 { + return nil, nil, fmt.Errorf("WG with id %v in k8s cluster %v not found", wgId, k8sId) + } + + workersComputeList := make([]kvmvm.ComputeGetResp, 0, 0) + for _, info := range curWg.DetailedInfo { + compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID) + if err != nil { + return nil, nil, err + } + + workersComputeList = append(workersComputeList, *compute) + } + + return &curWg, workersComputeList, nil +} + func utilityK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*K8SGroup, error) { c := m.(*controller.ControllerCfg) urlValues := &url.Values{} - urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) + var wgId int + var k8sId int + var err error + + if strings.Contains(d.Id(), "#") { + wgId, err = strconv.Atoi(strings.Split(d.Id(), "#")[0]) + if err != nil { + return nil, err + } + k8sId, err = strconv.Atoi(strings.Split(d.Id(), "#")[1]) + if err != nil { + return nil, err + } + } else { + wgId, err = strconv.Atoi(d.Id()) + if err != nil { + return nil, err + } + k8sId = d.Get("k8s_id").(int) + } + + urlValues.Add("k8sId", strconv.Itoa(k8sId)) resp, err := c.DecortAPICall(ctx, "POST", K8sGetAPI, urlValues) if err != nil { @@ -62,21 +129,11 @@ func utilityK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m in return nil, err } - var id int - if d.Id() != "" { - id, err = strconv.Atoi(d.Id()) - if err != nil { - return nil, err - } - } else { - id = d.Get("wg_id").(int) - } - for _, wg := range k8s.K8SGroups.Workers { - if wg.ID == uint64(id) { + if wg.ID == uint64(wgId) { return &wg, nil } } - return nil, fmt.Errorf("Not found wg with id: %v in k8s cluster: %v", id, k8s.ID) + return nil, fmt.Errorf("Not found wg with id: %v in k8s cluster: %v", wgId, k8s.ID) } diff --git a/internal/service/cloudapi/kvmvm/resource_compute.go b/internal/service/cloudapi/kvmvm/resource_compute.go index b6dbdee..80cdcef 100644 --- a/internal/service/cloudapi/kvmvm/resource_compute.go +++ b/internal/service/cloudapi/kvmvm/resource_compute.go @@ -82,8 +82,8 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf urlValues.Add("netType", "NONE") urlValues.Add("start", "0") // at the 1st step create compute in a stopped state - argVal, argSet := d.GetOk("description") - if argSet { + argVal, ok := d.GetOk("description") + if ok { urlValues.Add("desc", argVal.(string)) } @@ -138,8 +138,8 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86") } - argVal, argSet = d.GetOk("cloud_init") - if argSet { + argVal, ok = d.GetOk("cloud_init") + if ok { // userdata must not be empty string and must not be a reserved keyword "applied" userdata := argVal.(string) if userdata != "" && userdata != "applied" { @@ -177,8 +177,8 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf log.Debugf("resourceComputeCreate: new simple Compute ID %d, name %s created", compId, d.Get("name").(string)) - argVal, argSet = d.GetOk("extra_disks") - if argSet && argVal.(*schema.Set).Len() > 0 { + argVal, ok = d.GetOk("extra_disks") + if ok && argVal.(*schema.Set).Len() > 0 { log.Debugf("resourceComputeCreate: calling utilityComputeExtraDisksConfigure to attach %d extra disk(s)", argVal.(*schema.Set).Len()) err = utilityComputeExtraDisksConfigure(ctx, d, m, false) // do_delta=false, as we are working on a new compute if err != nil { @@ -187,8 +187,8 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf return diag.FromErr(err) } } - argVal, argSet = d.GetOk("network") - if argSet && argVal.(*schema.Set).Len() > 0 { + argVal, ok = d.GetOk("network") + if ok && argVal.(*schema.Set).Len() > 0 { log.Debugf("resourceComputeCreate: calling utilityComputeNetworksConfigure to attach %d network(s)", argVal.(*schema.Set).Len()) err = utilityComputeNetworksConfigure(ctx, d, m, false, true) if err != nil { @@ -447,6 +447,8 @@ func resourceComputeRead(ctx context.Context, d *schema.ResourceData, m interfac return diag.FromErr(err) } + hasChanged := false + switch compute.Status { case status.Deleted: urlValues := &url.Values{} @@ -459,22 +461,27 @@ func resourceComputeRead(ctx context.Context, d *schema.ResourceData, m interfac if err != nil { return diag.FromErr(err) } + + hasChanged = true case status.Destroyed: d.SetId("") return resourceComputeCreate(ctx, d, m) case status.Disabled: - log.Debugf("The compute is in status: %s, may troubles can be occured with update. Please, enable compute first.", compute.Status) + log.Debugf("The compute is in status: %s, troubles may occur with update. Please, enable compute first.", compute.Status) case status.Redeploying: case status.Deleting: case status.Destroying: return diag.Errorf("The compute is in progress with status: %s", compute.Status) case status.Modeled: - return diag.Errorf("The compute is in status: %s, please, contant the support for more information", compute.Status) + return diag.Errorf("The compute is in status: %s, please, contact support for more information", compute.Status) } - compute, err = utilityComputeCheckPresence(ctx, d, m) - if err != nil { - return diag.FromErr(err) + if hasChanged { + compute, err = utilityComputeCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } } if err = flattenCompute(d, compute); err != nil { diff --git a/internal/service/cloudapi/lb/resource_check_input_values.go b/internal/service/cloudapi/lb/resource_check_input_values.go new file mode 100644 index 0000000..0188f32 --- /dev/null +++ b/internal/service/cloudapi/lb/resource_check_input_values.go @@ -0,0 +1,139 @@ +package lb + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existLBID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + lbList := []struct { + ID int `json:"id"` + }{} + + lbListAPI := "/restmachine/cloudapi/lb/list" + + lbListRaw, err := c.DecortAPICall(ctx, "POST", lbListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(lbListRaw), &lbList) + if err != nil { + return false, err + } + + haveLB := false + lbId := d.Get("lb_id").(int) + + for _, lb := range lbList { + if lb.ID == lbId { + haveLB = true + break + } + } + + return haveLB, nil +} + +func existRGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + rgList := []struct { + ID int `json:"id"` + }{} + + rgListAPI := "/restmachine/cloudapi/rg/list" + + rgListRaw, err := c.DecortAPICall(ctx, "POST", rgListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(rgListRaw), &rgList) + if err != nil { + return false, err + } + + haveRG := false + rgId := d.Get("rg_id").(int) + for _, rg := range rgList { + if rg.ID == rgId { + haveRG = true + break + } + } + + return haveRG, nil +} + +func existExtNetID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + extNetList := []struct { + ID int `json:"id"` + }{} + + extNetListAPI := "/restmachine/cloudapi/extnet/list" + + extNetListRaw, err := c.DecortAPICall(ctx, "POST", extNetListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(extNetListRaw), &extNetList) + if err != nil { + return false, err + } + + haveExtNet := false + extNetID := d.Get("extnet_id").(int) + for _, extNet := range extNetList { + if extNet.ID == extNetID { + haveExtNet = true + break + } + } + + return haveExtNet, nil +} + +func existViNSID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + vinsList := []struct { + ID int `json:"id"` + }{} + + vinsListAPI := "/restmachine/cloudapi/vins/list" + + vinsListRaw, err := c.DecortAPICall(ctx, "POST", vinsListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(vinsListRaw), &vinsList) + if err != nil { + return false, err + } + + haveVins := false + vinsID := d.Get("vins_id").(int) + for _, vins := range vinsList { + if vins.ID == vinsID { + haveVins = true + break + } + } + + return haveVins, nil +} diff --git a/internal/service/cloudapi/lb/resource_lb.go b/internal/service/cloudapi/lb/resource_lb.go index 45138bc..15431ac 100644 --- a/internal/service/cloudapi/lb/resource_lb.go +++ b/internal/service/cloudapi/lb/resource_lb.go @@ -42,11 +42,39 @@ import ( log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" ) func resourceLBCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBCreate") + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceLBCreate: can't create LB because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + + haveExtNetID, err := existExtNetID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveExtNetID { + return diag.Errorf("resourceLBCreate: can't create LB because ExtNetID %d is not allowed or does not exist", d.Get("extnet_id").(int)) + } + + haveVins, err := existViNSID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveVins { + return diag.Errorf("resourceLBCreate: can't create LB because ViNSID %d is not allowed or does not exist", d.Get("vins_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("name", d.Get("name").(string)) @@ -100,12 +128,57 @@ func resourceLBCreate(ctx context.Context, d *schema.ResourceData, m interface{} func resourceLBRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBRead") + c := m.(*controller.ControllerCfg) + lb, err := utilityLBCheckPresence(ctx, d, m) if lb == nil { d.SetId("") return diag.FromErr(err) } + hasChanged := false + + switch lb.Status { + case status.Modeled: + return diag.Errorf("The LB is in status: %s, please, contact support for more information", lb.Status) + case status.Creating: + case status.Created: + case status.Deleting: + case status.Deleted: + urlValues := &url.Values{} + urlValues.Add("lbId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", lbRestoreAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + _, err = c.DecortAPICall(ctx, "POST", lbEnableAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Destroying: + return diag.Errorf("The LB is in progress with status: %s", lb.Status) + case status.Destroyed: + d.SetId("") + return resourceLBCreate(ctx, d, m) + case status.Enabled: + case status.Enabling: + case status.Disabling: + case status.Disabled: + log.Debugf("The LB is in status: %s, troubles may occur with update. Please, enable LB first.", lb.Status) + case status.Restoring: + } + + if hasChanged { + lb, err = utilityLBCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + d.Set("ha_mode", lb.HAMode) d.Set("backends", flattenLBBackends(lb.Backends)) d.Set("created_by", lb.CreatedBy) @@ -163,11 +236,87 @@ func resourceLBDelete(ctx context.Context, d *schema.ResourceData, m interface{} return nil } -func resourceLBEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceLBUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBEdit") c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceLBUpdate: can't update LB because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + + haveExtNetID, err := existExtNetID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveExtNetID { + return diag.Errorf("resourceLBUpdate: can't update LB because ExtNetID %d is not allowed or does not exist", d.Get("extnet_id").(int)) + } + + haveVins, err := existViNSID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveVins { + return diag.Errorf("resourceLBUpdate: can't update LB because ViNSID %d is not allowed or does not exist", d.Get("vins_id").(int)) + } + + lb, err := utilityLBCheckPresence(ctx, d, m) + if lb == nil { + d.SetId("") + return diag.FromErr(err) + } + + hasChanged := false + + switch lb.Status { + case status.Modeled: + return diag.Errorf("The LB is in status: %s, please, contact support for more information", lb.Status) + case status.Creating: + case status.Created: + case status.Deleting: + case status.Deleted: + urlValues := &url.Values{} + urlValues.Add("lbId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", lbRestoreAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + _, err = c.DecortAPICall(ctx, "POST", lbEnableAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Destroying: + return diag.Errorf("The LB is in progress with status: %s", lb.Status) + case status.Destroyed: + d.SetId("") + return resourceLBCreate(ctx, d, m) + case status.Enabled: + case status.Enabling: + case status.Disabling: + case status.Disabled: + log.Debugf("The LB is in status: %s, troubles may occur with update. Please, enable LB first.", lb.Status) + case status.Restoring: + } + + if hasChanged { + lb, err = utilityLBCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + if d.HasChange("enable") { api := lbDisableAPI enable := d.Get("enable").(bool) @@ -262,7 +411,7 @@ func ResourceLB() *schema.Resource { CreateContext: resourceLBCreate, ReadContext: resourceLBRead, - UpdateContext: resourceLBEdit, + UpdateContext: resourceLBUpdate, DeleteContext: resourceLBDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/lb/resource_lb_backend.go b/internal/service/cloudapi/lb/resource_lb_backend.go index 8ae4a1a..c597764 100644 --- a/internal/service/cloudapi/lb/resource_lb_backend.go +++ b/internal/service/cloudapi/lb/resource_lb_backend.go @@ -49,6 +49,15 @@ import ( func resourceLBBackendCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBBackendCreate") + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBBackendCreate: can't create LB backend because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("backendName", d.Get("name").(string)) @@ -82,7 +91,7 @@ func resourceLBBackendCreate(ctx context.Context, d *schema.ResourceData, m inte urlValues.Add("weight", strconv.Itoa(weight.(int))) } - _, err := c.DecortAPICall(ctx, "POST", lbBackendCreateAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbBackendCreateAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -155,11 +164,20 @@ func resourceLBBackendDelete(ctx context.Context, d *schema.ResourceData, m inte return nil } -func resourceLBBackendEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceLBBackendUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBBackendEdit") c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBBackendUpdate: can't update LB backend because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + urlValues.Add("backendName", d.Get("name").(string)) urlValues.Add("lbId", strconv.Itoa(d.Get("lb_id").(int))) @@ -191,7 +209,7 @@ func resourceLBBackendEdit(ctx context.Context, d *schema.ResourceData, m interf urlValues.Add("weight", strconv.Itoa(d.Get("weight").(int))) } - _, err := c.DecortAPICall(ctx, "POST", lbBackendUpdateAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbBackendUpdateAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -207,7 +225,7 @@ func ResourceLBBackend() *schema.Resource { CreateContext: resourceLBBackendCreate, ReadContext: resourceLBBackendRead, - UpdateContext: resourceLBBackendEdit, + UpdateContext: resourceLBBackendUpdate, DeleteContext: resourceLBBackendDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/lb/resource_lb_backend_server.go b/internal/service/cloudapi/lb/resource_lb_backend_server.go index 9514b7f..8056626 100644 --- a/internal/service/cloudapi/lb/resource_lb_backend_server.go +++ b/internal/service/cloudapi/lb/resource_lb_backend_server.go @@ -49,6 +49,15 @@ import ( func resourceLBBackendServerCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBBackendServerCreate") + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBBackendServerCreate: can't create LB backend server because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("backendName", d.Get("backend_name").(string)) @@ -86,7 +95,7 @@ func resourceLBBackendServerCreate(ctx context.Context, d *schema.ResourceData, urlValues.Add("weight", strconv.Itoa(weight.(int))) } - _, err := c.DecortAPICall(ctx, "POST", lbBackendServerAddAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbBackendServerAddAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -163,11 +172,20 @@ func resourceLBBackendServerDelete(ctx context.Context, d *schema.ResourceData, return nil } -func resourceLBBackendServerEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceLBBackendServerUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBBackendServerEdit") c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBBackendServerUpdate: can't update LB backend server because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + urlValues.Add("backendName", d.Get("backend_name").(string)) urlValues.Add("lbId", strconv.Itoa(d.Get("lb_id").(int))) urlValues.Add("serverName", d.Get("name").(string)) @@ -202,7 +220,7 @@ func resourceLBBackendServerEdit(ctx context.Context, d *schema.ResourceData, m urlValues.Add("weight", strconv.Itoa(d.Get("weight").(int))) } - _, err := c.DecortAPICall(ctx, "POST", lbBackendServerUpdateAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbBackendServerUpdateAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -218,7 +236,7 @@ func ResourceLBBackendServer() *schema.Resource { CreateContext: resourceLBBackendServerCreate, ReadContext: resourceLBBackendServerRead, - UpdateContext: resourceLBBackendServerEdit, + UpdateContext: resourceLBBackendServerUpdate, DeleteContext: resourceLBBackendServerDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/lb/resource_lb_frontend.go b/internal/service/cloudapi/lb/resource_lb_frontend.go index 6b321b9..cbbb749 100644 --- a/internal/service/cloudapi/lb/resource_lb_frontend.go +++ b/internal/service/cloudapi/lb/resource_lb_frontend.go @@ -48,13 +48,22 @@ import ( func resourceLBFrontendCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBFrontendCreate") + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBFrontendCreate: can't create LB frontend because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("backendName", d.Get("backend_name").(string)) urlValues.Add("lbId", strconv.Itoa(d.Get("lb_id").(int))) urlValues.Add("frontendName", d.Get("name").(string)) - _, err := c.DecortAPICall(ctx, "POST", lbFrontendCreateAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbFrontendCreateAPI, urlValues) if err != nil { return diag.FromErr(err) } diff --git a/internal/service/cloudapi/lb/resource_lb_frontend_bind.go b/internal/service/cloudapi/lb/resource_lb_frontend_bind.go index f7bf96e..7adcc85 100644 --- a/internal/service/cloudapi/lb/resource_lb_frontend_bind.go +++ b/internal/service/cloudapi/lb/resource_lb_frontend_bind.go @@ -48,6 +48,15 @@ import ( func resourceLBFrontendBindCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBFrontendBindCreate") + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBFrontendBindCreate: can't create LB frontend bind because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + c := m.(*controller.ControllerCfg) urlValues := &url.Values{} urlValues.Add("frontendName", d.Get("frontend_name").(string)) @@ -56,7 +65,7 @@ func resourceLBFrontendBindCreate(ctx context.Context, d *schema.ResourceData, m urlValues.Add("bindingAddress", d.Get("address").(string)) urlValues.Add("bindingPort", strconv.Itoa(d.Get("port").(int))) - _, err := c.DecortAPICall(ctx, "POST", lbFrontendBindAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbFrontendBindAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -124,11 +133,20 @@ func resourceLBFrontendBindDelete(ctx context.Context, d *schema.ResourceData, m return nil } -func resourceLBFrontendBindEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceLBFrontendBindUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { log.Debugf("resourceLBFrontendBindEdit") c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + haveLBID, err := existLBID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveLBID { + return diag.Errorf("resourceLBFrontendBindUpdate: can't update LB frontend bind because LBID %d is not allowed or does not exist", d.Get("lb_id").(int)) + } + urlValues.Add("frontendName", d.Get("frontend_name").(string)) urlValues.Add("bindingName", d.Get("name").(string)) urlValues.Add("lbId", strconv.Itoa(d.Get("lb_id").(int))) @@ -141,7 +159,7 @@ func resourceLBFrontendBindEdit(ctx context.Context, d *schema.ResourceData, m i urlValues.Add("bindingPort", strconv.Itoa(d.Get("port").(int))) } - _, err := c.DecortAPICall(ctx, "POST", lbFrontendBindUpdateAPI, urlValues) + _, err = c.DecortAPICall(ctx, "POST", lbFrontendBindUpdateAPI, urlValues) if err != nil { return diag.FromErr(err) } @@ -155,7 +173,7 @@ func ResourceLBFrontendBind() *schema.Resource { CreateContext: resourceLBFrontendBindCreate, ReadContext: resourceLBFrontendBindRead, - UpdateContext: resourceLBFrontendBindEdit, + UpdateContext: resourceLBFrontendBindUpdate, DeleteContext: resourceLBFrontendBindDelete, Importer: &schema.ResourceImporter{ diff --git a/internal/service/cloudapi/rg/resource_check_input_values.go b/internal/service/cloudapi/rg/resource_check_input_values.go index 0b51433..eb06168 100644 --- a/internal/service/cloudapi/rg/resource_check_input_values.go +++ b/internal/service/cloudapi/rg/resource_check_input_values.go @@ -74,6 +74,7 @@ func existAccountID(ctx context.Context, d *schema.ResourceData, m interface{}) } return haveAccount, nil } + func existGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { c := m.(*controller.ControllerCfg) @@ -107,6 +108,7 @@ func existGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, return haveGID, nil } + func existExtNetID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { c := m.(*controller.ControllerCfg) diff --git a/internal/service/cloudapi/rg/resource_rg.go b/internal/service/cloudapi/rg/resource_rg.go index d8d414b..4263b58 100644 --- a/internal/service/cloudapi/rg/resource_rg.go +++ b/internal/service/cloudapi/rg/resource_rg.go @@ -52,13 +52,8 @@ import ( ) func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - // First validate that we have all parameters required to create the new Resource Group - - // Valid account ID is required to create new resource group - // obtain Account ID by account name - it should not be zero on success - - rgName, argSet := d.GetOk("name") - if !argSet { + rgName, ok := d.GetOk("name") + if !ok { return diag.FromErr(fmt.Errorf("Cannot create new RG: missing name.")) } @@ -76,43 +71,39 @@ func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m inter } */ - // all required parameters are set in the schema - we can continue with RG creation log.Debugf("resourceResgroupCreate: called for RG name %s, account ID %d", rgName.(string), d.Get("account_id").(int)) - // Check input values - // AccountID haveAccount, err := existAccountID(ctx, d, m) if err != nil { return diag.FromErr(err) } if !haveAccount { - return diag.Errorf("resourceResgroupCreate: can't create RG bacause AccountID %d not allowed or does not exist", d.Get("account_id").(int)) + return diag.Errorf("resourceResgroupCreate: can't create RG because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) } - // GID + haveGID, err := existGID(ctx, d, m) if err != nil { return diag.FromErr(err) } if !haveGID { - return diag.Errorf("resourceResgroupCreate: can't create RG bacause GID %d not allowed or does not exist", d.Get("gid").(int)) + return diag.Errorf("resourceResgroupCreate: can't create RG because GID %d is not allowed or does not exist", d.Get("gid").(int)) } - // ExtNetID + if _, ok := d.GetOk("ext_net_id"); ok { haveExtNet, err := existExtNetID(ctx, d, m) if err != nil { return diag.FromErr(err) } if !haveExtNet { - return diag.Errorf("resourceResgroupCreate: can't create RG bacause ExtNetID %d not allowed or does not exist", d.Get("ext_net_id").(int)) + return diag.Errorf("resourceResgroupCreate: can't create RG because ExtNetID %d is not allowed or does not exist", d.Get("ext_net_id").(int)) } } - // quota settings are optional setQuota := false var quotaRecord QuotaRecord - argValue, argSet := d.GetOk("quota") - if argSet { + argValue, ok := d.GetOk("quota") + if ok { log.Debugf("resourceResgroupCreate: setting Quota on RG requested") quotaRecord = makeQuotaRecord(argValue.([]interface{})) setQuota = true @@ -125,53 +116,51 @@ func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m inter urlValues = &url.Values{} urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) urlValues.Add("name", rgName.(string)) - urlValues.Add("gid", strconv.Itoa(location.DefaultGridID)) // use default Grid ID, similar to disk resource mgmt convention + urlValues.Add("gid", strconv.Itoa(location.DefaultGridID)) urlValues.Add("owner", c.GetDecortUsername()) - // pass quota values as set if setQuota { urlValues.Add("maxCPUCapacity", strconv.Itoa(quotaRecord.Cpu)) urlValues.Add("maxVDiskCapacity", strconv.Itoa(quotaRecord.Disk)) - urlValues.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotaRecord.Ram)) // RAM quota is float; this may change in the future + urlValues.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotaRecord.Ram)) urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(quotaRecord.ExtTraffic)) urlValues.Add("maxNumPublicIP", strconv.Itoa(quotaRecord.ExtIPs)) } - // parse and handle network settings - defNetType, argSet := d.GetOk("def_net_type") - if argSet { + defNetType, ok := d.GetOk("def_net_type") + if ok { urlValues.Add("def_net", defNetType.(string)) // NOTE: in API default network type is set by "def_net" parameter } else { d.Set("def_net_type", "PRIVATE") } - ipcidr, argSet := d.GetOk("ipcidr") - if argSet { + ipcidr, ok := d.GetOk("ipcidr") + if ok { urlValues.Add("ipcidr", ipcidr.(string)) } - description, argSet := d.GetOk("description") - if argSet { + description, ok := d.GetOk("description") + if ok { urlValues.Add("desc", description.(string)) } - reason, argSet := d.GetOk("reason") - if argSet { + reason, ok := d.GetOk("reason") + if ok { urlValues.Add("reason", reason.(string)) } - extNetId, argSet := d.GetOk("ext_net_id") - if argSet { + extNetId, ok := d.GetOk("ext_net_id") + if ok { urlValues.Add("extNetId", strconv.Itoa(extNetId.(int))) } - extIp, argSet := d.GetOk("ext_ip") - if argSet { + extIp, ok := d.GetOk("ext_ip") + if ok { urlValues.Add("extIp", extIp.(string)) } - regComputes, argSet := d.GetOk("register_computes") - if argSet { + regComputes, ok := d.GetOk("register_computes") + if ok { urlValues.Add("registerComputes", strconv.FormatBool(regComputes.(bool))) } @@ -179,7 +168,8 @@ func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m inter if err != nil { return diag.FromErr(err) } - d.SetId(apiResp) // rg/create API returns ID of the newly creted resource group on success + + d.SetId(apiResp) w := dc.Warnings{} if access, ok := d.GetOk("access"); ok { @@ -257,7 +247,6 @@ func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m inter } } - // re-read newly created RG to make sure schema contains complete and up to date set of specifications defer resourceResgroupRead(ctx, d, m) return w.Get() } @@ -268,23 +257,34 @@ func resourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interfa c := m.(*controller.ControllerCfg) - rgFacts, err := utilityResgroupCheckPresence(ctx, d, m) + rg, err := utilityResgroupCheckPresence(ctx, d, m) if err != nil { - d.SetId("") // ensure ID is empty + d.SetId("") return diag.FromErr(err) } - switch rgFacts.Status { + hasChanged := false + + switch rg.Status { case status.Modeled: + return diag.Errorf("The resource group is in status: %s, please, contact support for more information", rg.Status) case status.Created: case status.Enabled: case status.Deleted: urlValues := &url.Values{} urlValues.Add("rgId", d.Id()) + _, err := c.DecortAPICall(ctx, "POST", RgRestoreAPI, urlValues) if err != nil { return diag.FromErr(err) } + _, err = c.DecortAPICall(ctx, "POST", RgEnableAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true + case status.Deleting: case status.Destroyed: d.SetId("") @@ -296,12 +296,14 @@ func resourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interfa case status.Enabling: } - rgFacts, err = utilityResgroupCheckPresence(ctx, d, m) - if err != nil { - d.SetId("") // ensure ID is empty - return diag.FromErr(err) + if hasChanged { + rg, err = utilityResgroupCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } } - return diag.FromErr(flattenResgroup(d, *rgFacts)) + return diag.FromErr(flattenResgroup(d, *rg)) } func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -311,8 +313,6 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter c := m.(*controller.ControllerCfg) urlValues := &url.Values{} - // Check input values - // AccountID haveAccount, err := existAccountID(ctx, d, m) if err != nil { return diag.FromErr(err) @@ -320,7 +320,7 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter if !haveAccount { return diag.Errorf("resourceResgroupUpdate: can't create RG bacause AccountID %d not allowed or does not exist", d.Get("account_id").(int)) } - // GID + haveGID, err := existGID(ctx, d, m) if err != nil { return diag.FromErr(err) @@ -328,7 +328,7 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter if !haveGID { return diag.Errorf("resourceResgroupUpdate: can't create RG bacause GID %d not allowed or does not exist", d.Get("gid").(int)) } - // ExtNetID + if _, ok := d.GetOk("ext_net_id"); ok { haveExtNet, err := existExtNetID(ctx, d, m) if err != nil { @@ -341,10 +341,12 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter rgFacts, err := utilityResgroupCheckPresence(ctx, d, m) if err != nil { - d.SetId("") // ensure ID is empty + d.SetId("") return diag.FromErr(err) } + hasChanged := false + switch rgFacts.Status { case status.Modeled: case status.Created: @@ -356,6 +358,12 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter if err != nil { return diag.FromErr(err) } + _, err = c.DecortAPICall(ctx, "POST", RgEnableAPI, urlValues) + if err != nil { + return diag.FromErr(err) + } + + hasChanged = true case status.Deleting: case status.Destroyed: d.SetId("") @@ -366,6 +374,14 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter case status.Enabled: case status.Enabling: } + + if hasChanged { + rgFacts, err = utilityDataResgroupCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } /* NOTE: we do not allow changing the following attributes of an existing RG via terraform: - def_net_type - ipcidr @@ -393,7 +409,7 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter return diag.FromErr(fmt.Errorf("resourceResgroupUpdate: RG ID %s: changing ext_net_id for existing RG is not allowed", d.Id())) } - doGeneralUpdate := false // will be true if general RG update is necessary (API rg/update) + doGeneralUpdate := false urlValues = &url.Values{} urlValues.Add("rgId", d.Id()) @@ -412,7 +428,7 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter if quotaSet { log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.") quotarecordNew := makeQuotaRecord(quotaValue.([]interface{})) - quotaValueOld, _ := d.GetChange("quota") // returns old as 1st, new as 2nd return value + quotaValueOld, _ := d.GetChange("quota") quotarecordOld := makeQuotaRecord(quotaValueOld.([]interface{})) log.Debug(quotaValueOld, quotarecordNew) @@ -428,7 +444,7 @@ func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m inter urlValues.Add("maxVDiskCapacity", strconv.Itoa(quotarecordNew.Disk)) } - if quotarecordNew.Ram != quotarecordOld.Ram { // NB: quota on RAM is stored as float32, in units of MB + if quotarecordNew.Ram != quotarecordOld.Ram { doGeneralUpdate = true log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecordNew.Ram, quotarecordOld.Ram) urlValues.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecordNew.Ram)) @@ -605,7 +621,7 @@ func ResourceRgSchemaMake() map[string]*schema.Schema { "gid": { Type: schema.TypeInt, Required: true, - ForceNew: true, // change of Grid ID will require new RG + ForceNew: true, Description: "Unique ID of the grid, where this resource group is deployed.", }, @@ -816,7 +832,7 @@ func ResourceRgSchemaMake() map[string]*schema.Schema { Computed: true, }, "vins": { - Type: schema.TypeList, //this is a list of ints + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeInt, @@ -825,7 +841,7 @@ func ResourceRgSchemaMake() map[string]*schema.Schema { }, "vms": { - Type: schema.TypeList, //t his is a list of ints + Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeInt, diff --git a/internal/service/cloudapi/vins/resource_check_input_values.go b/internal/service/cloudapi/vins/resource_check_input_values.go new file mode 100644 index 0000000..4a67915 --- /dev/null +++ b/internal/service/cloudapi/vins/resource_check_input_values.go @@ -0,0 +1,146 @@ +package vins + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" +) + +func existRGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + rgList := []struct { + ID int `json:"id"` + }{} + + rgListAPI := "/restmachine/cloudapi/rg/list" + + rgListRaw, err := c.DecortAPICall(ctx, "POST", rgListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(rgListRaw), &rgList) + if err != nil { + return false, err + } + + haveRG := false + rgId := d.Get("rg_id").(int) + for _, rg := range rgList { + if rg.ID == rgId { + haveRG = true + break + } + } + + return haveRG, nil +} + +func existExtNetID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + extNetID := d.Get("ext_net_id").(int) + + if extNetID == 0 || extNetID == -1 { + return true, nil + } + + c := m.(*controller.ControllerCfg) + urlValues := &url.Values{} + + extNetList := []struct { + ID int `json:"id"` + }{} + + extNetListAPI := "/restmachine/cloudapi/extnet/list" + + extNetListRaw, err := c.DecortAPICall(ctx, "POST", extNetListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(extNetListRaw), &extNetList) + if err != nil { + return false, err + } + + haveExtNet := false + for _, extNet := range extNetList { + if extNet.ID == extNetID { + haveExtNet = true + break + } + } + + return haveExtNet, nil +} + +func existAccountID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + accountList := []struct { + ID int `json:"id"` + }{} + + accountListAPI := "/restmachine/cloudapi/account/list" + + accountListRaw, err := c.DecortAPICall(ctx, "POST", accountListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(accountListRaw), &accountList) + if err != nil { + return false, err + } + + haveAccount := false + + myAccount := d.Get("account_id").(int) + for _, account := range accountList { + if account.ID == myAccount { + haveAccount = true + break + } + } + return haveAccount, nil +} + +func existGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) { + c := m.(*controller.ControllerCfg) + + urlValues := &url.Values{} + + locationList := []struct { + GID int `json:"gid"` + }{} + + locationsListAPI := "/restmachine/cloudapi/locations/list" + + locationListRaw, err := c.DecortAPICall(ctx, "POST", locationsListAPI, urlValues) + if err != nil { + return false, err + } + + err = json.Unmarshal([]byte(locationListRaw), &locationList) + if err != nil { + return false, err + } + + haveGID := false + + gid := d.Get("gid").(int) + for _, location := range locationList { + if location.GID == gid { + haveGID = true + break + } + } + + return haveGID, nil +} diff --git a/internal/service/cloudapi/vins/resource_vins.go b/internal/service/cloudapi/vins/resource_vins.go index cab1842..9205cef 100644 --- a/internal/service/cloudapi/vins/resource_vins.go +++ b/internal/service/cloudapi/vins/resource_vins.go @@ -38,11 +38,11 @@ import ( "net/url" "strconv" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/status" - log "github.com/sirupsen/logrus" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -53,6 +53,50 @@ func resourceVinsCreate(ctx context.Context, d *schema.ResourceData, m interface c := m.(*controller.ControllerCfg) urlValues := &url.Values{} + if _, ok := d.GetOk("rg_id"); ok { + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceVinsCreate: can't create ViNS because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + } + + if _, ok := d.GetOk("ext_net_id"); ok { + haveExtNetID, err := existExtNetID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveExtNetID { + return diag.Errorf("resourceVinsCreate: can't create ViNS because ExtNetID %d is not allowed or does not exist", d.Get("ext_net_id").(int)) + } + } + + if _, ok := d.GetOk("account_id"); ok { + haveAccountID, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveAccountID { + return diag.Errorf("resourceVinsCreate: can't create ViNS because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + } + + if _, ok := d.GetOk("gid"); ok { + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveGID { + return diag.Errorf("resourceVinsCreate: can't create ViNS because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + } + rgId, rgOk := d.GetOk("rg_id") accountId, accountIdOk := d.GetOk("account_id") if !rgOk && !accountIdOk { @@ -164,7 +208,6 @@ func resourceVinsCreate(ctx context.Context, d *schema.ResourceData, m interface func resourceVinsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { c := m.(*controller.ControllerCfg) - urlValues := &url.Values{} warnings := dc.Warnings{} vins, err := utilityVinsCheckPresence(ctx, d, m) @@ -173,40 +216,58 @@ func resourceVinsRead(ctx context.Context, d *schema.ResourceData, m interface{} return diag.FromErr(err) } + isEnabled := d.Get("enable").(bool) + hasChangeState := false - if vins.Status == status.Destroyed { + + switch vins.Status { + case status.Destroyed: d.SetId("") d.Set("vins_id", 0) return resourceVinsCreate(ctx, d, m) - } else if vins.Status == status.Deleted { + case status.Deleted: hasChangeState = true + urlValues := &url.Values{} urlValues.Add("vinsId", d.Id()) + _, err := c.DecortAPICall(ctx, "POST", VinsRestoreAPI, urlValues) if err != nil { warnings.Add(err) } - } - urlValues = &url.Values{} + case status.Modeled: + return diag.Errorf("ViNS are in status: %s, please, contact support for more information", vins.Status) + case status.Created: + case status.Enabled: + if !isEnabled { + hasChangeState = true + urlValues := &url.Values{} - isEnabled := d.Get("enable").(bool) - if vins.Status == status.Disabled && isEnabled { - hasChangeState = true + urlValues.Add("vinsId", d.Id()) - urlValues.Add("vinsId", d.Id()) - _, err := c.DecortAPICall(ctx, "POST", VinsEnableAPI, urlValues) - if err != nil { - warnings.Add(err) + _, err := c.DecortAPICall(ctx, "POST", VinsDisableAPI, urlValues) + if err != nil { + warnings.Add(err) + } } - } else if vins.Status == status.Enabled && !isEnabled { - hasChangeState = true + case status.Enabling: + case status.Disabled: + if isEnabled { + hasChangeState = true + urlValues := &url.Values{} - urlValues.Add("vinsId", d.Id()) - _, err := c.DecortAPICall(ctx, "POST", VinsDisableAPI, urlValues) - if err != nil { - warnings.Add(err) + urlValues.Add("vinsId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", VinsEnableAPI, urlValues) + if err != nil { + warnings.Add(err) + } } + case status.Disabling: + case status.Deleting: + return diag.Errorf("ViNS are in progress with status: %s", vins.Status) } + if hasChangeState { vins, err = utilityVinsCheckPresence(ctx, d, m) if err != nil { @@ -216,6 +277,7 @@ func resourceVinsRead(ctx context.Context, d *schema.ResourceData, m interface{} } flattenVins(d, *vins) + return warnings.Get() } @@ -245,18 +307,131 @@ func isContinsNatRule(els []interface{}, el interface{}) bool { func resourceVinsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { c := m.(*controller.ControllerCfg) - urlValues := &url.Values{} warnings := dc.Warnings{} + if _, ok := d.GetOk("rg_id"); ok { + haveRGID, err := existRGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveRGID { + return diag.Errorf("resourceVinsUpdate: can't update ViNS because RGID %d is not allowed or does not exist", d.Get("rg_id").(int)) + } + } + + if _, ok := d.GetOk("ext_net_id"); ok { + haveExtNetID, err := existExtNetID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveExtNetID { + return diag.Errorf("resourceVinsUpdate: can't update ViNS because ExtNetID %d is not allowed or does not exist", d.Get("ext_net_id").(int)) + } + } + + if _, ok := d.GetOk("account_id"); ok { + haveAccountID, err := existAccountID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveAccountID { + return diag.Errorf("resourceVinsUpdate: can't update ViNS because AccountID %d is not allowed or does not exist", d.Get("account_id").(int)) + } + } + + if _, ok := d.GetOk("gid"); ok { + haveGID, err := existGID(ctx, d, m) + if err != nil { + return diag.FromErr(err) + } + + if !haveGID { + return diag.Errorf("resourceVinsUpdate: can't update ViNS because GID %d is not allowed or does not exist", d.Get("gid").(int)) + } + } + + vins, err := utilityVinsCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + isEnabled := d.Get("enable").(bool) + + hasChangeState := false + + switch vins.Status { + case status.Destroyed: + d.SetId("") + return resourceVinsCreate(ctx, d, m) + case status.Deleted: + hasChangeState = true + urlValues := &url.Values{} + + urlValues.Add("vinsId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", VinsRestoreAPI, urlValues) + if err != nil { + warnings.Add(err) + } + case status.Modeled: + return diag.Errorf("ViNS are in status: %s, please, contact support for more information", vins.Status) + case status.Created: + case status.Enabled: + if !isEnabled { + hasChangeState = true + urlValues := &url.Values{} + + urlValues.Add("vinsId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", VinsDisableAPI, urlValues) + if err != nil { + warnings.Add(err) + } + } + case status.Enabling: + case status.Disabled: + if isEnabled { + hasChangeState = true + urlValues := &url.Values{} + + urlValues.Add("vinsId", d.Id()) + + _, err := c.DecortAPICall(ctx, "POST", VinsEnableAPI, urlValues) + if err != nil { + warnings.Add(err) + } + } + case status.Disabling: + case status.Deleting: + return diag.Errorf("ViNS are in progress with status: %s", vins.Status) + } + + if hasChangeState { + vins, err = utilityVinsCheckPresence(ctx, d, m) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + } + urlValues := &url.Values{} + enableOld, enableNew := d.GetChange("enable") if enableOld.(bool) && !enableNew.(bool) { - urlValues.Add("vinsId", d.Id()) + if !urlValues.Has("vinsId") { + urlValues.Add("vinsId", d.Id()) + } _, err := c.DecortAPICall(ctx, "POST", VinsDisableAPI, urlValues) if err != nil { warnings.Add(err) } } else if !enableOld.(bool) && enableNew.(bool) { - urlValues.Add("vinsId", d.Id()) + if !urlValues.Has("vinsId") { + urlValues.Add("vinsId", d.Id()) + } _, err := c.DecortAPICall(ctx, "POST", VinsEnableAPI, urlValues) if err != nil { warnings.Add(err) @@ -532,6 +707,7 @@ func resourceVinsSchemaMake() map[string]*schema.Schema { } rets["rg_id"] = &schema.Schema{ Type: schema.TypeInt, + Computed: true, Optional: true, } rets["account_id"] = &schema.Schema{ diff --git a/internal/service/cloudbroker/k8s/resource_k8s_wg.go b/internal/service/cloudbroker/k8s/resource_k8s_wg.go index 97466b5..b8c69fe 100644 --- a/internal/service/cloudbroker/k8s/resource_k8s_wg.go +++ b/internal/service/cloudbroker/k8s/resource_k8s_wg.go @@ -38,9 +38,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller" - log "github.com/sirupsen/logrus" ) func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -62,35 +62,6 @@ func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interfac d.SetId(resp) - // This code is the supposed flow, but at the time of writing it's not yet implemented by the platfom - - //urlValues = &url.Values{} - //urlValues.Add("auditId", strings.Trim(resp, `"`)) - - //for { - //resp, err := controller.decortAPICall("POST", AsyncTaskGetAPI, urlValues) - //if err != nil { - //return err - //} - - //task := AsyncTask{} - //if err := json.Unmarshal([]byte(resp), &task); err != nil { - //return err - //} - //log.Debugf("resourceK8sCreate: workers group creating - %s", task.Stage) - - //if task.Completed { - //if task.Error != "" { - //return fmt.Errorf("cannot create workers group: %v", task.Error) - //} - - //d.SetId(strconv.Itoa(int(task.Result))) - //break - //} - - //time.Sleep(time.Second * 5) - //} - return nil } diff --git a/internal/status/status.go b/internal/status/status.go index 31a0ff4..9fb4f8d 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -35,6 +35,11 @@ package status type Status = string var ( + // An object is Confirmed + // Status available for: + // - Account + Confirmed Status = "CONFIRMED" + // The disk is linked to any Compute // Status available for: // - Disk @@ -45,12 +50,18 @@ var ( // - Compute // - Disk // - Vins + // - BasicService + // - K8s Cluster + // - Load Balancer Enabled Status = "ENABLED" // Enabling in process // Status available for: // - Disk // - Vins + // - BasicService + // - K8s Cluster + // - Load Balancer Enabling Status = "ENABLING" // An object disabled for operations @@ -58,12 +69,19 @@ var ( // - Compute // - Disk // - Vins + // - Account + // - BasicService + // - K8s Cluster + // - Load Balancer Disabled Status = "DISABLED" // Disabling in process // Status available for: // - Disk // - Vins + // - BasicService + // - K8s Cluster + // - Load Balancer Disabling Status = "DISABLING" // An object model has been created in the database @@ -72,11 +90,17 @@ var ( // - Disk // - Compute // - Vins + // - BasicService + // - K8s Cluster + // - Load Balancer Modeled Status = "MODELED" // In the process of creation // Status available for: // - Image + // - Disk + // - K8s Cluster + // - Load Balancer Creating Status = "CREATING" // An object was created successfully @@ -85,16 +109,21 @@ var ( // - Disk // - Compute // - Vins + // - K8s Cluster + // - BasicService + // - Load Balancer Created Status = "CREATED" // Physical resources are allocated for the object // Status available for: // - Compute + // - Disk Allocated Status = "ALLOCATED" // The object has released (returned to the platform) the physical resources that it occupied // Status available for: // - Compute + // - Disk Unallocated Status = "UNALLOCATED" // Destroying in progress @@ -102,6 +131,10 @@ var ( // - Disk // - Compute // - Vins + // - Account + // - BasicService + // - K8s Cluster + // - Load Balancer Destroying Status = "DESTROYING" // Permanently deleted @@ -110,23 +143,36 @@ var ( // - Disk // - Compute // - Vins + // - Account + // - BasicService + // - K8s Cluster + // - Load Balancer Destroyed Status = "DESTROYED" // Deleting in progress to Trash // Status available for: // - Compute // - Vins + // - BasicService + // - K8s Cluster + // - Load Balancer Deleting Status = "DELETING" // Deleted to Trash // Status available for: // - Compute // - Vins + // - Account + // - BasicService + // - Disk + // - K8s Cluster + // - Load Balancer Deleted Status = "DELETED" // Deleted from storage // Status available for: // - Image + // - Disk Purged Status = "PURGED" // Repeating deploy of the object in progress @@ -138,4 +184,16 @@ var ( // Status available for: // - vins vnf Stashed Status = "STASHED" + + // Object is in restoration process + // Status available for: + // - BasicService + // - K8s Cluster + // - Load Balancer + Restoring Status = "RESTORING" + + // Object is in reconfiguration process + // Status available for: + // - BasicService + Reconfiguring Status = "RECONFIGURING" )