Skip to content

Commit

Permalink
Adding 'detach_unknown_disks_on_delete' flag for VM resource
Browse files Browse the repository at this point in the history
Optional, defaults to false.  If true, will detach disks not managed by
Terraform VM resource prior to VM deletion.

Issue: hashicorp#8945
  • Loading branch information
dagnello committed Sep 26, 2016
1 parent d129675 commit c6a2c6d
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 17 deletions.
41 changes: 39 additions & 2 deletions builtin/providers/vsphere/resource_vsphere_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,12 @@ func resourceVSphereVirtualMachine() *schema.Resource {
},
},

"detach_unknown_disks_on_delete": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},

"cdrom": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -1149,10 +1155,11 @@ func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{
}

// Safely eject any disks the user marked as keep_on_remove
var diskSetList []interface{}
if vL, ok := d.GetOk("disk"); ok {
if diskSet, ok := vL.(*schema.Set); ok {

for _, value := range diskSet.List() {
diskSetList = diskSet.List()
for _, value := range diskSetList {
disk := value.(map[string]interface{})

if v, ok := disk["keep_on_remove"].(bool); ok && v == true {
Expand All @@ -1168,6 +1175,36 @@ func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{
}
}

// Safely eject any disks that are not managed by this resource
if v, ok := d.GetOk("detach_unknown_disks_on_delete"); ok && v.(bool) {
var disksToRemove object.VirtualDeviceList
for _, device := range devices {
if devices.TypeName(device) != "VirtualDisk" {
continue
}
vd := device.GetVirtualDevice()
var skip bool
for _, value := range diskSetList {
disk := value.(map[string]interface{})
if int32(disk["key"].(int)) == vd.Key {
skip = true
break
}
}
if skip {
continue
}
disksToRemove = append(disksToRemove, device)
}
if len(disksToRemove) != 0 {
err = vm.RemoveDevice(context.TODO(), true, disksToRemove...)
if err != nil {
log.Printf("[ERROR] Update Remove Disk - Error removing disk: %v", err)
return err
}
}
}

task, err := vm.Destroy(context.TODO())
if err != nil {
return err
Expand Down
151 changes: 136 additions & 15 deletions builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1389,13 +1389,137 @@ func TestAccVSphereVirtualMachine_keepOnRemove(t *testing.T) {
},
resource.TestStep{
Config: " ",
Check: checkForDisk(datacenter, datastore, "terraform-test", "one.vmdk"),
Check: checkForDisk(datacenter, datastore, "terraform-test", "one.vmdk", true, true),
},
},
})
}

func checkForDisk(datacenter string, datastore string, vmName string, path string) resource.TestCheckFunc {
const testAccVSphereVirtualMachine_DetachUnknownDisks = `
resource "vsphere_virtual_machine" "keep_disk" {
name = "terraform-test"
` + testAccTemplateBasicBody + `
detach_unknown_disks_on_delete = true
disk {
size = 1
iops = 500
controller_type = "scsi"
name = "one"
keep_on_remove = true
}
disk {
size = 2
iops = 500
controller_type = "scsi"
name = "two"
keep_on_remove = false
}
disk {
size = 3
iops = 500
controller_type = "scsi"
name = "three"
keep_on_remove = true
}
}
`

func TestAccVSphereVirtualMachine_DetachUnknownDisks(t *testing.T) {
var vm virtualMachine
basic_vars := setupTemplateBasicBodyVars()
config := basic_vars.testSprintfTemplateBody(testAccVSphereVirtualMachine_DetachUnknownDisks)
var datastore string
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastore = v
}
var datacenter string
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
datacenter = v
}
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)

dc, err := finder.Datacenter(context.TODO(), datacenter)
if err != nil {
t.Fail()
}
finder = finder.SetDatacenter(dc)

ds, err := getDatastore(finder, datastore)
if err != nil {
t.Fail()
}

vmName := "vsphere_virtual_machine.detach_unknown_disks_on_delete"
test_exists, test_name, test_cpu, test_uuid, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
TestFuncData{vm: vm, label: basic_vars.label, vmName: vmName, numDisks: "4"}.testCheckFuncBasic()

