From ec4e63c2c851742b3648d056d7b7a9b7ee13fc64 Mon Sep 17 00:00:00 2001 From: Sergey Shubin svs1370 Date: Fri, 10 Sep 2021 18:14:31 +0300 Subject: [PATCH] Add cloud_init parameter to compute resource management --- decort/data_source_compute.go | 7 ++++++ decort/models_api.go | 3 +++ decort/resource_compute.go | 43 ++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/decort/data_source_compute.go b/decort/data_source_compute.go index c5cdb35..ea92243 100644 --- a/decort/data_source_compute.go +++ b/decort/data_source_compute.go @@ -241,6 +241,7 @@ func flattenCompute(d *schema.ResourceData, compFacts string) error { d.Set("boot_disk_id", parseBootDiskId(model.Disks)) // we may need boot disk ID in resize operations d.Set("image_id", model.ImageID) d.Set("description", model.Desc) + d.Set("cloud_init", "applied") // NOTE: for existing compute we hard-code this value as an indicator for DiffSuppress fucntion // d.Set("status", model.Status) // d.Set("tech_status", model.TechStatus) @@ -427,6 +428,12 @@ func dataSourceCompute() *schema.Resource { Description: "User-defined text description of this compute instance.", }, + "cloud_init": { + Type: schema.TypeString, + Computed: true, + Description: "Placeholder for cloud_init parameters.", + }, + /* "status": { Type: schema.TypeString, diff --git a/decort/models_api.go b/decort/models_api.go index 7aa08d5..0b3da84 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -201,6 +201,9 @@ type KvmVmCreateParam struct { // this is unified structure for both x86 and PPC Start bool `json:"start"` } +// structures related to cloudapi/compute/start API +const ComputeStartAPI = "/restmachine/cloudapi/compute/start" + // structures related to cloudapi/compute/delete API const ComputeDeleteAPI = "/restmachine/cloudapi/compute/delete" diff --git a/decort/resource_compute.go b/decort/resource_compute.go index 09cfa51..f2a73a4 100644 --- a/decort/resource_compute.go +++ b/decort/resource_compute.go @@ -36,6 +36,19 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) +func cloudInitDiffSupperss(key, oldVal, newVal string, d *schema.ResourceData) bool { + if oldVal == "" && newVal != "applied" { + // if old value for "cloud_init" resource is empty string, it means that we are creating new compute + // and there is a chance that the user will want custom cloud init parameters - so we check if + // cloud_init is explicitly set in TF file by making sure that its new value is different from "applied", + // which is a reserved key word. + log.Debugf("cloudInitDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=FALSE", key, oldVal, newVal) + return false // there is a difference between stored and new value + } + log.Debugf("cloudInitDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=TRUE", key, oldVal, newVal) + return true // suppress difference +} + func resourceComputeCreate(d *schema.ResourceData, m interface{}) error { // we assume all mandatory parameters it takes to create a comptue instance are properly // specified - we rely on schema "Required" attributes to let Terraform validate them for us @@ -55,7 +68,7 @@ func resourceComputeCreate(d *schema.ResourceData, m interface{}) error { urlValues.Add("imageId", fmt.Sprintf("%d", d.Get("image_id").(int))) urlValues.Add("bootDisk", fmt.Sprintf("%d", d.Get("boot_disk_size").(int))) urlValues.Add("netType", "NONE") // at the 1st step create isolated compute - // urlValues.Add("start", "false") // at the 1st step create compute in a stopped state + urlValues.Add("start", "0") // at the 1st step create compute in a stopped state argVal, argSet := d.GetOk("description") if argSet { @@ -77,6 +90,15 @@ func resourceComputeCreate(d *schema.ResourceData, m interface{}) error { } else { // note that we do not validate arch value for explicit "KVM_X86" here log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86") } + + argVal, argSet = d.GetOk("cloud_init") + if argSet { + // userdata must not be empty string and must not be a reserved keyword "applied" + userdata := argVal.(string) + if userdata != "" && userdata != "applied" { + urlValues.Add("userdata", userdata) + } + } apiResp, err := controller.decortAPICall("POST", computeCreateAPI, urlValues) if err != nil { @@ -134,6 +156,16 @@ func resourceComputeCreate(d *schema.ResourceData, m interface{}) error { // if there were no errors in setting any of the subresources, we may leave Partial mode d.Partial(false) } + + // Note bene: we created compute in a STOPPED state (this is required to properly attach 1st network interface), + // now we need to start it before we report the sequence complete + reqValues := &url.Values{} + reqValues.Add("computeId", fmt.Sprintf("%d", compId)) + log.Debugf("resourceComputeCreate: starting Compute ID %d after completing its resource configuration", compId) + apiResp, err = controller.decortAPICall("POST", ComputeStartAPI, reqValues) + if err != nil { + return err + } log.Debugf("resourceComputeCreate: new Compute ID %d, name %s creation sequence complete", compId, d.Get("name").(string)) @@ -405,6 +437,15 @@ func resourceCompute() *schema.Resource { Description: "Description of this compute instance.", }, + + "cloud_init": { + Type: schema.TypeString, + Optional: true, + Default: "applied", + DiffSuppressFunc: cloudInitDiffSupperss, + Description: "Optional cloud_init parameters. Applied when creating new compute instance only, ignored in all other cases.", + }, + // The rest are Compute properties, which are "computed" once it is created "rg_name": { Type: schema.TypeString,