Skip to main content

merge_as

Function merge_as 

Source
pub async fn merge_as<K, B>(
    api: &Api<K>,
    name: &str,
    field_manager: &str,
    body: &B,
) -> Result<K, Error>
Expand description

Merge-patch the PRIMARY resource endpoint of any kube Resource under field_manager with force = true — the merge-strategy sibling to apply on the (Patch-strategy × PatchParams-posture) matrix.

Owns the two-link chain apply_patch_params(<mgr>) + api.patch(name, &pp, &Patch::Merge(&body)) at ONE substrate owner. Closes the four-corner posture matrix the wire-side patch family stamps:

StrategyPatchParams::default()apply_patch_params(<mgr>)
Mergemergemerge_as (this one)
Apply(invalid — SSA requires a field manager)apply

merge owns the anonymous-writer merge-patch corner (PatchParams::default(), no field-manager ownership); apply owns the SSA corner (Patch::Apply under a named field manager); this primitive owns the remaining corner — a merge-patch that STILL stamps a named field manager on the write, chosen when the caller wants merge-patch semantics (server merges the caller’s partial body into the existing object per RFC 7396, rather than the SSA ownership reconciliation model) BUT wants the write attributed to a named controller in the field-manager ownership audit (so downstream kubectl get -o yaml’s managedFields distinguishes a tatara-pool-reconciler-stamped bind edit from a tatara-reconciler-stamped phase-transition status write).

Pre-lift the two-link chain was hand-authored at TWO workspace-wide consumer sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold, both inside tatara-pool-reconciler::controller_allocation:

  • Bind arm — stamps the compound spec.lifetime overlay + the three metadata.annotations requestor / allocation / requestor-kind labels onto the pool member Process on transition from Queued to Bound. Body: {"spec": {"lifetime": …}, "metadata": {"annotations": {REQUESTOR: …, ALLOCATION: …, REQUESTOR_KIND: …}}}. Field manager: ctx.config.field_manager (per-instance String).
  • Release arm — stamps the single tatara.pleme.io/return-trigger annotation onto the member Process to nudge the Pool reconciler into taking the return path. Body: annotation_body-composed single-key metadata edit. Field manager: ctx.config.field_manager (same String).

Both sites walked the SAME two-link chain — build a PatchParams via apply_patch_params with the pool-reconciler’s per-instance field_manager, then dispatch through api.patch(name, &pp, &Patch::Merge(&body)). Post-lift each callsite reads tatara_process::patch::merge_as(&api, name, <mgr>, &body).await and the params-build + Patch::Merge wire dispatch lives at ONE substrate owner. A future normalization of the named-merge-writer posture (an injectable dry_run mode for a shadow-mode rollout, a field_validation default when the pool-reconciler flips on strict validation, an injectable retry policy for the transient-conflict class the bind arm surfaces on race with a sibling pool controller, a resourceVersion precondition slot when the pool controller stamps generation-fenced binds) lands at THIS ONE substrate primitive (or at apply_patch_params on the params sub-axis) and every downstream named-merge writer inherits the upgrade mechanically.

Directly benefits the P3 kenshi-runner library lift (any test-Job controller that stamps a named-merge overlay on its owning Process — a suite-progress annotation, a per-run bind edit — rides through the same primitive as the pool-reconciler’s bind + release arms) and the P5 shigoto Dag refactor (any RecordingJob that stamps a per-instance-named merge edit on a phase transition, rather than through the crate::patch::apply SSA path or the anonymous merge path, rides through this substrate corner rather than hand-authoring the two-link chain a third time).

The bound relaxation K::DynamicType: Default is NOT required here (matching apply’s posture, differing from merge / merge_status) so a future Api<DynamicObject> consumer of the named-merge corner rides the same primitive as the current concrete-Api<Process> consumers. Api::patch itself needs only K: Clone + DeserializeOwned + Debug on its own impl block; the Default bound on the sibling merge primitives is a legacy of their pre-lift call sites, none of which exercised DynamicObject.

Return-form axis: Result<K, kube::Error> matches Api::patch verbatim. Both pre-lift consumers ignore the returned K (the bind arm captures the Err for a retry decision; the release arm discards through let _ = …); keeping the return in the signature lets a future writer that needs the reconciled resourceVersion / generation from the same wire round-trip read it without a re-fetch.

Theory anchor: THEORY.md §VI.1 (generation over composition — the two-link apply_patch_params(<mgr>) + api.patch(name, &pp, &Patch::Merge(&body)) chain recurred at 2 hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger inside one workspace crate, and is lifted onto ONE substrate owner here, closing the (Patch-strategy × PatchParams-posture) matrix’s remaining hand- authored corner). THEORY.md §II.1 invariant 5 (composition preserves proofs — the pin block below binds the Patch::Merge posture + the apply_patch_params pass-through + the byte-identical parity with the pre-lift two-link chain, so a regression that drifts any surface surfaces here rather than as silent named-merge writer skew across the two pool-reconciler callsites).