Added folder handling for folder-qualified vm names

Added acceptance test for creation in folders

Added 'baseName' as computed schema attribute for convenience

Added 'base_name' computed attribute for convenience

Added new vsphere folder resource

Fixed folder behavior

Assure test folders are properly removed

Avoid creating recreating search index in loop

Fix typeo in vsphere.createFolder

Updated website documentation

Renamed test folders to be unique across tests

Fixes based on acc test findings; code cleanup

Added combined folder and vm acc test

Restored newline; fixed skipped acc tests

Marked 'existing_path' as computed only

Removed debug logging from tests

Changed folder read to return error
This commit is contained in:
mdeboercw 2015-11-16 10:44:39 -08:00
parent dbd6a1fd72
commit 67bfc2faef
7 changed files with 814 additions and 26 deletions

View File

@ -32,6 +32,7 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"vsphere_folder": resourceVSphereFolder(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
},

View File

@ -0,0 +1,233 @@
package vsphere
import (
"fmt"
"log"
"path"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"golang.org/x/net/context"
)
type folder struct {
datacenter string
existingPath string
path string
}
func resourceVSphereFolder() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereFolderCreate,
Read: resourceVSphereFolderRead,
Delete: resourceVSphereFolderDelete,
Schema: map[string]*schema.Schema{
"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"existing_path": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceVSphereFolderCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)
f := folder{
path: strings.TrimRight(d.Get("path").(string), "/"),
}
if v, ok := d.GetOk("datacenter"); ok {
f.datacenter = v.(string)
}
createFolder(client, &f)
d.Set("existing_path", f.existingPath)
d.SetId(fmt.Sprintf("%v/%v", f.datacenter, f.path))
log.Printf("[INFO] Created folder: %s", f.path)
return resourceVSphereFolderRead(d, meta)
}
func createFolder(client *govmomi.Client, f *folder) error {
finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), f.datacenter)
if err != nil {
return fmt.Errorf("error %s", err)
}
finder = finder.SetDatacenter(dc)
si := object.NewSearchIndex(client.Client)
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
folder := dcFolders.VmFolder
var workingPath string
pathParts := strings.Split(f.path, "/")
for _, pathPart := range pathParts {
if len(workingPath) > 0 {
workingPath += "/"
}
workingPath += pathPart
subfolder, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, workingPath))
if err != nil {
return fmt.Errorf("error %s", err)
} else if subfolder == nil {
log.Printf("[DEBUG] folder not found; creating: %s", workingPath)
folder, err = folder.CreateFolder(context.TODO(), pathPart)
if err != nil {
return fmt.Errorf("Failed to create folder at %s; %s", workingPath, err)
}
} else {
log.Printf("[DEBUG] folder already exists: %s", workingPath)
f.existingPath = workingPath
folder = subfolder.(*object.Folder)
}
}
return nil
}
func resourceVSphereFolderRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] reading folder: %#v", d)
client := meta.(*govmomi.Client)
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", d.Get("datacenter").(string),
d.Get("path").(string)))
if err != nil {
return err
}
if folder == nil {
d.SetId("")
}
return nil
}
func resourceVSphereFolderDelete(d *schema.ResourceData, meta interface{}) error {
f := folder{
path: strings.TrimRight(d.Get("path").(string), "/"),
existingPath: d.Get("existing_path").(string),
}
if v, ok := d.GetOk("datacenter"); ok {
f.datacenter = v.(string)
}
client := meta.(*govmomi.Client)
deleteFolder(client, &f)
d.SetId("")
return nil
}
func deleteFolder(client *govmomi.Client, f *folder) error {
dc, err := getDatacenter(client, f.datacenter)
if err != nil {
return err
}
var folder *object.Folder
currentPath := f.path
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, f.path))
if err != nil {
return fmt.Errorf("[ERROR] Could not locate folder %s: %v", f.path, err)
} else {
folder = folderRef.(*object.Folder)
}
log.Printf("[INFO] Deleting empty sub-folders of existing path: %s", f.existingPath)
for currentPath != f.existingPath {
log.Printf("[INFO] Deleting folder: %s", currentPath)
children, err := folder.Children(context.TODO())
if err != nil {
return err
}
if len(children) > 0 {
return fmt.Errorf("Folder %s is non-empty and will not be deleted", currentPath)
} else {
log.Printf("[DEBUG] current folder: %#v", folder)
currentPath = path.Dir(currentPath)
if currentPath == "." {
currentPath = ""
}
log.Printf("[INFO] parent path of %s is calculated as %s", f.path, currentPath)
task, err := folder.Destroy(context.TODO())
if err != nil {
return err
}
err = task.Wait(context.TODO())
if err != nil {
return err
}
folderRef, err = si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, currentPath))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
}
return nil
}
// getDatacenter gets datacenter object
func getDatacenter(c *govmomi.Client, dc string) (*object.Datacenter, error) {
finder := find.NewFinder(c.Client, true)
if dc != "" {
d, err := finder.Datacenter(context.TODO(), dc)
return d, err
} else {
d, err := finder.DefaultDatacenter(context.TODO())
return d, err
}
}

