All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fabric8.chaos.monkey.ChaosMonkey Maven / Gradle / Ivy

There is a newer version: 2.2.335
Show newest version
/**
 * 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