From 91147fe11c32a8db25a061ac46b3cc12af7bdd91 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Jan 2017 07:00:32 -0700 Subject: [PATCH] provider/openstack: LoadBalancer Security Groups (#11074) * provider/openstack: LoadBalancer Security Groups This commit adds the ability to specify security groups on a loadbalancer resource. * provider/openstack: LoadBalancer Security Groups Refactor Moving common security group code into a dedicated function. --- .../resource_openstack_lb_loadbalancer_v2.go | 53 +++++ ...ource_openstack_lb_loadbalancer_v2_test.go | 191 +++++++++++++++++- .../r/lb_loadbalancer_v2.html.markdown | 5 + 3 files changed, 243 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2.go b/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2.go index 927c274a0..6a6f199b9 100644 --- a/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2.go +++ b/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) func resourceLoadBalancerV2() *schema.Resource { @@ -80,6 +81,13 @@ func resourceLoadBalancerV2() *schema.Resource { Computed: true, ForceNew: true, }, + + "security_group_ids": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, }, } } @@ -126,6 +134,13 @@ func resourceLoadBalancerV2Create(d *schema.ResourceData, meta interface{}) erro return err } + // Once the loadbalancer has been created, apply any requested security groups + // to the port that was created behind the scenes. + if err := resourceLoadBalancerV2SecurityGroups(networkingClient, lb.VipPortID, d); err != nil { + return err + } + + // If all has been successful, set the ID on the resource d.SetId(lb.ID) return resourceLoadBalancerV2Read(d, meta) @@ -155,6 +170,16 @@ func resourceLoadBalancerV2Read(d *schema.ResourceData, meta interface{}) error d.Set("flavor", lb.Flavor) d.Set("provider", lb.Provider) + // Get any security groups on the VIP Port + if lb.VipPortID != "" { + port, err := ports.Get(networkingClient, lb.VipPortID).Extract() + if err != nil { + return err + } + + d.Set("security_group_ids", port.SecurityGroups) + } + return nil } @@ -184,6 +209,14 @@ func resourceLoadBalancerV2Update(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error updating OpenStack LBaaSV2 LoadBalancer: %s", err) } + // Security Groups get updated separately + if d.HasChange("security_group_ids") { + vipPortID := d.Get("vip_port_id").(string) + if err := resourceLoadBalancerV2SecurityGroups(networkingClient, vipPortID, d); err != nil { + return err + } + } + return resourceLoadBalancerV2Read(d, meta) } @@ -212,6 +245,26 @@ func resourceLoadBalancerV2Delete(d *schema.ResourceData, meta interface{}) erro return nil } +func resourceLoadBalancerV2SecurityGroups(networkingClient *gophercloud.ServiceClient, vipPortID string, d *schema.ResourceData) error { + if vipPortID != "" { + if _, ok := d.GetOk("security_group_ids"); ok { + updateOpts := ports.UpdateOpts{ + SecurityGroups: resourcePortSecurityGroupsV2(d), + } + + log.Printf("[DEBUG] Adding security groups to OpenStack LoadBalancer "+ + "VIP Port (%s): %#v", vipPortID, updateOpts) + + _, err := ports.Update(networkingClient, vipPortID, updateOpts).Extract() + if err != nil { + return err + } + } + } + + return nil +} + func waitForLoadBalancerActive(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { lb, err := loadbalancers.Get(networkingClient, lbID).Extract() diff --git a/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2_test.go b/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2_test.go index d4652d64f..e1592ce62 100644 --- a/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2_test.go @@ -5,9 +5,12 @@ import ( "regexp" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) func TestAccLBV2LoadBalancer_basic(t *testing.T) { @@ -19,13 +22,13 @@ func TestAccLBV2LoadBalancer_basic(t *testing.T) { CheckDestroy: testAccCheckLBV2LoadBalancerDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: TestAccLBV2LoadBalancerConfig_basic, + Config: testAccLBV2LoadBalancerConfig_basic, Check: resource.ComposeTestCheckFunc( testAccCheckLBV2LoadBalancerExists("openstack_lb_loadbalancer_v2.loadbalancer_1", &lb), ), }, resource.TestStep{ - Config: TestAccLBV2LoadBalancerConfig_update, + Config: testAccLBV2LoadBalancerConfig_update, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "openstack_lb_loadbalancer_v2.loadbalancer_1", "name", "loadbalancer_1_updated"), @@ -38,6 +41,62 @@ func TestAccLBV2LoadBalancer_basic(t *testing.T) { }) } +func TestAccLBV2LoadBalancer_secGroup(t *testing.T) { + var lb loadbalancers.LoadBalancer + var sg_1, sg_2 groups.SecGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLBV2LoadBalancerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccLBV2LoadBalancer_secGroup, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV2LoadBalancerExists( + "openstack_lb_loadbalancer_v2.loadbalancer_1", &lb), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_1", &sg_1), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_1", &sg_2), + resource.TestCheckResourceAttr( + "openstack_lb_loadbalancer_v2.loadbalancer_1", "security_group_ids.#", "1"), + testAccCheckLBV2LoadBalancerHasSecGroup(&lb, &sg_1), + ), + }, + resource.TestStep{ + Config: testAccLBV2LoadBalancer_secGroup_update1, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV2LoadBalancerExists( + "openstack_lb_loadbalancer_v2.loadbalancer_1", &lb), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_2", &sg_1), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_2", &sg_2), + resource.TestCheckResourceAttr( + "openstack_lb_loadbalancer_v2.loadbalancer_1", "security_group_ids.#", "2"), + testAccCheckLBV2LoadBalancerHasSecGroup(&lb, &sg_1), + testAccCheckLBV2LoadBalancerHasSecGroup(&lb, &sg_2), + ), + }, + resource.TestStep{ + Config: testAccLBV2LoadBalancer_secGroup_update2, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV2LoadBalancerExists( + "openstack_lb_loadbalancer_v2.loadbalancer_1", &lb), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_2", &sg_1), + testAccCheckNetworkingV2SecGroupExists( + "openstack_networking_secgroup_v2.secgroup_2", &sg_2), + resource.TestCheckResourceAttr( + "openstack_lb_loadbalancer_v2.loadbalancer_1", "security_group_ids.#", "1"), + testAccCheckLBV2LoadBalancerHasSecGroup(&lb, &sg_2), + ), + }, + }, + }) +} + func testAccCheckLBV2LoadBalancerDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -59,7 +118,8 @@ func testAccCheckLBV2LoadBalancerDestroy(s *terraform.State) error { return nil } -func testAccCheckLBV2LoadBalancerExists(n string, lb *loadbalancers.LoadBalancer) resource.TestCheckFunc { +func testAccCheckLBV2LoadBalancerExists( + n string, lb *loadbalancers.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -91,7 +151,31 @@ func testAccCheckLBV2LoadBalancerExists(n string, lb *loadbalancers.LoadBalancer } } -const TestAccLBV2LoadBalancerConfig_basic = ` +func testAccCheckLBV2LoadBalancerHasSecGroup( + lb *loadbalancers.LoadBalancer, sg *groups.SecGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + port, err := ports.Get(networkingClient, lb.VipPortID).Extract() + if err != nil { + return err + } + + for _, p := range port.SecurityGroups { + if p == sg.ID { + return nil + } + } + + return fmt.Errorf("LoadBalancer does not have the security group") + } +} + +const testAccLBV2LoadBalancerConfig_basic = ` resource "openstack_networking_network_v2" "network_1" { name = "network_1" admin_state_up = "true" @@ -110,7 +194,7 @@ resource "openstack_lb_loadbalancer_v2" "loadbalancer_1" { } ` -const TestAccLBV2LoadBalancerConfig_update = ` +const testAccLBV2LoadBalancerConfig_update = ` resource "openstack_networking_network_v2" "network_1" { name = "network_1" admin_state_up = "true" @@ -129,3 +213,98 @@ resource "openstack_lb_loadbalancer_v2" "loadbalancer_1" { vip_subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" } ` + +const testAccLBV2LoadBalancer_secGroup = ` +resource "openstack_networking_secgroup_v2" "secgroup_1" { + name = "secgroup_1" + description = "secgroup_1" +} + +resource "openstack_networking_secgroup_v2" "secgroup_2" { + name = "secgroup_2" + description = "secgroup_2" +} + +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" +} + +resource "openstack_lb_loadbalancer_v2" "loadbalancer_1" { + name = "loadbalancer_1" + vip_subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + security_group_ids = [ + "${openstack_networking_secgroup_v2.secgroup_1.id}" + ] +} +` + +const testAccLBV2LoadBalancer_secGroup_update1 = ` +resource "openstack_networking_secgroup_v2" "secgroup_1" { + name = "secgroup_1" + description = "secgroup_1" +} + +resource "openstack_networking_secgroup_v2" "secgroup_2" { + name = "secgroup_2" + description = "secgroup_2" +} + +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" +} + +resource "openstack_lb_loadbalancer_v2" "loadbalancer_1" { + name = "loadbalancer_1" + vip_subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + security_group_ids = [ + "${openstack_networking_secgroup_v2.secgroup_1.id}", + "${openstack_networking_secgroup_v2.secgroup_2.id}" + ] +} +` + +const testAccLBV2LoadBalancer_secGroup_update2 = ` +resource "openstack_networking_secgroup_v2" "secgroup_1" { + name = "secgroup_1" + description = "secgroup_1" +} + +resource "openstack_networking_secgroup_v2" "secgroup_2" { + name = "secgroup_2" + description = "secgroup_2" +} + +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" +} + +resource "openstack_lb_loadbalancer_v2" "loadbalancer_1" { + name = "loadbalancer_1" + vip_subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + security_group_ids = [ + "${openstack_networking_secgroup_v2.secgroup_2.id}" + ] + depends_on = ["openstack_networking_secgroup_v2.secgroup_1"] +} +` diff --git a/website/source/docs/providers/openstack/r/lb_loadbalancer_v2.html.markdown b/website/source/docs/providers/openstack/r/lb_loadbalancer_v2.html.markdown index f861a4f50..172282893 100644 --- a/website/source/docs/providers/openstack/r/lb_loadbalancer_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_loadbalancer_v2.html.markdown @@ -53,6 +53,10 @@ The following arguments are supported: * `provider` - (Optional) The name of the provider. Changing this creates a new loadbalancer. +* `security_group_ids` - (Optional) A list of security group IDs to apply to the + loadbalancer. The security groups must be specified by ID and not name (as + opposed to how they are configured with the Compute Instance). + ## Attributes Reference The following attributes are exported: @@ -66,4 +70,5 @@ The following attributes are exported: * `admin_state_up` - See Argument Reference above. * `flavor` - See Argument Reference above. * `provider` - See Argument Reference above. +* `security_group_ids` - See Argument Reference above. * `vip_port_id` - The Port ID of the Load Balancer IP.