From 87c3716aa8af46c0a604c1095b196afc6059e1b0 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 19 Aug 2019 13:32:01 +0300 Subject: [PATCH 01/22] Initial guest properties setting for VM --- go.mod | 1 + go.sum | 2 ++ govcd/vm.go | 39 +++++++++++++++++++++++++++++++++++++++ govcd/vm_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/go.mod b/go.mod index 63d608087..e27bb0e12 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/vmware/go-vcloud-director/v2 require ( + github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/go-version v1.1.0 github.com/kr/pretty v0.1.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 diff --git a/go.sum b/go.sum index af2c6a600..a84039b13 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= diff --git a/govcd/vm.go b/govcd/vm.go index a34ec77a2..4806b3fa8 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -598,3 +598,42 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { return vm.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, "", errMessage, nil) } + +// SetGuestProperties +func (vm *VM) SetGuestProperties(productSectionList *types.ProductSectionList) (*types.ProductSectionList, error) { + productSectionList.Xmlns = types.XMLNamespaceVCloud + productSectionList.Ovf = types.XMLNamespaceOVF + + apiEndpoint, _ := url.ParseRequestURI(vm.VM.HREF) + apiEndpoint.Path += "/productSections" + + task, err := vm.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, + types.MimeProductSection, "error setting guest properties: %s", productSectionList) + + if err != nil { + return nil, fmt.Errorf("unable to set guest properties: %s", err) + } + + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("task for setting guest properties failed: %s", err) + } + + return vm.GetGuestProperties() +} + +// GetGuestProperties +func (vm *VM) GetGuestProperties() (*types.ProductSectionList, error) { + properties := &types.ProductSectionList{} + + if vm.VM.HREF == "" { + return properties, fmt.Errorf("cannot refresh VM, HREF is not set") + } + + _, err := vm.client.ExecuteRequest(vm.VM.HREF+"/productSections", http.MethodGet, + types.MimeNetworkConnectionSection, "error retrieving network connection: %s", nil, properties) + + // The request was successful + return properties, err + +} diff --git a/govcd/vm_test.go b/govcd/vm_test.go index d5de48aae..4a8ecd3a1 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -15,6 +15,8 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" + + "github.com/davecgh/go-spew/spew" ) func init() { @@ -828,3 +830,46 @@ func (vcd *TestVCD) Test_VMPowerOnPowerOff(check *C) { check.Assert(err, IsNil) check.Assert(vmStatus, Equals, "POWERED_OFF") } + +// Test_SetGuestProperties sets guest properties, retrieves them and deeply matches if properties +// were properly set. +func (vcd *TestVCD) Test_SetGuestProperties(check *C) { + if vcd.skipVappTests { + check.Skip("Skipping test because vapp was not successfully created at setup") + } + vapp := vcd.findFirstVapp() + vmType, vmName := vcd.findFirstVm(vapp) + if vmName == "" { + check.Skip("skipping test because no VM is found") + } + vm, err := vcd.client.Client.FindVMByHREF(vmType.HREF) + check.Assert(err, IsNil) + + vmProperties := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + Property: []*types.Property{ + &types.Property{ + Key: "sys_owner", + Type: "string", + Value: &types.Value{Value: "test"}, + }, + &types.Property{ + Key: "asset_tag", + Value: &types.Value{Value: "xxxyyy"}, + Type: "string", + }, + }, + }, + } + + gotProperties, err := vm.SetGuestProperties(vmProperties) + check.Assert(err, IsNil) + + getProperties, err := vm.GetGuestProperties() + check.Assert(err, IsNil) + + check.Assert(gotProperties.ProductSection.Property, DeepEquals, vmProperties.ProductSection.Property) + check.Assert(getProperties, DeepEquals, gotProperties) + +} From eb73b30368f45f5a9762295d4e9f10349d368068 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 20 Aug 2019 10:21:33 +0300 Subject: [PATCH 02/22] Adjust fields --- govcd/vm_test.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 4a8ecd3a1..72a753b11 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -15,8 +15,6 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" - - "github.com/davecgh/go-spew/spew" ) func init() { @@ -850,14 +848,25 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { Info: "Custom properties", Property: []*types.Property{ &types.Property{ - Key: "sys_owner", - Type: "string", - Value: &types.Value{Value: "test"}, + UserConfigurable: true, + Key: "sys_owner", + Label: "sys_owner", + Type: "string", + DefaultValue: "test", + }, + &types.Property{ + UserConfigurable: true, + Key: "asset_tag", + Label: "asset_tag", + DefaultValue: "xxxyyy", + Type: "string", }, &types.Property{ - Key: "asset_tag", - Value: &types.Value{Value: "xxxyyy"}, - Type: "string", + UserConfigurable: true, + Key: "guestinfo.config.bootstrap.ip", + Label: "guestinfo.config.bootstrap.ip", + DefaultValue: "192.168.12.180", + Type: "string", }, }, }, @@ -868,7 +877,7 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { getProperties, err := vm.GetGuestProperties() check.Assert(err, IsNil) - + check.Assert(gotProperties.ProductSection.Property, DeepEquals, vmProperties.ProductSection.Property) check.Assert(getProperties, DeepEquals, gotProperties) From 1676ffbbe9880618656e2ecf51122e063e2cc1c6 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 20 Aug 2019 14:17:49 +0300 Subject: [PATCH 03/22] Tune test --- govcd/vm_test.go | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 72a753b11..881484a81 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -848,25 +848,28 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { Info: "Custom properties", Property: []*types.Property{ &types.Property{ - UserConfigurable: true, + UserConfigurable: false, Key: "sys_owner", - Label: "sys_owner", + Label: "sys_owner_label", Type: "string", - DefaultValue: "test", + DefaultValue: "sys_owner_default", + Value: &types.Value{Value: "test"}, }, &types.Property{ UserConfigurable: true, Key: "asset_tag", - Label: "asset_tag", - DefaultValue: "xxxyyy", + Label: "asset_tag_label", Type: "string", + DefaultValue: "asset_tag_default", + Value: &types.Value{Value: "xxxyyy"}, }, &types.Property{ UserConfigurable: true, Key: "guestinfo.config.bootstrap.ip", - Label: "guestinfo.config.bootstrap.ip", - DefaultValue: "192.168.12.180", + Label: "guestinfo.config.bootstrap.ip_label", Type: "string", + DefaultValue: "default_ip", + Value: &types.Value{Value: "192.168.12.180"}, }, }, }, @@ -878,6 +881,29 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { getProperties, err := vm.GetGuestProperties() check.Assert(err, IsNil) + // Check that values were set in API + check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") + check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") + check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") + check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") + check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) + + check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") + check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") + check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") + check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "asset_tag_default") + check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) + + check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") + check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") + check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") + check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") + check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) + + // Ensure the object are deeply equal check.Assert(gotProperties.ProductSection.Property, DeepEquals, vmProperties.ProductSection.Property) check.Assert(getProperties, DeepEquals, gotProperties) From 0f15492807edd411d4be7a861efc88b0eb4db1b3 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 20 Aug 2019 15:16:04 +0300 Subject: [PATCH 04/22] vApp and VM support guest properties --- govcd/vapp.go | 44 +++++++++++++++++++++++++++ govcd/vapp_test.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++ govcd/vm.go | 13 ++++---- govcd/vm_test.go | 2 +- 4 files changed, 126 insertions(+), 7 deletions(-) diff --git a/govcd/vapp.go b/govcd/vapp.go index 267c06f34..372295cae 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -537,6 +537,9 @@ func (vapp *VApp) ChangeVMName(name string) (Task, error) { types.MimeVM, "error changing VM name: %s", newName) } +// SetOvf sets guest properties for the first child VM in vApp +// +// Deprecated: Use vm.SetGuestProperties() func (vapp *VApp) SetOvf(parameters map[string]string) (Task, error) { err := vapp.Refresh() if err != nil { @@ -834,3 +837,44 @@ func updateNetworkConfigurations(vapp *VApp, networkConfigurations []types.VAppN func (vapp *VApp) RemoveAllNetworks() (Task, error) { return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{}) } + +// SetVappProperties sets vApp properties +func (vapp *VApp) SetVappProperties(productSectionList *types.ProductSectionList) (*types.ProductSectionList, error) { + productSectionList.Xmlns = types.XMLNamespaceVCloud + productSectionList.Ovf = types.XMLNamespaceOVF + + apiEndpoint, _ := url.ParseRequestURI(vapp.VApp.HREF) + apiEndpoint.Path += "/productSections" + + task, err := vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, + types.MimeProductSection, "error setting vApp properties: %s", productSectionList) + + if err != nil { + return nil, fmt.Errorf("unable to set vApp properties: %s", err) + } + + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("task for setting vApp properties failed: %s", err) + } + + return vapp.GetVappProperties() +} + +// GetVappProperties retrieves vApp properties +func (vapp *VApp) GetVappProperties() (*types.ProductSectionList, error) { + properties := &types.ProductSectionList{} + + if vapp.VApp.HREF == "" { + return properties, fmt.Errorf("cannot refresh vApp, HREF is not set") + } + + _, err := vapp.client.ExecuteRequest(vapp.VApp.HREF+"/productSections", http.MethodGet, + types.MimeProductSection, "error retrieving vApp properties: %s", nil, properties) + + if err != nil { + return nil, fmt.Errorf("unable to retrieve vApp properties: %s", err) + } + + return properties, nil +} diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index b6921cdf6..6824e1477 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -680,3 +680,77 @@ func (vcd *TestVCD) Test_RemoveAllNetworks(check *C) { } check.Assert(hasNetworks, Equals, false) } + +// Test_SetVappProperties sets vApp properties, retrieves them and deeply matches if properties +// were properly set. +func (vcd *TestVCD) Test_SetVappProperties(check *C) { + if vcd.skipVappTests { + check.Skip("Skipping test because vapp was not successfully created at setup") + } + vapp := vcd.findFirstVapp() + + vappProperties := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + Property: []*types.Property{ + &types.Property{ + UserConfigurable: false, + Key: "sys_owner", + Label: "sys_owner_label", + Type: "string", + DefaultValue: "sys_owner_default", + Value: &types.Value{Value: "test"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "asset_tag", + Label: "asset_tag_label", + Type: "string", + DefaultValue: "asset_tag_default", + Value: &types.Value{Value: "xxxyyy"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "guestinfo.config.bootstrap.ip", + Label: "guestinfo.config.bootstrap.ip_label", + Type: "string", + DefaultValue: "default_ip", + Value: &types.Value{Value: "192.168.12.180"}, + }, + }, + }, + } + + gotProperties, err := vapp.SetVappProperties(vappProperties) + check.Assert(err, IsNil) + + getProperties, err := vapp.GetVappProperties() + check.Assert(err, IsNil) + + // Check that values were set in API + check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") + check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") + check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") + check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") + check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) + + check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") + check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") + check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") + check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") + check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) + + check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") + check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") + check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") + check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") + check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) + + // Ensure the object are deeply equal + check.Assert(gotProperties.ProductSection.Property, DeepEquals, vappProperties.ProductSection.Property) + check.Assert(getProperties, DeepEquals, gotProperties) + +} diff --git a/govcd/vm.go b/govcd/vm.go index 4806b3fa8..27da65336 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -599,7 +599,7 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { "", errMessage, nil) } -// SetGuestProperties +// SetGuestProperties sets guest properties for a VM func (vm *VM) SetGuestProperties(productSectionList *types.ProductSectionList) (*types.ProductSectionList, error) { productSectionList.Xmlns = types.XMLNamespaceVCloud productSectionList.Ovf = types.XMLNamespaceOVF @@ -622,18 +622,19 @@ func (vm *VM) SetGuestProperties(productSectionList *types.ProductSectionList) ( return vm.GetGuestProperties() } -// GetGuestProperties +// GetGuestProperties retrieves guest properties for a VM func (vm *VM) GetGuestProperties() (*types.ProductSectionList, error) { properties := &types.ProductSectionList{} - if vm.VM.HREF == "" { return properties, fmt.Errorf("cannot refresh VM, HREF is not set") } _, err := vm.client.ExecuteRequest(vm.VM.HREF+"/productSections", http.MethodGet, - types.MimeNetworkConnectionSection, "error retrieving network connection: %s", nil, properties) + types.MimeProductSection, "error retrieving guest properties: %s", nil, properties) - // The request was successful - return properties, err + if err != nil { + return nil, fmt.Errorf("unable to retrieve guest properties: %s", err) + } + return properties, nil } diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 881484a81..7a200c39e 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -893,7 +893,7 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") - check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "asset_tag_default") + check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") From 7617826a16e5ce564365d75fd68b19b0e1a2b83d Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 20 Aug 2019 16:01:41 +0300 Subject: [PATCH 05/22] use interface to share guest property testcases for VM and vApp --- govcd/guestproperties.go | 53 +++++++++++++++++++++++++ govcd/vapp.go | 42 ++++---------------- govcd/vapp_test.go | 72 ++------------------------------- govcd/vapp_vm_test.go | 86 ++++++++++++++++++++++++++++++++++++++++ govcd/vm.go | 33 ++------------- govcd/vm_test.go | 72 ++------------------------------- 6 files changed, 159 insertions(+), 199 deletions(-) create mode 100644 govcd/guestproperties.go create mode 100644 govcd/vapp_vm_test.go diff --git a/govcd/guestproperties.go b/govcd/guestproperties.go new file mode 100644 index 000000000..5ed185734 --- /dev/null +++ b/govcd/guestproperties.go @@ -0,0 +1,53 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// setGuestProperties is a shared function for +func setGuestProperties(client *Client, href string, properties *types.ProductSectionList) error { + if href == "" { + return fmt.Errorf("href cannot be empty to set guest properties") + } + + properties.Xmlns = types.XMLNamespaceVCloud + properties.Ovf = types.XMLNamespaceOVF + + task, err := client.ExecuteTaskRequest(href+"/productSections", http.MethodPut, + types.MimeProductSection, "error setting guest properties: %s", properties) + + if err != nil { + return fmt.Errorf("unable to set guest properties: %s", err) + } + + err = task.WaitTaskCompletion() + if err != nil { + return fmt.Errorf("task for setting guest properties failed: %s", err) + } + + return nil +} + +// getGuestProperties is a shared function for both vApp and VM +func getGuestProperties(client *Client, href string) (*types.ProductSectionList, error) { + if href == "" { + return nil, fmt.Errorf("href cannot be empty to set guest properties") + } + properties := &types.ProductSectionList{} + + _, err := client.ExecuteRequest(href+"/productSections", http.MethodGet, + types.MimeProductSection, "error retrieving guest properties: %s", nil, properties) + + if err != nil { + return nil, fmt.Errorf("unable to retrieve guest properties: %s", err) + } + + return properties, nil +} diff --git a/govcd/vapp.go b/govcd/vapp.go index 372295cae..42ce1218b 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -838,43 +838,17 @@ func (vapp *VApp) RemoveAllNetworks() (Task, error) { return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{}) } -// SetVappProperties sets vApp properties -func (vapp *VApp) SetVappProperties(productSectionList *types.ProductSectionList) (*types.ProductSectionList, error) { - productSectionList.Xmlns = types.XMLNamespaceVCloud - productSectionList.Ovf = types.XMLNamespaceOVF - - apiEndpoint, _ := url.ParseRequestURI(vapp.VApp.HREF) - apiEndpoint.Path += "/productSections" - - task, err := vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, - types.MimeProductSection, "error setting vApp properties: %s", productSectionList) - - if err != nil { - return nil, fmt.Errorf("unable to set vApp properties: %s", err) - } - - err = task.WaitTaskCompletion() +// SetGuestProperties sets guest properties for a vApp +func (vapp *VApp) SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) { + err := setGuestProperties(vapp.client, vapp.VApp.HREF, properties) if err != nil { - return nil, fmt.Errorf("task for setting vApp properties failed: %s", err) + return nil, fmt.Errorf("unable to set VM guest properties: %s", err) } - return vapp.GetVappProperties() + return vapp.GetGuestProperties() } -// GetVappProperties retrieves vApp properties -func (vapp *VApp) GetVappProperties() (*types.ProductSectionList, error) { - properties := &types.ProductSectionList{} - - if vapp.VApp.HREF == "" { - return properties, fmt.Errorf("cannot refresh vApp, HREF is not set") - } - - _, err := vapp.client.ExecuteRequest(vapp.VApp.HREF+"/productSections", http.MethodGet, - types.MimeProductSection, "error retrieving vApp properties: %s", nil, properties) - - if err != nil { - return nil, fmt.Errorf("unable to retrieve vApp properties: %s", err) - } - - return properties, nil +// GetGuestProperties retrieves guest properties for a vApp +func (vapp *VApp) GetGuestProperties() (*types.ProductSectionList, error) { + return getGuestProperties(vapp.client, vapp.VApp.HREF) } diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index 6824e1477..0ec992112 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -681,76 +681,12 @@ func (vcd *TestVCD) Test_RemoveAllNetworks(check *C) { check.Assert(hasNetworks, Equals, false) } -// Test_SetVappProperties sets vApp properties, retrieves them and deeply matches if properties -// were properly set. -func (vcd *TestVCD) Test_SetVappProperties(check *C) { +// Test_VappSetGuestProperties sets vApp properties, retrieves them and deeply matches if properties +// were properly set using a propertyTester helper. +func (vcd *TestVCD) Test_VappSetGuestProperties(check *C) { if vcd.skipVappTests { check.Skip("Skipping test because vapp was not successfully created at setup") } vapp := vcd.findFirstVapp() - - vappProperties := &types.ProductSectionList{ - ProductSection: &types.ProductSection{ - Info: "Custom properties", - Property: []*types.Property{ - &types.Property{ - UserConfigurable: false, - Key: "sys_owner", - Label: "sys_owner_label", - Type: "string", - DefaultValue: "sys_owner_default", - Value: &types.Value{Value: "test"}, - }, - &types.Property{ - UserConfigurable: true, - Key: "asset_tag", - Label: "asset_tag_label", - Type: "string", - DefaultValue: "asset_tag_default", - Value: &types.Value{Value: "xxxyyy"}, - }, - &types.Property{ - UserConfigurable: true, - Key: "guestinfo.config.bootstrap.ip", - Label: "guestinfo.config.bootstrap.ip_label", - Type: "string", - DefaultValue: "default_ip", - Value: &types.Value{Value: "192.168.12.180"}, - }, - }, - }, - } - - gotProperties, err := vapp.SetVappProperties(vappProperties) - check.Assert(err, IsNil) - - getProperties, err := vapp.GetVappProperties() - check.Assert(err, IsNil) - - // Check that values were set in API - check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") - check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") - check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") - check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") - check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) - - check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") - check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") - check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") - check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") - check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) - - check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") - check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") - check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") - check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") - check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) - - // Ensure the object are deeply equal - check.Assert(gotProperties.ProductSection.Property, DeepEquals, vappProperties.ProductSection.Property) - check.Assert(getProperties, DeepEquals, gotProperties) - + propertyTester(vcd, check, &vapp) } diff --git a/govcd/vapp_vm_test.go b/govcd/vapp_vm_test.go new file mode 100644 index 000000000..87a670ae1 --- /dev/null +++ b/govcd/vapp_vm_test.go @@ -0,0 +1,86 @@ +// +build vapp vm functional ALL + +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "github.com/vmware/go-vcloud-director/v2/types/v56" + . "gopkg.in/check.v1" +) + +// guestPropertyGetSetter interface is used for covering tests in both VM and vApp guest property +type guestPropertyGetSetter interface { + GetGuestProperties() (*types.ProductSectionList, error) + SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) +} + +// propertyTester is a guest property setter accepting guestPropertyGetSetter interface for trying +// out settings on all objects implementing such interface +func propertyTester(vcd *TestVCD, check *C, object guestPropertyGetSetter) { + vappProperties := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + Property: []*types.Property{ + &types.Property{ + UserConfigurable: false, + Key: "sys_owner", + Label: "sys_owner_label", + Type: "string", + DefaultValue: "sys_owner_default", + Value: &types.Value{Value: "test"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "asset_tag", + Label: "asset_tag_label", + Type: "string", + DefaultValue: "asset_tag_default", + Value: &types.Value{Value: "xxxyyy"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "guestinfo.config.bootstrap.ip", + Label: "guestinfo.config.bootstrap.ip_label", + Type: "string", + DefaultValue: "default_ip", + Value: &types.Value{Value: "192.168.12.180"}, + }, + }, + }, + } + + gotProperties, err := object.SetGuestProperties(vappProperties) + check.Assert(err, IsNil) + + getProperties, err := object.GetGuestProperties() + check.Assert(err, IsNil) + + // Check that values were set in API + check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") + check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") + check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") + check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") + check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) + + check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") + check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") + check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") + check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") + check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) + + check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") + check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") + check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") + check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") + check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") + check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) + + // Ensure the object are deeply equal + check.Assert(gotProperties.ProductSection.Property, DeepEquals, vappProperties.ProductSection.Property) + check.Assert(getProperties, DeepEquals, gotProperties) +} diff --git a/govcd/vm.go b/govcd/vm.go index 27da65336..ea8aca228 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -600,23 +600,10 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { } // SetGuestProperties sets guest properties for a VM -func (vm *VM) SetGuestProperties(productSectionList *types.ProductSectionList) (*types.ProductSectionList, error) { - productSectionList.Xmlns = types.XMLNamespaceVCloud - productSectionList.Ovf = types.XMLNamespaceOVF - - apiEndpoint, _ := url.ParseRequestURI(vm.VM.HREF) - apiEndpoint.Path += "/productSections" - - task, err := vm.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, - types.MimeProductSection, "error setting guest properties: %s", productSectionList) - - if err != nil { - return nil, fmt.Errorf("unable to set guest properties: %s", err) - } - - err = task.WaitTaskCompletion() +func (vm *VM) SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) { + err := setGuestProperties(vm.client, vm.VM.HREF, properties) if err != nil { - return nil, fmt.Errorf("task for setting guest properties failed: %s", err) + return nil, fmt.Errorf("unable to set VM guest properties: %s", err) } return vm.GetGuestProperties() @@ -624,17 +611,5 @@ func (vm *VM) SetGuestProperties(productSectionList *types.ProductSectionList) ( // GetGuestProperties retrieves guest properties for a VM func (vm *VM) GetGuestProperties() (*types.ProductSectionList, error) { - properties := &types.ProductSectionList{} - if vm.VM.HREF == "" { - return properties, fmt.Errorf("cannot refresh VM, HREF is not set") - } - - _, err := vm.client.ExecuteRequest(vm.VM.HREF+"/productSections", http.MethodGet, - types.MimeProductSection, "error retrieving guest properties: %s", nil, properties) - - if err != nil { - return nil, fmt.Errorf("unable to retrieve guest properties: %s", err) - } - - return properties, nil + return getGuestProperties(vm.client, vm.VM.HREF) } diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 7a200c39e..20e681bb0 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -829,9 +829,9 @@ func (vcd *TestVCD) Test_VMPowerOnPowerOff(check *C) { check.Assert(vmStatus, Equals, "POWERED_OFF") } -// Test_SetGuestProperties sets guest properties, retrieves them and deeply matches if properties -// were properly set. -func (vcd *TestVCD) Test_SetGuestProperties(check *C) { +// Test_VMSetGuestProperties sets guest properties, retrieves them and deeply matches if properties +// were properly set using a propertyTester helper. +func (vcd *TestVCD) Test_VMSetGuestProperties(check *C) { if vcd.skipVappTests { check.Skip("Skipping test because vapp was not successfully created at setup") } @@ -842,69 +842,5 @@ func (vcd *TestVCD) Test_SetGuestProperties(check *C) { } vm, err := vcd.client.Client.FindVMByHREF(vmType.HREF) check.Assert(err, IsNil) - - vmProperties := &types.ProductSectionList{ - ProductSection: &types.ProductSection{ - Info: "Custom properties", - Property: []*types.Property{ - &types.Property{ - UserConfigurable: false, - Key: "sys_owner", - Label: "sys_owner_label", - Type: "string", - DefaultValue: "sys_owner_default", - Value: &types.Value{Value: "test"}, - }, - &types.Property{ - UserConfigurable: true, - Key: "asset_tag", - Label: "asset_tag_label", - Type: "string", - DefaultValue: "asset_tag_default", - Value: &types.Value{Value: "xxxyyy"}, - }, - &types.Property{ - UserConfigurable: true, - Key: "guestinfo.config.bootstrap.ip", - Label: "guestinfo.config.bootstrap.ip_label", - Type: "string", - DefaultValue: "default_ip", - Value: &types.Value{Value: "192.168.12.180"}, - }, - }, - }, - } - - gotProperties, err := vm.SetGuestProperties(vmProperties) - check.Assert(err, IsNil) - - getProperties, err := vm.GetGuestProperties() - check.Assert(err, IsNil) - - // Check that values were set in API - check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") - check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") - check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") - check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") - check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) - - check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") - check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") - check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") - check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") - check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) - - check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") - check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") - check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") - check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") - check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) - - // Ensure the object are deeply equal - check.Assert(gotProperties.ProductSection.Property, DeepEquals, vmProperties.ProductSection.Property) - check.Assert(getProperties, DeepEquals, gotProperties) - + propertyTester(vcd, check, &vm) } From 7308a3931f3503fa5c4cfd0546b9f23a6096e8ee Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 20 Aug 2019 17:19:59 +0300 Subject: [PATCH 06/22] Fix title --- govcd/vapp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/vapp.go b/govcd/vapp.go index 42ce1218b..6dd562c44 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -842,7 +842,7 @@ func (vapp *VApp) RemoveAllNetworks() (Task, error) { func (vapp *VApp) SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) { err := setGuestProperties(vapp.client, vapp.VApp.HREF, properties) if err != nil { - return nil, fmt.Errorf("unable to set VM guest properties: %s", err) + return nil, fmt.Errorf("unable to set vApp guest properties: %s", err) } return vapp.GetGuestProperties() From 1de091a74b1015dd4f2c091c86224f26fb3e6fad Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 21 Aug 2019 09:02:01 +0300 Subject: [PATCH 07/22] Fix function title --- govcd/vapp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/vapp.go b/govcd/vapp.go index 6dd562c44..f6adf0118 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -833,7 +833,7 @@ func updateNetworkConfigurations(vapp *VApp, networkConfigurations []types.VAppN types.MimeNetworkConfigSection, "error updating vApp Network: %s", networkConfig) } -// Function RemoveAllNetworks unattach all networks from VAPP +// RemoveAllNetworks detaches all networks from vApp func (vapp *VApp) RemoveAllNetworks() (Task, error) { return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{}) } From d309448b09cd9fbb0e9283df12fa368646d5facb Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 09:15:42 +0300 Subject: [PATCH 08/22] Add changelog note --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd527e775..cc289ac10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` +* Added methods `vm.SetGuestProperties` and `vm.GetGuestProperties` [#]() +* Added methods `vapp.SetGuestProperties` and `vapp.GetGuestProperties` [#]() IMPROVEMENTS: From e211100fdf4e64b899d877bbe9e9511552202303 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 12:14:05 +0300 Subject: [PATCH 09/22] go mod tidy --- go.mod | 3 +-- go.sum | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/go.mod b/go.mod index e27bb0e12..1c75c93e1 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module github.com/vmware/go-vcloud-director/v2 require ( - github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/go-version v1.1.0 - github.com/kr/pretty v0.1.0 + github.com/kr/pretty v0.1.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index a84039b13..91cfce57f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -7,7 +5,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/vmware/go-vcloud-director v2.0.0+incompatible h1:3B121XZVdEOxRhv5ARswKVxXt4KznAbun8GoXNbbZWs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 05025c24e7523357d89ae49cf03b84f15edd56ba Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 12:15:46 +0300 Subject: [PATCH 10/22] Add PR number to changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc289ac10..3662e4211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,8 +22,8 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` -* Added methods `vm.SetGuestProperties` and `vm.GetGuestProperties` [#]() -* Added methods `vapp.SetGuestProperties` and `vapp.GetGuestProperties` [#]() +* Added methods `vm.SetGuestProperties` and `vm.GetGuestProperties` [#235](https://github.com/vmware/go-vcloud-director/pull/235) +* Added methods `vapp.SetGuestProperties` and `vapp.GetGuestProperties` [#235](https://github.com/vmware/go-vcloud-director/pull/235) IMPROVEMENTS: From 370c4d454e051c7ca75244a1bbf103a08029bc02 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 14:18:26 +0300 Subject: [PATCH 11/22] Address comments --- govcd/guestproperties.go | 2 +- govcd/vapp_vm_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/govcd/guestproperties.go b/govcd/guestproperties.go index 5ed185734..25a7b91b8 100644 --- a/govcd/guestproperties.go +++ b/govcd/guestproperties.go @@ -11,7 +11,7 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" ) -// setGuestProperties is a shared function for +// setGuestProperties is a shared function for both vApp and VM func setGuestProperties(client *Client, href string, properties *types.ProductSectionList) error { if href == "" { return fmt.Errorf("href cannot be empty to set guest properties") diff --git a/govcd/vapp_vm_test.go b/govcd/vapp_vm_test.go index 87a670ae1..2ef1a5d44 100644 --- a/govcd/vapp_vm_test.go +++ b/govcd/vapp_vm_test.go @@ -59,6 +59,10 @@ func propertyTester(vcd *TestVCD, check *C, object guestPropertyGetSetter) { check.Assert(err, IsNil) // Check that values were set in API + check.Assert(getProperties, NotNil) + check.Assert(getProperties.ProductSection, NotNil) + check.Assert(len(getProperties.ProductSection.Property), Equals, 3) + check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") From d6547a2163fd6ef0c05b294715eb33ee02370587 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 14:47:08 +0300 Subject: [PATCH 12/22] Fix incorrect error message --- govcd/guestproperties.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/guestproperties.go b/govcd/guestproperties.go index 25a7b91b8..493484de5 100644 --- a/govcd/guestproperties.go +++ b/govcd/guestproperties.go @@ -38,7 +38,7 @@ func setGuestProperties(client *Client, href string, properties *types.ProductSe // getGuestProperties is a shared function for both vApp and VM func getGuestProperties(client *Client, href string) (*types.ProductSectionList, error) { if href == "" { - return nil, fmt.Errorf("href cannot be empty to set guest properties") + return nil, fmt.Errorf("href cannot be empty to get guest properties") } properties := &types.ProductSectionList{} From fee96256561733743b6b10bd5e2d75a2a6b6e73c Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 26 Aug 2019 15:48:37 +0300 Subject: [PATCH 13/22] Do not print nil error in load balancer test --- govcd/lb_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/lb_test.go b/govcd/lb_test.go index bb25b560f..54ca2396a 100644 --- a/govcd/lb_test.go +++ b/govcd/lb_test.go @@ -274,7 +274,7 @@ func checkLb(queryUrl string, expectedResponses []string, maxRetryTimeout int) e for { select { case <-timeoutAfter: - return fmt.Errorf("timed out waiting for all nodes to be up: %s", err) + return fmt.Errorf("timed out waiting for all nodes to be up") case <-tick.C: var resp *http.Response resp, err = httpClient.Get(queryUrl) From 4c39ac7d33180ac9eb0e3573956363df2a4fb10f Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 10:11:04 +0300 Subject: [PATCH 14/22] Change naming GuestProperties -> ProductSectionList --- govcd/guestproperties.go | 53 ----------------------------------- govcd/productsection.go | 53 +++++++++++++++++++++++++++++++++++ govcd/vapp.go | 18 ++++++------ govcd/vapp_test.go | 6 ++-- govcd/vapp_vm_test.go | 60 ++++++++++++++++++++-------------------- govcd/vm.go | 16 +++++------ govcd/vm_test.go | 4 +-- 7 files changed, 105 insertions(+), 105 deletions(-) delete mode 100644 govcd/guestproperties.go create mode 100644 govcd/productsection.go diff --git a/govcd/guestproperties.go b/govcd/guestproperties.go deleted file mode 100644 index 493484de5..000000000 --- a/govcd/guestproperties.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. - */ - -package govcd - -import ( - "fmt" - "net/http" - - "github.com/vmware/go-vcloud-director/v2/types/v56" -) - -// setGuestProperties is a shared function for both vApp and VM -func setGuestProperties(client *Client, href string, properties *types.ProductSectionList) error { - if href == "" { - return fmt.Errorf("href cannot be empty to set guest properties") - } - - properties.Xmlns = types.XMLNamespaceVCloud - properties.Ovf = types.XMLNamespaceOVF - - task, err := client.ExecuteTaskRequest(href+"/productSections", http.MethodPut, - types.MimeProductSection, "error setting guest properties: %s", properties) - - if err != nil { - return fmt.Errorf("unable to set guest properties: %s", err) - } - - err = task.WaitTaskCompletion() - if err != nil { - return fmt.Errorf("task for setting guest properties failed: %s", err) - } - - return nil -} - -// getGuestProperties is a shared function for both vApp and VM -func getGuestProperties(client *Client, href string) (*types.ProductSectionList, error) { - if href == "" { - return nil, fmt.Errorf("href cannot be empty to get guest properties") - } - properties := &types.ProductSectionList{} - - _, err := client.ExecuteRequest(href+"/productSections", http.MethodGet, - types.MimeProductSection, "error retrieving guest properties: %s", nil, properties) - - if err != nil { - return nil, fmt.Errorf("unable to retrieve guest properties: %s", err) - } - - return properties, nil -} diff --git a/govcd/productsection.go b/govcd/productsection.go new file mode 100644 index 000000000..b6233089c --- /dev/null +++ b/govcd/productsection.go @@ -0,0 +1,53 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// setProductSectionList is a shared function for both vApp and VM +func setProductSectionList(client *Client, href string, productSection *types.ProductSectionList) error { + if href == "" { + return fmt.Errorf("href cannot be empty to set product section") + } + + productSection.Xmlns = types.XMLNamespaceVCloud + productSection.Ovf = types.XMLNamespaceOVF + + task, err := client.ExecuteTaskRequest(href+"/productSections", http.MethodPut, + types.MimeProductSection, "error setting product section: %s", productSection) + + if err != nil { + return fmt.Errorf("unable to set product section: %s", err) + } + + err = task.WaitTaskCompletion() + if err != nil { + return fmt.Errorf("task for setting product section failed: %s", err) + } + + return nil +} + +// getProductSectionList is a shared function for both vApp and VM +func getProductSectionList(client *Client, href string) (*types.ProductSectionList, error) { + if href == "" { + return nil, fmt.Errorf("href cannot be empty to get product section") + } + productSection := &types.ProductSectionList{} + + _, err := client.ExecuteRequest(href+"/productSections", http.MethodGet, + types.MimeProductSection, "error retrieving product section : %s", nil, productSection) + + if err != nil { + return nil, fmt.Errorf("unable to retrieve product section: %s", err) + } + + return productSection, nil +} diff --git a/govcd/vapp.go b/govcd/vapp.go index 4089e1d83..701a93d98 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -539,7 +539,7 @@ func (vapp *VApp) ChangeVMName(name string) (Task, error) { // SetOvf sets guest properties for the first child VM in vApp // -// Deprecated: Use vm.SetGuestProperties() +// Deprecated: Use vm.SetProductSectionList() func (vapp *VApp) SetOvf(parameters map[string]string) (Task, error) { err := vapp.Refresh() if err != nil { @@ -838,17 +838,17 @@ func (vapp *VApp) RemoveAllNetworks() (Task, error) { return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{}) } -// SetGuestProperties sets guest properties for a vApp -func (vapp *VApp) SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) { - err := setGuestProperties(vapp.client, vapp.VApp.HREF, properties) +// SetProductSectionList sets product section for a vApp +func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { + err := setProductSectionList(vapp.client, vapp.VApp.HREF, productSection) if err != nil { - return nil, fmt.Errorf("unable to set vApp guest properties: %s", err) + return nil, fmt.Errorf("unable to set vApp product section: %s", err) } - return vapp.GetGuestProperties() + return vapp.GetProductSectionList() } -// GetGuestProperties retrieves guest properties for a vApp -func (vapp *VApp) GetGuestProperties() (*types.ProductSectionList, error) { - return getGuestProperties(vapp.client, vapp.VApp.HREF) +// GetProductSectionList retrieves product section for a vApp +func (vapp *VApp) GetProductSectionList() (*types.ProductSectionList, error) { + return getProductSectionList(vapp.client, vapp.VApp.HREF) } diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index 0ec992112..67fe0c677 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -681,9 +681,9 @@ func (vcd *TestVCD) Test_RemoveAllNetworks(check *C) { check.Assert(hasNetworks, Equals, false) } -// Test_VappSetGuestProperties sets vApp properties, retrieves them and deeply matches if properties -// were properly set using a propertyTester helper. -func (vcd *TestVCD) Test_VappSetGuestProperties(check *C) { +// Test_VappSetProductSectionList sets vApp product section, retrieves it and deeply matches if +// properties were properly set using a propertyTester helper. +func (vcd *TestVCD) Test_VappSetProductSectionList(check *C) { if vcd.skipVappTests { check.Skip("Skipping test because vapp was not successfully created at setup") } diff --git a/govcd/vapp_vm_test.go b/govcd/vapp_vm_test.go index 2ef1a5d44..285b68a54 100644 --- a/govcd/vapp_vm_test.go +++ b/govcd/vapp_vm_test.go @@ -12,15 +12,15 @@ import ( ) // guestPropertyGetSetter interface is used for covering tests in both VM and vApp guest property -type guestPropertyGetSetter interface { - GetGuestProperties() (*types.ProductSectionList, error) - SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) +type productSectionListGetSetter interface { + GetProductSectionList() (*types.ProductSectionList, error) + SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) } // propertyTester is a guest property setter accepting guestPropertyGetSetter interface for trying // out settings on all objects implementing such interface -func propertyTester(vcd *TestVCD, check *C, object guestPropertyGetSetter) { - vappProperties := &types.ProductSectionList{ +func propertyTester(vcd *TestVCD, check *C, object productSectionListGetSetter) { + productSection := &types.ProductSectionList{ ProductSection: &types.ProductSection{ Info: "Custom properties", Property: []*types.Property{ @@ -52,39 +52,39 @@ func propertyTester(vcd *TestVCD, check *C, object guestPropertyGetSetter) { }, } - gotProperties, err := object.SetGuestProperties(vappProperties) + gotproductSection, err := object.SetProductSectionList(productSection) check.Assert(err, IsNil) - getProperties, err := object.GetGuestProperties() + getproductSection, err := object.GetProductSectionList() check.Assert(err, IsNil) // Check that values were set in API - check.Assert(getProperties, NotNil) - check.Assert(getProperties.ProductSection, NotNil) - check.Assert(len(getProperties.ProductSection.Property), Equals, 3) + check.Assert(getproductSection, NotNil) + check.Assert(getproductSection.ProductSection, NotNil) + check.Assert(len(getproductSection.ProductSection.Property), Equals, 3) - check.Assert(getProperties.ProductSection.Property[0].Key, Equals, "sys_owner") - check.Assert(getProperties.ProductSection.Property[0].Label, Equals, "sys_owner_label") - check.Assert(getProperties.ProductSection.Property[0].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[0].Value.Value, Equals, "test") - check.Assert(getProperties.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") - check.Assert(getProperties.ProductSection.Property[0].UserConfigurable, Equals, false) + check.Assert(getproductSection.ProductSection.Property[0].Key, Equals, "sys_owner") + check.Assert(getproductSection.ProductSection.Property[0].Label, Equals, "sys_owner_label") + check.Assert(getproductSection.ProductSection.Property[0].Type, Equals, "string") + check.Assert(getproductSection.ProductSection.Property[0].Value.Value, Equals, "test") + check.Assert(getproductSection.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") + check.Assert(getproductSection.ProductSection.Property[0].UserConfigurable, Equals, false) - check.Assert(getProperties.ProductSection.Property[1].Key, Equals, "asset_tag") - check.Assert(getProperties.ProductSection.Property[1].Label, Equals, "asset_tag_label") - check.Assert(getProperties.ProductSection.Property[1].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") - check.Assert(getProperties.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") - check.Assert(getProperties.ProductSection.Property[1].UserConfigurable, Equals, true) + check.Assert(getproductSection.ProductSection.Property[1].Key, Equals, "asset_tag") + check.Assert(getproductSection.ProductSection.Property[1].Label, Equals, "asset_tag_label") + check.Assert(getproductSection.ProductSection.Property[1].Type, Equals, "string") + check.Assert(getproductSection.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") + check.Assert(getproductSection.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") + check.Assert(getproductSection.ProductSection.Property[1].UserConfigurable, Equals, true) - check.Assert(getProperties.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") - check.Assert(getProperties.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") - check.Assert(getProperties.ProductSection.Property[2].Type, Equals, "string") - check.Assert(getProperties.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") - check.Assert(getProperties.ProductSection.Property[2].DefaultValue, Equals, "default_ip") - check.Assert(getProperties.ProductSection.Property[2].UserConfigurable, Equals, true) + check.Assert(getproductSection.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") + check.Assert(getproductSection.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") + check.Assert(getproductSection.ProductSection.Property[2].Type, Equals, "string") + check.Assert(getproductSection.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") + check.Assert(getproductSection.ProductSection.Property[2].DefaultValue, Equals, "default_ip") + check.Assert(getproductSection.ProductSection.Property[2].UserConfigurable, Equals, true) // Ensure the object are deeply equal - check.Assert(gotProperties.ProductSection.Property, DeepEquals, vappProperties.ProductSection.Property) - check.Assert(getProperties, DeepEquals, gotProperties) + check.Assert(gotproductSection.ProductSection.Property, DeepEquals, productSection.ProductSection.Property) + check.Assert(getproductSection, DeepEquals, gotproductSection) } diff --git a/govcd/vm.go b/govcd/vm.go index 780e068c5..2ed74f47d 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -724,17 +724,17 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { "", errMessage, nil) } -// SetGuestProperties sets guest properties for a VM -func (vm *VM) SetGuestProperties(properties *types.ProductSectionList) (*types.ProductSectionList, error) { - err := setGuestProperties(vm.client, vm.VM.HREF, properties) +// SetProductSectionList sets product section for a VM +func (vm *VM) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { + err := setProductSectionList(vm.client, vm.VM.HREF, productSection) if err != nil { - return nil, fmt.Errorf("unable to set VM guest properties: %s", err) + return nil, fmt.Errorf("unable to set VM product section: %s", err) } - return vm.GetGuestProperties() + return vm.GetProductSectionList() } -// GetGuestProperties retrieves guest properties for a VM -func (vm *VM) GetGuestProperties() (*types.ProductSectionList, error) { - return getGuestProperties(vm.client, vm.VM.HREF) +// GetProductSectionList retrieves product section for a VM +func (vm *VM) GetProductSectionList() (*types.ProductSectionList, error) { + return getProductSectionList(vm.client, vm.VM.HREF) } diff --git a/govcd/vm_test.go b/govcd/vm_test.go index f2dc05245..c734d5c13 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -982,9 +982,9 @@ func (vcd *TestVCD) Test_BlockWhileGuestCustomizationStatus(check *C) { check.Assert(err, IsNil) } -// Test_VMSetGuestProperties sets guest properties, retrieves them and deeply matches if properties +// Test_VMSetProductSectionList sets product section, retrieves it and deeply matches if properties // were properly set using a propertyTester helper. -func (vcd *TestVCD) Test_VMSetGuestProperties(check *C) { +func (vcd *TestVCD) Test_VMSetProductSectionList(check *C) { if vcd.skipVappTests { check.Skip("Skipping test because vapp was not successfully created at setup") } From 9ff3419cd71de906d1c7abf6bfb1036def325507 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 13:42:35 +0300 Subject: [PATCH 15/22] Improve tests, add ordering --- govcd/vapp.go | 4 ++ govcd/vapp_vm_test.go | 33 +++++++------ govcd/vm.go | 4 ++ govcd/vm_unit_test.go | 103 ++++++++++++++++++++++++++++++++++++++++ types/v56/types.go | 10 ++++ types/v56/types_test.go | 36 ++++++++++++++ 6 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 types/v56/types_test.go diff --git a/govcd/vapp.go b/govcd/vapp.go index 701a93d98..0dec777ce 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -839,6 +839,8 @@ func (vapp *VApp) RemoveAllNetworks() (Task, error) { } // SetProductSectionList sets product section for a vApp +// +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { err := setProductSectionList(vapp.client, vapp.VApp.HREF, productSection) if err != nil { @@ -849,6 +851,8 @@ func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList } // GetProductSectionList retrieves product section for a vApp +// +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before func (vapp *VApp) GetProductSectionList() (*types.ProductSectionList, error) { return getProductSectionList(vapp.client, vapp.VApp.HREF) } diff --git a/govcd/vapp_vm_test.go b/govcd/vapp_vm_test.go index 285b68a54..cc23d10d9 100644 --- a/govcd/vapp_vm_test.go +++ b/govcd/vapp_vm_test.go @@ -52,37 +52,42 @@ func propertyTester(vcd *TestVCD, check *C, object productSectionListGetSetter) }, } + productSection.SortByPropertyKeyName() + gotproductSection, err := object.SetProductSectionList(productSection) check.Assert(err, IsNil) + gotproductSection.SortByPropertyKeyName() getproductSection, err := object.GetProductSectionList() check.Assert(err, IsNil) + getproductSection.SortByPropertyKeyName() // Check that values were set in API check.Assert(getproductSection, NotNil) check.Assert(getproductSection.ProductSection, NotNil) check.Assert(len(getproductSection.ProductSection.Property), Equals, 3) - check.Assert(getproductSection.ProductSection.Property[0].Key, Equals, "sys_owner") - check.Assert(getproductSection.ProductSection.Property[0].Label, Equals, "sys_owner_label") + check.Assert(getproductSection.ProductSection.Property[0].Key, Equals, "asset_tag") + check.Assert(getproductSection.ProductSection.Property[0].Label, Equals, "asset_tag_label") check.Assert(getproductSection.ProductSection.Property[0].Type, Equals, "string") - check.Assert(getproductSection.ProductSection.Property[0].Value.Value, Equals, "test") - check.Assert(getproductSection.ProductSection.Property[0].DefaultValue, Equals, "sys_owner_default") - check.Assert(getproductSection.ProductSection.Property[0].UserConfigurable, Equals, false) + check.Assert(getproductSection.ProductSection.Property[0].Value.Value, Equals, "xxxyyy") + check.Assert(getproductSection.ProductSection.Property[0].DefaultValue, Equals, "asset_tag_default") + check.Assert(getproductSection.ProductSection.Property[0].UserConfigurable, Equals, true) - check.Assert(getproductSection.ProductSection.Property[1].Key, Equals, "asset_tag") - check.Assert(getproductSection.ProductSection.Property[1].Label, Equals, "asset_tag_label") + check.Assert(getproductSection.ProductSection.Property[1].Key, Equals, "guestinfo.config.bootstrap.ip") + check.Assert(getproductSection.ProductSection.Property[1].Label, Equals, "guestinfo.config.bootstrap.ip_label") check.Assert(getproductSection.ProductSection.Property[1].Type, Equals, "string") - check.Assert(getproductSection.ProductSection.Property[1].Value.Value, Equals, "xxxyyy") - check.Assert(getproductSection.ProductSection.Property[1].DefaultValue, Equals, "asset_tag_default") + check.Assert(getproductSection.ProductSection.Property[1].Value.Value, Equals, "192.168.12.180") + check.Assert(getproductSection.ProductSection.Property[1].DefaultValue, Equals, "default_ip") check.Assert(getproductSection.ProductSection.Property[1].UserConfigurable, Equals, true) - check.Assert(getproductSection.ProductSection.Property[2].Key, Equals, "guestinfo.config.bootstrap.ip") - check.Assert(getproductSection.ProductSection.Property[2].Label, Equals, "guestinfo.config.bootstrap.ip_label") + check.Assert(getproductSection.ProductSection.Property[2].Key, Equals, "sys_owner") + check.Assert(getproductSection.ProductSection.Property[2].Label, Equals, "sys_owner_label") check.Assert(getproductSection.ProductSection.Property[2].Type, Equals, "string") - check.Assert(getproductSection.ProductSection.Property[2].Value.Value, Equals, "192.168.12.180") - check.Assert(getproductSection.ProductSection.Property[2].DefaultValue, Equals, "default_ip") - check.Assert(getproductSection.ProductSection.Property[2].UserConfigurable, Equals, true) + check.Assert(getproductSection.ProductSection.Property[2].Value.Value, Equals, "test") + check.Assert(getproductSection.ProductSection.Property[2].DefaultValue, Equals, "sys_owner_default") + check.Assert(getproductSection.ProductSection.Property[2].UserConfigurable, Equals, false) + // Ensure the object are deeply equal check.Assert(gotproductSection.ProductSection.Property, DeepEquals, productSection.ProductSection.Property) diff --git a/govcd/vm.go b/govcd/vm.go index 2ed74f47d..0081b0cdb 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -725,6 +725,8 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { } // SetProductSectionList sets product section for a VM +// +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before func (vm *VM) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { err := setProductSectionList(vm.client, vm.VM.HREF, productSection) if err != nil { @@ -735,6 +737,8 @@ func (vm *VM) SetProductSectionList(productSection *types.ProductSectionList) (* } // GetProductSectionList retrieves product section for a VM +// +// The slice of properties is not necessarily ordered or returned as set before func (vm *VM) GetProductSectionList() (*types.ProductSectionList, error) { return getProductSectionList(vm.client, vm.VM.HREF) } diff --git a/govcd/vm_unit_test.go b/govcd/vm_unit_test.go index 88f457e95..62cad89a3 100644 --- a/govcd/vm_unit_test.go +++ b/govcd/vm_unit_test.go @@ -7,6 +7,7 @@ package govcd import ( + "reflect" "testing" "github.com/vmware/go-vcloud-director/v2/types/v56" @@ -270,3 +271,105 @@ func Test_VMupdateNicParameters_singleNIC(t *testing.T) { } } + + +// TestProductSectionList_SortByPropertyKeyName validates that a +// SortByPropertyKeyName() works on ProductSectionList +func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { + sliceProductSection := &types.ProductSectionList{ + ProductSection: &types.ProductSection{}, + } + + emptyProductSection := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + }, + } + + // unordered list for test + sortOrder := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + Property: []*types.Property{ + &types.Property{ + UserConfigurable: false, + Key: "sys_owner", + Label: "sys_owner_label", + Type: "string", + DefaultValue: "sys_owner_default", + Value: &types.Value{Value: "test"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "asset_tag", + Label: "asset_tag_label", + Type: "string", + DefaultValue: "asset_tag_default", + Value: &types.Value{Value: "xxxyyy"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "guestinfo.config.bootstrap.ip", + Label: "guestinfo.config.bootstrap.ip_label", + Type: "string", + DefaultValue: "default_ip", + Value: &types.Value{Value: "192.168.12.180"}, + }, + }, + }, + } + // correct state after ordering + expectedSortedOrder := &types.ProductSectionList{ + ProductSection: &types.ProductSection{ + Info: "Custom properties", + Property: []*types.Property{ + &types.Property{ + UserConfigurable: true, + Key: "asset_tag", + Label: "asset_tag_label", + Type: "string", + DefaultValue: "asset_tag_default", + Value: &types.Value{Value: "xxxyyy"}, + }, + &types.Property{ + UserConfigurable: true, + Key: "guestinfo.config.bootstrap.ip", + Label: "guestinfo.config.bootstrap.ip_label", + Type: "string", + DefaultValue: "default_ip", + Value: &types.Value{Value: "192.168.12.180"}, + }, + &types.Property{ + UserConfigurable: false, + Key: "sys_owner", + Label: "sys_owner_label", + Type: "string", + DefaultValue: "sys_owner_default", + Value: &types.Value{Value: "test"}, + }, + }, + }, + } + + + tests := []struct { + name string + ps *types.ProductSectionList + expected *types.ProductSectionList + }{ + {name: "Slice", ps: sliceProductSection, expected: sliceProductSection}, + {name: "Empty", ps: emptyProductSection, expected: emptyProductSection}, + {name: "SortOrder", ps: sortOrder, expected: expectedSortedOrder}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := tt.ps + p.SortByPropertyKeyName() + + if !reflect.DeepEqual(tt.ps, tt.expected) { + t.Errorf("Results was not as expected: \n%#+v\n, got:\n %#+v\n", tt.expected, tt.ps) + } + + }) + } +} diff --git a/types/v56/types.go b/types/v56/types.go index 7af1c2139..272c97070 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -5,6 +5,7 @@ package types import ( + "sort" "encoding/xml" "fmt" ) @@ -1172,6 +1173,15 @@ type ProductSectionList struct { ProductSection *ProductSection `xml:"http://schemas.dmtf.org/ovf/envelope/1 ProductSection,omitempty"` } +// SortByPropertyKeyName allows to sort ProductSectionList property slice by key name as the API is +// does not always return an ordered slice +func (p *ProductSectionList) SortByPropertyKeyName() { + sort.SliceStable(p.ProductSection.Property, func(i, j int) bool { + return p.ProductSection.Property[i].Key < p.ProductSection.Property[j].Key + }) + return +} + type ProductSection struct { Info string `xml:"Info,omitempty"` Property []*Property `xml:"http://schemas.dmtf.org/ovf/envelope/1 Property,omitempty"` diff --git a/types/v56/types_test.go b/types/v56/types_test.go new file mode 100644 index 000000000..0acef9434 --- /dev/null +++ b/types/v56/types_test.go @@ -0,0 +1,36 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package types + +import ( + "encoding/xml" + "testing" +) + +func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { + type fields struct { + XMLName xml.Name + Ovf string + Xmlns string + ProductSection *ProductSection + } + tests := []struct { + name string + fields fields + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &ProductSectionList{ + XMLName: tt.fields.XMLName, + Ovf: tt.fields.Ovf, + Xmlns: tt.fields.Xmlns, + ProductSection: tt.fields.ProductSection, + } + p.SortByPropertyKeyName() + }) + } +} From 6a50cdeb673b436168cb42379f710e86585575ad Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 13:43:39 +0300 Subject: [PATCH 16/22] Remove types_test.go --- types/v56/types_test.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 types/v56/types_test.go diff --git a/types/v56/types_test.go b/types/v56/types_test.go deleted file mode 100644 index 0acef9434..000000000 --- a/types/v56/types_test.go +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. - */ - -package types - -import ( - "encoding/xml" - "testing" -) - -func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { - type fields struct { - XMLName xml.Name - Ovf string - Xmlns string - ProductSection *ProductSection - } - tests := []struct { - name string - fields fields - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := &ProductSectionList{ - XMLName: tt.fields.XMLName, - Ovf: tt.fields.Ovf, - Xmlns: tt.fields.Xmlns, - ProductSection: tt.fields.ProductSection, - } - p.SortByPropertyKeyName() - }) - } -} From 7f38471b34d0fdf0ab106d4f185e787ea8eb6d78 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 13:47:45 +0300 Subject: [PATCH 17/22] Fix unit test --- govcd/vm_unit_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/govcd/vm_unit_test.go b/govcd/vm_unit_test.go index 62cad89a3..f8f609ea0 100644 --- a/govcd/vm_unit_test.go +++ b/govcd/vm_unit_test.go @@ -274,7 +274,8 @@ func Test_VMupdateNicParameters_singleNIC(t *testing.T) { // TestProductSectionList_SortByPropertyKeyName validates that a -// SortByPropertyKeyName() works on ProductSectionList +// SortByPropertyKeyName() works on ProductSectionList and can handle empty properties as well as +// sort correctly func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { sliceProductSection := &types.ProductSectionList{ ProductSection: &types.ProductSection{}, @@ -354,20 +355,20 @@ func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { tests := []struct { name string - ps *types.ProductSectionList - expected *types.ProductSectionList + setValue *types.ProductSectionList + expectedValue *types.ProductSectionList }{ - {name: "Slice", ps: sliceProductSection, expected: sliceProductSection}, - {name: "Empty", ps: emptyProductSection, expected: emptyProductSection}, - {name: "SortOrder", ps: sortOrder, expected: expectedSortedOrder}, + {name: "Empty", setValue: emptyProductSection, expectedValue: emptyProductSection}, + {name: "Slice", setValue: sliceProductSection, expectedValue: sliceProductSection}, + {name: "SortOrder", setValue: sortOrder, expectedValue: expectedSortedOrder}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := tt.ps + p := tt.setValue p.SortByPropertyKeyName() - if !reflect.DeepEqual(tt.ps, tt.expected) { - t.Errorf("Results was not as expected: \n%#+v\n, got:\n %#+v\n", tt.expected, tt.ps) + if !reflect.DeepEqual(p, tt.expectedValue) { + t.Errorf("Objects were not deeply equal: \n%#+v\n, got:\n %#+v\n", tt.expectedValue, p) } }) From 45bab31a1e8143c993c682f4331d4a57c2202ca1 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 13:49:01 +0300 Subject: [PATCH 18/22] make fmt --- govcd/vapp_vm_test.go | 1 - govcd/vm_unit_test.go | 8 +++----- types/v56/types.go | 6 +++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/govcd/vapp_vm_test.go b/govcd/vapp_vm_test.go index cc23d10d9..3800f9d7a 100644 --- a/govcd/vapp_vm_test.go +++ b/govcd/vapp_vm_test.go @@ -88,7 +88,6 @@ func propertyTester(vcd *TestVCD, check *C, object productSectionListGetSetter) check.Assert(getproductSection.ProductSection.Property[2].DefaultValue, Equals, "sys_owner_default") check.Assert(getproductSection.ProductSection.Property[2].UserConfigurable, Equals, false) - // Ensure the object are deeply equal check.Assert(gotproductSection.ProductSection.Property, DeepEquals, productSection.ProductSection.Property) check.Assert(getproductSection, DeepEquals, gotproductSection) diff --git a/govcd/vm_unit_test.go b/govcd/vm_unit_test.go index f8f609ea0..502649bf8 100644 --- a/govcd/vm_unit_test.go +++ b/govcd/vm_unit_test.go @@ -272,8 +272,7 @@ func Test_VMupdateNicParameters_singleNIC(t *testing.T) { } } - -// TestProductSectionList_SortByPropertyKeyName validates that a +// TestProductSectionList_SortByPropertyKeyName validates that a // SortByPropertyKeyName() works on ProductSectionList and can handle empty properties as well as // sort correctly func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { @@ -352,10 +351,9 @@ func TestProductSectionList_SortByPropertyKeyName(t *testing.T) { }, } - tests := []struct { - name string - setValue *types.ProductSectionList + name string + setValue *types.ProductSectionList expectedValue *types.ProductSectionList }{ {name: "Empty", setValue: emptyProductSection, expectedValue: emptyProductSection}, diff --git a/types/v56/types.go b/types/v56/types.go index 272c97070..b3a9c639b 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -5,9 +5,9 @@ package types import ( - "sort" "encoding/xml" "fmt" + "sort" ) // Maps status Attribute Values for VAppTemplate, VApp, Vm, and Media Objects @@ -1177,9 +1177,9 @@ type ProductSectionList struct { // does not always return an ordered slice func (p *ProductSectionList) SortByPropertyKeyName() { sort.SliceStable(p.ProductSection.Property, func(i, j int) bool { - return p.ProductSection.Property[i].Key < p.ProductSection.Property[j].Key + return p.ProductSection.Property[i].Key < p.ProductSection.Property[j].Key }) - return + return } type ProductSection struct { From 05858ea0c32e4ecb77e58b1bade00c3bb3c98e6b Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 14:00:01 +0300 Subject: [PATCH 19/22] Fix changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3662e4211..765c3d4c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,8 +22,8 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` -* Added methods `vm.SetGuestProperties` and `vm.GetGuestProperties` [#235](https://github.com/vmware/go-vcloud-director/pull/235) -* Added methods `vapp.SetGuestProperties` and `vapp.GetGuestProperties` [#235](https://github.com/vmware/go-vcloud-director/pull/235) +* Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` [#235](https://github.com/vmware/go-vcloud-director/pull/235) +* Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` [#235](https://github.com/vmware/go-vcloud-director/pull/235) IMPROVEMENTS: From 7ba6a7fb81464fc8142a24569bc8424a4e8469d2 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 14:07:39 +0300 Subject: [PATCH 20/22] Tune comments to hint guest property setting --- CHANGELOG.md | 6 ++++-- govcd/vapp.go | 10 ++++++---- govcd/vm.go | 10 ++++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 765c3d4c9..cdda18860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,8 +22,10 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` -* Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` [#235](https://github.com/vmware/go-vcloud-director/pull/235) -* Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` [#235](https://github.com/vmware/go-vcloud-director/pull/235) +* Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` allowing to maniapulate VM +guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) +* Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` allowing to manipulate +vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) IMPROVEMENTS: diff --git a/govcd/vapp.go b/govcd/vapp.go index 0dec777ce..61e831be2 100644 --- a/govcd/vapp.go +++ b/govcd/vapp.go @@ -838,9 +838,10 @@ func (vapp *VApp) RemoveAllNetworks() (Task, error) { return updateNetworkConfigurations(vapp, []types.VAppNetworkConfiguration{}) } -// SetProductSectionList sets product section for a vApp +// SetProductSectionList sets product section for a vApp. It allows to change vApp guest properties. // -// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered +// or returned as set before func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { err := setProductSectionList(vapp.client, vapp.VApp.HREF, productSection) if err != nil { @@ -850,9 +851,10 @@ func (vapp *VApp) SetProductSectionList(productSection *types.ProductSectionList return vapp.GetProductSectionList() } -// GetProductSectionList retrieves product section for a vApp +// GetProductSectionList retrieves product section for a vApp. It allows to read vApp guest properties. // -// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered +// or returned as set before func (vapp *VApp) GetProductSectionList() (*types.ProductSectionList, error) { return getProductSectionList(vapp.client, vapp.VApp.HREF) } diff --git a/govcd/vm.go b/govcd/vm.go index 0081b0cdb..744564045 100644 --- a/govcd/vm.go +++ b/govcd/vm.go @@ -724,9 +724,10 @@ func (vm *VM) ToggleHardwareVirtualization(isEnabled bool) (Task, error) { "", errMessage, nil) } -// SetProductSectionList sets product section for a VM +// SetProductSectionList sets product section for a VM. It allows to change VM guest properties. // -// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered or returned as set before +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered +// or returned as set before func (vm *VM) SetProductSectionList(productSection *types.ProductSectionList) (*types.ProductSectionList, error) { err := setProductSectionList(vm.client, vm.VM.HREF, productSection) if err != nil { @@ -736,9 +737,10 @@ func (vm *VM) SetProductSectionList(productSection *types.ProductSectionList) (* return vm.GetProductSectionList() } -// GetProductSectionList retrieves product section for a VM +// GetProductSectionList retrieves product section for a VM. It allows to read VM guest properties. // -// The slice of properties is not necessarily ordered or returned as set before +// The slice of properties "ProductSectionList.ProductSection.Property" is not necessarily ordered +// or returned as set before func (vm *VM) GetProductSectionList() (*types.ProductSectionList, error) { return getProductSectionList(vm.client, vm.VM.HREF) } From 5978a76dbfda5dd71aef41fee4bd4938cc85f84d Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 27 Aug 2019 14:17:14 +0300 Subject: [PATCH 21/22] Fix changelog typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdda18860..d8dd04223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` -* Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` allowing to maniapulate VM +* Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` allowing to manipulate VM guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` allowing to manipulate vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) From fb4c821ed449d8b42880d24f210330bc05e94bbc Mon Sep 17 00:00:00 2001 From: Dainius S Date: Thu, 29 Aug 2019 06:47:10 +0300 Subject: [PATCH 22/22] Add note to changelog about PR #200 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b287d9f73..228164c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * Added methods `AdminOrg.GetVDCByName` and related `GetVDCById`, `GetVDCByNameOrId` * Added methods `AdminOrg.GetAdminVDCByName` and related `GetAdminVDCById`, `GetAdminVDCByNameOrId` * Added methods `Catalog.Refresh` and `AdminCatalog.Refresh` +* Added method `vm.GetVirtualHardwareSection` to retrieve virtual hardware items [#200](https://github.com/vmware/go-vcloud-director/pull/200) * Added methods `vm.SetProductSectionList` and `vm.GetProductSectionList` allowing to manipulate VM guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` allowing to manipulate