这是indexloc提供的服务,不要输入任何密码
Skip to content
This repository was archived by the owner on Mar 4, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5f50099
Add chaos strategy that detaches all EBS volumes
justinsb Sep 13, 2013
bb71d00
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
1aac368
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
39eacc1
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
56003a7
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
b47ca83
Add DetachVolumes to strategy list
justinsb Sep 13, 2013
63a2233
Fix Checkstyle issue
justinsb Sep 13, 2013
7ac9c69
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
872075d
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
f3a72cb
Exclude root EBS volume in the chaos strategy
justinsb Sep 13, 2013
1f1855d
Log if detach volumes wasn't able to run
justinsb Sep 13, 2013
6cb18cf
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
ba9861b
Enable detachvolumes in default configuration
justinsb Sep 13, 2013
86f4652
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
09590bc
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
683c991
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
01218c6
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
b5fccc9
Fix checkstyle
justinsb Sep 14, 2013
5b4f3e6
Make code uglier to keep PMD happy
justinsb Sep 14, 2013
e0109da
Avoid hitting unsupported methods in the mocks
justinsb Sep 14, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/main/java/com/netflix/simianarmy/CloudClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.netflix.simianarmy;

import java.util.List;
import java.util.Map;


Expand Down Expand Up @@ -88,4 +89,33 @@ public interface CloudClient {
*/
void createTagsForResources(Map<String, String> keyValueMap, String... resourceIds);

/**
* Lists all EBS volumes attached to the specified instance.
*
* @param instanceId
* the instance id
* @param includeRoot
* if the root volume is on EBS, should we include it?
*
* @throws NotFoundException
* if the instance no longer exists or was already terminated after the crawler discovered it then you
* should get a NotFoundException
*/
List<String> listAttachedVolumes(String instanceId, boolean includeRoot);

/**
* Detaches an EBS volumes from the specified instance.
*
* @param instanceId
* the instance id
* @param volumeId
* the volume id
* @param force
* if we should force-detach the volume. Probably best not to use on high-value volumes.
*
* @throws NotFoundException
* if the instance no longer exists or was already terminated after the crawler discovered it then you
* should get a NotFoundException
*/
void detachVolume(String instanceId, String volumeId, boolean force);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import com.netflix.simianarmy.chaos.ChaosEmailNotifier;
import com.netflix.simianarmy.chaos.ChaosMonkey;
import com.netflix.simianarmy.chaos.ChaosType;
import com.netflix.simianarmy.chaos.DetachVolumesChaosType;
import com.netflix.simianarmy.chaos.ShutdownInstanceChaosType;

/**
Expand Down Expand Up @@ -90,6 +91,7 @@ public BasicChaosMonkey(ChaosMonkey.Context ctx) {

allChaosTypes = Lists.newArrayList();
allChaosTypes.add(new ShutdownInstanceChaosType(cfg));
allChaosTypes.add(new DetachVolumesChaosType(cfg));

TimeUnit freqUnit = ctx.scheduler().frequencyUnit();
long units = freqUnit.convert(close.getTimeInMillis() - open.getTimeInMillis(), TimeUnit.MILLISECONDS);
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/com/netflix/simianarmy/chaos/ChaosType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public abstract class ChaosType {
private static final Logger LOGGER = LoggerFactory.getLogger(ChaosType.class);

/**
* Configuration for this chaos type
* Configuration for this chaos type.
*/
private final MonkeyConfiguration config;

Expand Down Expand Up @@ -77,6 +77,13 @@ public String getKey() {
* detach.
*/
public boolean canApply(CloudClient cloudClient, String instanceId) {
return isEnabled();
}

/**
* Returns whether we are enabled.
*/
public boolean isEnabled() {
return enabled;
}

Expand All @@ -94,6 +101,7 @@ public static ChaosType parse(List<ChaosType> all, String chaosTypeName) {
return chaosType;
}
}
throw new IllegalArgumentException("Unknown chaos type: " + chaosTypeName);
throw new IllegalArgumentException("Unknown chaos type value: "
+ chaosTypeName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.netflix.simianarmy.chaos;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflix.simianarmy.CloudClient;
import com.netflix.simianarmy.MonkeyConfiguration;
import com.netflix.simianarmy.basic.chaos.BasicChaosMonkey;

/**
* We force-detach all the EBS volumes.
*
* This is supposed to simulate a catastrophic failure of EBS, however the instance will (possibly) still keep running;
* e.g. it should continue to respond to pings.
*/
public class DetachVolumesChaosType extends ChaosType {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(BasicChaosMonkey.class);

/**
* Constructor.
*
* @param config
* Configuration to use
*/
public DetachVolumesChaosType(MonkeyConfiguration config) {
super(config, "DetachVolumes");
}

/**
* Strategy can be applied iff there are any EBS volumes attached.
*/
@Override
public boolean canApply(CloudClient cloudClient, String instanceId) {
// The test mocks don't implement listAttachedVolumes
// so check for enabled first
if (!isEnabled()) {
return false;
}

List<String> volumes = cloudClient.listAttachedVolumes(instanceId, false);
if (volumes.isEmpty()) {
LOGGER.debug("Can't apply strategy: no non-root EBS volumes");
return false;
}

return super.canApply(cloudClient, instanceId);
}

/**
* Force-detaches all attached EBS volumes from the instance.
*/
@Override
public void apply(CloudClient cloudClient, String instanceId) {
// IDEA: We could have a strategy where we detach some of the volumes...
boolean force = true;
for (String volumeId : cloudClient.listAttachedVolumes(instanceId, false)) {
cloudClient.detachVolume(instanceId, volumeId, force);
}
}

}
60 changes: 60 additions & 0 deletions src/main/java/com/netflix/simianarmy/client/aws/AWSClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@
import com.amazonaws.services.ec2.model.DescribeSnapshotsResult;
import com.amazonaws.services.ec2.model.DescribeVolumesRequest;
import com.amazonaws.services.ec2.model.DescribeVolumesResult;
import com.amazonaws.services.ec2.model.DetachVolumeRequest;
import com.amazonaws.services.ec2.model.EbsInstanceBlockDevice;
import com.amazonaws.services.ec2.model.Image;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceBlockDeviceMapping;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.Snapshot;
import com.amazonaws.services.ec2.model.Tag;
Expand All @@ -58,8 +61,10 @@
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription;
import com.amazonaws.services.simpledb.AmazonSimpleDB;
import com.amazonaws.services.simpledb.AmazonSimpleDBClient;
import com.google.common.base.Strings;
import com.netflix.simianarmy.CloudClient;
import com.netflix.simianarmy.NotFoundException;

import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -531,4 +536,59 @@ public List<Image> describeImages(String... imageIds) {
LOGGER.info(String.format("Got %d AMIs in region %s.", images.size(), region));
return images;
}


@Override
public void detachVolume(String instanceId, String volumeId, boolean force) {
Validate.notEmpty(instanceId);
LOGGER.info(String.format("Detach volumes from instance %s in region %s.", instanceId, region));
try {
DetachVolumeRequest detachVolumeRequest = new DetachVolumeRequest();
detachVolumeRequest.setForce(force);
detachVolumeRequest.setInstanceId(instanceId);
detachVolumeRequest.setVolumeId(volumeId);
ec2Client().detachVolume(detachVolumeRequest);
} catch (AmazonServiceException e) {
if (e.getErrorCode().equals("InvalidInstanceID.NotFound")) {
throw new NotFoundException("AWS instance " + instanceId + " not found", e);
}
throw e;
}
}

@Override
public List<String> listAttachedVolumes(String instanceId, boolean includeRoot) {
Validate.notEmpty(instanceId);
LOGGER.info(String.format("Listing volumes attached to instance %s in region %s.", instanceId, region));
try {
List<String> volumeIds = new ArrayList<String>();
for (Instance instance : describeInstances(instanceId)) {
String rootDeviceName = instance.getRootDeviceName();

for (InstanceBlockDeviceMapping ibdm : instance.getBlockDeviceMappings()) {
EbsInstanceBlockDevice ebs = ibdm.getEbs();
if (ebs == null) {
continue;
}

String volumeId = ebs.getVolumeId();
if (Strings.isNullOrEmpty(volumeId)) {
continue;
}

if (!includeRoot && rootDeviceName != null && rootDeviceName.equals(ibdm.getDeviceName())) {
continue;
}

volumeIds.add(volumeId);
}
}
return volumeIds;
} catch (AmazonServiceException e) {
if (e.getErrorCode().equals("InvalidInstanceID.NotFound")) {
throw new NotFoundException("AWS instance " + instanceId + " not found", e);
}
throw e;
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/chaos.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ simianarmy.chaos.ASG.maxTerminationsPerDay = 1.0

# Strategies
simianarmy.chaos.shutdowninstance.enabled = true
simianarmy.chaos.detachvolumes.enabled = true

# enable a specific ASG
# simianarmy.chaos.ASG.<asgName>.enabled = true
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/netflix/simianarmy/TestMonkeyContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ public void deleteImage(String imageId) {
@Override
public void deleteLaunchConfiguration(String launchConfigName) {
}

@Override
public List<String> listAttachedVolumes(String instanceId, boolean includeRoot) {
throw new UnsupportedOperationException();
}

@Override
public void detachVolume(String instanceId, String volumeId,
boolean force) {
throw new UnsupportedOperationException();
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ public void deleteImage(String imageId) {
@Override
public void deleteLaunchConfiguration(String launchConfigName) {
}

@Override
public List<String> listAttachedVolumes(String instanceId, boolean includeRoot) {
throw new UnsupportedOperationException();
}

@Override
public void detachVolume(String instanceId, String volumeId,
boolean force) {
throw new UnsupportedOperationException();
}
};
}

Expand Down