feat: add image field to Claw CRD for user-specified OpenClaw image (#217)#218
feat: add image field to Claw CRD for user-specified OpenClaw image (#217)#218xcoulon wants to merge 4 commits into
image field to Claw CRD for user-specified OpenClaw image (#217)#218Conversation
…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>
WalkthroughAdds ChangesOpenClaw image field and propagation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (20)
Makefileapi/v1alpha1/claw_types.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamlinternal/assets/manifests/claw/deployment.yamlinternal/assets/manifests/claw/kustomization.yamlinternal/controller/claw_deployment_test.gointernal/controller/claw_plugins.gointernal/controller/claw_plugins_test.gointernal/controller/claw_providers.gointernal/controller/claw_providers_test.gointernal/controller/claw_resource_controller.goopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/.openspec.yamlopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/proposal.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/claw-crd/spec.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/version-field/spec.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/tasks.mdopenspec/specs/claw-crd/spec.mdopenspec/specs/version-field/spec.mdtest/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
##[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
##[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.mdMakefileopenspec/specs/version-field/spec.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/proposal.mdinternal/assets/manifests/claw/deployment.yamlopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/version-field/spec.mdinternal/controller/claw_providers.goopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/tasks.mdopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/design.mdapi/v1alpha1/claw_types.gointernal/controller/claw_resource_controller.gotest/e2e/e2e_test.gointernal/controller/claw_providers_test.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamlinternal/controller/claw_deployment_test.goopenspec/changes/archive/2026-06-26-crd-openclaw-version-field/specs/claw-crd/spec.mdinternal/controller/claw_plugins.gointernal/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
SetreadOnlyRootFilesystem: trueon proxy andwait-for-proxycontainers, 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.gointernal/controller/claw_resource_controller.gointernal/controller/claw_providers_test.gointernal/controller/claw_deployment_test.gointernal/controller/claw_plugins.gointernal/controller/claw_plugins_test.go
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Include Go license header fromhack/boilerplate.go.txttemplate in all Go source files
Run golangci-lint viamake lintand usemake lint-fixfor auto-fixing, with.golangci.ymlconfiguration includinglllanddupllinters enabled
Usemake fmt(go fmt) andmake vet(go vet) for code formatting and vetting before committing
Files:
internal/controller/claw_providers.goapi/v1alpha1/claw_types.gointernal/controller/claw_resource_controller.gotest/e2e/e2e_test.gointernal/controller/claw_providers_test.gointernal/controller/claw_deployment_test.gointernal/controller/claw_plugins.gointernal/controller/claw_plugins_test.go
api/v1alpha1/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Modify API types in
api/v1alpha1/and runmake manifeststo regenerate CRD YAML inconfig/crd/bases/andmake generateto 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: Userequireassertion from testify for fatal setup errors in tests, andassertfor value comparisons
Structure tests withTest*function names, uset.Run()for subtests,t.Cleanup()for cleanup, and implement table-driven test patterns
UsewaitFor(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.gointernal/controller/claw_providers_test.gointernal/controller/claw_deployment_test.gointernal/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
| 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") | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 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.
| 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.
| 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`). |
There was a problem hiding this comment.
🎯 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`.
There was a problem hiding this comment.
🧩 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.mdLength of output: 22359
@xcoulon thanks — I checked 7fd79d31.
The implementation side looks fixed now:
internal/controller/claw_providers.gousesgithub.com/google/go-containerregistry/pkg/nameimagePluginVersion()parses the reference OCI-aware and only returns a version for tagged images- digest references now return
"", andrequiredProviderPlugins()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>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/controller/claw_status.go (1)
230-235: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDe-duplicate image resolution.
This default-fallback (
Spec.Image→DefaultOpenClawImage) is duplicated inclaw_resource_controller.go(theOPENCLAW_IMAGEreplacement around lines 958-963). If the resolution rule ever changes, the rendered Deployment andstatus.imagecould 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
📒 Files selected for processing (6)
.github/workflows/lint.ymlapi/v1alpha1/claw_types.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamlinternal/controller/claw_deployment_test.gointernal/controller/claw_status.gotest/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.gointernal/controller/claw_deployment_test.go
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Include Go license header fromhack/boilerplate.go.txttemplate in all Go source files
Run golangci-lint viamake lintand usemake lint-fixfor auto-fixing, with.golangci.ymlconfiguration includinglllanddupllinters enabled
Usemake fmt(go fmt) andmake vet(go vet) for code formatting and vetting before committing
Files:
internal/controller/claw_status.goapi/v1alpha1/claw_types.gointernal/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.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamlapi/v1alpha1/claw_types.gointernal/controller/claw_deployment_test.go
api/v1alpha1/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Modify API types in
api/v1alpha1/and runmake manifeststo regenerate CRD YAML inconfig/crd/bases/andmake generateto regenerate zz_generated.deepcopy.go
Files:
api/v1alpha1/claw_types.go
**/*_test.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*_test.go: Userequireassertion from testify for fatal setup errors in tests, andassertfor value comparisons
Structure tests withTest*function names, uset.Run()for subtests,t.Cleanup()for cleanup, and implement table-driven test patterns
UsewaitFor(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 Report❌ Patch coverage is
❌ 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
🚀 New features to boost your workflow:
|
rajivnathan
left a comment
There was a problem hiding this comment.
Looks like the daily e2e job needs to be updated
too because it tries to run all tests twice but first with a pinned version and then with theslim 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?
| # - 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 |
There was a problem hiding this comment.
I think this is only used for e2e tests now so maybe worth renaming it to E2E_GATEWAY_IMAGE?
There was a problem hiding this comment.
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:
| openclawImage := instance.Spec.Image | ||
| if openclawImage == "" { | ||
| openclawImage = DefaultOpenClawImage | ||
| } |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
I think this plugin version is specifically for the vertex provider plugin right? If so, then we should probably name it something vertex-specific.
Allow users to specify the full OpenClaw container image per Claw
instance via
spec.image(default:ghcr.io/openclaw/openclaw:slim).Imagefield toClawSpecwith kubebuilder default andImageprint column on theClawtypedeployment.yamlwith anOPENCLAW_IMAGEplaceholder resolved bybuildKustomizedObjects()imagessection fromkustomization.yaml; image injectionnow uses the same pattern as
CLAW_INSTANCE_NAMEimagePluginVersion()helper to extract the release version fromimage tags (handles
slimvariants:2026.6.10-slim-arm64→2026.6.10,slim→ empty/latest)VertexPlugininknownProvidersfrom a hardcoded versionedstring to a plain package name; version suffix is appended conditionally
by
requiredProviderPlugins()GATEWAY_IMAGEinMakefileto use the static defaultimagePluginVersion(), image propagation todeployment containers, and plugin version extraction
assertions
Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Xavier Coulon xcoulon@redhat.com
Summary by CodeRabbit
New Features
spec.imageto the Claw CR (default:ghcr.io/openclaw/openclaw:slim).kubectl getnow shows the image via a newImagecolumn, and the resolved value is reported in status.Bug Fixes
slim,latest, and digest-based images).Tests