├── .gitignore ├── README.md ├── api-gateway ├── Dockerfile ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── chaos │ │ └── monkey │ │ └── shopping │ │ └── gateway │ │ ├── ApiGatewayApplication.java │ │ ├── commands │ │ ├── BestsellerFashionCommand.java │ │ ├── BestsellerToysCommand.java │ │ └── HotDealsCommand.java │ │ ├── domain │ │ ├── ProductResponse.java │ │ ├── ResponseType.java │ │ └── Startpage.java │ │ └── rest │ │ └── ApiGatewayRestController.java │ └── resources │ ├── application-docker.yml │ └── application.yml ├── bestseller-fashion ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── chaos │ │ │ └── monkey │ │ │ └── shopping │ │ │ └── bestseller │ │ │ └── fashion │ │ │ ├── BestsellerFashionApplication.java │ │ │ └── BestsellerFashionRestController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── bestseller │ └── fashion │ └── BestsellerFashionApplicationTest.java ├── bestseller-toys ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── chaos │ │ │ └── monkey │ │ │ └── shopping │ │ │ └── bestseller │ │ │ └── toys │ │ │ ├── BestsellerToysApplication.java │ │ │ └── BestsellerToysRestController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── bestseller │ └── toys │ └── BestsellerToysApplicationTest.java ├── chaos-scripts ├── disk_delay.sh ├── disk_error.sh ├── disk_random_hole_puncher.sh ├── echo.sh ├── net_clear.sh ├── net_delay.sh ├── net_delay_rate_limit.sh ├── net_loss.sh ├── net_partition.sh ├── net_partition_remove.sh └── stress_cpu.sh ├── chaostoolkit-base ├── Dockerfile ├── chaostoolkit-experiments │ └── exp_spring_latency.json ├── config │ ├── .bashrc │ └── settings.yaml └── install-chaos.sh ├── docker-base-image ├── .bashrc ├── Dockerfile └── chaos-scripts │ ├── disk_delay.sh │ ├── disk_error.sh │ ├── disk_random_hole_puncher.sh │ ├── echo.sh │ ├── net_clear.sh │ ├── net_delay.sh │ ├── net_delay_rate_limit.sh │ ├── net_loss.sh │ ├── net_partition.sh │ ├── net_partition_remove.sh │ └── stress_cpu.sh ├── docker-compose.yml ├── hot-deals ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── chaos │ │ │ └── monkey │ │ │ └── shopping │ │ │ └── hotdeals │ │ │ ├── HotDealsApplication.java │ │ │ └── HotDealsRestController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── hotdeals │ └── HotDealsApplicationTest.java ├── pom.xml └── shared ├── pom.xml └── src └── main └── java └── com └── example └── chaos └── monkey └── shopping └── domain ├── Product.java ├── ProductBuilder.java └── ProductCategory.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | 7 | # Package Files # 8 | *.jar 9 | *.war 10 | *.ear 11 | 12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 13 | hs_err_pid* 14 | .idea/ 15 | *.iml 16 | *.iws 17 | 18 | *.log 19 | maven-archiver* 20 | target/ 21 | 22 | #flattened POMs 23 | .flattened-pom.xml 24 | 25 | # gnupg keyring 26 | /.gnupg 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chaos-monkey-spring-boot-demo 2 | Demo of Chaos Monkey for Spring Boot 3 | 4 | ## How to build 5 | 1. git clone https://github.com/MrBW/chaos-monkey-spring-boot-demo.git 6 | 2. git checkout chaostoolkit 7 | 3. docker-compose build docker-base-image 8 | 4. mvn clean package 9 | 5. docker-compose up 10 | 11 | ## What you will find 12 | ... 13 | -------------------------------------------------------------------------------- /api-gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker-base-image 2 | ARG SERVICE_NAME 3 | 4 | ARG JAR_FILE 5 | COPY ${JAR_FILE} app.jar 6 | 7 | ENV SERVICE_NAME="${SERVICE_NAME}" 8 | LABEL APP=${SERVICE_NAME} 9 | LABEL DOMAIN="shopping-demo" 10 | 11 | ENV JAVA_OPTS="-Xmx128m -Xms64m" 12 | ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar --spring.profiles.active=docker"] 13 | -------------------------------------------------------------------------------- /api-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | api-gateway 12 | api-gateway 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-actuator 18 | 19 | 20 | io.micrometer 21 | micrometer-registry-prometheus 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-devtools 31 | runtime 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | com.example.chaos.monkey.shopping 40 | shared 41 | ${parent.version} 42 | 43 | 44 | com.netflix.hystrix 45 | hystrix-core 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | com.spotify 56 | dockerfile-maven-plugin 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 62 | 8 63 | 8 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/ApiGatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ApiGatewayApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ApiGatewayApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/commands/BestsellerFashionCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.commands; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.gateway.domain.ProductResponse; 5 | import com.example.chaos.monkey.shopping.gateway.domain.ResponseType; 6 | import com.netflix.hystrix.*; 7 | import io.micrometer.core.instrument.MeterRegistry; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.core.ParameterizedTypeReference; 11 | import org.springframework.http.HttpMethod; 12 | import org.springframework.web.client.RestTemplate; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * @author Benjamin Wilms 19 | */ 20 | public class BestsellerFashionCommand extends HystrixCommand { 21 | 22 | private static final Logger log = LoggerFactory.getLogger(HotDealsCommand.class); 23 | private final RestTemplate restTemplate; 24 | private final String url; 25 | 26 | public BestsellerFashionCommand(HystrixCommandGroupKey group, int timeout, RestTemplate restTemplate, 27 | String url) { 28 | 29 | super(Setter.withGroupKey(group).andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeout)) 30 | .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("fashionThreadPool")) 31 | .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() 32 | .withCoreSize(10).withMaximumSize(50).withAllowMaximumSizeToDivergeFromCoreSize(true).withMaxQueueSize(100))); 33 | 34 | this.restTemplate = restTemplate; 35 | this.url = url; 36 | } 37 | 38 | protected ProductResponse run() throws Exception { 39 | ProductResponse response = new ProductResponse(); 40 | 41 | response.setProducts(restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference>() { 42 | }).getBody()); 43 | 44 | response.setResponseType(ResponseType.REMOTE_SERVICE); 45 | 46 | return response; 47 | } 48 | 49 | @Override 50 | protected ProductResponse getFallback() { 51 | 52 | return new ProductResponse(ResponseType.FALLBACK,Collections.emptyList()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/commands/BestsellerToysCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.commands; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.gateway.domain.ProductResponse; 5 | import com.example.chaos.monkey.shopping.gateway.domain.ResponseType; 6 | import com.netflix.hystrix.*; 7 | import com.netflix.hystrix.util.HystrixRollingNumberEvent; 8 | import io.micrometer.core.instrument.MeterRegistry; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.core.ParameterizedTypeReference; 12 | import org.springframework.http.HttpMethod; 13 | import org.springframework.web.client.RestTemplate; 14 | 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | /** 19 | * @author Benjamin Wilms 20 | */ 21 | public class BestsellerToysCommand extends HystrixCommand { 22 | 23 | private static final Logger log = LoggerFactory.getLogger(HotDealsCommand.class); 24 | private final RestTemplate restTemplate; 25 | private final String url; 26 | 27 | 28 | public BestsellerToysCommand(HystrixCommandGroupKey group, int timeout, RestTemplate restTemplate, 29 | String url) { 30 | 31 | super(Setter.withGroupKey(group).andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeout)) 32 | .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("toysThreadPool")) 33 | .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() 34 | .withCoreSize(10).withMaximumSize(50).withAllowMaximumSizeToDivergeFromCoreSize(true).withMaxQueueSize(100))); 35 | 36 | this.restTemplate = restTemplate; 37 | this.url = url; 38 | } 39 | 40 | protected ProductResponse run() throws Exception { 41 | ProductResponse response = new ProductResponse(); 42 | 43 | response.setProducts(restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference>() { 44 | }).getBody()); 45 | 46 | response.setResponseType(ResponseType.REMOTE_SERVICE); 47 | 48 | return response; 49 | 50 | 51 | } 52 | 53 | @Override 54 | protected ProductResponse getFallback() { 55 | return new ProductResponse(ResponseType.FALLBACK, Collections.emptyList()); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/commands/HotDealsCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.commands; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.gateway.domain.ProductResponse; 5 | import com.example.chaos.monkey.shopping.gateway.domain.ResponseType; 6 | import com.netflix.hystrix.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.core.ParameterizedTypeReference; 10 | import org.springframework.http.HttpMethod; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | /** 17 | * @author Benjamin Wilms 18 | */ 19 | public class HotDealsCommand extends HystrixCommand { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(HotDealsCommand.class); 22 | 23 | private final RestTemplate restTemplate; 24 | private final String url; 25 | 26 | public HotDealsCommand(HystrixCommandGroupKey group, int timeout, RestTemplate restTemplate, 27 | String url) { 28 | 29 | super(Setter.withGroupKey(group).andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeout)) 30 | .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("hotDealsThreadPool")) 31 | .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() 32 | .withCoreSize(10).withMaximumSize(50).withAllowMaximumSizeToDivergeFromCoreSize(true).withMaxQueueSize(100))); 33 | 34 | this.restTemplate = restTemplate; 35 | this.url = url; 36 | } 37 | 38 | protected ProductResponse run() throws Exception { 39 | ProductResponse response = new ProductResponse(); 40 | 41 | response.setProducts(restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference>() { 42 | }).getBody()); 43 | 44 | response.setResponseType(ResponseType.REMOTE_SERVICE); 45 | 46 | return response; 47 | } 48 | 49 | @Override 50 | protected ProductResponse getFallback() { 51 | return new ProductResponse(ResponseType.FALLBACK, Collections.emptyList()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/ProductResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author Benjamin Wilms 12 | */ 13 | 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Data 17 | public class ProductResponse { 18 | 19 | private ResponseType responseType; 20 | private List products; 21 | } 22 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/ResponseType.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | /** 4 | * @author Benjamin Wilms 5 | */ 6 | public enum ResponseType { 7 | 8 | REMOTE_SERVICE, SECOND_TRY, FALLBACK, ERROR; 9 | } 10 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/Startpage.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author Benjamin Wilms 13 | */ 14 | 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @JsonPropertyOrder({ "fashionResponse", "toysResponse", "hotDealsResponse", "duration", "statusFashion","statusToys","statusHotDeals" }) 19 | public class Startpage { 20 | 21 | private long duration; 22 | private String statusFashion; 23 | private String statusToys; 24 | private String statusHotDeals; 25 | private ProductResponse fashionResponse; 26 | private ProductResponse toysResponse; 27 | private ProductResponse hotDealsResponse; 28 | } 29 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/rest/ApiGatewayRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.rest; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.gateway.commands.BestsellerFashionCommand; 5 | import com.example.chaos.monkey.shopping.gateway.commands.BestsellerToysCommand; 6 | import com.example.chaos.monkey.shopping.gateway.commands.HotDealsCommand; 7 | import com.example.chaos.monkey.shopping.gateway.domain.ProductResponse; 8 | import com.example.chaos.monkey.shopping.gateway.domain.ResponseType; 9 | import com.example.chaos.monkey.shopping.gateway.domain.Startpage; 10 | import com.netflix.hystrix.HystrixCommandGroupKey; 11 | import com.netflix.hystrix.exception.HystrixRuntimeException; 12 | import io.micrometer.core.instrument.MeterRegistry; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.boot.web.client.RestTemplateBuilder; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.client.RestTemplate; 18 | 19 | import java.util.Collections; 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.concurrent.Future; 22 | 23 | /** 24 | * @author Benjamin Wilms 25 | */ 26 | @RestController 27 | public class ApiGatewayRestController { 28 | 29 | private MeterRegistry meterRegistry; 30 | 31 | private RestTemplate restTemplate; 32 | 33 | @Value("${rest.endpoint.fashion}") 34 | private String urlFashion; 35 | 36 | @Value("${rest.endpoint.toys}") 37 | private String urlToys; 38 | 39 | @Value("${rest.endpoint.hotdeals}") 40 | private String urlHotDeals; 41 | private HystrixCommandGroupKey hotdealsCommandKey; 42 | private HystrixCommandGroupKey toysCommandKey; 43 | private HystrixCommandGroupKey fashionCommandKey; 44 | 45 | public ApiGatewayRestController(MeterRegistry meterRegistry) { 46 | this.meterRegistry = meterRegistry; 47 | this.restTemplate = new RestTemplateBuilder().build(); 48 | 49 | hotdealsCommandKey = HystrixCommandGroupKey.Factory.asKey("hotdeals"); 50 | toysCommandKey = HystrixCommandGroupKey.Factory.asKey("toys"); 51 | fashionCommandKey = HystrixCommandGroupKey.Factory.asKey("fashion"); 52 | } 53 | 54 | @GetMapping("/startpage") 55 | public Startpage getStartpage() { 56 | Startpage page = new Startpage(); 57 | long start = System.currentTimeMillis(); 58 | 59 | // Create Futures for requesting results 60 | Future bestsellerFashionFuture = getBestsellerFashion(); 61 | Future bestsellerToysFuture = getBestsellerToys(); 62 | Future hotDealsFuture = getHotDeals(); 63 | 64 | // Get Responses from Futures 65 | page.setFashionResponse(extractResponse(bestsellerFashionFuture)); 66 | page.setToysResponse(extractResponse(bestsellerToysFuture)); 67 | page.setHotDealsResponse(extractResponse(hotDealsFuture)); 68 | 69 | // Summary 70 | page.setStatusFashion(page.getFashionResponse().getResponseType().name()); 71 | page.setStatusToys(page.getToysResponse().getResponseType().name()); 72 | page.setStatusHotDeals(page.getHotDealsResponse().getResponseType().name()); 73 | 74 | // Request duration 75 | page.setDuration(System.currentTimeMillis() - start); 76 | 77 | return page; 78 | } 79 | 80 | private ProductResponse extractResponse(Future responseFuture) { 81 | try { 82 | return responseFuture.get(); 83 | } catch (InterruptedException | ExecutionException | HystrixRuntimeException e) { 84 | return new ProductResponse(ResponseType.ERROR, Collections.emptyList()); 85 | } 86 | } 87 | 88 | 89 | private Future getHotDeals() { 90 | 91 | return new HotDealsCommand(hotdealsCommandKey, 200, restTemplate, urlHotDeals).queue(); 92 | } 93 | 94 | private Future getBestsellerToys() { 95 | 96 | return new BestsellerToysCommand(toysCommandKey, 200, restTemplate, urlToys).queue(); 97 | } 98 | 99 | private Future getBestsellerFashion() { 100 | 101 | return new BestsellerFashionCommand(fashionCommandKey, 200, restTemplate, urlFashion).queue(); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /api-gateway/src/main/resources/application-docker.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | rest: 4 | base-path: 5 | application: 6 | name: api-gateway 7 | 8 | rest: 9 | endpoint: 10 | fashion: "http://fashion:8082/fashion/bestseller" 11 | toys: "http://toys:8081/toys/bestseller/" 12 | hotdeals: "http://hot-deals:8083/hotdeals" 13 | management: 14 | endpoints: 15 | web: 16 | exposure: 17 | include: '*' 18 | server: 19 | port: 8080 20 | -------------------------------------------------------------------------------- /api-gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | data: 4 | rest: 5 | base-path: 6 | 7 | rest: 8 | endpoint: 9 | fashion: "http://localhost:8082/fashion/bestseller" 10 | toys: "http://localhost:8081/toys/bestseller/" 11 | hotdeals: "http://localhost:8083/hotdeals" 12 | management: 13 | endpoints: 14 | web: 15 | exposure: 16 | include: '*' 17 | server: 18 | port: 8080 19 | -------------------------------------------------------------------------------- /bestseller-fashion/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker-base-image 2 | ARG SERVICE_NAME 3 | ARG JAR_FILE 4 | 5 | VOLUME /tmp 6 | 7 | COPY ${JAR_FILE} app.jar 8 | 9 | ENV JAVA_OPTS="-Xmx128m -Xms64m" 10 | ENV SERVICE_NAME=${SERVICE_NAME} 11 | LABEL APP=${SERVICE_NAME} 12 | LABEL DOMAIN="shopping-demo" 13 | 14 | ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar"] -------------------------------------------------------------------------------- /bestseller-fashion/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | bestseller-fashion 13 | fashion 14 | 15 | 16 | 17 | 18 | de.codecentric 19 | chaos-monkey-spring-boot 20 | 2.0.0 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-actuator 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-devtools 35 | runtime 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | com.example.chaos.monkey.shopping 44 | hot-deals 45 | ${parent.version} 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | com.spotify 56 | dockerfile-maven-plugin 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /bestseller-fashion/src/main/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BestsellerFashionApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BestsellerFashionApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bestseller-fashion/src/main/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 5 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | /** 15 | * @author Benjamin Wilms 16 | */ 17 | @RestController 18 | @RequestMapping("/fashion") 19 | public class BestsellerFashionRestController { 20 | 21 | @GetMapping("/bestseller") 22 | public List getBestsellerProducts() { 23 | AtomicLong aLong = new AtomicLong(4); 24 | 25 | ProductBuilder productBuilder = new ProductBuilder(); 26 | 27 | Product product1 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Bob Mailor Slim Jeans") 28 | .createProduct(); 29 | 30 | Product product2 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Lewi's Jeanshose 511 " + 31 | "Slim Fit") 32 | .createProduct(); 33 | 34 | Product product3 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Urban Classics T-Shirt " + 35 | "Shaped Long Tee") 36 | .createProduct(); 37 | return Arrays.asList(product1, product2, product3); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /bestseller-fashion/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | management: 5 | endpoint: 6 | chaosmonkey: 7 | enabled: true 8 | endpoints: 9 | web: 10 | exposure: 11 | include: "*" 12 | chaos: 13 | monkey: 14 | enabled: false 15 | watcher: 16 | restController: true 17 | service: false 18 | assaults: 19 | latency-active: true 20 | spring: 21 | profiles: 22 | active: chaos-monkey 23 | application: 24 | name: fashion-besteller 25 | -------------------------------------------------------------------------------- /bestseller-fashion/src/test/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * @author Benjamin Wilms 10 | */ 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest 13 | public class BestsellerFashionApplicationTest { 14 | @Test 15 | public void contextLoads() { 16 | } 17 | } -------------------------------------------------------------------------------- /bestseller-toys/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker-base-image 2 | ARG SERVICE_NAME 3 | ARG JAR_FILE 4 | 5 | VOLUME /tmp 6 | 7 | COPY ${JAR_FILE} app.jar 8 | 9 | ENV JAVA_OPTS="-Xmx128m -Xms64m" 10 | ENV SERVICE_NAME=${SERVICE_NAME} 11 | LABEL APP=${SERVICE_NAME} 12 | LABEL DOMAIN="shopping-demo" 13 | 14 | ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar"] -------------------------------------------------------------------------------- /bestseller-toys/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | bestseller-toys 13 | toys 14 | 15 | 16 | 17 | de.codecentric 18 | chaos-monkey-spring-boot 19 | 2.0.0 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | runtime 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | com.example.chaos.monkey.shopping 43 | shared 44 | ${parent.version} 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | com.spotify 55 | dockerfile-maven-plugin 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /bestseller-toys/src/main/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BestsellerToysApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BestsellerToysApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bestseller-toys/src/main/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 5 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | /** 15 | * @author Benjamin Wilms 16 | */ 17 | @RestController 18 | @RequestMapping("/toys") 19 | public class BestsellerToysRestController { 20 | 21 | @GetMapping("/bestseller") 22 | public List getBestsellerProducts() { 23 | AtomicLong aLong = new AtomicLong(1); 24 | 25 | ProductBuilder productBuilder = new ProductBuilder(); 26 | 27 | Product product1 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Yodas Hut") 28 | .createProduct(); 29 | 30 | Product product2 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Millennium Falcon") 31 | .createProduct(); 32 | 33 | Product product3 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Imperial Tie Fighter") 34 | .createProduct(); 35 | return Arrays.asList(product1, product2, product3); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /bestseller-toys/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | 4 | 5 | # Init Chaos Monkey for Spring Boot by profile 6 | spring: 7 | profiles: 8 | active: chaos-monkey 9 | application: 10 | name: toys-besteller 11 | 12 | 13 | # Configure Chaos Monkey - enabled = false 14 | chaos: 15 | monkey: 16 | enabled: false 17 | watcher: 18 | restController: true 19 | service: false 20 | assaults: 21 | latency-active: false 22 | 23 | # Spring Boot Actuator Endpoint Chaos Monkey for Spring Boot 24 | management: 25 | endpoint: 26 | chaosmonkey: 27 | enabled: true 28 | endpoints: 29 | web: 30 | exposure: 31 | include: chaosmonkey -------------------------------------------------------------------------------- /bestseller-toys/src/test/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * @author Benjamin Wilms 10 | */ 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest 13 | public class BestsellerToysApplicationTest { 14 | 15 | @Test 16 | public void contextLoads() { 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /chaos-scripts/disk_delay.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | device='/dev/ram0' 3 | #create a RAM disk 4 | modprobe brd rd_nr=1 rd_size=131027 5 | readdelay=100 6 | writedelay=100 7 | size=$(blockdev --getsize $device) # Size in 512-bytes sectors 8 | mkfs.ext4 $device 9 | echo "0 $size delay $device 0 $readdelay $device 0 $writedelay" | dmsetup create delay 10 | -------------------------------------------------------------------------------- /chaos-scripts/disk_error.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | device='/dev/loop0' 3 | #Create device with 128MB 4 | dd if=/dev/zero of=/tmp/error.img bs=512 count=256000 5 | losetup $device /tmp/error.img 6 | mkfs.ext4 $device 7 | #Damage the device, linear-region=ok, error-region=hole, linear-region=ok 8 | echo "0 17 linear $device 0\n17 5 error\n22 255978 linear $device 22" | dmsetup create error -------------------------------------------------------------------------------- /chaos-scripts/disk_random_hole_puncher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | start_sector=0 3 | good_sector_size=0 4 | 5 | for sector in {0..1048576}; do 6 | 7 | if [[ ${RANDOM} == 0 ]]; then 8 | echo "${start_sector} ${good_sector_size} linear /dev/loop0 ${start_sector}" 9 | echo "${sector} 1 error" 10 | start_sector=$((${sector}+1)) 11 | good_sector_size=0 12 | else 13 | good_sector_size=$((${good_sector_size}+1)) 14 | fi 15 | done 16 | 17 | echo "${start_sector} $((${good_sector_size}-1)) linear /dev/loop0 ${start_sector}" 18 | 19 | -------------------------------------------------------------------------------- /chaos-scripts/echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IFS=', ' read -r -a array <<< "$1" 3 | 4 | echo "Narf ${array[$2]}" 5 | -------------------------------------------------------------------------------- /chaos-scripts/net_clear.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $1=="--help-chaos" ]] 6 | then 7 | printf "net_clear : Clear all tc-rules from device " 8 | exit 0 9 | fi 10 | 11 | tc qdisc delete dev $1 root 12 | -------------------------------------------------------------------------------- /chaos-scripts/net_delay.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $# -eq 1 && $1=="--help-chaos" ]] 6 | then 7 | printf "net_delay : Add a delay to device for each packet." 8 | exit 0 9 | fi 10 | #Config using FIFO adding a fixed delay of 100ms 11 | tc qdisc add dev $1 root netem delay $2 12 | -------------------------------------------------------------------------------- /chaos-scripts/net_delay_rate_limit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $# -eq 1 && $1=="--help-chaos" ]] 6 | then 7 | printf "net_delay_rate_limit : Add a token bucket with a size of 900 bytes to the device." 8 | exit 0 9 | fi 10 | 11 | #Config using token bucket filter, bucket size 900 bytes 12 | tc qdisc add dev $2 root tbf rate 16kbit latency 20ms burst 900 13 | -------------------------------------------------------------------------------- /chaos-scripts/net_loss.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #Lose 1% of incoming packages 3 | tc qdisc change dev eth1 root netem loss $2.0% 4 | -------------------------------------------------------------------------------- /chaos-scripts/net_partition.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | IFS=', ' read -r -a hosts <<< "$1" 3 | iptables -I INPUT -s ${hosts[$2]} -j DROP 4 | iptables -I OUTPUT -d ${hosts[$2]} -j DROP 5 | -------------------------------------------------------------------------------- /chaos-scripts/net_partition_remove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | IFS=', ' read -r -a hosts <<< "$1" 3 | iptables -D INPUT -s ${hosts[$2]} -j DROP 4 | iptables -D OUTPUT -d ${hosts[$2]} -j DROP 5 | -------------------------------------------------------------------------------- /chaos-scripts/stress_cpu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | stress --cpu $1 --timeout $2 3 | -------------------------------------------------------------------------------- /chaostoolkit-base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | # BUG: Getting tons of debconf messages unless TERM is set to linux 3 | ENV TERM linux 4 | 5 | COPY install-chaos.sh /root 6 | COPY config/.bashrc /root 7 | COPY config/settings.yaml /root/.chaostoolkit/settings.yaml 8 | 9 | ADD chaostoolkit-experiments/ /root 10 | 11 | # Install. 12 | SHELL ["/bin/bash", "-c"] 13 | RUN \ 14 | sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list && \ 15 | apt-get update && \ 16 | apt-get -y install apt-utils && \ 17 | apt-get -y upgrade && \ 18 | apt-get -y install nano && \ 19 | apt-get -y install inetutils-ping && \ 20 | apt-get -y install curl 21 | 22 | RUN chmod a+x /root/install-chaos.sh 23 | RUN . /root/install-chaos.sh 24 | 25 | # Set environment variables. 26 | ENV HOME /root 27 | ENV LC_ALL=C.UTF-8 28 | ENV LANG=C.UTF-8 29 | 30 | # Define working directory. 31 | WORKDIR /root 32 | 33 | # Define default command. 34 | CMD ["bash"] 35 | -------------------------------------------------------------------------------- /chaostoolkit-base/chaostoolkit-experiments/exp_spring_latency.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "title": "Exploring assumptions if a dependency starts responding slowly", 4 | "description": "Uses the Spring Chaos Monkey to manipulate a service", 5 | "tags": [ 6 | "service", 7 | "spring" 8 | ], 9 | "steady-state-hypothesis": { 10 | "probes": [ 11 | { 12 | "name": "api-gateway-must-still-respond", 13 | "provider": { 14 | "type": "http", 15 | "url": "http://api-gateway:8080/startpage", 16 | "timeout": [0.25, 0.25] 17 | }, 18 | "tolerance": 200, 19 | "type": "probe" 20 | } 21 | ], 22 | "title": "System is healthy" 23 | }, 24 | "method": [ 25 | { 26 | "name": "enable_chaosmonkey", 27 | "provider": { 28 | "arguments": { 29 | "base_url": "http://fashion:8082/actuator" 30 | }, 31 | "func": "enable_chaosmonkey", 32 | "module": "chaosspring.actions", 33 | "type": "python" 34 | }, 35 | "type": "action" 36 | }, 37 | { 38 | "name": "configure_assaults", 39 | "provider": { 40 | "arguments": { 41 | "base_url": "http://fashion:8082/actuator", 42 | "assaults_configuration": { 43 | "level": 1, 44 | "latencyRangeStart": 5000, 45 | "latencyRangeEnd": 10000, 46 | "latencyActive": true, 47 | "exceptionsActive": false, 48 | "killApplicationActive": false 49 | } 50 | }, 51 | "func": "change_assaults_configuration", 52 | "module": "chaosspring.actions", 53 | "type": "python" 54 | }, 55 | "type": "action" 56 | } 57 | ], 58 | "rollbacks": [ 59 | { 60 | "name": "disable-chaosmonkey", 61 | "type": "action", 62 | "provider": { 63 | "type": "python", 64 | "module": "chaosspring.actions", 65 | "func": "disable_chaosmonkey", 66 | "arguments": { 67 | "base_url": "http://fashion:8082/actuator" 68 | } 69 | } 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /chaostoolkit-base/config/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) 3 | # for examples 4 | 5 | # If not running interactively, don't do anything 6 | [ -z "$PS1" ] && return 7 | 8 | # don't put duplicate lines in the history. See bash(1) for more options 9 | # ... or force ignoredups and ignorespace 10 | HISTCONTROL=ignoredups:ignorespace 11 | 12 | # append to the history file, don't overwrite it 13 | shopt -s histappend 14 | 15 | # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) 16 | HISTSIZE=1000 17 | HISTFILESIZE=2000 18 | 19 | # check the window size after each command and, if necessary, 20 | # update the values of LINES and COLUMNS. 21 | shopt -s checkwinsize 22 | 23 | # make less more friendly for non-text input files, see lesspipe(1) 24 | [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" 25 | 26 | # set variable identifying the chroot you work in (used in the prompt below) 27 | if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then 28 | debian_chroot=$(cat /etc/debian_chroot) 29 | fi 30 | 31 | # set a fancy prompt (non-color, unless we know we "want" color) 32 | case "$TERM" in 33 | xterm-color) color_prompt=yes;; 34 | esac 35 | 36 | # uncomment for a colored prompt, if the terminal has the capability; turned 37 | # off by default to not distract the user: the focus in a terminal window 38 | # should be on the output of commands, not on the prompt 39 | #force_color_prompt=yes 40 | 41 | if [ -n "$force_color_prompt" ]; then 42 | if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then 43 | # We have color support; assume it's compliant with Ecma-48 44 | # (ISO/IEC-6429). (Lack of such support is extremely rare, and such 45 | # a case would tend to support setf rather than setaf.) 46 | color_prompt=yes 47 | else 48 | color_prompt= 49 | fi 50 | fi 51 | 52 | if [ "$color_prompt" = yes ]; then 53 | PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 54 | else 55 | PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 56 | fi 57 | unset color_prompt force_color_prompt 58 | 59 | # If this is an xterm set the title to user@host:dir 60 | case "$TERM" in 61 | xterm*|rxvt*) 62 | PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" 63 | ;; 64 | *) 65 | ;; 66 | esac 67 | 68 | # enable color support of ls and also add handy aliases 69 | if [ -x /usr/bin/dircolors ]; then 70 | test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" 71 | alias ls='ls --color=auto' 72 | #alias dir='dir --color=auto' 73 | #alias vdir='vdir --color=auto' 74 | 75 | alias grep='grep --color=auto' 76 | alias fgrep='fgrep --color=auto' 77 | alias egrep='egrep --color=auto' 78 | fi 79 | 80 | # some more ls aliases 81 | alias ll='ls -alF' 82 | alias la='ls -A' 83 | alias l='ls -CF' 84 | alias chaosenv='source ~/.venvs/chaostk/bin/activate' 85 | 86 | # Alias definitions. 87 | # You may want to put all your additions into a separate file like 88 | # ~/.bash_aliases, instead of adding them here directly. 89 | # See /usr/share/doc/bash-doc/examples in the bash-doc package. 90 | 91 | if [ -f ~/.bash_aliases ]; then 92 | . ~/.bash_aliases 93 | fi 94 | 95 | # enable programmable completion features (you don't need to enable 96 | # this, if it's already enabled in /etc/bash.bashrc and /etc/profile 97 | # sources /etc/bash.bashrc). 98 | #if [ -f /etc/bash_completion ] && ! shopt -oq posix; then 99 | # . /etc/bash_completion 100 | #fi 101 | -------------------------------------------------------------------------------- /chaostoolkit-base/config/settings.yaml: -------------------------------------------------------------------------------- 1 | auths: 2 | dummyhost:9999: 3 | type: bearer 4 | value: 3666c5be86b61ee7bc6553d81217cdc704fe25000dce1fb3a9f017b7782f 5 | -------------------------------------------------------------------------------- /chaostoolkit-base/install-chaos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo C.UTF exports 3 | export LC_ALL=C.UTF-8 4 | export LANG=C.UTF-8 5 | echo Install python3 6 | apt-get -y install python3 python3-venv 7 | echo Install pkill openssl 8 | apt-get -y install procps 9 | apt-get -y install openssl 10 | echo Create a virtual environment 11 | python3 -m venv ~/.venvs/chaostk 12 | source ~/.venvs/chaostk/bin/activate 13 | pip install wheel 14 | echo Install Chaos Toolkit 15 | pip install chaostoolkit 16 | echo Check Chaos Toolkit version 17 | chaos --version 18 | echo Install driver Spring Boot 19 | pip install -U chaostoolkit-spring 20 | echo Install ChaosToolkit plugin chaoshub 21 | pip install chaostoolkit-chaoshub 22 | -------------------------------------------------------------------------------- /docker-base-image/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) 3 | # for examples 4 | 5 | # If not running interactively, don't do anything 6 | [ -z "$PS1" ] && return 7 | 8 | # don't put duplicate lines in the history. See bash(1) for more options 9 | # ... or force ignoredups and ignorespace 10 | HISTCONTROL=ignoredups:ignorespace 11 | 12 | # append to the history file, don't overwrite it 13 | shopt -s histappend 14 | 15 | # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) 16 | HISTSIZE=1000 17 | HISTFILESIZE=2000 18 | 19 | # check the window size after each command and, if necessary, 20 | # update the values of LINES and COLUMNS. 21 | shopt -s checkwinsize 22 | 23 | # make less more friendly for non-text input files, see lesspipe(1) 24 | [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" 25 | 26 | # set variable identifying the chroot you work in (used in the prompt below) 27 | if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then 28 | debian_chroot=$(cat /etc/debian_chroot) 29 | fi 30 | 31 | # set a fancy prompt (non-color, unless we know we "want" color) 32 | case "$TERM" in 33 | xterm-color) color_prompt=yes;; 34 | esac 35 | 36 | # uncomment for a colored prompt, if the terminal has the capability; turned 37 | # off by default to not distract the user: the focus in a terminal window 38 | # should be on the output of commands, not on the prompt 39 | #force_color_prompt=yes 40 | 41 | if [ -n "$force_color_prompt" ]; then 42 | if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then 43 | # We have color support; assume it's compliant with Ecma-48 44 | # (ISO/IEC-6429). (Lack of such support is extremely rare, and such 45 | # a case would tend to support setf rather than setaf.) 46 | color_prompt=yes 47 | else 48 | color_prompt= 49 | fi 50 | fi 51 | 52 | if [ "$color_prompt" = yes ]; then 53 | PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 54 | else 55 | PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 56 | fi 57 | unset color_prompt force_color_prompt 58 | 59 | # If this is an xterm set the title to user@host:dir 60 | case "$TERM" in 61 | xterm*|rxvt*) 62 | PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" 63 | ;; 64 | *) 65 | ;; 66 | esac 67 | 68 | # enable color support of ls and also add handy aliases 69 | if [ -x /usr/bin/dircolors ]; then 70 | test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" 71 | alias ls='ls --color=auto' 72 | #alias dir='dir --color=auto' 73 | #alias vdir='vdir --color=auto' 74 | 75 | alias grep='grep --color=auto' 76 | alias fgrep='fgrep --color=auto' 77 | alias egrep='egrep --color=auto' 78 | fi 79 | 80 | # some more ls aliases 81 | alias ll='ls -alF' 82 | alias la='ls -A' 83 | alias l='ls -CF' 84 | 85 | # Alias definitions. 86 | # You may want to put all your additions into a separate file like 87 | # ~/.bash_aliases, instead of adding them here directly. 88 | # See /usr/share/doc/bash-doc/examples in the bash-doc package. 89 | 90 | if [ -f ~/.bash_aliases ]; then 91 | . ~/.bash_aliases 92 | fi 93 | 94 | # enable programmable completion features (you don't need to enable 95 | # this, if it's already enabled in /etc/bash.bashrc and /etc/profile 96 | # sources /etc/bash.bashrc). 97 | #if [ -f /etc/bash_completion ] && ! shopt -oq posix; then 98 | # . /etc/bash_completion 99 | #fi 100 | PS1='${debian_chroot:+($debian_chroot)}\u @ $SERVICE_NAME:\w\$ ' -------------------------------------------------------------------------------- /docker-base-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM harisekhon/ubuntu-java 2 | LABEL application="Chaos Shopping Demo" 3 | LABEL student=${STUDENT} 4 | VOLUME /tmp 5 | COPY ./chaos-scripts /var/chaosscripts 6 | COPY .bashrc root/ 7 | RUN apt-get update && apt-get install -y --no-install-recommends apt-utils 8 | RUN sh -c 'apt-get update' 9 | RUN sh -c 'apt-get install iproute2 -qq' 10 | RUN sh -c 'apt-get install net-tools -qq' 11 | RUN sh -c 'apt-get install sudo -qq' 12 | RUN sh -c 'apt-get install iputils-ping -qq' 13 | RUN sh -c 'apt-get install apache2-utils -qq' 14 | RUN sh -c 'apt-get install nano -qq' 15 | RUN sh -c 'apt-get install curl -qq' 16 | RUN sh -c 'apt-get install stress -qq' 17 | RUN useradd -ms /bin/bash chaos 18 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/disk_delay.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | device='/dev/ram0' 3 | #create a RAM disk 4 | modprobe brd rd_nr=1 rd_size=131027 5 | readdelay=100 6 | writedelay=100 7 | size=$(blockdev --getsize $device) # Size in 512-bytes sectors 8 | mkfs.ext4 $device 9 | echo "0 $size delay $device 0 $readdelay $device 0 $writedelay" | dmsetup create delay 10 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/disk_error.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | device='/dev/loop0' 3 | #Create device with 128MB 4 | dd if=/dev/zero of=/tmp/error.img bs=512 count=256000 5 | losetup $device /tmp/error.img 6 | mkfs.ext4 $device 7 | #Damage the device, linear-region=ok, error-region=hole, linear-region=ok 8 | echo "0 17 linear $device 0\n17 5 error\n22 255978 linear $device 22" | dmsetup create error -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/disk_random_hole_puncher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | start_sector=0 3 | good_sector_size=0 4 | 5 | for sector in {0..1048576}; do 6 | 7 | if [[ ${RANDOM} == 0 ]]; then 8 | echo "${start_sector} ${good_sector_size} linear /dev/loop0 ${start_sector}" 9 | echo "${sector} 1 error" 10 | start_sector=$((${sector}+1)) 11 | good_sector_size=0 12 | else 13 | good_sector_size=$((${good_sector_size}+1)) 14 | fi 15 | done 16 | 17 | echo "${start_sector} $((${good_sector_size}-1)) linear /dev/loop0 ${start_sector}" 18 | 19 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IFS=', ' read -r -a array <<< "$1" 3 | 4 | echo "Narf ${array[$2]}" 5 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_clear.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $1=="--help-chaos" ]] 6 | then 7 | printf "net_clear : Clear all tc-rules from device " 8 | exit 0 9 | fi 10 | 11 | tc qdisc delete dev $1 root 12 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_delay.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $# -eq 1 && $1=="--help-chaos" ]] 6 | then 7 | printf "net_delay : Add a delay to device for each packet." 8 | exit 0 9 | fi 10 | #Config using FIFO adding a fixed delay of 100ms 11 | tc qdisc add dev $1 root netem delay $2 12 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_delay_rate_limit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o nounset 3 | set -o errexit 4 | 5 | if [[ $# -eq 0 ]] || [[ $# -eq 1 && $1=="--help-chaos" ]] 6 | then 7 | printf "net_delay_rate_limit : Add a token bucket with a size of 900 bytes to the device." 8 | exit 0 9 | fi 10 | 11 | #Config using token bucket filter, bucket size 900 bytes 12 | tc qdisc add dev $2 root tbf rate 16kbit latency 20ms burst 900 13 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_loss.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #Lose 1% of incoming packages 3 | tc qdisc change dev eth1 root netem loss $2.0% 4 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_partition.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | IFS=', ' read -r -a hosts <<< "$1" 3 | iptables -I INPUT -s ${hosts[$2]} -j DROP 4 | iptables -I OUTPUT -d ${hosts[$2]} -j DROP 5 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/net_partition_remove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | IFS=', ' read -r -a hosts <<< "$1" 3 | iptables -D INPUT -s ${hosts[$2]} -j DROP 4 | iptables -D OUTPUT -d ${hosts[$2]} -j DROP 5 | -------------------------------------------------------------------------------- /docker-base-image/chaos-scripts/stress_cpu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | stress --cpu $1 --timeout $2 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | docker-base-image: 5 | build: docker-base-image/ 6 | image: docker-base-image 7 | 8 | chaostoolkit: 9 | container_name: chaostoolkit 10 | build: chaostoolkit-base/ 11 | tty: true 12 | depends_on: 13 | - docker-base-image 14 | 15 | chaoshub: 16 | image: chaostoolkit/chaoshub:0.1.2 17 | container_name: chaoshub 18 | ports: 19 | - "8888:8080" 20 | tty: true 21 | depends_on: 22 | - docker-base-image 23 | 24 | api-gateway: 25 | container_name: api-gateway 26 | image: api-gateway 27 | # need NET_ADMIN to run chaos linux experiments 28 | cap_add: 29 | - NET_ADMIN 30 | ports: 31 | - "8080:8080" 32 | volumes: 33 | - ./chaos-scripts:/var/chaosscripts 34 | depends_on: 35 | - docker-base-image 36 | 37 | fashion: 38 | container_name: fashion 39 | image: bestseller-fashion 40 | # need NET_ADMIN to run chaos linux experiments 41 | cap_add: 42 | - NET_ADMIN 43 | ports: 44 | - "8082:8082" 45 | volumes: 46 | - ./chaos-scripts:/var/chaosscripts 47 | depends_on: 48 | - docker-base-image 49 | 50 | toys: 51 | image: bestseller-toys 52 | container_name: toys 53 | # need NET_ADMIN to run chaos linux experiments 54 | cap_add: 55 | - NET_ADMIN 56 | ports: 57 | - "8081:8081" 58 | volumes: 59 | - ./chaos-scripts:/var/chaosscripts 60 | depends_on: 61 | - docker-base-image 62 | 63 | hot-deals: 64 | image: hot-deals 65 | container_name: hot-deals 66 | # need NET_ADMIN to run chaos linux experiments 67 | cap_add: 68 | - NET_ADMIN 69 | ports: 70 | - "8083:8083" 71 | volumes: 72 | - ./chaos-scripts:/var/chaosscripts 73 | depends_on: 74 | - docker-base-image 75 | networks: 76 | - servicenet 77 | -------------------------------------------------------------------------------- /hot-deals/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker-base-image 2 | ARG SERVICE_NAME 3 | ARG JAR_FILE 4 | 5 | VOLUME /tmp 6 | 7 | COPY ${JAR_FILE} app.jar 8 | 9 | ENV JAVA_OPTS="-Xmx128m -Xms64m" 10 | ENV SERVICE_NAME=${SERVICE_NAME} 11 | LABEL APP=${SERVICE_NAME} 12 | LABEL DOMAIN="shopping-demo" 13 | 14 | ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar"] -------------------------------------------------------------------------------- /hot-deals/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | hot-deals 13 | hot-deals 14 | 15 | 16 | 17 | 18 | de.codecentric 19 | chaos-monkey-spring-boot 20 | 2.0.0 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.projectlombok 33 | lombok 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-devtools 39 | runtime 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | com.example.chaos.monkey.shopping 48 | shared 49 | ${parent.version} 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | com.spotify 60 | dockerfile-maven-plugin 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /hot-deals/src/main/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class HotDealsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(HotDealsApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hot-deals/src/main/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | 4 | import com.example.chaos.monkey.shopping.domain.Product; 5 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 6 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | /** 16 | * @author Benjamin Wilms 17 | */ 18 | @RestController 19 | public class HotDealsRestController { 20 | 21 | @GetMapping("/hotdeals") 22 | public List getHotDeals() { 23 | AtomicLong aLong = new AtomicLong(7); 24 | 25 | ProductBuilder productBuilder = new ProductBuilder(); 26 | 27 | Product product1 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Thermal Winter Warm Hot Heat" + 28 | " Socks") 29 | .createProduct(); 30 | 31 | Product product2 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("RC Quadcopter Drone with 2.0MP Camera Live") 32 | .createProduct(); 33 | 34 | Product product3 = productBuilder.setCategory(ProductCategory.BOOKS).setId(aLong.getAndIncrement()).setName("Spring Boot 2: Moderne Softwareentwicklung mit Spring 5") 35 | .createProduct(); 36 | return Arrays.asList(product1, product2, product3); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /hot-deals/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8083 3 | 4 | management: 5 | endpoint: 6 | chaosmonkey: 7 | enabled: true 8 | endpoints: 9 | web: 10 | exposure: 11 | include: chaosmonkey 12 | chaos: 13 | monkey: 14 | enabled: false 15 | watcher: 16 | restController: true 17 | service: false 18 | assaults: 19 | latency-active: false 20 | spring: 21 | profiles: 22 | active: chaos-monkey 23 | application: 24 | name: hotdeals -------------------------------------------------------------------------------- /hot-deals/src/test/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * @author Benjamin Wilms 10 | */ 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest 13 | public class HotDealsApplicationTest { 14 | @Test 15 | public void contextLoads() { 16 | } 17 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.example.chaos.monkey.shopping 8 | shopping-parent 9 | 1.0-SNAPSHOT 10 | 11 | hot-deals 12 | bestseller-fashion 13 | bestseller-toys 14 | api-gateway 15 | shared 16 | 17 | pom 18 | 19 | 20 | 21 | 22 | mrbwilms 23 | 1.4.3 24 | 25 | UTF-8 26 | UTF-8 27 | 1.8 28 | 2.0.5.RELEASE 29 | 1.16.20 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-dependencies 38 | ${spring-boot-dependencies.version} 39 | pom 40 | import 41 | 42 | 43 | com.netflix.hystrix 44 | hystrix-core 45 | 1.5.12 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-actuator 50 | ${spring-boot-dependencies.version} 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-web 55 | ${spring-boot-dependencies.version} 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-devtools 61 | ${spring-boot-dependencies.version} 62 | runtime 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | ${spring-boot-dependencies.version} 68 | test 69 | 70 | 71 | org.projectlombok 72 | lombok 73 | ${lombok.version} 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-maven-plugin 85 | 86 | 87 | 88 | repackage 89 | 90 | 91 | 92 | 93 | 94 | 95 | com.spotify 96 | dockerfile-maven-plugin 97 | ${docker-maven-plugin.version} 98 | 99 | 100 | ${project.artifactId} 101 | 102 | target/${project.build.finalName}.jar 103 | ${project.name} 104 | 105 | false 106 | 107 | 108 | 109 | build-image 110 | package 111 | 112 | build 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /shared/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | shared 13 | 14 | 15 | 16 | org.projectlombok 17 | lombok 18 | 19 | 20 | -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/Product.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author Benjamin Wilms 9 | */ 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class Product { 14 | 15 | private long id; 16 | private String name; 17 | private ProductCategory category; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/ProductBuilder.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | public class ProductBuilder { 4 | private long id; 5 | private String name; 6 | private ProductCategory category; 7 | 8 | public ProductBuilder setId(long id) { 9 | this.id = id; 10 | return this; 11 | } 12 | 13 | public ProductBuilder setName(String name) { 14 | this.name = name; 15 | return this; 16 | } 17 | 18 | public ProductBuilder setCategory(ProductCategory category) { 19 | this.category = category; 20 | return this; 21 | } 22 | 23 | public Product createProduct() { 24 | return new Product(id, name, category); 25 | } 26 | } -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/ProductCategory.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | /** 4 | * @author Benjamin Wilms 5 | */ 6 | public enum ProductCategory { 7 | FASHION,TOYS,BOOKS; 8 | } 9 | --------------------------------------------------------------------------------