Skip to content

feat: add image field to Claw CRD for user-specified OpenClaw image (#217)#218

Open
xcoulon wants to merge 4 commits into
codeready-toolchain:masterfrom
xcoulon:crd-openclaw-version-field
Open

feat: add image field to Claw CRD for user-specified OpenClaw image (#217)#218
xcoulon wants to merge 4 commits into
codeready-toolchain:masterfrom
xcoulon:crd-openclaw-version-field

Conversation

@xcoulon

@xcoulon xcoulon commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Allow users to specify the full OpenClaw container image per Claw
instance via spec.image (default: ghcr.io/openclaw/openclaw:slim).

  • Add optional Image field to ClawSpec with kubebuilder default and
    Image print column on the Claw type
  • Replace hardcoded image references in deployment.yaml with an
    OPENCLAW_IMAGE placeholder resolved by buildKustomizedObjects()
  • Remove the images section from kustomization.yaml; image injection
    now uses the same pattern as CLAW_INSTANCE_NAME
  • Add imagePluginVersion() helper to extract the release version from
    image tags (handles slim variants: 2026.6.10-slim-arm642026.6.10,
    slim → empty/latest)
  • Change VertexPlugin in knownProviders from a hardcoded versioned
    string to a plain package name; version suffix is appended conditionally
    by requiredProviderPlugins()
  • Update GATEWAY_IMAGE in Makefile to use the static default
  • Add unit tests for imagePluginVersion(), image propagation to
    deployment containers, and plugin version extraction
  • Add e2e test verifying custom image deployment and container image
    assertions

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Xavier Coulon xcoulon@redhat.com

Summary by CodeRabbit

  • New Features

    • Added spec.image to the Claw CR (default: ghcr.io/openclaw/openclaw:slim).
    • The operator uses the selected image for all gateway-related containers (init-volume, init-config, gateway).
    • kubectl get now shows the image via a new Image column, and the resolved value is reported in status.
  • Bug Fixes

    • Provider plugin references now align with the selected gateway image tag when available (with safe handling for slim, latest, and digest-based images).
  • Tests

    • Extended controller tests and added an e2e case to verify custom-image deployment and status reporting.

…codeready-toolchain#217)

Allow users to specify the full OpenClaw container image per Claw
instance via `spec.image` (default: `ghcr.io/openclaw/openclaw:slim`).

- Add optional `Image` field to `ClawSpec` with kubebuilder default and
  `Image` print column on the `Claw` type
- Replace hardcoded image references in `deployment.yaml` with an
  `OPENCLAW_IMAGE` placeholder resolved by `buildKustomizedObjects()`
- Remove the `images` section from `kustomization.yaml`; image injection
  now uses the same pattern as `CLAW_INSTANCE_NAME`
- Add `imagePluginVersion()` helper to extract the release version from
  image tags (handles `slim` variants: `2026.6.10-slim-arm64` → `2026.6.10`,
  `slim` → empty/latest)
- Change `VertexPlugin` in `knownProviders` from a hardcoded versioned
  string to a plain package name; version suffix is appended conditionally
  by `requiredProviderPlugins()`
- Update `GATEWAY_IMAGE` in `Makefile` to use the static default
- Add unit tests for `imagePluginVersion()`, image propagation to
  deployment containers, and plugin version extraction
- Add e2e test verifying custom image deployment and container image
  assertions

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Walkthrough

Adds spec.image and status.image to Claw, uses the resolved image in rendered gateway manifests, derives Vertex plugin versions from image tags, and updates tests plus OpenSpec documents for the new flow.

Changes

OpenClaw image field and propagation

Layer / File(s) Summary
API, CRD, and spec docs
api/v1alpha1/claw_types.go, config/crd/bases/claw.sandbox.redhat.com_claws.yaml, openspec/changes/archive/2026-06-26-crd-openclaw-version-field/*, openspec/specs/*
Adds spec.image and status.image, updates the CRD printer column and schema default, and records the behavior in OpenSpec documents.
Manifest injection and status rendering
Makefile, internal/assets/manifests/claw/{deployment.yaml,kustomization.yaml}, internal/controller/claw_resource_controller.go, internal/controller/claw_status.go
Switches the rendered gateway deployment to OPENCLAW_IMAGE, removes the kustomize image override, sets the default image constant, and writes the resolved image into status.
Plugin version derivation
go.mod, internal/controller/{claw_providers.go,claw_plugins.go}, .github/workflows/lint.yml, internal/controller/{claw_providers_test.go,claw_plugins_test.go}
Adds image reference parsing, updates the Anthropic Vertex plugin spec, and adjusts lint and unit tests for versioned and unversioned cases.
Controller and e2e validation
internal/controller/claw_deployment_test.go, test/e2e/e2e_test.go
Adds coverage for default image handling, custom image propagation into workload containers, and the end-to-end custom-image reconciliation path.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature, test, dependencies

Suggested reviewers

  • alexeykazakov
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a user-configurable image field to the Claw CRD.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot added feature New feature or request test Work that adds, fixes, or maintains automated tests or coverage (unit, integration, e2e, flakiness) labels Jun 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/crd/bases/claw.sandbox.redhat.com_claws.yaml`:
- Around line 411-417: Add the missing status schema for the resolved image so
the CRD will not prune it when the controller sets status.image. Update the Claw
CRD schema to include a matching status.image field alongside the existing
spec.image field, and keep the definition consistent with the image
type/description used elsewhere. Use the existing image-related schema entries
in the Claw CRD as the reference point for where to add the status field.

In `@internal/controller/claw_deployment_test.go`:
- Around line 2006-2019: The propagation test can pass even when expected
containers are missing because the current loops only assert on matching names.
Update the checks around deployment.Spec.Template.Spec.InitContainers and
deployment.Spec.Template.Spec.Containers to track whether init-volume,
ClawInitConfigContainerName, and ClawGatewayContainerName were actually
observed, then assert that every expected container name was found and validated
in addition to comparing its Image to customImage.

In `@openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md`:
- Around line 41-55: The image-tag extraction for Vertex plugin injection is too
naive and will break valid OCI references, especially registry ports and
digest-pinned images. Update the logic around `spec.image` and `VertexPlugin` in
`claw_providers.go` to use an OCI-aware image parser instead of splitting on `:`
or `@`, and derive the plugin version from the parsed tag only. If digest
references cannot be mapped safely to a release tag, either reject them for
Vertex injection or disable auto-injection for that case rather than falling
back to `latest`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: a697e13c-04d2-49b8-a444-44d7c68099df

📥 Commits

Reviewing files that changed from the base of the PR and between f226f45 and ae34ee1.

📒 Files selected for processing (20)
  • Makefile
  • api/v1alpha1/claw_types.go
  • config/crd/bases/claw.sandbox.redhat.com_claws.yaml
  • internal/assets/manifests/claw/deployment.yaml
  • internal/assets/manifests/claw/kustomization.yaml
  • internal/controller/claw_deployment_test.go
  • internal/controller/claw_plugins.go
  • internal/controller/claw_plugins_test.go
  • internal/controller/claw_providers.go
  • internal/controller/claw_providers_test.go
  • internal/controller/claw_resource_controller.go
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/.openspec.yaml
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/proposal.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/claw-crd/spec.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/version-field/spec.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/tasks.md
  • openspec/specs/claw-crd/spec.md
  • openspec/specs/version-field/spec.md
  • test/e2e/e2e_test.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • codeready-toolchain/api (manual)
  • codeready-toolchain/toolchain-common (manual)
  • codeready-toolchain/host-operator (manual)
  • codeready-toolchain/toolchain-e2e (manual)
💤 Files with no reviewable changes (1)
  • internal/assets/manifests/claw/kustomization.yaml
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: E2E Tests
  • GitHub Check: Unit Tests
⚠️ CI failures not shown inline (2)

GitHub Actions: Lint / 0_Lint.txt: feat: add image field to Claw CRD for user-specified OpenClaw image (#217)

Conclusion: failure

View job details

##[group]Run KUSTOMIZE=internal/assets/manifests/claw/kustomization.yaml
 �[36;1mKUSTOMIZE=internal/assets/manifests/claw/kustomization.yaml�[0m
 �[36;1mIMAGE_TAG=$(yq '.images[] | select(.name == "ghcr.io/openclaw/openclaw") | .newTag' "$KUSTOMIZE")�[0m
 �[36;1mPLUGIN_VERSION=$(sed -n 's/.*anthropic-vertex-provider@\([^"]*\)".*/\1/p' internal/controller/claw_providers.go)�[0m
 �[36;1mif [ -z "$IMAGE_TAG" ] || [ -z "$PLUGIN_VERSION" ]; then�[0m
 �[36;1m  echo "::error::Failed to extract versions (IMAGE_TAG=${IMAGE_TAG:-<empty>}, PLUGIN_VERSION=${PLUGIN_VERSION:-<empty>})"�[0m

GitHub Actions: Lint / Lint: feat: add image field to Claw CRD for user-specified OpenClaw image (#217)

Conclusion: failure

View job details

##[group]Run KUSTOMIZE=internal/assets/manifests/claw/kustomization.yaml
 �[36;1mKUSTOMIZE=internal/assets/manifests/claw/kustomization.yaml�[0m
 �[36;1mIMAGE_TAG=$(yq '.images[] | select(.name == "ghcr.io/openclaw/openclaw") | .newTag' "$KUSTOMIZE")�[0m
 �[36;1mPLUGIN_VERSION=$(sed -n 's/.*anthropic-vertex-provider@\([^"]*\)".*/\1/p' internal/controller/claw_providers.go)�[0m
 �[36;1mif [ -z "$IMAGE_TAG" ] || [ -z "$PLUGIN_VERSION" ]; then�[0m
 �[36;1m  echo "::error::Failed to extract versions (IMAGE_TAG=${IMAGE_TAG:-<empty>}, PLUGIN_VERSION=${PLUGIN_VERSION:-<empty>})"�[0m
🧰 Additional context used
📓 Path-based instructions (7)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • openspec/specs/claw-crd/spec.md
  • Makefile
  • openspec/specs/version-field/spec.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/proposal.md
  • internal/assets/manifests/claw/deployment.yaml
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/version-field/spec.md
  • internal/controller/claw_providers.go
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/tasks.md
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md
  • api/v1alpha1/claw_types.go
  • internal/controller/claw_resource_controller.go
  • test/e2e/e2e_test.go
  • internal/controller/claw_providers_test.go
  • config/crd/bases/claw.sandbox.redhat.com_claws.yaml
  • internal/controller/claw_deployment_test.go
  • openspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/claw-crd/spec.md
  • internal/controller/claw_plugins.go
  • internal/controller/claw_plugins_test.go
internal/assets/manifests/**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

internal/assets/manifests/**/*.{yaml,yml}: Configure pod security with non-root user (uid 65532), restricted seccomp profile, and drop all Linux capabilities
Set readOnlyRootFilesystem: true on proxy and wait-for-proxy containers, but not on init-config or gateway containers which require writable paths for Node.js and AI tools

Files:

  • internal/assets/manifests/claw/deployment.yaml
internal/controller/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Set owner references on all created resources via controllerutil.SetControllerReference

Files:

  • internal/controller/claw_providers.go
  • internal/controller/claw_resource_controller.go
  • internal/controller/claw_providers_test.go
  • internal/controller/claw_deployment_test.go
  • internal/controller/claw_plugins.go
  • internal/controller/claw_plugins_test.go
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Include Go license header from hack/boilerplate.go.txt template in all Go source files
Run golangci-lint via make lint and use make lint-fix for auto-fixing, with .golangci.yml configuration including lll and dupl linters enabled
Use make fmt (go fmt) and make vet (go vet) for code formatting and vetting before committing

Files:

  • internal/controller/claw_providers.go
  • api/v1alpha1/claw_types.go
  • internal/controller/claw_resource_controller.go
  • test/e2e/e2e_test.go
  • internal/controller/claw_providers_test.go
  • internal/controller/claw_deployment_test.go
  • internal/controller/claw_plugins.go
  • internal/controller/claw_plugins_test.go
api/v1alpha1/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Modify API types in api/v1alpha1/ and run make manifests to regenerate CRD YAML in config/crd/bases/ and make generate to regenerate zz_generated.deepcopy.go

Files:

  • api/v1alpha1/claw_types.go
internal/controller/**/*_controller.go

📄 CodeRabbit inference engine (CLAUDE.md)

Apply RBAC authorization via // +kubebuilder:rbac:... markers on reconciler methods in the controller

Files:

  • internal/controller/claw_resource_controller.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*_test.go: Use require assertion from testify for fatal setup errors in tests, and assert for value comparisons
Structure tests with Test* function names, use t.Run() for subtests, t.Cleanup() for cleanup, and implement table-driven test patterns
Use waitFor(t, timeout, interval, condition, message) helper for async test assertions with 10s timeout and 250ms poll interval
Create separate test files per resource type (e.g., claw_configmap_test.go, claw_credentials_test.go)

Files:

  • test/e2e/e2e_test.go
  • internal/controller/claw_providers_test.go
  • internal/controller/claw_deployment_test.go
  • internal/controller/claw_plugins_test.go
🧠 Learnings (1)
📚 Learning: 2026-05-27T15:29:16.197Z
Learnt from: alexeykazakov
Repo: codeready-toolchain/claw-operator PR: 149
File: internal/assets/manifests/claw/kustomization.yaml:19-19
Timestamp: 2026-05-27T15:29:16.197Z
Learning: In this repo’s claw Kustomize YAML manifests, when an image tag for ghcr.io (specifically ghcr.io/openclaw/openclaw) is being verified via `crane manifest` in an unauthenticated environment, a `not found` result can be caused by missing registry authentication (false positives). Do not flag the tag as missing based solely on that `crane manifest` output; require an authenticated verification step (e.g., GHCR login/token) or another independent check before concluding the tag is absent.

Applied to files:

  • internal/assets/manifests/claw/deployment.yaml
🔇 Additional comments (12)
openspec/changes/archive/2026-06-26-crd-openclaw-version-field/proposal.md (1)

1-28: LGTM!

openspec/changes/archive/2026-06-26-crd-openclaw-version-field/tasks.md (1)

1-32: LGTM!

config/crd/bases/claw.sandbox.redhat.com_claws.yaml (1)

18-20: LGTM!

Makefile (1)

89-89: LGTM!

internal/controller/claw_resource_controller.go (1)

67-67: LGTM!

Also applies to: 960-964

internal/assets/manifests/claw/deployment.yaml (1)

51-51: LGTM!

Also applies to: 74-74, 137-137

api/v1alpha1/claw_types.go (1)

496-502: LGTM!

Also applies to: 600-600

test/e2e/e2e_test.go (1)

88-106: LGTM!

Also applies to: 1859-1925

internal/controller/claw_providers.go (1)

105-105: LGTM!

Also applies to: 150-176

internal/controller/claw_plugins.go (1)

74-97: LGTM!

internal/controller/claw_providers_test.go (1)

735-758: LGTM!

internal/controller/claw_plugins_test.go (1)

34-34: LGTM!

Also applies to: 71-71, 413-507, 514-568

Comment thread config/crd/bases/claw.sandbox.redhat.com_claws.yaml
Comment on lines +2006 to +2019
gatewayContainerNames := []string{"init-volume", ClawInitConfigContainerName, ClawGatewayContainerName}
for _, ic := range deployment.Spec.Template.Spec.InitContainers {
for _, name := range gatewayContainerNames {
if ic.Name == name {
assert.Equal(t, customImage, ic.Image,
"init container %s should use custom image", name)
}
}
}
for _, c := range deployment.Spec.Template.Spec.Containers {
if c.Name == ClawGatewayContainerName {
assert.Equal(t, customImage, c.Image, "gateway container should use custom image")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assertions can pass vacuously if container names don't match.

assert.Equal only runs when a container name matches an expected name. If init-volume, init-config, or gateway were renamed or dropped from the rendered Deployment, the loops would simply find no matches and the test would still pass — defeating the purpose of the propagation check. Track that each expected container was actually observed.

💚 Track observed containers and assert all were checked
-		gatewayContainerNames := []string{"init-volume", ClawInitConfigContainerName, ClawGatewayContainerName}
-		for _, ic := range deployment.Spec.Template.Spec.InitContainers {
-			for _, name := range gatewayContainerNames {
-				if ic.Name == name {
-					assert.Equal(t, customImage, ic.Image,
-						"init container %s should use custom image", name)
-				}
-			}
-		}
-		for _, c := range deployment.Spec.Template.Spec.Containers {
-			if c.Name == ClawGatewayContainerName {
-				assert.Equal(t, customImage, c.Image, "gateway container should use custom image")
-			}
-		}
+		seen := map[string]bool{}
+		expectedInit := []string{"init-volume", ClawInitConfigContainerName}
+		for _, ic := range deployment.Spec.Template.Spec.InitContainers {
+			for _, name := range expectedInit {
+				if ic.Name == name {
+					seen[name] = true
+					assert.Equal(t, customImage, ic.Image,
+						"init container %s should use custom image", name)
+				}
+			}
+		}
+		for _, name := range expectedInit {
+			assert.True(t, seen[name], "expected init container %s to be present", name)
+		}
+		gatewayFound := false
+		for _, c := range deployment.Spec.Template.Spec.Containers {
+			if c.Name == ClawGatewayContainerName {
+				gatewayFound = true
+				assert.Equal(t, customImage, c.Image, "gateway container should use custom image")
+			}
+		}
+		assert.True(t, gatewayFound, "expected gateway container to be present")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
gatewayContainerNames := []string{"init-volume", ClawInitConfigContainerName, ClawGatewayContainerName}
for _, ic := range deployment.Spec.Template.Spec.InitContainers {
for _, name := range gatewayContainerNames {
if ic.Name == name {
assert.Equal(t, customImage, ic.Image,
"init container %s should use custom image", name)
}
}
}
for _, c := range deployment.Spec.Template.Spec.Containers {
if c.Name == ClawGatewayContainerName {
assert.Equal(t, customImage, c.Image, "gateway container should use custom image")
}
}
seen := map[string]bool{}
expectedInit := []string{"init-volume", ClawInitConfigContainerName}
for _, ic := range deployment.Spec.Template.Spec.InitContainers {
for _, name := range expectedInit {
if ic.Name == name {
seen[name] = true
assert.Equal(t, customImage, ic.Image,
"init container %s should use custom image", name)
}
}
}
for _, name := range expectedInit {
assert.True(t, seen[name], "expected init container %s to be present", name)
}
gatewayFound := false
for _, c := range deployment.Spec.Template.Spec.Containers {
if c.Name == ClawGatewayContainerName {
gatewayFound = true
assert.Equal(t, customImage, c.Image, "gateway container should use custom image")
}
}
assert.True(t, gatewayFound, "expected gateway container to be present")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/claw_deployment_test.go` around lines 2006 - 2019, The
propagation test can pass even when expected containers are missing because the
current loops only assert on matching names. Update the checks around
deployment.Spec.Template.Spec.InitContainers and
deployment.Spec.Template.Spec.Containers to track whether init-volume,
ClawInitConfigContainerName, and ClawGatewayContainerName were actually
observed, then assert that every expected container name was found and validated
in addition to comparing its Image to customImage.

Comment on lines +41 to +55
The Anthropic Vertex plugin version must match the OpenClaw release. Extract the tag from `spec.image` by splitting on `:` (or `@` for digests). Replace the hardcoded version in `VertexPlugin` in `claw_providers.go` with a format string (`@openclaw/anthropic-vertex-provider@%s`) resolved at plugin-injection time using the extracted tag.

For digest-based images (e.g., `ghcr.io/openclaw/openclaw@sha256:abc123`), the plugin version falls back to `latest` since npm packages don't support digest pinning.

**Alternative considered:** Keeping the plugin version independent from the image. Rejected because the Anthropic Vertex plugin is released in lockstep with OpenClaw — version mismatch causes runtime errors.

### 4. Status field

Add `Image string` to `ClawStatus` so users can confirm the deployed image via `kubectl get claw <name> -o jsonpath='{.status.image}'`. The reconciler writes this during status updates.

## Risks / Trade-offs

- **[Risk] Invalid image reference** → The operator does not validate that the image exists in the container registry. If a user specifies a non-existent image, the pod will fail with `ImagePullBackOff`. Mitigation: this is standard Kubernetes behavior; the `Ready` condition will surface the error.
- **[Risk] Plugin/image tag coupling** → The Vertex plugin version is derived from the image tag. If a user uses a custom image with a non-standard tag (e.g., `my-registry/openclaw:custom`), the plugin install may fail. Mitigation: acceptable for now; users with custom images likely don't need the auto-injected Vertex plugin.
- **[Trade-off] Default `slim` vs pinned version** → `slim` is a rolling tag that changes without user action. Users who want reproducibility must pin explicitly. This matches the current behavior (base deployment.yaml uses `:slim`).

@coderabbitai coderabbitai Bot Jun 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use OCI-aware parsing here.

Splitting spec.image on :/@ will mis-handle valid references with registry ports, and the digest fallback to latest breaks the release lockstep this change is trying to preserve. Please either use an OCI-aware parser or make digest-pinned images unsupported for Vertex injection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md`
around lines 41 - 55, The image-tag extraction for Vertex plugin injection is
too naive and will break valid OCI references, especially registry ports and
digest-pinned images. Update the logic around `spec.image` and `VertexPlugin` in
`claw_providers.go` to use an OCI-aware image parser instead of splitting on `:`
or `@`, and derive the plugin version from the parsed tag only. If digest
references cannot be mapped safely to a release tag, either reject them for
Vertex injection or disable auto-injection for that case rather than falling
back to `latest`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

see 7fd79d3

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== COMMIT ==="
git show --stat --oneline --decorate --no-renames 7fd79d31 -- \
  internal/controller/claw_providers.go \
  internal/controller/claw_plugins.go \
  openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md || true

echo
echo "=== claw_providers.go outline ==="
ast-grep outline internal/controller/claw_providers.go --view expanded || true

echo
echo "=== claw_providers.go relevant slice ==="
sed -n '1,220p' internal/controller/claw_providers.go

echo
echo "=== claw_plugins.go relevant slice ==="
sed -n '1,220p' internal/controller/claw_plugins.go

echo
echo "=== design.md relevant slice ==="
sed -n '1,120p' openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md

Length of output: 22359



@xcoulon thanks — I checked 7fd79d31.

The implementation side looks fixed now:

  • internal/controller/claw_providers.go uses github.com/google/go-containerregistry/pkg/name
  • imagePluginVersion() parses the reference OCI-aware and only returns a version for tagged images
  • digest references now return "", and requiredProviderPlugins() only appends @<version> when one was actually derived

So the code no longer has the split on ':' / '@' issue, and it no longer forces digest-pinned images onto latest.

One thing still looks out of sync, though: openspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.md still says:

  • the tag is extracted by splitting on : / @
  • digest-based images fall back to latest

That doc text is now stale relative to the implementation. If you update the design note to match the new behavior, this thread would be fully resolved.

Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
@coderabbitai coderabbitai Bot added the dependencies Pull requests that update a dependency file label Jun 26, 2026
xcoulon added 2 commits June 26, 2026 17:04
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/controller/claw_status.go (1)

230-235: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

De-duplicate image resolution.

This default-fallback (Spec.ImageDefaultOpenClawImage) is duplicated in claw_resource_controller.go (the OPENCLAW_IMAGE replacement around lines 958-963). If the resolution rule ever changes, the rendered Deployment and status.image could diverge. Extract a single helper (e.g. resolveOpenClawImage(instance)) and use it in both places.

♻️ Proposed helper
func resolveOpenClawImage(instance *clawv1alpha1.Claw) string {
	if instance.Spec.Image == "" {
		return DefaultOpenClawImage
	}
	return instance.Spec.Image
}
-	resolvedImage := instance.Spec.Image
-	if resolvedImage == "" {
-		resolvedImage = DefaultOpenClawImage
-	}
-	instance.Status.Image = resolvedImage
+	instance.Status.Image = resolveOpenClawImage(instance)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/claw_status.go` around lines 230 - 235, The image
fallback logic is duplicated between the status update in claw_status.go and the
OPENCLAW_IMAGE substitution in claw_resource_controller.go, so they can drift.
Add a shared helper like resolveOpenClawImage(instance) that applies the
Spec.Image fallback to DefaultOpenClawImage, then use it in both the status
assignment and the deployment rendering path to keep status.image and the
rendered Deployment consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/controller/claw_status.go`:
- Around line 230-235: The image fallback logic is duplicated between the status
update in claw_status.go and the OPENCLAW_IMAGE substitution in
claw_resource_controller.go, so they can drift. Add a shared helper like
resolveOpenClawImage(instance) that applies the Spec.Image fallback to
DefaultOpenClawImage, then use it in both the status assignment and the
deployment rendering path to keep status.image and the rendered Deployment
consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: c84f9351-ec7f-4655-8fe6-ee33d14d0b80

📥 Commits

Reviewing files that changed from the base of the PR and between 7fd79d3 and 94412ee.

📒 Files selected for processing (6)
  • .github/workflows/lint.yml
  • api/v1alpha1/claw_types.go
  • config/crd/bases/claw.sandbox.redhat.com_claws.yaml
  • internal/controller/claw_deployment_test.go
  • internal/controller/claw_status.go
  • test/e2e/e2e_test.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • codeready-toolchain/api (manual)
  • codeready-toolchain/toolchain-common (manual)
  • codeready-toolchain/host-operator (manual)
  • codeready-toolchain/toolchain-e2e (manual)
💤 Files with no reviewable changes (1)
  • .github/workflows/lint.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/e2e_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests
🧰 Additional context used
📓 Path-based instructions (5)
internal/controller/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Set owner references on all created resources via controllerutil.SetControllerReference

Files:

  • internal/controller/claw_status.go
  • internal/controller/claw_deployment_test.go
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Include Go license header from hack/boilerplate.go.txt template in all Go source files
Run golangci-lint via make lint and use make lint-fix for auto-fixing, with .golangci.yml configuration including lll and dupl linters enabled
Use make fmt (go fmt) and make vet (go vet) for code formatting and vetting before committing

Files:

  • internal/controller/claw_status.go
  • api/v1alpha1/claw_types.go
  • internal/controller/claw_deployment_test.go
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • internal/controller/claw_status.go
  • config/crd/bases/claw.sandbox.redhat.com_claws.yaml
  • api/v1alpha1/claw_types.go
  • internal/controller/claw_deployment_test.go
api/v1alpha1/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Modify API types in api/v1alpha1/ and run make manifests to regenerate CRD YAML in config/crd/bases/ and make generate to regenerate zz_generated.deepcopy.go

Files:

  • api/v1alpha1/claw_types.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*_test.go: Use require assertion from testify for fatal setup errors in tests, and assert for value comparisons
Structure tests with Test* function names, use t.Run() for subtests, t.Cleanup() for cleanup, and implement table-driven test patterns
Use waitFor(t, timeout, interval, condition, message) helper for async test assertions with 10s timeout and 250ms poll interval
Create separate test files per resource type (e.g., claw_configmap_test.go, claw_credentials_test.go)

Files:

  • internal/controller/claw_deployment_test.go
🔀 Multi-repo context codeready-toolchain/host-operator, codeready-toolchain/toolchain-e2e

[::codeready-toolchain/host-operator::] Search results surfaced only vendored Claw-related templates/CRD assets (deploy/templates/nstemplatetiers/claw/tier.yaml, deploy/templates/nstemplatetiers/claw/ns_claw.yaml), with no direct spec.image / status.image consumer match shown in the output.

[::codeready-toolchain/toolchain-e2e::] Search results surfaced the vendored Claw CRD (deploy/crds/claw.sandbox.redhat.com_claws.yaml) plus e2e status assertion files, but no direct spec.image / status.image consumer match was shown in the output.

🔇 Additional comments (3)
api/v1alpha1/claw_types.go (1)

577-581: LGTM!

config/crd/bases/claw.sandbox.redhat.com_claws.yaml (1)

18-20: LGTM!

Also applies to: 411-417, 927-931

internal/controller/claw_deployment_test.go (1)

2022-2058: LGTM!

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.62%. Comparing base (f226f45) to head (94412ee).

Files with missing lines Patch % Lines
internal/controller/claw_providers.go 86.66% 1 Missing and 1 partial ⚠️
internal/controller/claw_status.go 50.00% 1 Missing and 1 partial ⚠️

❌ Your changes status has failed because you have indirect coverage changes. Learn more about Unexpected Coverage Changes and reasons for indirect coverage changes.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #218      +/-   ##
==========================================
- Coverage   78.63%   78.62%   -0.01%     
==========================================
  Files          33       33              
  Lines        4329     4356      +27     
==========================================
+ Hits         3404     3425      +21     
- Misses        590      593       +3     
- Partials      335      338       +3     
Files with missing lines Coverage Δ
internal/controller/claw_plugins.go 91.89% <100.00%> (-1.17%) ⬇️
internal/controller/claw_resource_controller.go 65.11% <100.00%> (+0.16%) ⬆️
internal/controller/claw_providers.go 97.26% <86.66%> (-2.74%) ⬇️
internal/controller/claw_status.go 76.64% <50.00%> (-0.81%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rajivnathan rajivnathan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like the daily e2e job needs to be updated

OPENCLAW_TAG=$(yq '.images[] | select(.name == "ghcr.io/openclaw/openclaw") | .newTag' "$KUSTOMIZE")
too because it tries to run all tests twice but first with a pinned version and then with the slim tag but the mechanism for setting the tag is being changed with this PR.

Regarding the spec files, there's quite a few of them so I was wondering whether we need to include those or should we summarize them into an ADR document or maybe this is a simple enough change that we don't need to add docs? WDYT?

Comment thread Makefile
# - CERT_MANAGER_INSTALL_SKIP=true
KIND_CLUSTER ?= claw-operator-test-e2e
GATEWAY_IMAGE ?= $(shell yq '.images[] | select(.name == "ghcr.io/openclaw/openclaw") | .newName + ":" + .newTag' internal/assets/manifests/claw/kustomization.yaml)
GATEWAY_IMAGE ?= ghcr.io/openclaw/openclaw:slim

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is only used for e2e tests now so maybe worth renaming it to E2E_GATEWAY_IMAGE?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, it looks like this env var no longer changes the actual image used to run the tests, which causes a problem for the daily-e2e test job:

make test-e2e GATEWAY_IMAGE=${{ steps.versions.outputs.openclaw_image }}

Comment on lines +960 to +963
openclawImage := instance.Spec.Image
if openclawImage == "" {
openclawImage = DefaultOpenClawImage
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a common function for this that can be used in claw_status.go too?

// "ghcr.io/openclaw/openclaw:slim" → "" (latest)
// "ghcr.io/openclaw/openclaw" → "" (latest)
// "ghcr.io/openclaw/openclaw@sha256:abc" → "" (latest)
func imagePluginVersion(image string) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this plugin version is specifically for the vertex provider plugin right? If so, then we should probably name it something vertex-specific.

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

Labels

dependencies Pull requests that update a dependency file feature New feature or request test Work that adds, fixes, or maintains automated tests or coverage (unit, integration, e2e, flakiness)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants