Skip to content

Commit

Permalink
Add xenorchestra_vm resource argument to opt into graceful termination
Browse files Browse the repository at this point in the history
Add acceptance tests that verify if graceful termination was successful
  • Loading branch information
ddelnano committed Dec 14, 2022
1 parent bd91642 commit 5d9cfbf
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 15 deletions.
1 change: 1 addition & 0 deletions docs/resources/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
* `vga` - (Optional) The video adapter the VM should use. Possible values include std and cirrus.
* `videoram` - (Optional) The videoram option the VM should use. Possible values include 1, 2, 4, 8, 16
* `start_delay` - (Optional) Number of seconds the VM should be delayed from starting
* `use_graceful_termination` - (Optional) If true the VM will be shutdown before it is terminated when destroyed. Defaults to false.

## Timeouts

Expand Down
47 changes: 47 additions & 0 deletions xoa/internal/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,50 @@ func GetFailToStartAndHaltXOClient(d *schema.ResourceData) (interface{}, error)
}
return newFailToStartAndHaltClient(config)
}

// graceful is a mock client used to ensure that HaltVm
// and StartVm is not called. This is useful for tests that need to ensure that
// a Vm is modified without rebooting for CPU or memory changes
type gracefulVmTerminationClient struct {
*client.Client
}

func (c gracefulVmTerminationClient) DeleteVm(id string) error {
vm, err := c.GetVm(client.Vm{Id: id})

if err != nil {
return err
}

if vm.PowerState != "Halted" {
return errors.New("mock client did not receive a stopped Vm. Graceful termination was bypassed!\n")
}

return c.Client.DeleteVm(id)
}

func newGracefulVmTerminationClient(config client.Config) (client.XOClient, error) {
xoClient, err := client.NewClient(config)

if err != nil {
return nil, err
}

c := xoClient.(*client.Client)

return &gracefulVmTerminationClient{c}, nil
}

func GetGracefulVmTerminationClient(d *schema.ResourceData) (interface{}, error) {
url := d.Get("url").(string)
username := d.Get("username").(string)
password := d.Get("password").(string)
insecure := d.Get("insecure").(bool)
config := client.Config{
Url: url,
Username: username,
Password: password,
InsecureSkipVerify: insecure,
}
return newGracefulVmTerminationClient(config)
}
8 changes: 8 additions & 0 deletions xoa/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (

var testAccProviders map[string]*schema.Provider
var testAccFailToStartAndHaltProviders map[string]*schema.Provider
var testAccGracefulVmTerminationProviders map[string]*schema.Provider

var testAccProvider *schema.Provider
var testAccFailToStartHaltVmProvider *schema.Provider
var testAccGracefulVmTerminationProvider *schema.Provider

func init() {
testAccProvider = Provider()
Expand All @@ -25,6 +27,12 @@ func init() {
testAccFailToStartAndHaltProviders = map[string]*schema.Provider{
"xenorchestra": testAccFailToStartHaltVmProvider,
}

testAccGracefulVmTerminationProvider = Provider()
testAccGracefulVmTerminationProvider.ConfigureFunc = internal.GetGracefulVmTerminationClient
testAccGracefulVmTerminationProviders = map[string]*schema.Provider{
"xenorchestra": testAccGracefulVmTerminationProvider,
}
}

func testAccPreCheck(t *testing.T) {
Expand Down
29 changes: 17 additions & 12 deletions xoa/resource_xenorchestra_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ func resourceVmSchema() map[string]*schema.Schema {
Default: 0,
Optional: true,
},
"use_graceful_termination": &schema.Schema{
Type: schema.TypeBool,
Default: false,
Optional: true,
},
// TODO: (#145) Uncomment this once issues with secure_boot have been figured out
// "secure_boot": &schema.Schema{
// Type: schema.TypeBool,
Expand Down Expand Up @@ -832,24 +837,24 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {

func resourceVmDelete(d *schema.ResourceData, m interface{}) error {
c := m.(client.XOClient)
gracefulTermination := d.Get("use_graceful_termination").(bool)
vmId := d.Id()

vmReq := client.Vm{
Id: d.Id(),
}

vm, err := c.GetVm(vmReq)
if err != nil {
return err
}

if vm.PowerState == "Running" {
err = c.HaltVm(vmReq.Id)
if gracefulTermination {
vm, err := c.GetVm(client.Vm{Id: vmId})
if err != nil {
return err
}

if vm.PowerState == "Running" {
err = c.HaltVm(vmId)
if err != nil {
return err
}
}
}

err = c.DeleteVm(d.Id())
err := c.DeleteVm(vmId)
if err != nil {
return err
}
Expand Down
57 changes: 54 additions & 3 deletions xoa/resource_xenorchestra_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -960,8 +960,30 @@ func TestAccXenorchestraVm_createWithMutipleDisks(t *testing.T) {
})
}

func TestAccXenorchestraVm_gracefulTermination(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccGracefulVmTerminationProviders,
CheckDestroy: testAccCheckXenorchestraVmDestroy,
Steps: []resource.TestStep{
{
Config: testAccVmConfigWithGracefulTermination(vmName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id")),
},
{
Config: testAccVmConfigWithGracefulTermination(vmName),
Destroy: true,
},
},
})
}

// Reference: https://github.com/terra-farm/terraform-provider-xenorchestra/pull/212
func TestAccXenorchestraVm_shutdownVmIsProperlyTerminated(t *testing.T) {
func TestAccXenorchestraVm_gracefulTerminationForShutdownVm(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
shutdownVm := func() {
Expand All @@ -982,11 +1004,11 @@ func TestAccXenorchestraVm_shutdownVmIsProperlyTerminated(t *testing.T) {
}
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Providers: testAccGracefulVmTerminationProviders,
CheckDestroy: testAccCheckXenorchestraVmDestroy,
Steps: []resource.TestStep{
{
Config: testAccVmConfig(vmName),
Config: testAccVmConfigWithGracefulTermination(vmName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id")),
Expand Down Expand Up @@ -1706,6 +1728,35 @@ resource "xenorchestra_vm" "bar" {
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
}

func testAccVmConfigWithGracefulTermination(vmName string) string {
return testAccCloudConfigConfig(fmt.Sprintf("vm-template-%s", vmName), "template") + testAccTemplateConfig() + fmt.Sprintf(`
data "xenorchestra_network" "network" {
name_label = "%s"
pool_id = "%s"
}
resource "xenorchestra_vm" "bar" {
memory_max = 4295000000
cpus = 1
cloud_config = "${xenorchestra_cloud_config.bar.template}"
name_label = "%s"
name_description = "description"
template = "${data.xenorchestra_template.template.id}"
network {
network_id = "${data.xenorchestra_network.network.id}"
}
disk {
sr_id = "%s"
name_label = "disk 1"
size = 10001317888
}
use_graceful_termination = true
}
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
}

func testAccVmConfigPXEBoot(vmName string) string {
return testAccCloudConfigConfig(fmt.Sprintf("vm-template-%s", vmName), "template") + testAccNonDefaultTemplateConfig(disklessTestTemplate.NameLabel) + fmt.Sprintf(`
data "xenorchestra_network" "network" {
Expand Down

0 comments on commit 5d9cfbf

Please sign in to comment.