Skip to content

Commit

Permalink
Merge pull request hashicorp#126 from jorgenunez/new-resource-datacenter
Browse files Browse the repository at this point in the history
Enhancement: New resource datacenter
  • Loading branch information
vancluever authored Aug 17, 2017
2 parents a1c1cb1 + f337494 commit dbd9b09
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 0 deletions.
1 change: 1 addition & 0 deletions vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"vsphere_datacenter": resourceVSphereDatacenter(),
"vsphere_file": resourceVSphereFile(),
"vsphere_folder": resourceVSphereFolder(),
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
Expand Down
169 changes: 169 additions & 0 deletions vsphere/resource_vsphere_datacenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package vsphere

import (
"fmt"
"log"
"time"

"github.com/hashicorp/terraform/helper/resource"
"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/methods"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context"
)

func resourceVSphereDatacenter() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereDatacenterCreate,
Read: resourceVSphereDatacenterRead,
Delete: resourceVSphereDatacenterDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"folder": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}

func resourceVSphereDatacenterCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)
name := d.Get("name").(string)

var f *object.Folder
if v, ok := d.GetOk("folder"); ok {
finder := find.NewFinder(client.Client, true)
var err error
f, err = finder.Folder(context.TODO(), v.(string))
if err != nil {
return fmt.Errorf("failed to find folder that will contain the datacenter: %s", err)
}
} else {
f = object.NewRootFolder(client.Client)
}

dc, err := f.CreateDatacenter(context.TODO(), name)
if err != nil || dc == nil {
return fmt.Errorf("failed to create datacenter: %s", err)
}
// From govmomi code: "Response will be nil if this is an ESX host that does not belong to a vCenter"
if dc == nil {
return fmt.Errorf("ESX host does not belong to a vCenter")
}

// Wait for the datacenter resource to be ready
stateConf := &resource.StateChangeConf{
Pending: []string{"InProgress"},
Target: []string{"Created"},
Refresh: resourceVSphereDatacenterStateRefreshFunc(d, meta),
Timeout: 10 * time.Minute,
MinTimeout: 3 * time.Second,
Delay: 5 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("error waiting for datacenter (%s) to become ready: %s", name, err)
}

d.SetId(name)

return resourceVSphereDatacenterRead(d, meta)

}

func resourceVSphereDatacenterStateRefreshFunc(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Print("[TRACE] Refreshing datacenter state")
dc, err := datacenterExists(d, meta)
if err != nil {
switch err.(type) {
case *find.NotFoundError:
log.Printf("[TRACE] Refreshing state. Datacenter not found: %s", err)
return nil, "InProgress", nil
default:
return nil, "Failed", err
}
}
log.Print("[TRACE] Refreshing state. Datacenter found")
return dc, "Created", nil
}
}

func datacenterExists(d *schema.ResourceData, meta interface{}) (*object.Datacenter, error) {
client := meta.(*govmomi.Client)
name := d.Get("name").(string)

path := name
if v, ok := d.GetOk("folder"); ok {
path = v.(string) + "/" + name
}

finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), path)
return dc, err
}

func resourceVSphereDatacenterRead(d *schema.ResourceData, meta interface{}) error {
_, err := datacenterExists(d, meta)
if err != nil {
log.Printf("couldn't find the specified datacenter: %s", err)
d.SetId("")
}

return nil
}

func resourceVSphereDatacenterDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)
name := d.Get("name").(string)

path := name
if v, ok := d.GetOk("folder"); ok {
path = v.(string) + "/" + name
}

finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), path)
if err != nil {
log.Printf("couldn't find the specified datacenter: %s", err)
d.SetId("")
return nil
}

req := &types.Destroy_Task{
This: dc.Common.Reference(),
}

_, err = methods.Destroy_Task(context.TODO(), client, req)
if err != nil {
return fmt.Errorf("%s", err)
}

// Wait for the datacenter resource to be destroyed
stateConf := &resource.StateChangeConf{
Pending: []string{"Created"},
Target: []string{},
Refresh: resourceVSphereDatacenterStateRefreshFunc(d, meta),
Timeout: 10 * time.Minute,
MinTimeout: 3 * time.Second,
Delay: 5 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("error waiting for datacenter (%s) to become ready: %s", name, err)
}

