Job 级可复用 Workflow 执行
状态:待评审
本文定义 v0.x 中 job 级可复用 Workflow 的执行边界。
决策
WorkflowRun 只表示带 inline jobs 的一次执行,不在 root 引用可复用 Workflow。Workflow 是模板:
krt workflow trigger <name>读取模板、校验并渲染 inputs,然后创建带 inline jobs 的WorkflowRun;- job 的
uses表示一次可复用 Workflow 调用; - 该 job ready 后,parent 创建一个带 inline jobs 的 child
WorkflowRun; - 每个 WorkflowRun 都拥有自己的 immutable execution snapshot,只协调直接 jobs 和直接 child WorkflowRuns。
这使嵌套复用天然递归,而不要求一个 controller 携带 root 范围的执行树。parent 将每次调用视为一个 job;child 拥有该调用展开的全部 jobs。
执行拓扑
本模型的 ownership 很直接:
WorkflowRun release
直接 job: build
直接调用 job: deploy
WorkflowRun release-call-deploy
直接 job: apply
直接调用 job: verify
WorkflowRun release-call-deploy-call-verify
直接 job: smoke
每个 controller 只创建和观察自己所 reconcile 的 WorkflowRun 直接拥有的对象。parent/child 状态传播、取消、artifacts 以及未来 PersistentWorkspace 的边界因此都是局部的。
API 形式
WorkflowRun.spec.jobs 必填。删除 WorkflowRun.spec.uses 和 WorkflowRun.spec.with。直接使用 kubectl create 时必须提供 inline jobs。
apiVersion: kruntimes.io/v1alpha1
kind: WorkflowRun
metadata:
name: release
spec:
jobs:
build:
runs-on: bash
steps:
- name: package
run: make package
deploy:
needs: [build]
uses: deploy-workflow
with:
environment: ${{ jobs.build.outputs.environment }}
复用模板的标准触发方式为:
krt workflow trigger deploy-workflow --input environment=staging
-> 校验模板 inputs
-> 将 inputs 渲染到 inline jobs
-> 创建 WorkflowRun
最终 WorkflowRun 保存的是渲染后的 jobs,而不是模板引用,因此模板后续修改不会改变已创建的 root execution。
调用可复用 Workflow
deploy ready 后,parent controller:
- 从同一 namespace 读取
deploy-workflow。 - 使用 caller context 渲染
with,并校验 callee inputs。 - 将
inputs.*渲染到 callee jobs。 - 用这些 inline jobs 创建直接 child WorkflowRun,并为 source Workflow 的每个冻结
output expression 写入一个
kruntimes.io/workflow-output.<name>annotation。 - 设置 owner reference,并将 child 名称写入
parent.status.jobs.deploy.workflowRunName。
child 正常执行,也可以为其 uses jobs 创建自己的直接 child WorkflowRuns。parent 不拥有也不检查 grandchildren。
调用是延迟绑定:被引用的 Workflow 在 caller job ready 时读取。模板修改会影响尚未创建的 child;child 创建后,其渲染 jobs 和 snapshot 都是 immutable。未来若需要更早绑定,应引入显式 template versioning。
局部 Snapshot 与 Output Contract
每个 WorkflowRun 自己拥有一个 ControllerRevision,名称由自身 UID 确定,并记录在 status.snapshotName。它只包含:
spec:接受的 inlineWorkflowRun.spec,包括本地 jobs topology。
apiVersion: apps/v1
kind: ControllerRevision
metadata:
name: release-call-deploy-snapshot-8d91c3f4
ownerReferences:
- apiVersion: kruntimes.io/v1alpha1
kind: WorkflowRun
name: release-call-deploy
data:
spec:
jobs:
apply:
runs-on: bash
steps: [{ name: deploy, run: deploy --environment=staging }]
对于由 reusable Workflow materialize 的 child,controller 会在 child 创建时将冻结的 source output contract 写入 child:
metadata:
annotations:
kruntimes.io/workflow-output.endpoint: ${{ jobs.apply.outputs.endpoint }}
child 在自己的 reconciliation 中初始化自己的 snapshot。output annotations 与它的
inline jobs 一起构成 contract,因此 parent 可以使用 child.status.jobs 计算 outputs,
而无需加载或拥有 child 的 ControllerRevision。如果 child 完成后读取可变的当前 Workflow,
模板变更会让同一 execution 产生不同的 parent output。
一个 snapshot 只由自己的 WorkflowRun 拥有和使用。
Workflow Call Graph Validation
Workflow reuse 不能创建 A -> B -> A 这样的无界调用链。cycle validation 在解析 reusable
Workflow definitions 时完成,且必须在为所选模板或 call job 创建任何 WorkflowRun 之前完成。
krt wf trigger A 递归读取从 A 的 job-level uses 可达的所有 namespace-local Workflow。
它在渲染 inputs 和创建 inline root WorkflowRun 前校验 missing references、cycles 和初始最大
嵌套深度 8。对于 A -> B -> A,trigger 以确定性的
workflow call cycle: A -> B -> A error 失败,且不创建 WorkflowRun。
WorkflowRun controller 对 ready inline job 的 uses: A 应用相同的 graph validation。它在
渲染 inputs 或创建直接 child WorkflowRun 前校验以 A 为根的图。确定性的 validation failure
只将对应 call job 标为 Failed;不创建 child,也不重试。之后正常的 failed-dependency
propagation 会将 dependent jobs 标为 Skipped,普通的 WorkflowRun terminal aggregation 决定
parent phase。
CLI 和 controller 必须共享同一个 graph-validation implementation 和 error format,以便两个 路径对相同的 Workflow graph 作出相同决定。shared logic 加载 namespace-local Workflow definitions,以当前 name stack 做 depth-first traversal,不保存 provenance annotations、 owner-chain metadata 或 root-wide execution tree。scheduler 和 runtimed 行为不变。
Inputs 与 Outputs
JobStatus 增加有界的 outputs map。inline job 和可复用 Workflow 调用的输出都在同一位置暴露:
status:
jobs:
deploy:
phase: Succeeded
workflowRunName: release-call-deploy
outputs:
endpoint: https://staging.example.com
inline job 的所有 steps 成功后,controller 使用 JobSpec.outputs 和 Run status 中的 step outputs 计算 job output。可复用调用的 child WorkflowRun 成功后,parent 读取 child 冻结的 kruntimes.io/workflow-output.<name> annotations,使用 child.status.jobs 求值,并将结果写到 caller job 的 JobStatus.outputs。parent 不读取 child 的 private snapshot。
下游渲染统一使用:
${{ jobs.deploy.outputs.endpoint }}
只有显式声明、有界、结构化的键值 outputs 可以进入 status。日志和大文件不进入 status,继续使用 logging 和 artifact 机制。缺失引用或 output 求值失败会在启动下一个 dependent target 前使受影响 job 失败。
状态、失败与取消
可复用调用 job 有 workflowRunName,没有 step statuses。parent 如下投影其直接 child 的状态:
| Child WorkflowRun | Caller job |
|---|---|
Pending 或 Running | Running |
Succeeded 且 output 求值成功 | Succeeded |
Succeeded 但 output 求值失败 | Failed |
Failed | Failed |
parent 未取消时的 Cancelled | Failed |
取消时,每个 WorkflowRun 只请求取消直接 child Runs 和直接 child WorkflowRuns。parent 仅在这些直接 children 都结束后变为 terminal;递归取消通过 owner watches 和每个 child 的自身 reconcile 自然完成。
Controller 职责
每次 reconcile 中,WorkflowRun controller:
- 加载 WorkflowRun、其局部 snapshot、直接 child Runs 和直接 child WorkflowRuns。
- 从这些资源推导本地 job 和 WorkflowRun status。
- 根据 snapshot spec 与已完成 dependency outputs 计算可运行的本地 targets。
- 创建所有相互独立的 ready targets:inline step 创建 Run,
usesjob 创建 child WorkflowRun。 - 仅当推导状态变化时 patch status。
Scheduler 和 runtimed 仍然只处理独立 Run,不了解 Workflow reuse、snapshot 或 output contract。
校验与限制
WorkflowRun.spec.jobs非空,创建后 immutable。- WorkflowRun 自身不能包含
uses或with。 - 调用 job 包含
needs、uses和可选with,不能包含runs-on或steps。 - 创建 child 前校验 inputs 和 expression references。
- reusable Workflow output name 必须是有效 annotation suffix,并且其冻结 output contract 必须满足 Kubernetes annotation budget。
- 在创建 root 或 child WorkflowRun 前,在解析被引用的 Workflow graph 时检测 Workflow cycle; 初始最大嵌套深度为 8。
- job 与 step outputs 受 CRD 大小限制;artifacts 不是 outputs。
可复用 Actions
本文不定义 Action 的执行机制,但确立一条原则:复用在直接 execution boundary 展开。未来 Action 将在 caller step/Run 中解析,而不加入 root 范围的 Workflow snapshot 或 controller traversal tree。
实现计划
- 增加 local WorkflowRun snapshot envelope 与
JobStatus.outputs。 - 实现
krt workflow trigger的模板 input 校验、渲染和 inline WorkflowRun 创建。 - 实现直接 child WorkflowRun 创建、input rendering 和冻结 output contracts。
- 实现局部 job-output 求值、child-output projection、restart recovery 和模板变更语义测试。
- 添加 nested calls E2E coverage,包括 self-reference 和
A -> B -> Acycle rejection、 output propagation、cancellation,以及 child 创建前后模板更新。 - 单独设计 Action expansion,并沿用相同的直接边界原则。