Skip to content

Commit 805f97b

Browse files
committed
Adding some IPv6 support
1 parent 20517e5 commit 805f97b

File tree

5 files changed

+198
-19
lines changed

5 files changed

+198
-19
lines changed

bigip/resource_bigip_ltm_node.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ func resourceBigipLtmNodeRead(d *schema.ResourceData, meta interface{}) error {
185185
}
186186
} else {
187187
// xxx.xxx.xxx.xxx(%x)
188-
regex := regexp.MustCompile(`((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?`)
188+
// x:x(%x)
189+
regex := regexp.MustCompile(`((?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:.*:[^%]*))(?:\%\d+)?`)
189190
address := regex.FindStringSubmatch(node.Address)
190191
if err := d.Set("address", address[1]); err != nil {
191192
return fmt.Errorf("[DEBUG] Error saving address to state for Node (%s): %s", d.Id(), err)
@@ -234,7 +235,7 @@ func resourceBigipLtmNodeUpdate(d *schema.ResourceData, meta interface{}) error
234235

235236
name := d.Id()
236237
address := d.Get("address").(string)
237-
r, _ := regexp.Compile("^((?:[0-9]{1,3}.){3}[0-9]{1,3})|(.*:.*)$")
238+
r, _ := regexp.Compile("^((?:[0-9]{1,3}.){3}[0-9]{1,3})|(.*:[^%]*)$")
238239

239240
var node *bigip.Node
240241
if r.MatchString(address) {

bigip/resource_bigip_ltm_node_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
)
1111

1212
var TEST_NODE_NAME = fmt.Sprintf("/%s/test-node", TEST_PARTITION)
13+
var TEST_V6_NODE_NAME = fmt.Sprintf("/%s/test-v6-node", TEST_PARTITION)
1314
var TEST_FQDN_NODE_NAME = fmt.Sprintf("/%s/test-fqdn-node", TEST_PARTITION)
1415

1516
var TEST_NODE_RESOURCE = `
@@ -22,6 +23,16 @@ resource "bigip_ltm_node" "test-node" {
2223
rate_limit = "disabled"
2324
}
2425
`
26+
var TEST_V6_NODE_RESOURCE = `
27+
resource "bigip_ltm_node" "test-node" {
28+
name = "` + TEST_V6_NODE_NAME + `"
29+
address = "fe80::10"
30+
connection_limit = "0"
31+
dynamic_ratio = "1"
32+
monitor = "default"
33+
rate_limit = "disabled"
34+
}
35+
`
2536
var TEST_FQDN_NODE_RESOURCE = `
2637
resource "bigip_ltm_node" "test-fqdn-node" {
2738
name = "` + TEST_FQDN_NODE_NAME + `"
@@ -58,6 +69,29 @@ func TestAccBigipLtmNode_create(t *testing.T) {
5869
},
5970
})
6071

72+
resource.Test(t, resource.TestCase{
73+
PreCheck: func() {
74+
testAcctPreCheck(t)
75+
},
76+
Providers: testAccProviders,
77+
CheckDestroy: testCheckNodesDestroyed,
78+
Steps: []resource.TestStep{
79+
{
80+
Config: TEST_V6_NODE_RESOURCE,
81+
Check: resource.ComposeTestCheckFunc(
82+
testCheckNodeExists(TEST_V6_NODE_NAME, true),
83+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "name", TEST_V6_NODE_NAME),
84+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "address", "fe80::10"),
85+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "connection_limit", "0"),
86+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "dynamic_ratio", "1"),
87+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "monitor", "default"),
88+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "rate_limit", "disabled"),
89+
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "state", "unchecked"),
90+
),
91+
},
92+
},
93+
})
94+
6195
resource.Test(t, resource.TestCase{
6296
PreCheck: func() {
6397
testAcctPreCheck(t)
@@ -103,6 +137,25 @@ func TestAccBigipLtmNode_import(t *testing.T) {
103137
},
104138
})
105139

140+
resource.Test(t, resource.TestCase{
141+
PreCheck: func() {
142+
testAcctPreCheck(t)
143+
},
144+
Providers: testAccProviders,
145+
CheckDestroy: testCheckNodesDestroyed,
146+
Steps: []resource.TestStep{
147+
{
148+
Config: TEST_V6_NODE_RESOURCE,
149+
Check: resource.ComposeTestCheckFunc(
150+
testCheckNodeExists(TEST_V6_NODE_NAME, true),
151+
),
152+
ResourceName: TEST_V6_NODE_NAME,
153+
ImportState: false,
154+
ImportStateVerify: true,
155+
},
156+
},
157+
})
158+
106159
resource.Test(t, resource.TestCase{
107160
PreCheck: func() {
108161
testAcctPreCheck(t)

bigip/resource_bigip_ltm_virtual_server.go

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"regexp"
77
"strconv"
8+
"strings"
89

910
"github.com/f5devcentral/go-bigip"
1011
"github.com/hashicorp/terraform/helper/schema"
@@ -38,7 +39,7 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
3839
"source": {
3940
Type: schema.TypeString,
4041
Optional: true,
41-
Default: "0.0.0.0/0",
42+
Computed: true,
4243
Description: "Source IP and mask for the virtual server",
4344
},
4445

@@ -57,7 +58,7 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
5758
"mask": {
5859
Type: schema.TypeString,
5960
Optional: true,
60-
Default: "255.255.255.255",
61+
Computed: true,
6162
Description: "Mask can either be in CIDR notation or decimal, i.e.: \"24\" or \"255.255.255.0\". A CIDR mask of \"0\" is the same as \"0.0.0.0\"",
6263
},
6364

@@ -162,9 +163,36 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
162163
}
163164
}
164165

166+
func resourceBigipLtmVirtualServerAttrDefaults(d *schema.ResourceData) {
167+
_, hasMask := d.GetOk("mask")
168+
_, hasSource := d.GetOk("source")
169+
170+
// Set default mask if nil
171+
if !hasMask {
172+
// looks like IPv6, lets set to /128
173+
if strings.Contains(d.Get("destination").(string), ":") {
174+
d.Set("mask", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
175+
} else { // /32 for IPv4
176+
d.Set("mask", "255.255.255.255")
177+
}
178+
}
179+
180+
// set default source if nil
181+
if !hasSource {
182+
// looks like IPv6, lets set to ::/0
183+
if strings.Contains(d.Get("destination").(string), ":") {
184+
d.Set("source", "::/0")
185+
} else { // 0.0.0.0/0
186+
d.Set("source", "0.0.0.0/0")
187+
}
188+
}
189+
}
190+
165191
func resourceBigipLtmVirtualServerCreate(d *schema.ResourceData, meta interface{}) error {
166192
client := meta.(*bigip.BigIP)
167193

194+
resourceBigipLtmVirtualServerAttrDefaults(d)
195+
168196
name := d.Get("name").(string)
169197
port := d.Get("port").(int)
170198
TranslateAddress := d.Get("translate_port").(string)
@@ -215,18 +243,36 @@ func resourceBigipLtmVirtualServerRead(d *schema.ResourceData, meta interface{})
215243
return nil
216244
}
217245
// Extract destination address from "/partition_name/(virtual_server_address)[%route_domain]:port"
218-
regex := regexp.MustCompile(`(\/.+\/)((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(\:\d+)`)
246+
regex := regexp.MustCompile(`(\/.+\/)((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(?:\:(\d+))`)
219247
destination := regex.FindStringSubmatch(vs.Destination)
220-
if len(destination) < 3 {
221-
return fmt.Errorf("Unable to extract destination address from virtual server destination: " + vs.Destination)
248+
if destination == nil {
249+
// We should try a IPv6 extraction
250+
251+
regex = regexp.MustCompile(`^(\/.+\/)(.*:[^%]*)(?:\%\d+)?(?:\.(\d+))$`)
252+
destination = regex.FindStringSubmatch(vs.Destination)
253+
254+
if destination == nil {
255+
return fmt.Errorf("Unable to extract destination address and port from virtual server destination: " + vs.Destination)
256+
}
222257
}
258+
223259
if err := d.Set("destination", destination[2]); err != nil {
224260
return fmt.Errorf("[DEBUG] Error saving Destination to state for Virtual Server (%s): %s", d.Id(), err)
225261
}
226262

227263
// Extract source address from "(source_address)[%route_domain](/mask)" groups 1 + 2
228264
regex = regexp.MustCompile(`((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(\/\d+)`)
229265
source := regex.FindStringSubmatch(vs.Source)
266+
if source == nil {
267+
// We should try a IPv6 extraction
268+
269+
regex = regexp.MustCompile(`^(.*:[^%]*)(?:\%\d+)?(\/\d+)$`)
270+
source = regex.FindStringSubmatch(vs.Source)
271+
272+
if source == nil {
273+
return fmt.Errorf("Unable to extract source address and mask from virtual server destination: " + vs.Source)
274+
}
275+
}
230276
parsedSource := source[1] + source[2]
231277
if err := d.Set("source", parsedSource); err != nil {
232278
return fmt.Errorf("[DEBUG] Error saving Source to state for Virtual Server (%s): %s", d.Id(), err)
@@ -241,15 +287,8 @@ func resourceBigipLtmVirtualServerRead(d *schema.ResourceData, meta interface{})
241287
return fmt.Errorf("[DEBUG] Error saving Mask to state for Virtual Server (%s): %s", d.Id(), err)
242288
}
243289

244-
/* Service port is provided by the API in the destination attribute "/partition_name/virtual_server_address[%route_domain]:(port)"
245-
so we need to extract it
246-
*/
247-
regex = regexp.MustCompile(`\:(\d+)`)
248-
port := regex.FindStringSubmatch(vs.Destination)
249-
if len(port) < 2 {
250-
return fmt.Errorf("Unable to extract service port from virtual server destination: %s", vs.Destination)
251-
}
252-
parsedPort, _ := strconv.Atoi(port[1])
290+
// Service port was extracted earlier
291+
parsedPort, _ := strconv.Atoi(destination[3])
253292
d.Set("port", parsedPort)
254293

255294
d.Set("irules", makeStringList(&vs.Rules))
@@ -330,6 +369,8 @@ func resourceBigipLtmVirtualServerExists(d *schema.ResourceData, meta interface{
330369
func resourceBigipLtmVirtualServerUpdate(d *schema.ResourceData, meta interface{}) error {
331370
client := meta.(*bigip.BigIP)
332371

372+
resourceBigipLtmVirtualServerAttrDefaults(d)
373+
333374
name := d.Id()
334375

335376
var profiles []bigip.Profile
@@ -371,8 +412,13 @@ func resourceBigipLtmVirtualServerUpdate(d *schema.ResourceData, meta interface{
371412
rules = listToStringSlice(cfg_rules.([]interface{}))
372413
}
373414

415+
destPort := fmt.Sprintf("%s:%d", d.Get("destination").(string), d.Get("port").(int))
416+
if strings.Contains(d.Get("destination").(string), ":") {
417+
destPort = fmt.Sprintf("%s.%d", d.Get("destination").(string), d.Get("port").(int))
418+
}
419+
374420
vs := &bigip.VirtualServer{
375-
Destination: fmt.Sprintf("%s:%d", d.Get("destination").(string), d.Get("port").(int)),
421+
Destination: destPort,
376422
FallbackPersistenceProfile: d.Get("fallback_persistence_profile").(string),
377423
Source: d.Get("source").(string),
378424
Pool: d.Get("pool").(string),

bigip/resource_bigip_ltm_virtual_server_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ resource "bigip_ltm_virtual_server" "test-vs" {
3232
}
3333
`
3434

35+
var TEST_VS6_NAME = fmt.Sprintf("/%s/test-vs6", TEST_PARTITION)
36+
37+
var TEST_VS6_RESOURCE = TEST_IRULE_RESOURCE + `
38+
39+
40+
resource "bigip_ltm_virtual_server" "test-vs" {
41+
name = "` + TEST_VS6_NAME + `"
42+
destination = "fe80::11"
43+
port = 9999
44+
source_address_translation = "automap"
45+
ip_protocol = "tcp"
46+
irules = ["${bigip_ltm_irule.test-rule.name}"]
47+
profiles = ["/Common/http"]
48+
client_profiles = ["/Common/tcp"]
49+
server_profiles = ["/Common/tcp-lan-optimized"]
50+
persistence_profiles = ["/Common/source_addr"]
51+
fallback_persistence_profile = "/Common/dest_addr"
52+
}
53+
`
54+
3555
func TestAccBigipLtmVS_create(t *testing.T) {
3656
resource.Test(t, resource.TestCase{
3757
PreCheck: func() {
@@ -51,6 +71,47 @@ func TestAccBigipLtmVS_create(t *testing.T) {
5171
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "destination", "10.255.255.254"),
5272
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "port", "9999"),
5373
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "mask", "255.255.255.255"),
74+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source", "0.0.0.0/0"),
75+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source_address_translation", "automap"),
76+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "ip_protocol", "tcp"),
77+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "irules.0", TEST_IRULE_NAME),
78+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
79+
fmt.Sprintf("profiles.%d", schema.HashString("/Common/http")),
80+
"/Common/http"),
81+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
82+
fmt.Sprintf("client_profiles.%d", schema.HashString("/Common/tcp")),
83+
"/Common/tcp"),
84+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
85+
fmt.Sprintf("server_profiles.%d", schema.HashString("/Common/tcp-lan-optimized")),
86+
"/Common/tcp-lan-optimized"),
87+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
88+
fmt.Sprintf("persistence_profiles.%d", schema.HashString("/Common/source_addr")),
89+
"/Common/source_addr"),
90+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "fallback_persistence_profile", "/Common/dest_addr"),
91+
),
92+
},
93+
},
94+
})
95+
96+
resource.Test(t, resource.TestCase{
97+
PreCheck: func() {
98+
testAcctPreCheck(t)
99+
},
100+
Providers: testAccProviders,
101+
CheckDestroy: resource.ComposeTestCheckFunc(
102+
testCheckVSsDestroyed,
103+
testCheckIRulesDestroyed,
104+
),
105+
Steps: []resource.TestStep{
106+
{
107+
Config: TEST_VS6_RESOURCE,
108+
Check: resource.ComposeTestCheckFunc(
109+
testCheckVSExists(TEST_VS6_NAME, true),
110+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "name", TEST_VS6_NAME),
111+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "destination", "fe80::11"),
112+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "port", "9999"),
113+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "mask", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
114+
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source", "::/0"),
54115
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source_address_translation", "automap"),
55116
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "ip_protocol", "tcp"),
56117
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "irules.0", TEST_IRULE_NAME),
@@ -92,6 +153,24 @@ func TestAccBigipLtmVS_import(t *testing.T) {
92153
},
93154
},
94155
})
156+
resource.Test(t, resource.TestCase{
157+
PreCheck: func() {
158+
testAcctPreCheck(t)
159+
},
160+
Providers: testAccProviders,
161+
CheckDestroy: testCheckVSsDestroyed,
162+
Steps: []resource.TestStep{
163+
{
164+
Config: TEST_VS6_RESOURCE,
165+
Check: resource.ComposeTestCheckFunc(
166+
testCheckVSExists(TEST_VS6_NAME, true),
167+
),
168+
ResourceName: TEST_VS6_NAME,
169+
ImportState: false,
170+
ImportStateVerify: true,
171+
},
172+
},
173+
})
95174
}
96175

97176
func testCheckVSExists(name string, exists bool) resource.TestCheckFunc {

bigip/validators.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ func validateF5Name(value interface{}, field string) (ws []string, errors []erro
5050
}
5151

5252
for _, v := range values {
53-
match, _ := regexp.MatchString("^/[\\w_\\-.]+/[\\w_\\-.]+$", v)
53+
match, _ := regexp.MatchString("^/[\\w_\\-.]+/[\\w_\\-.:]+$", v)
5454
if !match {
55-
errors = append(errors, fmt.Errorf("%q must match /Partition/Name and contain letters, numbers or [._-]. e.g. /Common/my-pool", field))
55+
errors = append(errors, fmt.Errorf("%q must match /Partition/Name and contain letters, numbers or [._-:]. e.g. /Common/my-pool", field))
5656
}
5757
}
5858
return

0 commit comments

Comments
 (0)