Skip to content

Add gcore image build + publish to the Packer pipeline - #292

Open
thomasjib wants to merge 22 commits into
mainfrom
add-gcore-image-build
Open

Add gcore image build + publish to the Packer pipeline#292
thomasjib wants to merge 22 commits into
mainfrom
add-gcore-image-build

Conversation

@thomasjib

@thomasjib thomasjib commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Adds a gcore builder so lantern-cloud's gcore provider finds a lantern-box boot image (fixes no gcore image matching prefix "lantern-box-" in region 180).

This has already been run successfully.

gcore has no Packer plugin and no cross-region copy API, so one QEMU-built qcow2 — same provisioners as every other builder — is staged in gcore object storage and imported into 29 regions; prune keeps the 3 newest per region.

  • Staging — gcore ingests only by URL via an unauthenticated HEAD+GET, so the qcow2 is briefly anonymous-read in a throwaway randomly-named bucket with a per-run access key. Names masked; always() teardown ordered revoke → empty → delete; 1-day expiry rule and a next-run sweep as backstops; "could not tell" always fails loudly.
  • Imports — all started, then polled together with one sleep per round, spaced by IMPORT_STAGGER_SECS (30). gcore reaps a task not started within 10min of its own created_on, and submitting ~30 at once gave them a shared deadline it could not meet.

GCORE_API_KEY (needs object-storage permission) and GCORE_PROJECT_ID are already set; nothing to pre-create. 37 offline tests. Does not build on the PR — verify with:

gh workflow run build-images.yaml --ref add-gcore-image-build -f builders=gcore -f gcore_regions=180

🤖 Generated with Claude Code

thomasjib and others added 7 commits July 27, 2026 10:50
Gcore has no Packer plugin; build one qcow2 from the stock Ubuntu 24.04
cloud image reusing the shared provisioners, plus a qemu-only generalize
step so the raw disk boots fresh on import.
Import a hosted qcow2 into each gcore region via cloud/v1/downloadimage and
poll each task to FINISHED. Tested locally with a fake curl (no network).
… temp cleanup

Fixes three review findings on the gcore-publish test suite:
- Add fake-curl modes for poll_task's retry (RUNNING then FINISHED, via a
  per-invocation counter file) and timeout (always RUNNING, exhausts
  POLL_ATTEMPTS) branches, previously untested.
- Add a mixed multi-region test (one region FINISHED, another ERROR) to
  confirm the failures counter aggregates heterogeneous results.
- Track and clean up every make_fake_curl temp dir via an EXIT trap, fixing
  a leak (mktemp -d with no cleanup).

gcore-publish.sh is unchanged.
Add gcore to the build matrix and workflow_dispatch defaults with a
configurable gcore_regions input (default 180). gcore builds the qcow2,
uploads it to GCS via the existing WIF auth, signs a 24h URL, imports it
into each region, and prunes to the 3 newest per region.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 14:58
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

The pull request adds Gcore image support through a QEMU Packer build, GitHub Actions orchestration, temporary object-storage staging, per-region imports, polling and visibility handling, image pruning, offline tests, and deployment documentation.

Gcore image pipeline

Layer / File(s) Summary
Packer Gcore image source
deploy/packer/lantern-box.pkr.hcl, deploy/packer/variables.pkr.hcl
Adds the QEMU Gcore source, sensitive SSH password variable, qcow2 output, datacap handling, and image generalization provisioning.
Gcore object-storage control plane
deploy/packer/gcore-storage.sh, deploy/packer/gcore-storage.test.sh
Provisions or reuses storage, creates temporary buckets, rotates access keys, supports cleanup, and tests the storage lifecycle with a fake API client.
Workflow build and publish orchestration
.github/workflows/build-images.yaml
Adds Gcore inputs, matrix selection, QEMU/KVM setup, Packer execution, object-storage staging, regional publication, and cleanup.
Gcore publisher and validation
deploy/packer/gcore-publish.sh, deploy/packer/gcore-publish.test.sh
Imports qcow2 images into configured regions, polls task states, reports visibility, aggregates failures, and tests success, errors, retries, timeouts, HTTP failures, and visibility outcomes.
Gcore pruning and operator documentation
.github/workflows/build-images.yaml, deploy/packer/README.md
Prunes older private Gcore images and documents environment variables, the import flow, local validation, and operator prerequisites.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant Packer
  participant GcoreStorage
  participant GcorePublish
  participant GcoreAPI
  GitHubActions->>Packer: Build qemu.lantern-box-gcore
  Packer-->>GitHubActions: Produce qcow2 artifact
  GitHubActions->>GcoreStorage: Provision temporary storage
  GitHubActions->>GcoreStorage: Upload qcow2 and expose object URL
  GitHubActions->>GcorePublish: Provide URL and regions
  GcorePublish->>GcoreAPI: Import image for each region
  GcoreAPI-->>GcorePublish: Return and update import task
  GitHubActions->>GcoreStorage: Delete staged object and access key
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Gcore image building and publishing to the Packer pipeline.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-gcore-image-build

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.

