Skip to content

Commit f8f2207

Browse files
authored
feat: Chaos testing endpoints (#818)
1 parent 40f9e98 commit f8f2207

31 files changed

+1529
-74
lines changed

src/cart/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ The following environment variables are available for configuring the service:
1818
| `RETAIL_CART_PERSISTENCE_DYNAMODB_ENDPOINT` | The Amazon DynamoDB endpoint to use | `""` |
1919
| `RETAIL_CART_PERSISTENCE_DYNAMODB_CREATE_TABLE` | Enable to automatically create the Amazon DynamoDB table required | `false` |
2020

21+
## Endpoints
22+
23+
Several "utility" endpoints are provided with useful functionality for various scenarios:
24+
25+
| Method | Name | Description |
26+
| -------- | ------------------------ | ---------------------------------------------------------------------------------- |
27+
| `POST` | `/chaos/status/{code}` | All HTTP requests to API paths will return the given HTTP status code |
28+
| `DELETE` | `/chaos/status` | Disables the HTTP status response above |
29+
| `POST` | `/chaos/latency/{delay}` | All HTTP requests to API paths will have the specified delay added in milliseconds |
30+
| `DELETE` | `/chaos/latency` | Disables the HTTP response latency above |
31+
| `POST` | `/chaos/health` | Causes all health check requests to fail |
32+
| `DELETE` | `/chaos/health` | Returns the health check to its default behavior |
33+
2134
## Running
2235

2336
There are two main options for running the service:

src/cart/docker-compose.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,3 @@ services:
5353
interval: 5s
5454
timeout: 15s
5555
retries: 3
56-
develop:
57-
watch:
58-
- action: rebuild
59-
path: src
60-
- action: rebuild
61-
path: pom.xml

src/cart/pom.xml

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737
<scope>import</scope>
3838
</dependency>
3939
<dependency>
40-
<groupId>io.opentelemetry.instrumentation</groupId>
41-
<artifactId>opentelemetry-instrumentation-bom</artifactId>
42-
<version>2.12.0</version>
43-
<type>pom</type>
44-
<scope>import</scope>
40+
<groupId>io.opentelemetry.instrumentation</groupId>
41+
<artifactId>opentelemetry-instrumentation-bom</artifactId>
42+
<version>2.12.0</version>
43+
<type>pom</type>
44+
<scope>import</scope>
4545
</dependency>
4646
</dependencies>
4747
</dependencyManagement>
@@ -127,29 +127,18 @@
127127
<scope>test</scope>
128128
</dependency>
129129
<dependency>
130-
<groupId>de.codecentric</groupId>
131-
<artifactId>chaos-monkey-spring-boot</artifactId>
132-
<version>3.2.0</version>
133-
<exclusions>
134-
<exclusion>
135-
<artifactId>jackson-databind</artifactId>
136-
<groupId>com.fasterxml.jackson.core</groupId>
137-
</exclusion>
138-
</exclusions>
139-
</dependency>
140-
<dependency>
141-
<groupId>com.fasterxml.jackson.core</groupId>
142-
<artifactId>jackson-databind</artifactId>
130+
<groupId>com.fasterxml.jackson.core</groupId>
131+
<artifactId>jackson-databind</artifactId>
143132
</dependency>
144133
<dependency>
145-
<groupId>io.opentelemetry.instrumentation</groupId>
146-
<artifactId>opentelemetry-spring-boot-starter</artifactId>
134+
<groupId>io.opentelemetry.instrumentation</groupId>
135+
<artifactId>opentelemetry-spring-boot-starter</artifactId>
147136
</dependency>
148137
<dependency>
149-
<groupId>io.opentelemetry.instrumentation</groupId>
150-
<artifactId>opentelemetry-aws-sdk-2.2-autoconfigure</artifactId>
151-
<version>2.12.0-alpha</version>
152-
<scope>runtime</scope>
138+
<groupId>io.opentelemetry.instrumentation</groupId>
139+
<artifactId>opentelemetry-aws-sdk-2.2-autoconfigure</artifactId>
140+
<version>2.12.0-alpha</version>
141+
<scope>runtime</scope>
153142
</dependency>
154143
</dependencies>
155144

@@ -189,19 +178,19 @@
189178
<target>${java.version}</target>
190179
<annotationProcessorPaths>
191180
<path>
192-
<groupId>org.mapstruct</groupId>
193-
<artifactId>mapstruct-processor</artifactId>
194-
<version>${mapstruct.version}</version>
181+
<groupId>org.mapstruct</groupId>
182+
<artifactId>mapstruct-processor</artifactId>
183+
<version>${mapstruct.version}</version>
195184
</path>
196185
<path>
197-
<groupId>org.projectlombok</groupId>
198-
<artifactId>lombok-mapstruct-binding</artifactId>
199-
<version>0.2.0</version>
186+
<groupId>org.projectlombok</groupId>
187+
<artifactId>lombok-mapstruct-binding</artifactId>
188+
<version>0.2.0</version>
200189
</path>
201190
<path>
202-
<groupId>org.projectlombok</groupId>
203-
<artifactId>lombok</artifactId>
204-
<version>${lombok.version}</version>
191+
<groupId>org.projectlombok</groupId>
192+
<artifactId>lombok</artifactId>
193+
<version>${lombok.version}</version>
205194
</path>
206195
</annotationProcessorPaths>
207196
</configuration>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: MIT-0
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
6+
* software and associated documentation files (the "Software"), to deal in the Software
7+
* without restriction, including without limitation the rights to use, copy, modify,
8+
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17+
*/
18+
19+
package com.amazon.sample.carts.chaos;
20+
21+
import java.util.Map;
22+
import org.springframework.http.HttpStatus;
23+
import org.springframework.http.ResponseEntity;
24+
import org.springframework.web.bind.annotation.DeleteMapping;
25+
import org.springframework.web.bind.annotation.PathVariable;
26+
import org.springframework.web.bind.annotation.PostMapping;
27+
import org.springframework.web.bind.annotation.RequestMapping;
28+
import org.springframework.web.bind.annotation.RestController;
29+
30+
@RestController
31+
@RequestMapping("/chaos")
32+
public class ChaosController {
33+
34+
private final ChaosService chaosService;
35+
36+
public ChaosController(ChaosService chaosService) {
37+
this.chaosService = chaosService;
38+
}
39+
40+
@PostMapping("/latency/{ms}")
41+
public ResponseEntity<Map<String, String>> setLatency(@PathVariable int ms) {
42+
if (ms < 0) {
43+
return ResponseEntity.badRequest()
44+
.body(Map.of("error", "Latency must be a positive number"));
45+
}
46+
chaosService.setLatency(ms);
47+
return ResponseEntity.ok(Map.of("message", "Latency set to " + ms + "ms"));
48+
}
49+
50+
@DeleteMapping("/latency")
51+
public ResponseEntity<Map<String, String>> disableLatency() {
52+
chaosService.disableLatency();
53+
return ResponseEntity.ok(Map.of("message", "Latency disabled"));
54+
}
55+
56+
@PostMapping("/status/{code}")
57+
public ResponseEntity<Map<String, String>> setErrorStatus(
58+
@PathVariable int code
59+
) {
60+
if (HttpStatus.resolve(code) == null) {
61+
return ResponseEntity.badRequest()
62+
.body(Map.of("error", "Invalid HTTP status code"));
63+
}
64+
chaosService.setErrorStatus(code);
65+
return ResponseEntity.ok(Map.of("message", "Error status set to " + code));
66+
}
67+
68+
@DeleteMapping("/status")
69+
public ResponseEntity<Map<String, String>> disableErrorStatus() {
70+
chaosService.disableErrorStatus();
71+
return ResponseEntity.ok(Map.of("message", "Error status disabled"));
72+
}
73+
74+
@PostMapping("/health")
75+
public ResponseEntity<Map<String, String>> disableHealth() {
76+
chaosService.setHealth(false);
77+
return ResponseEntity.ok(Map.of("message", "Health check disabled"));
78+
}
79+
80+
@DeleteMapping("/health")
81+
public ResponseEntity<Map<String, String>> enableHealth() {
82+
chaosService.setHealth(true);
83+
return ResponseEntity.ok(Map.of("message", "Health check enabled"));
84+
}
85+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: MIT-0
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
6+
* software and associated documentation files (the "Software"), to deal in the Software
7+
* without restriction, including without limitation the rights to use, copy, modify,
8+
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17+
*/
18+
19+
package com.amazon.sample.carts.chaos;
20+
21+
import jakarta.servlet.FilterChain;
22+
import jakarta.servlet.ServletException;
23+
import jakarta.servlet.http.HttpServletRequest;
24+
import jakarta.servlet.http.HttpServletResponse;
25+
import java.io.IOException;
26+
import java.util.Optional;
27+
import org.springframework.core.Ordered;
28+
import org.springframework.core.annotation.Order;
29+
import org.springframework.stereotype.Component;
30+
import org.springframework.web.filter.OncePerRequestFilter;
31+
32+
@Component
33+
@Order(Ordered.HIGHEST_PRECEDENCE)
34+
public class ChaosFilter extends OncePerRequestFilter {
35+
36+
private final ChaosService chaosService;
37+
38+
public ChaosFilter(ChaosService chaosService) {
39+
this.chaosService = chaosService;
40+
}
41+
42+
@Override
43+
protected boolean shouldNotFilter(HttpServletRequest request) {
44+
return !request.getRequestURI().startsWith("/carts");
45+
}
46+
47+
@Override
48+
protected void doFilterInternal(
49+
HttpServletRequest request,
50+
HttpServletResponse response,
51+
FilterChain filterChain
52+
) throws ServletException, IOException {
53+
// Apply latency if enabled
54+
chaosService
55+
.getLatencyDelay()
56+
.ifPresent(delay -> {
57+
try {
58+
Thread.sleep(delay);
59+
} catch (InterruptedException e) {
60+
Thread.currentThread().interrupt();
61+
}
62+
});
63+
64+
// Apply error status if enabled
65+
Optional<Integer> errorStatus = chaosService.getErrorStatus();
66+
if (errorStatus.isPresent()) {
67+
response.sendError(errorStatus.get());
68+
return;
69+
}
70+
71+
filterChain.doFilter(request, response);
72+
}
73+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: MIT-0
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
6+
* software and associated documentation files (the "Software"), to deal in the Software
7+
* without restriction, including without limitation the rights to use, copy, modify,
8+
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17+
*/
18+
19+
package com.amazon.sample.carts.chaos;
20+
21+
import org.springframework.boot.actuate.health.Health;
22+
import org.springframework.boot.actuate.health.HealthIndicator;
23+
import org.springframework.stereotype.Component;
24+
25+
@Component
26+
public class ChaosHealthIndicator implements HealthIndicator {
27+
28+
private final ChaosService chaosService;
29+
30+
public ChaosHealthIndicator(ChaosService chaosService) {
31+
this.chaosService = chaosService;
32+
}
33+
34+
@Override
35+
public Health health() {
36+
if (!chaosService.isHealthy()) {
37+
return Health.down()
38+
.withDetail("reason", "Chaos failure enabled")
39+
.build();
40+
}
41+
return Health.up().build();
42+
}
43+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: MIT-0
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
6+
* software and associated documentation files (the "Software"), to deal in the Software
7+
* without restriction, including without limitation the rights to use, copy, modify,
8+
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17+
*/
18+
19+
package com.amazon.sample.carts.chaos;
20+
21+
import java.util.Optional;
22+
import org.springframework.stereotype.Service;
23+
24+
@Service
25+
public class ChaosService {
26+
27+
private int latency = 0;
28+
private int errorStatus = 0;
29+
private boolean isLatencyEnabled = false;
30+
private boolean isErrorStatusEnabled = false;
31+
private boolean isHealthy = true;
32+
33+
public void setLatency(int milliseconds) {
34+
this.latency = milliseconds;
35+
this.isLatencyEnabled = true;
36+
}
37+
38+
public void setErrorStatus(int status) {
39+
this.errorStatus = status;
40+
this.isErrorStatusEnabled = true;
41+
}
42+
43+
public void disableLatency() {
44+
this.isLatencyEnabled = false;
45+
}
46+
47+
public void disableErrorStatus() {
48+
this.isErrorStatusEnabled = false;
49+
}
50+
51+
public void setHealth(boolean healthy) {
52+
this.isHealthy = healthy;
53+
}
54+
55+
public boolean isHealthy() {
56+
return isHealthy;
57+
}
58+
59+
public Optional<Integer> getLatencyDelay() {
60+
return isLatencyEnabled ? Optional.of(latency) : Optional.empty();
61+
}
62+
63+
public Optional<Integer> getErrorStatus() {
64+
return isErrorStatusEnabled ? Optional.of(errorStatus) : Optional.empty();
65+
}
66+
}

src/cart/src/main/resources/application-chaos-errors.yml

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/cart/src/main/resources/application-chaos-latency.yml

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)