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

Mutex change #255

Merged
merged 33 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cb6e118
Add initial Mutex change
vbauzys May 31, 2019
5dd310c
Improvements
vbauzys Jun 3, 2019
72816cd
Improvements
vbauzys Jun 3, 2019
5d13a51
Added logging and improved key construction
vbauzys Jun 3, 2019
e754201
Added lock for vm update
vbauzys Jun 3, 2019
fb8ec5f
Improve imports
vbauzys Jun 3, 2019
f015028
Improvements
vbauzys Jun 3, 2019
0ebe81d
Add missing dependency
vbauzys Jun 3, 2019
4cc5f27
fix mistype
vbauzys Jun 3, 2019
836c4a3
Removed additional retry call
vbauzys Jun 3, 2019
5c18736
Improvement
vbauzys Jun 4, 2019
280890a
Improvement
vbauzys Jun 4, 2019
1891e7b
Fix test
vbauzys Jun 4, 2019
eec8035
Test improvements
vbauzys Jun 4, 2019
7dd5da4
Add improvements
vbauzys Jun 5, 2019
a1bed33
Bump govcd version and add networks detach from VAPP before delete
vbauzys Jun 5, 2019
30041c7
Remove not need code
vbauzys Jun 5, 2019
94e9d7d
Improve test
vbauzys Jun 5, 2019
480536d
fix mistypes
vbauzys Jun 5, 2019
b66f6bc
Improve comment
vbauzys Jun 5, 2019
c84aae9
Remove local env specifics
vbauzys Jun 5, 2019
30b9f24
Add needed depends on
vbauzys Jun 5, 2019
9f68eb2
Add needed depends on
vbauzys Jun 5, 2019
4fa08c3
Fix for race condition with deleting vm and depending independent disk
vbauzys Jun 6, 2019
998776c
Improve locking - fix issue with not exisiting inheritance
vbauzys Jun 6, 2019
9f77106
Moved detach disk after power off
vbauzys Jun 6, 2019
253516f
Rename function names
vbauzys Jun 6, 2019
82fdc30
Add checks for errors
vbauzys Jun 6, 2019
fcca995
Improve comment
vbauzys Jun 7, 2019
f39bb62
Added custom locks for vapp network
vbauzys Jun 7, 2019
2f738a4
Added custom locks for insert media, cause impacts vApp
vbauzys Jun 7, 2019
63430f8
Improved error messages
vbauzys Jun 7, 2019
94bb963
Improved error messages
vbauzys Jun 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 2.4.0 (Unreleased)
IMPROVEMENTS:

