Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding volume resource creation to vSphere provider #6273

Merged
merged 1 commit into from
May 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/providers/vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"vsphere_file": resourceVSphereFile(),
"vsphere_folder": resourceVSphereFolder(),
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
},

Expand Down
259 changes: 259 additions & 0 deletions builtin/providers/vsphere/resource_vsphere_virtual_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
package vsphere

import (
"fmt"
"log"

"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context"
)

type virtualDisk struct {
size int
vmdkPath string
initType string
adapterType string
datacenter string
datastore string
}

// Define VirtualDisk args
func resourceVSphereVirtualDisk() *schema.Resource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method comments ... Pretty please. What does this do ..

return &schema.Resource{
Create: resourceVSphereVirtualDiskCreate,
Read: resourceVSphereVirtualDiskRead,
Delete: resourceVSphereVirtualDiskDelete,

Schema: map[string]*schema.Schema{
// Size in GB
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true, //TODO Can this be optional (resize)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like these can be false if an upgrade supports the change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be changed in a PR that also adds vm Update functionality. For now it needs to stay this way, else TF would go looking for a resourceVSphereVirtualMachineUpdate that isn't there.

},

"vmdk_path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true, //TODO Can this be optional (move)?
},

"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "eagerZeroedThick",
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "thin" && value != "eagerZeroedThick" {
errors = append(errors, fmt.Errorf(
"only 'thin' and 'eagerZeroedThick' are supported values for 'type'"))
}
return
},
},

"adapter_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "ide",
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "ide" && value != "busLogic" && value != "lsiLogic" {
errors = append(errors, fmt.Errorf(
"only 'ide', 'busLogic', and 'lsiLogic' are supported values for 'adapter_type'"))
}
return
},
},

"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"datastore": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}

func resourceVSphereVirtualDiskCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Creating Virtual Disk")
client := meta.(*govmomi.Client)

vDisk := virtualDisk{
size: d.Get("size").(int),
}

if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stack72 you would expect the set usage here? More info please.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrislovecnm the setting of the state usually happens I'm the read func so we are sure that we expected to happen has happened.

}

if v, ok := d.GetOk("type"); ok {
vDisk.initType = v.(string)
}

if v, ok := d.GetOk("adapter_type"); ok {
vDisk.adapterType = v.(string)
}

if v, ok := d.GetOk("datacenter"); ok {
vDisk.datacenter = v.(string)
}

if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}

diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath)

err := createHardDisk(client, vDisk.size, diskPath, vDisk.initType, vDisk.adapterType, vDisk.datacenter)
if err != nil {
return err
}

d.SetId(diskPath)
log.Printf("[DEBUG] Virtual Disk id: %v", diskPath)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have some logging? In general a touch more logging would be nice ;)

return resourceVSphereVirtualDiskRead(d, meta)
}

func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Reading virtual disk.")
client := meta.(*govmomi.Client)

vDisk := virtualDisk{
size: d.Get("size").(int),
}

if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}

if v, ok := d.GetOk("type"); ok {
vDisk.initType = v.(string)
}

if v, ok := d.GetOk("adapter_type"); ok {
vDisk.adapterType = v.(string)
}

if v, ok := d.GetOk("datacenter"); ok {
vDisk.datacenter = v.(string)
}

if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}

dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}

finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)

ds, err := finder.Datastore(context.TODO(), d.Get("datastore").(string))
if err != nil {
return err
}

fileInfo, err := ds.Stat(context.TODO(), vDisk.vmdkPath)
if err != nil {
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - stat failed on: %v", vDisk.vmdkPath)
d.SetId("")
return err
}
fileInfo = fileInfo.GetFileInfo()
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo)
size := fileInfo.(*types.FileInfo).FileSize / 1024 / 1024 / 1024

d.SetId(vDisk.vmdkPath)

