这是indexloc提供的服务,不要输入任何密码
Skip to content
This repository was archived by the owner on Mar 4, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
42aa4a2
Merge branch 'dont_email_foo_bar_com'
justinsb Sep 13, 2013
5f50099
Add chaos strategy that detaches all EBS volumes
justinsb Sep 13, 2013
db75c28
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
bb71d00
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
877a615
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
c7b8e7e
Merge branch 'auto_create_simpledb_domain'
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
18e3a83
Merge branch 'auto_create_simpledb_domain'
justinsb Sep 13, 2013
38a5d90
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
7ac9c69
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
d560090
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
872075d
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
0d36918
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
f3a72cb
Exclude root EBS volume in the chaos strategy
justinsb Sep 13, 2013
1494b44
Initial commit on jclouds support
justinsb Sep 13, 2013
fa56dc5
A bit more jclouds exposed
justinsb Sep 13, 2013
1b4aae3
Merge branch 'block_network_chaos_type'
justinsb Sep 13, 2013
56a6b73
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
bf2234a
Log if detach volumes wasn't able to run
justinsb Sep 13, 2013
1f1855d
Log if detach volumes wasn't able to run
justinsb Sep 13, 2013
e1ac912
Set instance id in ModifyInstanceRequest
justinsb Sep 13, 2013
a81d133
Merge branch 'block_network_chaos_type'
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
bf7e0b8
Merge branch 'block_network_chaos_type'
justinsb Sep 13, 2013
3c8fc71
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
86f4652
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 13, 2013
46a1956
Merge branch 'block_network_chaos_type'
justinsb Sep 13, 2013
06de659
Merge branch 'simulate_ebs_failure'
justinsb Sep 13, 2013
8764f78
Merge branch 'upstream'
justinsb Sep 14, 2013
09590bc
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
cafca39
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
f9be9bf
Merge branch 'simulate_ebs_failure'
justinsb Sep 14, 2013
24d3d38
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
683c991
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
d2473b2
Merge branch 'simulate_ebs_failure'
justinsb Sep 14, 2013
4c5e6df
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
18c9aab
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
01218c6
Merge branch 'pluggable_chaos_type' into simulate_ebs_failure
justinsb Sep 14, 2013
1c33d0e
Merge branch 'simulate_ebs_failure'
justinsb Sep 14, 2013
f6f9a3c
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
c4ad5e8
Merge branch 'block_network_chaos_type'
justinsb Sep 14, 2013
cc52e80
Merge branch 'use_jclouds'
justinsb Sep 14, 2013
bc1a63b
Expose jClouds compute context
justinsb Sep 14, 2013
24d2a5a
Another chaos type: burn cpu
justinsb Sep 14, 2013
8626201
Checkstyle fixes
justinsb Sep 14, 2013
8cc6982
Make code uglier to keep PMD happy
justinsb Sep 14, 2013
28fedff
Avoid hitting unsupported methods in the mocks
justinsb Sep 14, 2013
2d5f9c6
Lazy-init jclouds because of ... you guessed it ... the mocks
justinsb Sep 14, 2013
a98f8a3
Abstracted out common code into ScriptChaosType
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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ dependencies {
compile 'com.google.guava:guava:11.+'
compile 'org.apache.httpcomponents:httpclient:4.2.1'
compile 'org.apache.httpcomponents:httpcore:4.2.1'
compile 'org.jclouds:jclouds-all:1.6.0'
compile 'org.jclouds.driver:jclouds-jsch:1.6.0'
compile 'org.jclouds.driver:jclouds-slf4j:1.6.0'

testCompile 'org.testng:testng:6.3.1'
testCompile 'org.mockito:mockito-core:1.8.5'
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/com/netflix/simianarmy/CloudClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;

import org.jclouds.compute.ComputeService;

/**
* The CloudClient interface. This abstractions provides the interface that the monkeys need to interact with
Expand Down Expand Up @@ -89,4 +90,43 @@ 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);

/**
* Returns the jClouds compute service.
*/
ComputeService getJcloudsComputeService();

/**
* Returns the jClouds node id for an instance id on this CloudClient.
*/
String getJcloudsId(String instanceId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
import com.netflix.simianarmy.NotFoundException;
import com.netflix.simianarmy.chaos.BlockAllNetworkTrafficChaosType;
import com.netflix.simianarmy.chaos.ChaosCrawler.InstanceGroup;
import com.netflix.simianarmy.chaos.BurnCpuChaosType;
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 @@ -92,6 +94,8 @@ public BasicChaosMonkey(ChaosMonkey.Context ctx) {
allChaosTypes = Lists.newArrayList();
allChaosTypes.add(new ShutdownInstanceChaosType(cfg));
allChaosTypes.add(new BlockAllNetworkTrafficChaosType(cfg));
allChaosTypes.add(new DetachVolumesChaosType(cfg));
allChaosTypes.add(new BurnCpuChaosType(cfg));

TimeUnit freqUnit = ctx.scheduler().frequencyUnit();
long units = freqUnit.convert(close.getTimeInMillis() - open.getTimeInMillis(), TimeUnit.MILLISECONDS);
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/netflix/simianarmy/chaos/BurnCpuChaosType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.netflix.simianarmy.chaos;

import java.io.IOException;
import com.netflix.simianarmy.MonkeyConfiguration;

/**
* Executes a CPU intensive program on the node, using up all available CPU.
*
* This simulates either a noisy CPU neighbor on the box or just a general issue with the CPU.
*/
public class BurnCpuChaosType extends ScriptChaosType {
/**
* Constructor.
*
* @param config
* Configuration to use
* @throws IOException
*/
public BurnCpuChaosType(MonkeyConfiguration config) {
super(config, "BurnCpu");
}
}
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);
}
}

}
146 changes: 146 additions & 0 deletions src/main/java/com/netflix/simianarmy/chaos/ScriptChaosType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.netflix.simianarmy.chaos;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Set;