return nil
}
126 changes: 126 additions & 0 deletions vsphere/resource_vsphere_datacenter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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"
"golang.org/x/net/context"
)

const resourceName = "vsphere_datacenter.testDC"

const testAccCheckVSphereDatacenterConfig = `
resource "vsphere_datacenter" "testDC" {
name = "testDC"
}
`

const testAccCheckVSphereDatacenterConfigSubfolder = `
resource "vsphere_datacenter" "testDC" {
name = "testDC"
folder = "%s"
}
`

// Create a datacenter on the root folder
func TestAccVSphereDatacenter_createOnRootFolder(t *testing.T) {

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereDatacenterDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckVSphereDatacenterConfig,
Check: resource.ComposeTestCheckFunc(testAccCheckVSphereDatacenterExists(resourceName, true)),
},
},
})
}

// Create a datacenter on a subfolder
func TestAccVSphereDatacenter_createOnSubfolder(t *testing.T) {
dcFolder := os.Getenv("VSPHERE_DC_FOLDER")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereDatacenterDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(testAccCheckVSphereDatacenterConfigSubfolder, dcFolder),
Check: resource.ComposeTestCheckFunc(testAccCheckVSphereDatacenterExists(resourceName, true)),
},
},
})
}

func testAccCheckVSphereDatacenterDestroy(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_datacenter" {
continue
}

path := rs.Primary.Attributes["name"]
if _, ok := rs.Primary.Attributes["folder"]; ok {
path = rs.Primary.Attributes["folder"] + "/" + path
}
_, err := finder.Datacenter(context.TODO(), path)
if err != nil {
switch err.(type) {
case *find.NotFoundError:
fmt.Printf("Expected error received: %s\n", err)
return nil
default:
return err
}
} else {
return fmt.Errorf("datacenter '%s' still exists", path)
}
}

return nil
}

func testAccCheckVSphereDatacenterExists(n string, exists bool) 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)

path := rs.Primary.Attributes["name"]
if _, ok := rs.Primary.Attributes["folder"]; ok {
path = rs.Primary.Attributes["folder"] + "/" + path
}
_, err := finder.Datacenter(context.TODO(), path)
if err != nil {
switch e := err.(type) {
case *find.NotFoundError:
if exists {
return fmt.Errorf("datacenter does not exist: %s", e.Error())
}
fmt.Printf("Expected error received: %s\n", e.Error())
return nil
default:
return err
}
}
return nil
}
}
39 changes: 39 additions & 0 deletions website/docs/r/datacenter.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
layout: "vsphere"
page_title: "VMware vSphere: vsphere_datacenter"
sidebar_current: "docs-vsphere-resource-datacenter"
description: |-
Provides a VMware vSphere datacenter resource. This can be used as the primary container of inventory objects such as hosts and virtual machines.
---

# vsphere\_datacenter

Provides a VMware vSphere datacenter resource. This can be used as the primary container of inventory objects such as hosts and virtual machines.

## Example Usages

**Create datacenter on the root folder:**

```hcl
resource "vsphere_datacenter" "prod_datacenter" {
name = "my_prod_datacenter"
}
```

**Create datacenter on a subfolder:**

```hcl
resource "vsphere_datacenter" "research_datacenter" {
name = "my_research_datacenter"
folder = "/research/"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the datacenter. This name needs to be unique within the folder.
* `folder` - (Optional) The folder where the datacenter should be created.

~> **NOTE**: Datacenters cannot be changed once they are created. Modifying any of these attributes will force a new resource!
3 changes: 3 additions & 0 deletions website/vsphere.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
<li<%= sidebar_current("docs-vsphere-resource-virtual-disk") %>>
<a href="/docs/providers/vsphere/r/virtual_disk.html">vsphere_virtual_disk</a>
</li>
<li<%= sidebar_current("docs-vsphere-resource-datacenter") %>>
<a href="/docs/providers/vsphere/r/datacenter.html">vsphere_datacenter</a>
</li>
</ul>
</li>
</ul>
Expand Down

0 comments on commit dbd9b09

Please sign in to comment.