Skip to content

add init container to prepare workspace pod#184

Merged
bennyz merged 1 commit into
centos-automotive-suite:mainfrom
bennyz:init-container
Mar 29, 2026
Merged

add init container to prepare workspace pod#184
bennyz merged 1 commit into
centos-automotive-suite:mainfrom
bennyz:init-container

Conversation

@bennyz

@bennyz bennyz commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

in order to make it possible for arbitrary containers to work

Summary by CodeRabbit

  • New Features

    • Added an init container that prepares the workspace PVC (creates dirs, generates SSH key if missing, and imports client config when present).
  • Bug Fixes

    • Improved workspace initialization reliability, ensuring required files and directories are created automatically.
    • Clarified error reporting when user namespaces are not supported.
  • Improvements

    • Simplified container security capabilities and adjusted entrypoint/working-directory behavior so custom images run their own startup.
  • Tests

    • Added tests covering security context and privilege behavior across image and platform variations.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@bennyz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 52 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 21 minutes and 52 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3208d64-067a-4a17-910e-a862a581bb4d

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6b903 and 3d89afd.

📒 Files selected for processing (3)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go
📝 Walkthrough

Walkthrough

Adds a workspace init container that prepares the PVC (dirs, SSH key, optional Jumpstarter client config) and unifies capability selection; the main container command/workingDir is set only for the configured toolchain image. Also updates an SCC-related error message and adds tests for SecurityContext behavior.

Changes

Cohort / File(s) Summary
Workspace Pod Initialization & Security
internal/controller/workspace/controller.go
Adds InitContainers with a workspace-init init container (configured image) that creates workspace dirs, generates an Ed25519 SSH key if missing, mounts optional Jumpstarter client secret and runs client config (best-effort). Simplifies capability selection to always add SETUID, SETGID, SYS_ADMIN, DAC_OVERRIDE, CHOWN, FOWNER. Main toolchain container Command/WorkingDir set only when image == configuredImage.
Security Context Tests
internal/controller/workspace/controller_test.go
Adds TestBuildPod_SecurityContext table-driven tests covering combinations of configured vs custom image and user namespace support; validates Privileged, SYS_ADMIN capability, ProcMount, and drop-all behavior.
Infra error message
internal/controller/operatorconfig/controller.go
Changes workspace SCC error message text when user namespaces are unsupported from "(privileged)" to "(no user namespaces)".

Sequence Diagram

sequenceDiagram
    participant Pod as Pod Creation
    participant Init as workspace-init (Init Container)
    participant PVC as PVC (/workspace)
    participant Main as toolchain (Main Container)
    participant JS as Jumpstarter Client

    Pod->>Init: start init container
    Init->>PVC: create dirs, set permissions
    Init->>PVC: generate Ed25519 SSH key if missing
    Init->>PVC: copy /jumpstarter/client.yaml (if mounted)
    Init->>JS: run jumpstarter client config (best-effort)
    Init-->>Pod: exit successfully
    Pod->>Main: start main container (entrypoint depends on image)
    Main->>PVC: use prepared workspace
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • bkhizgiy

Poem

