| 1 | package legatus |
| 2 | |
| 3 | // Flow is an ordered, host-agnostic sequence of steps — a playbook. A Runner |
| 4 | // executes a Flow against each selected host, fanning out per host. |
| 5 | type Flow struct { |
| 6 | Name string |
| 7 | Steps []Step |
| 8 | } |
| 9 | |
| 10 | // FlowSource is anything that lowers to a runnable Flow: a static Flow (the |
| 11 | // identity case) or a declarative spec that resolves to one (see |
| 12 | // legatus/flowspec). Runner.Run accepts a FlowSource, so callers pass either kind |
| 13 | // interchangeably. Resolve is host-independent — it yields the same Flow for |
| 14 | // every host, so the Runner resolves once before fanning out. |
| 15 | type FlowSource interface { |
| 16 | Resolve() (Flow, error) |
| 17 | } |
| 18 | |
| 19 | // Resolve makes Flow its own FlowSource: a materialized Flow is already resolved, |
| 20 | // so it returns itself unchanged with a nil error. |
| 21 | func (f Flow) Resolve() (Flow, error) { return f, nil } |
| 22 | |
| 23 | // Step is one unit of work. Check probes whether a change is needed (need=false |
| 24 | // reports Skipped and, in dry-run, ends the step). Apply performs the change and |
| 25 | // reports what it did. |
| 26 | type Step interface { |
| 27 | Name() string |
| 28 | Check(fc *FlowCtx) (need bool, err error) |
| 29 | Apply(fc *FlowCtx) (Outcome, error) |
| 30 | } |
| 31 | |
| 32 | // Outcome is what a step reports about what it did. The engine wraps this in a |
| 33 | // StepResult with timing and a derived Status. |
| 34 | type Outcome struct { |
| 35 | Changed bool |
| 36 | Message string |
| 37 | Data any |
| 38 | } |
| 39 | |
| 40 | // AlwaysApply embeds into steps with no meaningful check; Check always reports a |
| 41 | // change is needed. |
| 42 | type AlwaysApply struct{} |
| 43 | |
| 44 | // Check always reports need=true. |
| 45 | func (AlwaysApply) Check(*FlowCtx) (bool, error) { return true, nil } |
| 46 | |
| 47 | type stepFunc struct { |
| 48 | AlwaysApply |
| 49 | name string |
| 50 | fn func(*FlowCtx) (Outcome, error) |
| 51 | } |
| 52 | |
| 53 | func (s stepFunc) Name() string { return s.name } |
| 54 | func (s stepFunc) Apply(fc *FlowCtx) (Outcome, error) { return s.fn(fc) } |
| 55 | |
| 56 | // StepFunc adapts a function into a Step that always applies. |
| 57 | func StepFunc(name string, fn func(*FlowCtx) (Outcome, error)) Step { |
| 58 | return stepFunc{name: name, fn: fn} |
| 59 | } |
| 60 | |