Skip to content

feat(image): add property support to create/update/show - #8

Merged
kallioli merged 1 commit into
mainfrom
feat/image-properties
Apr 27, 2026
Merged

feat(image): add property support to create/update/show#8
kallioli merged 1 commit into
mainfrom
feat/image-properties

Conversation

@kallioli

Copy link
Copy Markdown
Contributor

Context

The CLI could not set or update custom Glance image properties
(os_distro, os_version, hw_qemu_guest_agent,
cinder_img_volume_type, …), which made inter-cloud image migration
lossy: properties present on the source image were silently
dropped on re-upload. This PR is the building block needed for a
future orca image migrate (out of scope here, tracked separately).

Changes

  • orca image create --property KEY=VALUE (repeatable). Properties
    ride on the Glance v2 POST body as top-level keys.
  • orca image update --property KEY=VALUE and --remove-property KEY
    compose into a single atomic JSON-Patch document. Add-vs-replace
    is decided after fetching the current image so untouched properties
    survive. Removes are strict by default and turn idempotent under
    --ignore-missing.
  • orca image show surfaces every custom property Glance returns
    plus integrity fields (checksum, os_hash_algo, os_hash_value,
    direct_url, tags):
    • table: a Properties sub-table after the standard fields,
      sorted by key
    • json: a top-level "properties" aggregate; each custom key
      is also mirrored at the JSON root for backward compatibility
      (jq .os_distro keeps working alongside jq .properties.os_distro)
    • value: trailing KEY VALUE lines after the standard fields

Property keys are validated client-side against Glance's
^[A-Za-z0-9_:.\-]{1,255}$ schema before any HTTP round-trip
malformed keys fail fast with a friendly error instead of an opaque
Glance 400. Only the first = splits a value, so URL-like values
(--property url=https://x?a=1&b=2) survive intact.

Tests

15 new tests under tests/test_image.py covering the full lifecycle:

  • create round-trip with multiple --property flags
  • create rejects bad keys before any HTTP write
  • create accepts values containing =
  • update emits replace for existing keys, add for new ones
  • update mixes --property and --remove-property in one patch
  • update remove is strict by default, idempotent under --ignore-missing
  • show renders the Properties sub-table (table format)
  • show nests properties under a top-level properties key (json)
  • show dual-renders custom props at the JSON root (rétrocompat)
  • show emits KEY VALUE lines (value format)
  • show omits the sub-table when there are no custom properties

pytest -q --cov=orca_cli --cov-fail-under=85: 2338 passed, coverage
88.42%. ruff + mypy clean.

Risks

  • os_distro and os_version move from the standard image show
    field set into the Properties section.
    Table/value users see them
    in a different visual section. JSON consumers are unaffected
    thanks to the dual-render at the root. Documented in CHANGELOG.
  • No new dependencies. Reuses the existing JSON-Patch path on
    ImageService.update. Service layer untouched.

Out of scope (tracked separately)

  • One-shot orca image migrate (cloud-to-cloud copy preserving
    everything). This PR is the building block it needs.
  • Property deletion on image create (no use case yet).
  • Schema-aware completion of well-known property keys (nice-to-have).

The CLI could not set or update custom Glance image properties
(`os_distro`, `os_version`, `hw_qemu_guest_agent`,
`cinder_img_volume_type`, …), which made inter-cloud image migration
lossy: properties present on the source image were silently dropped
on re-upload.

Three commands gain matching surface:

- `orca image create --property KEY=VALUE` (repeatable). Properties
  ride on the Glance v2 POST body as top-level keys.
- `orca image update --property KEY=VALUE` and `--remove-property KEY`
  (both repeatable), composable into a single atomic JSON-Patch.
  Add-vs-replace is decided after fetching the current image so
  untouched properties survive. Removes are strict by default and
  turn idempotent under `--ignore-missing`.
- `orca image show` now surfaces every custom property Glance returns
  plus the integrity fields (`checksum`, `os_hash_algo`,
  `os_hash_value`, `direct_url`, `tags`): a `Properties` sub-table
  in table format; a top-level `"properties"` aggregate in JSON; and
  `KEY VALUE` lines in value format. JSON dual-renders each custom
  property at the root *and* under `properties` so existing scripts
  doing `jq .os_distro` keep working alongside the new
  `jq .properties.os_distro`.

Property keys are validated client-side against Glance's
`^[A-Za-z0-9_:.\-]{1,255}$` schema before any HTTP round-trip — a
malformed key fails fast with a friendly error instead of an opaque
Glance 400. Only the first `=` splits a value, so URL-like values
(`--property url=https://x?a=1&b=2`) survive intact.

Tests cover the round-trip on create, untouched-on-update preservation
(only the changed key appears in the JSON-Patch), strict + idempotent
remove modes, key validation aborting before any HTTP write, value
preservation for `=`-bearing strings, and the three show formats
including the JSON dual-render rétrocompat.
@kallioli
kallioli force-pushed the feat/image-properties branch from 0eec10c to 6665791 Compare April 27, 2026 10:15
@kallioli
kallioli merged commit 48cf657 into main Apr 27, 2026
10 checks passed
@kallioli
kallioli deleted the feat/image-properties branch April 27, 2026 10:17
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