View File

@ -0,0 +1,279 @@
package vsphere
import (
"fmt"
"os"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"golang.org/x/net/context"
)
// Basic top-level folder creation
func TestAccVSphereFolder_basic(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "basic"
resourceName := "vsphere_folder." + testMethod
path := "tf_test_basic"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereFolderDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", ""),
),
},
},
})
}
func TestAccVSphereFolder_nested(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "nested"
resourceName := "vsphere_folder." + testMethod
path := "tf_test_nested/tf_test_folder"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereFolderDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", ""),
),
},
},
})
}
func TestAccVSphereFolder_dontDeleteExisting(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "dontDeleteExisting"
resourceName := "vsphere_folder." + testMethod
existingPath := "tf_test_dontDeleteExisting/tf_existing"
path := existingPath + "/tf_nested/tf_test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
assertVSphereFolderExists(datacenter, existingPath),
removeVSphereFolder(datacenter, existingPath, ""),
),
Steps: []resource.TestStep{
resource.TestStep{
PreConfig: func() {
createVSphereFolder(datacenter, existingPath)
},
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExistingPathExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", existingPath),
),
},
},
})
}
func testAccCheckVSphereFolderDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
for _, rs := range s.RootModule().Resources {
if rs.Type != "vsphere_folder" {
continue
}
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["path"])
if err == nil {
return fmt.Errorf("Record still exists")
}
}
return nil
}
func testAccCheckVSphereFolderExists(n string, f *folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Resource not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["path"])
*f = folder{
path: rs.Primary.Attributes["path"],
}
return nil
}
}
func testAccCheckVSphereFolderExistingPathExists(n string, f *folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Resource %s not found in %#v", n, s.RootModule().Resources)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["existing_path"])
*f = folder{
path: rs.Primary.Attributes["path"],
}
return nil
}
}
func assertVSphereFolderExists(datacenter string, folder_name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if err != nil {
return fmt.Errorf("Error: %s", err)
} else if folder == nil {
return fmt.Errorf("Folder %s does not exist!", folder_name)
}
return nil
}
}
func createVSphereFolder(datacenter string, folder_name string) error {
client := testAccProvider.Meta().(*govmomi.Client)
f := folder{path: folder_name, datacenter: datacenter,}
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if err != nil {
return fmt.Errorf("error %s", err)
}
if folder == nil {
createFolder(client, &f)
} else {
return fmt.Errorf("Folder %s already exists", folder_name)
}
return nil
}
func removeVSphereFolder(datacenter string, folder_name string, existing_path string) resource.TestCheckFunc {
f := folder{path: folder_name, datacenter: datacenter, existingPath: existing_path,}
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
// finder := find.NewFinder(client.Client, true)
folder, _ := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if folder != nil {
deleteFolder(client, &f)
}
return nil
}
}
const testAccCheckVSphereFolderConfig = `
resource "vsphere_folder" "%s" {
path = "%s"
datacenter = "%s"
}
`

View File

