Skip to content

Commit

Permalink
Allow to recreate VMs if in DONE state (#563)
Browse files Browse the repository at this point in the history
Detect done state for VMs and removes them from the Terraform state. 

Also renames vm variable into vmInfo to prevent collision with the github.com/OpenNebula/one/src/oca/go/src/goca/schemas/vm package import.

Commits squashed:
* Allow to recreate VMs if in DONE state
* Do not terminateHard and fix comment
  • Loading branch information
Thiryn authored Oct 17, 2024
1 parent 9646c44 commit 8236db8
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 15 deletions.
35 changes: 20 additions & 15 deletions opennebula/resource_opennebula_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ func resourceOpennebulaVirtualMachineReadCustom(ctx context.Context, d *schema.R

// TODO: fix it after 5.10 release
// Force the "decrypt" bool to false to keep ONE 5.8 behavior
vm, err := vmc.Info(false)
vmInfo, err := vmc.Info(false)
if err != nil {
if NoExists(err) {
log.Printf("[WARN] Removing virtual machine %s from state because it no longer exists in", d.Get("name"))
Expand All @@ -552,16 +552,21 @@ func resourceOpennebulaVirtualMachineReadCustom(ctx context.Context, d *schema.R
})
return diags
}
d.SetId(fmt.Sprintf("%v", vm.ID))
d.Set("name", vm.Name)
d.Set("uid", vm.UID)
d.Set("gid", vm.GID)
d.Set("uname", vm.UName)
d.Set("gname", vm.GName)
d.Set("state", vm.StateRaw)
d.Set("lcmstate", vm.LCMStateRaw)
d.SetId(fmt.Sprintf("%v", vmInfo.ID))
d.Set("name", vmInfo.Name)
d.Set("uid", vmInfo.UID)
d.Set("gid", vmInfo.GID)
d.Set("uname", vmInfo.UName)
d.Set("gname", vmInfo.GName)
d.Set("state", vmInfo.StateRaw)
d.Set("lcmstate", vmInfo.LCMStateRaw)
if vm.State(vmInfo.StateRaw) == vm.Done {
log.Printf("[WARN] Replacing virtual machine %s (id: %s) because VM is 'Done'; ", d.Get("name"), d.Id())
d.SetId("")
return nil
}
//TODO fix this:
err = d.Set("permissions", permissionsUnixString(*vm.Permissions))
err = d.Set("permissions", permissionsUnixString(*vmInfo.Permissions))
if err != nil {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Expand All @@ -572,7 +577,7 @@ func resourceOpennebulaVirtualMachineReadCustom(ctx context.Context, d *schema.R
}

if customVM != nil {
customDiags := customVM(ctx, d, vm)
customDiags := customVM(ctx, d, vmInfo)
if len(customDiags) > 0 {
return customDiags
}
Expand All @@ -583,7 +588,7 @@ func resourceOpennebulaVirtualMachineReadCustom(ctx context.Context, d *schema.R
if inheritedVectorsIf != nil {
inheritedVectors = inheritedVectorsIf.(map[string]interface{})
}
err = flattenTemplate(d, inheritedVectors, &vm.Template)
err = flattenTemplate(d, inheritedVectors, &vmInfo.Template)
if err != nil {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Expand All @@ -599,14 +604,14 @@ func resourceOpennebulaVirtualMachineReadCustom(ctx context.Context, d *schema.R
inheritedTags = inheritedTagsIf.(map[string]interface{})
}

flattenDiags := flattenVMUserTemplate(d, meta, inheritedTags, &vm.UserTemplate.Template)
flattenDiags := flattenVMUserTemplate(d, meta, inheritedTags, &vmInfo.UserTemplate.Template)
for _, diag := range flattenDiags {
diag.Detail = fmt.Sprintf("virtual machine (ID: %s): %s", d.Id(), err)
diags = append(diags, diag)
}

if vm.LockInfos != nil {
d.Set("lock", LockLevelToString(vm.LockInfos.Locked))
if vmInfo.LockInfos != nil {
d.Set("lock", LockLevelToString(vmInfo.LockInfos.Locked))
}

return diags
Expand Down
104 changes: 104 additions & 0 deletions opennebula/resource_opennebula_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package opennebula

import (
"fmt"
"github.com/OpenNebula/one/src/oca/go/src/goca/schemas/vm"
"os"
"reflect"
"strconv"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
Expand Down Expand Up @@ -313,6 +315,36 @@ func TestAccVirtualMachinePending(t *testing.T) {
})
}

func TestAccVirtualMachineDoneTriggerRecreation(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVirtualMachineDestroy,
Steps: []resource.TestStep{
{
Config: testAccVirtualMachineDone,
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("opennebula_virtual_machine.test", "name", "virtual_machine_done"),
testAccTerminateVM("virtual_machine_done"),
),
},
{
RefreshState: true,
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
func(state *terraform.State) error {
if !state.Empty() && len(state.RootModule().Resources) != 0 {
return fmt.Errorf("expected state to be empty")
}
return nil
},
),
},
},
})
}

func TestAccVirtualMachineResize(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -454,6 +486,52 @@ func testAccSetDSdummy() resource.TestCheckFunc {
}
}

func testAccTerminateVM(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Configuration)
controller := config.Controller

id, err := controller.VMs().ByName(name)
if err != nil {
return err
}

err = controller.VM(id).Terminate()
if err != nil {
return err
}
return waitForVMState(id, vm.Done, time.Minute*5)
}
}

// waitForVMState waits until the VM with vmId has the desiredState.
// returns an error if timeout is reached.
func waitForVMState(vmId int, desiredState vm.State, timeout time.Duration) error {
config := testAccProvider.Meta().(*Configuration)
controller := config.Controller
interval := time.NewTicker(5 * time.Second)
deadline := time.NewTimer(timeout)

// Ensure ticker and timer are stopped after use
defer interval.Stop()
defer deadline.Stop()

for {
select {
case <-interval.C:
info, err := controller.VM(vmId).Info(false)
if err != nil {
return err
}
if vm.State(info.StateRaw) == desiredState {
return nil
}
case <-deadline.C:
return fmt.Errorf("timeout waiting for vm id '%d' to reach desired state %s\n", vmId, desiredState)
}
}
}

func testAccCheckVirtualMachinePermissions(expected *shared.Permissions) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Configuration)
Expand Down Expand Up @@ -861,6 +939,32 @@ resource "opennebula_virtual_machine" "test" {
}
`

var testAccVirtualMachineDone = `
resource "opennebula_virtual_machine" "test" {
name = "virtual_machine_done"
group = "oneadmin"
permissions = "642"
memory = 128
cpu = 0.1
context = {
NETWORK = "YES"
SET_HOSTNAME = "$NAME"
}
graphics {
type = "VNC"
listen = "0.0.0.0"
keymap = "en-us"
}
os {
arch = "x86_64"
boot = ""
}
}
`

var testAccVirtualMachineTemplateAddvCPU = `
resource "opennebula_virtual_machine" "test" {
name = "test-virtual_machine"
Expand Down

0 comments on commit 8236db8

Please sign in to comment.