Copilot AI 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.

Pull request overview

Adds first-class support for publishing lantern-box images to Gcore by introducing a QEMU-based Packer build (to produce a qcow2) and a CI pipeline that uploads/signs the artifact and imports it into multiple Gcore regions.

Changes:

  • Add a QEMU Packer source (qemu.lantern-box-gcore) plus a gcore-only “generalize” step to produce an importable qcow2.
  • Add gcore-publish.sh (+ offline test script) to import the qcow2 URL into each target Gcore region and poll tasks to completion.
  • Extend build-images.yaml with a gcore builder path: build qcow2, upload to GCS, mint a signed URL via WIF, import to regions, and prune old images.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
deploy/packer/variables.pkr.hcl Adds qemu_ssh_password to allow Packer SSH into the QEMU-built cloud image.
deploy/packer/README.md Documents the gcore build→host→import pipeline and operator prerequisites.
deploy/packer/lantern-box.pkr.hcl Adds the QEMU (gcore) source and gcore-only generalization provisioner.
deploy/packer/gcore-publish.sh Implements per-region import and polling of Gcore image tasks.
deploy/packer/gcore-publish.test.sh Adds an offline test harness using a fake curl for deterministic script tests.
.github/workflows/build-images.yaml Adds gcore to the builder matrix, introduces gcore region input, and implements build/upload/sign/publish + pruning for gcore.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread deploy/packer/lantern-box.pkr.hcl
Comment thread deploy/packer/lantern-box.pkr.hcl
Comment thread .github/workflows/build-images.yaml 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.

Actionable comments posted: 2

🤖 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 @.github/workflows/build-images.yaml:
- Around line 558-566: Update the build job timeout expression near the existing
alicloud-ecs timeout logic to also select an extended timeout when
matrix.builder is gcore. Ensure the value scales with the configured
gcore_regions count, while preserving the current default timeout for other
builders and the existing alicloud-ecs behavior.

In `@deploy/packer/gcore-publish.sh`:
- Around line 38-45: Update the jq payload assigned to body in the downloadimage
request to remove the unsupported os_distro and os_version fields, while
preserving name, url, architecture, and os_type.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1abe9e66-abf2-4991-a8c2-9f4db9183e22

📥 Commits

Reviewing files that changed from the base of the PR and between e5f0e33 and 88add66.

📒 Files selected for processing (6)
  • .github/workflows/build-images.yaml
  • deploy/packer/README.md
  • deploy/packer/gcore-publish.sh
  • deploy/packer/gcore-publish.test.sh
  • deploy/packer/lantern-box.pkr.hcl
  • deploy/packer/variables.pkr.hcl

Comment thread .github/workflows/build-images.yaml
Comment thread deploy/packer/gcore-publish.sh
thomasjib and others added 4 commits July 27, 2026 11:33
…alize

The NoCloud build seed sets ssh_pwauth: true so Packer can SSH into the
build VM, which leaves cloud-init's 50-cloud-init.conf with
PasswordAuthentication yes. provision.sh's 01-lantern-harden.conf already
disables password auth and sorts ahead of it, so the shipped image already
rejects password auth — but remove the contradictory build-only drop-in so
that hardening no longer relies on drop-in ordering. Addresses PR #292 review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Verify gcore secrets step (mirroring the Verify OCI region secrets step) that fails fast with a clear error listing any missing gcore secret (GCORE_API_KEY, GCORE_PROJECT_ID, QEMU_SSH_PASSWORD, GCORE_IMAGE_GCS_BUCKET) instead of failing later in packer/gcloud/gcore. Addresses PR #292 review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 60min default is fine for the single-region default (gcore_regions=180);
document that expanding to many regions needs a scaled timeout or parallel
imports (gcore-publish.sh polls regions sequentially). See PR #292 review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace GCS-bucket hosting (and its GCP WIF/IAM + lantern-cloud Terraform)
with gcore's own S3-compatible object storage, provisioned entirely in the
workflow. gcore-storage.sh (gcore control plane) find-or-creates a dedicated
storage instance + bucket and mints an ephemeral access key (revoked in a
cleanup step); the AWS CLI, as a generic S3 client pointed at gcore's endpoint
(not AWS), uploads the qcow2 and presigns a 24h URL.

