From 2798c884ac995164963b543f6f535c37eae981d3 Mon Sep 17 00:00:00 2001 From: Frederic Berot-Armand Date: Mon, 31 Oct 2022 14:55:07 +0000 Subject: [PATCH] Enable attaching vms to LBU with 'backendIps' and update LoadBalancerAttachement --- .../data_source_outscale_load_balancer_vms.go | 10 +- .../resource_outscale_load_balancer_vms.go | 256 +++++++++++++----- ...esource_outscale_load_balancer_vms_test.go | 86 +++--- 3 files changed, 227 insertions(+), 125 deletions(-) diff --git a/outscale/data_source_outscale_load_balancer_vms.go b/outscale/data_source_outscale_load_balancer_vms.go index ba03d1333..2cd5c97ab 100644 --- a/outscale/data_source_outscale_load_balancer_vms.go +++ b/outscale/data_source_outscale_load_balancer_vms.go @@ -9,16 +9,14 @@ func DataSourceOutscaleLoadBalancerVms() *schema.Resource { return &schema.Resource{ Read: DataSourceOutscaleLoadBalancerVmsRead, Schema: getDataSourceSchemas(map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), "load_balancer_name": { Type: schema.TypeString, - ForceNew: true, - Required: true, + Computed: true, }, - "backend_vm_ids": { - Type: schema.TypeList, - ForceNew: true, - Required: true, + Type: schema.TypeSet, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, "request_id": { diff --git a/outscale/resource_outscale_load_balancer_vms.go b/outscale/resource_outscale_load_balancer_vms.go index 919623fbd..3c803b2db 100644 --- a/outscale/resource_outscale_load_balancer_vms.go +++ b/outscale/resource_outscale_load_balancer_vms.go @@ -10,6 +10,7 @@ import ( "github.com/outscale/terraform-provider-outscale/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -26,10 +27,14 @@ func ResourceLBUAttachment() *schema.Resource { ForceNew: true, Required: true, }, - "backend_vm_ids": { Type: schema.TypeSet, - Required: true, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "backend_ips": { + Type: schema.TypeSet, + Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, "request_id": { @@ -42,25 +47,34 @@ func ResourceLBUAttachment() *schema.Resource { func ResourceLBUAttachmentCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI lbuName := d.Get("load_balancer_name").(string) - vmIds := d.Get("backend_vm_ids").(*schema.Set) + vmIds := utils.SetToStringSlice(d.Get("backend_vm_ids").(*schema.Set)) + vmIps := d.Get("backend_ips").(*schema.Set) + if len(vmIds) == 0 && vmIps.List() == nil { + return fmt.Errorf("error: the 'backend_vm_ids' and 'backend_ips' parameters cannot both be empty") + } + if vmIps.Len() > 0 { + vm_ips, err := getVmIdsThroughVmIps(conn, vmIps) + if err != nil { + return err + } + vmIds = append(vmIds, vm_ips...) + } req := oscgo.RegisterVmsInLoadBalancerRequest{ LoadBalancerName: lbuName, - BackendVmIds: SetStringToListString(vmIds), + BackendVmIds: vmIds, } - var err error - err = resource.Retry(5*time.Minute, func() *resource.RetryError { - _, httpResp, err := conn.LoadBalancerApi. - RegisterVmsInLoadBalancer(context.Background()). - RegisterVmsInLoadBalancerRequest(req). - Execute() + + err := retry.Retry(5*time.Minute, func() *retry.RetryError { + _, httpResp, err := conn.LoadBalancerApi.RegisterVmsInLoadBalancer( + context.Background()).RegisterVmsInLoadBalancerRequest(req).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } return nil }) if err != nil { - return fmt.Errorf("Failure registering backend_vm_ids with LBU: %s", err) + return fmt.Errorf("Failure Linking LoadBalancer backend_vm_ids/backend_ips with LBU: %w", err) } d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", lbuName))) return ResourceLBUAttachmentRead(d, meta) @@ -69,121 +83,223 @@ func ResourceLBUAttachmentCreate(d *schema.ResourceData, meta interface{}) error func ResourceLBUAttachmentRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI lbuName := d.Get("load_balancer_name").(string) - lb, _, err := readResourceLb(conn, lbuName) + lbu, _, err := readResourceLb(conn, lbuName) if err != nil { return err } - if lb == nil { + if lbu == nil { utils.LogManuallyDeleted("LoadBalancerVms", d.Id()) d.SetId("") return nil } - - expected := d.Get("backend_vm_ids").(*schema.Set) - all_backends := schema.Set{F: expected.F} - for _, v := range *lb.BackendVmIds { - all_backends.Add(v) + if len(lbu.GetBackendVmIds()) == 0 { + utils.LogManuallyDeleted("LoadBalancerVms", d.Id()) + d.SetId("") + return nil } + expectedVmIds := d.Get("backend_vm_ids").(*schema.Set) + all_backendVms := d.Get("backend_vm_ids").(*schema.Set) + expectedIps := d.Get("backend_ips").(*schema.Set) + all_backendIps := d.Get("backend_ips").(*schema.Set) - managed := all_backends.Intersection(expected) - d.Set("backend_vm_ids", managed) + for _, vmId := range lbu.GetBackendVmIds() { + all_backendVms.Add(vmId) + } + publicIps, err := getVmIpsThroughVmIds(conn, all_backendVms) + if err != nil { + return err + } + for _, vmIp := range publicIps { + all_backendIps.Add(vmIp) + } + managedVmIds := all_backendVms.Intersection(expectedVmIds) + managedIps := all_backendIps.Intersection(expectedIps) - if managed.Len() == 0 { + if managedVmIds.Len() == 0 && managedIps.Len() == 0 { log.Printf("[WARN] not expected attachments found in LBU %s", lbuName) - log.Printf("[WARN] lbu current attachments are %#v", &all_backends) - log.Printf("[WARN] we would manage only these attachments %#v", expected) - log.Printf("[WARN] no managed attachments are present.") d.SetId("") + return nil + } + if err := d.Set("load_balancer_name", lbu.GetLoadBalancerName()); err != nil { + return err + } + if err := d.Set("backend_vm_ids", managedVmIds); err != nil { + return err + } + if err := d.Set("backend_ips", managedIps); err != nil { + return err } - return nil } func ResourceLBUAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI lbuName := d.Get("load_balancer_name").(string) - var err error - - if !d.HasChange("backend_vm_ids") { - return nil + linkReq, unLinkReq, err := buildUpdateBackendsRequest(d, conn, lbuName) + if err != nil { + return err } - oldBackends, newBackends := d.GetChange("backend_vm_ids") - inter := oldBackends.(*schema.Set).Intersection(newBackends.(*schema.Set)) - created := newBackends.(*schema.Set).Difference(inter) - removed := oldBackends.(*schema.Set).Difference(inter) - - if created.Len() > 0 { - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + if unLinkReq.HasBackendVmIds() { + err := retry.Retry(5*time.Minute, func() *retry.RetryError { _, httpResp, err := conn.LoadBalancerApi. - RegisterVmsInLoadBalancer(context.Background()). - RegisterVmsInLoadBalancerRequest( - oscgo.RegisterVmsInLoadBalancerRequest{ - LoadBalancerName: lbuName, - BackendVmIds: SetStringToListString(created), - }). - Execute() + UnlinkLoadBalancerBackendMachines(context.Background()). + UnlinkLoadBalancerBackendMachinesRequest(*unLinkReq).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } return nil }) if err != nil { - return fmt.Errorf("Failure registering new backend_vm_ids with LBU: %s", err) + return fmt.Errorf("failure unlinking backends from LBU: %s", err) } } - if removed.Len() > 0 { - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + + if linkReq.HasBackendVmIds() { + err := retry.Retry(5*time.Minute, func() *retry.RetryError { _, httpResp, err := conn.LoadBalancerApi. - DeregisterVmsInLoadBalancer(context.Background()). - DeregisterVmsInLoadBalancerRequest( - oscgo.DeregisterVmsInLoadBalancerRequest{ - LoadBalancerName: lbuName, - BackendVmIds: SetStringToListString(removed), - }). - Execute() + LinkLoadBalancerBackendMachines(context.Background()). + LinkLoadBalancerBackendMachinesRequest(*linkReq).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } return nil }) if err != nil { - return fmt.Errorf("Failure deregistering old backend_vm_ids from LBU: %s", err) + return fmt.Errorf("failure linking backends to LBU: %s", err) } } return ResourceLBUAttachmentRead(d, meta) } +func buildUpdateBackendsRequest(d *schema.ResourceData, conn *oscgo.APIClient, lbuName string) (*oscgo.LinkLoadBalancerBackendMachinesRequest, *oscgo.UnlinkLoadBalancerBackendMachinesRequest, error) { + linkReq := oscgo.NewLinkLoadBalancerBackendMachinesRequest(lbuName) + unLinkReq := oscgo.NewUnlinkLoadBalancerBackendMachinesRequest(lbuName) + linkVmIds := make([]string, 0, 0) + unlinkVmIds := make([]string, 0, 0) + if d.HasChange("backend_vm_ids") { + oldBackends, newBackends := d.GetChange("backend_vm_ids") + inter := oldBackends.(*schema.Set).Intersection(newBackends.(*schema.Set)) + created := newBackends.(*schema.Set).Difference(inter) + removed := oldBackends.(*schema.Set).Difference(inter) + + if created.Len() > 0 { + linkVmIds = append(linkVmIds, utils.SetToStringSlice(created)...) + } + if removed.Len() > 0 { + unlinkVmIds = append(linkVmIds, utils.SetToStringSlice(removed)...) + } + } + if d.HasChange("backend_ips") { + oldBackends, newBackends := d.GetChange("backend_ips") + inter := oldBackends.(*schema.Set).Intersection(newBackends.(*schema.Set)) + created := newBackends.(*schema.Set).Difference(inter) + removed := oldBackends.(*schema.Set).Difference(inter) + + if created.Len() > 0 { + vmIds, err := getVmIdsThroughVmIps(conn, created) + if err != nil { + return nil, nil, err + } + linkVmIds = append(linkVmIds, vmIds...) + } + if removed.Len() > 0 { + vmIds, err := getVmIdsThroughVmIps(conn, removed) + if err != nil { + return nil, nil, err + } + unlinkVmIds = append(linkVmIds, vmIds...) + } + } + if len(linkVmIds) > 0 { + linkReq.SetBackendVmIds(linkVmIds) + } + if len(unlinkVmIds) > 0 { + unLinkReq.SetBackendVmIds(unlinkVmIds) + } + return linkReq, unLinkReq, nil +} + func ResourceLBUAttachmentDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*OutscaleClient).OSCAPI lbuName := d.Get("load_balancer_name").(string) - vmIds := d.Get("backend_vm_ids").(*schema.Set) - - req := oscgo.DeregisterVmsInLoadBalancerRequest{ - LoadBalancerName: lbuName, - BackendVmIds: SetStringToListString(vmIds), + unlinkVmIds := utils.SetToStringSlicePtr(d.Get("backend_vm_ids").(*schema.Set)) + if ips := d.Get("backend_ips").(*schema.Set); ips.Len() > 0 { + vmIps, err := getVmIdsThroughVmIps(conn, ips) + if err != nil { + return err + } + *unlinkVmIds = append(*unlinkVmIds, vmIps...) } - var err error - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + err := retry.Retry(5*time.Minute, func() *retry.RetryError { _, httpResp, err := conn.LoadBalancerApi. - DeregisterVmsInLoadBalancer(context.Background()). - DeregisterVmsInLoadBalancerRequest(req). - Execute() + UnlinkLoadBalancerBackendMachines(context.Background()). + UnlinkLoadBalancerBackendMachinesRequest( + oscgo.UnlinkLoadBalancerBackendMachinesRequest{ + LoadBalancerName: lbuName, + BackendVmIds: unlinkVmIds, + }).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } return nil }) if err != nil { - return fmt.Errorf("Failure deregistering backend_vm_ids from LBU: %s", err) + return fmt.Errorf("failure unlinking backend_ips from LBU: %s", err) } return nil } -func SetStringToListString(set *schema.Set) []string { - result := make([]string, 0, set.Len()) - for _, val := range set.List() { - result = append(result, val.(string)) +func getVmIdsThroughVmIps(conn *oscgo.APIClient, vmIps *schema.Set) ([]string, error) { + filterIps := oscgo.NewFiltersVm() + filterIps.SetPublicIps(utils.SetToStringSlice(vmIps)) + var resp oscgo.ReadVmsResponse + err := retry.Retry(30*time.Second, func() *retry.RetryError { + rp, httpResp, err := conn.VmApi.ReadVms(context.Background()).ReadVmsRequest(oscgo.ReadVmsRequest{ + Filters: filterIps}).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return nil, err + } + vms := resp.GetVms() + if len(vms) == 0 { + return nil, fmt.Errorf("not found Vms with public_ip [%v]", utils.SetToStringSlice(vmIps)) + } + vmsIds := make([]string, 0, len(vms)) + for _, vm := range vms { + vmsIds = append(vmsIds, vm.GetVmId()) + } + return vmsIds, nil +} + +func getVmIpsThroughVmIds(conn *oscgo.APIClient, vmIds *schema.Set) ([]string, error) { + filters := oscgo.NewFiltersVm() + filters.SetVmIds(utils.SetToStringSlice(vmIds)) + var resp oscgo.ReadVmsResponse + err := retry.Retry(30*time.Second, func() *retry.RetryError { + rp, httpResp, err := conn.VmApi.ReadVms(context.Background()).ReadVmsRequest(oscgo.ReadVmsRequest{ + Filters: filters}).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return nil, err + } + vms := resp.GetVms() + if len(vms) == 0 { + return nil, fmt.Errorf("not found Vms with vm_ids [%v]", utils.SetToStringSlice(vmIds)) + } + publicIps := make([]string, 0, len(vms)) + for _, vm := range vms { + publicIps = append(publicIps, vm.GetPublicIp()) } - return result + return publicIps, nil } diff --git a/outscale/resource_outscale_load_balancer_vms_test.go b/outscale/resource_outscale_load_balancer_vms_test.go index 7116493a2..2c9d24829 100644 --- a/outscale/resource_outscale_load_balancer_vms_test.go +++ b/outscale/resource_outscale_load_balancer_vms_test.go @@ -18,6 +18,7 @@ func TestAccVM_WithLBUAttachment_basic(t *testing.T) { var conf oscgo.LoadBalancer omi := os.Getenv("OUTSCALE_IMAGEID") rand := acctest.RandIntRange(0, 50) + region := utils.GetRegion() testCheckInstanceAttached := func(count int) resource.TestCheckFunc { return func(*terraform.State) error { if conf.BackendVmIds != nil { @@ -32,14 +33,27 @@ func TestAccVM_WithLBUAttachment_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheckValues(t) }, - IDRefreshName: "outscale_load_balancer.barTach", + IDRefreshName: "outscale_load_balancer.lbu_test", Providers: testAccProviders, - CheckDestroy: testAccCheckOutscaleLBUDestroy, Steps: []resource.TestStep{ { - Config: testAccOutscaleLBUAttachmentConfig1(rand, omi, utils.GetRegion()), + Config: testAccOutscaleLBUAttachmentConfig1(rand, omi, region), Check: resource.ComposeTestCheckFunc( - testAccCheckOutscaleLBUExists("outscale_load_balancer.barTach", &conf), + testAccCheckOutscaleLBUExists("outscale_load_balancer.lbu_test", &conf), + testCheckInstanceAttached(1), + ), + }, + { + Config: testAcc_ConfigLBUAttachmentAddUpdate(omi, region), + Check: resource.ComposeTestCheckFunc( + testAccCheckOutscaleLBUExists("outscale_load_balancer.lbu_test", &conf), + testCheckInstanceAttached(2), + ), + }, + { + Config: testAcc_ConfigLBUAttachmentRemoveUpdate(omi, region), + Check: resource.ComposeTestCheckFunc( + testAccCheckOutscaleLBUExists("outscale_load_balancer.lbu_test", &conf), testCheckInstanceAttached(1), ), }, @@ -50,7 +64,7 @@ func TestAccVM_WithLBUAttachment_basic(t *testing.T) { // add one attachment func testAccOutscaleLBUAttachmentConfig1(num int, omi, region string) string { return fmt.Sprintf(` -resource "outscale_load_balancer" "barTach" { +resource "outscale_load_balancer" "lbu_test" { load_balancer_name = "load-test-%d" subregion_names = ["%[2]sa"] listeners { @@ -72,35 +86,26 @@ resource "outscale_security_group" "sg_lb1" { resource "outscale_vm" "foo1" { image_id = "%[3]s" - vm_type = "tinav4.c1r1p1" + vm_type = "tinav5.c2r2p2" security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } -resource "outscale_security_group" "sg_lb02" { - security_group_name = "terraform_test_lb02" - description = "Used in the terraform acceptance tests" - tags { - key = "Name" - value = "tf-acc-test" - } -} - resource "outscale_vm" "foo2" { image_id = "%[3]s" - vm_type = "tinav4.c1r1p1" - security_group_ids = [outscale_security_group.sg_lb02.security_group_id] + vm_type = "tinav5.c2r2p2" + security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } resource "outscale_load_balancer_vms" "foo1" { - load_balancer_name = outscale_load_balancer.barTach.id - backend_vm_ids = [outscale_vm.foo1.id] + load_balancer_name = outscale_load_balancer.lbu_test.load_balancer_name + backend_vm_ids = [outscale_vm.foo1.vm_id] } `, num, region, omi) } func testAcc_ConfigLBUAttachmentAddUpdate(omi, region string) string { return fmt.Sprintf(` -resource "outscale_load_balancer" "barTach" { +resource "outscale_load_balancer" "lbu_test" { load_balancer_name = "load-test12" subregion_names = ["%[1]sa"] listeners { @@ -122,35 +127,27 @@ resource "outscale_security_group" "sg_lb1" { resource "outscale_vm" "foo1" { image_id = "%[2]s" - vm_type = "tinav4.c1r1p1" + vm_type = "tinav5.c2r2p2" security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } -resource "outscale_security_group" "sg_lb02" { - security_group_name = "terraform_test_lb02" - description = "Used in the terraform acceptance tests" - tags { - key = "Name" - value = "tf-acc-test" - } -} - resource "outscale_vm" "foo2" { image_id = "%[2]s" - vm_type = "tinav4.c1r1p1" - security_group_ids = [outscale_security_group.sg_lb02.security_group_id] + vm_type = "tinav5.c2r2p2" + security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } resource "outscale_load_balancer_vms" "foo1" { - load_balancer_name = outscale_load_balancer.barTach.id - backend_vm_ids = [outscale_vm.foo1.id, outscale_vm.foo2.id] + load_balancer_name = outscale_load_balancer.lbu_test.load_balancer_name + backend_vm_ids = [outscale_vm.foo1.vm_id] + backend_ips = [outscale_vm.foo2.public_ip] } `, region, omi) } func testAcc_ConfigLBUAttachmentRemoveUpdate(omi, region string) string { return fmt.Sprintf(` -resource "outscale_load_balancer" "barTach" { +resource "outscale_load_balancer" "lbu_test" { load_balancer_name = "load-test12" subregion_names = ["%sa"] listeners { @@ -172,28 +169,19 @@ resource "outscale_security_group" "sg_lb1" { resource "outscale_vm" "foo1" { image_id = "%[2]s" - vm_type = "tinav4.c1r1p1" + vm_type = "tinav5.c2r2p2" security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } -resource "outscale_security_group" "sg_lb02" { - security_group_name = "terraform_test_lb02" - description = "Used in the terraform acceptance tests" - tags { - key = "Name" - value = "tf-acc-test" - } -} - resource "outscale_vm" "foo2" { image_id = "%[2]s" - vm_type = "tinav4.c1r1p1" - security_group_ids = [outscale_security_group.sg_lb02.security_group_id] + vm_type = "tinav5.c2r2p2" + security_group_ids = [outscale_security_group.sg_lb1.security_group_id] } resource "outscale_load_balancer_vms" "foo1" { - load_balancer_name = outscale_load_balancer.barTach.id - backend_vm_ids = [outscale_vm.foo2.id] + load_balancer_name = outscale_load_balancer.lbu_test.load_balancer_name + backend_vm_ids = [outscale_vm.foo1.vm_id] } `, region, omi) }