* Change resource handling to use locking mechanism when resource parallel handling is not supported by vCD. [GH-#255]

## 2.3.0 (May 29, 2019)

IMPROVEMENTS:
Expand All @@ -17,7 +21,7 @@ FEATURES:
* `vcd_vapp_vm` - Ability to add metadata to a VM. For previous behaviour please see `BACKWARDS INCOMPATIBILITIES` ([#158](https://github.com/terraform-providers/terraform-provider-vcd/issues/158))
* `vcd_vapp_vm` - Ability to enable hardware assisted CPU virtualization for VM. It allows hypervisor nesting. ([#219](https://github.com/terraform-providers/terraform-provider-vcd/issues/219))
* **New Resource:** external network - `vcd_external_network` - ([#230](https://github.com/terraform-providers/terraform-provider-vcd/issues/230))
* **New Resource:** VDC resource `vcd_org_vdc` - ([#234](https://github.com/terraform-providers/terraform-provider-vcd/issues/234))
* **New Resource:** VDC resource `vcd_org_vdc` - ([#236](https://github.com/terraform-providers/terraform-provider-vcd/issues/236))
* resource/vcd_vapp_vm: Add `network` argument for multiple NIC support and more flexible configuration ([#233](https://github.com/terraform-providers/terraform-provider-vcd/issues/233))
* resource/vcd_vapp_vm: Add `mac` argument to store MAC address in state file ([#233](https://github.com/terraform-providers/terraform-provider-vcd/issues/233))

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ go 1.12

require (
github.com/hashicorp/terraform v0.12.0
github.com/vmware/go-vcloud-director/v2 v2.2.0
github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.1
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmware/go-vcloud-director/v2 v2.2.0 h1:mfxgMKeErH9lSVyYI7QOcM9OYsSbphcptPa6EG+haMw=
github.com/vmware/go-vcloud-director/v2 v2.2.0/go.mod h1:HonlGxbjJ1NAibWh99eE4/S2l6ZOZ5KJzKK1rh2a9vc=
github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.1 h1:zQD5RbcompjwAeACoJjtMgTeUc4lZaLceBv66j5Ayy8=
github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.1/go.mod h1:HonlGxbjJ1NAibWh99eE4/S2l6ZOZ5KJzKK1rh2a9vc=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
Expand Down
74 changes: 74 additions & 0 deletions vcd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"runtime"
"time"

"github.com/hashicorp/terraform/helper/mutexkv"
"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/go-vcloud-director/v2/govcd"
)
Expand Down Expand Up @@ -90,6 +91,79 @@ func debugPrintf(format string, args ...interface{}) {
}
}

// This is a global MutexKV for all resources
var vcdMutexKV = mutexkv.NewMutexKV()

func (cli *VCDClient) lockVapp(d *schema.ResourceData) {
vappName := d.Get("name").(string)
if vappName == "" {
panic("vapp name isn't found")
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
}
key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s", cli.getOrgName(d), cli.getVdcName(d), vappName)
vcdMutexKV.Lock(key)
}

func (cli *VCDClient) unLockVapp(d *schema.ResourceData) {
vappName := d.Get("name").(string)
if vappName == "" {
panic("vapp name isn't found")
}
key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s", cli.getOrgName(d), cli.getVdcName(d), vappName)
vcdMutexKV.Unlock(key)
}

func (cli *VCDClient) lockParentVapp(d *schema.ResourceData) {
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
vappName := d.Get("vapp_name").(string)
if vappName == "" {
panic("vapp name isn't found")
}
key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s", cli.getOrgName(d), cli.getVdcName(d), vappName)
vcdMutexKV.Lock(key)
}

func (cli *VCDClient) unLockParentVapp(d *schema.ResourceData) {
vappName := d.Get("vapp_name").(string)
if vappName == "" {
panic("vapp name isn't found")
}
key := fmt.Sprintf("org:%s|vdc:%s|vapp:%s", cli.getOrgName(d), cli.getVdcName(d), vappName)
vcdMutexKV.Unlock(key)
}

func (cli *VCDClient) lockParentEdgeGtw(d *schema.ResourceData) {
edgeGtwName := d.Get("edge_gateway").(string)
if edgeGtwName == "" {
panic("edge gtw name isn't found")
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
}
key := fmt.Sprintf("org:%s|vdc:%s|edge:%s", cli.getOrgName(d), cli.getVdcName(d), edgeGtwName)
vcdMutexKV.Lock(key)
}

func (cli *VCDClient) unLockParentEdgeGtw(d *schema.ResourceData) {
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
edgeGtwName := d.Get("edge_gateway").(string)
if edgeGtwName == "" {
panic("edge gtw name isn't found")
}
key := fmt.Sprintf("org:%s|vdc:%s|edge:%s", cli.getOrgName(d), cli.getVdcName(d), edgeGtwName)
vcdMutexKV.Unlock(key)
}

func (cli *VCDClient) getOrgName(d *schema.ResourceData) string {
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
orgName := d.Get("org").(string)
if orgName == "" {
orgName = cli.Org
}
return orgName
}

func (cli *VCDClient) getVdcName(d *schema.ResourceData) string {
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
orgName := d.Get("vdc").(string)
if orgName == "" {
orgName = cli.Vdc
}
return orgName
}

// GetOrgAndVdc finds a pair of org and vdc using the names provided
// in the args. If the names are empty, it will use the default
// org and vdc names from the provider.
Expand Down
94 changes: 42 additions & 52 deletions vcd/resource_vcd_dnat.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log"
"strings"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/vmware/go-vcloud-director/v2/types/v56"
Expand Down Expand Up @@ -90,8 +89,9 @@ func resourceVcdDNATCreate(d *schema.ResourceData, meta interface{}) error {
// Multiple VCD components need to run operations on the Edge Gateway, as
// the edge gateway will throw back an error if it is already performing an
// operation we must wait until we can acquire a lock on the client
vcdClient.Mutex.Lock()
defer vcdClient.Mutex.Unlock()
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)

portString := getPortString(d.Get("port").(int))
translatedPortString := portString // default
if d.Get("translated_port").(int) > 0 {
Expand Down Expand Up @@ -122,46 +122,38 @@ func resourceVcdDNATCreate(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("unable to find orgVdcnetwork: %s, err: %s", providedNetworkName.(string), err)
}

// Creating a loop to offer further protection from the edge gateway erroring
// due to being busy eg another person is using another client so wouldn't be
// constrained by out lock. If the edge gateway reurns with a busy error, wait
// 3 seconds and then try again. Continue until a non-busy error or success

if nil != providedNetworkName && providedNetworkName != "" {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.AddNATPortMappingWithUplink(orgVdcnetwork, "DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString, protocol,
icmpSubType)
if err != nil {
return resource.RetryableError(
fmt.Errorf("error setting DNAT rules: %#v", err))
}
task, err := edgeGateway.AddNATPortMappingWithUplink(orgVdcnetwork, "DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString, protocol,
icmpSubType)
if err != nil {
return fmt.Errorf("error setting DNAT rules: %#v", err)
}

err = task.WaitTaskCompletion()
dataclouder marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return fmt.Errorf("error completing tasks: %#v", err)
}

return resource.RetryableError(task.WaitTaskCompletion())
})
} else {
// TODO remove when major release is done
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.AddNATPortMapping("DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString, protocol,
icmpSubType)
if err != nil {
return resource.RetryableError(
fmt.Errorf("error setting DNAT rules: %#v", err))
}

return resource.RetryableError(task.WaitTaskCompletion())
})
}
task, err := edgeGateway.AddNATPortMapping("DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString, protocol,
icmpSubType)
if err != nil {
return fmt.Errorf("error setting DNAT rules: %#v", err)
}

if err != nil {
return fmt.Errorf("error completing tasks: %#v", err)
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf("error completing tasks: %#v", err)
}
}

if nil != providedNetworkName && "" != providedNetworkName {
Expand Down Expand Up @@ -218,8 +210,9 @@ func resourceVcdDNATDelete(d *schema.ResourceData, meta interface{}) error {
// Multiple VCD components need to run operations on the Edge Gateway, as
// the edge gatway will throw back an error if it is already performing an
// operation we must wait until we can aquire a lock on the client
vcdClient.Mutex.Lock()
defer vcdClient.Mutex.Unlock()
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)

portString := getPortString(d.Get("port").(int))
translatedPortString := portString // default
if d.Get("translated_port").(int) > 0 {
Expand All @@ -231,19 +224,16 @@ func resourceVcdDNATDelete(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return fmt.Errorf(errorUnableToFindEdgeGateway, err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.RemoveNATPortMapping("DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString)
if err != nil {
return resource.RetryableError(
fmt.Errorf("error setting DNAT rules: %#v", err))
}
task, err := edgeGateway.RemoveNATPortMapping("DNAT",
d.Get("external_ip").(string),
portString,
d.Get("internal_ip").(string),
translatedPortString)
if err != nil {
return fmt.Errorf("error setting DNAT rules: %#v", err)
}

return resource.RetryableError(task.WaitTaskCompletion())
})
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf("error completing tasks: %#v", err)
}
Expand Down
61 changes: 26 additions & 35 deletions vcd/resource_vcd_edgegateway_vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log"
"strconv"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/go-vcloud-director/v2/types/v56"
)
Expand Down Expand Up @@ -142,8 +141,8 @@ func resourceVcdEdgeGatewayVpnCreate(d *schema.ResourceData, meta interface{}) e
vcdClient := meta.(*VCDClient)
log.Printf("[TRACE] CLIENT: %#v", vcdClient)

vcdClient.Mutex.Lock()
defer vcdClient.Mutex.Unlock()
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)

edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d)
if err != nil {
Expand Down Expand Up @@ -206,22 +205,18 @@ func resourceVcdEdgeGatewayVpnCreate(d *schema.ResourceData, meta interface{}) e

log.Printf("[INFO] ipsecVPNConfig: %#v", ipsecVPNConfig)

err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
err := edgeGateway.Refresh()
if err != nil {
log.Printf("[INFO] Error refreshing edge gateway: %#v", err)
return resource.RetryableError(
fmt.Errorf("error error refreshing edge gateway: %#v", err))
}
task, err := edgeGateway.AddIpsecVPN(ipsecVPNConfig)
if err != nil {
log.Printf("[INFO] Error setting ipsecVPNConfig rules: %s", err)
return resource.RetryableError(
fmt.Errorf("error setting ipsecVPNConfig rules: %#v", err))
}
err = edgeGateway.Refresh()
if err != nil {
log.Printf("[INFO] Error refreshing edge gateway: %#v", err)
return fmt.Errorf("error refreshing edge gateway: %#v", err)
}
task, err := edgeGateway.AddIpsecVPN(ipsecVPNConfig)
if err != nil {
log.Printf("[INFO] Error setting ipsecVPNConfig rules: %s", err)
return fmt.Errorf("error setting ipsecVPNConfig rules: %#v", err)
}

return resource.RetryableError(task.WaitTaskCompletion())
})
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf(errorCompletingTask, err)
}
Expand All @@ -236,8 +231,8 @@ func resourceVcdEdgeGatewayVpnDelete(d *schema.ResourceData, meta interface{}) e

log.Printf("[TRACE] CLIENT: %#v", vcdClient)

vcdClient.Mutex.Lock()
defer vcdClient.Mutex.Unlock()
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)

edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d)
if err != nil {
Expand All @@ -253,22 +248,18 @@ func resourceVcdEdgeGatewayVpnDelete(d *schema.ResourceData, meta interface{}) e

log.Printf("[INFO] ipsecVPNConfig: %#v", ipsecVPNConfig)

err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
err := edgeGateway.Refresh()
if err != nil {
log.Printf("[INFO] Error refreshing edge gateway: %#v", err)
return resource.RetryableError(
fmt.Errorf("error error refreshing edge gateway: %#v", err))
}
task, err := edgeGateway.AddIpsecVPN(ipsecVPNConfig)
if err != nil {
log.Printf("[INFO] Error setting ipsecVPNConfig rules: %s", err)
return resource.RetryableError(
fmt.Errorf("error setting ipsecVPNConfig rules: %#v", err))
}
err = edgeGateway.Refresh()
if err != nil {
log.Printf("[INFO] Error refreshing edge gateway: %#v", err)
return fmt.Errorf("error refreshing edge gateway: %#v", err)
}
task, err := edgeGateway.AddIpsecVPN(ipsecVPNConfig)
if err != nil {
log.Printf("[INFO] Error setting ipsecVPNConfig rules: %s", err)
return fmt.Errorf("error setting ipsecVPNConfig rules: %#v", err)
}

return resource.RetryableError(task.WaitTaskCompletion())
})
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf(errorCompletingTask, err)
}
Expand Down
Loading