Skip to content

Commit 57d2097

Browse files
feat(deployment): add sequencer readiness check stage to system test workflow
1 parent 787c114 commit 57d2097

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

.github/workflows/consolidated_system_test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ jobs:
120120
- name: Set default namespace
121121
run: kubectl config set-context --current --namespace ${{ env.namespace }}
122122

123+
- name: Run readiness check
124+
run: pipenv run python ./scripts/system_tests/readiness_check.py --deployment_config_path ${{ env.deployment_config_path }} --namespace ${{ env.namespace }}
125+
123126
- name: Get container logs
124127
if: always()
125128
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)