From 69929e0d1369d759defad9181820a5797f52eb8c Mon Sep 17 00:00:00 2001 From: Ujjwal Kumar Date: Wed, 17 Aug 2022 13:09:05 +0530 Subject: [PATCH] fix(security_group): added wait logic to wait for target removal to avoid 409 --- .../vpc/resource_ibm_is_security_group.go | 108 +++++++++++++++++- .../resource_ibm_is_security_group_test.go | 101 ++++++++++++++++ 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_security_group.go b/ibm/service/vpc/resource_ibm_is_security_group.go index b0d1bcf885..58466e5af0 100644 --- a/ibm/service/vpc/resource_ibm_is_security_group.go +++ b/ibm/service/vpc/resource_ibm_is_security_group.go @@ -9,11 +9,13 @@ import ( "log" "os" "reflect" + "time" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" "github.com/IBM/vpc-go-sdk/vpcv1" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -42,6 +44,11 @@ func ResourceIBMISSecurityGroup() *schema.Resource { }, ), + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + Schema: map[string]*schema.Schema{ isSecurityGroupName: { @@ -410,7 +417,19 @@ func resourceIBMISSecurityGroupDelete(d *schema.ResourceData, meta interface{}) deleteSecurityGroupTargetBindingOptions := sess.NewDeleteSecurityGroupTargetBindingOptions(id, *securityGroupTargetReference.ID) response, err = sess.DeleteSecurityGroupTargetBinding(deleteSecurityGroupTargetBindingOptions) if err != nil { - return fmt.Errorf("[ERROR] Error deleting security group target binding while deleting security group : %s\n%s", err, response) + if response != nil { + if response.StatusCode == 404 { + log.Printf("[DEBUG] Security group target(%s) binding is already deleted", *securityGroupTargetReference.ID) + } else if response.StatusCode == 409 { + log.Printf("[DEBUG] Security group target(%s) binding is in deleting status, waiting till target is removed", *securityGroupTargetReference.ID) + _, err = isWaitForTargetDeleted(sess, id, *securityGroupTargetReference.ID, securityGroupTargetReferenceIntf, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return err + } + } + } else { + return fmt.Errorf("[ERROR] Error deleting security group target binding while deleting security group : %s\n%s", err, response) + } } } @@ -421,8 +440,21 @@ func resourceIBMISSecurityGroupDelete(d *schema.ResourceData, meta interface{}) ID: &id, } response, err = sess.DeleteSecurityGroup(deleteSecurityGroupOptions) + if err != nil { - return fmt.Errorf("[ERROR] Error Deleting Security Group : %s\n%s", err, response) + if response != nil { + if response.StatusCode == 404 { + log.Printf("[DEBUG] Security group(%s) target bindings are already deleted", id) + } else if response.StatusCode == 409 { + log.Printf("[DEBUG] Security group(%s) has target bindings is in deleting, will wait till target is removed", id) + _, err = isWaitForSgCleanup(sess, id, allrecs, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return err + } + } + } else { + return fmt.Errorf("[ERROR] Error Deleting Security Group : %s\n%s", err, response) + } } d.SetId("") return nil @@ -495,3 +527,75 @@ func makeIBMISSecurityRuleSchema() map[string]*schema.Schema { }, } } + +func isWaitForTargetDeleted(client *vpcv1.VpcV1, sgId, targetId string, target vpcv1.SecurityGroupTargetReferenceIntf, timeout time.Duration) (interface{}, error) { + log.Printf("Waiting for Security group(%s) target(%s) to be deleted.", sgId, targetId) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"deleting"}, + Target: []string{"done", ""}, + Refresh: isTargetRefreshFunc(client, sgId, targetId, target), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + } + + return stateConf.WaitForState() +} + +func isTargetRefreshFunc(client *vpcv1.VpcV1, sgId, targetId string, target vpcv1.SecurityGroupTargetReferenceIntf) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + targetgetoptions := &vpcv1.GetSecurityGroupTargetOptions{ + SecurityGroupID: &sgId, + ID: &targetId, + } + sgTarget, response, err := client.GetSecurityGroupTarget(targetgetoptions) + if err != nil { + return target, "", fmt.Errorf("[ERROR] Error getting target(%s): %s\n%s", targetId, err, response) + } + if response != nil && response.StatusCode == 404 { + return target, "done", nil + } + return sgTarget, "deleting", nil + } +} +func isWaitForSgCleanup(client *vpcv1.VpcV1, sgId string, targets []vpcv1.SecurityGroupTargetReferenceIntf, timeout time.Duration) (interface{}, error) { + log.Printf("Waiting for Security group(%s) target(%s) to be deleted.", sgId, targets) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"deleting"}, + Target: []string{"done", ""}, + Refresh: isSgRefreshFunc(client, sgId, targets), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + } + + return stateConf.WaitForState() +} + +func isSgRefreshFunc(client *vpcv1.VpcV1, sgId string, groups []vpcv1.SecurityGroupTargetReferenceIntf) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + start := "" + allrecs := []vpcv1.SecurityGroupTargetReferenceIntf{} + for { + listSecurityGroupTargetsOptions := client.NewListSecurityGroupTargetsOptions(sgId) + + sggroups, response, err := client.ListSecurityGroupTargets(listSecurityGroupTargetsOptions) + if err != nil || sggroups == nil { + return groups, "", fmt.Errorf("[ERROR] Error Getting Security Group Targets %s\n%s", err, response) + } + if *sggroups.TotalCount == int64(0) { + return groups, "done", nil + } + + start = flex.GetNext(sggroups.Next) + allrecs = append(allrecs, sggroups.Targets...) + + if start == "" { + break + } + } + return allrecs, "deleting", nil + } +} diff --git a/ibm/service/vpc/resource_ibm_is_security_group_test.go b/ibm/service/vpc/resource_ibm_is_security_group_test.go index b9510f8cd0..45eac4976a 100644 --- a/ibm/service/vpc/resource_ibm_is_security_group_test.go +++ b/ibm/service/vpc/resource_ibm_is_security_group_test.go @@ -6,6 +6,7 @@ package vpc_test import ( "errors" "fmt" + "strings" "testing" acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" @@ -52,6 +53,48 @@ func TestAccIBMISSecurityGroup_basic(t *testing.T) { }, }) } +func TestAccIBMISSecurityGroup_wait(t *testing.T) { + var securityGroup string + + vpcname := fmt.Sprintf("tfsg-vpc-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tfsg-subnet-%d", acctest.RandIntRange(10, 100)) + sshname := fmt.Sprintf("tfsg-ssh-%d", acctest.RandIntRange(10, 100)) + vsiname := fmt.Sprintf("tfsg-vsi-%d", acctest.RandIntRange(10, 100)) + bmname := fmt.Sprintf("tfsg-bm-%d", acctest.RandIntRange(10, 100)) + name1 := fmt.Sprintf("tfsg-createname-%d", acctest.RandIntRange(10, 100)) + publickey := strings.TrimSpace(` + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR + `) + //name2 := fmt.Sprintf("tfsg-updatename-%d", acctest.RandIntRange(10, 100)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISsecurityGroupWaitConfig(name1, vpcname, subnetname, sshname, publickey, vsiname, bmname), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISSecurityGroupExists("ibm_is_security_group.testacc_security_group", securityGroup), + resource.TestCheckResourceAttr( + "ibm_is_security_group.testacc_security_group", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_security_group.testacc_security_group", "tags.#", "2"), + resource.TestCheckResourceAttr( + "ibm_is_vpc.testacc_vpc", "name", vpcname), + resource.TestCheckResourceAttr( + "ibm_is_subnet.testacc_subnet", "name", subnetname), + resource.TestCheckResourceAttr( + "ibm_is_ssh_key.testacc_sshkey", "name", sshname), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "name", vsiname), + resource.TestCheckResourceAttr( + "ibm_is_bare_metal_server.testacc_bms", "name", bmname), + ), + }, + }, + }) +} func testAccCheckIBMISSecurityGroupDestroy(s *terraform.State) error { sess, _ := acc.TestAccProvider.Meta().(conns.ClientSession).VpcV1API() @@ -111,6 +154,64 @@ resource "ibm_is_security_group" "testacc_security_group" { tags = ["Tag1", "tag2"] }`, vpcname, name) +} +func testAccCheckIBMISsecurityGroupWaitConfig(name, vpcname, subnetname, sshname, publicKey, vsiname, bmname string) string { + return fmt.Sprintf(` +resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" +} + +resource ibm_is_subnet testacc_subnet { + name = "%s" + zone = "%s" + vpc = "${ibm_is_vpc.testacc_vpc.id}" + total_ipv4_address_count = 16 +} + +resource "ibm_is_security_group" "testacc_security_group" { + name = "%s" + vpc = "${ibm_is_vpc.testacc_vpc.id}" + tags = ["tag1", "tag2"] +} + + +resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" +} + +resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + security_groups = [ibm_is_security_group.testacc_security_group.id] + } + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + network_interfaces { + subnet = ibm_is_subnet.testacc_subnet.id + name = "eth12" + security_groups = [ibm_is_security_group.testacc_security_group.id] + } +} + +resource "ibm_is_bare_metal_server" "testacc_bms" { + profile = "%s" + name = "%s" + image = "%s" + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + security_groups = [ibm_is_security_group.testacc_security_group.id] + } + vpc = ibm_is_vpc.testacc_vpc.id +} +`, vpcname, subnetname, acc.ISZoneName, name, sshname, publicKey, vsiname, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, acc.IsBareMetalServerProfileName, bmname, acc.IsBareMetalServerImage, acc.ISZoneName) + } func testAccCheckIBMISsecurityGroupConfigUpdate(vpcname, name string) string {