@ -42,6 +42,7 @@ type hardDisk struct {
type virtualMachine struct {
name string
folder string
datacenter string
cluster string
resourcePool string
@ -59,6 +60,18 @@ type virtualMachine struct {
customConfigurations map[string](types.AnyType)
}
func (v virtualMachine) Path() string {
return vmPath(v.folder, v.name)
}
func vmPath(folder string, name string) string {
var path string
if len(folder) > 0 {
path += folder + "/"
}
return path + name
}
func resourceVSphereVirtualMachine() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereVirtualMachineCreate,
@ -72,6 +85,12 @@ func resourceVSphereVirtualMachine() *schema.Resource {
ForceNew: true,
},
"folder": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"vcpu": &schema.Schema{
Type: schema.TypeInt,
Required: true,
@ -228,6 +247,10 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
memoryMb: int64(d.Get("memory").(int)),
}
if v, ok := d.GetOk("folder"); ok {
vm.folder = v.(string)
}
if v, ok := d.GetOk("datacenter"); ok {
vm.datacenter = v.(string)
}
@ -344,7 +367,7 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "active",
Refresh: waitForNetworkingActive(client, vm.datacenter, vm.name),
Refresh: waitForNetworkingActive(client, vm.datacenter, vm.Path()),
Timeout: 600 * time.Second,
Delay: time.Duration(v.(int)) * time.Second,
MinTimeout: 2 * time.Second,
@ -356,13 +379,15 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
}
}
}
d.SetId(vm.name)
d.SetId(vm.Path())
log.Printf("[INFO] Created virtual machine: %s", d.Id())
return resourceVSphereVirtualMachineRead(d, meta)
}
func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] reading virtual machine: %#v", d)
client := meta.(*govmomi.Client)
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
@ -371,9 +396,8 @@ func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{})
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
vm, err := finder.VirtualMachine(context.TODO(), d.Id())
if err != nil {
log.Printf("[ERROR] Virtual machine not found: %s", d.Get("name").(string))
d.SetId("")
return nil
}
@ -458,7 +482,7 @@ func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
vm, err := finder.VirtualMachine(context.TODO(), vmPath(d.Get("folder").(string), d.Get("name").(string)))
if err != nil {
return err
}
@ -522,18 +546,6 @@ func waitForNetworkingActive(client *govmomi.Client, datacenter, name string) re
}
}
// getDatacenter gets datacenter object
func getDatacenter(c *govmomi.Client, dc string) (*object.Datacenter, error) {
finder := find.NewFinder(c.Client, true)
if dc != "" {
d, err := finder.Datacenter(context.TODO(), dc)
return d, err
} else {
d, err := finder.DefaultDatacenter(context.TODO())
return d, err
}
}
// addHardDisk adds a new Hard Disk to the VirtualMachine.
func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) error {
devices, err := vm.Device(context.TODO())
@ -766,6 +778,7 @@ func findDatastore(c *govmomi.Client, sps types.StoragePlacementSpec) (*object.D
// createVirtualMchine creates a new VirtualMachine.
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
dc, err := getDatacenter(c, vm.datacenter)
if err != nil {
return err
}
@ -798,6 +811,21 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
return err
}
log.Printf("[DEBUG] folder: %#v", vm.folder)
folder := dcFolders.VmFolder
if len(vm.folder) > 0 {
si := object.NewSearchIndex(c.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", vm.datacenter, vm.folder))
if err != nil {
return fmt.Errorf("Error reading folder %s: %s", vm.folder, err)
} else if folderRef == nil {
return fmt.Errorf("Cannot find folder %s", vm.folder)
} else {
folder = folderRef.(*object.Folder)
}
}
// network
networkDevices := []types.BaseVirtualDeviceConfigSpec{}
for _, network := range vm.networkInterfaces {
@ -886,7 +914,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
})
configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}
task, err := dcFolders.VmFolder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
if err != nil {
log.Printf("[ERROR] %s", err)
}
@ -896,7 +924,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
log.Printf("[ERROR] %s", err)
}
newVM, err := finder.VirtualMachine(context.TODO(), vm.name)
newVM, err := finder.VirtualMachine(context.TODO(), vm.Path())
if err != nil {
return err
}
@ -954,6 +982,21 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
if err != nil {
return err
}
log.Printf("[DEBUG] folder: %#v", vm.folder)
folder := dcFolders.VmFolder
if len(vm.folder) > 0 {
si := object.NewSearchIndex(c.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", vm.datacenter, vm.folder))
if err != nil {
return fmt.Errorf("Error reading folder %s: %s", vm.folder, err)
} else if folderRef == nil {
return fmt.Errorf("Cannot find folder %s", vm.folder)
} else {
folder = folderRef.(*object.Folder)
}
}
var datastore *object.Datastore
if vm.datastore == "" {
@ -1084,7 +1127,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
log.Printf("[DEBUG] clone spec: %v", cloneSpec)
task, err := template.Clone(context.TODO(), dcFolders.VmFolder, vm.name, cloneSpec)
task, err := template.Clone(context.TODO(), folder, vm.name, cloneSpec)
if err != nil {
return err
}
@ -1094,7 +1137,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err
}
newVM, err := finder.VirtualMachine(context.TODO(), vm.name)
newVM, err := finder.VirtualMachine(context.TODO(), vm.Path())
if err != nil {
return err
}

View File

@ -191,6 +191,140 @@ func TestAccVSphereVirtualMachine_custom_configs(t *testing.T) {
})
}
func TestAccVSphereVirtualMachine_createInExistingFolder(t *testing.T) {
var vm virtualMachine
var locationOpt string
var datastoreOpt string
var datacenter string
folder := "tf_test_createInExistingFolder"
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
datacenter = v
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineDestroy,
removeVSphereFolder(datacenter, folder, ""),
),
Steps: []resource.TestStep{
resource.TestStep{
PreConfig: func() { createVSphereFolder(datacenter, folder) },
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_createInFolder,
folder,
locationOpt,
label,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.folder", &vm),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "name", "terraform-test-folder"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "folder", folder),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "network_interface.0.label", label),
),
},
},
})
}
func TestAccVSphereVirtualMachine_createWithFolder(t *testing.T) {
var vm virtualMachine
var f folder
var locationOpt string
var folderLocationOpt string
var datastoreOpt string
folder := "tf_test_createWithFolder"
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
folderLocationOpt = fmt.Sprintf(" datacenter = \"%s\"\n", v)
locationOpt += folderLocationOpt
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineDestroy,
testAccCheckVSphereFolderDestroy,
),
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_createWithFolder,
folder,
folderLocationOpt,
locationOpt,
label,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.with_folder", &vm),
testAccCheckVSphereFolderExists("vsphere_folder.with_folder", &f),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "name", "terraform-test-with-folder"),
// resource.TestCheckResourceAttr(
// "vsphere_virtual_machine.with_folder", "folder", folder),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "network_interface.0.label", label),
),
},
},
})
}
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
@ -210,7 +344,21 @@ func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
folder := dcFolders.VmFolder
if len(rs.Primary.Attributes["folder"]) > 0 {
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
if err == nil {
return fmt.Errorf("Record still exists")
}
@ -306,9 +454,9 @@ func testAccCheckVSphereVirtualMachineExistsHasCustomConfig(n string, vm *virtua
return nil
}
}
func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
@ -331,14 +479,26 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
folder := dcFolders.VmFolder
if len(rs.Primary.Attributes["folder"]) > 0 {
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
*vm = virtualMachine{
name: rs.Primary.ID,
}
return nil
}
}
@ -401,3 +561,41 @@ resource "vsphere_virtual_machine" "car" {
}
}
`
const testAccCheckVSphereVirtualMachineConfig_createInFolder = `
resource "vsphere_virtual_machine" "folder" {
name = "terraform-test-folder"
folder = "%s"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
disk {
%s
template = "%s"
}
}
`
const testAccCheckVSphereVirtualMachineConfig_createWithFolder = `
resource "vsphere_folder" "with_folder" {
path = "%s"
%s
}
resource "vsphere_virtual_machine" "with_folder" {
name = "terraform-test-with-folder"
folder = "${vsphere_folder.with_folder.path}"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
disk {
%s
template = "%s"
}
}
`

View File

@ -30,9 +30,15 @@ provider "vsphere" {
vsphere_server = "${var.vsphere_server}"
}
# Create a virtual machine
# Create a folder
resource "vsphere_folder" "frontend" {
path = "frontend"
}
# Create a virtual machine within the folder
resource "vsphere_virtual_machine" "web" {
name = "terraform_web"
folder = "${vsphere_folder.frontend.path}"
vcpu = 2
memory = 4096

View File

@ -0,0 +1,28 @@
---
layout: "vsphere"
page_title: "VMware vSphere: vsphere_folder"
sidebar_current: "docs-vsphere-resource-folder"
description: |-
Provides a VMware vSphere virtual machine folder resource. This can be used to create and delete virtual machine folders.
---
# vsphere\_virtual\_machine
Provides a VMware vSphere virtual machine folder resource. This can be used to create and delete virtual machine folders.
## Example Usage
```
resource "vsphere_folder" "web" {
path = "terraform_web_folder"
}
```
## Argument Reference
The following arguments are supported:
* `path` - (Required) The path of the folder to be created (relative to the datacenter root); should not begin or end with a "/"
* `datacenter` - (Optional) The name of a Datacenter in which the folder will be created
* `existing_path` - (Computed) The path of any parent folder segments which existed at the time this folder was created; on a
destroy action, the (pre-) existing path is not removed.