
io.fabric8.chaos.monkey.ChaosMonkey Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of chaos-monkey Show documentation
Show all versions of chaos-monkey Show documentation
Kills random pods for chaos fun!
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.fabric8.chaos.monkey;
import io.fabric8.annotations.Eager;
import io.fabric8.hubot.HubotNotifier;
import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.utils.Filter;
import io.fabric8.utils.Filters;
import org.apache.deltaspike.core.api.config.ConfigProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
/**
* Implements a Chaos Monkey which is a kubernetes port of the Netflix Chaos Monkey
*/
@ApplicationScoped
@Eager
public class ChaosMonkey {
public static final String DEFAULT_ROOM_EXPRESSION = "#fabric8_${namespace}";
public static final String DEFAULT_EXCLUDES = "letschat*,gogs*";
private static final transient Logger LOG = LoggerFactory.getLogger(ChaosMonkey.class);
private TimerTask task;
private HubotNotifier notifier;
private final String roomExpression;
private final String includePatterns;
private final String excludePatterns;
private final int killFrequency;
private Filter includeFilter;
private Filter excludeFilter;
private String namespace = KubernetesHelper.defaultNamespace();
private KubernetesClient kubernetes = new DefaultKubernetesClient();
private Timer timer = new Timer("Chaos Monkey timer", true);
/**
* Note this constructor is only added to help CDI work with the {@link Eager} extension
*/
public ChaosMonkey() {
roomExpression = DEFAULT_ROOM_EXPRESSION;
includePatterns = null;
excludePatterns = DEFAULT_EXCLUDES;
killFrequency = 60;
}
@Inject
public ChaosMonkey(HubotNotifier notifier,
@ConfigProperty(name = "CHAOS_MONKEY_ROOM", defaultValue = DEFAULT_ROOM_EXPRESSION) String roomExpression,
@ConfigProperty(name = "CHAOS_MONKEY_INCLUDES") String includePatterns,
@ConfigProperty(name = "CHAOS_MONKEY_EXCLUDES", defaultValue = DEFAULT_EXCLUDES) String excludePatterns,
@ConfigProperty(name = "CHAOS_MONKEY_KILL_FREQUENCY_SECONDS", defaultValue = "60") int killFrequency) throws Exception {
this.notifier = notifier;
this.roomExpression = roomExpression;
this.includePatterns = includePatterns;
this.excludePatterns = excludePatterns;
this.killFrequency = killFrequency;
this.includeFilter = createTextFilter(includePatterns);
this.excludeFilter = createTextFilter(excludePatterns);
if (killFrequency < 1) {
LOG.warn("Ignoring invalid killFrequency of " + killFrequency);
killFrequency = 60;
}
LOG.info("Starting Chaos Monkey on Kubernetes namespace " + getNamespace() + " at " + kubernetes.getMasterUrl() + " with includes " + includePatterns + " excludes " + excludePatterns + " " + " kill frequency " + killFrequency + " seconds");
notify("Chaos Monkey is starting in namespace " + getNamespace() + " with include patterns '" + includePatterns + "' exclude patterns '" + excludePatterns + "' and a kill frequency of " + killFrequency + " seconds. Here I come!");
task = new TimerTask() {
@Override
public void run() {
killPod();
}
};
long period = killFrequency * 1000;
long initialDelay = 1;
timer.schedule(task, initialDelay, period);
waitUntilCompleted();
}
public static Filter createTextFilter(String patterns) {
if (patterns != null && patterns.contains(",")) {
String[] split = patterns.split(",");
List array = Arrays.asList(split);
return Filters.createStringFilters(array);
} else {
return Filters.createStringFilter(patterns);
}
}
protected void waitUntilCompleted() {
CountDownLatch latch = new CountDownLatch(1);
while (true) {
try {
latch.await();
} catch (InterruptedException e) {
// ignore
}
}
}
public String getNamespace() {
return namespace;
}
protected void killPod() {
String namespace = getNamespace();
PodList pods = kubernetes.pods().inNamespace(namespace).list();
List targets = new ArrayList<>();
if (pods != null) {
List podList = pods.getItems();
for (Pod pod : podList) {
if (isTarget(pod)) {
targets.add(pod);
}
}
}
String message = null;
Pod pod = null;
if (targets.size() > 0) {
pod = pickRandom(targets);
}
boolean killed = false;
if (pod == null) {
message = "No matching pods available to kill. Boo!";
} else {
String name = KubernetesHelper.getName(pod);
try {
kubernetes.pods().inNamespace(namespace).withName(KubernetesHelper.getName(pod)).delete();
message = "Chaos Monkey killed pod " + name + " in namespace " + namespace;
killed = true;
} catch (Exception e) {
message = "Chaos Monkey failed to kill pod " + name + " in namespace " + namespace + " due to: " + e;
}
}
notify(message);
if (killed) {
String monkey = getMonkey();
if (monkey != null) {
notify(monkey);
}
}
}
/**
* Returns a random selection from the list
*/
public static T pickRandom(List list) {
int size = list.size();
if (size <= 0) {
return null;
} else {
int idx = (int) Math.round(Math.random() * (size - 1));
return list.get(idx);
}
}
protected String getMonkey() {
String[] monkeys = {
"http://i.giphy.com/yD9vMV7aDl3xK.gif",
"http://i.giphy.com/ncBhyy0TVZHmU.gif",
"http://i.giphy.com/12oyZr7VXoTn68.gif",
"http://i.giphy.com/43nZCD3lLbri0.gif",
"http://i.giphy.com/M93ZgxJzIV8v6.gif",
"http://i.giphy.com/bLHJ71uLsgqWI.gif",
"http://i.giphy.com/5Zesu5VPNGJlm.gif",
"http://i.giphy.com/ava8sWgcW387C.gif",
"http://i.giphy.com/dchERAZ73GvOE.gif",
"http://i.giphy.com/pFwRzOLfuGHok.gif",
"http://i.giphy.com/VH3X9TU7aQLrq.gif",
"http://i.giphy.com/1Ia8zGu9QtH2w.gif",
"http://i.giphy.com/2Faz9JzRpalvpYKGc.gif",
"http://i.giphy.com/2ymva1ROJjoEU.gif",
"http://i.giphy.com/TLulTJKuyLgMU.gif",
"http://i.giphy.com/fw4EIdDeVvjna.gif",
"http://i.giphy.com/OYJ2kbvdTPW6I.gif",
"http://i.giphy.com/BBkKEBJkmFbTG.gif",
"http://i.giphy.com/KqN5Nw9TuBJWE.gif",
"http://i.giphy.com/yLZQKurQvmIAo.gif",
"http://i.giphy.com/vIyZvUXy7O7XW.gif",
"http://i.giphy.com/13nRKeBVhrAqmk.gif",
"http://i.giphy.com/5YWrACKqcsPcI.gif",
"http://i.giphy.com/kfse8uuYqn30c.gif",
"http://i.giphy.com/Aak2oJcN9BS1y.gif",
"http://i.giphy.com/AwXAC8MMX1JUk.gif",
"http://i.giphy.com/rEEYzhbkalKLK.gif",
"http://i.giphy.com/7RVJ5arTndqvK.gif",
"http://i.giphy.com/j7Ol3sUfRY39S.gif",
"http://i.giphy.com/CKZHnkRNIcr8A.gif"
};
return pickRandom(Arrays.asList(monkeys));
}
protected void notify(String message) {
if (notifier == null) {
LOG.warn("No notifier so can't say: " + message);
} else {
String room = getRoom();
try {
notifier.notifyRoom(room, message);
} catch (Throwable e) {
LOG.warn("Caught: " + e);
}
}
}
/**
* Returns true if the given pod matches the selection criteria
*/
public boolean isTarget(Pod pod) {
String name = KubernetesHelper.getName(pod);
return includeFilter.matches(name) && !excludeFilter.matches(name);
}
protected String getRoom() {
String namespace = getNamespace();
return this.roomExpression.replace("${namespace}", namespace);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy