Skip to content

Commit

Permalink
Add support for destroyCloudConfigVdiAfterBoot to the VM resource (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
ddelnano authored Aug 21, 2023
1 parent ed97076 commit 76e9a7d
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 16 deletions.
20 changes: 13 additions & 7 deletions client/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,14 @@ type Vm struct {
// These fields are used for passing in disk inputs when
// creating Vms, however, this is not a real field as far
// as the XO api or XAPI is concerned
Disks []Disk `json:"-"`
CloudNetworkConfig string `json:"-"`
VIFsMap []map[string]string `json:"-"`
WaitForIps bool `json:"-"`
Installation Installation `json:"-"`
ManagementAgentDetected bool `json:"managementAgentDetected"`
PVDriversDetected bool `json:"pvDriversDetected"`
Disks []Disk `json:"-"`
CloudNetworkConfig string `json:"-"`
VIFsMap []map[string]string `json:"-"`
WaitForIps bool `json:"-"`
Installation Installation `json:"-"`
ManagementAgentDetected bool `json:"managementAgentDetected"`
PVDriversDetected bool `json:"pvDriversDetected"`
DestroyCloudConfigVdiAfterBoot bool `json:"-"`
}

type Installation struct {
Expand Down Expand Up @@ -232,6 +233,11 @@ func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
"high_availability": vmReq.HA,
}

destroyCloudConfigVdiAfterBoot := vmReq.DestroyCloudConfigVdiAfterBoot
if destroyCloudConfigVdiAfterBoot {
params["destroyCloudConfigVdiAfterBoot"] = destroyCloudConfigVdiAfterBoot
}

videoram := vmReq.Videoram.Value
if videoram != 0 {
params["videoram"] = videoram
Expand Down
32 changes: 23 additions & 9 deletions xoa/resource_xenorchestra_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ var validInstallationMethods = []string{
"network",
}

var defaultCloudConfigDiskName string = "XO CloudConfigDrive"

func resourceVm() *schema.Resource {
vmSchema := resourceVmSchema()
delete(vmSchema, "cdrom")
delete(vmSchema, "installation_method")
delete(vmSchema, "destroy_cloud_config_vdi_after_boot")
vmSchema["id"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -118,6 +121,16 @@ func resourceVmSchema() map[string]*schema.Schema {
Type: schema.TypeString,
Optional: true,
},
"destroy_cloud_config_vdi_after_boot": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
RequiredWith: []string{
"cloud_config",
},
ForceNew: true,
Description: "Determines whether the cloud config VDI should be deleted once the VM has booted. Defaults to `false`.",
},
"core_os": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -412,14 +425,15 @@ func resourceVmCreate(d *schema.ResourceData, m interface{}) error {
Boot: client.Boot{
Firmware: d.Get("hvm_boot_firmware").(string),
},
ExpNestedHvm: d.Get("exp_nested_hvm").(bool),
NameLabel: d.Get("name_label").(string),
NameDescription: d.Get("name_description").(string),
Template: d.Get("template").(string),
CloudConfig: d.Get("cloud_config").(string),
ResourceSet: rs,
HA: d.Get("high_availability").(string),
AutoPoweron: d.Get("auto_poweron").(bool),
DestroyCloudConfigVdiAfterBoot: d.Get("destroy_cloud_config_vdi_after_boot").(bool),
ExpNestedHvm: d.Get("exp_nested_hvm").(bool),
NameLabel: d.Get("name_label").(string),
NameDescription: d.Get("name_description").(string),
Template: d.Get("template").(string),
CloudConfig: d.Get("cloud_config").(string),
ResourceSet: rs,
HA: d.Get("high_availability").(string),
AutoPoweron: d.Get("auto_poweron").(bool),
CPUs: client.CPUs{
Number: d.Get("cpus").(int),
},
Expand Down Expand Up @@ -514,7 +528,7 @@ func sortDiskMapByPostion(networks []map[string]interface{}) []map[string]interf
func disksToMapList(disks []client.Disk) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(disks))
for _, disk := range disks {
if disk.NameLabel == "XO CloudConfigDrive" {
if disk.NameLabel == defaultCloudConfigDiskName {
continue
}
diskMap := map[string]interface{}{
Expand Down
87 changes: 87 additions & 0 deletions xoa/resource_xenorchestra_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,60 @@ func TestAccXenorchestraVm_createAndPlanWithNonExistantVm(t *testing.T) {
})
}

func TestAccXenorchestraVm_createWithDestroyCloudConfigDrive(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
verifyCloudConfigDiskDeleted := func() {
c, err := client.NewClient(client.GetConfigFromEnv())
if err != nil {
t.Fatalf("failed to create client with error: %v", err)
}

vm, err := c.GetVm(client.Vm{
NameLabel: vmName,
})

if err != nil {
t.Fatalf("failed to find VM with error: %v", err)
}

vmDisks, err := c.GetDisks(vm)
if err != nil {
t.Fatalf("failed to get Vm's disks with error: %v", err)
}

for _, disk := range vmDisks {
if disk.NameLabel == defaultCloudConfigDiskName {
t.Errorf("expected the VM to have its cloud config VDI removed, instead found: %v", disk)
}
}
}
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckXenorchestraVmDestroy,
Steps: []resource.TestStep{
{
Config: testAccVmConfigWithDestroyCloudConfigAfterBoot(vmName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
internal.TestCheckTypeSetElemAttrPair(resourceName, "network.*.*", "data.xenorchestra_network.network", "id")),
Destroy: false,
},
{
PreConfig: verifyCloudConfigDiskDeleted,
Config: testAccVmConfigWithDestroyCloudConfigAfterBoot(vmName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
internal.TestCheckTypeSetElemAttrPair(resourceName, "network.*.*", "data.xenorchestra_network.network", "id")),
PlanOnly: true,
},
},
})
}

func TestAccXenorchestraVm_createWhenWaitingForIp(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
Expand Down Expand Up @@ -1666,6 +1720,39 @@ resource "xenorchestra_vm" "bar" {
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
}

// This sets destroy_cloud_config_vdi_after_boot and wait_for_ip. The former is required for
// the test expectations while the latter is to ensure the test holds its assertions until the
// disk was actually deleted. The XO api uses the guest metrics to determine when it can remove
// the disk, so an IP address allocation happens at the same time.
func testAccVmConfigWithDestroyCloudConfigAfterBoot(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}"
destroy_cloud_config_vdi_after_boot = true
network {
network_id = "${data.xenorchestra_network.network.id}"
}
wait_for_ip = true
disk {
sr_id = "%s"
name_label = "disk 1"
size = 10001317888
}
}
`, 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 76e9a7d

Please sign in to comment.