fix(wfe): propagate shared_volume to sub-workflows via instance data

Sub-workflow steps were not getting a PVC because the K8s executor
checked context.definition.shared_volume which is the child definition
(e.g. lint) — not the root (ci) that declares shared_volume. Only
root definitions carry the config; sub-workflow definitions don't.

Fix: WorkflowHost::start_workflow_with_name injects the config as
_wfe_shared_volume in instance.data. SubWorkflowStep propagates the
parent's data to children, so the config reaches every descendant.
The K8s executor reads it from workflow.data when
definition.shared_volume is None.

Adds a regression test that mirrors the real topology: a child
workflow instance with root_workflow_id set, no shared_volume on its
definition, and _wfe_shared_volume in data — must still get the PVC.
This commit is contained in:
2026-04-09 17:00:24 +01:00
parent 8473b9ca8d
commit cd1ad468f6
3 changed files with 150 additions and 7 deletions

View File

@@ -93,14 +93,35 @@ impl StepBody for KubernetesStep {
// 2. Ensure namespace exists.
ensure_namespace(&client, &ns, workflow_id).await?;
// 2b. If the definition declares a shared volume, ensure the PVC
// 2b. If the workflow declares a shared volume, ensure the PVC
// exists in the namespace and compute the mount spec we'll inject
// into the Job. The PVC is created once per namespace (one per
// top-level workflow run) and reused by every step and
// sub-workflow. Backends with no definition in the step context
// (test fixtures) just skip the shared volume.
let shared_mount = if let Some(def) = context.definition {
if let Some(sv) = &def.shared_volume {
// sub-workflow.
//
// Check two sources: the step's definition (top-level workflow)
// and the inherited `_wfe_shared_volume` key in workflow.data
// (sub-workflows inherit it via parent-data propagation). The
// data path is the normal case for sub-workflows since only the
// root definition declares `shared_volume:`.
let shared_mount = {
// Try definition first (top-level workflow running its own steps).
let sv_from_def = context.definition.and_then(|d| d.shared_volume.as_ref());
// Fall back to inherited data (sub-workflow inherits parent data).
let sv_from_data: Option<wfe_core::models::SharedVolume> = if sv_from_def.is_none() {
context
.workflow
.data
.get("_wfe_shared_volume")
.and_then(|v| serde_json::from_value(v.clone()).ok())
} else {
None
};
let sv = sv_from_def.or(sv_from_data.as_ref());
if let Some(sv) = sv {
let size = sv
.size
.as_deref()
@@ -119,8 +140,6 @@ impl StepBody for KubernetesStep {
} else {
None
}
} else {
None
};
// 3. Merge env vars: workflow.data (uppercased) + config.env.