Skip to content

Commit 2531ab0

Browse files
ddegoedeSander van Harmelen
authored andcommitted
Adding private gateway and static route resource to cloudstack provider (hashicorp#9637)
* Adding private gateway and static route resource to cloudstack provider Testing the private gateway and static route resource requires a ROOT account in Cloudstack * changes requested by reviewer
1 parent d2eab5a commit 2531ab0

9 files changed

Lines changed: 640 additions & 0 deletions

builtin/providers/cloudstack/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ func Provider() terraform.ResourceProvider {
5353
"cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(),
5454
"cloudstack_nic": resourceCloudStackNIC(),
5555
"cloudstack_port_forward": resourceCloudStackPortForward(),
56+
"cloudstack_private_gateway": resourceCloudStackPrivateGateway(),
5657
"cloudstack_secondary_ipaddress": resourceCloudStackSecondaryIPAddress(),
5758
"cloudstack_security_group": resourceCloudStackSecurityGroup(),
5859
"cloudstack_security_group_rule": resourceCloudStackSecurityGroupRule(),
5960
"cloudstack_ssh_keypair": resourceCloudStackSSHKeyPair(),
6061
"cloudstack_static_nat": resourceCloudStackStaticNAT(),
62+
"cloudstack_static_route": resourceCloudStackStaticRoute(),
6163
"cloudstack_template": resourceCloudStackTemplate(),
6264
"cloudstack_vpc": resourceCloudStackVPC(),
6365
"cloudstack_vpn_connection": resourceCloudStackVPNConnection(),

builtin/providers/cloudstack/provider_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,21 @@ func testAccPreCheck(t *testing.T) {
144144
if v := os.Getenv("CLOUDSTACK_ZONE"); v == "" {
145145
t.Fatal("CLOUDSTACK_ZONE must be set for acceptance tests")
146146
}
147+
if v := os.Getenv("CLOUDSTACK_PRIVGW_GATEWAY"); v == "" {
148+
t.Fatal("CLOUDSTACK_PRIVGW_GATEWAY must be set for acceptance tests")
149+
}
150+
if v := os.Getenv("CLOUDSTACK_PRIVGW_IPADDRESS"); v == "" {
151+
t.Fatal("CLOUDSTACK_PRIVGW_IPADDRESS must be set for acceptance tests")
152+
}
153+
if v := os.Getenv("CLOUDSTACK_PRIVGW_NETMASK"); v == "" {
154+
t.Fatal("CLOUDSTACK_PRIVGW_NETMASK must be set for acceptance tests")
155+
}
156+
if v := os.Getenv("CLOUDSTACK_PRIVGW_VLAN"); v == "" {
157+
t.Fatal("CLOUDSTACK_PRIVGW_VLAN must be set for acceptance tests")
158+
}
159+
if v := os.Getenv("CLOUDSTACK_STATIC_ROUTE_CIDR"); v == "" {
160+
t.Fatal("CLOUDSTACK_STATIC_ROUTE_CIDR must be set for acceptance tests")
161+
}
147162
}
148163

149164
// Name of a valid disk offering
@@ -223,3 +238,12 @@ var CLOUDSTACK_PROJECT_NETWORK = os.Getenv("CLOUDSTACK_PROJECT_NETWORK")
223238

224239
// Name of a zone that exists already
225240
var CLOUDSTACK_ZONE = os.Getenv("CLOUDSTACK_ZONE")
241+
242+
// Details of the private gateway that will be added to VPC testing this, should be done using ROOT keys
243+
var CLOUDSTACK_PRIVGW_GATEWAY = os.Getenv("CLOUDSTACK_PRIVGW_GATEWAY")
244+
var CLOUDSTACK_PRIVGW_IPADDRESS = os.Getenv("CLOUDSTACK_PRIVGW_IPADDRESS")
245+
var CLOUDSTACK_PRIVGW_NETMASK = os.Getenv("CLOUDSTACK_PRIVGW_NETMASK")
246+
var CLOUDSTACK_PRIVGW_VLAN = os.Getenv("CLOUDSTACK_PRIVGW_VLAN")
247+
248+
// Details of the static route that will be added to private gateway testing this.
249+
var CLOUDSTACK_STATIC_ROUTE_CIDR = os.Getenv("CLOUDSTACK_STATIC_ROUTE_CIDR")
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package cloudstack
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strings"
7+
8+
"github.com/hashicorp/terraform/helper/schema"
9+
"github.com/xanzy/go-cloudstack/cloudstack"
10+
)
11+
12+
func resourceCloudStackPrivateGateway() *schema.Resource {
13+
return &schema.Resource{
14+
Create: resourceCloudStackPrivateGatewayCreate,
15+
Read: resourceCloudStackPrivateGatewayRead,
16+
Update: resourceCloudStackPrivateGatewayUpdate,
17+
Delete: resourceCloudStackPrivateGatewayDelete,
18+
19+
Schema: map[string]*schema.Schema{
20+
"gateway": &schema.Schema{
21+
Type: schema.TypeString,
22+
Required: true,
23+
ForceNew: true,
24+
},
25+
26+
"ip_address": &schema.Schema{
27+
Type: schema.TypeString,
28+
Required: true,
29+
ForceNew: true,
30+
},
31+
32+
"netmask": &schema.Schema{
33+
Type: schema.TypeString,
34+
Required: true,
35+
ForceNew: true,
36+
},
37+
38+
"vlan": &schema.Schema{
39+
Type: schema.TypeString,
40+
Required: true,
41+
ForceNew: true,
42+
},
43+
44+
"physical_network_id": &schema.Schema{
45+
Type: schema.TypeString,
46+
Optional: true,
47+
ForceNew: true,
48+
},
49+
50+
"network_offering": &schema.Schema{
51+
Type: schema.TypeString,
52+
Optional: true,
53+
ForceNew: true,
54+
},
55+
56+
"acl_id": &schema.Schema{
57+
Type: schema.TypeString,
58+
Required: true,
59+
},
60+
61+
"vpc_id": &schema.Schema{
62+
Type: schema.TypeString,
63+
Required: true,
64+
ForceNew: true,
65+
},
66+
},
67+
}
68+
}
69+
70+
func resourceCloudStackPrivateGatewayCreate(d *schema.ResourceData, meta interface{}) error {
71+
cs := meta.(*cloudstack.CloudStackClient)
72+
73+
ipaddress := d.Get("ip_address").(string)
74+
networkofferingid := d.Get("network_offering").(string)
75+
76+
// Create a new parameter struct
77+
p := cs.VPC.NewCreatePrivateGatewayParams(
78+
d.Get("gateway").(string),
79+
ipaddress,
80+
d.Get("netmask").(string),
81+
d.Get("vlan").(string),
82+
d.Get("vpc_id").(string),
83+
)
84+
85+
// Retrieve the network_offering ID
86+
if networkofferingid != "" {
87+
networkofferingid, e := retrieveID(cs, "network_offering", networkofferingid)
88+
if e != nil {
89+
return e.Error()
90+
}
91+
p.SetNetworkofferingid(networkofferingid)
92+
}
93+
94+
// Check if we want to associate an ACL
95+
if aclid, ok := d.GetOk("acl_id"); ok {
96+
// Set the acl ID
97+
p.SetAclid(aclid.(string))
98+
}
99+
100+
// Create the new private gateway
101+
r, err := cs.VPC.CreatePrivateGateway(p)
102+
if err != nil {
103+
return fmt.Errorf("Error creating private gateway for %s: %s", ipaddress, err)
104+
}
105+
106+
d.SetId(r.Id)
107+
108+
return resourceCloudStackPrivateGatewayRead(d, meta)
109+
}
110+
111+
func resourceCloudStackPrivateGatewayRead(d *schema.ResourceData, meta interface{}) error {
112+
cs := meta.(*cloudstack.CloudStackClient)
113+
114+
// Get the private gateway details
115+
gw, count, err := cs.VPC.GetPrivateGatewayByID(d.Id())
116+
if err != nil {
117+
if count == 0 {
118+
log.Printf("[DEBUG] Private gateway %s does no longer exist", d.Id())
119+
d.SetId("")
120+
return nil
121+
}
122+
123+
return err
124+
}
125+
126+
d.Set("gateway", gw.Gateway)
127+
d.Set("ip_address", gw.Ipaddress)
128+
d.Set("netmask", gw.Netmask)
129+
d.Set("vlan", gw.Vlan)
130+
d.Set("acl_id", gw.Aclid)
131+
d.Set("vpc_id", gw.Vpcid)
132+
133+
return nil
134+
}
135+
136+
func resourceCloudStackPrivateGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
137+
cs := meta.(*cloudstack.CloudStackClient)
138+
139+
// Replace the ACL if the ID has changed
140+
if d.HasChange("acl_id") {
141+
p := cs.NetworkACL.NewReplaceNetworkACLListParams(d.Get("acl_id").(string))
142+
p.SetNetworkid(d.Id())
143+
144+
_, err := cs.NetworkACL.ReplaceNetworkACLList(p)
145+
if err != nil {
146+
return fmt.Errorf("Error replacing ACL: %s", err)
147+
}
148+
}
149+
150+
return resourceCloudStackNetworkRead(d, meta)
151+
}
152+
153+
func resourceCloudStackPrivateGatewayDelete(d *schema.ResourceData, meta interface{}) error {
154+
cs := meta.(*cloudstack.CloudStackClient)
155+
156+
// Create a new parameter struct
157+
p := cs.VPC.NewDeletePrivateGatewayParams(d.Id())
158+
159+
// Delete the private gateway
160+
_, err := cs.VPC.DeletePrivateGateway(p)
161+
if err != nil {
162+
// This is a very poor way to be told the ID does no longer exist :(
163+
if strings.Contains(err.Error(), fmt.Sprintf(
164+
"Invalid parameter id value=%s due to incorrect long value format, "+
165+
"or entity does not exist", d.Id())) {
166+
return nil
167+
}
168+
169+
return fmt.Errorf("Error deleting private gateway %s: %s", d.Id(), err)
170+
}
171+
172+
return nil
173+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package cloudstack
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform/helper/resource"
8+
"github.com/hashicorp/terraform/terraform"
9+
"github.com/xanzy/go-cloudstack/cloudstack"
10+
)
11+
12+
func TestAccCloudStackPrivateGateway_basic(t *testing.T) {
13+
var gateway cloudstack.PrivateGateway
14+
15+
resource.Test(t, resource.TestCase{
16+
PreCheck: func() { testAccPreCheck(t) },
17+
Providers: testAccProviders,
18+
CheckDestroy: testAccCheckCloudStackPrivateGatewayDestroy,
19+
Steps: []resource.TestStep{
20+
resource.TestStep{
21+
Config: testAccCloudStackPrivateGateway_basic,
22+
Check: resource.ComposeTestCheckFunc(
23+
testAccCheckCloudStackPrivateGatewayExists(
24+
"cloudstack_private_gateway.foo", &gateway),
25+
testAccCheckCloudStackPrivateGatewayAttributes(&gateway),
26+
),
27+
},
28+
},
29+
})
30+
}
31+
32+
func testAccCheckCloudStackPrivateGatewayExists(
33+
n string, gateway *cloudstack.PrivateGateway) resource.TestCheckFunc {
34+
return func(s *terraform.State) error {
35+
rs, ok := s.RootModule().Resources[n]
36+
if !ok {
37+
return fmt.Errorf("Not found: %s", n)
38+
}
39+
40+
if rs.Primary.ID == "" {
41+
return fmt.Errorf("No Private Gateway ID is set")
42+
}
43+
44+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
45+
pgw, _, err := cs.VPC.GetPrivateGatewayByID(rs.Primary.ID)
46+
47+
if err != nil {
48+
return err
49+
}
50+
51+
if pgw.Id != rs.Primary.ID {
52+
return fmt.Errorf("Private Gateway not found")
53+
}
54+
55+
*gateway = *pgw
56+
57+
return nil
58+
}
59+
}
60+
61+
func testAccCheckCloudStackPrivateGatewayAttributes(
62+
gateway *cloudstack.PrivateGateway) resource.TestCheckFunc {
63+
return func(s *terraform.State) error {
64+
65+
if gateway.Gateway != CLOUDSTACK_PRIVGW_GATEWAY {
66+
return fmt.Errorf("Bad Gateway: %s", gateway.Gateway)
67+
}
68+
69+
if gateway.Ipaddress != CLOUDSTACK_PRIVGW_IPADDRESS {
70+
return fmt.Errorf("Bad Gateway: %s", gateway.Ipaddress)
71+
}
72+
73+
if gateway.Netmask != CLOUDSTACK_PRIVGW_NETMASK {
74+
return fmt.Errorf("Bad Gateway: %s", gateway.Netmask)
75+
}
76+
77+
return nil
78+
}
79+
}
80+
81+
func testAccCheckCloudStackPrivateGatewayDestroy(s *terraform.State) error {
82+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
83+
84+
for _, rs := range s.RootModule().Resources {
85+
if rs.Type != "cloudstack_private_gateway" {
86+
continue
87+
}
88+
89+
if rs.Primary.ID == "" {
90+
return fmt.Errorf("No private gateway ID is set")
91+
}
92+
93+
gateway, _, err := cs.VPC.GetPrivateGatewayByID(rs.Primary.ID)
94+
if err == nil && gateway.Id != "" {
95+
return fmt.Errorf("Private gateway %s still exists", rs.Primary.ID)
96+
}
97+
}
98+
99+
return nil
100+
}
101+
102+
var testAccCloudStackPrivateGateway_basic = fmt.Sprintf(`
103+
resource "cloudstack_vpc" "foobar" {
104+
name = "terraform-vpc"
105+
cidr = "%s"
106+
vpc_offering = "%s"
107+
zone = "%s"
108+
}
109+
110+
resource "cloudstack_private_gateway" "foo" {
111+
gateway = "%s"
112+
ip_address = "%s"
113+
netmask = "%s"
114+
vlan = "%s"
115+
vpc_id = "${cloudstack_vpc.foobar.id}"
116+
}`,
117+
CLOUDSTACK_VPC_CIDR_1,
118+
CLOUDSTACK_VPC_OFFERING,
119+
CLOUDSTACK_ZONE,
120+
CLOUDSTACK_PRIVGW_GATEWAY,
121+
CLOUDSTACK_PRIVGW_IPADDRESS,
122+
CLOUDSTACK_PRIVGW_NETMASK,
123+
CLOUDSTACK_PRIVGW_VLAN)

0 commit comments

Comments
 (0)