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

Add Custom Attribute resource and data source #229

Merged
merged 9 commits into from
Dec 18, 2017
48 changes: 48 additions & 0 deletions vsphere/data_source_vsphere_custom_attribute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package vsphere

import (
"fmt"

"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-vsphere/vsphere/internal/helper/customattribute"
"github.com/vmware/govmomi/object"
)

func dataSourceVSphereCustomAttribute() *schema.Resource {
return &schema.Resource{
Read: dataSourceVSphereCustomAttributeRead,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Description: "The display name of the custom attribute.",
Required: true,
},
"managed_object_type": {
Type: schema.TypeString,
Computed: true,
Description: "Object type for which the custom attribute is valid. If not specified, the attribute is valid for all managed object types.",
},
},
}
}

func dataSourceVSphereCustomAttributeRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*VSphereClient).vimClient
err := customattribute.VerifySupport(client)
if err != nil {
return err
}

fm, err := object.GetCustomFieldsManager(client.Client)
if err != nil {
return err
}

field, err := customattribute.ByName(fm, d.Get("name").(string))
if err != nil {
return err
}
d.SetId(fmt.Sprint(field.Key))
d.Set("managed_object_type", field.ManagedObjectType)
return nil
}
81 changes: 81 additions & 0 deletions vsphere/data_source_vsphere_custom_attribute_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package vsphere

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceVSphereCustomAttribute(t *testing.T) {
var tp *testing.T
testAccDataSourceVSphereCustomAttributeCases := []struct {
name string
testCase resource.TestCase
}{
{
"basic",
resource.TestCase{
PreCheck: func() {
testAccPreCheck(tp)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceVSphereCustomAttributeConfig(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.vsphere_custom_attribute.terraform-test-attribute-data",
"name",
testAccDataSourceVSphereCustomAttributeConfigName,
),
resource.TestCheckResourceAttr(
"data.vsphere_custom_attribute.terraform-test-attribute-data",
"managed_object_type",
testAccDataSourceVSphereCustomAttributeConfigType,
),
resource.TestCheckResourceAttrPair(
"data.vsphere_custom_attribute.terraform-test-attribute-data", "id",
"vsphere_custom_attribute.terraform-test-attribute", "id",
),
),
},
},
},
},
}

for _, tc := range testAccDataSourceVSphereCustomAttributeCases {
t.Run(tc.name, func(t *testing.T) {
tp = t
resource.Test(t, tc.testCase)
})
}
}

const testAccDataSourceVSphereCustomAttributeConfigName = "terraform-test-attribute"
const testAccDataSourceVSphereCustomAttributeConfigType = "VirtualMachine"

func testAccDataSourceVSphereCustomAttributeConfig() string {
return fmt.Sprintf(`
variable "attribute_name" {
default = "%s"
}

variable "attribute_type" {
default = "%s"
}

resource "vsphere_custom_attribute" "terraform-test-attribute" {
name = "${var.attribute_name}"
managed_object_type = "${var.attribute_type}"
}

data "vsphere_custom_attribute" "terraform-test-attribute-data" {
name = "${vsphere_custom_attribute.terraform-test-attribute.name}"
}
`,
testAccDataSourceVSphereCustomAttributeConfigName,
testAccDataSourceVSphereCustomAttributeConfigType,
)
}
11 changes: 11 additions & 0 deletions vsphere/datacenter_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context"
)
Expand Down Expand Up @@ -47,3 +48,13 @@ func datacenterFromID(client *govmomi.Client, id string) (*object.Datacenter, er
}
return ds.(*object.Datacenter), nil
}

func datacenterCustomAttributes(dc *object.Datacenter) (*mo.Datacenter, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer cancel()
var props mo.Datacenter
if err := dc.Properties(ctx, dc.Reference(), []string{"customValue"}, &props); err != nil {
return nil, err
}
return &props, nil
}
84 changes: 84 additions & 0 deletions vsphere/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"reflect"
"regexp"
"strconv"
"testing"
Expand Down Expand Up @@ -217,6 +218,26 @@ func testGetVirtualMachineSCSIBusState(s *terraform.State, resourceName string)
return virtualdevice.ReadSCSIBusState(l, count), nil
}

func testGetDatacenter(s *terraform.State, resourceName string) (*object.Datacenter, error) {
tVars, err := testClientVariablesForResource(s, fmt.Sprintf("vsphere_datacenter.%s", resourceName))
if err != nil {
return nil, err
}
dcName, ok := tVars.resourceAttributes["name"]
if !ok {
return nil, fmt.Errorf("Datacenter resource %q has no name", resourceName)
}
return getDatacenter(tVars.client, dcName)
}

func testGetDatacenterCustomAttributes(s *terraform.State, resourceName string) (*mo.Datacenter, error) {
dc, err := testGetDatacenter(s, resourceName)
if err != nil {
return nil, err
}
return datacenterCustomAttributes(dc)
}

// testPowerOffVM does an immediate power-off of the supplied virtual machine
// resource defined by the supplied resource address name. It is used to help
// set up a test scenarios where a VM is powered off.
Expand Down Expand Up @@ -487,6 +508,16 @@ func testGetDatastore(s *terraform.State, resAddr string) (*object.Datastore, er
return datastore.FromID(vars.client, vars.resourceID)
}

// testGetDatastoreProperties is a convenience method that adds an extra step
// to testGetDatastore to get the properties of a datastore.
func testGetDatastoreProperties(s *terraform.State, datastoreType string, resourceName string) (*mo.Datastore, error) {
ds, err := testGetDatastore(s, "vsphere_"+datastoreType+"_datastore."+resourceName)
if err != nil {
return nil, err
}
return datastore.Properties(ds)
}

// testAccResourceVSphereDatastoreCheckTags is a check to ensure that the
// supplied datastore has had the tags that have been created with the supplied
// tag resource name attached.
Expand Down Expand Up @@ -579,3 +610,56 @@ func testCheckResourceNotAttr(name, key, value string) resource.TestCheckFunc {
return fmt.Errorf("%s: Attribute '%s' expected to not match %#v", name, key, value)
}
}

// testGetCustomAttribute gets a custom attribute by name.
func testGetCustomAttribute(s *terraform.State, resourceName string) (*types.CustomFieldDef, error) {
tVars, err := testClientVariablesForResource(s, fmt.Sprintf("vsphere_custom_attribute.%s", resourceName))
if err != nil {
return nil, err
}

key, err := strconv.ParseInt(tVars.resourceID, 10, 32)
if err != nil {
return nil, err
}
fm, err := object.GetCustomFieldsManager(tVars.client.Client)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer cancel()
fields, err := fm.Field(ctx)
if err != nil {
return nil, err
}
field := fields.ByKey(int32(key))

return field, nil
}

func testResourceHasCustomAttributeValues(s *terraform.State, resourceType string, resourceName string, entity *mo.ManagedEntity) error {
testVars, err := testClientVariablesForResource(s, fmt.Sprintf("%s.%s", resourceType, resourceName))
if err != nil {
return err
}
expectedAttrs := make(map[string]string)
re := regexp.MustCompile(`custom_attributes\.(\d+)`)
for key, value := range testVars.resourceAttributes {
if m := re.FindStringSubmatch(key); m != nil {
expectedAttrs[m[1]] = value
}
}

actualAttrs := make(map[string]string)
for _, fv := range entity.CustomValue {
value := fv.(*types.CustomFieldStringValue).Value
if value != "" {
actualAttrs[fmt.Sprint(fv.GetCustomFieldValue().Key)] = value
}
}

if !reflect.DeepEqual(expectedAttrs, actualAttrs) {
return fmt.Errorf("expected custom attributes to be %q, got %q", expectedAttrs, actualAttrs)
}
return nil
}
Loading