github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/imagepublishers/amipublisher/expire.go (about)

     1  package amipublisher
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/Cloud-Foundations/Dominator/lib/awsutil"
     7  	"github.com/Cloud-Foundations/Dominator/lib/log"
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/service/ec2"
    10  )
    11  
    12  func expireResources(targets awsutil.TargetList, skipList awsutil.TargetList,
    13  	logger log.Logger) error {
    14  	currentTime := time.Now() // Need a common "now" time.
    15  	waitChannel := make(chan struct{})
    16  	numTargets, err := awsutil.ForEachTarget(targets, skipList,
    17  		func(awsService *ec2.EC2, account, region string, logger log.Logger) {
    18  			expireRegionResources(awsService, currentTime, logger)
    19  			waitChannel <- struct{}{}
    20  		},
    21  		logger)
    22  	for i := 0; i < numTargets; i++ {
    23  		<-waitChannel
    24  	}
    25  	return err
    26  }
    27  
    28  func expireRegionResources(awsService *ec2.EC2, currentTime time.Time,
    29  	logger log.Logger) {
    30  	images, err := awsService.DescribeImages(&ec2.DescribeImagesInput{
    31  		Filters: []*ec2.Filter{
    32  			{
    33  				Name:   aws.String("tag-key"),
    34  				Values: aws.StringSlice([]string{"ExpiresAt"}),
    35  			},
    36  			{
    37  				Name:   aws.String("is-public"),
    38  				Values: aws.StringSlice([]string{"false"}),
    39  			},
    40  		},
    41  	})
    42  	if err == nil {
    43  		for _, image := range images.Images {
    44  			expireImage(awsService, image, currentTime, logger)
    45  		}
    46  	}
    47  	filters := []*ec2.Filter{
    48  		{
    49  			Name:   aws.String("tag-key"),
    50  			Values: aws.StringSlice([]string{"ExpiresAt"}),
    51  		},
    52  	}
    53  	instances, err := awsService.DescribeInstances(&ec2.DescribeInstancesInput{
    54  		Filters: filters,
    55  	})
    56  	if err == nil {
    57  		for _, reservation := range instances.Reservations {
    58  			for _, instance := range reservation.Instances {
    59  				expireInstance(awsService, instance, currentTime, logger)
    60  			}
    61  		}
    62  	}
    63  	snapshots, err := awsService.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
    64  		Filters: filters,
    65  	})
    66  	if err == nil {
    67  		for _, snapshot := range snapshots.Snapshots {
    68  			expireSnapshot(awsService, snapshot, currentTime, logger)
    69  		}
    70  	}
    71  	volumes, err := awsService.DescribeVolumes(&ec2.DescribeVolumesInput{
    72  		Filters: filters,
    73  	})
    74  	if err == nil {
    75  		for _, volume := range volumes.Volumes {
    76  			expireVolume(awsService, volume, currentTime, logger)
    77  		}
    78  	}
    79  }
    80  
    81  func expireImage(awsService *ec2.EC2, image *ec2.Image, currentTime time.Time,
    82  	logger log.Logger) {
    83  	if hasExpired(image.Tags, currentTime) {
    84  		imageId := aws.StringValue(image.ImageId)
    85  		if err := deregisterAmi(awsService, imageId); err != nil {
    86  			logger.Printf("error deleting: %s: %s\n", imageId, err)
    87  		} else {
    88  			logger.Printf("deleted: %s\n", imageId)
    89  		}
    90  	}
    91  }
    92  
    93  func expireInstance(awsService *ec2.EC2, instance *ec2.Instance,
    94  	currentTime time.Time, logger log.Logger) {
    95  	if aws.StringValue(instance.State.Name) == ec2.InstanceStateNameTerminated {
    96  		return
    97  	}
    98  	if hasExpired(instance.Tags, currentTime) {
    99  		instanceId := aws.StringValue(instance.InstanceId)
   100  		if err := libTerminateInstances(awsService, instanceId); err != nil {
   101  			logger.Printf("error terminating: %s: %s\n", instanceId, err)
   102  		} else {
   103  			logger.Printf("terminated: %s\n", instanceId)
   104  		}
   105  	}
   106  }
   107  
   108  func expireSnapshot(awsService *ec2.EC2, snapshot *ec2.Snapshot,
   109  	currentTime time.Time, logger log.Logger) {
   110  	if hasExpired(snapshot.Tags, currentTime) {
   111  		snapshotId := aws.StringValue(snapshot.SnapshotId)
   112  		if err := deleteSnapshot(awsService, snapshotId); err != nil {
   113  			logger.Printf("error deleting: %s: %s\n", snapshotId, err)
   114  		} else {
   115  			logger.Printf("deleted: %s\n", snapshotId)
   116  		}
   117  	}
   118  }
   119  
   120  func expireVolume(awsService *ec2.EC2, volume *ec2.Volume,
   121  	currentTime time.Time, logger log.Logger) {
   122  	if hasExpired(volume.Tags, currentTime) {
   123  		volumeId := aws.StringValue(volume.VolumeId)
   124  		if err := deleteVolume(awsService, volumeId); err != nil {
   125  			logger.Printf("error deleting: %s: %s\n", volumeId, err)
   126  		} else {
   127  			logger.Printf("deleted: %s\n", volumeId)
   128  		}
   129  	}
   130  }
   131  
   132  func hasExpired(tags []*ec2.Tag, currentTime time.Time) bool {
   133  	for _, tag := range tags {
   134  		if *tag.Key != "ExpiresAt" {
   135  			continue
   136  		}
   137  		expirationTime, err := time.Parse(ExpiresAtFormat, *tag.Value)
   138  		if err != nil {
   139  			continue
   140  		}
   141  		return currentTime.After(expirationTime)
   142  	}
   143  	return false
   144  }