Skip to content

Commit a730bbd

Browse files
Move Antithesis demo to etcd repo
Signed-off-by: Abdur Rehman <razashahid107@gmail.com>
1 parent 0ad7ea4 commit a730bbd

File tree

5 files changed

+188
-0
lines changed

5 files changed

+188
-0
lines changed

tests/antithesis/Dockerfile.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from scratch
2+
3+
COPY docker-compose.yml /docker-compose.yml

tests/antithesis/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
This directory enables integration of Antithesis with etcd. There are 4 containers running in this system: 3 that make up an etcd cluster (etcd0, etcd1, etcd2) and one that "[makes the system go](https://antithesis.com/docs/getting_started/basic_test_hookup/)" (client).
2+
3+
## Quickstart
4+
5+
### 1. Build and Tag the Docker Image
6+
7+
Run this command from the `antithesis/test-template` directory:
8+
9+
```bash
10+
docker build . -f Dockerfile.client -t etcd-client:latest
11+
```
12+
13+
### 2. (Optional) Check the Image Locally
14+
15+
You can verify your new image is built:
16+
17+
```bash
18+
docker images | grep etcd-client
19+
```
20+
21+
It should show something like:
22+
23+
```
24+
etcd-client latest <IMAGE_ID> <DATE>
25+
```
26+
27+
### 3. Use in Docker Compose
28+
29+
Run the following command from the root directory for Antithesis tests (`tests/antithesis`):
30+
31+
```bash
32+
docker-compose up
33+
```
34+
35+
The client will continuously check the health of the etcd nodes and print logs similar to:
36+
37+
```
38+
[+] Running 4/4
39+
✔ Container etcd0 Created 0.0s
40+
✔ Container etcd2 Created 0.0s
41+
✔ Container etcd1 Created 0.0s
42+
✔ Container client Recreated 0.1s
43+
Attaching to client, etcd0, etcd1, etcd2
44+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.134294Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_ADVERTISE_CLIENT_URLS","variable-value":"http://etcd2.etcd:2379"}
45+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.138501Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_ADVERTISE_PEER_URLS","variable-value":"http://etcd2:2380"}
46+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.138646Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_CLUSTER","variable-value":"etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"}
47+
etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138434Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_ADVERTISE_CLIENT_URLS","variable-value":"http://etcd0.etcd:2379"}
48+
etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138582Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_ADVERTISE_PEER_URLS","variable-value":"http://etcd0:2380"}
49+
etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138592Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_CLUSTER","variable-value":"etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"}
50+
51+
...
52+
...
53+
(skipping some repeated logs for brevity)
54+
...
55+
...
56+
57+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.484698Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
58+
etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484092Z","caller":"embed/serve.go:210","msg":"serving client traffic insecurely; this is strongly discouraged!","traffic":"grpc+http","address":"[::]:2379"}
59+
etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.484563Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
60+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.485101Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
61+
etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484130Z","caller":"etcdmain/main.go:44","msg":"notifying init daemon"}
62+
etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.485782Z","caller":"embed/serve.go:210","msg":"serving client traffic insecurely; this is strongly discouraged!","traffic":"grpc+http","address":"[::]:2379"}
63+
etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484198Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
64+
client | Client [entrypoint]: starting...
65+
client | Client [entrypoint]: checking cluster health...
66+
client | Client [entrypoint]: connection successful with etcd0
67+
client | Client [entrypoint]: connection successful with etcd1
68+
client | Client [entrypoint]: connection successful with etcd2
69+
client | Client [entrypoint]: cluster is healthy!
70+
```
71+
72+
And it will stay running indefinitely.
73+
74+
## Troubleshooting
75+
76+
- **Image Pull Errors**: If Docker can’t pull `etcd-client:latest`, make sure you built it locally (see the “Build and Tag” step) or push it to a registry that Compose can access.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
services:
3+
4+
etcd0:
5+
image: 'gcr.io/etcd-development/etcd:v3.5.21'
6+
container_name: etcd0
7+
hostname: etcd0
8+
environment:
9+
ETCD_NAME: "etcd0"
10+
ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd0:2380"
11+
ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
12+
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
13+
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd0.etcd:2379"
14+
ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
15+
ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
16+
ETCD_INITIAL_CLUSTER_STATE: "new"
17+
18+
etcd1:
19+
image: 'gcr.io/etcd-development/etcd:v3.5.21'
20+
container_name: etcd1
21+
hostname: etcd1
22+
environment:
23+
ETCD_NAME: "etcd1"
24+
ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd1:2380"
25+
ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
26+
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
27+
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd1.etcd:2379"
28+
ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
29+
ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
30+
ETCD_INITIAL_CLUSTER_STATE: "new"
31+
32+
etcd2:
33+
image: 'gcr.io/etcd-development/etcd:v3.5.21'
34+
container_name: etcd2
35+
hostname: etcd2
36+
environment:
37+
ETCD_NAME: "etcd2"
38+
ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd2:2380"
39+
ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
40+
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
41+
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd2.etcd:2379"
42+
ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
43+
ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
44+
ETCD_INITIAL_CLUSTER_STATE: "new"
45+
46+
client:
47+
image: 'etcd-client:latest'
48+
container_name: client
49+
entrypoint: ["/entrypoint.py"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM ubuntu:latest
2+
3+
# Update package index first, then install Python
4+
RUN apt-get update && \
5+
apt-get install -y python3 python3-pip
6+
7+
# Then install additional Python packages
8+
RUN apt-get install -y python3-etcd3 python3-numpy python3-protobuf python3-filelock
9+
10+
# Install Antithesis Python SDK
11+
RUN pip install antithesis cffi --break-system-packages
12+
13+
# Copy your entrypoint script
14+
COPY ./entrypoint/entrypoint.py /entrypoint.py
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env -S python3 -u
2+
3+
# This file serves as the client's entrypoint. It:
4+
# 1. Confirms that all nodes in the cluster are available
5+
# 2. Signals "setupComplete" using the Antithesis SDK
6+
7+
import etcd3, time
8+
9+
from antithesis.lifecycle import (
10+
setup_complete,
11+
)
12+
13+
SLEEP = 10
14+
15+
def check_health():
16+
17+
node_options = ["etcd0", "etcd1", "etcd2"]
18+
19+
for i in range(0, len(node_options)):
20+
try:
21+
c = etcd3.client(host=node_options[i], port=2379)
22+
c.get('setting-up')
23+
print(f"Client [entrypoint]: connection successful with {node_options[i]}")
24+
except Exception as e:
25+
print(f"Client [entrypoint]: connection failed with {node_options[i]}")
26+
print(f"Client [entrypoint]: error: {e}")
27+
return False
28+
return True
29+
30+
print("Client [entrypoint]: starting...")
31+
32+
while True:
33+
print("Client [entrypoint]: checking cluster health...")
34+
if check_health():
35+
print("Client [entrypoint]: cluster is healthy!")
36+
break
37+
else:
38+
print(f"Client [entrypoint]: cluster is not healthy. retrying in {SLEEP} seconds...")
39+
time.sleep(SLEEP)
40+
41+
42+
# Here is the python format for setup_complete. At this point, our system is fully initialized and ready to test.
43+
setup_complete({"Message":"ETCD cluster is healthy"})
44+
45+
# sleep infinity
46+
time.sleep(31536000)

0 commit comments

Comments
 (0)