From fc1d31af8b2f27784c994aeb3b3c114facd1327a Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Thu, 17 Sep 2026 17:03:59 +0530 Subject: [PATCH 1/4] Build an obs-tests-runner image with Claude Code CLI for openshift-logging-e2e-tests Adds a second image build, layered on the existing openshift-logging-e2e-tests image, that installs the Claude Code CLI and tags the result obs-tests-runner. It is promoted alongside the existing image (promotion.to applies to every image in this config), landing at logging/obs-tests-runner:main. This lets consumers of openshift-logging-e2e-tests (cluster-logging-operator and loki's e2e-prgate jobs, see openshift/release#85177) run the openshift-observability-qe-agent post-step by importing this one pre-built image via base_images, instead of each consumer installing the CLI itself in a duplicated Dockerfile layer. See ci-operator/step-registry/openshift-observability/qe-agent/README.md ("Tag your test runner image as obs-tests-runner") for why the step requires this specific tag name. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Dp1Dxge5p9yinA3yJggfFt --- ...enshift-eng-openshift-logging-e2e-tests-main.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml index 6ce067022c431..53057f6bd501a 100644 --- a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml +++ b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml @@ -12,6 +12,18 @@ images: install -m 0755 bin/openshift-logging-e2e-tests-tests-ext /usr/local/bin/openshift-logging-e2e-tests-tests-ext from: src to: openshift-logging-e2e-tests + - dockerfile_literal: | + FROM openshift-logging-e2e-tests + RUN install -d -m 0755 /etc/pki/rpm-gpg \ + && curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \ + -o /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ + && rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ + && echo -e "[claude-code]\nname=Claude Code\nbaseurl=https://downloads.claude.ai/claude-code/rpm/stable/\$basearch\nenabled=1\ngpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code" \ + > /etc/yum.repos.d/claude-code.repo \ + && dnf install -y claude-code \ + && dnf clean all + from: openshift-logging-e2e-tests + to: obs-tests-runner promotion: to: - namespace: logging From 4592b745e785fab770600413336551537156f065 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Thu, 17 Sep 2026 17:06:29 +0530 Subject: [PATCH 2/4] Merge the Claude CLI install into the single obs-tests-runner build Simplifies the previous commit: instead of building openshift-logging-e2e-tests and then a second obs-tests-runner image layered on top of it, add the Claude Code CLI install to the same dockerfile_literal and tag the one resulting image obs-tests-runner directly. One build, one promoted image, used for both the regular PRGate test step and the qe-agent post-step - matching the qe-agent README's own guidance to tag your team's test runner image itself as obs-tests-runner, rather than maintaining a separate derivative image. This renames the promoted image from logging/openshift-logging-e2e-tests:main to logging/obs-tests-runner:main. openshift/release#85177's openshift-observability-logging-e2e-tests step ref (from_image) and its cluster-logging-operator/loki base_images will be updated to match. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Dp1Dxge5p9yinA3yJggfFt --- .../openshift-eng-openshift-logging-e2e-tests-main.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml index 53057f6bd501a..0ae300a5f9b37 100644 --- a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml +++ b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml @@ -10,10 +10,6 @@ images: RUN GONOSUMDB="*" GOFLAGS="" go install github.com/go-bindata/go-bindata/v3/go-bindata@latest && \ make build && \ install -m 0755 bin/openshift-logging-e2e-tests-tests-ext /usr/local/bin/openshift-logging-e2e-tests-tests-ext - from: src - to: openshift-logging-e2e-tests - - dockerfile_literal: | - FROM openshift-logging-e2e-tests RUN install -d -m 0755 /etc/pki/rpm-gpg \ && curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \ -o /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ @@ -22,7 +18,7 @@ images: > /etc/yum.repos.d/claude-code.repo \ && dnf install -y claude-code \ && dnf clean all - from: openshift-logging-e2e-tests + from: src to: obs-tests-runner promotion: to: From 22a4f2b7ae963de30dacf3b88fe3e6f38d131a90 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Thu, 17 Sep 2026 17:21:28 +0530 Subject: [PATCH 3/4] Verify the Claude Code signing key fingerprint before importing it Addresses a CodeRabbit finding (CWE-494, Download of Code Without Integrity Check): the build downloaded the signing key over HTTPS and imported it directly with no independent check. A compromise of downloads.claude.ai could have served a replacement key, and dnf install would trust any RPM signed by it. Fetch the key to a temp path, compute its fingerprint with gpg --import-options show-only (no trust database changes), and compare it against the fingerprint observed and pinned in this commit (31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE, "Anthropic Claude Code Release Signing ") before rpm --import and before dnf install ever run. Abort the build on any mismatch. This doesn't protect against compromise of the legitimate private signing key itself, but it does mean a future key swap that isn't accompanied by a reviewed change to this pinned value fails the build instead of silently trusting whatever downloads.claude.ai serves. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Dp1Dxge5p9yinA3yJggfFt --- .../openshift-eng-openshift-logging-e2e-tests-main.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml index 0ae300a5f9b37..676202a52f09b 100644 --- a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml +++ b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml @@ -10,9 +10,14 @@ images: RUN GONOSUMDB="*" GOFLAGS="" go install github.com/go-bindata/go-bindata/v3/go-bindata@latest && \ make build && \ install -m 0755 bin/openshift-logging-e2e-tests-tests-ext /usr/local/bin/openshift-logging-e2e-tests-tests-ext - RUN install -d -m 0755 /etc/pki/rpm-gpg \ + RUN dnf install -y gnupg2 \ + && install -d -m 0755 /etc/pki/rpm-gpg \ && curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \ - -o /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ + -o /tmp/RPM-GPG-KEY-claude-code \ + && FPR=$(gpg --with-colons --import-options show-only --import /tmp/RPM-GPG-KEY-claude-code 2>/dev/null | awk -F: '/^fpr:/ {print $10; exit}') \ + && test "$FPR" = "31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE" \ + || { echo "Claude Code signing key fingerprint mismatch: got '$FPR', expected 31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE" >&2; exit 1; } \ + && mv /tmp/RPM-GPG-KEY-claude-code /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ && rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ && echo -e "[claude-code]\nname=Claude Code\nbaseurl=https://downloads.claude.ai/claude-code/rpm/stable/\$basearch\nenabled=1\ngpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code" \ > /etc/yum.repos.d/claude-code.repo \ From e30856c120091ad8cda17cd2f3c4f24897701f44 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Thu, 17 Sep 2026 17:51:12 +0530 Subject: [PATCH 4/4] Install Claude Code via its native installer, not dnf The rehearsal build failed: "No match for argument: claude-code" / "Error: Unable to find a match: claude-code". OpenShift CI build pods run dnf through an ART yum/dnf wrapper that restricts package resolution to a fixed, curated set of RHEL/OCP repos and does not pick up custom repos added under /etc/yum.repos.d/ - so the claude-code.repo file and its GPG-verified signing key were never actually reachable by dnf, even though both were set up correctly. Switch to Claude Code's native install script instead, which downloads a self-contained binary directly (bypassing dnf/the ART wrapper entirely) and verifies it against a SHA256 checksum published in a signed manifest - the same integrity guarantee the GPG key pinning was providing, without depending on a package repo. This is also the same pattern already used in this repo for kubectl (see cluster-logging-operator's Dockerfile: curl, chmod, install to a bin dir - no package manager). The installer places the binary under $HOME/.local/bin; explicitly install it to /usr/local/bin so it's on PATH regardless of which UID/HOME the consuming step runs as, and run `claude --version` as a build-time smoke test. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Dp1Dxge5p9yinA3yJggfFt --- ...ift-eng-openshift-logging-e2e-tests-main.yaml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml index 676202a52f09b..7dfbe4a86b851 100644 --- a/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml +++ b/ci-operator/config/openshift-eng/openshift-logging-e2e-tests/openshift-eng-openshift-logging-e2e-tests-main.yaml @@ -10,19 +10,9 @@ images: RUN GONOSUMDB="*" GOFLAGS="" go install github.com/go-bindata/go-bindata/v3/go-bindata@latest && \ make build && \ install -m 0755 bin/openshift-logging-e2e-tests-tests-ext /usr/local/bin/openshift-logging-e2e-tests-tests-ext - RUN dnf install -y gnupg2 \ - && install -d -m 0755 /etc/pki/rpm-gpg \ - && curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \ - -o /tmp/RPM-GPG-KEY-claude-code \ - && FPR=$(gpg --with-colons --import-options show-only --import /tmp/RPM-GPG-KEY-claude-code 2>/dev/null | awk -F: '/^fpr:/ {print $10; exit}') \ - && test "$FPR" = "31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE" \ - || { echo "Claude Code signing key fingerprint mismatch: got '$FPR', expected 31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE" >&2; exit 1; } \ - && mv /tmp/RPM-GPG-KEY-claude-code /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ - && rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code \ - && echo -e "[claude-code]\nname=Claude Code\nbaseurl=https://downloads.claude.ai/claude-code/rpm/stable/\$basearch\nenabled=1\ngpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code" \ - > /etc/yum.repos.d/claude-code.repo \ - && dnf install -y claude-code \ - && dnf clean all + RUN curl -fsSL https://claude.ai/install.sh | bash \ + && install -m 0755 "$HOME/.local/bin/claude" /usr/local/bin/claude \ + && claude --version from: src to: obs-tests-runner promotion: