Skip to content

Commit 9a2fcf4

Browse files
committed
Merge pull request Netflix#236 from atlassian/ASGTagFiltering
Apply the ASG Tag filtering on ASGCrawler
2 parents 06a2af5 + 9b49c9f commit 9a2fcf4

14 files changed

+279
-24
lines changed

src/main/java/com/netflix/simianarmy/basic/BasicChaosMonkeyContext.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.netflix.simianarmy.chaos.ChaosInstanceSelector;
2929
import com.netflix.simianarmy.chaos.ChaosMonkey;
3030
import com.netflix.simianarmy.client.aws.chaos.ASGChaosCrawler;
31+
import com.netflix.simianarmy.client.aws.chaos.FilteringChaosCrawler;
32+
import com.netflix.simianarmy.client.aws.chaos.TagPredicate;
3133

3234
/**
3335
* The Class BasicContext. This provide the basic context needed for the Chaos Monkey to run. It will configure
@@ -50,9 +52,13 @@ public class BasicChaosMonkeyContext extends BasicSimianArmyContext implements C
5052
*/
5153
public BasicChaosMonkeyContext() {
5254
super("simianarmy.properties", "client.properties", "chaos.properties");
53-
setChaosCrawler(new ASGChaosCrawler(awsClient()));
54-
setChaosInstanceSelector(new BasicChaosInstanceSelector());
5555
MonkeyConfiguration cfg = configuration();
56+
String tagKey = cfg.getStrOrElse("simianarmy.chaos.ASGtag.key", "");
57+
String tagValue = cfg.getStrOrElse("simianarmy.chaos.ASGtag.value", "");
58+
59+
ASGChaosCrawler chaosCrawler = new ASGChaosCrawler(awsClient());
60+
setChaosCrawler(tagKey.isEmpty() ? chaosCrawler : new FilteringChaosCrawler(chaosCrawler, new TagPredicate(tagKey, tagValue)));
61+
setChaosInstanceSelector(new BasicChaosInstanceSelector());
5662
AmazonSimpleEmailServiceClient sesClient = new AmazonSimpleEmailServiceClient(awsClientConfig);
5763
if (configuration().getStr("simianarmy.aws.email.region") != null) {
5864
sesClient.setRegion(Region.getRegion(Regions.fromName(configuration().getStr("simianarmy.aws.email.region"))));

src/main/java/com/netflix/simianarmy/basic/chaos/BasicInstanceGroup.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.LinkedList;
2222
import java.util.List;
2323

24+
import com.amazonaws.services.autoscaling.model.TagDescription;
2425
import com.netflix.simianarmy.GroupType;
2526
import com.netflix.simianarmy.chaos.ChaosCrawler.InstanceGroup;
2627

@@ -38,20 +39,28 @@ public class BasicInstanceGroup implements InstanceGroup {
3839
/** The region. */
3940
private final String region;
4041

42+
/** list of the tags of the ASG */
43+
private final List<TagDescription> tags;
44+
4145
/**
4246
* Instantiates a new basic instance group.
4347
*
4448
* @param name
4549
* the name
4650
* @param type
4751
* the type
52+
* @param tags
53+
* the ASG tags
4854
*/
49-
public BasicInstanceGroup(String name, GroupType type, String region) {
55+
public BasicInstanceGroup(String name, GroupType type, String region, List<TagDescription> tags) {
5056
this.name = name;
5157
this.type = type;
5258
this.region = region;
59+
this.tags = tags;
5360
}
5461

62+
63+
5564
/** {@inheritDoc} */
5665
public GroupType type() {
5766
return type;
@@ -67,6 +76,11 @@ public String region() {
6776
return region;
6877
}
6978

79+
/** {@inheritDoc} */
80+
public List<TagDescription> tags() {
81+
return tags;
82+
}
83+
7084
/** The list. */
7185
private List<String> list = new LinkedList<String>();
7286

@@ -85,7 +99,7 @@ public void addInstance(String instance) {
8599
/** {@inheritDoc} */
86100
@Override
87101
public BasicInstanceGroup copyAs(String newName) {
88-
BasicInstanceGroup newGroup = new BasicInstanceGroup(newName, this.type(), this.region());
102+
BasicInstanceGroup newGroup = new BasicInstanceGroup(newName, this.type(), this.region(), this.tags());
89103
for (String instance: this.instances()) {
90104
newGroup.addInstance(instance);
91105
}

src/main/java/com/netflix/simianarmy/chaos/ChaosCrawler.java

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.EnumSet;
2121
import java.util.List;
2222

23+
import com.amazonaws.services.autoscaling.model.TagDescription;
2324
import com.netflix.simianarmy.GroupType;
2425

2526
/**
@@ -53,6 +54,13 @@ public interface InstanceGroup {
5354
*/
5455
String region();
5556

57+
/**
58+
* Tags.
59+
*
60+
* @return the list of tags associated with group type
61+
*/
62+
List<TagDescription> tags();
63+
5664
/**
5765
* Instances.
5866
*

src/main/java/com/netflix/simianarmy/client/aws/chaos/ASGChaosCrawler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ protected InstanceGroup getInstanceGroup(AutoScalingGroup asg, double aggression
108108

109109
// if coefficient is 1 then the BasicInstanceGroup is fine, otherwise use Tunable
110110
if (aggressionCoefficient == 1.0) {
111-
instanceGroup = new BasicInstanceGroup(asg.getAutoScalingGroupName(), Types.ASG, awsClient.region());
111+
instanceGroup = new BasicInstanceGroup(asg.getAutoScalingGroupName(), Types.ASG, awsClient.region(), asg.getTags());
112112
} else {
113-
TunableInstanceGroup tunable = new TunableInstanceGroup(asg.getAutoScalingGroupName(), Types.ASG, awsClient.region());
113+
TunableInstanceGroup tunable = new TunableInstanceGroup(asg.getAutoScalingGroupName(), Types.ASG, awsClient.region(), asg.getTags());
114114
tunable.setAggressionCoefficient(aggressionCoefficient);
115115

116116
instanceGroup = tunable;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
*
3+
* Copyright 2012 Netflix, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
package com.netflix.simianarmy.client.aws.chaos;
19+
20+
import com.google.common.base.Predicate;
21+
import com.google.common.collect.Iterables;
22+
import com.google.common.collect.Lists;
23+
import com.netflix.simianarmy.chaos.ChaosCrawler;
24+
25+
import java.util.EnumSet;
26+
import java.util.List;
27+
28+
/**
29+
* The Class FilteringChaosCrawler. This will filter the result from ASGChaosCrawler for all available AutoScalingGroups associated with the AWS account based on requested filter.
30+
*/
31+
public class FilteringChaosCrawler implements ChaosCrawler {
32+
33+
private final ChaosCrawler crawler;
34+
private final Predicate<? super InstanceGroup> predicate;
35+
36+
public FilteringChaosCrawler(ChaosCrawler crawler, Predicate<? super InstanceGroup> predicate) {
37+
this.crawler = crawler;
38+
this.predicate = predicate;
39+
}
40+
41+
/** {@inheritDoc} */
42+
@Override
43+
public EnumSet<?> groupTypes() {
44+
return crawler.groupTypes();
45+
}
46+
47+
/** {@inheritDoc} */
48+
@Override
49+
public List<InstanceGroup> groups() {
50+
return filter(crawler.groups());
51+
}
52+
53+
/** {@inheritDoc} */
54+
@Override
55+
public List<InstanceGroup> groups(String... names) {
56+
return filter(crawler.groups(names));
57+
}
58+
59+
60+
/**
61+
* Return the filtered list of InstanceGroups using the requested predicate. The filter is applied on the InstanceGroup retrieved from the ASGChaosCrawler class.
62+
* @param list list of InstanceGroups result of the chaos crawler
63+
* @return The appropriate {@link InstanceGroup}
64+
*/
65+
protected List<InstanceGroup> filter(List<InstanceGroup> list) {
66+
return Lists.newArrayList(Iterables.filter(list, predicate));
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
*
3+
* Copyright 2012 Netflix, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
package com.netflix.simianarmy.client.aws.chaos;
19+
20+
import com.amazonaws.services.autoscaling.model.TagDescription;
21+
import com.google.common.base.Predicate;
22+
import com.google.common.collect.Iterables;
23+
import com.netflix.simianarmy.chaos.ChaosCrawler;
24+
25+
/**
26+
* * The Class TagPredicate. This will apply the tag-key and the tag-value filter on the list of InstanceGroups .
27+
*/
28+
public class TagPredicate implements Predicate<ChaosCrawler.InstanceGroup> {
29+
30+
private final String key, value;
31+
32+
public TagPredicate(String key, String value) {
33+
this.key = key;
34+
this.value = value;
35+
}
36+
37+
@Override
38+
public boolean apply(ChaosCrawler.InstanceGroup instanceGroup) {
39+
return Iterables.any(instanceGroup.tags(), new com.google.common.base.Predicate<TagDescription>() {
40+
@Override
41+
public boolean apply(TagDescription tagDescription) {
42+
return tagDescription.getKey().equals(key) && tagDescription.getValue().equals(value);
43+
}
44+
});
45+
}
46+
}

src/main/java/com/netflix/simianarmy/tunable/TunableInstanceGroup.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.netflix.simianarmy.tunable;
22

3+
import com.amazonaws.services.autoscaling.model.TagDescription;
34
import com.netflix.simianarmy.GroupType;
45
import com.netflix.simianarmy.basic.chaos.BasicInstanceGroup;
56

7+
import java.util.List;
8+
69
/**
710
* Allows for individual InstanceGroups to alter the aggressiveness
811
* of ChaosMonkey.
@@ -12,8 +15,8 @@
1215
*/
1316
public class TunableInstanceGroup extends BasicInstanceGroup {
1417

15-
public TunableInstanceGroup(String name, GroupType type, String region) {
16-
super(name, type, region);
18+
public TunableInstanceGroup(String name, GroupType type, String region, List<TagDescription> tags) {
19+
super(name, type, region, tags);
1720
}
1821

1922
private double aggressionCoefficient = 1.0;

src/main/resources/chaos.properties

+3
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,6 @@ simianarmy.chaos.mandatoryTermination.defaultProbability = 0.5
9898

9999
# Enable the email subject to be the same as the body, to include terminated instance and group information
100100
#simianarmy.chaos.notification.subject.isBody = true
101+
#set the tag filter on the ASGs to terminate only instances from the ASG with the this tag key and value
102+
#simianarmy.chaos.ASGtag.key = chaos_monkey
103+
#simianarmy.chaos.ASGtag.value = true

src/test/java/com/netflix/simianarmy/basic/chaos/TestBasicChaosInstanceSelector.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@
1818
// CHECKSTYLE IGNORE Javadoc
1919
package com.netflix.simianarmy.basic.chaos;
2020

21-
import java.util.Collection;
22-
import java.util.List;
23-
import java.util.Arrays;
24-
import java.util.Map;
25-
import java.util.HashMap;
21+
import java.util.*;
2622

23+
import com.amazonaws.services.autoscaling.model.TagDescription;
2724
import com.netflix.simianarmy.GroupType;
2825
import com.netflix.simianarmy.chaos.ChaosInstanceSelector;
2926
import com.netflix.simianarmy.chaos.ChaosCrawler.InstanceGroup;
@@ -61,6 +58,10 @@ public String region() {
6158
return "region";
6259
}
6360

61+
public List<TagDescription> tags() {
62+
return Collections.<TagDescription>emptyList();
63+
}
64+
6465
public List<String> instances() {
6566
return Arrays.asList("i-123456789012345670", "i-123456789012345671", "i-123456789012345672", "i-123456789012345673", "i-123456789012345674",
6667
"i-123456789012345675", "i-123456789012345676", "i-123456789012345677", "i-123456789012345678", "i-123456789012345679");

src/test/java/com/netflix/simianarmy/basic/chaos/TestBasicChaosMonkey.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
// CHECKSTYLE IGNORE Javadoc
1919
package com.netflix.simianarmy.basic.chaos;
2020

21+
import java.util.ArrayList;
22+
import java.util.Collections;
2123
import java.util.List;
2224
import java.util.concurrent.TimeUnit;
2325

@@ -33,6 +35,7 @@
3335
import com.netflix.simianarmy.chaos.ChaosMonkey;
3436
import com.netflix.simianarmy.chaos.TestChaosMonkeyContext;
3537
import com.netflix.simianarmy.resources.chaos.ChaosMonkeyResource;
38+
import com.amazonaws.services.autoscaling.model.TagDescription;
3639

3740
// CHECKSTYLE IGNORE MagicNumberCheck
3841
public class TestBasicChaosMonkey {
@@ -360,17 +363,18 @@ public void testMaxTerminationCountPerDayGroupLevel() {
360363

361364
@Test
362365
public void testGetValueFromCfgWithDefault() {
366+
363367
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("propertiesWithDefaults.properties");
364368
BasicChaosMonkey chaos = new BasicChaosMonkey(ctx);
365369

366370
// named 1 has actual values in config
367-
InstanceGroup named1 = new BasicInstanceGroup("named1", GroupTypes.TYPE_A, "test-dev-1");
371+
InstanceGroup named1 = new BasicInstanceGroup("named1", GroupTypes.TYPE_A, "test-dev-1", Collections.<TagDescription>emptyList());
368372

369373
// named 2 doesn't have values but it's group has values
370-
InstanceGroup named2 = new BasicInstanceGroup("named2", GroupTypes.TYPE_A, "test-dev-1");
374+
InstanceGroup named2 = new BasicInstanceGroup("named2", GroupTypes.TYPE_A, "test-dev-1", Collections.<TagDescription>emptyList());
371375

372376
// named 3 doesn't have values and it's group doesn't have values
373-
InstanceGroup named3 = new BasicInstanceGroup("named3", GroupTypes.TYPE_B, "test-dev-1");
377+
InstanceGroup named3 = new BasicInstanceGroup("named3", GroupTypes.TYPE_B, "test-dev-1", Collections.<TagDescription>emptyList());
374378

375379
Assert.assertEquals(chaos.getBoolFromCfgOrDefault(named1, "enabled", true), false);
376380
Assert.assertEquals(chaos.getNumFromCfgOrDefault(named1, "probability", 3.0), 1.1);

src/test/java/com/netflix/simianarmy/basic/chaos/TestCloudFormationChaosMonkey.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// CHECKSTYLE IGNORE Javadoc
1919
package com.netflix.simianarmy.basic.chaos;
2020

21+
import com.amazonaws.services.autoscaling.model.TagDescription;
2122
import org.testng.Assert;
2223
import org.testng.annotations.Test;
2324
import static org.testng.Assert.assertEquals;
@@ -27,6 +28,8 @@
2728
import com.netflix.simianarmy.chaos.TestChaosMonkeyContext;
2829
import com.netflix.simianarmy.chaos.ChaosCrawler.InstanceGroup;
2930

31+
import java.util.Collections;
32+
3033
public class TestCloudFormationChaosMonkey {
3134

3235
public static final long EXPECTED_MILLISECONDS = 2000;
@@ -36,9 +39,9 @@ public void testIsGroupEnabled() {
3639
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("cloudformation.properties");
3740
CloudFormationChaosMonkey chaos = new CloudFormationChaosMonkey(ctx);
3841
InstanceGroup group1 = new BasicInstanceGroup("new-group-TestGroup1-XCFNFNFNF",
39-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
42+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
4043
InstanceGroup group2 = new BasicInstanceGroup("new-group-TestGroup2-XCFNGHFNF",
41-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
44+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
4245
assertTrue(chaos.isGroupEnabled(group1));
4346
assertFalse(chaos.isGroupEnabled(group2));
4447
}
@@ -48,7 +51,7 @@ public void testIsMaxTerminationCountExceeded() {
4851
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("cloudformation.properties");
4952
CloudFormationChaosMonkey chaos = new CloudFormationChaosMonkey(ctx);
5053
InstanceGroup group1 = new BasicInstanceGroup("new-group-TestGroup1-XCFNFNFNF",
51-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
54+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
5255
assertFalse(chaos.isMaxTerminationCountExceeded(group1));
5356
}
5457

@@ -57,7 +60,7 @@ public void testGetEffectiveProbability() {
5760
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("cloudformation.properties");
5861
CloudFormationChaosMonkey chaos = new CloudFormationChaosMonkey(ctx);
5962
InstanceGroup group1 = new BasicInstanceGroup("new-group-TestGroup1-XCFNFNFNF",
60-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
63+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
6164
assertEquals(1.0, chaos.getEffectiveProbability(group1));
6265
}
6366

@@ -66,7 +69,7 @@ public void testNoSuffixInstanceGroup() {
6669
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("disabled.properties");
6770
CloudFormationChaosMonkey chaos = new CloudFormationChaosMonkey(ctx);
6871
InstanceGroup group = new BasicInstanceGroup("new-group-TestGroup-XCFNFNFNF",
69-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
72+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
7073
InstanceGroup newGroup = chaos.noSuffixInstanceGroup(group);
7174
assertEquals(newGroup.name(), "new-group-TestGroup");
7275
}
@@ -76,7 +79,7 @@ public void testGetLastOptInMilliseconds() {
7679
TestChaosMonkeyContext ctx = new TestChaosMonkeyContext("cloudformation.properties");
7780
CloudFormationChaosMonkey chaos = new CloudFormationChaosMonkey(ctx);
7881
InstanceGroup group = new BasicInstanceGroup("new-group-TestGroup1-XCFNFNFNF",
79-
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region");
82+
TestChaosMonkeyContext.CrawlerTypes.TYPE_D, "region", Collections.<TagDescription>emptyList());
8083
assertEquals(chaos.getLastOptInMilliseconds(group), EXPECTED_MILLISECONDS);
8184
}
8285

0 commit comments

Comments
 (0)