Skip to content

Commit

Permalink
Adding some IPv6 support
Browse files Browse the repository at this point in the history
  • Loading branch information
tpretz committed Jun 15, 2019
1 parent 20517e5 commit 805f97b
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 19 deletions.
5 changes: 3 additions & 2 deletions bigip/resource_bigip_ltm_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ func resourceBigipLtmNodeRead(d *schema.ResourceData, meta interface{}) error {
}
} else {
// xxx.xxx.xxx.xxx(%x)
regex := regexp.MustCompile(`((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?`)
// x:x(%x)
regex := regexp.MustCompile(`((?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:.*:[^%]*))(?:\%\d+)?`)
address := regex.FindStringSubmatch(node.Address)
if err := d.Set("address", address[1]); err != nil {
return fmt.Errorf("[DEBUG] Error saving address to state for Node (%s): %s", d.Id(), err)
Expand Down Expand Up @@ -234,7 +235,7 @@ func resourceBigipLtmNodeUpdate(d *schema.ResourceData, meta interface{}) error

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

var node *bigip.Node
if r.MatchString(address) {
Expand Down
53 changes: 53 additions & 0 deletions bigip/resource_bigip_ltm_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
)

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

var TEST_NODE_RESOURCE = `
Expand All @@ -22,6 +23,16 @@ resource "bigip_ltm_node" "test-node" {
rate_limit = "disabled"
}
`
var TEST_V6_NODE_RESOURCE = `
resource "bigip_ltm_node" "test-node" {
name = "` + TEST_V6_NODE_NAME + `"
address = "fe80::10"
connection_limit = "0"
dynamic_ratio = "1"
monitor = "default"
rate_limit = "disabled"
}
`
var TEST_FQDN_NODE_RESOURCE = `
resource "bigip_ltm_node" "test-fqdn-node" {
name = "` + TEST_FQDN_NODE_NAME + `"
Expand Down Expand Up @@ -58,6 +69,29 @@ func TestAccBigipLtmNode_create(t *testing.T) {
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testCheckNodesDestroyed,
Steps: []resource.TestStep{
{
Config: TEST_V6_NODE_RESOURCE,
Check: resource.ComposeTestCheckFunc(
testCheckNodeExists(TEST_V6_NODE_NAME, true),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "name", TEST_V6_NODE_NAME),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "address", "fe80::10"),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "connection_limit", "0"),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "dynamic_ratio", "1"),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "monitor", "default"),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "rate_limit", "disabled"),
resource.TestCheckResourceAttr("bigip_ltm_node.test-node", "state", "unchecked"),
),
},
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
Expand Down Expand Up @@ -103,6 +137,25 @@ func TestAccBigipLtmNode_import(t *testing.T) {
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testCheckNodesDestroyed,
Steps: []resource.TestStep{
{
Config: TEST_V6_NODE_RESOURCE,
Check: resource.ComposeTestCheckFunc(
testCheckNodeExists(TEST_V6_NODE_NAME, true),
),
ResourceName: TEST_V6_NODE_NAME,
ImportState: false,
ImportStateVerify: true,
},
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
Expand Down
76 changes: 61 additions & 15 deletions bigip/resource_bigip_ltm_virtual_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"regexp"
"strconv"
"strings"

"github.com/f5devcentral/go-bigip"
"github.com/hashicorp/terraform/helper/schema"
Expand Down Expand Up @@ -38,7 +39,7 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
"source": {
Type: schema.TypeString,
Optional: true,
Default: "0.0.0.0/0",
Computed: true,
Description: "Source IP and mask for the virtual server",
},

Expand All @@ -57,7 +58,7 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
"mask": {
Type: schema.TypeString,
Optional: true,
Default: "255.255.255.255",
Computed: true,
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\"",
},

Expand Down Expand Up @@ -162,9 +163,36 @@ func resourceBigipLtmVirtualServer() *schema.Resource {
}
}

func resourceBigipLtmVirtualServerAttrDefaults(d *schema.ResourceData) {
_, hasMask := d.GetOk("mask")
_, hasSource := d.GetOk("source")

// Set default mask if nil
if !hasMask {
// looks like IPv6, lets set to /128
if strings.Contains(d.Get("destination").(string), ":") {
d.Set("mask", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
} else { // /32 for IPv4
d.Set("mask", "255.255.255.255")
}
}

// set default source if nil
if !hasSource {
// looks like IPv6, lets set to ::/0
if strings.Contains(d.Get("destination").(string), ":") {
d.Set("source", "::/0")
} else { // 0.0.0.0/0
d.Set("source", "0.0.0.0/0")
}
}
}

func resourceBigipLtmVirtualServerCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*bigip.BigIP)

resourceBigipLtmVirtualServerAttrDefaults(d)

name := d.Get("name").(string)
port := d.Get("port").(int)
TranslateAddress := d.Get("translate_port").(string)
Expand Down Expand Up @@ -215,18 +243,36 @@ func resourceBigipLtmVirtualServerRead(d *schema.ResourceData, meta interface{})
return nil
}
// Extract destination address from "/partition_name/(virtual_server_address)[%route_domain]:port"
regex := regexp.MustCompile(`(\/.+\/)((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(\:\d+)`)
regex := regexp.MustCompile(`(\/.+\/)((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(?:\:(\d+))`)
destination := regex.FindStringSubmatch(vs.Destination)
if len(destination) < 3 {
return fmt.Errorf("Unable to extract destination address from virtual server destination: " + vs.Destination)
if destination == nil {
// We should try a IPv6 extraction

regex = regexp.MustCompile(`^(\/.+\/)(.*:[^%]*)(?:\%\d+)?(?:\.(\d+))$`)
destination = regex.FindStringSubmatch(vs.Destination)

if destination == nil {
return fmt.Errorf("Unable to extract destination address and port from virtual server destination: " + vs.Destination)
}
}

if err := d.Set("destination", destination[2]); err != nil {
return fmt.Errorf("[DEBUG] Error saving Destination to state for Virtual Server (%s): %s", d.Id(), err)
}

// Extract source address from "(source_address)[%route_domain](/mask)" groups 1 + 2
regex = regexp.MustCompile(`((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\%\d+)?(\/\d+)`)
source := regex.FindStringSubmatch(vs.Source)
if source == nil {
// We should try a IPv6 extraction

regex = regexp.MustCompile(`^(.*:[^%]*)(?:\%\d+)?(\/\d+)$`)
source = regex.FindStringSubmatch(vs.Source)

if source == nil {
return fmt.Errorf("Unable to extract source address and mask from virtual server destination: " + vs.Source)
}
}
parsedSource := source[1] + source[2]
if err := d.Set("source", parsedSource); err != nil {
return fmt.Errorf("[DEBUG] Error saving Source to state for Virtual Server (%s): %s", d.Id(), err)
Expand All @@ -241,15 +287,8 @@ func resourceBigipLtmVirtualServerRead(d *schema.ResourceData, meta interface{})
return fmt.Errorf("[DEBUG] Error saving Mask to state for Virtual Server (%s): %s", d.Id(), err)
}

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

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

resourceBigipLtmVirtualServerAttrDefaults(d)

name := d.Id()

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

destPort := fmt.Sprintf("%s:%d", d.Get("destination").(string), d.Get("port").(int))
if strings.Contains(d.Get("destination").(string), ":") {
destPort = fmt.Sprintf("%s.%d", d.Get("destination").(string), d.Get("port").(int))
}

vs := &bigip.VirtualServer{
Destination: fmt.Sprintf("%s:%d", d.Get("destination").(string), d.Get("port").(int)),
Destination: destPort,
FallbackPersistenceProfile: d.Get("fallback_persistence_profile").(string),
Source: d.Get("source").(string),
Pool: d.Get("pool").(string),
Expand Down
79 changes: 79 additions & 0 deletions bigip/resource_bigip_ltm_virtual_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ resource "bigip_ltm_virtual_server" "test-vs" {
}
`

var TEST_VS6_NAME = fmt.Sprintf("/%s/test-vs6", TEST_PARTITION)

var TEST_VS6_RESOURCE = TEST_IRULE_RESOURCE + `
resource "bigip_ltm_virtual_server" "test-vs" {
name = "` + TEST_VS6_NAME + `"
destination = "fe80::11"
port = 9999
source_address_translation = "automap"
ip_protocol = "tcp"
irules = ["${bigip_ltm_irule.test-rule.name}"]
profiles = ["/Common/http"]
client_profiles = ["/Common/tcp"]
server_profiles = ["/Common/tcp-lan-optimized"]
persistence_profiles = ["/Common/source_addr"]
fallback_persistence_profile = "/Common/dest_addr"
}
`

func TestAccBigipLtmVS_create(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand All @@ -51,6 +71,47 @@ func TestAccBigipLtmVS_create(t *testing.T) {
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "destination", "10.255.255.254"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "port", "9999"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "mask", "255.255.255.255"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source", "0.0.0.0/0"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source_address_translation", "automap"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "ip_protocol", "tcp"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "irules.0", TEST_IRULE_NAME),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
fmt.Sprintf("profiles.%d", schema.HashString("/Common/http")),
"/Common/http"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
fmt.Sprintf("client_profiles.%d", schema.HashString("/Common/tcp")),
"/Common/tcp"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
fmt.Sprintf("server_profiles.%d", schema.HashString("/Common/tcp-lan-optimized")),
"/Common/tcp-lan-optimized"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs",
fmt.Sprintf("persistence_profiles.%d", schema.HashString("/Common/source_addr")),
"/Common/source_addr"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "fallback_persistence_profile", "/Common/dest_addr"),
),
},
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
testCheckVSsDestroyed,
testCheckIRulesDestroyed,
),
Steps: []resource.TestStep{
{
Config: TEST_VS6_RESOURCE,
Check: resource.ComposeTestCheckFunc(
testCheckVSExists(TEST_VS6_NAME, true),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "name", TEST_VS6_NAME),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "destination", "fe80::11"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "port", "9999"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "mask", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source", "::/0"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "source_address_translation", "automap"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "ip_protocol", "tcp"),
resource.TestCheckResourceAttr("bigip_ltm_virtual_server.test-vs", "irules.0", TEST_IRULE_NAME),
Expand Down Expand Up @@ -92,6 +153,24 @@ func TestAccBigipLtmVS_import(t *testing.T) {
},
},
})
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAcctPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testCheckVSsDestroyed,
Steps: []resource.TestStep{
{
Config: TEST_VS6_RESOURCE,
Check: resource.ComposeTestCheckFunc(
testCheckVSExists(TEST_VS6_NAME, true),
),
ResourceName: TEST_VS6_NAME,
ImportState: false,
ImportStateVerify: true,
},
},
})
}

func testCheckVSExists(name string, exists bool) resource.TestCheckFunc {
Expand Down
4 changes: 2 additions & 2 deletions bigip/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ func validateF5Name(value interface{}, field string) (ws []string, errors []erro
}

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

0 comments on commit 805f97b

Please sign in to comment.