log.Printf("[DEBUG] template= %s", testAccVSphereVirtualMachine_DetachUnknownDisks)
log.Printf("[DEBUG] template config= %s", config)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: config,
Check: resource.ComposeTestCheckFunc(
test_exists, test_name, test_cpu, test_uuid, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
),
},
resource.TestStep{
PreConfig: func() {
createAndAttachDisk(t, finder, vmName, client, 1, ds, vmName+"/tf_custom_disk", "lazy", "lsiLogic", datacenter)
},
Config: " ",
Check: resource.ComposeTestCheckFunc(
checkForDisk(datacenter, datastore, "terraform-test", "one.vmdk", true, false),
checkForDisk(datacenter, datastore, "terraform-test", "two.vmdk", false, false),
checkForDisk(datacenter, datastore, "terraform-test", "three.vmdk", true, false),
checkForDisk(datacenter, datastore, "terraform-test", "tf_custom_disk.vmdk", true, true),
),
},
},
})
}

func createAndAttachDisk(t *testing.T, f *find.Finder, vmName string, client *govmomi.Client, size int, ds *object.Datastore, diskPath string, diskType string, adapterType string, dc string) {
var err error
var vm *object.VirtualMachine

if err = createHardDisk(client, size, diskPath, diskType, adapterType, dc); err != nil {
t.Fail()
return
}
if vm, err = f.VirtualMachine(context.TODO(), vmName); err != nil {
t.Fail()
return
}
if err = addHardDisk(vm, int64(size), int64(0), diskType, ds, diskPath, adapterType); err != nil {
t.Fail()
return
}
}

func vmCleanup(dc *object.Datacenter, ds *object.Datastore, vmName string) error {
client := testAccProvider.Meta().(*govmomi.Client)
fileManager := object.NewFileManager(client.Client)
task, err := fileManager.DeleteDatastoreFile(context.TODO(), ds.Path(vmName), dc)
if err != nil {
log.Printf("[ERROR] checkForDisk - Couldn't delete vm folder '%v': %v", vmName, err)
return err
}

_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[ERROR] checForDisk - Failed while deleting vm folder '%v': %v", vmName, err)
return err
}
return nil
}

func checkForDisk(datacenter string, datastore string, vmName string, path string, exists bool, cleanup bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
Expand All @@ -1415,23 +1539,20 @@ func checkForDisk(datacenter string, datastore string, vmName string, path strin
diskPath := vmName + "/" + path

_, err = ds.Stat(context.TODO(), diskPath)
if err != nil {
if err != nil && exists {
log.Printf("[ERROR] checkForDisk - Couldn't stat file '%v': %v", diskPath, err)
return err
} else if !exists {
errorMessage := fmt.Sprintf("checkForDisk - disk %s still exists", diskPath)
err = vmCleanup(dc, ds, vmName)
if err != nil {
return fmt.Errorf("[ERROR] %s, cleanup also failed: %v", errorMessage, err)
}
return fmt.Errorf("[ERROR] %s", errorMessage)
}

// Cleanup
fileManager := object.NewFileManager(client.Client)
task, err := fileManager.DeleteDatastoreFile(context.TODO(), ds.Path(vmName), dc)
if err != nil {
log.Printf("[ERROR] checkForDisk - Couldn't delete vm folder '%v': %v", vmName, err)
return err
}

_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[ERROR] checForDisk - Failed while deleting vm folder '%v': %v", vmName, err)
return err
if !cleanup || !exists {
return nil
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The following arguments are supported:
* `dns_servers` - (Optional) List of DNS servers for the virtual network adapter; defaults to 8.8.8.8, 8.8.4.4
* `network_interface` - (Required) Configures virtual network interfaces; see [Network Interfaces](#network-interfaces) below for details.
* `disk` - (Required) Configures virtual disks; see [Disks](#disks) below for details
* `detach_unknown_disks_on_delete` - (Optional) will detach disks not managed by this resource on delete (avoids deletion of disks attached after resource creation outside of Terraform scope).
* `cdrom` - (Optional) Configures a CDROM device and mounts an image as its media; see [CDROM](#cdrom) below for more details.
* `windows_opt_config` - (Optional) Extra options for clones of Windows machines.
* `linked_clone` - (Optional) Specifies if the new machine is a [linked clone](https://www.vmware.com/support/ws5/doc/ws_clone_overview.html#wp1036396) of another machine or not.
Expand Down

0 comments on commit c6a2c6d

Please sign in to comment.