🐇
I hopped in at dawn to gently prep the ground,
Made folders, keys, and configs all around,
Init danced first, then main took the stage,
Workspace ready — clean, secure, and sage,
A rabbit’s cheer for code well found. 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'add init container to prepare workspace pod' directly and concisely describes the main change—adding an init container to the workspace pod preparation logic, which is the primary feature implemented across the modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/workspace/controller.go`:
- Around line 340-343: The copied client.yaml is placed at
/workspace/.config/jumpstarter/clients/workspace.yaml but the activation step is
missing; after the cp in the init path ensure you run the Jumpstarter activation
command (jmp config client use workspace) so the copied client becomes the
active client (replicate what hack/workspace-entrypoint.sh did previously) —
update the init logic to execute the activation command (or the equivalent
config toggle) immediately after creating/copying workspace.yaml.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfe7b237-058b-41ed-abca-b5b139882cc6

📥 Commits

Reviewing files that changed from the base of the PR and between cf96e5b and 13a9334.

📒 Files selected for processing (1)
  • internal/controller/workspace/controller.go

Comment thread internal/controller/workspace/controller.go
Comment thread internal/controller/workspace/controller.go Outdated

@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/workspace/controller_test.go (1)

500-500: Guard container indexing to avoid panic in test failures.

Directly indexing pod.Spec.Containers[0] can panic and obscure the root cause when pod construction regresses.

Suggested hardening
-			secCtx := pod.Spec.Containers[0].SecurityContext
+			if len(pod.Spec.Containers) == 0 {
+				t.Fatal("expected at least one container in pod spec")
+			}
+			secCtx := pod.Spec.Containers[0].SecurityContext
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller_test.go` at line 500, The test
directly indexes pod.Spec.Containers[0], which can panic if the pod has no
containers; update the test around where secCtx is set to first verify
containers exist (e.g. check len(pod.Spec.Containers) > 0) and fail the test
with a clear message if not, then safely assign secCtx :=
pod.Spec.Containers[0].SecurityContext; this guards the test from panics and
makes failures clearer (referencing pod.Spec.Containers and secCtx).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/controller/workspace/controller_test.go`:
- Line 500: The test directly indexes pod.Spec.Containers[0], which can panic if
the pod has no containers; update the test around where secCtx is set to first
verify containers exist (e.g. check len(pod.Spec.Containers) > 0) and fail the
test with a clear message if not, then safely assign secCtx :=
pod.Spec.Containers[0].SecurityContext; this guards the test from panics and
makes failures clearer (referencing pod.Spec.Containers and secCtx).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55f7f920-1eba-452d-9841-f2c93adb812d

📥 Commits

Reviewing files that changed from the base of the PR and between 13a9334 and 269d1ae.

📒 Files selected for processing (3)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/controller/operatorconfig/controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/workspace/controller.go

@bennyz bennyz requested a review from bkhizgiy March 29, 2026 10:46

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/common/tasks/scripts/build_image.sh (1)

391-467: ⚠️ Potential issue | 🔴 Critical

Don't let the split-build path publish a container after a later failure.

Line 460 starts the external docker://$CONTAINER_PUSH write in the background, but Line 474 (aib to-disk-image) and Line 701 (artifact compression) can still fail afterward. That means a failed Task can still leave a published container behind, and the final progress/result updates are emitted before the push outcome is known. Keep the local OCI export/annotation async if you want the overlap, but move the final registry skopeo copy behind the success boundary, or push to a staging tag and only promote it once the step is otherwise complete.

Also applies to: 470-482, 699-701, 730-742

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/common/tasks/scripts/build_image.sh` around lines 391 - 467, The
background subshell currently performs the final registry push ("skopeo copy ...
docker://$CONTAINER_PUSH") which can publish a container even if later steps
fail; preserve the async OCI export/annotation (use symbols PUSH_SRC, OCI_DIR,
AIB_METADATA_PID, ANNOTATE_DONE) but remove the remote push from that subshell
and instead wait for the task success boundary before performing the final push.
Concretely: let the subshell only create and annotate the OCI layout and exit
(exporting OCI_DIR and new manifest info), keep CONTAINER_PUSH_PID for the
background work that completes early, then after the disk-image and compression
steps succeed, perform the registry upload (use CONTAINER_PUSH or a staging tag
and promote it) synchronously and only on success; ensure any temporary OCI_DIR
cleanup happens after the registry push and that failures do not trigger
publishing.
🧹 Nitpick comments (2)
internal/controller/workspace/controller.go (1)

320-342: Consider sharing the workspace bootstrap code instead of embedding a second copy.

initScript now overlaps with hack/workspace-entrypoint.sh:1-56 on directory creation, SSH key generation, and Jumpstarter setup. Extracting a shared script, or adding an init-only mode to the existing entrypoint, would make these two paths much harder to drift apart.

internal/common/tasks/scripts/build_image.sh (1)

263-278: Use the configured auth file when pinning the AIB image reference.

Line 267 does an unauthenticated skopeo inspect, even though the script has already prepared REGISTRY_AUTH_FILE. If $(params.automotive-image-builder) points at an authenticated mirror, Lines 275 and 557 will silently fall back to the mutable ref instead of the digest-pinned one, which weakens the recorded provenance. Please verify this path against your private-registry AIB override case.

🔍 Suggested adjustment
 (
   aib --version 2>&1 | head -1 | tr -d '\r\n' | sed 's/^aib //' > /tmp/aib-version.txt 2>/dev/null || true
-  AIB_DIGEST=$(skopeo inspect --format '{{.Digest}}' "docker://${AIB_IMAGE_REF}" 2>/dev/null || echo "")
+  if [ -n "${REGISTRY_AUTH_FILE:-}" ]; then
+    AIB_DIGEST=$(skopeo inspect --authfile="$REGISTRY_AUTH_FILE" --format '{{.Digest}}' "docker://${AIB_IMAGE_REF}" 2>/dev/null || echo "")
+  else
+    AIB_DIGEST=$(skopeo inspect --format '{{.Digest}}' "docker://${AIB_IMAGE_REF}" 2>/dev/null || echo "")
+  fi
   if [ -n "$AIB_DIGEST" ]; then

Also applies to: 553-559

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/common/tasks/scripts/build_image.sh` around lines 263 - 278, The
skopeo inspect call used to compute AIB_DIGEST for AIB_IMAGE_REF is
unauthenticated and should use the configured registry auth file; update the
skopeo inspect invocation (the one assigning AIB_DIGEST) to include the
--authfile "$REGISTRY_AUTH_FILE" option (and do the same for the other skopeo
inspect occurrence later in the script) so authenticated private-registry AIB
overrides produce a digest-pinned value written to /tmp/aib-pinned.txt; keep the
existing fallback behavior if inspect fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/common/tasks/scripts/build_image.sh`:
- Around line 550-552: The human-readable "AIB build phase" timing currently
subtracts BUILD_START_TIME which re-includes setup time; change the subtraction
to use AIB_INVOKE_TIME when computing the readable duration (i.e., use
AIB_END_TIME - AIB_INVOKE_TIME) so it matches the structured metric; update both
places that print the AIB build phase duration (any echo/log that uses
AIB_END_TIME minus BUILD_START_TIME) to use AIB_INVOKE_TIME instead so the two
outputs stay consistent.

In `@internal/controller/workspace/controller.go`:
- Around line 347-350: The container spec currently hardcodes WorkingDir to
"/workspace" for all images which overrides custom images' built-in WORKDIR;
change the logic that builds the container spec so that WorkingDir is set to
"/workspace" only when image == configuredImage (the same condition used for
setting the command variable), and leave WorkingDir unset/empty for any other
image to preserve the image's default WORKDIR; adjust where WorkingDir is
assigned in the container creation near the Command/command handling to check
configuredImage before setting it.

---

Outside diff comments:
In `@internal/common/tasks/scripts/build_image.sh`:
- Around line 391-467: The background subshell currently performs the final
registry push ("skopeo copy ... docker://$CONTAINER_PUSH") which can publish a
container even if later steps fail; preserve the async OCI export/annotation
(use symbols PUSH_SRC, OCI_DIR, AIB_METADATA_PID, ANNOTATE_DONE) but remove the
remote push from that subshell and instead wait for the task success boundary
before performing the final push. Concretely: let the subshell only create and
annotate the OCI layout and exit (exporting OCI_DIR and new manifest info), keep
CONTAINER_PUSH_PID for the background work that completes early, then after the
disk-image and compression steps succeed, perform the registry upload (use
CONTAINER_PUSH or a staging tag and promote it) synchronously and only on
success; ensure any temporary OCI_DIR cleanup happens after the registry push
and that failures do not trigger publishing.

---

Nitpick comments:
In `@internal/common/tasks/scripts/build_image.sh`:
- Around line 263-278: The skopeo inspect call used to compute AIB_DIGEST for
AIB_IMAGE_REF is unauthenticated and should use the configured registry auth
file; update the skopeo inspect invocation (the one assigning AIB_DIGEST) to
include the --authfile "$REGISTRY_AUTH_FILE" option (and do the same for the
other skopeo inspect occurrence later in the script) so authenticated
private-registry AIB overrides produce a digest-pinned value written to
/tmp/aib-pinned.txt; keep the existing fallback behavior if inspect fails.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 720fb2de-df1b-4212-ac7f-61b04f117459

📥 Commits

Reviewing files that changed from the base of the PR and between 269d1ae and b4bed8b.

📒 Files selected for processing (12)
  • cmd/main.go
  • config/manager/manager.yaml
  • internal/common/tasks/scripts/build_image.sh
  • internal/common/tasks/scripts/common.sh
  • internal/common/tasks/tasks.go
  • internal/controller/imagebuild/controller.go
  • internal/controller/imagebuild/metrics.go
  • internal/controller/imagebuild/metrics_test.go
  • internal/controller/operatorconfig/controller.go
  • internal/controller/operatorconfig/resources.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller_test.go

Comment on lines +550 to +552
AIB_END_TIME=$(date +%s)
echo "⏱ AIB build phase: $((AIB_END_TIME - BUILD_START_TIME))s"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the human-readable build-phase timing.

Line 551 subtracts BUILD_START_TIME, so the "AIB build phase" log includes setup time again. The structured result at Line 750 already uses AIB_INVOKE_TIME, so the two outputs disagree.

🛠️ Suggested fix
 AIB_END_TIME=$(date +%s)
-echo "⏱ AIB build phase: $((AIB_END_TIME - BUILD_START_TIME))s"
+echo "⏱ AIB build phase: $((AIB_END_TIME - AIB_INVOKE_TIME))s"

Also applies to: 748-750

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/common/tasks/scripts/build_image.sh` around lines 550 - 552, The
human-readable "AIB build phase" timing currently subtracts BUILD_START_TIME
which re-includes setup time; change the subtraction to use AIB_INVOKE_TIME when
computing the readable duration (i.e., use AIB_END_TIME - AIB_INVOKE_TIME) so it
matches the structured metric; update both places that print the AIB build phase
duration (any echo/log that uses AIB_END_TIME minus BUILD_START_TIME) to use
AIB_INVOKE_TIME instead so the two outputs stay consistent.

Comment thread internal/controller/workspace/controller.go

@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: 1

♻️ Duplicate comments (1)
internal/controller/workspace/controller.go (1)

344-350: ⚠️ Potential issue | 🟠 Major

Leave WorkingDir unset for custom images.

Line 377 still hardcodes /workspace, which overrides an image-defined WORKDIR even though this branch now preserves custom ENTRYPOINT/CMD. Images that rely on their own startup directory can still fail immediately.

💡 Proposed fix
-	var command []string
+	var (
+		command    []string
+		workingDir string
+	)
 	if image == configuredImage {
 		command = []string{"/usr/local/bin/workspace-entrypoint.sh"}
+		workingDir = "/workspace"
 	}
@@
-				Command:         command,
-				WorkingDir:      "/workspace",
+				Command:         command,
+				WorkingDir:      workingDir,

Also applies to: 372-377

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller.go` around lines 344 - 350,
WorkingDir is being set to "/workspace" even for custom images, which overrides
an image-defined WORKDIR; change the logic so WorkingDir is only assigned when
image == configuredImage (the toolchain image) — e.g., move or add the
WorkingDir = "/workspace" assignment into the same branch that sets command =
[]string{"/usr/local/bin/workspace-entrypoint.sh"} so that for custom images
(image != configuredImage) WorkingDir remains unset and the container's own
WORKDIR is preserved.
🧹 Nitpick comments (1)
internal/controller/workspace/controller_test.go (1)

416-552: Cover the arbitrary-image startup contract in this table.

This matrix checks SecurityContext, but it doesn't assert the behavior that actually makes custom images usable: Command should stay nil, WorkingDir should preserve the image default, and the init container should be present. Adding those expectations here would catch the current custom-image regression immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller_test.go` around lines 416 - 552,
Update TestBuildPod_SecurityContext to also assert the arbitrary-image startup
contract: when the test case uses a custom image (image == customImage or image
!= "" && image != automotivev1alpha1.DefaultToolchainImage) assert
pod.Spec.Containers[0].Command is nil (so the image CMD is preserved),
pod.Spec.Containers[0].WorkingDir is nil (so image default working directory is
preserved), and that the init container(s) exist (pod.Spec.InitContainers is
non-empty). Add these assertions alongside the existing SecurityContext checks
in the loop that calls r.buildPod(ws, operatorConfig) so the table covers both
security and startup-contract expectations for custom vs toolchain images.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/workspace/controller.go`:
- Around line 344-350: isWorkspaceActive currently relies on execing "/bin/sh
-c" with a shell pipeline (ls | grep | ps | awk) which fails in
minimal/distroless custom images; update isWorkspaceActive to avoid shell
pipelines by invoking binaries directly as command slices and parsing outputs in
Go (e.g. run exec command ["ps","-o","pid,cmd"] and parse for the workspace
process, and run ["ls","-A","/some/path"] when listing is needed) instead of
using "/bin/sh -c"; additionally, treat missing binaries as non-fatal (if exec
returns ENOENT, assume active or use alternative marker) so probes do not
falsely fail for shell-less images — change references inside isWorkspaceActive
and any helper that builds the shell command to use direct command/args and
Go-side parsing.

---

Duplicate comments:
In `@internal/controller/workspace/controller.go`:
- Around line 344-350: WorkingDir is being set to "/workspace" even for custom
images, which overrides an image-defined WORKDIR; change the logic so WorkingDir
is only assigned when image == configuredImage (the toolchain image) — e.g.,
move or add the WorkingDir = "/workspace" assignment into the same branch that
sets command = []string{"/usr/local/bin/workspace-entrypoint.sh"} so that for
custom images (image != configuredImage) WorkingDir remains unset and the
container's own WORKDIR is preserved.

---

Nitpick comments:
In `@internal/controller/workspace/controller_test.go`:
- Around line 416-552: Update TestBuildPod_SecurityContext to also assert the
arbitrary-image startup contract: when the test case uses a custom image (image
== customImage or image != "" && image !=
automotivev1alpha1.DefaultToolchainImage) assert pod.Spec.Containers[0].Command
is nil (so the image CMD is preserved), pod.Spec.Containers[0].WorkingDir is nil
(so image default working directory is preserved), and that the init
container(s) exist (pod.Spec.InitContainers is non-empty). Add these assertions
alongside the existing SecurityContext checks in the loop that calls
r.buildPod(ws, operatorConfig) so the table covers both security and
startup-contract expectations for custom vs toolchain images.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dc02b4d5-e3ab-407d-829e-213d95a9f305

📥 Commits

Reviewing files that changed from the base of the PR and between b4bed8b and 7e6b903.

📒 Files selected for processing (3)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/controller/operatorconfig/controller.go

Comment on lines +344 to +350
// For the toolchain image, run the entrypoint (handles user creation, overlayfs,
// subuid/subgid setup). For custom images, don't override Command so the
// image's own ENTRYPOINT/CMD runs — the init container already prepared the PVC.
var command []string
if image == configuredImage {
command = []string{"/usr/local/bin/workspace-entrypoint.sh"}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Custom images still fail the activity-probe contract.

Once Command is left unset, isWorkspaceActive() on Lines 558-563 still execs /bin/sh -c and assumes ls, grep, ps, and awk exist in the workspace image. Minimal/distroless images will now start, but every auto-pause probe will fail, so they never auto-pause and the controller will keep retrying.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller.go` around lines 344 - 350,
isWorkspaceActive currently relies on execing "/bin/sh -c" with a shell pipeline
(ls | grep | ps | awk) which fails in minimal/distroless custom images; update
isWorkspaceActive to avoid shell pipelines by invoking binaries directly as
command slices and parsing outputs in Go (e.g. run exec command
["ps","-o","pid,cmd"] and parse for the workspace process, and run
["ls","-A","/some/path"] when listing is needed) instead of using "/bin/sh -c";
additionally, treat missing binaries as non-fatal (if exec returns ENOENT,
assume active or use alternative marker) so probes do not falsely fail for
shell-less images — change references inside isWorkspaceActive and any helper
that builds the shell command to use direct command/args and Go-side parsing.

in order to make it possible for arbitrary containers to work

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Assited-by: claude-sonnet-4.6

@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.

♻️ Duplicate comments (1)
internal/controller/workspace/controller.go (1)

344-352: ⚠️ Potential issue | 🟠 Major

Custom images still break the auto-pause probe.

Leaving Command unset here is the right startup behavior, but isWorkspaceActive() on Lines 560-565 still execs ["/bin/sh", "-c", ...] and assumes ls, grep, ps, and awk exist in the workspace image. Minimal/distroless custom images will start, then every activity check fails and the controller requeues forever instead of honoring auto-pause. Either skip that probe for image != configuredImage or make missing probe commands a non-fatal path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller.go` around lines 344 - 352,
isWorkspaceActive() currently execs a shell with ls/grep/ps/awk which fails on
minimal/distroless custom images; modify the controller so that when image !=
configuredImage it either skips the auto-pause activity probe entirely or treats
command-not-found from the probe as non-fatal (return "unknown" or "active"
rather than erroring/requeuing). Locate isWorkspaceActive (and the code path
constructing [" /bin/sh","-c", ...]) and add an early check against the
configuredImage/ image comparison to skip running the probe for custom images,
or wrap the exec result/error handling to detect shell/command-not-found and
return a benign result so auto-pause is honored.
🧹 Nitpick comments (1)
internal/controller/workspace/controller.go (1)

320-342: Extract the shared workspace bootstrap into one place.

This raw-shell block now partially duplicates the directory/ownership setup in hack/workspace-entrypoint.sh, Lines 4-42. Keeping the init path and the toolchain-entrypoint path aligned will get brittle quickly; moving the common prep into a shared script/helper would reduce drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller.go` around lines 320 - 342, The
initScript literal in controller.go duplicates setup logic found in
hack/workspace-entrypoint.sh; extract the common mkdir/sshkey/chown/bootstrap
steps into a single shared bootstrap script (e.g., workspace-bootstrap.sh) and
update both places to invoke that shared script instead of inlining the shell
block. Concretely: create the shared script with the existing directory,
ssh-keygen, jumpstarter-copy and chown commands, add it to the image or deliver
it via a ConfigMap/Volume, replace the initScript variable usage in
controller.go (initScript) to run the shared script, and update
hack/workspace-entrypoint.sh to source/run the same shared script so both paths
stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/controller/workspace/controller.go`:
- Around line 344-352: isWorkspaceActive() currently execs a shell with
ls/grep/ps/awk which fails on minimal/distroless custom images; modify the
controller so that when image != configuredImage it either skips the auto-pause
activity probe entirely or treats command-not-found from the probe as non-fatal
(return "unknown" or "active" rather than erroring/requeuing). Locate
isWorkspaceActive (and the code path constructing [" /bin/sh","-c", ...]) and
add an early check against the configuredImage/ image comparison to skip running
the probe for custom images, or wrap the exec result/error handling to detect
shell/command-not-found and return a benign result so auto-pause is honored.

---

Nitpick comments:
In `@internal/controller/workspace/controller.go`:
- Around line 320-342: The initScript literal in controller.go duplicates setup
logic found in hack/workspace-entrypoint.sh; extract the common
mkdir/sshkey/chown/bootstrap steps into a single shared bootstrap script (e.g.,
workspace-bootstrap.sh) and update both places to invoke that shared script
instead of inlining the shell block. Concretely: create the shared script with
the existing directory, ssh-keygen, jumpstarter-copy and chown commands, add it
to the image or deliver it via a ConfigMap/Volume, replace the initScript
variable usage in controller.go (initScript) to run the shared script, and
update hack/workspace-entrypoint.sh to source/run the same shared script so both
paths stay in sync.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f03f4c4d-23fe-4294-a5de-5816be6cccc1

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6b903 and 3d89afd.

📒 Files selected for processing (3)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller_test.go

@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/workspace/controller.go (1)

331-342: Consider parameterizing the hardcoded UID 1000.

The chown -R 1000:1000 assumes the workspace user will be UID 1000. While this works for the toolchain image's entrypoint (which creates a user with this UID), custom images might expect different ownership.

For the toolchain image, the entrypoint already handles user creation. For custom images running as a different UID, these directories will have incorrect ownership.

This may be intentional given the current scope, but worth documenting or making configurable if arbitrary images need different UIDs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/workspace/controller.go` around lines 331 - 342, The
chown in the initScript is hardcoded to 1000:1000; change initScript
construction in controller.go to parameterize the UID/GID (e.g., use
environment-configurable variables like WORKSPACE_UID and WORKSPACE_GID with
sensible defaults) and substitute them into the chown command instead of
1000:1000; ensure the code that builds initScript reads those env vars (or is
passed the resolved uid/gid) so custom images can override ownership, and update
any related docs/comments to mention the new WORKSPACE_UID/WORKSPACE_GID
variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/controller/workspace/controller.go`:
- Around line 331-342: The chown in the initScript is hardcoded to 1000:1000;
change initScript construction in controller.go to parameterize the UID/GID
(e.g., use environment-configurable variables like WORKSPACE_UID and
WORKSPACE_GID with sensible defaults) and substitute them into the chown command
instead of 1000:1000; ensure the code that builds initScript reads those env
vars (or is passed the resolved uid/gid) so custom images can override
ownership, and update any related docs/comments to mention the new
WORKSPACE_UID/WORKSPACE_GID variables.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ef438f4f-05fd-4ad5-a8b9-20bf3fd83dc3

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6b903 and 3d89afd.

📒 Files selected for processing (3)
  • internal/controller/operatorconfig/controller.go
  • internal/controller/workspace/controller.go
  • internal/controller/workspace/controller_test.go

@bennyz bennyz merged commit 8950b03 into centos-automotive-suite:main Mar 29, 2026
4 checks passed
@bennyz bennyz deleted the init-container branch March 29, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants