Skip to content

[Draft] D8 snapshot unified contract#409

Open
kneumoin wants to merge 11 commits into
feat/d8-snapshot-new-endpointsfrom
d8-snapshot-unified-contract
Open

[Draft] D8 snapshot unified contract#409
kneumoin wants to merge 11 commits into
feat/d8-snapshot-new-endpointsfrom
d8-snapshot-unified-contract

Conversation

@kneumoin

Copy link
Copy Markdown

d8 CLI: migrate snapshot commands to the unified-snapshot contract

Context

Aligns d8 snapshot (download / upload(import) / restore / describe) with the unified
snapshot contract. Contract SSOT: docs/2026-06-29-unified-snapshots-overview.md.

PR base is the d8-snapshot branch (tip cdc2e9b); the contract changes sit on top of it.
Old archives / on-disk format backward compatibility are intentionally not supported.

What changes

Stage 1 — API group migration

  • Core Snapshot / SnapshotContent → group state-snapshotter.deckhouse.io.
  • DataImport import path reworked to the storage-foundation PopulateData contract.
  • DataExport → group storage-foundation.deckhouse.io.

Stage 2a — import marker + cleanup

  • spec.source.import: {}spec.mode: Import (dedicated SnapshotMode type;
    an unknown / non-string mode is an error).
  • Dropped snapshotClassName from create (the --snapshot-class flag is removed).
  • New namespaced-status parsing: ParseNodeStatus + NodeData / SourceRefIdentity
    (actual wire contract is status.data.{source,artifact,...}; the legacy
    SnapshotDataBinding type is no longer used).
  • Fail-closed validation: status.sourceRef.uid is required; the full SnapshotIdentity
    (apiVersion/kind/namespace/name/uid) is validated at the start of ParseNodeStatus.
  • status.sourceRef.namespace is required for namespaced sources, but legitimately absent
    for the cluster-scoped v1/Namespace source (root capture Snapshot).
  • On-disk archive layout preserved (Variant A): the premature ArchiveNodeDirName is
    removed; the readable directory base is SourceRef.Name (falling back to Node.Name).

Stage 2b — download/import without SnapshotContent

  • The snapshot tree is built solely from namespaced status.sourceRef / status.data /
    status.childrenSnapshotRefs; cluster-scoped SnapshotContent is no longer read.
  • source.Node model: SourceRef *SourceRefIdentity, Data *NodeData, plus
    Identity(), IsVolumeLeaf(), DirBaseName().
  • Node identity / resume / collision keys are anchored on UID (archive SnapshotYAML.UID
    replaces the string SourceRef).
  • Import waits on the namespaced Ready condition; removed waitSnapshotContentReady,
    snapshotContentGVR, and the old content conditions
    (ManifestsReady/VolumesReady/ChildrenReady).
  • Removed the dead aggapi.ContentManifestsDownload — the last runtime path to snapshotcontents.

Invariants (enforced by tests)

  • No runtime Get/List/Watch of SnapshotContent in the download/import paths.
  • At most one captured volume (status.data) per node — Variant A.
  • Status sources are order-independent; identity is validated fail-closed.

Testing

  • go test ./internal/snapshot/... — green (all fixtures moved off SnapshotContent to
    namespaced status / unstructured objects).
  • Behavioral guard test TestPipeline_Download_NeverReadsSnapshotContent: wraps the fake
    client with an interceptor that fails on any access to SnapshotContent, then runs the
    full download over an aggregator + orphan-leaf tree (the scenario that previously resolved
    via boundSnapshotContentName → SnapshotContent).
  • go vet ./internal/snapshot/... — clean.
  • golangci-lint run ./internal/snapshot/...0 issues.

Real-cluster e2e (download → upload(import) → restore) is out of scope for this PR
(Stage 3: linux/amd64 cross-build + deploy to a node).

Out of scope / follow-ups

  • Optional DataImport hardening (PopulateData: complete on Completed=True + presence of
    status.data.artifact) — tracked separately to keep the diff focused.
  • d8 snapshot restore still reads status.boundSnapshotContentName (the Snapshot's own
    status, not a Get of a SnapshotContent object); fully removing the legacy field is separate.

kkozoriz and others added 10 commits July 14, 2026 13:32
…eData contract

Target the storage-foundation.deckhouse.io/v1alpha1 DataImport and build a
PopulateData spec (mode + snapshotRef + storageParams sourced from the archived
volume info) instead of the removed Mode A dataArtifactType/targetRef; read the
produced artifact from status.data.artifact.
…khouse.io

The download path (and d8 data export) targets the storage-foundation DataExport
CRD now served on the cluster. targetRef already carries kind, which the
storage-foundation contract requires; the legacy resource field is pruned by the
CRD's preserveUnknownFields: false.
…tus parsing, spec.mode import marker, drop snapshot-class
…entity in ParseNodeStatus; sync stage2 plan to NodeData
@kneumoin kneumoin requested a review from ldmonster as a code owner July 16, 2026 11:39
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