Skip to content

Commit 569333f

Browse files
feat(deployment): add sequencer readiness check stage to system test workflow
1 parent c465b39 commit 569333f

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

.github/workflows/consolidated_system_test.yaml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,67 @@ jobs:
7777
- name: Setup cdk8s-cli
7878
run: npm install -g cdk8s-cli
7979

80+
- name: Deploy Dummy Recorder
81+
env:
82+
dummy_recorder_namespace: dummy-recorder
83+
working-directory: deployments/dummy_recorder
84+
run: |
85+
echo "Deploying Dummy Recorder..."
86+
pipenv install
87+
cdk8s import
88+
cdk8s synth --app "pipenv run python main.py --namespace ${{ env.dummy_recorder_namespace }}"
89+
kubectl create namespace ${{ env.dummy_recorder_namespace }} || echo "Namespace already exists"
90+
kubectl apply -R -f ./dist
91+
echo "⏳ Waiting for Dummy Recorder to become ready..."
92+
kubectl wait --namespace ${{ env.dummy_recorder_namespace }} --for=condition=Ready -l app=dummy-recorder pod --timeout=60s
93+
94+
echo "📋 Getting pod information..."
95+
kubectl get pods --namespace ${{ env.dummy_recorder_namespace }}
96+
97+
echo "📝 Describing pods..."
98+
for pod in $(kubectl get pods -n ${{ env.dummy_recorder_namespace }} -l app=dummy-recorder -o jsonpath="{.items[*].metadata.name}"); do
99+
echo "🔍 Description for pod: $pod"
100+
kubectl describe pod $pod -n ${{ env.dummy_recorder_namespace }}
101+
done
102+
103+
echo "📦 Fetching pod logs..."
104+
for pod in $(kubectl get pods -n ${{ env.dummy_recorder_namespace }} -l app=dummy-recorder -o jsonpath="{.items[*].metadata.name}"); do
105+
echo "📜 Logs for pod: $pod"
106+
kubectl logs $pod -n ${{ env.dummy_recorder_namespace }}
107+
done
108+
109+
echo "🚀 Dummy Recorder deployed successfully."
110+
111+
- name: Deploy Dummy Eth2Strk Oracle
112+
env:
113+
namespace: dummy-eth2strk-oracle
114+
working-directory: deployments/dummy_eth2strk_oracle
115+
run: |
116+
echo "Deploying Dummy Eth2Strk Oracle..."
117+
pipenv install
118+
cdk8s import
119+
cdk8s synth --app "pipenv run python main.py --namespace ${{ env.namespace }}"
120+
kubectl create namespace ${{ env.namespace }}
121+
kubectl apply -R -f ./dist
122+
echo "⏳ Waiting for Dummy Eth2Strk Oracle to become ready..."
123+
kubectl wait --namespace ${{ env.namespace }} --for=condition=Ready -l app=dummy-eth2strk-oracle pod --timeout 60s
124+
echo "🚀 Dummy Eth2Strk Oracle deployed successfully."
125+
126+
- name: Deploy Anvil
127+
env:
128+
namespace: anvil
129+
working-directory: deployments/anvil
130+
run: |
131+
echo "Deploying Anvil..."
132+
pipenv install
133+
cdk8s import
134+
cdk8s synth --app "pipenv run python main.py --namespace ${{ env.namespace }}"
135+
kubectl create namespace ${{ env.namespace }}
136+
kubectl apply -R -f ./dist
137+
echo "⏳ Waiting for Anvil to become ready..."
138+
kubectl wait --namespace ${{ env.namespace }} --for=condition=Ready -l app=anvil pod --timeout 60s
139+
echo "🚀 Anvil deployed successfully."
140+
80141
- name: Build binaries
81142
run: cargo build --bin sequencer_node_setup --bin sequencer_simulator
82143

@@ -118,6 +179,9 @@ jobs:
118179
- name: Set default namespace
119180
run: kubectl config set-context --current --namespace ${{ env.namespace }}
120181

182+
- name: Run readiness check
183+
run: pipenv run python ./scripts/system_tests/readiness_check.py --deployment_config_path ${{ env.deployment_config_path }} --namespace ${{ env.namespace }}
184+
121185
- name: Get container logs
122186
if: always()
123187
run: |
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import argparse
2+
import os
3+
from kubernetes import client, config
4+
from kubernetes.client.rest import ApiException
5+
from pathlib import Path
6+
import json
7+
import sys
8+
import time
9+
10+
11+
def check_manifest_files(deployment_config_path: str, workspace: str):
12+
with open(deployment_config_path, "r", encoding="utf-8") as f:
13+
deployment_config = json.load(f)
14+
15+
services = deployment_config["services"]
16+
17+
for service in services:
18+
controller, service_name_lower, controller_lower = extract_service_info(service)
19+
20+
manifest_path = (
21+
Path(workspace)
22+
/ f"deployments/sequencer/dist/sequencer-{service_name_lower}/{controller}.sequencer-{service_name_lower}-{controller_lower}.k8s.yaml"
23+
)
24+
25+
if not manifest_path.exists():
26+
print(
27+
f"❌ Manifest {manifest_path} for {service_name_lower} not found. Aborting..."
28+
)
29+
try:
30+
dir_listing = list(manifest_path.parent.iterdir())
31+
print(
32+
f"Contents of {manifest_path.parent}: {[str(f) for f in dir_listing]}"
33+
)
34+
except FileNotFoundError:
35+
print(f"(Directory {manifest_path.parent} does not exist)")
36+
sys.exit(1)
37+
38+
39+
def extract_service_info(service):
40+
service_name = service["name"]
41+
controller = service["controller"]
42+
return controller, service_name.lower(), controller.lower()
43+
44+
45+
def wait_for_services_ready(deployment_config_path: str, namespace: str):
46+
config.load_kube_config()
47+
48+
with open(deployment_config_path, "r", encoding="utf-8") as f:
49+
deployment_config = json.load(f)
50+
51+
services = deployment_config["services"]
52+
53+
apps_v1 = client.AppsV1Api()
54+
55+
for service in services:
56+
controller, service_name_lower, controller_lower = extract_service_info(service)
57+
58+
resource_name = f"sequencer-{service_name_lower}-{controller_lower}"
59+
60+
print(f"🔍 Checking {controller_lower}: {resource_name}")
61+
62+
try:
63+
if controller_lower == "statefulset":
64+
obj = apps_v1.read_namespaced_stateful_set(
65+
resource_name, namespace=namespace
66+
)
67+
elif controller_lower == "deployment":
68+
obj = apps_v1.read_namespaced_deployment(
69+
resource_name, namespace=namespace
70+
)
71+
else:
72+
print(f"❌ Unknown controller: {controller}. Skipping...")
73+
sys.exit(1)
74+
except ApiException as e:
75+
print(f"❌ API Exception occurred: {e}")
76+
raise
77+
78+
# Describe & status info (light equivalent of kubectl describe)
79+
print(
80+
f"🔍 {controller} {resource_name} status: replicas={obj.status.replicas}, ready={obj.status.ready_replicas}"
81+
)
82+
83+
print(f"⏳ Waiting for {controller_lower}/{resource_name} to become ready...")
84+
85+
timeout_seconds = 180
86+
poll_interval = 5
87+
elapsed = 0
88+
89+
while elapsed < timeout_seconds:
90+
try:
91+
if controller_lower == "statefulset":
92+
status = apps_v1.read_namespaced_stateful_set_status(
93+
resource_name, namespace
94+
).status
95+
ready = status.ready_replicas or 0
96+
desired = status.replicas or 0
97+
elif controller_lower == "deployment":
98+
status = apps_v1.read_namespaced_deployment_status(
99+
resource_name, namespace
100+
).status
101+
ready = status.ready_replicas or 0
102+
desired = status.replicas or 0
103+
else:
104+
print(f"❌ Unknown controller: {controller}.")
105+
sys.exit(1)
106+
107+
if ready == desired and ready > 0:
108+
print(f"✅ {controller} {resource_name} is ready.")
109+
break
110+
except ApiException as e:
111+
print(f"❌ Error while checking status: {e}")
112+
113+
time.sleep(poll_interval)
114+
elapsed += poll_interval
115+
else:
116+
print(
117+
f"❌ Timeout waiting for {controller} {resource_name} to become ready."
118+
)
119+
sys.exit(1)
120+
121+
122+
if __name__ == "__main__":
123+
parser = argparse.ArgumentParser(
124+
description="Check manifest files and wait for K8s services to be ready."
125+
)
126+
parser.add_argument(
127+
"--deployment_config_path",
128+
required=True,
129+
help="Path to the deployment config JSON file",
130+
)
131+
parser.add_argument(
132+
"--namespace",
133+
required=True,
134+
help="Kubernetes namespace",
135+
)
136+
args = parser.parse_args()
137+
138+
github_workspace = os.environ["GITHUB_WORKSPACE"]
139+
140+
check_manifest_files(args.deployment_config_path, github_workspace)
141+
wait_for_services_ready(args.deployment_config_path, namespace=args.namespace)
142+
print("✅ All sequencer services are ready.")

0 commit comments

Comments
 (0)