-
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.
provider/openstack: Load Balancing Member Resource
This commit adds the openstack_lb_member_v1 resource. This resource models a load balancing member which was previously coupled to the openstack_lb_pool_v1 resource. By creating an actual member resource, load balancing members can now be dynamically managed through terraform.
- Loading branch information
Showing
7 changed files
with
515 additions
and
16 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
214 changes: 214 additions & 0 deletions
214
builtin/providers/openstack/resource_openstack_lb_member_v1.go
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,214 @@ | ||
package openstack | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
"github.com/rackspace/gophercloud" | ||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" | ||
) | ||
|
||
func resourceLBMemberV1() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceLBMemberV1Create, | ||
Read: resourceLBMemberV1Read, | ||
Update: resourceLBMemberV1Update, | ||
Delete: resourceLBMemberV1Delete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"region": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"), | ||
}, | ||
"tenant_id": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"pool_id": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"address": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"port": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"weight": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
"admin_state_up": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
ForceNew: false, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
networkingClient, err := config.networkingV2Client(d.Get("region").(string)) | ||
if err != nil { | ||
return fmt.Errorf("Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
createOpts := members.CreateOpts{ | ||
TenantID: d.Get("tenant_id").(string), | ||
PoolID: d.Get("pool_id").(string), | ||
Address: d.Get("address").(string), | ||
ProtocolPort: d.Get("port").(int), | ||
} | ||
|
||
log.Printf("[DEBUG] Create Options: %#v", createOpts) | ||
m, err := members.Create(networkingClient, createOpts).Extract() | ||
if err != nil { | ||
return fmt.Errorf("Error creating OpenStack LB member: %s", err) | ||
} | ||
log.Printf("[INFO] LB member ID: %s", m.ID) | ||
|
||
log.Printf("[DEBUG] Waiting for OpenStack LB member (%s) to become available.", m.ID) | ||
|
||
stateConf := &resource.StateChangeConf{ | ||
Target: "ACTIVE", | ||
Refresh: waitForLBMemberActive(networkingClient, m.ID), | ||
Timeout: 2 * time.Minute, | ||
Delay: 5 * time.Second, | ||
MinTimeout: 3 * time.Second, | ||
} | ||
|
||
_, err = stateConf.WaitForState() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(m.ID) | ||
|
||
return resourceLBMemberV1Read(d, meta) | ||
} | ||
|
||
func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
networkingClient, err := config.networkingV2Client(d.Get("region").(string)) | ||
if err != nil { | ||
return fmt.Errorf("Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
m, err := members.Get(networkingClient, d.Id()).Extract() | ||
if err != nil { | ||
return CheckDeleted(d, err, "LB member") | ||
} | ||
|
||
log.Printf("[DEBUG] Retreived OpenStack LB member %s: %+v", d.Id(), m) | ||
|
||
d.Set("weight", m.Weight) | ||
d.Set("admin_state_up", m.AdminStateUp) | ||
|
||
return nil | ||
} | ||
|
||
func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
networkingClient, err := config.networkingV2Client(d.Get("region").(string)) | ||
if err != nil { | ||
return fmt.Errorf("Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
var updateOpts members.UpdateOpts | ||
if d.HasChange("admin_state_up") { | ||
asu := d.Get("admin_state_up").(bool) | ||
updateOpts.AdminStateUp = asu | ||
} | ||
|
||
log.Printf("[DEBUG] Updating LB member %s with options: %+v", d.Id(), updateOpts) | ||
|
||
_, err = members.Update(networkingClient, d.Id(), updateOpts).Extract() | ||
if err != nil { | ||
return fmt.Errorf("Error updating OpenStack LB member: %s", err) | ||
} | ||
|
||
return resourceLBMemberV1Read(d, meta) | ||
} | ||
|
||
func resourceLBMemberV1Delete(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
networkingClient, err := config.networkingV2Client(d.Get("region").(string)) | ||
if err != nil { | ||
return fmt.Errorf("Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
err = members.Delete(networkingClient, d.Id()).ExtractErr() | ||
if err != nil { | ||
CheckDeleted(d, err, "LB member") | ||
} | ||
|
||
stateConf := &resource.StateChangeConf{ | ||
Pending: []string{"ACTIVE"}, | ||
Target: "DELETED", | ||
Refresh: waitForLBMemberDelete(networkingClient, d.Id()), | ||
Timeout: 2 * time.Minute, | ||
Delay: 5 * time.Second, | ||
MinTimeout: 3 * time.Second, | ||
} | ||
|
||
_, err = stateConf.WaitForState() | ||
if err != nil { | ||
return fmt.Errorf("Error deleting OpenStack LB member: %s", err) | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func waitForLBMemberActive(networkingClient *gophercloud.ServiceClient, memberId string) resource.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
m, err := members.Get(networkingClient, memberId).Extract() | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
log.Printf("[DEBUG] OpenStack LB member: %+v", m) | ||
if m.Status == "ACTIVE" { | ||
return m, "ACTIVE", nil | ||
} | ||
|
||
return m, m.Status, nil | ||
} | ||
} | ||
|
||
func waitForLBMemberDelete(networkingClient *gophercloud.ServiceClient, memberId string) resource.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
log.Printf("[DEBUG] Attempting to delete OpenStack LB member %s", memberId) | ||
|
||
m, err := members.Get(networkingClient, memberId).Extract() | ||
if err != nil { | ||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) | ||
if !ok { | ||
return m, "ACTIVE", err | ||
} | ||
if errCode.Actual == 404 { | ||
log.Printf("[DEBUG] Successfully deleted OpenStack LB member %s", memberId) | ||
return m, "DELETED", nil | ||
} | ||
} | ||
|
||
log.Printf("[DEBUG] OpenStack LB member %s still active.", memberId) | ||
return m, "ACTIVE", nil | ||
} | ||
|
||
} |
138 changes: 138 additions & 0 deletions
138
builtin/providers/openstack/resource_openstack_lb_member_v1_test.go
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,138 @@ | ||
package openstack | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" | ||
) | ||
|
||
func TestAccLBV1Member_basic(t *testing.T) { | ||
var member members.Member | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckLBV1MemberDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccLBV1Member_basic, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckLBV1MemberExists(t, "openstack_lb_member_v1.member_1", &member), | ||
), | ||
}, | ||
resource.TestStep{ | ||
Config: testAccLBV1Member_update, | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("openstack_lb_member_v1.member_1", "admin_state_up", "false"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckLBV1MemberDestroy(s *terraform.State) error { | ||
config := testAccProvider.Meta().(*Config) | ||
networkingClient, err := config.networkingV2Client(OS_REGION_NAME) | ||
if err != nil { | ||
return fmt.Errorf("(testAccCheckLBV1MemberDestroy) Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "openstack_lb_member_v1" { | ||
continue | ||
} | ||
|
||
_, err := members.Get(networkingClient, rs.Primary.ID).Extract() | ||
if err == nil { | ||
return fmt.Errorf("LB Member still exists") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccCheckLBV1MemberExists(t *testing.T, n string, member *members.Member) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("No ID is set") | ||
} | ||
|
||
config := testAccProvider.Meta().(*Config) | ||
networkingClient, err := config.networkingV2Client(OS_REGION_NAME) | ||
if err != nil { | ||
return fmt.Errorf("(testAccCheckLBV1MemberExists) Error creating OpenStack networking client: %s", err) | ||
} | ||
|
||
found, err := members.Get(networkingClient, rs.Primary.ID).Extract() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if found.ID != rs.Primary.ID { | ||
return fmt.Errorf("Member not found") | ||
} | ||
|
||
*member = *found | ||
|
||
return nil | ||
} | ||
} | ||
|
||
var testAccLBV1Member_basic = fmt.Sprintf(` | ||
resource "openstack_networking_network_v2" "network_1" { | ||
name = "network_1" | ||
admin_state_up = "true" | ||
} | ||
resource "openstack_networking_subnet_v2" "subnet_1" { | ||
network_id = "${openstack_networking_network_v2.network_1.id}" | ||
cidr = "192.168.199.0/24" | ||
ip_version = 4 | ||
} | ||
resource "openstack_lb_pool_v1" "pool_1" { | ||
name = "tf_test_lb_pool" | ||
protocol = "HTTP" | ||
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" | ||
lb_method = "ROUND_ROBIN" | ||
} | ||
resource "openstack_lb_member_v1" "member_1" { | ||
pool_id = "${openstack_lb_pool_v1.pool_1.id}" | ||
address = "192.168.199.10" | ||
port = 80 | ||
}`) | ||
|
||
var testAccLBV1Member_update = fmt.Sprintf(` | ||
resource "openstack_networking_network_v2" "network_1" { | ||
name = "network_1" | ||
admin_state_up = "true" | ||
} | ||
resource "openstack_networking_subnet_v2" "subnet_1" { | ||
network_id = "${openstack_networking_network_v2.network_1.id}" | ||
cidr = "192.168.199.0/24" | ||
ip_version = 4 | ||
} | ||
resource "openstack_lb_pool_v1" "pool_1" { | ||
name = "tf_test_lb_pool" | ||
protocol = "HTTP" | ||
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" | ||
lb_method = "ROUND_ROBIN" | ||
} | ||
resource "openstack_lb_member_v1" "member_1" { | ||
pool_id = "${openstack_lb_pool_v1.pool_1.id}" | ||
address = "192.168.199.10" | ||
port = 80 | ||
admin_state_up = false | ||
}`) |
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
Oops, something went wrong.