Skip to content

✨ Fail-fast and reap AgentRuns whose pods never start - #203

Open
ibolton336 wants to merge 2 commits into
konveyor:mainfrom
ibolton336:feature/agentrun-failfast-broken-pods
Open

✨ Fail-fast and reap AgentRuns whose pods never start#203
ibolton336 wants to merge 2 commits into
konveyor:mainfrom
ibolton336:feature/agentrun-failfast-broken-pods

Conversation

@ibolton336

Copy link
Copy Markdown
Member

Problem

An AgentRun whose sandbox pod never reaches a healthy Running state —
ImagePullBackOff, CrashLoopBackOff, InvalidImageName,
CreateContainerConfigError, or Pending because it is unschedulable — sat in
Pending forever. Nothing observed the pod-level error condition, so the run
was neither usable nor eligible for cleanup. These are the "broken agent runs
with error conditions on the pod" that pile up.

Change

The AgentRun reconciler now, on a not-yet-running pod:

  • Fails fast on fatal pod errors. Container statuses are scanned (init
    containers first, so a bad skill-loader image is caught before the agent
    container) for settled waiting reasons — ImagePullBackOff,
    CrashLoopBackOff, InvalidImageName, CreateContainerConfigError. The
    kubelet's own reason is surfaced verbatim on the Succeeded condition, so
    the reason vocabulary matches what an operator sees on the pod (aligns with
    the reporting direction of Skill-loader failures and never-starting pods never reach AgentRun.status #171). Checked before the pod-phase gate, since a
    container stuck in CrashLoopBackOff can still put the pod in Running.
  • Enforces an optional startup deadline. If the pod has not reached a
    running state within spec.startupDeadlineSeconds (or the controller default
    --agentrun-startup-deadline), the run is failed with
    StartupDeadlineExceeded. A requeue drives the deadline even when no further
    pod event arrives (e.g. a pod stuck unschedulable). Both default to disabled;
    fatal pod errors fail runs immediately regardless.

Both paths set a terminal Failed phase + CompletionTime, so the run stops
occupying an active slot and becomes eligible for TTL GC (#198).

API

  • AgentRunSpec.startupDeadlineSeconds (*int32, +optional, Minimum=0).
  • --agentrun-startup-deadline controller flag (default 0 = disabled).

Tests

envtest integration coverage:

  • pod init container stuck on ImagePullBackOff → run Failed, reason
    ImagePullBackOff, message names the container, CompletionTime set.
  • unschedulable pod past a short startupDeadlineSeconds → run Failed,
    reason StartupDeadlineExceeded.

Closes #199

🤖 Generated with Claude Code

An AgentRun whose sandbox pod never reaches a healthy Running state —
ImagePullBackOff, CrashLoopBackOff, InvalidImageName,
CreateContainerConfigError, or Pending-because-unschedulable — used to
sit in Pending forever: nothing observed the pod-level error, so the run
was neither usable nor eligible for cleanup. These are the "broken agent
runs with error conditions on the pod" that pile up.

The AgentRun reconciler now, on a not-yet-running pod:

- Fails the run immediately when a container (init containers first, so a
  bad skill-loader image is caught) is stuck on a fatal waiting reason.
  The kubelet's own reason (ImagePullBackOff, CrashLoopBackOff,
  InvalidImageName, CreateContainerConfigError) is surfaced verbatim on
  the Succeeded condition so the vocabulary matches the pod (shares the
  reporting direction of konveyor#171).
- Enforces an optional startup deadline: if the pod has not reached a
  running state within spec.startupDeadlineSeconds (or the controller
  default --agentrun-startup-deadline), the run is failed with
  StartupDeadlineExceeded. A requeue drives the deadline even when no
  further pod event arrives (e.g. a pod stuck unschedulable).

Both set a terminal Failed phase + CompletionTime, so the run stops
occupying an active slot and becomes eligible for TTL GC (konveyor#198).

Closes konveyor#199

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: ibolton336 <ibolton@redhat.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 44e2c368-8e62-46b0-9134-5681096eab9e


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ibolton336 ibolton336 changed the title ✨ Fail-fast and reap AgentRuns whose pods never start ✨ Fail-fast and reap AgentRuns whose pods never start Aug 27, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: ibolton336 <ibolton@redhat.com>

@djzager djzager left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — clean fail-fast logic with good envtest coverage. Two low-severity, non-blocking notes below.

// the container cannot be configured, or it crashes on every start. They are
// settled states — kubelet has already retried into a back-off, or the
// config is permanently wrong — so the run is failed rather than waited on.
var fatalWaitingReasons = map[string]bool{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sandbox pods use RestartPolicyNever (pod template ~L677, per the #51 comment), so a crashing container terminates rather than entering a CrashLoopBackOff waiting state — that case is handled by the Sandbox Finished/setTerminalOutcome path instead. CrashLoopBackOff here is effectively unreachable for this workload. Harmless as a guard, but a one-line note that it's defending against a future restart-policy change would help a reader who knows these pods never restart.

if pod != nil {
podPhase = string(pod.Status.Phase)
}
setRunSucceeded(run, metav1.ConditionUnknown, "PodNotRunning",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this branch reports the bare podPhase, while the deadline-configured branch above reports the richer podStartupDetail(pod). The Pending message an operator sees then depends on whether a deadline happens to be set (e.g. an unschedulable pod shows unschedulable: ... in one path, just Pending in the other). Consider using podStartupDetail(pod) here too for consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fail-fast and reap runs whose pods are stuck on error conditions (ImagePullBackOff, CrashLoopBackOff, unschedulable)

2 participants