Skip to content

Commit 748c131

Browse files
committed
chore(ref): order of deleting cloud formation resources matters
1 parent 26a928a commit 748c131

File tree

1 file changed

+78
-53
lines changed

1 file changed

+78
-53
lines changed

test/e2e/shared/aws.go

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -469,66 +469,91 @@ func SetMultitenancyEnvVars(prov client.ConfigProvider) error {
469469
func deleteResourcesInCloudFormation(prov client.ConfigProvider, t *cfn_bootstrap.Template) {
470470
iamSvc := iam.New(prov)
471471
temp := *renderCustomCloudFormation(t)
472+
var (
473+
iamRoles []*cfn_iam.Role
474+
instanceProfiles []*cfn_iam.InstanceProfile
475+
policies []*cfn_iam.ManagedPolicy
476+
groups []*cfn_iam.Group
477+
)
478+
// the deletion order of these resources is important. Policies need to be last,
479+
// so they don't have any attached resources which prevents their deletion.
480+
// temp.Resources is a map. Traversing that directly results in undetermined order.
472481
for _, val := range temp.Resources {
473-
By(fmt.Sprintf("deleting the following resource: %s", val.AWSCloudFormationType()))
474-
tayp := val.AWSCloudFormationType()
475-
if tayp == configservice.ResourceTypeAwsIamRole {
482+
switch val.AWSCloudFormationType() {
483+
case configservice.ResourceTypeAwsIamRole:
476484
role := val.(*cfn_iam.Role)
477-
By(fmt.Sprintf("cleanup for role with name '%s'", role.RoleName))
478-
// added repeat to not keep flooding the logs.
479-
repeat := false
480-
Eventually(func(gomega Gomega) bool {
481-
_, err := iamSvc.DeleteRole(&iam.DeleteRoleInput{RoleName: aws.String(role.RoleName)})
482-
if err != nil && !repeat {
483-
By(fmt.Sprintf("failed to delete role '%s'; reason: %+v", role.RoleName, err))
484-
repeat = true
485-
}
486-
code, ok := awserrors.Code(err)
487-
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
488-
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
489-
}
490-
if val.AWSCloudFormationType() == "AWS::IAM::InstanceProfile" {
485+
iamRoles = append(iamRoles, role)
486+
case "AWS::IAM::InstanceProfile":
491487
profile := val.(*cfn_iam.InstanceProfile)
492-
By(fmt.Sprintf("cleanup for profile with name '%s'", profile.InstanceProfileName))
493-
repeat := false
494-
Eventually(func(gomega Gomega) bool {
495-
_, err := iamSvc.DeleteInstanceProfile(&iam.DeleteInstanceProfileInput{InstanceProfileName: aws.String(profile.InstanceProfileName)})
496-
if err != nil && !repeat {
497-
By(fmt.Sprintf("failed to delete role '%s'; reason: %+v", profile.InstanceProfileName, err))
498-
repeat = true
499-
}
500-
code, ok := awserrors.Code(err)
501-
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
502-
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
503-
}
504-
if val.AWSCloudFormationType() == "AWS::IAM::ManagedPolicy" {
488+
instanceProfiles = append(instanceProfiles, profile)
489+
case "AWS::IAM::ManagedPolicy":
505490
policy := val.(*cfn_iam.ManagedPolicy)
506-
policies, err := iamSvc.ListPolicies(&iam.ListPoliciesInput{})
507-
Expect(err).NotTo(HaveOccurred())
508-
if len(policies.Policies) > 0 {
509-
for _, p := range policies.Policies {
510-
if aws.StringValue(p.PolicyName) == policy.ManagedPolicyName {
511-
By(fmt.Sprintf("cleanup for policy '%s'", p.String()))
512-
repeat := false
513-
Eventually(func(gomega Gomega) bool {
514-
_, err := iamSvc.DeletePolicy(&iam.DeletePolicyInput{PolicyArn: p.Arn})
515-
if err != nil && !repeat {
516-
By(fmt.Sprintf("failed to delete policy '%s'; reason: %+v", policy.Description, err))
517-
repeat = true
518-
}
519-
code, ok := awserrors.Code(err)
520-
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
521-
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
522-
// TODO: why is there a break here? Don't we want to clean up everything?
523-
break
524-
}
491+
policies = append(policies, policy)
492+
case configservice.ResourceTypeAwsIamGroup:
493+
group := val.(*cfn_iam.Group)
494+
groups = append(groups, group)
495+
}
496+
}
497+
for _, role := range iamRoles {
498+
By(fmt.Sprintf("deleting the following role: %s", role.RoleName))
499+
repeat := false
500+
Eventually(func(gomega Gomega) bool {
501+
_, err := iamSvc.DeleteRole(&iam.DeleteRoleInput{RoleName: aws.String(role.RoleName)})
502+
if err != nil && !repeat {
503+
By(fmt.Sprintf("failed to delete role '%s'; reason: %+v", role.RoleName, err))
504+
repeat = true
505+
}
506+
code, ok := awserrors.Code(err)
507+
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
508+
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
509+
}
510+
for _, profile := range instanceProfiles {
511+
By(fmt.Sprintf("cleanup for profile with name '%s'", profile.InstanceProfileName))
512+
repeat := false
513+
Eventually(func(gomega Gomega) bool {
514+
_, err := iamSvc.DeleteInstanceProfile(&iam.DeleteInstanceProfileInput{InstanceProfileName: aws.String(profile.InstanceProfileName)})
515+
if err != nil && !repeat {
516+
By(fmt.Sprintf("failed to delete role '%s'; reason: %+v", profile.InstanceProfileName, err))
517+
repeat = true
518+
}
519+
code, ok := awserrors.Code(err)
520+
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
521+
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
522+
}
523+
for _, policy := range policies {
524+
policies, err := iamSvc.ListPolicies(&iam.ListPoliciesInput{})
525+
Expect(err).NotTo(HaveOccurred())
526+
if len(policies.Policies) > 0 {
527+
for _, p := range policies.Policies {
528+
if aws.StringValue(p.PolicyName) == policy.ManagedPolicyName {
529+
By(fmt.Sprintf("cleanup for policy '%s'", p.String()))
530+
repeat := false
531+
Eventually(func(gomega Gomega) bool {
532+
_, err := iamSvc.DeletePolicy(&iam.DeletePolicyInput{PolicyArn: p.Arn})
533+
if err != nil && !repeat {
534+
By(fmt.Sprintf("failed to delete policy '%s'; reason: %+v", policy.Description, err))
535+
repeat = true
536+
}
537+
code, ok := awserrors.Code(err)
538+
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
539+
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
540+
// TODO: why is there a break here? Don't we want to clean up everything?
541+
break
525542
}
526543
}
527544
}
528-
if val.AWSCloudFormationType() == configservice.ResourceTypeAwsIamGroup {
529-
group := val.(*cfn_iam.Group)
530-
_, _ = iamSvc.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(group.GroupName)})
531-
}
545+
}
546+
for _, group := range groups {
547+
repeat := false
548+
Eventually(func(gomega Gomega) bool {
549+
_, err := iamSvc.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(group.GroupName)})
550+
if err != nil && !repeat {
551+
By(fmt.Sprintf("failed to delete group '%s'; reason: %+v", group.GroupName, err))
552+
repeat = true
553+
}
554+
code, ok := awserrors.Code(err)
555+
return err == nil || (ok && code == iam.ErrCodeNoSuchEntityException)
556+
}, 5*time.Minute, 5*time.Second).Should(BeTrue())
532557
}
533558
}
534559

0 commit comments

Comments
 (0)