├── .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 |
--------------------------------------------------------------------------------