- add deploy/packer/gcore-storage.sh (+ offline tests) for the storage control plane
- rework gcore build steps: provision -> aws upload/presign -> publish -> always() cleanup
- drop the GCORE_IMAGE_GCS_BUCKET secret and the GCP auth/gcloud steps
- README: document the gcore object-storage flow; only GCORE_API_KEY (object-storage
  scope), GCORE_PROJECT_ID, QEMU_SSH_PASSWORD are needed. No GCP/Terraform setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
.github/workflows/build-images.yaml (1)

256-258: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Remove the unused OIDC permission.

The shown Gcore flow authenticates with GCORE_API_KEY; no step consumes an ID token. Drop id-token: write unless a required action needs it.

🤖 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 @.github/workflows/build-images.yaml around lines 256 - 258, Remove the
unused id-token: write permission from the permissions block in the Gcore
workflow, keeping contents: read unchanged and preserving authentication through
GCORE_API_KEY.
🤖 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 @.github/workflows/build-images.yaml:
- Around line 581-588: Update the qcow2 staging flow around the upload and
presign commands to delete the uniquely keyed S3 object only after all imports
have completed. Add failure/recovery cleanup that removes the same object when
imports fail, while preserving it whenever an import is still pending; keep
access-key revocation behavior unchanged.
- Around line 560-563: In the provisioning step containing the gcore-storage.sh
provision pipeline, enable pipefail before the pipeline so failures from
gcore-storage.sh propagate through the while loop. Preserve the existing output
masking and GITHUB_OUTPUT writes, ensuring subsequent upload steps are skipped
when provisioning fails.

In `@deploy/packer/gcore-storage.sh`:
- Around line 111-120: Update the Gcore storage workflow around prune_keys so
concurrent runs cannot revoke each other’s active access keys. Add
Gcore-specific serialization before pruning and key creation, or change
prune_keys to delete only keys proven stale rather than every existing key;
preserve the existing cleanup and provisioning behavior for non-concurrent runs.

In `@deploy/packer/README.md`:
- Around line 155-157: Correct the cleanup statement in the README to match the
workflow: state that the Gcore storage instance and bucket persist or are
reused, while only the ephemeral access keys are revoked. Do not claim that
gcore-storage.sh tears down the storage resources unless the workflow is updated
to delete them.

---

Nitpick comments:
In @.github/workflows/build-images.yaml:
- Around line 256-258: Remove the unused id-token: write permission from the
permissions block in the Gcore workflow, keeping contents: read unchanged and
preserving authentication through GCORE_API_KEY.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dcb7d4a4-751e-4f17-9347-60f0f08d9db9

📥 Commits

Reviewing files that changed from the base of the PR and between 88add66 and 88373ae.

📒 Files selected for processing (5)
  • .github/workflows/build-images.yaml
  • deploy/packer/README.md
  • deploy/packer/gcore-storage.sh
  • deploy/packer/gcore-storage.test.sh
  • deploy/packer/lantern-box.pkr.hcl
🚧 Files skipped from review as they are similar to previous changes (1)
  • deploy/packer/lantern-box.pkr.hcl

