Skip to content

Commit b1cad98

Browse files
committed
contrib/pkg/awstagdeprovision: Allow for OR filters
AWS docs for filters are a bit sparse, but [1] has: Combining search filters In general, multiple filters with the same key field (for example, tag:Name, search, Instance State) are automatically joined with OR. This is intentional, as the vast majority of filters would not be logical if they were joined with AND. For example, you would get zero results for a search on Instance State=running AND Instance State=stopped. In many cases, you can granulate the results by using complementary search terms on different key fields, where the AND rule is automatically applied instead. If you search for tag: Name:=All values and tag:Instance State=running, you get search results that contain both those criteria. To fine-tune your results, simply remove one filter in the string until the results fit your requirements. Unfortunately, there seems to be no way to represent OR filters in ec2's Filter structure [2]. With the approach I have here, resources that match multiple filters will be fetched multiple times, and may have parallel, racing delete attempts. But we should be robust in the face of racing delete attempt, and hopefully one of the deletes will go through before too long ;). It would be possible to adjust our locally-filtered types (which use filterObjects) to avoid this issue for those types. I may do that in follow-up work, but for now I'm treating all of our types the same way. I've added the filter information to some of the logs to help distinguish between multiple goroutines handling different filters for the same resource. I've also switched hiveutil over to use OR for multiple arguments (it used to use AND) at Joel's suggestion [3]. [1]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Filtering.html#advancedsearch [2]: https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#Filter [3]: #47 (comment)
1 parent a72a886 commit b1cad98

File tree

2 files changed

+51
-29
lines changed

2 files changed

+51
-29
lines changed