import org.jclouds.compute.ComputeService;
import org.jclouds.compute.Utils;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ssh.SshClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import com.netflix.simianarmy.CloudClient;
import com.netflix.simianarmy.MonkeyConfiguration;

/**
* Base class for chaos types that run a script over JClouds/SSH on the node.
*/
public abstract class ScriptChaosType extends ChaosType {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(ScriptChaosType.class);

/**
* The SSH credentials to log on to an instance.
*/
private final LoginCredentials sshCredentials;

/**
* Constructor.
*
* @param config
* Configuration to use
* @throws IOException
*/
public ScriptChaosType(MonkeyConfiguration config, String key) {
super(config, key);

String sshUser = config.getStrOrElse("simianarmy.chaos.ssh.user", "root");
String privateKey = null;

String sshKeyPath = config.getStrOrElse("simianarmy.chaos.ssh.key", null);
if (sshKeyPath != null) {
sshKeyPath = sshKeyPath.trim();
if (sshKeyPath.startsWith("~/")) {
String home = System.getProperty("user.home");
if (!Strings.isNullOrEmpty(home)) {
if (!home.endsWith("/")) {
home += "/";
}
sshKeyPath = home + sshKeyPath.substring(2);
}
}
try {
privateKey = Files.toString(new File(sshKeyPath), Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Unable to read the specified SSH key: " + sshKeyPath, e);
}
}

if (privateKey == null) {
this.sshCredentials = null;
} else {
this.sshCredentials = LoginCredentials.builder().user(sshUser).privateKey(privateKey).build();
}
}

/**
* We can apply the strategy iff we can SSH to the instance.
*/
@Override
public boolean canApply(CloudClient cloudClient, String instanceId) {
// TODO: Check that SSH connection works here?

if (this.sshCredentials == null) {
LOGGER.info("Strategy disabled because SSH credentials not set");
return false;
}

return super.canApply(cloudClient, instanceId);
}

/**
* Runs the script.
*/
@Override
public void apply(CloudClient cloudClient, String instanceId) {
ComputeService computeService = cloudClient.getJcloudsComputeService();

String jcloudsId = cloudClient.getJcloudsId(instanceId);

// Work around a jclouds bug / documentation issue...
// Set<NodeMetadata> nodes = computeService.listNodesByIds(Collections.singletonList(jcloudsId));
Set<NodeMetadata> nodes = Sets.newHashSet();
for (ComputeMetadata n : computeService.listNodes()) {
if (jcloudsId.equals(n.getId())) {
nodes.add((NodeMetadata) n);
}
}

if (nodes.isEmpty()) {
LOGGER.warn("Unable to jclouds node: {}", jcloudsId);
for (ComputeMetadata n : computeService.listNodes()) {
LOGGER.info("Did find node: {}", n);
}
throw new IllegalStateException("Unable to find node using jclouds: " + jcloudsId);
}
NodeMetadata node = Iterables.getOnlyElement(nodes);

node = NodeMetadataBuilder.fromNodeMetadata(node).credentials(sshCredentials).build();

LOGGER.info("Running script for {} on instance {}", getKey(), instanceId);

Utils utils = computeService.getContext().getUtils();
SshClient ssh = utils.sshForNode().apply(node);

ssh.connect();

String filename = getKey().toLowerCase() + ".sh";
URL url = Resources.getResource("/scripts/" + filename);
String script;
try {
script = Resources.toString(url, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Error reading script resource", e);
}

ssh.put("/tmp/" + filename, script);
ExecResponse response = ssh.exec("/bin/bash /tmp/" + filename);
if (response.getExitStatus() != 0) {
LOGGER.warn("Got non-zero output from running script: {}", response);
}
ssh.disconnect();
}
}
Loading