mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
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:
parent
dbd6a1fd72
commit
67bfc2faef
@ -32,6 +32,7 @@ func Provider() terraform.ResourceProvider {
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"vsphere_folder": resourceVSphereFolder(),
|
||||
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
|
||||
},
|
||||
|
||||
|
233
builtin/providers/vsphere/resource_vsphere_folder.go
Normal file
233
builtin/providers/vsphere/resource_vsphere_folder.go
Normal 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
|
||||
}
|
||||
}
|
279
builtin/providers/vsphere/resource_vsphere_folder_test.go
Normal file
279
builtin/providers/vsphere/resource_vsphere_folder_test.go
Normal 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"
|
||||
}
|
||||
`
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
`
|
@ -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
|
||||
|
||||
|
28
website/source/docs/providers/vsphere/r/folder.html.markdown
Normal file
28
website/source/docs/providers/vsphere/r/folder.html.markdown
Normal 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.
|
Loading…
Reference in New Issue
Block a user