Comment thread .github/workflows/build-images.yaml Outdated
Comment thread .github/workflows/build-images.yaml Outdated
Comment thread deploy/packer/gcore-storage.sh
Comment thread deploy/packer/README.md Outdated
thomasjib and others added 5 commits July 27, 2026 13:57
Keep the pkr.hcl diff scoped to gcore: the earlier packer fmt run re-aligned
pre-existing (drifted) whitespace in the linode and alicloud-ecs source blocks.
Restore those blocks to main's exact formatting so this PR only touches gcore
code. Whitespace-only revert; packer validate still passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The qcow2 is only needed while gcore fetches it during the publish step, so
remove it immediately instead of leaving a multi-GB object in the bucket. Runs
in an always() step before the access-key revoke (the delete needs the still-
valid minted S3 credentials); '|| true' so a missing object never fails the job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Packer's QEMU builder needs xorriso (or mkisofs/hdiutil/oscdimg) to build the
NoCloud cidata seed ISO from the source's cd_content; it isn't preinstalled on
ubuntu-latest, so the build errored with 'could not find a supported CD ISO
creation command'. Add it to the gcore deps step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
import_region used 'curl -f', which discards the response body on an HTTP
error — so a rejected import printed 'returned no task id:' with nothing after
it. Capture body + status instead and report 'HTTP <code>: <body>' so gcore's
reason is visible. Add a test for the HTTP-error path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The object_storages 'address' field (e.g. luxembourg-2.storage.gcore.dev) is
NOT the S3 API endpoint — that host rejects TLS. The working S3-compatible
endpoint is https://<region>.cloud.gcore.lu where <region> is the location's
technical_name (luxembourg-2 -> s-ed1), and SigV4 needs that same region (not
us-east-1). gcore-storage.sh now resolves technical_name and emits region +
endpoint; the workflow's aws steps use them. Verified end-to-end: aws s3 cp +
presign + fetch all succeed against https://s-ed1.cloud.gcore.lu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.

Caution

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

⚠️ Outside diff range comments (2)
deploy/packer/lantern-box.pkr.hcl (2)

1010-1024: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Serialize qemu_ssh_password instead of interpolating it directly.

A password containing ', :, #, a newline, or null can break or change the injected NoCloud userdata. Store the generated userdata from a small templated config, or encode safely, and run shutdown_command without the password-prompt pattern when the ubuntu user already has passwordless sudo.

🤖 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 `@deploy/packer/lantern-box.pkr.hcl` around lines 1010 - 1024, Update the
NoCloud userdata and shutdown configuration in the Packer source to serialize
qemu_ssh_password safely rather than interpolating it directly, preserving
values containing quotes, YAML-special characters, newlines, or null. Generate
the userdata through a small template or equivalent safe encoding, and change
shutdown_command to invoke passwordless sudo without piping the password prompt
when the ubuntu user already has passwordless sudo.

1166-1166: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the build-password verifier from the published image.

passwd -l ubuntu locks the Ubuntu account but keeps the build-time password hash in /etc/shadow, leaving an offline verifier for QEMU_SSH_PASSWORD in every imported image. Replace it with a locked password field only.

Proposed fix
-      "passwd -l ubuntu",
+      "usermod --password '!' ubuntu",
🤖 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 `@deploy/packer/lantern-box.pkr.hcl` at line 1166, Replace the "passwd -l
ubuntu" provisioning command with an operation that sets the ubuntu account’s
password field to a locked value, removing the build-time password hash from
/etc/shadow while preserving the account lock.
🤖 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.

Outside diff comments:
In `@deploy/packer/lantern-box.pkr.hcl`:
- Around line 1010-1024: Update the NoCloud userdata and shutdown configuration
in the Packer source to serialize qemu_ssh_password safely rather than
interpolating it directly, preserving values containing quotes, YAML-special
characters, newlines, or null. Generate the userdata through a small template or
equivalent safe encoding, and change shutdown_command to invoke passwordless
sudo without piping the password prompt when the ubuntu user already has
passwordless sudo.
- Line 1166: Replace the "passwd -l ubuntu" provisioning command with an
operation that sets the ubuntu account’s password field to a locked value,
removing the build-time password hash from /etc/shadow while preserving the
account lock.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c5c0b0f-e9eb-438c-a169-51eaa62c874f

📥 Commits

Reviewing files that changed from the base of the PR and between 88373ae and 11db77b.

📒 Files selected for processing (6)
  • .github/workflows/build-images.yaml
  • deploy/packer/gcore-publish.sh
  • deploy/packer/gcore-publish.test.sh
  • deploy/packer/gcore-storage.sh
  • deploy/packer/gcore-storage.test.sh
  • deploy/packer/lantern-box.pkr.hcl
🚧 Files skipped from review as they are similar to previous changes (3)
  • deploy/packer/gcore-publish.sh
  • deploy/packer/gcore-storage.sh
  • .github/workflows/build-images.yaml

gcore's image importer fetches an unauthenticated URL and does a HEAD
then a GET. The API has no field for source credentials, and a presigned
SigV4 URL is bound to a single HTTP method, so its HEAD preflight 403s.
Switch staging to a throwaway, randomly-named bucket that is made briefly
public-read (anonymous s3:GetObject) for the duration of the import, then
torn down.

