Skip to content

Unify CreateVersionMetalFromUrl into CreateVersionMetal#5655

Open
pykello wants to merge 2 commits into
mainfrom
hadi/mi-unify-create
Open

Unify CreateVersionMetalFromUrl into CreateVersionMetal#5655
pykello wants to merge 2 commits into
mainfrom
hadi/mi-unify-create

Conversation

@pykello

@pykello pykello commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Fold the URL-source archive prog into CreateVersionMetal as a second class entry point. The new prog exposes two .assemble_from_* methods, two archive labels (archive_from_vm and archive_from_url, byte-for-byte the current bodies), and a shared #finish that derives both archive_size_mib (from physical_size_bytes) and actual_size_mib (from logical_size_bytes) out of the stats file the host backend already writes for both sources.

  • .assemble_from_vm rolls up the source-VM preflight (arch / metal / single volume / stopped / track_written / key_encryption_key_1 / size cap) and writes a strand at label archive_from_vm. Behavior is unchanged from the old CreateVersionMetal.assemble except that actual_size_mib is no longer pre-computed from source_vm.storage_size_gib; #finish reads logical_size_bytes from the stats file instead, so both source paths derive size the same way.
  • .assemble_from_url picks a VhostBlockBackend host with archive support and writes a strand at label archive_from_url. Behavior is unchanged from the old CreateVersionMetalFromUrl.assemble.

The destroy prog (DestroyVersionMetal) is untouched; this commit only collapses the two create entry points.

The single caller of CreateVersionMetalFromUrl in tree (helpers/machine_image.rb's .assemble call moves to .assemble_from_vm) and the spec/thawed_mock allow_mocking entry are updated; prog/machine_image/create_version_metal_from_url.rb and its spec are deleted.

pykello and others added 2 commits June 12, 2026 21:33
Fold the URL-source archive prog into `CreateVersionMetal` as a second
class entry point. The new prog exposes two `.assemble_from_*` methods,
two archive labels (`archive_from_vm` and `archive_from_url`,
byte-for-byte the current bodies), and a shared `#finish` that derives
both `archive_size_mib` (from `physical_size_bytes`) and
`actual_size_mib` (from `logical_size_bytes`) out of the stats file the
host backend already writes for both sources.

- `.assemble_from_vm` rolls up the source-VM preflight (arch / metal /
  single volume / stopped / `track_written` / `key_encryption_key_1` /
  size cap) and writes a strand at label `archive_from_vm`. Behavior is
  unchanged from the old `CreateVersionMetal.assemble` except that
  `actual_size_mib` is no longer pre-computed from
  `source_vm.storage_size_gib`; `#finish` reads `logical_size_bytes`
  from the stats file instead, so both source paths derive size the
  same way.
- `.assemble_from_url` picks a `VhostBlockBackend` host with archive
  support and writes a strand at label `archive_from_url`. Behavior is
  unchanged from the old `CreateVersionMetalFromUrl.assemble`.

The destroy prog (`DestroyVersionMetal`) is untouched; this commit only
collapses the two create entry points.

The single caller of `CreateVersionMetalFromUrl` in tree
(`helpers/machine_image.rb`'s `.assemble` call moves to
`.assemble_from_vm`) and the `spec/thawed_mock` `allow_mocking` entry
are updated; `prog/machine_image/create_version_metal_from_url.rb` and
its spec are deleted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The `archive_from_vm` / `archive_from_url` labels currently loop
forever when the host-side daemon reports `Failed` or an unexpected
status — the row stays at `status='creating'`, the strand keeps
waking the scheduler, the R2 prefix keeps any partially-uploaded
objects, and there is no signal a user or operator can react to.

After 5 consecutive non-progressing daemon states (`Failed` plus the
"Unexpected daemonizer2 status" else branch), the prog now hops to a
new `cleanup` label, which buds `DestroyVersionMetal` at its
`destroy_objects` entry point and waits for it to pop before the
create prog itself pops with `"Metal machine image version archive
failed"`.

Why bud rather than call `DestroyVersionMetal.assemble`: the parent
strand is at `metal.id`, so a sibling assemble would collide on the
primary key. Bud creates a child strand with a fresh id whose frame
carries `subject_id = metal.id`, and `DestroyVersionMetal`'s
`subject_is :machine_image_version_metal` resolves through that
subject_id transparently. We enter at `destroy_objects` rather than
the assemble preflight because the row we're abandoning is still at
`status='creating'`, has no billing record (created in `#finish`),
and was never wired into `latest_version_id` — so the assemble
preflight is a no-op for this row anyway.

`ARCHIVE_MAX_RETRIES = 5` is a constant for now; it can be lifted to
`Config` later if we discover real-world archive paths that
legitimately need more headroom.

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