Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
45b662f
fix(controller): set KOPIA_LOG_DIR on the kopia server Deployment (#461)
perfectra1n Sep 17, 2026
bd5714c
feat(api,kopia): stream source + stream restore types and the stdin s…
Sep 7, 2026
01e1da3
feat(mover): exec a workload command and stream its stdout into kopia
Sep 7, 2026
f5aa46f
feat(controller,rbac): dedicated stream-mover identity behind a names…
Sep 7, 2026
7abaf87
feat(controller): wire the streamExec restore target through to the m…
Sep 7, 2026
5eeabd9
fix(controller): read the restore destination off RestoreDispatch
perfectra1n Sep 17, 2026
9b52eb5
docs: streamed command sources page + apply-ready examples
Sep 7, 2026
7c4fdc1
docs: renumber the stream-source examples to 45/46
perfectra1n Sep 17, 2026
e9da90f
feat(api): reject epoch parameters kopia would refuse (#458)
perfectra1n Sep 17, 2026
7306990
test(e2e): stream source acceptance scenarios
Sep 7, 2026
1f14110
fix(api): do not mistake materialized schema defaults for user intent
Sep 7, 2026
c3802c9
ci(e2e): give stream sources their own shard
Sep 7, 2026
e718e49
fix(api): list streamExec in the RestoreSpec CEL rule
Sep 7, 2026
000a4c0
fix(mover): compare the epoch size threshold byte-exactly (#458)
perfectra1n Sep 17, 2026
7cc3ad3
fix(controller): derive the stream restore path without naming Source…
perfectra1n Sep 17, 2026
6f3f9dc
feat(api): Snapshot.spec.mover — per-run mover overrides over the pol…
perfectra1n Sep 17, 2026
7760a03
docs: document Snapshot.spec.mover per-run overrides (#464)
perfectra1n Sep 17, 2026
254e557
test(kopia): pin the epoch floors against the real kopia binary (#458)
perfectra1n Sep 17, 2026
ed441c1
fix(verification): fan verification out per matched PVC (#456)
perfectra1n Sep 17, 2026
25c27e2
test(e2e): verification fan-out scenario, per-member #168 gate, docs …
perfectra1n Sep 17, 2026
06ef622
fix(api): reject a zero refreshFrequency and own the derived bounds' …
perfectra1n Sep 17, 2026
871ce88
fix(maintenance): report the bytes a run actually reclaimed (#458)
perfectra1n Sep 17, 2026
9f5d5b3
fix: review round 1 on Snapshot.spec.mover — four findings (#464)
perfectra1n Sep 17, 2026
9126347
Merge branch 'w/458abc' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
b5841fd
feat(controller): dedicated hold when a live-pod inherit resolves not…
perfectra1n Sep 17, 2026
c3d89da
chore(mover): clear three pre-existing clippy denials in stream.rs (#…
perfectra1n Sep 17, 2026
9eed43a
fix(api,controller,webhook): one kopia source-path derivation for eve…
perfectra1n Sep 17, 2026
64d0ce7
fix(verification): fail a quick verify that covered no snapshot (#456)
perfectra1n Sep 17, 2026
cc2ca86
fix(repository): recount index blobs after maintenance (#458)
perfectra1n Sep 17, 2026
5673707
Merge branch 'w/464a' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
c15a551
fix(kopia,mover): bound, de-duplicate and fail-closed the stdin snaps…
perfectra1n Sep 17, 2026
6504131
fix(mover): unstall the exec stderr drain, split the exec-start budge…
perfectra1n Sep 17, 2026
70caacd
fix(controller): the stream-exec gate refuses, and fails CLOSED when …
perfectra1n Sep 17, 2026
2928fb6
fix(maintenance): bound every post-run measurement, clear unmeasured …
perfectra1n Sep 17, 2026
e4d8e91
fix(verification): narrow the #168 discovered unlock to the member id…
perfectra1n Sep 17, 2026
754d28e
fix(controller): heal the inherit hold, trim its message, cover it in…
perfectra1n Sep 17, 2026
ad42a84
fix(api,controller,kopia): fork-guard stream renames, adopt stream sn…
perfectra1n Sep 17, 2026
42fb693
Merge branch 'w/458de' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
0b89a51
docs(verification): stop claiming verification detects the override d…
perfectra1n Sep 17, 2026
8ea2690
Merge branch 'w/456' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
ccb5ab4
fix(kopia,mover): honest retry scope, consumer-bounded object read, u…
perfectra1n Sep 17, 2026
cc5248c
fix(kopia): scope the second stdin-producer retry claim too (#451)
perfectra1n Sep 17, 2026
874f0df
Merge branch 'w/451hard' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
6e6eafc
refactor(api,controller,cli): close #451's convention gaps (#451)
perfectra1n Sep 17, 2026
c802073
docs(451),test(e2e): document streamed sources where readers look, an…
perfectra1n Sep 17, 2026
d061fdf
fix(controller): make the inherit heal survive its own reconcile pass…
perfectra1n Sep 17, 2026
755688a
Merge branch 'w/464b' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
6d3835f
docs(cleanup): correct statements the six issues made stale
perfectra1n Sep 17, 2026
6cf4958
fix(chart,controller): build every mover identity from one pre-capped…
perfectra1n Sep 17, 2026
e59e455
fix(controller),docs: name the object that carries a mover elevation;…
perfectra1n Sep 17, 2026
7a1fc98
fix(controller): rank the index-blob recount on the parsed instant, n…
perfectra1n Sep 17, 2026
bff970c
fix(api): compile-couple the source classifiers, and guard the CEL ru…
perfectra1n Sep 17, 2026
ab5756b
fix(mover): a fanned-out verify failure must not write the replace-on…
perfectra1n Sep 17, 2026
2f83b9e
fix(controller): hoist the #464 inherit heal so doctor names the real…
perfectra1n Sep 17, 2026
d84b9f6
fix(controller): a member-tag collision is refused, and "matched noth…
perfectra1n Sep 17, 2026
2a803cc
Merge branch 'w/fix-c' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
2943083
Merge branch 'w/fix-b' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
5d10942
fix(e2e): restore the unconditional ignore gate on the stream tests (…
perfectra1n Sep 17, 2026
37d5115
chore(deps): patch the rustls advisory and the yanked chacha20 (RUSTS…
perfectra1n Sep 17, 2026
a5d260c
fix(api,cli): correct doc-comment/schema/message defects (D8, D9, D12…
perfectra1n Sep 17, 2026
c3af33d
docs: correct factual defects in restore/stream-source/repository-hea…
perfectra1n Sep 17, 2026
cbf437b
Merge branch 'w/fix-d' into feat/issues-436-451-456-458-461-464
perfectra1n Sep 17, 2026
ae9047e
fix(e2e): the stream-source namespace opt-in used an invalid patch co…
perfectra1n Sep 17, 2026
76e9ff3
fix(e2e): MinIO moved off Docker Hub — pin both images to quay.io
perfectra1n Sep 18, 2026
9633b83
fix(kopia): a broken consumer pipe is not a kopia spawn failure (#451)
perfectra1n Sep 18, 2026
2539f97
fix(mover): a broken consumer pipe must carry the consumer's own expl…
perfectra1n Sep 18, 2026
f026e47
fix(mover): read the exec Status before giving up on a broken consume…
perfectra1n Sep 18, 2026
25f11b6
fix(mover): a streamExec restore silently truncated the data (#451)
perfectra1n Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ jobs:
- shard: hooks-retention
bins: "hooks retention policy_knobs adoption"
backends: true
# Streamed command sources: the mover execs into a workload pod and pipes
# stdout into kopia. Its own shard because the scenarios mutate the
# NAMESPACE annotation that gates stream-exec (on, then off, then on
# again) — a binary sharing the cluster could see a Snapshot parked or
# released by someone else's edit rather than its own.
- shard: stream-sources
bins: "stream_sources"
backends: false
- shard: reshape-async
bins: "verification replication"
backends: false
Expand Down Expand Up @@ -257,8 +265,12 @@ jobs:
# external-snapshotter 8.2, but still default-OFF) — a stack shape no
# other shard uses, and one that would drag every CSI shard along if it
# regressed.
# `verification_fanout` (#456) rides this shard rather than
# `reshape-async`: it needs TWO labelled source PVCs a `pvcSelector`
# can fan out over, and `reshape-async` provisions exactly one
# hostPath source PVC per namespace.
- shard: multi-pvc
bins: "multi_pvc_group"
bins: "multi_pvc_group verification_fanout"
backends: false
csi: true
# #443 populator fan-out: ONE populator Restore fills EVERY claiming
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions crates/api/src/common/mover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,60 @@ impl MoverSpec {
self.privileged_mode,
)
}

/// Overlay `self` — the **HIGHER** layer — onto `base`, field by field, producing
/// one `MoverSpec` that stands in for both. This is what lets an invocation carry
/// its own mover overrides on top of a shared, GitOps-managed recipe:
/// `Snapshot.spec.mover` merged over `SnapshotPolicy.spec.mover` (#464).
///
/// The merge delegates to the SAME primitives the repository-layer merge uses, so a
/// per-run override can never resolve differently from a per-recipe one:
///
/// - `securityContext`/`podSecurityContext` go through [`merge_context_pair`] as one
/// `(container, pod)` layer pair — field-wise per dimension **plus** identity
/// promotion, so a policy's container-level `runAsUser` can never shadow a
/// snapshot's pod-level one. That merge is associative, so folding these two
/// layers here and then handing the result to [`resolve_mover`] is identical to a
/// flat `hardened ⊂ moverDefaults ⊂ inherited ⊂ policy ⊂ snapshot` merge.
/// - `resources` via [`merge_resources_opt`] (per-key requests/limits).
/// - `cache` via [`CacheDefaults::merge`] (per-field).
/// - `privilegedMode`, `ttlSecondsAfterFinished` and `inheritSecurityContextFrom`
/// are whole-value knobs with no inner structure to merge, so the higher layer's
/// value wins outright and `None` falls through to `base`.
///
/// `base: None` is the identity: the result is `self` unchanged. Because the layer
/// order is fixed by this signature, a caller cannot accidentally invert it.
pub fn merge_over(&self, base: Option<&MoverSpec>) -> MoverSpec {
let Some(base) = base else {
return self.clone();
};
// One layer pair, not two independent dimension merges — a lone
// `merge_security_context` here would reintroduce cross-dimension identity
// shadowing (see `merge_context_pair`'s docs).
let (security_context, pod_security_context) = merge_context_pair(
base.security_context.as_ref(),
base.pod_security_context.as_ref(),
self.security_context.as_ref(),
self.pod_security_context.as_ref(),
);
MoverSpec {
resources: merge_resources_opt(base.resources.as_ref(), self.resources.as_ref()),
cache: CacheDefaults::merge(base.cache.as_ref(), self.cache.as_ref()),
security_context,
pod_security_context,
privileged_mode: self.privileged_mode.or(base.privileged_mode),
// Whole-value: a higher layer that names its own inherit source replaces the
// lower layer's outright rather than blending two workload selectors into
// something neither manifest asked for.
inherit_security_context_from: self
.inherit_security_context_from
.clone()
.or_else(|| base.inherit_security_context_from.clone()),
ttl_seconds_after_finished: self
.ttl_seconds_after_finished
.or(base.ttl_seconds_after_finished),
}
}
}

/// How the mover co-locates with the node an RWO source/destination PVC is attached to.
Expand Down
Loading