From 6932f9d3055071c6d2d4106393aadf7c90c1d440 Mon Sep 17 00:00:00 2001 From: Nikita Sorokin Date: Mon, 28 Aug 2023 13:02:41 +0300 Subject: [PATCH] 4.3.5 --- CHANGELOG.md | 6 +- Makefile | 2 +- .../cloudapi/kvmvm/data_source_compute.go | 8 +- .../kvmvm/data_source_compute_list.go | 8 +- internal/service/cloudapi/kvmvm/flattens.go | 81 +-- .../service/cloudapi/kvmvm/old_schemas.go | 550 ++++++++++++++++++ .../cloudapi/kvmvm/resource_compute.go | 61 +- .../service/cloudapi/kvmvm/state_upgraders.go | 33 ++ .../service/cloudapi/kvmvm/utility_compute.go | 4 +- 9 files changed, 695 insertions(+), 58 deletions(-) create mode 100644 internal/service/cloudapi/kvmvm/old_schemas.go create mode 100644 internal/service/cloudapi/kvmvm/state_upgraders.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e32b4de..b9cbe92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Version 4.3.4 +## Version 4.3.5 -## Bugfixes -- Fixed bug with resource decort_cb_extnet: extnet did not switct status to "ENABLED" if field enable=true while resource create. \ No newline at end of file +## Feature +- Added state upgrader for custom_fields field upgrade in kvmvm resource \ No newline at end of file diff --git a/Makefile b/Makefile index 2c11788..9936cf0 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ ZIPDIR = ./zip BINARY=${NAME} WORKPATH= ./examples/terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAMESPACE}/${VERSION}/${OS_ARCH} MAINPATH = ./cmd/decort/ -VERSION=4.3.4 +VERSION=4.3.5 OS_ARCH=$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH) FILES = ${BINARY}_${VERSION}_darwin_amd64\ diff --git a/internal/service/cloudapi/kvmvm/data_source_compute.go b/internal/service/cloudapi/kvmvm/data_source_compute.go index c0307d8..0901174 100644 --- a/internal/service/cloudapi/kvmvm/data_source_compute.go +++ b/internal/service/cloudapi/kvmvm/data_source_compute.go @@ -637,10 +637,10 @@ func dataSourceComputeSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, Computed: true, }, - // "custom_fields": { - // Type: schema.TypeString, - // Computed: true, - // }, + "custom_fields": { + Type: schema.TypeString, + Computed: true, + }, "deleted_by": { Type: schema.TypeString, Computed: true, diff --git a/internal/service/cloudapi/kvmvm/data_source_compute_list.go b/internal/service/cloudapi/kvmvm/data_source_compute_list.go index 04bfde8..32f38e4 100644 --- a/internal/service/cloudapi/kvmvm/data_source_compute_list.go +++ b/internal/service/cloudapi/kvmvm/data_source_compute_list.go @@ -154,10 +154,10 @@ func itemComputeSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, Computed: true, }, - // "custom_fields": { //NEED - // Type: schema.TypeString, - // Computed: true, - // }, + "custom_fields": { //NEED + Type: schema.TypeString, + Computed: true, + }, "deleted_by": { Type: schema.TypeString, Computed: true, diff --git a/internal/service/cloudapi/kvmvm/flattens.go b/internal/service/cloudapi/kvmvm/flattens.go index 5b007f8..974caf7 100644 --- a/internal/service/cloudapi/kvmvm/flattens.go +++ b/internal/service/cloudapi/kvmvm/flattens.go @@ -150,7 +150,7 @@ func flattenListACL(listAcl compute.ListACL) []map[string]interface{} { func flattenComputeList(computes *compute.ListComputes) []map[string]interface{} { res := make([]map[string]interface{}, 0) for _, compute := range computes.Data { - // customFields, _ := json.Marshal(compute.CustomFields) + customFields, _ := json.Marshal(compute.CustomFields) devices, _ := json.Marshal(compute.Devices) temp := map[string]interface{}{ "acl": flattenListACL(compute.ACL), @@ -169,44 +169,44 @@ func flattenComputeList(computes *compute.ListComputes) []map[string]interface{} "cpus": compute.CPU, "created_by": compute.CreatedBy, "created_time": compute.CreatedTime, - // "custom_fields": string(customFields), - "deleted_by": compute.DeletedBy, - "deleted_time": compute.DeletedTime, - "desc": compute.Description, - "devices": string(devices), - "disks": flattenDisks(compute.Disks), - "driver": compute.Driver, - "gid": compute.GID, - "guid": compute.GUID, - "compute_id": compute.ID, - "image_id": compute.ImageID, - "interfaces": flattenInterfaces(compute.Interfaces), - "lock_status": compute.LockStatus, - "manager_id": compute.ManagerID, - "manager_type": compute.ManagerType, - "migrationjob": compute.MigrationJob, - "milestones": compute.Milestones, - "name": compute.Name, - "pinned": compute.Pinned, - "ram": compute.RAM, - "reference_id": compute.ReferenceID, - "registered": compute.Registered, - "res_name": compute.ResName, - "rg_id": compute.RGID, - "rg_name": compute.RGName, - "snap_sets": flattenSnapSets(compute.SnapSets), - "stateless_sep_id": compute.StatelessSepID, - "stateless_sep_type": compute.StatelessSepType, - "status": compute.Status, - "tags": flattenTags(compute.Tags), - "tech_status": compute.TechStatus, - "total_disk_size": compute.TotalDiskSize, - "updated_by": compute.UpdatedBy, - "updated_time": compute.UpdatedTime, - "user_managed": compute.UserManaged, - "vgpus": compute.VGPUs, - "vins_connected": compute.VINSConnected, - "virtual_image_id": compute.VirtualImageID, + "custom_fields": string(customFields), + "deleted_by": compute.DeletedBy, + "deleted_time": compute.DeletedTime, + "desc": compute.Description, + "devices": string(devices), + "disks": flattenDisks(compute.Disks), + "driver": compute.Driver, + "gid": compute.GID, + "guid": compute.GUID, + "compute_id": compute.ID, + "image_id": compute.ImageID, + "interfaces": flattenInterfaces(compute.Interfaces), + "lock_status": compute.LockStatus, + "manager_id": compute.ManagerID, + "manager_type": compute.ManagerType, + "migrationjob": compute.MigrationJob, + "milestones": compute.Milestones, + "name": compute.Name, + "pinned": compute.Pinned, + "ram": compute.RAM, + "reference_id": compute.ReferenceID, + "registered": compute.Registered, + "res_name": compute.ResName, + "rg_id": compute.RGID, + "rg_name": compute.RGName, + "snap_sets": flattenSnapSets(compute.SnapSets), + "stateless_sep_id": compute.StatelessSepID, + "stateless_sep_type": compute.StatelessSepType, + "status": compute.Status, + "tags": flattenTags(compute.Tags), + "tech_status": compute.TechStatus, + "total_disk_size": compute.TotalDiskSize, + "updated_by": compute.UpdatedBy, + "updated_time": compute.UpdatedTime, + "user_managed": compute.UserManaged, + "vgpus": compute.VGPUs, + "vins_connected": compute.VINSConnected, + "virtual_image_id": compute.VirtualImageID, } res = append(res, temp) } @@ -316,6 +316,7 @@ func flattenCompute(d *schema.ResourceData, computeRec compute.RecordCompute) er d.Set("computeci_id", computeRec.ComputeCIID) d.Set("created_by", computeRec.CreatedBy) d.Set("created_time", computeRec.CreatedTime) + // d.Set("custom_fields", flattenCustomFields(computeRec.CustomFields)) d.Set("deleted_by", computeRec.DeletedBy) d.Set("deleted_time", computeRec.DeletedTime) d.Set("description", computeRec.Description) @@ -541,7 +542,7 @@ func flattenDataCompute(d *schema.ResourceData, computeRec compute.RecordCompute d.Set("cpus", computeRec.CPU) d.Set("created_by", computeRec.CreatedBy) d.Set("created_time", computeRec.CreatedTime) - // d.Set("custom_fields", flattenCustomFields(computeRec.CustomFields)) + d.Set("custom_fields", flattenCustomFields(computeRec.CustomFields)) d.Set("deleted_by", computeRec.DeletedBy) d.Set("deleted_time", computeRec.DeletedTime) d.Set("desc", computeRec.Description) diff --git a/internal/service/cloudapi/kvmvm/old_schemas.go b/internal/service/cloudapi/kvmvm/old_schemas.go new file mode 100644 index 0000000..945a369 --- /dev/null +++ b/internal/service/cloudapi/kvmvm/old_schemas.go @@ -0,0 +1,550 @@ +package kvmvm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants" + "repository.basistech.ru/BASIS/terraform-provider-decort/internal/statefuncs" +) + +func resourceComputeResourceV1() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of this compute. Compute names are case sensitive and must be unique in the resource group.", + }, + "rg_id": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + Description: "ID of the resource group where this compute should be deployed.", + }, + "driver": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + StateFunc: statefuncs.StateFuncToUpper, + ValidateFunc: validation.StringInSlice([]string{"SVA_KVM_X86", "KVM_X86", "KVM_PPC"}, false), // observe case while validating + Description: "Hardware architecture of this compute instance.", + }, + "cpu": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, constants.MaxCpusPerCompute), + Description: "Number of CPUs to allocate to this compute instance.", + }, + "ram": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(constants.MinRamPerCompute), + Description: "Amount of RAM in MB to allocate to this compute instance.", + }, + "image_id": { + Type: schema.TypeInt, + Required: true, + //ForceNew: true, //REDEPLOY + Description: "ID of the OS image to base this compute instance on.", + }, + "boot_disk_size": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "This compute instance boot disk size in GB. Make sure it is large enough to accomodate selected OS image.", + }, + "affinity_label": { + Type: schema.TypeString, + Optional: true, + Description: "Set affinity label for compute", + }, + "affinity_rules": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "topology": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"node", "compute"}, false), + Description: "compute or node, for whom rule applies", + }, + "policy": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"RECOMMENDED", "REQUIRED"}, false), + Description: "RECOMMENDED or REQUIRED, the degree of 'strictness' of this rule", + }, + "mode": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"EQ", "NE", "ANY"}, false), + Description: "EQ or NE or ANY - the comparison mode is 'value', recorded by the specified 'key'", + }, + "key": { + Type: schema.TypeString, + Required: true, + Description: "key that are taken into account when analyzing this rule will be identified", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "value that must match the key to be taken into account when analyzing this rule", + }, + }, + }, + }, + "anti_affinity_rules": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "topology": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"node", "compute"}, false), + Description: "compute or node, for whom rule applies", + }, + "policy": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"RECOMMENDED", "REQUIRED"}, false), + Description: "RECOMMENDED or REQUIRED, the degree of 'strictness' of this rule", + }, + "mode": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"EQ", "NE", "ANY"}, false), + Description: "EQ or NE or ANY - the comparison mode is 'value', recorded by the specified 'key'", + }, + "key": { + Type: schema.TypeString, + Required: true, + Description: "key that are taken into account when analyzing this rule will be identified", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "value that must match the key to be taken into account when analyzing this rule", + }, + }, + }, + }, + "disks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: disksSubresourceSchemaMake(), + }, + }, + "custom_fields": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Computed: true, + }, + "val": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "stateless": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Compute will be stateless (SVA_KVM_X86) if set to True", + }, + "with_default_vins": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Create compute with default resgroup ViNS (true) or without any interfaces (false). This parameter is ignored if network block is specified", + }, + "boot_disk": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: disksSubresourceSchemaMake(), + }, + }, + "sep_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + Description: "ID of SEP to create bootDisk on. Uses image's sepId if not set.", + }, + "pool": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "Pool to use if sepId is set, can be also empty if needed to be chosen by system.", + }, + + "extra_disks": { + Type: schema.TypeSet, + Optional: true, + MaxItems: constants.MaxExtraDisksPerCompute, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + Description: "Optional list of IDs of extra disks to attach to this compute. You may specify several extra disks.", + }, + + "network": { + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + MaxItems: constants.MaxNetworksPerCompute, + Elem: &schema.Resource{ + Schema: networkSubresourceSchemaMake(), + }, + Description: "Optional network connection(s) for this compute. You may specify several network blocks, one for each connection.", + }, + + "tags": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: tagsSubresourceSchemaMake(), + }, + }, + + "port_forwarding": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: portForwardingSubresourceSchemaMake(), + }, + }, + + "user_access": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: userAccessSubresourceSchemaMake(), + }, + }, + + "snapshot": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: snapshotSubresourceSchemaMake(), + }, + }, + + "rollback": { + Type: schema.TypeSet, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: snapshotRollbackSubresourceSchemaMake(), + }, + }, + + "cd": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: cdSubresourceSchemaMake(), + }, + }, + + "pin_to_stack": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + Description: "Optional text description of this compute instance.", + }, + + "cloud_init": { + Type: schema.TypeString, + Optional: true, + Description: "Optional cloud_init parameters. Applied when creating new compute instance only, ignored in all other cases.", + }, + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "If true - enable compute, else - disable", + }, + + "pause": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "reset": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "auto_start": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Flag for redeploy compute", + }, + "force_stop": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Flag for redeploy compute", + }, + "data_disks": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"KEEP", "DETACH", "DESTROY"}, false), + Default: "DETACH", + Description: "Flag for redeploy compute", + }, + "started": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Is compute started.", + }, + "detach_disks": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "is": { + Type: schema.TypeString, + Optional: true, + Description: "system name", + }, + "ipa_type": { + Type: schema.TypeString, + Optional: true, + Description: "compute purpose", + }, + // The rest are Compute properties, which are "computed" once it is created + "account_id": { + Type: schema.TypeInt, + Computed: true, + Description: "ID of the account this compute instance belongs to.", + }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the account this compute instance belongs to.", + }, + "affinity_weight": { + Type: schema.TypeInt, + Computed: true, + }, + "arch": { + Type: schema.TypeString, + Computed: true, + }, + "boot_order": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "boot_disk_id": { + Type: schema.TypeInt, + Computed: true, + Description: "This compute instance boot disk ID.", + }, + "clone_reference": { + Type: schema.TypeInt, + Computed: true, + }, + "clones": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "computeci_id": { + Type: schema.TypeInt, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeInt, + Computed: true, + }, + "deleted_by": { + Type: schema.TypeString, + Computed: true, + }, + "deleted_time": { + Type: schema.TypeInt, + Computed: true, + }, + "devices": { + Type: schema.TypeString, + Computed: true, + }, + "gid": { + Type: schema.TypeInt, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "compute_id": { + Type: schema.TypeInt, + Computed: true, + }, + "interfaces": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: computeInterfacesSchemaMake(), + }, + }, + "lock_status": { + Type: schema.TypeString, + Computed: true, + }, + "manager_id": { + Type: schema.TypeInt, + Computed: true, + }, + "manager_type": { + Type: schema.TypeString, + Computed: true, + }, + "migrationjob": { + Type: schema.TypeInt, + Computed: true, + }, + "milestones": { + Type: schema.TypeInt, + Computed: true, + }, + "natable_vins_id": { + Type: schema.TypeInt, + Computed: true, + }, + "natable_vins_ip": { + Type: schema.TypeString, + Computed: true, + }, + "natable_vins_name": { + Type: schema.TypeString, + Computed: true, + }, + "natable_vins_network": { + Type: schema.TypeString, + Computed: true, + }, + "natable_vins_network_name": { + Type: schema.TypeString, + Computed: true, + }, + "os_users": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: osUsersSubresourceSchemaMake(), + }, + Description: "Guest OS users provisioned on this compute instance.", + }, + "pinned": { + Type: schema.TypeBool, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "registered": { + Type: schema.TypeBool, + Computed: true, + }, + "res_name": { + Type: schema.TypeString, + Computed: true, + }, + "rg_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the resource group where this compute instance is located.", + }, + "snap_sets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: computeSnapSetsSchemaMake(), + }, + }, + "stateless_sep_id": { + Type: schema.TypeInt, + Computed: true, + }, + "stateless_sep_type": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "tech_status": { + Type: schema.TypeString, + Computed: true, + }, + "updated_by": { + Type: schema.TypeString, + Computed: true, + }, + "updated_time": { + Type: schema.TypeInt, + Computed: true, + }, + "user_managed": { + Type: schema.TypeBool, + Computed: true, + }, + "vgpus": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "virtual_image_id": { + Type: schema.TypeInt, + Computed: true, + }, + "virtual_image_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} diff --git a/internal/service/cloudapi/kvmvm/resource_compute.go b/internal/service/cloudapi/kvmvm/resource_compute.go index 7665b39..727c646 100644 --- a/internal/service/cloudapi/kvmvm/resource_compute.go +++ b/internal/service/cloudapi/kvmvm/resource_compute.go @@ -192,6 +192,16 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf createReqX86.Driver = driver + if custom_fields, ok := d.GetOk("custom_fields"); ok { + val := custom_fields.(string) + val = strings.ReplaceAll(val, "\\", "") + val = strings.ReplaceAll(val, "\n", "") + val = strings.ReplaceAll(val, "\t", "") + val = strings.TrimSpace(val) + + createReqX86.CustomFields = val + } + log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86") apiResp, err := c.CloudAPI().KVMX86().Create(ctx, createReqX86) if err != nil { @@ -247,13 +257,13 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf if enabled, ok := d.GetOk("enabled"); ok { if enabled.(bool) { req := compute.EnableRequest{ComputeID: computeId} - log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", computeId, enabled) + log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", enabled, computeId) if _, err := c.CloudAPI().Compute().Enable(ctx, req); err != nil { warnings.Add(err) } } else { req := compute.DisableRequest{ComputeID: computeId} - log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", computeId, enabled) + log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", enabled, computeId) if _, err := c.CloudAPI().Compute().Disable(ctx, req); err != nil { warnings.Add(err) } @@ -574,11 +584,11 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf switch networkData["net_type"].(string) { case "VINS": if vinsId, ok := existVinsId(ctx, m, networkData["net_id"].(int)); !ok { - return diag.Errorf("resourceComputeCreate: can't create compute because vins ID %d is not allowed or does not exist", vinsId) + return diag.Errorf("resourceComputeCreate: can't update compute because vins ID %d is not allowed or does not exist", vinsId) } case "EXTNET": if extNetId, ok := existExtNetId(ctx, m, networkData["net_id"].(int)); !ok { - return diag.Errorf("resourceComputeCreate: can't create compute because extnet ID %d is not allowed or does not exist", extNetId) + return diag.Errorf("resourceComputeCreate: can't update compute because extnet ID %d is not allowed or does not exist", extNetId) } default: @@ -1335,6 +1345,35 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf } } + if d.HasChange("custom_fields") { + val := d.Get("custom_fields").(string) + val = strings.ReplaceAll(val, "\\", "") + val = strings.ReplaceAll(val, "\n", "") + val = strings.ReplaceAll(val, "\t", "") + val = strings.TrimSpace(val) + + if len(val) > 0 { + req := compute.SetCustomFieldsRequest{ + ComputeID: computeRec.ID, + CustomFields: val, + } + + _, err := c.CloudAPI().Compute().SetCustomFields(ctx, req) + if err != nil { + return diag.FromErr(err) + } + } else { + req := compute.DeleteCustomFieldsRequest{ + ComputeID: computeRec.ID, + } + + _, err := c.CloudAPI().Compute().DeleteCustomFields(ctx, req) + if err != nil { + return diag.FromErr(err) + } + } + } + // we may reuse dataSourceComputeRead here as we maintain similarity // between Compute resource and Compute data source schemas defer resourceComputeRead(ctx, d, m) @@ -1678,6 +1717,11 @@ func ResourceComputeSchemaMake() map[string]*schema.Schema { Schema: disksSubresourceSchemaMake(), }, }, + "custom_fields": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, "stateless": { Type: schema.TypeBool, Optional: true, @@ -2075,7 +2119,7 @@ func ResourceComputeSchemaMake() map[string]*schema.Schema { func ResourceCompute() *schema.Resource { return &schema.Resource{ - SchemaVersion: 1, + SchemaVersion: 2, CreateContext: resourceComputeCreate, ReadContext: resourceComputeRead, @@ -2095,5 +2139,12 @@ func ResourceCompute() *schema.Resource { }, Schema: ResourceComputeSchemaMake(), + StateUpgraders: []schema.StateUpgrader{ + { + Type: resourceComputeResourceV1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceCompueteStateUpgradeV1, + Version: 1, + }, + }, } } diff --git a/internal/service/cloudapi/kvmvm/state_upgraders.go b/internal/service/cloudapi/kvmvm/state_upgraders.go new file mode 100644 index 0000000..1a9a713 --- /dev/null +++ b/internal/service/cloudapi/kvmvm/state_upgraders.go @@ -0,0 +1,33 @@ +package kvmvm + +import ( + "context" + "fmt" + "strings" + + log "github.com/sirupsen/logrus" +) + +func resourceCompueteStateUpgradeV1(ctx context.Context, rawState map[string]interface{}, meta any) (map[string]interface{}, error) { + log.Debug("resourceCompueteStateUpgradeV1: upgrading state") + customFields, ok := rawState["custom_fields"] + if !ok || customFields == nil { + rawState["custom_fields"] = "{}" + return rawState, nil + } + + b := &strings.Builder{} + b.WriteString("{") + oldCustomFieldsSlice := customFields.([]interface{}) + for i := range oldCustomFieldsSlice { + oldCustomFields := oldCustomFieldsSlice[i].(map[string]interface{}) + b.WriteString(fmt.Sprintf(`"%s":"%s"`, oldCustomFields["key"], oldCustomFields["val"])) + if i < len(oldCustomFieldsSlice)-1 { + b.WriteString(",") + } + } + b.WriteString("}") + rawState["custom_fields"] = b.String() + + return rawState, nil +} diff --git a/internal/service/cloudapi/kvmvm/utility_compute.go b/internal/service/cloudapi/kvmvm/utility_compute.go index 6fce42b..d09e492 100644 --- a/internal/service/cloudapi/kvmvm/utility_compute.go +++ b/internal/service/cloudapi/kvmvm/utility_compute.go @@ -165,6 +165,7 @@ func utilityComputeNetworksConfigure(ctx context.Context, d *schema.ResourceData Force: true, } + log.Debugf("utilityComputeNetworksConfigure: stopping compute %d", computeID) _, err := c.CloudAPI().Compute().Stop(ctx, req) if err != nil { return err @@ -255,6 +256,7 @@ func utilityComputeNetworksConfigure(ctx context.Context, d *schema.ResourceData startReq := compute.StartRequest{ComputeID: computeID} + log.Debugf("utilityComputeNetworksConfigure: starting compute %d", computeID) _, err = c.CloudAPI().Compute().Start(ctx, startReq) if err != nil { apiErrCount++ @@ -279,7 +281,7 @@ func utilityComputeCheckPresence(ctx context.Context, d *schema.ResourceData, m computeRecord, err := c.CloudAPI().Compute().Get(ctx, req) if err != nil { - return *computeRecord, err + return compute.RecordCompute{}, err } return *computeRecord, nil