- gcore-storage.sh: provision sweeps leftover stage buckets, creates a
  fresh lantern-box-stage-<random> bucket, and emits it; cleanup deletes
  the stage bucket in addition to the access key.
- build-images.yaml: replace the presign step with upload + anonymous
  bucket policy; mask the bucket name and URL; delete the object before
  the control-plane bucket teardown (needs an empty bucket); drop the
  unused id-token: write permission.
- README: document the public-bucket rationale, the accepted tradeoff,
  and the hard-kill sweep caveat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.

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 `@deploy/packer/gcore-storage.sh`:
- Around line 130-134: Update the stale-bucket cleanup loop and the related
prune_keys logic to be ownership-safe under concurrent provision runs. Serialize
lifecycle operations through cleanup, or restrict deletion and access-key
revocation to resources proven stale and owned by the current run; do not rely
on random bucket names or prefix matching alone. Apply the same ownership
protection to the additional cleanup block noted in the comment.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f425b91c-0cbb-4360-ad51-c5fb65ea1d69

📥 Commits

Reviewing files that changed from the base of the PR and between 11db77b and 7c486d0.

📒 Files selected for processing (4)
  • .github/workflows/build-images.yaml
  • deploy/packer/README.md
  • deploy/packer/gcore-storage.sh
  • deploy/packer/gcore-storage.test.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • deploy/packer/README.md

Comment thread deploy/packer/gcore-storage.sh Outdated
gcore has no API to *set* image visibility — neither POST /downloadimage nor
PATCH /images accepts the field, and private/visibility exist only as list
filters — so gcore-publish.sh reads it back once each import task FINISHES:

- private: logged, nothing else.
- public: fails the publish. That's gcore's global catalog, i.e. an exposure.
- shared: warns only. Still project-only (gcore exposes no image-member API),
  but ?private=true listings skip it, including the prune job's, so the symptom
  is a green build and an image nothing finds later.

A missing or unreadable value only warns; the import already succeeded, so a
failed diagnostic call must not fail the publish.

Also shortens the comments added across the gcore pipeline to match the
surrounding 1-2 line style, and drops this repo's description of how the boot
image is resolved downstream — lantern-box is public.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.

Actionable comments posted: 2

🤖 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 `@deploy/packer/gcore-publish.sh`:
- Around line 108-110: Make the gcore-publish.sh visibility handling return a
non-zero status for shared images instead of treating them as a successful
publish; retain the warning for other non-private visibilities. Update
deploy/packer/gcore-publish.test.sh lines 208-221 to expect failure for shared
visibility, and update deploy/packer/README.md lines 161-164 to document shared
as failed/nonconformant unless shared-image retention is explicitly implemented.
- Around line 92-100: Update the visibility lookup in the image publish flow so
failures or empty/malformed results from the image API or jq cause the function
to return a nonzero status instead of returning success. Preserve the existing
warning, and optionally add only bounded retries, ensuring publishing cannot
complete when image visibility is unverifiable.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f8ba3a71-96b7-46f7-8c92-f01ffc58e91a

📥 Commits

Reviewing files that changed from the base of the PR and between 7c486d0 and a5a9ba9.

📒 Files selected for processing (7)
  • .github/workflows/build-images.yaml
  • deploy/packer/README.md
  • deploy/packer/gcore-publish.sh
  • deploy/packer/gcore-publish.test.sh
  • deploy/packer/gcore-storage.sh
  • deploy/packer/lantern-box.pkr.hcl
  • deploy/packer/variables.pkr.hcl
🚧 Files skipped from review as they are similar to previous changes (4)
  • deploy/packer/gcore-storage.sh
  • deploy/packer/variables.pkr.hcl
  • deploy/packer/lantern-box.pkr.hcl
  • .github/workflows/build-images.yaml

Comment thread deploy/packer/gcore-publish.sh Outdated
Comment thread deploy/packer/gcore-publish.sh
thomasjib and others added 3 commits July 28, 2026 18:01
Addresses the outstanding review feedback on the gcore staging path.

