-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3785 from hmrc/master
VMWare vCloud Director Support
- Loading branch information
Showing
24 changed files
with
2,347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/builtin/providers/vcd" | ||
"github.com/hashicorp/terraform/plugin" | ||
) | ||
|
||
func main() { | ||
plugin.Serve(&plugin.ServeOpts{ | ||
ProviderFunc: vcd.Provider, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package vcd | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
|
||
"github.com/hmrc/vmware-govcd" | ||
) | ||
|
||
type Config struct { | ||
User string | ||
Password string | ||
Org string | ||
Href string | ||
VDC string | ||
MaxRetryTimeout int | ||
} | ||
|
||
type VCDClient struct { | ||
*govcd.VCDClient | ||
MaxRetryTimeout int | ||
} | ||
|
||
func (c *Config) Client() (*VCDClient, error) { | ||
u, err := url.ParseRequestURI(c.Href) | ||
if err != nil { | ||
return nil, fmt.Errorf("Something went wrong: %s", err) | ||
} | ||
|
||
vcdclient := &VCDClient{ | ||
govcd.NewVCDClient(*u), | ||
c.MaxRetryTimeout} | ||
org, vcd, err := vcdclient.Authenticate(c.User, c.Password, c.Org, c.VDC) | ||
if err != nil { | ||
return nil, fmt.Errorf("Something went wrong: %s", err) | ||
} | ||
vcdclient.Org = org | ||
vcdclient.OrgVdc = vcd | ||
return vcdclient, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package vcd | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
// Provider returns a terraform.ResourceProvider. | ||
func Provider() terraform.ResourceProvider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{ | ||
"user": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_USER", nil), | ||
Description: "The user name for vcd API operations.", | ||
}, | ||
|
||
"password": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_PASSWORD", nil), | ||
Description: "The user password for vcd API operations.", | ||
}, | ||
|
||
"org": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_ORG", nil), | ||
Description: "The vcd org for API operations", | ||
}, | ||
|
||
"url": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_URL", nil), | ||
Description: "The vcd url for vcd API operations.", | ||
}, | ||
|
||
"vdc": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_VDC", ""), | ||
Description: "The name of the VDC to run operations on", | ||
}, | ||
|
||
"maxRetryTimeout": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
DefaultFunc: schema.EnvDefaultFunc("VCD_MAX_RETRY_TIMEOUT", 60), | ||
Description: "Max num seconds to wait for successful response when operating on resources within vCloud (defaults to 60)", | ||
}, | ||
}, | ||
|
||
ResourcesMap: map[string]*schema.Resource{ | ||
"vcd_network": resourceVcdNetwork(), | ||
"vcd_vapp": resourceVcdVApp(), | ||
"vcd_firewall_rules": resourceVcdFirewallRules(), | ||
"vcd_dnat": resourceVcdDNAT(), | ||
"vcd_snat": resourceVcdSNAT(), | ||
}, | ||
|
||
ConfigureFunc: providerConfigure, | ||
} | ||
} | ||
|
||
func providerConfigure(d *schema.ResourceData) (interface{}, error) { | ||
config := Config{ | ||
User: d.Get("user").(string), | ||
Password: d.Get("password").(string), | ||
Org: d.Get("org").(string), | ||
Href: d.Get("url").(string), | ||
VDC: d.Get("vdc").(string), | ||
MaxRetryTimeout: d.Get("maxRetryTimeout").(int), | ||
} | ||
|
||
return config.Client() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package vcd | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
var testAccProviders map[string]terraform.ResourceProvider | ||
var testAccProvider *schema.Provider | ||
|
||
func init() { | ||
testAccProvider = Provider().(*schema.Provider) | ||
testAccProviders = map[string]terraform.ResourceProvider{ | ||
"vcd": testAccProvider, | ||
} | ||
} | ||
|
||
func TestProvider(t *testing.T) { | ||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
} | ||
|
||
func TestProvider_impl(t *testing.T) { | ||
var _ terraform.ResourceProvider = Provider() | ||
} | ||
|
||
func testAccPreCheck(t *testing.T) { | ||
if v := os.Getenv("VCD_USER"); v == "" { | ||
t.Fatal("VCD_USER must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("VCD_PASSWORD"); v == "" { | ||
t.Fatal("VCD_PASSWORD must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("VCD_ORG"); v == "" { | ||
t.Fatal("VCD_ORG must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("VCD_URL"); v == "" { | ||
t.Fatal("VCD_URL must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("VCD_EDGE_GATEWAY"); v == "" { | ||
t.Fatal("VCD_EDGE_GATEWAY must be set for acceptance tests") | ||
} | ||
if v := os.Getenv("VCD_VDC"); v == "" { | ||
t.Fatal("VCD_VDC must be set for acceptance tests") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package vcd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceVcdDNAT() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceVcdDNATCreate, | ||
Delete: resourceVcdDNATDelete, | ||
Read: resourceVcdDNATRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"edge_gateway": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"external_ip": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"port": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"internal_ip": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceVcdDNATCreate(d *schema.ResourceData, meta interface{}) error { | ||
vcdClient := meta.(*VCDClient) | ||
// 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() | ||
portString := getPortString(d.Get("port").(int)) | ||
|
||
edgeGateway, err := vcdClient.OrgVdc.FindEdgeGateway(d.Get("edge_gateway").(string)) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Unable to find edge gateway: %#v", 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 | ||
|
||
err = retryCall(vcdClient.MaxRetryTimeout, func() error { | ||
task, err := edgeGateway.AddNATMapping("DNAT", d.Get("external_ip").(string), | ||
d.Get("internal_ip").(string), | ||
portString) | ||
if err != nil { | ||
return fmt.Errorf("Error setting DNAT rules: %#v", err) | ||
} | ||
|
||
return task.WaitTaskCompletion() | ||
}) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Error completing tasks: %#v", err) | ||
} | ||
|
||
d.SetId(d.Get("external_ip").(string) + "_" + portString) | ||
return nil | ||
} | ||
|
||
func resourceVcdDNATRead(d *schema.ResourceData, meta interface{}) error { | ||
vcdClient := meta.(*VCDClient) | ||
e, err := vcdClient.OrgVdc.FindEdgeGateway(d.Get("edge_gateway").(string)) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Unable to find edge gateway: %#v", err) | ||
} | ||
|
||
var found bool | ||
|
||
for _, r := range e.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { | ||
if r.RuleType == "DNAT" && | ||
r.GatewayNatRule.OriginalIP == d.Get("external_ip").(string) && | ||
r.GatewayNatRule.OriginalPort == getPortString(d.Get("port").(int)) { | ||
found = true | ||
d.Set("internal_ip", r.GatewayNatRule.TranslatedIP) | ||
} | ||
} | ||
|
||
if !found { | ||
d.SetId("") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceVcdDNATDelete(d *schema.ResourceData, meta interface{}) error { | ||
vcdClient := meta.(*VCDClient) | ||
// 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() | ||
portString := getPortString(d.Get("port").(int)) | ||
|
||
edgeGateway, err := vcdClient.OrgVdc.FindEdgeGateway(d.Get("edge_gateway").(string)) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Unable to find edge gateway: %#v", err) | ||
} | ||
err = retryCall(vcdClient.MaxRetryTimeout, func() error { | ||
task, err := edgeGateway.RemoveNATMapping("DNAT", d.Get("external_ip").(string), | ||
d.Get("internal_ip").(string), | ||
portString) | ||
if err != nil { | ||
return fmt.Errorf("Error setting DNAT rules: %#v", err) | ||
} | ||
|
||
return task.WaitTaskCompletion() | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("Error completing tasks: %#v", err) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.