A Go package for orchestrating operations across OSAPI-managed hosts β typed operations, chaining, conditions, and result decoding built on top of the osapi-sdk engine.
go install github.com/osapi-io/osapi-orchestrator@latestAs a library dependency:
go get github.com/osapi-io/osapi-orchestratorMost operations accept a target parameter to control which agents receive
the request:
| Target | Behavior |
|---|---|
_any |
Send to any available agent (load balanced) |
_all |
Broadcast to every agent |
hostname |
Send to a specific host |
key:value |
Send to agents matching a label |
Agents expose labels (used for targeting) and extended system facts via
AgentList and AgentGet. Facts come from agent-side providers and
include OS, hardware, and network details.
Typed constructors, typed results, and chainable step methods. See the usage docs for full details, examples, and per-operation reference.
Every OSAPI operation has a strongly typed constructor β no raw maps or string constants.
| Method | Operation | Docs | Source |
|---|---|---|---|
HealthCheck |
Liveness probe | docs | ops.go |
NodeHostnameGet |
node.hostname.get |
docs | ops.go |
NodeStatusGet |
node.status.get |
docs | ops.go |
NodeUptimeGet |
node.uptime.get |
docs | ops.go |
NodeDiskGet |
node.disk.get |
docs | ops.go |
NodeMemoryGet |
node.memory.get |
docs | ops.go |
NodeLoadGet |
node.load.get |
docs | ops.go |
NetworkDNSGet |
network.dns.get |
docs | ops.go |
NetworkDNSUpdate |
network.dns.update |
docs | ops.go |
NetworkPingDo |
network.ping.do |
docs | ops.go |
CommandExec |
command.exec.execute |
docs | ops.go |
CommandShell |
command.shell.execute |
docs | ops.go |
FileDeploy |
file.deploy.execute |
docs | ops.go |
FileStatusGet |
file.status.get |
docs | ops.go |
FileUpload |
Upload to Object Store | docs | ops.go |
FileChanged |
Check file drift | docs | ops.go |
AgentList |
List active agents | docs | ops.go |
AgentGet |
Get agent details | docs | ops.go |
Decode step results into typed structs instead of digging through
map[string]any. See result_types.go.
| Struct | Fields |
|---|---|
HostnameResult |
Hostname, Labels |
DiskResult |
Disks (slice of DiskUsage) |
MemoryResult |
Total, Free, Cached |
LoadResult |
Load1, Load5, Load15 |
CommandResult |
Stdout, Stderr, ExitCode, DurationMs, Error |
PingResult |
PacketsSent, PacketsReceived, PacketLoss, Error |
DNSConfigResult |
DNSServers, SearchDomains |
DNSUpdateResult |
Success, Message, Error |
FileDeployResult |
Changed, SHA256, Path |
FileStatusResult |
Path, Status, SHA256 |
FileUploadResult |
Name, SHA256, Size, Changed, ContentType |
FileChangedResult |
Name, Changed, SHA256 |
AgentListResult |
Agents (slice of AgentResult), Total |
AgentResult |
Hostname, Status, Architecture, OSInfo, Memory |
Declare ordering, conditions, and error handling with chainable methods.
See step.go.
o.CommandExec("_any", "whoami").
After(health, hostname).
Retry(2).
OnlyIfChanged().
When(func(r orchestrator.Results) bool {
var h orchestrator.HostnameResult
r.Decode("get-hostname", &h)
return h.Hostname != ""
}).
OnError(orchestrator.Continue)| Method | What it does |
|---|---|
After |
Run after the given steps complete |
Retry |
Retry on failure up to N times |
OnlyIfChanged |
Skip unless a dependency reported changes |
OnlyIfFailed |
Skip unless at least one dependency failed |
OnlyIfAllChanged |
Skip unless all dependencies reported changes |
When |
Guard β only run if predicate returns true |
WhenFact |
Guard β only run if agent fact predicate is true |
OnError |
Set error strategy (StopAll or Continue) |
Use TaskFunc to create custom steps that receive completed results from
prior steps β useful for decision logic, aggregation, or conditional
branching:
o.TaskFunc("summarize", func(ctx context.Context, r orchestrator.Results) (*sdk.Result, error) {
var h orchestrator.HostnameResult
r.Decode("get-hostname", &h)
return &sdk.Result{
Changed: true,
Data: map[string]any{"summary": h.Hostname},
}, nil
}).After(hostname)Pass options to New to configure behavior:
o := orchestrator.New(url, token, orchestrator.WithVerbose())| Option | What it does |
|---|---|
WithVerbose() |
Show stdout, stderr, and response data for all tasks |
After Run() completes, decode individual task results from the report:
report, err := o.Run()
var cmd orchestrator.CommandResult
err = report.Decode("run-uptime", &cmd)
fmt.Println(cmd.Stdout)Inside When guards, inspect the status of completed dependencies:
step.When(func(r orchestrator.Results) bool {
return r.Status("health-check") == orchestrator.TaskStatusChanged
})| Constant | Meaning |
|---|---|
TaskStatusUnknown |
Step not found or has not run |
TaskStatusChanged |
Step ran and reported changes |
TaskStatusUnchanged |
Step ran with no changes |
TaskStatusSkipped |
Step was skipped |
TaskStatusFailed |
Step failed |
When targeting _all or label selectors, access per-host results:
hrs := results.HostResults("deploy")
for _, hr := range hrs {
fmt.Printf("host=%s changed=%v error=%s\n", hr.Hostname, hr.Changed, hr.Error)
var cmd orchestrator.CommandResult
hr.Decode(&cmd)
}Query agents at plan-build time and filter by typed predicates:
agents, err := o.Discover(ctx,
orchestrator.OS("Ubuntu"),
orchestrator.Arch("amd64"),
orchestrator.MinCPU(4),
)
for _, a := range agents {
o.CommandShell(a.Hostname, "apt upgrade -y").After(health)
}| Method | What it does |
|---|---|
Discover |
Query agents filtered by predicates |
GroupByFact |
Group agents by a fact key (e.g. os.distribution) |
Composable filters passed to Discover and GroupByFact:
| Predicate | What it matches |
|---|---|
OS |
Agent OS distribution (case-insensitive) |
Arch |
Agent architecture (case-insensitive) |
MinMemory |
Minimum total memory |
MinCPU |
Minimum CPU count |
HasLabel |
Label key-value pair |
FactEquals |
Arbitrary fact key-value equality |
HasCondition |
Agent has active condition of given type |
NoCondition |
Agent does NOT have active condition |
Healthy |
Agent has no active conditions |
Use WhenFact for execution-time fact checks with a prior AgentList
step:
agents := o.AgentList().After(health)
o.CommandShell("web-01", "apt upgrade -y").
After(agents).
WhenFact("list-agents", func(a orchestrator.AgentResult) bool {
return a.OSInfo != nil && a.OSInfo.Distribution == "Ubuntu"
})Each example is a standalone Go program you can read and run.
| Example | What it shows |
|---|---|
| basic | Simple DAG with health check and hostname query |
| parallel | Five parallel queries depending on health check |
| retry | Retry on failure with configurable attempts |
| command | Command exec and shell with result decoding |
| verbose | Verbose output with stdout/stderr/response data |
| Example | What it shows |
|---|---|
| guards | When predicate for conditional execution |
| only-if-changed | Skip step unless dependency reported changes |
| error-recovery | Continue strategy with OnlyIfFailed cleanup |
| Example | What it shows |
|---|---|
| broadcast | Per-host results from broadcast operations |
| task-func | Custom steps with typed result decoding |
| dns-update | Read-then-write pattern with DNS operations |
| Example | What it shows |
|---|---|
| file-deploy | Upload, deploy, and verify a file end-to-end |
| file-changed | Conditional upload with FileChanged + OnlyIfChanged |
| Example | What it shows |
|---|---|
| agent-facts | List agents with OS, load, memory, and interfaces |
| discover | Find agents by OS and architecture predicates |
| group-by-fact | Group agents by distro, run per-group commands |
| when-fact | Fact-based guard on a step |
| fact-predicates | Compose multiple predicates for discovery |
| label-filter | Filter by labels and arbitrary fact values |
| condition-filter | Filter by node conditions (e.g., DiskPressure) |
cd examples/discover
OSAPI_TOKEN="<jwt>" go run main.goSee the Development guide for prerequisites, setup, and conventions. See the Contributing guide before submitting a PR.
The MIT License.