Concurrency (CodeRabbit: prune_keys/sweep across concurrent runs). The
storage instance is shared, so provision prunes ALL its access keys and
sweeps ALL its stage buckets — two overlapping runs would revoke each
other's credentials mid-upload and delete each other's buckets. Add a
job-level `build-images-<builder>` concurrency group with
cancel-in-progress: false, which serializes the whole lifecycle from
provision through the always() teardown steps. Never cancel in flight: a
cancelled gcore build is the one case that can strand a public bucket.

Provision failures (CodeRabbit: pipefail). The old
`gcore-storage.sh provision | while read` form took the loop's exit
status, so a failed provision reported success and the real error only
surfaced two steps later inside `aws s3 cp`. Capture the output instead,
under an explicit `shell: bash` for -eo pipefail, and validate that every
required key was emitted before writing any output.

Staged object lifetime (CodeRabbit: expire the staged qcow2). The object
is now removed on every path: an always() empty-bucket step (which also
aborts incomplete multipart uploads, since `s3 rm` leaves the parts of a
killed upload behind and those block the bucket delete forever), the
bucket delete after it, a stale-bucket sweep on the next run's provision,
and a 1-day object-expiry rule attached at provision time as the backstop
for a run that dies so hard no teardown runs at all.

Visibility now fails closed (CodeRabbit: fail closed when visibility
cannot be verified). Both lookups behind the check — the image ID from
the import task, then that image's visibility — used to warn and return
0. That turned "we cannot tell whether this image is in gcore's global
catalog" into a green build, on the only check that would catch it. They
now retry VISIBILITY_ATTEMPTS times (default 3) and then fail the region,
naming the image so a human can check it. The bounded retry is what keeps
one API blip from sinking an otherwise good build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two review findings.

The region list had two conflicting defaults: the workflow_dispatch input was
prefilled with 30 regions while the resolve step's fallback — the value a
push-to-main run actually uses — was the single region 180, which the README
also documented as "the default". Merging therefore published to one region
while a manual run with prefilled defaults published to thirty. lantern-cloud
resolves the boot image per region and fails outright where one is missing, so
the automatic build now covers every region it provisions in, exactly as
oracle-oci (36) and alicloud (8) already do. The list lives in exactly one
place — the resolve step — and the dispatch input is left blank rather than
prefilled so there is no second copy to drift, while still overriding the list
for a scoped test.

QEMU_SSH_PASSWORD was a long-lived repo secret whose hash ships in the qcow2's
/etc/shadow, and that qcow2 is briefly world-readable while gcore imports it.
The build job now mints `openssl rand -hex 24` per run and masks it, so the
hash is worthless the moment the job ends rather than valid for every later
build. Hex rather than base64 because the value is interpolated into the
cloud-init YAML and into shutdown_command's single-quoted shell string; that
YAML interpolation is now quoted too, so an all-digit value cannot parse as an
int. Drops the secret from the merge prerequisites entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two fixes from the first full 30-region publish run, which lost 3 of 30 regions
for two unrelated reasons.

Region 6 (Luxembourg) is retired for VM use: state=ACTIVE but 0 flavors, and the
project has no image quota in it (image_count and image_size both limit=0), so
its import is rejected at the POST on every run. Requesting quota would not help
— nothing can boot there. Luxembourg stays covered by 76 (Luxembourg-2) and 148
(Luxembourg-3), both of which imported fine, so drop 6 from the list.

Regions 64 (Hong Kong) and 68 (Chicago) failed differently: their tasks never
started. Gcore reaped both with "Task was not started within 10 minutes of
creation. Marked as failed by scheduler cleanup." The POSTs were accepted, but
all 29 landed inside one 22s window, giving every task a near-identical deadline
the scheduler could not meet. The reaped pair was arbitrary rather than the last
queued — seven regions POSTed after them succeeded — so this would keep hitting
different regions run to run. Space the POSTs by IMPORT_STAGGER_SECS (default
30) so each task's deadline moves with its own created_on; it also keeps ~30
clients off the same 2.8GB object at once. Neither the job timeout nor
POLL_ATTEMPTS needs adjusting: the budget already allows 3min/region for the
POST phase, and the poll window is unchanged in length, only shifted later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comments only — no code, config, or test logic changed. Cuts the branch's added
comment lines from 470 to 375, keeping the load-bearing facts (why the POSTs are
staggered, why visibility fails closed, why multipart uploads are aborted first)
and dropping the restatement around them. Pre-existing comments from main are
left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@thomasjib
thomasjib requested review from Crosse and myleshorton August 6, 2026 16:27
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