diff --git a/src/main/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/NoGeneratedAMIRule.java b/src/main/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/NoGeneratedAMIRule.java index 711331ad..af53f9c3 100644 --- a/src/main/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/NoGeneratedAMIRule.java +++ b/src/main/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/NoGeneratedAMIRule.java @@ -44,6 +44,8 @@ public class NoGeneratedAMIRule implements Rule { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(NoGeneratedAMIRule.class); + private String ownerEmailOverride = null; + private static final String TERMINATION_REASON = "No AMI is generated for this snapshot"; private final MonkeyCalendar calendar; @@ -67,14 +69,33 @@ public class NoGeneratedAMIRule implements Rule { * as cleanup candidate */ public NoGeneratedAMIRule(MonkeyCalendar calendar, int ageThreshold, int retentionDays) { + this(calendar, ageThreshold, retentionDays, null); + } + + /** + * Constructor. + * + * @param calendar + * The calendar used to calculate the termination time + * @param ageThreshold + * The number of days that a snapshot is considered as cleanup candidate since it is created + * @param retentionDays + * The number of days that the volume is retained before being terminated after being marked + * as cleanup candidate + * @param ownerEmailOverride + * If null, send notifications to the resource owner. + * If not null, send notifications to the provided owner email address instead of the resource owner. + */ + public NoGeneratedAMIRule(MonkeyCalendar calendar, int ageThreshold, int retentionDays, String ownerEmailOverride) { Validate.notNull(calendar); Validate.isTrue(ageThreshold >= 0); Validate.isTrue(retentionDays >= 0); this.calendar = calendar; this.ageThreshold = ageThreshold; this.retentionDays = retentionDays; + this.ownerEmailOverride = ownerEmailOverride; } - + @Override public boolean isValid(Resource resource) { Validate.notNull(resource); @@ -96,6 +117,9 @@ public boolean isValid(Resource resource) { Date userSpecifiedDate = new Date(TERMINATION_DATE_FORMATTER.parseDateTime(janitorTag).getMillis()); resource.setExpectedTerminationTime(userSpecifiedDate); resource.setTerminationReason(String.format("User specified termination date %s", janitorTag)); + if (ownerEmailOverride != null) { + resource.setOwnerEmail(ownerEmailOverride); + } return false; } catch (Exception e) { LOGGER.error(String.format("The janitor tag is not a user specified date: %s", janitorTag)); @@ -113,6 +137,9 @@ public boolean isValid(Resource resource) { DateTime launchTime = new DateTime(resource.getLaunchTime().getTime()); DateTime now = new DateTime(calendar.now().getTimeInMillis()); if (launchTime.plusDays(ageThreshold).isBefore(now)) { + if (ownerEmailOverride != null) { + resource.setOwnerEmail(ownerEmailOverride); + } if (resource.getExpectedTerminationTime() == null) { Date terminationTime = calendar.getBusinessDay(new Date(now.getMillis()), retentionDays); resource.setExpectedTerminationTime(terminationTime); diff --git a/src/main/java/com/netflix/simianarmy/basic/janitor/BasicJanitorMonkeyContext.java b/src/main/java/com/netflix/simianarmy/basic/janitor/BasicJanitorMonkeyContext.java index f9595347..540ee517 100644 --- a/src/main/java/com/netflix/simianarmy/basic/janitor/BasicJanitorMonkeyContext.java +++ b/src/main/java/com/netflix/simianarmy/basic/janitor/BasicJanitorMonkeyContext.java @@ -293,7 +293,9 @@ private EBSSnapshotJanitor getEBSSnapshotJanitor() { ruleEngine.addRule(new NoGeneratedAMIRule(monkeyCalendar, (int) configuration().getNumOrElse("simianarmy.janitor.rule.noGeneratedAMIRule.ageThreshold", 30), (int) configuration().getNumOrElse( - "simianarmy.janitor.rule.noGeneratedAMIRule.retentionDays", 7))); + "simianarmy.janitor.rule.noGeneratedAMIRule.retentionDays", 7), + configuration().getStrOrElse( + "simianarmy.janitor.rule.noGeneratedAMIRule.ownerEmail", null))); } if (configuration().getBoolOrElse("simianarmy.janitor.rule.untaggedRule.enabled", false)) { ruleEngine.addRule(new UntaggedRule(monkeyCalendar, getPropertySet("simianarmy.janitor.rule.untaggedRule.requiredTags"), diff --git a/src/test/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/TestNoGeneratedAMIRule.java b/src/test/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/TestNoGeneratedAMIRule.java index fe260030..ab7c1949 100644 --- a/src/test/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/TestNoGeneratedAMIRule.java +++ b/src/test/java/com/netflix/simianarmy/aws/janitor/rule/snapshot/TestNoGeneratedAMIRule.java @@ -162,6 +162,36 @@ public void testResourceWithExpectedTerminationTimeSet() { Assert.assertEquals(oldTermReason, resource.getTerminationReason()); } + @Test + public void testOldSnapshotWithoutAMIWithOwnerOverride() { + int ageThreshold = 5; + DateTime now = DateTime.now(); + Resource resource = new AWSResource().withId("snap123").withOwnerEmail("owner@netflix.com").withResourceType(AWSResourceType.EBS_SNAPSHOT) + .withLaunchTime(new Date(now.minusDays(ageThreshold + 1).getMillis())); + ((AWSResource) resource).setAWSResourceState("completed"); + int retentionDays = 4; + NoGeneratedAMIRule rule = new NoGeneratedAMIRule(new TestMonkeyCalendar(), + ageThreshold, retentionDays, "new_owner@netflix.com"); + Assert.assertFalse(rule.isValid(resource)); + Assert.assertEquals(resource.getOwnerEmail(), "new_owner@netflix.com"); + TestUtils.verifyTerminationTimeRough(resource, retentionDays, now); + } + + @Test + public void testOldSnapshotWithoutAMIWithoutOwnerOverride() { + int ageThreshold = 5; + DateTime now = DateTime.now(); + Resource resource = new AWSResource().withId("snap123").withOwnerEmail("owner@netflix.com").withResourceType(AWSResourceType.EBS_SNAPSHOT) + .withLaunchTime(new Date(now.minusDays(ageThreshold + 1).getMillis())); + ((AWSResource) resource).setAWSResourceState("completed"); + int retentionDays = 4; + NoGeneratedAMIRule rule = new NoGeneratedAMIRule(new TestMonkeyCalendar(), + ageThreshold, retentionDays); + Assert.assertFalse(rule.isValid(resource)); + Assert.assertEquals(resource.getOwnerEmail(), "owner@netflix.com"); + TestUtils.verifyTerminationTimeRough(resource, retentionDays, now); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testNullResource() { NoGeneratedAMIRule rule = new NoGeneratedAMIRule(new TestMonkeyCalendar(), 5, 4);