Prow Job Analysis
Analyze failures in OpenShift Prow CI jobs. Identify the job type, inspect artifacts, classify
the failure, and route to the specialized reference for deep analysis.
Input Format
The user will provide:
Prow job URL (required) — Prow UI or gcsweb URL
https://prow.ci.openshift.org/view/gs/test-platform-results/logs/<job>/<build_id>
https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/...
Test name (optional) — specific failed test to focus on
Flags (optional):
--backends <list> — focus disruption analysis on specific backends
Prerequisites
- Python 3.7+:
which python3
- jq:
which jq
- gcloud CLI (recommended, not required):
which gcloud — fastest access to the
public bucket (no auth required). Without it, every artifact operation works over
plain HTTPS: prow_job_artifact_search.py
(stdlib-only list/search/fetch) or curl against
https://storage.googleapis.com/test-platform-results/....
Investigation Workflow
Step 1: Parse URL and Extract Metadata
- Find
test-platform-results/ in the URL and extract the bucket path
- Extract
build_id — pattern (\d{10,}) in the path
- Construct GCS base:
gs://test-platform-results/{bucket-path}/
Step 2: Fetch prowjob.json
Use the fetch-prowjob-json skill to get job metadata. Extract:
- Job name from
.spec.job
- Target from
--target= in ci-operator args
- Job state from
.status.state
- Refs (org, repo, PR number) from
.spec.refs
Step 3: Classify Job Type from Name
Parse the job name to determine the environment and expected failure modes:
| Pattern in Name |
Job Type |
Key Implications |
upgrade |
Upgrade job |
Installs first, then upgrades — see upgrade reference |
metal, baremetal |
Bare metal |
Uses dev-scripts + Metal3/Ironic — see metal install reference |
hypershift |
HyperShift |
Hosted control planes — see hypershift reference |
fips |
FIPS-enabled |
Watch for crypto/TLS errors |
ipv6, dualstack |
IPv6/dualstack |
Often disconnected, uses mirror registry |
single-node, sno |
Single-node |
Resource exhaustion more likely |
aggregated- prefix |
Aggregated |
Statistical analysis of multiple runs — see aggregated reference |
aws, gcp, azure |
Cloud platform |
Platform-specific errors — see cloud provider reference |
techpreview |
Tech preview |
Feature gates enabled, features may be unstable |
rhcos9, rhcos10, rhcos9_10, rt |
RHCOS variant / RT kernel |
OS variant pinned or heterogeneous; OS-level differences (kernel/systemd/SELinux) — see operating system changes reference |
Step 4: Download Key Artifacts
mkdir -p .work/prow-job-analysis/{build_id}/logs
# Build log (always)
gcloud storage cp gs://test-platform-results/{bucket-path}/build-log.txt \
.work/prow-job-analysis/{build_id}/logs/ --no-user-output-enabled
# JUnit XML (always — identifies failed tests/steps)
gcloud storage ls "gs://test-platform-results/{bucket-path}/artifacts/**/junit*.xml" 2>/dev/null
# Node journals (always, when the job created a cluster) — required input for the
# Step 5 OS-layer check. Gzip-compressed WITHOUT a .gz extension: zcat/zgrep only.
gcloud storage cp -r \
"gs://test-platform-results/{bucket-path}/artifacts/{target}/gather-extra/artifacts/nodes" \
.work/prow-job-analysis/{build_id}/ --no-user-output-enabled 2>/dev/null || true
Step 5: Classify Failure and Route to Reference
Examine the build log and JUnit results to classify the failure, then consult the
appropriate reference file for detailed analysis procedures.
OS-layer evidence check (mandatory for every job, before routing)
Operating-system (RHCOS) layer breakage frequently masquerades as an unrelated product
failure: a single RHCOS bump swaps the kernel, cri-o, systemd, NetworkManager, and SELinux
policy across the whole cluster at once, so the real cause surfaces as a symptom in some
other domain. Before selecting a row from the routing table, complete BOTH steps:
1. Compare runtime versions across boots in the node journals (downloaded in
Step 4; gzip-compressed without a .gz extension — plain grep silently matches
nothing, use zcat/zgrep):
# Runtime versions per boot. End-of-run snapshots (oc_cmds/nodes, nodes.json)
# show only the final version; changes within the run are visible only here.
zgrep -hE "Starting CRI-O, version|Container runtime initialized" \
.work/prow-job-analysis/{build_id}/nodes/*/journal | sort | uniq -c
2. Scan the build log, JUnit, oc_cmds (node / clusteroperator status),
MachineConfig data, and the journals for these signals:
NetworkPluginNotReady, or a missing CNI config (/etc/cni/net.d empty / no CNI plugin)
- A
ContainerRuntimeVersion change on nodes (cri-o version bump between runs)
- A MachineConfigDaemon (MCD) rendered-config diff touching
passwd, files, or units
- Multiple nodes going
NotReady after a reboot
CreateContainerError, RunContainerError, or OCI runtime errors (crun / runc)
- Kernel
panic, BUG, Oops, or soft lockup in node journals or the serial console
avc: denied / SELinux denials
- The same failure spanning multiple unrelated jobs at a payload boundary
If step 1 shows more than one runtime version on any node, or any step-2 signal is
present, the RHCOS layer is implicated: still route via the table below using whichever
reference matches the surface symptom, but also read
operating-system-changes.md alongside it.
Never clear the OS layer from end-of-run snapshots alone.
Failure Routing Table
| Failure Signal |
Reference |
When to Use |
install should succeed fails in JUnit |
Install — General |
Install failed at config/infra/bootstrap/cluster-creation/operator-stability stage |
| Metal/baremetal job + install failure |
Install — Metal |
Bare-metal install (dev-scripts, Metal3/Ironic, libvirt) — use alongside Install — General |
| A test failed (start here) |
Flaky Test Identification |
Triage entry for any failing test: classify infra vs product regression vs flake, then route onward |
| Confirmed regression in a plain e2e test |
Test Failure Root-Cause |
Root-cause a real product regression in a plain (non-extension/install/upgrade) e2e test — e.g. [sig-network] ... should serve endpoints: test source, cluster state, originating error |
*-tests-ext extension binary error |
Test Extension Binaries |
OTE extension-binary extraction/discovery/version-skew failures — not core openshift-tests |
| Disruption events in intervals |
Disruption |
API backends stopped responding; interpret interval/timeline data (cause vs symptom vs noise) |
| Upgrade-phase failure or regression |
Upgrade |
CVO stuck, operators degraded, MCO drain/reboot stalls, or version skew during upgrade |
| HyperShift / HCP job failure |
HyperShift |
Hosted control planes — correlate management and hosted clusters |
aggregated- job failure |
Aggregated Jobs |
Statistical regression analysis across parallel child runs |
| Cloud API errors, quota, throttling |
Cloud Provider Errors |
AWS/GCP/Azure API/quota/provisioning failures before/during cluster creation |
| Node NotReady, OOM, disk pressure |
Resource Exhaustion |
CPU/memory/disk/PID/etcd exhaustion, eviction, unschedulable pods |
| DNS, OVN, registry/pull, ingress errors |
Networking |
OVN-Kubernetes/SDN, DNS, image pull/registry, load balancer/ingress, network policy |
| Container-start (cri-o), kernel panic, NetworkManager, RHCOS variant-isolated failure |
Operating System Changes |
Node OS (RHCOS) layer — cri-o/crun, kernel, systemd, NetworkManager, SELinux, or an RHCOS bump in the payload |
| Lease/quota, ci-operator, Prow infra |
CI Infrastructure |
Distinguish "product broke" from "CI config changed"; ci-operator, step registry, leases |
| Need a specific artifact file |
Artifacts |
Artifact directory structure, paths, and gcloud fetch commands |
Job-name routing (Step 3) picks which reference to read. Failure classification (install | test | upgrade | infra) follows the root cause, not the job name.
Common Artifact Paths
These are the most frequently needed artifacts. See artifacts reference for the complete directory structure.
| Path |
Description |
build-log.txt |
Top-level ci-operator log |
artifacts/{target}/openshift-e2e-test/build-log.txt |
E2E test console log |
artifacts/{target}/openshift-e2e-test/artifacts/junit/ |
JUnit XML results |
artifacts/{target}/openshift-e2e-test/artifacts/junit/e2e-timelines_spyglass_*.json |
Disruption timeline data |
artifacts/{target}/gather-extra/artifacts/oc_cmds/ |
Cluster state snapshots |
artifacts/{target}/gather-extra/artifacts/pods/ |
Pod logs by namespace |
artifacts/{target}/gather-extra/artifacts/audit_logs/ |
API server audit logs |
artifacts/{target}/gather-must-gather/artifacts/must-gather.tar |
Must-gather archive |
prowjob.json |
Job metadata and timing |
URL Formats
Both formats are accepted and interchangeable:
# Prow UI
https://prow.ci.openshift.org/view/gs/test-platform-results/logs/{job}/{build_id}
# gcsweb (direct GCS browser)
https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs/{job}/{build_id}
The GCS bucket is always test-platform-results, publicly accessible, no auth required.
Tips
- Start with build-log.txt — it shows the ci-operator orchestration and which steps failed
- JUnit XML is the source of truth for test pass/fail status
- Job name encodes environment — always parse it before diving into logs
- Check
prowjob.json for timing, payload tag, and whether the job timed out
- Upgrade jobs install first — an "upgrade" job failing at install is an install failure, not an upgrade failure
- Aggregated jobs need statistical analysis, not individual test debugging
- Use
.work/prow-job-analysis/{build_id}/ as the working directory for downloads
1---2name: prow-job-analysis3description: Use this skill when debugging a failed Prow CI job.4---56# Prow Job Analysis78Analyze failures in OpenShift Prow CI jobs. Identify the job type, inspect artifacts, classify9the failure, and route to the specialized reference for deep analysis.1011## Input Format1213The user will provide:14151. **Prow job URL** (required) — Prow UI or gcsweb URL16 - `https://prow.ci.openshift.org/view/gs/test-platform-results/logs/<job>/<build_id>`17 - `https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/...`18192. **Test name** (optional) — specific failed test to focus on20213. **Flags** (optional):22 - `--backends <list>` — focus disruption analysis on specific backends2324## Prerequisites2526- **Python 3.7+**: `which python3`27- **jq**: `which jq`28- **gcloud CLI** (recommended, not required): `which gcloud` — fastest access to the29 public bucket (no auth required). Without it, every artifact operation works over30 plain HTTPS: [prow_job_artifact_search.py](prow_job_artifact_search.py)31 (stdlib-only `list`/`search`/`fetch`) or `curl` against32 `https://storage.googleapis.com/test-platform-results/...`.3334## Investigation Workflow3536### Step 1: Parse URL and Extract Metadata37381. Find `test-platform-results/` in the URL and extract the bucket path392. Extract `build_id` — pattern `(\d{10,})` in the path403. Construct GCS base: `gs://test-platform-results/{bucket-path}/`4142### Step 2: Fetch prowjob.json4344Use the `fetch-prowjob-json` skill to get job metadata. Extract:45- **Job name** from `.spec.job`46- **Target** from `--target=` in ci-operator args47- **Job state** from `.status.state`48- **Refs** (org, repo, PR number) from `.spec.refs`4950### Step 3: Classify Job Type from Name5152Parse the job name to determine the environment and expected failure modes:5354| Pattern in Name | Job Type | Key Implications |55|-----------------|----------|------------------|56| `upgrade` | Upgrade job | Installs first, then upgrades — see [upgrade reference](references/upgrade.md) |57| `metal`, `baremetal` | Bare metal | Uses dev-scripts + Metal3/Ironic — see [metal install reference](references/install/metal.md) |58| `hypershift` | HyperShift | Hosted control planes — see [hypershift reference](references/hypershift.md) |59| `fips` | FIPS-enabled | Watch for crypto/TLS errors |60| `ipv6`, `dualstack` | IPv6/dualstack | Often disconnected, uses mirror registry |61| `single-node`, `sno` | Single-node | Resource exhaustion more likely |62| `aggregated-` prefix | Aggregated | Statistical analysis of multiple runs — see [aggregated reference](references/aggregated.md) |63| `aws`, `gcp`, `azure` | Cloud platform | Platform-specific errors — see [cloud provider reference](references/cloud-provider-errors.md) |64| `techpreview` | Tech preview | Feature gates enabled, features may be unstable |65| `rhcos9`, `rhcos10`, `rhcos9_10`, `rt` | RHCOS variant / RT kernel | OS variant pinned or heterogeneous; OS-level differences (kernel/systemd/SELinux) — see [operating system changes reference](references/operating-system-changes.md) |6667### Step 4: Download Key Artifacts6869```bash70mkdir -p .work/prow-job-analysis/{build_id}/logs7172# Build log (always)73gcloud storage cp gs://test-platform-results/{bucket-path}/build-log.txt \74 .work/prow-job-analysis/{build_id}/logs/ --no-user-output-enabled7576# JUnit XML (always — identifies failed tests/steps)77gcloud storage ls "gs://test-platform-results/{bucket-path}/artifacts/**/junit*.xml" 2>/dev/null7879# Node journals (always, when the job created a cluster) — required input for the80# Step 5 OS-layer check. Gzip-compressed WITHOUT a .gz extension: zcat/zgrep only.81gcloud storage cp -r \82 "gs://test-platform-results/{bucket-path}/artifacts/{target}/gather-extra/artifacts/nodes" \83 .work/prow-job-analysis/{build_id}/ --no-user-output-enabled 2>/dev/null || true84```8586### Step 5: Classify Failure and Route to Reference8788Examine the build log and JUnit results to classify the failure, then consult the89appropriate reference file for detailed analysis procedures.9091#### OS-layer evidence check (mandatory for every job, before routing)9293Operating-system (RHCOS) layer breakage frequently masquerades as an unrelated product94failure: a single RHCOS bump swaps the kernel, cri-o, systemd, NetworkManager, and SELinux95policy across the whole cluster at once, so the real cause surfaces as a symptom in some96other domain. Before selecting a row from the routing table, complete BOTH steps:9798**1. Compare runtime versions across boots in the node journals** (downloaded in99Step 4; gzip-compressed **without** a `.gz` extension — plain `grep` silently matches100nothing, use `zcat`/`zgrep`):101102```bash103# Runtime versions per boot. End-of-run snapshots (oc_cmds/nodes, nodes.json)104# show only the final version; changes within the run are visible only here.105zgrep -hE "Starting CRI-O, version|Container runtime initialized" \106 .work/prow-job-analysis/{build_id}/nodes/*/journal | sort | uniq -c107```108109**2. Scan the build log, JUnit, `oc_cmds` (node / clusteroperator status),110MachineConfig data, and the journals for these signals:**111112- `NetworkPluginNotReady`, or a missing CNI config (`/etc/cni/net.d` empty / no CNI plugin)113- A `ContainerRuntimeVersion` change on nodes (cri-o version bump between runs)114- A MachineConfigDaemon (MCD) rendered-config diff touching `passwd`, `files`, or `units`115- Multiple nodes going `NotReady` after a reboot116- `CreateContainerError`, `RunContainerError`, or OCI runtime errors (`crun` / `runc`)117- Kernel `panic`, `BUG`, `Oops`, or `soft lockup` in node journals or the serial console118- `avc: denied` / SELinux denials119- The same failure spanning multiple unrelated jobs at a payload boundary120121If step 1 shows more than one runtime version on any node, or any step-2 signal is122present, the RHCOS layer is implicated: still route via the table below using whichever123reference matches the surface symptom, but **also** read124[operating-system-changes.md](references/operating-system-changes.md) alongside it.125Never clear the OS layer from end-of-run snapshots alone.126127## Failure Routing Table128129| Failure Signal | Reference | When to Use |130|----------------|-----------|-------------|131| `install should succeed` fails in JUnit | [Install — General](references/install/general.md) | Install failed at config/infra/bootstrap/cluster-creation/operator-stability stage |132| Metal/baremetal job + install failure | [Install — Metal](references/install/metal.md) | Bare-metal install (dev-scripts, Metal3/Ironic, libvirt) — use alongside Install — General |133| A test failed (start here) | [Flaky Test Identification](references/flaky-test-identification.md) | Triage entry for any failing test: classify infra vs product regression vs flake, then route onward |134| Confirmed regression in a plain e2e test | [Test Failure Root-Cause](references/test-failure.md) | Root-cause a real product regression in a plain (non-extension/install/upgrade) e2e test — e.g. `[sig-network] ... should serve endpoints`: test source, cluster state, originating error |135| `*-tests-ext` extension binary error | [Test Extension Binaries](references/test-extension-binaries.md) | OTE extension-binary extraction/discovery/version-skew failures — not core `openshift-tests` |136| Disruption events in intervals | [Disruption](references/disruption.md) | API backends stopped responding; interpret interval/timeline data (cause vs symptom vs noise) |137| Upgrade-phase failure or regression | [Upgrade](references/upgrade.md) | CVO stuck, operators degraded, MCO drain/reboot stalls, or version skew during upgrade |138| HyperShift / HCP job failure | [HyperShift](references/hypershift.md) | Hosted control planes — correlate management and hosted clusters |139| `aggregated-` job failure | [Aggregated Jobs](references/aggregated.md) | Statistical regression analysis across parallel child runs |140| Cloud API errors, quota, throttling | [Cloud Provider Errors](references/cloud-provider-errors.md) | AWS/GCP/Azure API/quota/provisioning failures before/during cluster creation |141| Node NotReady, OOM, disk pressure | [Resource Exhaustion](references/resource-exhaustion.md) | CPU/memory/disk/PID/etcd exhaustion, eviction, unschedulable pods |142| DNS, OVN, registry/pull, ingress errors | [Networking](references/networking.md) | OVN-Kubernetes/SDN, DNS, image pull/registry, load balancer/ingress, network policy |143| Container-start (cri-o), kernel panic, NetworkManager, RHCOS variant-isolated failure | [Operating System Changes](references/operating-system-changes.md) | Node OS (RHCOS) layer — cri-o/crun, kernel, systemd, NetworkManager, SELinux, or an RHCOS bump in the payload |144| Lease/quota, ci-operator, Prow infra | [CI Infrastructure](references/ci-infrastructure-changes.md) | Distinguish "product broke" from "CI config changed"; ci-operator, step registry, leases |145| Need a specific artifact file | [Artifacts](references/artifacts.md) | Artifact directory structure, paths, and gcloud fetch commands |146147Job-name routing (Step 3) picks which reference to read. Failure classification (`install` | `test` | `upgrade` | `infra`) follows the root cause, not the job name.148149## Common Artifact Paths150151These are the most frequently needed artifacts. See [artifacts reference](references/artifacts.md) for the complete directory structure.152153| Path | Description |154|------|-------------|155| `build-log.txt` | Top-level ci-operator log |156| `artifacts/{target}/openshift-e2e-test/build-log.txt` | E2E test console log |157| `artifacts/{target}/openshift-e2e-test/artifacts/junit/` | JUnit XML results |158| `artifacts/{target}/openshift-e2e-test/artifacts/junit/e2e-timelines_spyglass_*.json` | Disruption timeline data |159| `artifacts/{target}/gather-extra/artifacts/oc_cmds/` | Cluster state snapshots |160| `artifacts/{target}/gather-extra/artifacts/pods/` | Pod logs by namespace |161| `artifacts/{target}/gather-extra/artifacts/audit_logs/` | API server audit logs |162| `artifacts/{target}/gather-must-gather/artifacts/must-gather.tar` | Must-gather archive |163| `prowjob.json` | Job metadata and timing |164165## URL Formats166167Both formats are accepted and interchangeable:168169```text170# Prow UI171https://prow.ci.openshift.org/view/gs/test-platform-results/logs/{job}/{build_id}172173# gcsweb (direct GCS browser)174https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs/{job}/{build_id}175```176177The GCS bucket is always `test-platform-results`, publicly accessible, no auth required.178179## Tips180181- **Start with build-log.txt** — it shows the ci-operator orchestration and which steps failed182- **JUnit XML is the source of truth** for test pass/fail status183- **Job name encodes environment** — always parse it before diving into logs184- **Check `prowjob.json`** for timing, payload tag, and whether the job timed out185- **Upgrade jobs install first** — an "upgrade" job failing at install is an install failure, not an upgrade failure186- **Aggregated jobs** need statistical analysis, not individual test debugging187- **Use `.work/prow-job-analysis/{build_id}/`** as the working directory for downloads