Skip to content

Commit 376d12f

Browse files
committed
test: make soak retry delay configurable with validation
Add RETRY_DELAY_SECONDS as a validated integer override and use it in operator pod retry logging while keeping the existing 15-second default behavior. Made-with: Cursor
1 parent 36837b7 commit 376d12f

1 file changed

Lines changed: 91 additions & 5 deletions

File tree

scripts/soak-test.sh

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ THRESHOLD_KB=5120 # 5 MB growth limit
1717
NODE_COUNT=100
1818
TEST_NAMESPACE="${TEST_NAMESPACE:-soak-test}"
1919
RESULTS_FILE="${RESULTS_FILE:-/tmp/soak-memory.log}"
20+
CLEANUP_DONE=false
21+
CLEANUP_TIMEOUT_SECONDS="${CLEANUP_TIMEOUT_SECONDS:-120}"
22+
RETRY_DELAY_SECONDS="${RETRY_DELAY_SECONDS:-15}"
2023

2124
# ── Helpers ──────────────────────────────────────────────────────────────────
2225

@@ -26,6 +29,36 @@ get_operator_pid() {
2629
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null
2730
}
2831

32+
validate_positive_integer() {
33+
local name="$1"
34+
local value="$2"
35+
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
36+
echo "ERROR: ${name} must be an integer, got '${value}'"
37+
exit 1
38+
fi
39+
if [[ "$value" -lt 1 ]]; then
40+
echo "ERROR: ${name} must be >= 1, got '${value}'"
41+
exit 1
42+
fi
43+
}
44+
45+
get_operator_pid_with_retry() {
46+
local max_attempts="${1:-5}"
47+
local attempt=1
48+
local pod_name=""
49+
while [[ "$attempt" -le "$max_attempts" ]]; do
50+
pod_name=$(get_operator_pid)
51+
if [[ -n "$pod_name" ]]; then
52+
echo "$pod_name"
53+
return 0
54+
fi
55+
echo "Operator pod not found (attempt ${attempt}/${max_attempts}); retrying in ${RETRY_DELAY_SECONDS}s..."
56+
sleep "$RETRY_DELAY_SECONDS"
57+
attempt=$(( attempt + 1 ))
58+
done
59+
return 1
60+
}
61+
2962
get_rss_kb() {
3063
local pod="$1"
3164
# Read /proc/1/status from inside the container (PID 1 = operator process)
@@ -58,24 +91,77 @@ delete_nodes() {
5891
kubectl delete stellarnode "soak-node-${i}" -n "$TEST_NAMESPACE" \
5992
--ignore-not-found --wait=false
6093
done
61-
# Wait for all to be gone before the next wave
62-
kubectl wait stellarnode \
94+
# Wait for all to be gone before the next wave.
95+
if kubectl wait stellarnode \
6396
--for=delete \
6497
--all \
6598
-n "$TEST_NAMESPACE" \
66-
--timeout=120s 2>/dev/null || true
99+
--timeout="${CLEANUP_TIMEOUT_SECONDS}s" 2>/dev/null; then
100+
echo "Cleanup finished within ${CLEANUP_TIMEOUT_SECONDS}s"
101+
return 0
102+
fi
103+
104+
local remaining
105+
remaining=$(kubectl get stellarnode -n "$TEST_NAMESPACE" --no-headers 2>/dev/null | wc -l | tr -d ' ')
106+
echo "WARN: Cleanup timeout reached after ${CLEANUP_TIMEOUT_SECONDS}s with ${remaining} resource(s) still present."
107+
echo "Aborting soak loop because it is unsafe to proceed with leftover resources."
108+
return 1
67109
}
68110

111+
cleanup_resources() {
112+
local reason="${1:-exit}"
113+
if [[ "$CLEANUP_DONE" == "true" ]]; then
114+
echo "[cleanup] Already completed (reason: ${reason})"
115+
return
116+
fi
117+
CLEANUP_DONE=true
118+
119+
echo "[cleanup] Starting cleanup (reason: ${reason})..."
120+
delete_nodes || true
121+
kubectl delete namespace "$TEST_NAMESPACE" --ignore-not-found --wait=false >/dev/null 2>&1 || true
122+
echo "[cleanup] Cleanup finished for namespace: ${TEST_NAMESPACE}"
123+
}
124+
125+
handle_exit() {
126+
local exit_code=$?
127+
cleanup_resources "exit"
128+
if [[ $exit_code -ne 0 ]]; then
129+
echo "[cleanup] Exiting with failure code: ${exit_code}"
130+
else
131+
echo "[cleanup] Exiting successfully"
132+
fi
133+
exit "$exit_code"
134+
}
135+
136+
handle_signal() {
137+
local signal_name="$1"
138+
local signal_code=1
139+
case "$signal_name" in
140+
INT) signal_code=130 ;;
141+
TERM) signal_code=143 ;;
142+
esac
143+
echo "[cleanup] Received ${signal_name}; requesting graceful shutdown..."
144+
exit "$signal_code"
145+
}
146+
147+
trap 'handle_signal INT' INT
148+
trap 'handle_signal TERM' TERM
149+
trap 'handle_exit' EXIT
150+
69151
# ── Setup ─────────────────────────────────────────────────────────────────────
70152

71153
kubectl create namespace "$TEST_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
72154

73-
OPERATOR_POD=$(get_operator_pid)
155+
validate_positive_integer "RETRY_DELAY_SECONDS" "$RETRY_DELAY_SECONDS"
156+
echo "Retry delay: ${RETRY_DELAY_SECONDS}s"
157+
158+
OPERATOR_POD=$(get_operator_pid_with_retry 5)
74159
if [[ -z "$OPERATOR_POD" ]]; then
75160
echo "ERROR: No stellar-operator pod found in namespace $OPERATOR_NAMESPACE"
76161
exit 1
77162
fi
78163
echo "Operator pod: $OPERATOR_POD"
164+
echo "Cleanup timeout: ${CLEANUP_TIMEOUT_SECONDS}s"
79165

80166
# Baseline — let the operator settle for one sample interval first
81167
sleep "$SAMPLE_INTERVAL"
@@ -99,7 +185,7 @@ while [[ $ELAPSED -lt $SOAK_DURATION ]]; do
99185
delete_nodes
100186

101187
# Sample memory after each wave (and on the fixed interval)
102-
OPERATOR_POD=$(get_operator_pid)
188+
OPERATOR_POD=$(get_operator_pid_with_retry 5)
103189
CURRENT_KB=$(get_rss_kb "$OPERATOR_POD")
104190
GROWTH_KB=$(( CURRENT_KB - BASELINE_KB ))
105191
NOW=$(date +%s)

0 commit comments

Comments
 (0)