d.Set("size", size)
d.Set("vmdk_path", vDisk.vmdkPath)
d.Set("datacenter", d.Get("datacenter"))
d.Set("datastore", d.Get("datastore"))
// Todo collect and write type info

return nil

}

func resourceVSphereVirtualDiskDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)

vDisk := virtualDisk{}

if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}
if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}

dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}
diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath)

virtualDiskManager := object.NewVirtualDiskManager(client.Client)

task, err := virtualDiskManager.DeleteVirtualDisk(context.TODO(), diskPath, dc)
if err != nil {
return err
}

_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[INFO] Failed to delete disk: %v", err)
return err
}

log.Printf("[INFO] Deleted disk: %v", diskPath)
d.SetId("")
return nil
}

// createHardDisk creates a new Hard Disk.
func createHardDisk(client *govmomi.Client, size int, diskPath string, diskType string, adapterType string, dc string) error {
virtualDiskManager := object.NewVirtualDiskManager(client.Client)
spec := &types.FileBackedVirtualDiskSpec{
VirtualDiskSpec: types.VirtualDiskSpec{
AdapterType: adapterType,
DiskType: diskType,
},
CapacityKb: int64(1024 * 1024 * size),
}
datacenter, err := getDatacenter(client, dc)
if err != nil {
return err
}
log.Printf("[DEBUG] Disk spec: %v", spec)

task, err := virtualDiskManager.CreateVirtualDisk(context.TODO(), diskPath, datacenter, spec)
if err != nil {
return err
}

_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[INFO] Failed to create disk: %v", err)
return err
}
log.Printf("[INFO] Created disk.")

return nil
}
129 changes: 129 additions & 0 deletions builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package vsphere
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the TF framework support negative test cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at other tests and at helper/resource/testing.go library, it looks like negative tests aren't supported.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stack72 @jen20 @phinze say it ain't so ... no negative testing?? I will file an issues

Copy link
Contributor

@jen20 jen20 Apr 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow what you mean by negative testing? You can make whatever assertions you want for acceptance tests - I can't think of an example offhand but the function signature for the checkFuncs should be reasonably self-explanatory.


import (
"fmt"
"log"
"os"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"golang.org/x/net/context"
)

func TestAccVSphereVirtualDisk_basic(t *testing.T) {
var datacenterOpt string
var datastoreOpt string
var initTypeOpt string
var adapterTypeOpt string

if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
datacenterOpt = v
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = v
}
if v := os.Getenv("VSPHERE_INIT_TYPE"); v != "" {
initTypeOpt += fmt.Sprintf(" type = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_ADAPTER_TYPE"); v != "" {
adapterTypeOpt += fmt.Sprintf(" adapter_type = \"%s\"\n", v)
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualDiskDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtuaDiskConfig_basic,
initTypeOpt,
adapterTypeOpt,
datacenterOpt,
datastoreOpt,
),
Check: resource.ComposeTestCheckFunc(
testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo"),
),
},
},
})
}

func testAccVSphereVirtualDiskExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
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)
}
finder = finder.SetDatacenter(dc)

ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"])
if err != nil {
return fmt.Errorf("error %s", err)
}

_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"])
if err != nil {
return fmt.Errorf("error %s", err)
}

return nil
}
}

func testAccCheckVSphereVirtualDiskDestroy(s *terraform.State) error {
log.Printf("[FINDME] test Destroy")
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)

for _, rs := range s.RootModule().Resources {
if rs.Type != "vsphere_virtual_disk" {
continue
}

dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}

finder = finder.SetDatacenter(dc)

ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"])
if err != nil {
return fmt.Errorf("error %s", err)
}

_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"])
if err == nil {
return fmt.Errorf("error %s", err)
}
}

return nil
}

const testAccCheckVSphereVirtuaDiskConfig_basic = `
resource "vsphere_virtual_disk" "foo" {
size = 1
vmdk_path = "tfTestDisk.vmdk"
%s
%s
datacenter = "%s"
datastore = "%s"
}
`
Loading