contrib/cmd/hiveutil/awstagdeprovision.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ import (
3030
// NewDeprovisionAWSWithTagsCommand is the entrypoint to create the 'aws-tag-deprovision' subcommand
3131
func NewDeprovisionAWSWithTagsCommand() *cobra.Command {
3232
opt := &awstagdeprovision.ClusterUninstaller{}
33-
opt.Filters = awstagdeprovision.AWSFilter{}
3433
cmd := &cobra.Command{
3534
Use: "aws-tag-deprovision KEY=VALUE ...",
3635
Short: "Deprovision AWS assets (as created by openshift-installer) with the given tag(s)",
37-
Long: "Deprovision AWS assets (as created by openshift-installer) with the given tag(s). A resource matches the filter if all of the key/value pairs are in its tags.",
36+
Long: "Deprovision AWS assets (as created by openshift-installer) with the given tag(s). A resource matches the filter if any of the key/value pairs are in its tags.",
3837
Run: func(cmd *cobra.Command, args []string) {
3938
if err := completeAWSUninstaller(opt, args); err != nil {
4039
log.WithError(err).Error("Cannot complete command")
@@ -54,10 +53,12 @@ func NewDeprovisionAWSWithTagsCommand() *cobra.Command {
5453

5554
func completeAWSUninstaller(o *awstagdeprovision.ClusterUninstaller, args []string) error {
5655
for _, arg := range args {
57-
err := parseFilter(o.Filters, arg)
56+
filter := awstagdeprovision.AWSFilter{}
57+
err := parseFilter(filter, arg)
5858
if err != nil {
5959
return fmt.Errorf("cannot parse filter %s: %v", arg, err)
6060
}
61+
o.Filters = append(o.Filters, filter)
6162
}
6263

6364
// Set log level

contrib/pkg/awstagdeprovision/awstagdeprovision.go

+47-26
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,23 @@ type deleteFunc func(awsClient *session.Session, filters AWSFilter, clusterName
6060

6161
// ClusterUninstaller holds the various options for the cluster we want to delete
6262
type ClusterUninstaller struct {
63-
Filters AWSFilter // filter(s) we will be searching for
63+
64+
// Filters is a slice of filters for matching resources. A
65+
// resources matches the whole slice if it matches any of the
66+
// entries. For example:
67+
//
68+
// filter := []map[string]string{
69+
// {
70+
// "a": "b",
71+
// "c": "d:,
72+
// },
73+
// {
74+
// "d": "e",
75+
// },
76+
// }
77+
//
78+
// will match resources with (a:b and c:d) or d:e.
79+
Filters []AWSFilter // filter(s) we will be searching for
6480
Logger log.FieldLogger
6581
LogLevel string
6682
Region string
@@ -107,15 +123,20 @@ func (o *ClusterUninstaller) Run() error {
107123
}
108124

109125
// launch goroutines
126+
goroutines := 0
110127
for name, function := range deleteFuncs {
111-
go deleteRunner(name, function, awsSession, o.Filters, o.ClusterName, o.Logger, returnChannel)
128+
for _, filter := range o.Filters {
129+
go deleteRunner(name, function, awsSession, filter, o.ClusterName, o.Logger, returnChannel)
130+
goroutines++
131+
}
112132
}
113133

114134
// wait for them to finish
115-
for i := 0; i < len(deleteFuncs); i++ {
135+
for goroutines > 0 {
116136
select {
117137
case res := <-returnChannel:
118-
o.Logger.Debugf("goroutine %v complete", res)
138+
goroutines--
139+
o.Logger.Debugf("goroutine %v complete (%d left)", res, goroutines)
119140
}
120141
}
121142

@@ -211,8 +232,8 @@ func filterLBsByVPC(lbs []*elb.LoadBalancerDescription, vpc *ec2.Vpc, logger log
211232
// deleteLBs finds all load balancers under the provided VPC and attempts to delete them
212233
// returns bool representing whether it has completed its work (ie no LBs left to delete)
213234
func deleteLBs(vpc *ec2.Vpc, awsSession *session.Session, logger log.FieldLogger) bool {
214-
logger.Debugf("Deleting load balancers")
215-
defer logger.Debugf("Exiting deleting load balancers")
235+
logger.Debugf("Deleting load balancers (%s)", vpc.VpcId)
236+
defer logger.Debugf("Exiting deleting load balancers (%s)", vpc.VpcId)
216237
elbClient := elb.New(awsSession)
217238

218239
describeLoadBalancersInput := elb.DescribeLoadBalancersInput{}
@@ -307,8 +328,8 @@ func deleteRouteTablesWithVPC(vpc *ec2.Vpc, ec2Client *ec2.EC2, logger log.Field
307328

308329
// deleteVPCs will delete any VPCs that match the provided filters/tags
309330
func deleteVPCs(awsSession *session.Session, filters AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
310-
logger.Debug("Deleting VPCs")
311-
defer logger.Debug("Exiting deleting VPCs")
331+
logger.Debugf("Deleting VPCs (%s)", filters)
332+
defer logger.Debugf("Exiting deleting VPCs (%s)", filters)
312333
ec2Client := getEC2Client(awsSession)
313334

314335
describeVpcsInput := ec2.DescribeVpcsInput{}
@@ -365,8 +386,8 @@ func getEC2Client(awsSession *session.Session) *ec2.EC2 {
365386
// deleteNATGateways will attempt to delete all NAT Gateways that match the provided filters
366387
func deleteNATGateways(awsSession *session.Session, filters AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
367388

368-
logger.Debugf("Deleting NAT Gateways")
369-
defer logger.Debugf("Exiting deleting NAT Gateways")
389+
logger.Debugf("Deleting NAT Gateways (%s)", filters)
390+
defer logger.Debugf("Exiting deleting NAT Gateways (%s)", filters)
370391

371392
ec2Client := getEC2Client(awsSession)
372393
describeNatGatewaysInput := ec2.DescribeNatGatewaysInput{}
@@ -442,8 +463,8 @@ func deleteNetworkIface(iface *string, ec2Client *ec2.EC2, logger log.FieldLogge
442463

443464
// deleteEIPs will attempt to delete any elastic IPs matching the provided filters
444465
func deleteEIPs(awsSession *session.Session, filters AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
445-
logger.Debug("Deleting EIPs")
446-
defer logger.Debug("Exiting deleting EIPs")
466+
logger.Debugf("Deleting EIPs (%s)", filters)
467+
defer logger.Debugf("Exiting deleting EIPs (%s)", filters)
447468
ec2Client := getEC2Client(awsSession)
448469

449470
describeAddressesInput := ec2.DescribeAddressesInput{}
@@ -665,8 +686,8 @@ func tryDeleteRoleProfileByName(roleName string, profileName string, session *se
665686
// Currently openshift/installer creates 3 roles per cluster, 1 for master|worker|bootstrap and identified by the
666687
// cluster name used to install the cluster.
667688
func deleteIAMresources(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
668-
logger.Debugf("Deleting IAM resources")
669-
defer logger.Debugf("Exiting deleting IAM resources")
689+
logger.Debugf("Deleting IAM resources (%s)", filter)
690+
defer logger.Debugf("Exiting deleting IAM resources (%s)", filter)
670691
installerType := []string{"master", "worker", "bootstrap"}
671692
for _, t := range installerType {
672693
// Naming of IAM resources expected from https://github.com/openshift/installer as follows:
@@ -685,8 +706,8 @@ func deleteIAMresources(session *session.Session, filter AWSFilter, clusterName
685706
// deleteInstances will find any running instances that match the given filter and terminate them
686707
// and any instance profiles attached to the instance(s)
687708
func deleteInstances(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
688-
logger.Debugf("Deleting instances")
689-
defer logger.Debugf("Exiting deleting instances")
709+
logger.Debugf("Deleting instances (%s)", filter)
710+
defer logger.Debugf("Exiting deleting instances (%s)", filter)
690711

691712
ec2Client := getEC2Client(session)
692713
iamClient := iam.New(session)
@@ -771,8 +792,8 @@ func deleteSecurityGroupRules(sg *ec2.SecurityGroup, ec2Client *ec2.EC2, logger
771792

772793
// deleteSecurityGroups will attempt to delete all security groups matching the given filter
773794
func deleteSecurityGroups(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
774-
logger.Debugf("Deleting security groups")
775-
defer logger.Debugf("Exiting deleting security groups")
795+
logger.Debugf("Deleting security groups (%s)", filter)
796+
defer logger.Debugf("Exiting deleting security groups (%s)", filter)
776797

777798
ec2Client := getEC2Client(session)
778799
describeSecurityGroupsInput := ec2.DescribeSecurityGroupsInput{}
@@ -830,8 +851,8 @@ func detachInternetGateways(gw *ec2.InternetGateway, ec2Client *ec2.EC2, logger
830851

831852
// deleteInternetGateways will attemp to delete any Internet Gateways matching the given filter
832853
func deleteInternetGateways(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
833-
logger.Debugf("Deleting internet gateways")
834-
defer logger.Debugf("Exiting deleting internet gateways")
854+
logger.Debugf("Deleting internet gateways (%s)", filter)
855+
defer logger.Debugf("Exiting deleting internet gateways (%s)", filter)
835856

836857
ec2Client := getEC2Client(session)
837858

@@ -921,8 +942,8 @@ func deleteRoutesFromTable(rt *ec2.RouteTable, ec2Client *ec2.EC2, logger log.Fi
921942

922943
// deleteSubnets will attempt to delete all Subnets matching the given filter
923944
func deleteSubnets(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
924-
logger.Debug("Deleting subnets")
925-
defer logger.Debug("Exiting deleting subnets")
945+
logger.Debugf("Deleting subnets (%s)", filter)
946+
defer logger.Debugf("Exiting deleting subnets (%s)", filter)
926947

927948
ec2Client := getEC2Client(session)
928949

@@ -1021,8 +1042,8 @@ func filterObjects(awsObjects []awsObjectWithTags, filters AWSFilter) []awsObjec
10211042

10221043
// deleteS3Buckets will attempt to delete (and empty) any S3 bucket matching the provided filter
10231044
func deleteS3Buckets(session *session.Session, filter AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
1024-
logger.Debugf("Deleting S3 buckets")
1025-
defer logger.Debugf("Exiting deleting buckets")
1045+
logger.Debugf("Deleting S3 buckets (%s)", filter)
1046+
defer logger.Debugf("Exiting deleting buckets (%s)", filter)
10261047

10271048
s3Client := s3.New(session)
10281049

@@ -1244,8 +1265,8 @@ func emptyAndDeleteRoute53Zone(zoneID string, r53Client *route53.Route53, logger
12441265
// deleteRoute53 will attempt to delete any route53 zone matching the given filter.
12451266
// it will also attempt to delete any entries in the shared/public route53 zone
12461267
func deleteRoute53(session *session.Session, filters AWSFilter, clusterName string, logger log.FieldLogger) (bool, error) {
1247-
logger.Debugf("Deleting Route53 zones")
1248-
defer logger.Debugf("Exiting deleting Route53 zones")
1268+
logger.Debugf("Deleting Route53 zones (%s)", filters)
1269+
defer logger.Debugf("Exiting deleting Route53 zones (%s)", filters)
12491270

12501271
r53Client := route53.New(session)
12511272

0 commit comments

Comments
 (0)