From 5b03f1972ca7b70caa7e33f611432f121a3cb22c Mon Sep 17 00:00:00 2001 From: Sergey Shubin svs1370 Date: Tue, 16 Mar 2021 00:07:28 +0300 Subject: [PATCH] Implement disk management, phase 2 --- decort/data_source_disk.go | 3 +- decort/models_api.go | 14 ++++++-- decort/resource_compute.go | 2 +- decort/resource_disk.go | 69 ++++++++++++++++++++++++++++++++------ 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/decort/data_source_disk.go b/decort/data_source_disk.go index f6ff797..c357c18 100644 --- a/decort/data_source_disk.go +++ b/decort/data_source_disk.go @@ -67,7 +67,8 @@ func flattenDisk(d *schema.ResourceData, disk_facts string) error { // d.Set("status", model.Status) // d.Set("tech_status", model.TechStatus) - /* we do not manage snapshots via Terraform yet, so keep this commented out for a while + /* we do not manage snapshots via Terraform yet (and probably, never will), so + // keep this block commented out for a while if len(model.Snapshots) > 0 { log.Debugf("flattenDisk: calling flattenDiskSnapshots") if err = d.Set("nics", flattenDiskSnapshots(model.Snapshots)); err != nil { diff --git a/decort/models_api.go b/decort/models_api.go index 1c5ced6..3908c1c 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -476,18 +476,26 @@ const ComputeDiskDetachAPI = "/restmachine/cloudapi/compute/diskDetach" // // structures related to /cloudapi/disks/create // -const DiskCreateAPI = "/restmachine/cloudapi/disks/create" +const DisksCreateAPI = "/restmachine/cloudapi/disks/create" // // structures related to /cloudapi/disks/get // -const DisksCreateAPI = "/restmachine/cloudapi/disks/create" - const DisksGetAPI = "/restmachine/cloudapi/disks/get" // Returns single DiskRecord on success const DisksListAPI = "/restmachine/cloudapi/disks/list" // Returns list of DiskRecord on success type DisksListResp []DiskRecord +// +// structures related to /cloudapi/disks/resize +// +const DisksResizeAPI = "/restmachine/cloudapi/disks/resize2" + +// +// structures related to /cloudapi/disks/delete +// +const DisksDeleteAPI = "/restmachine/cloudapi/disks/delete" + // // Grid ID structures // diff --git a/decort/resource_compute.go b/decort/resource_compute.go index a590c39..9fc550b 100644 --- a/decort/resource_compute.go +++ b/decort/resource_compute.go @@ -197,7 +197,7 @@ func resourceComputeDelete(d *schema.ResourceData, m interface{}) error { params.Add("permanently", "true") controller := m.(*ControllerCfg) - compFacts, err = controller.decortAPICall("POST", ComputeDeleteAPI, params) + _, err = controller.decortAPICall("POST", ComputeDeleteAPI, params) if err != nil { return err } diff --git a/decort/resource_disk.go b/decort/resource_disk.go index b5eb953..45d4563 100644 --- a/decort/resource_disk.go +++ b/decort/resource_disk.go @@ -55,7 +55,7 @@ func resourceDiskCreate(d *schema.ResourceData, m interface{}) error { urlValues.Add("decs", argVal.(string)) } - apiResp, err := controller.decortAPICall("POST", DiskCreateAPI, urlValues) + apiResp, err := controller.decortAPICall("POST", DisksCreateAPI, urlValues) if err != nil { return err } @@ -63,7 +63,7 @@ func resourceDiskCreate(d *schema.ResourceData, m interface{}) error { d.SetId(apiResp) // update ID of the resource to tell Terraform that the disk resource exists diskId, _ := strconv.Atoi(apiResp) - log.Debugf("resourceDiskCreate: new Disk ID %d, name %q creation sequence complete", diskId, d.Get("name").(string)) + log.Debugf("resourceDiskCreate: new Disk ID / name %d / %s creation sequence complete", diskId, d.Get("name").(string)) // We may reuse dataSourceDiskRead here as we maintain similarity // between Disk resource and Disk data source schemas @@ -73,22 +73,45 @@ func resourceDiskCreate(d *schema.ResourceData, m interface{}) error { } func resourceDiskRead(d *schema.ResourceData, m interface{}) error { - disk_facts, err := utilityDiskCheckPresence(d, m) - if disk_facts == "" { + diskFacts, err := utilityDiskCheckPresence(d, m) + if diskFacts == "" { // if empty string is returned from utilityDiskCheckPresence then there is no // such Disk and err tells so - just return it to the calling party d.SetId("") // ensure ID is empty return err } - return flattenDisk(d, disk_facts) + return flattenDisk(d, diskFacts) } func resourceDiskUpdate(d *schema.ResourceData, m interface{}) error { - log.Debugf("resourceDiskUpdate: called for disk name %q, Account ID %d", - d.Get("name").(string), d.Get("account_id").(int)) + // update will only change Disk size and, to keep data safe, will not allow + // shrinking disk. + // Attempt to reduce disk size will throw an error + log.Debugf("resourceDiskUpdate: called for Disk ID / name % d / %s, Account ID %d", + d.Get("disk_id").(int), d.Get("name").(string), d.Get("account_id").(int)) + + oldSize, newSize := d.GetChange("size") + if oldSize.(int) > newSize.(int) { + return fmt.Errorf("resourceDiskUpdate: Disk ID %d - shrinking disk size from %d to %d not allowed", + d.Get("disk_id").(int), oldSize.(int), newSize.(int)) + } + + if oldSize.(int) == newSize.(int) { + log.Debugf("resourceDiskUpdate: Disk ID %d - no size change required", d.Get("disk_id").(int)) + // and there is no need to re-read disk specs either + return nil + } - log.Warn("resourceDiskUpdate: NOT IMPLEMENTED YET!") + params := &url.Values{} + params.Add("diskId", d.Id()) + params.Add("size", fmt.Sprintf("%d", newSize.(int))) + + controller := m.(*ControllerCfg) + _, err := controller.decortAPICall("POST", DisksResizeAPI, params) + if err != nil { + return err + } // we may reuse dataSourceDiskRead here as we maintain similarity // between Compute resource and Compute data source schemas @@ -96,14 +119,38 @@ func resourceDiskUpdate(d *schema.ResourceData, m interface{}) error { } func resourceDiskDelete(d *schema.ResourceData, m interface{}) error { - log.Warn("resourceDiskDelete: NOT IMPLEMENTED YET!") + // NOTE: this function tries to destroy target Disk "permanently", so + // there is no way to restore it. + // If, however, the disk is attached to a compute, the method will + // fail (by failing the underpinning DECORt API call, which is issued with detach=false) + log.Debugf("resourceDiskDelete: called for Disk ID / name %d / %s, Account ID %d", + d.Get("disk_id").(int), d.Get("name").(string), d.Get("account_id").(int)) + + diskFacts, err := utilityDiskCheckPresence(d, m) + if diskFacts == "" { + // the specified Disk does not exist - in this case according to Terraform best practice + // we exit from Destroy method without error + return nil + } + + params := &url.Values{} + params.Add("diskId", d.Id()) + params.Add("detach", "false") + params.Add("permanently", "true") + + controller := m.(*ControllerCfg) + _, err = controller.decortAPICall("POST", DisksDeleteAPI, params) + if err != nil { + return err + } + return nil } func resourceDiskExists(d *schema.ResourceData, m interface{}) (bool, error) { // Reminder: according to Terraform rules, this function should not modify its ResourceData argument - log.Debugf("resourceDiskExists: called for Disk name %q, Account ID %d", - d.Get("name").(string), d.Get("account_id").(int)) + log.Debugf("resourceDiskExists: called for Disk ID / name %d / %s, Account ID %d", + d.Get("disk_id").(int), d.Get("name").(string), d.Get("account_id").(int)) diskFacts, err := utilityDiskCheckPresence(d, m) if diskFacts == "" {