Skip to content

Cache boot-critical images before pruning; require --pull-secret; fix pull flavor naming - #32

Open
omer-vishlitzky wants to merge 1 commit into
osac-project:mainfrom
omer-vishlitzky:snapshot-caching-and-pull-name-fix
Open

Cache boot-critical images before pruning; require --pull-secret; fix pull flavor naming#32
omer-vishlitzky wants to merge 1 commit into
osac-project:mainfrom
omer-vishlitzky:snapshot-caching-and-pull-name-fix

Conversation

@omer-vishlitzky

@omer-vishlitzky omer-vishlitzky commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Snapshot lifecycle (mandatory pull-secret + image caching):

  • cluster-tool boot --pull-secret is now required. Snapshots are always credential-stripped, so injecting a pull-secret at boot is no longer optional.
  • Added cache_image(): ensures an image is present in the node's local podman store and anchors it with a stopped container so podman image prune won't remove it. Skips the pull if the image is already local, since after credentials are stripped a pull would fail even for an image that's already fully present.
  • New snapshot step caches RECERT_IMAGE and the node's nodeip-configuration image (auto-detected from the systemd unit) before pruning, so a freshly-booted, credential-stripped clone doesn't need to pull them itself. The prune step cleans up the anchor containers afterward.

cluster-tool pull flavor naming fix:

  • cmd_pull used to name the local flavor from either an unused --name flag or the pulled image's embedded manifest["flavor"] field. manifest["flavor"] reflects whatever the pushing side's local flavor was named at push time -- not the tag it was published under. Pushing a flavor under a different tag than its source name (e.g. publishing "caas-4-22" under the floating tag "caas") caused pull ...:caas to silently register locally under an unrelated name, leaving the local "caas" flavor stale.
  • The local flavor name is now always derived from the pulled reference's tag, deterministically. Removed the unused --name flag (zero real callers in any CI workflow, script, or doc).

Test plan

  • python3 -m pytest test_cluster_tool.py -- 160 passed, 8 pre-existing unrelated failures (DNS-related, predate this branch)
  • New regression test test_pull_names_flavor_from_image_tag_not_manifest proves the embedded manifest name is ignored in favor of the tag

Summary by CodeRabbit

  • New Features

    • Simplified the pull command to accept an image reference without a separate name override.
    • Pulled flavors are now named automatically from the image reference.
  • Documentation

    • Updated CLI help and README examples to reflect the simplified pull syntax.
  • Bug Fixes

    • Improved consistency between pulled flavor names, destination paths, and registration behavior.

cmd_pull used args.name (from a --name flag with zero real callers) or
manifest["flavor"] as the local flavor name. Since manifest["flavor"] is
whatever the pushing side's local flavor was named, pulling an image
published under a different tag than its source flavor's name (e.g.
pushing "caas-4-22" under the floating tag "caas") silently registered it
locally under the wrong, unrelated name -- pulling ":caas" would leave the
local "caas" flavor untouched.

The local name now always comes from the pulled reference's tag.
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The pull command no longer accepts --name. It derives the local flavor name from the image reference, with tests and documentation updated to reflect the new behavior.

Changes

Pull flavor naming

Layer / File(s) Summary
Image-derived pull naming
cluster-tool, test_cluster_tool.py, README.md, .claude/skills/spawn-cluster.md
The parser removes --name, cmd_pull derives flavor names from the image reference, tests validate tag-based naming and paths, and CLI documentation is updated.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: eliorerz


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
No-Injection-Vectors ❌ Error cmd_pull interpolates args.image into env.run("skopeo copy docker://{image_ref}..."), and env.run uses shell=True locally; untrusted input reaches a shell. Validate image refs/flavor names with an allow-list and pass subprocess args as lists (or quote each segment); never interpolate raw user input into shell commands.
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: image caching, required pull secret, and pull flavor naming.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
No-Hardcoded-Secrets ✅ Passed Touched files add docs/CLI/test updates only; no hardcoded API keys, passwords, private keys, credentialed URLs, or long base64 secrets were added.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret/token compares appear in the changed pull/snapshot code; only sha256 refs.
Container-Privileges ✅ Passed Changed files are docs/Python only; diff/search found no privileged, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation settings.
No-Sensitive-Data-In-Logs ✅ Passed No new secret/PII-bearing logging found in the changed paths; pull-secret is read and uploaded, not printed, and the new pull/snapshot steps log only status text.
Ai-Attribution ✅ Passed PR head has no AI-tool mention and no Assisted-by/Generated-by/Co-Authored-By trailer; only historical ancestor commits do.
✨ 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.

@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

🤖 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 `@cluster-tool`:
- Line 1473: At the start of cmd_pull, validate args.image/image_ref against a
strict allow-list for permitted image-reference characters and reject traversal
components such as ".." before any shell command or path construction uses it.
Also validate the derived flavor_name with an allow-list that excludes path
separators and traversal values, then continue using the validated value when
constructing flavor_dir and related paths.
🪄 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: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 223f8643-6dee-44fc-a9b6-e15487c4a5a4

📥 Commits

Reviewing files that changed from the base of the PR and between 91c625a and 24c37b0.

📒 Files selected for processing (4)
  • .claude/skills/spawn-cluster.md
  • README.md
  • cluster-tool
  • test_cluster_tool.py

Comment thread cluster-tool
print_step(2, total, "Reading manifest", "done")

flavor_name = args.name or manifest["flavor"]
flavor_name = image_ref.rsplit("/", 1)[-1].rsplit(":", 1)[-1]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Validate flavor_name and image_ref with an allow-list.

Since flavor_name is derived from untrusted user input (image_ref) and used directly to construct file paths (e.g., flavor_dir), it can result in path traversal if the parsed value evaluates to exactly ...

Additionally, because image_ref is interpolated directly into shell commands upstream (e.g., skopeo copy docker://{image_ref}), the absence of validation introduces a command injection vulnerability.

As per path instructions, you must validate at trust boundaries using allow-lists, reject path traversal vectors, and never pass raw user input to shell executions. Consider validating image_ref at the start of cmd_pull to safeguard both shell execution and downstream path construction.

🛡️ Proposed mitigation for flavor_name
+        import re
         flavor_name = image_ref.rsplit("/", 1)[-1].rsplit(":", 1)[-1]
+        if not re.match(r"^[a-zA-Z0-9_.-]+$", flavor_name) or flavor_name in (".", ".."):
+            sys.exit(f"Invalid flavor name derived from image: {flavor_name}")
         state = load_state()

(Note: You should also apply strict allow-list validation on args.image at the start of the function to prevent command 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 `@cluster-tool` at line 1473, At the start of cmd_pull, validate
args.image/image_ref against a strict allow-list for permitted image-reference
characters and reject traversal components such as ".." before any shell command
or path construction uses it. Also validate the derived flavor_name with an
allow-list that excludes path separators and traversal values, then continue
using the validated value when constructing flavor_dir and related paths.

Source: Path instructions

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.

1 participant