-
Notifications
You must be signed in to change notification settings - Fork 34
🐛 Fix KEDA APIService race condition in nightly E2E #761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -136,6 +136,59 @@ log_error() { | |||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # APIService guard: background loop that continuously ensures the | ||||||||||||||||||||||||||||||
| # v1beta1.external.metrics.k8s.io APIService points to prometheus-adapter. | ||||||||||||||||||||||||||||||
| # On clusters with KEDA, the operator continuously reconciles the APIService | ||||||||||||||||||||||||||||||
| # back to keda-metrics-apiserver, breaking HPA scaling for WVA. | ||||||||||||||||||||||||||||||
| # This guard re-patches it every 10 seconds without modifying KEDA itself. | ||||||||||||||||||||||||||||||
| APISERVICE_GUARD_PID="" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| start_apiservice_guard() { | ||||||||||||||||||||||||||||||
| local monitoring_ns="$1" | ||||||||||||||||||||||||||||||
| log_info "Starting APIService guard (background re-patch loop every 10s)" | ||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||
| while true; do | ||||||||||||||||||||||||||||||
| sleep 10 | ||||||||||||||||||||||||||||||
| current_svc=$(kubectl get apiservice v1beta1.external.metrics.k8s.io \ | ||||||||||||||||||||||||||||||
| -o jsonpath='{.spec.service.name}' 2>/dev/null || echo "") | ||||||||||||||||||||||||||||||
| current_ns=$(kubectl get apiservice v1beta1.external.metrics.k8s.io \ | ||||||||||||||||||||||||||||||
| -o jsonpath='{.spec.service.namespace}' 2>/dev/null || echo "") | ||||||||||||||||||||||||||||||
| if [ "$current_svc" != "prometheus-adapter" ] || [ "$current_ns" != "$monitoring_ns" ]; then | ||||||||||||||||||||||||||||||
| echo "[apiservice-guard] KEDA reclaimed APIService (now: $current_svc/$current_ns), re-patching to prometheus-adapter/$monitoring_ns" | ||||||||||||||||||||||||||||||
| kubectl patch apiservice v1beta1.external.metrics.k8s.io --type=merge -p "{ | ||||||||||||||||||||||||||||||
| \"spec\": { | ||||||||||||||||||||||||||||||
| \"insecureSkipTLSVerify\": true, | ||||||||||||||||||||||||||||||
| \"service\": { | ||||||||||||||||||||||||||||||
| \"name\": \"prometheus-adapter\", | ||||||||||||||||||||||||||||||
| \"namespace\": \"$monitoring_ns\" | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }" 2>/dev/null || true | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||
| ) & | ||||||||||||||||||||||||||||||
| APISERVICE_GUARD_PID=$! | ||||||||||||||||||||||||||||||
| echo "$APISERVICE_GUARD_PID" > /tmp/apiservice-guard.pid | ||||||||||||||||||||||||||||||
| log_success "APIService guard started (PID: $APISERVICE_GUARD_PID)" | ||||||||||||||||||||||||||||||
|
Comment on lines
+170
to
+172
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stop_apiservice_guard() { | ||||||||||||||||||||||||||||||
| if [ -n "$APISERVICE_GUARD_PID" ] && kill -0 "$APISERVICE_GUARD_PID" 2>/dev/null; then | ||||||||||||||||||||||||||||||
| log_info "Stopping APIService guard (PID: $APISERVICE_GUARD_PID)" | ||||||||||||||||||||||||||||||
| kill "$APISERVICE_GUARD_PID" 2>/dev/null || true | ||||||||||||||||||||||||||||||
| wait "$APISERVICE_GUARD_PID" 2>/dev/null || true | ||||||||||||||||||||||||||||||
| elif [ -f /tmp/apiservice-guard.pid ]; then | ||||||||||||||||||||||||||||||
| local pid | ||||||||||||||||||||||||||||||
| pid=$(cat /tmp/apiservice-guard.pid) | ||||||||||||||||||||||||||||||
| if kill -0 "$pid" 2>/dev/null; then | ||||||||||||||||||||||||||||||
| log_info "Stopping APIService guard (PID: $pid from pidfile)" | ||||||||||||||||||||||||||||||
| kill "$pid" 2>/dev/null || true | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
| rm -f /tmp/apiservice-guard.pid | ||||||||||||||||||||||||||||||
| APISERVICE_GUARD_PID="" | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| print_help() { | ||||||||||||||||||||||||||||||
| cat <<EOF | ||||||||||||||||||||||||||||||
| Usage: $(basename "$0") [OPTIONS] | ||||||||||||||||||||||||||||||
|
|
@@ -1019,6 +1072,12 @@ deploy_prometheus_adapter() { | |||||||||||||||||||||||||||||
| }" && log_success "APIService patched to use Prometheus Adapter" \ | ||||||||||||||||||||||||||||||
| || log_warning "Failed to patch external.metrics.k8s.io APIService — HPA may not work" | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Start background guard to prevent KEDA from reclaiming the APIService. | ||||||||||||||||||||||||||||||
| # KEDA's operator continuously reconciles the APIService back to its own | ||||||||||||||||||||||||||||||
| # metrics server within ~2 minutes of any patch. The guard re-patches it | ||||||||||||||||||||||||||||||
| # every 10 seconds without modifying KEDA itself. | ||||||||||||||||||||||||||||||
| start_apiservice_guard "$MONITORING_NAMESPACE" | ||||||||||||||||||||||||||||||
|
Comment on lines
1074
to
+1080
|
||||||||||||||||||||||||||||||
| fi | |
| # Start background guard to prevent KEDA from reclaiming the APIService. | |
| # KEDA's operator continuously reconciles the APIService back to its own | |
| # metrics server within ~2 minutes of any patch. The guard re-patches it | |
| # every 10 seconds without modifying KEDA itself. | |
| start_apiservice_guard "$MONITORING_NAMESPACE" | |
| # Start background guard to prevent KEDA from reclaiming the APIService. | |
| # KEDA's operator continuously reconciles the APIService back to its own | |
| # metrics server within ~2 minutes of any patch. The guard re-patches it | |
| # every 10 seconds without modifying KEDA itself. | |
| start_apiservice_guard "$MONITORING_NAMESPACE" | |
| fi |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start_apiservice_guard is started here, but the normal deployment path never calls stop_apiservice_guard before main exits. In a non-interactive script, this background loop can outlive the script and continue patching the cluster unexpectedly (and may interfere with later CI cleanup that deletes the adapter/namespace). Consider giving the guard an explicit lifecycle (e.g., start only for E2E runs and add a corresponding stop action invoked by CI cleanup, or stop it automatically on script exit when persistence isn’t required).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This log line hard-codes “KEDA reclaimed APIService”, but the condition triggers for any mismatch (e.g., a different external-metrics provider or an empty service/namespace when the APIService is temporarily unavailable). Consider making the message provider-agnostic (e.g., “APIService drift detected”) and include the observed service/namespace without attributing it to KEDA unless KEDA is explicitly detected.