Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1e27aff
docs: add orchestrator Op layer design
retr0h Mar 14, 2026
f51b826
docs: add orchestrator Op layer implementation plan
retr0h Mar 14, 2026
edfd64c
docs: rewrite orchestrator plan for bridge helpers approach
retr0h Mar 14, 2026
2cf2099
docs: finalize orchestrator bridge helpers plan
retr0h Mar 14, 2026
e69d7ea
docs: remove superseded Op design doc
retr0h Mar 14, 2026
6caff0f
refactor: remove orchestrator container DSL and provider run
retr0h Mar 13, 2026
998dd33
docs: add container runtime rename design
retr0h Mar 13, 2026
fa29048
docs: add container runtime rename implementation plan
retr0h Mar 13, 2026
d0a2a2b
refactor: rename container permissions to docker
retr0h Mar 13, 2026
40eb72f
refactor: rename container internals to docker
retr0h Mar 13, 2026
8eff37f
refactor: rewrite container-targeting example with Docker DSL
retr0h Mar 13, 2026
323e3b4
docs: update container references to docker
retr0h Mar 13, 2026
adb16d5
refactor: rename remaining container references to docker
retr0h Mar 13, 2026
1d8bed8
refactor: remove redundant Docker Service wrapper
retr0h Mar 13, 2026
2f085e1
test: achieve 100% coverage on orchestrator Docker DSL
retr0h Mar 13, 2026
606a310
fix: add Changed to Docker provider result types
retr0h Mar 13, 2026
be3ac09
docs: nest CLI docs under container/docker hierarchy
retr0h Mar 13, 2026
c82bd35
docs: rename orchestrator operation docs to docker prefix
retr0h Mar 14, 2026
77dd676
fix: correct DAG ordering in orchestrator example
retr0h Mar 14, 2026
9a2403e
chore: remove accidentally committed binary
retr0h Mar 14, 2026
37f72c5
fix: remove deliberately-fails from cleanup deps
retr0h Mar 14, 2026
b84a1d2
fix: add pre-cleanup step to orchestrator example
retr0h Mar 14, 2026
0f20423
style: fix table alignment in orchestrator operations
retr0h Mar 14, 2026
f5743ad
feat(orchestrator): add CollectionResult and StructToMap helpers
retr0h Mar 14, 2026
836cead
refactor(orchestrator): remove docker DSL methods
retr0h Mar 14, 2026
af9495d
refactor: update container-targeting to use TaskFunc with bridge helpers
retr0h Mar 14, 2026
172f952
fix: rewrite all orchestrator examples to use TaskFunc
retr0h Mar 14, 2026
a3016c3
docs: update orchestrator docs for TaskFunc pattern
retr0h Mar 14, 2026
4cc79f2
test: achieve 100% coverage on orchestrator bridge helpers
retr0h Mar 14, 2026
49418bb
style: fix line wrapping in orchestrator bridge helpers docs
retr0h Mar 14, 2026
a974301
style: fix line wrapping in orchestrator plan doc
retr0h Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions docs/docs/sidebar/sdk/orchestrator/features/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,48 @@ Create a plan, add tasks with dependencies, and run them in order.
## Usage

```go
client := client.New(url, token)
plan := orchestrator.NewPlan(client)
c := client.New(url, token)
plan := orchestrator.NewPlan(c)

health := plan.TaskFunc("check-health",
func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) {
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
_, err := c.Health.Liveness(ctx)
return &orchestrator.Result{Changed: false}, err
},
)

hostname := plan.Task("get-hostname", &orchestrator.Op{
Operation: "node.hostname.get",
Target: "_any",
})
hostname := plan.TaskFunc("get-hostname",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Hostname(ctx, "_any")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.HostnameResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)
hostname.DependsOn(health)

report, err := plan.Run(context.Background())
```

`Task` creates an Op-based task (sends a job to an agent). `TaskFunc` embeds
custom Go logic. `DependsOn` declares ordering.
`TaskFunc` creates a task with custom Go logic that calls the SDK client
directly. `DependsOn` declares ordering.

## Example

Expand Down
29 changes: 24 additions & 5 deletions docs/docs/sidebar/sdk/orchestrator/features/broadcast.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,28 @@ every matching agent and per-host results are available via `HostResults`.
## Usage

```go
getAll := plan.Task("get-hostname-all", &orchestrator.Op{
Operation: "node.hostname.get",
Target: "_all",
})
getAll := plan.TaskFunc("get-hostname-all",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Hostname(ctx, "_all")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.HostnameResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)

// Access per-host results via TaskFuncWithResults.
printHosts := plan.TaskFuncWithResults("print-hosts",
Expand All @@ -35,7 +53,8 @@ printHosts := plan.TaskFuncWithResults("print-hosts",
) (*orchestrator.Result, error) {
r := results.Get("get-hostname-all")
for _, hr := range r.HostResults {
fmt.Printf(" %s changed=%v\n", hr.Hostname, hr.Changed)
fmt.Printf(" %s changed=%v\n",
hr.Hostname, hr.Changed)
}
return &orchestrator.Result{Changed: false}, nil
},
Expand Down
70 changes: 52 additions & 18 deletions docs/docs/sidebar/sdk/orchestrator/features/file-deploy-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,69 @@ Store, deploy it to agents with template rendering, then verify status.
```go
// Step 1: Upload the template file.
upload := plan.TaskFunc("upload-template",
func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) {
resp, err := c.File.Upload(ctx, "app.conf.tmpl", "template",
bytes.NewReader(tmpl), client.WithForce())
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.File.Upload(
ctx, "app.conf.tmpl", "template",
bytes.NewReader(tmpl), client.WithForce(),
)
if err != nil {
return nil, err
}
return &orchestrator.Result{Changed: resp.Data.Changed}, nil
return &orchestrator.Result{
Changed: resp.Data.Changed,
}, nil
},
)

// Step 2: Deploy to all agents.
deploy := plan.Task("deploy-config", &orchestrator.Op{
Operation: "file.deploy.execute",
Target: "_all",
Params: map[string]any{
"object_name": "app.conf.tmpl",
"path": "/etc/app/config.yaml",
"content_type": "template",
"vars": map[string]any{"port": 8080},
deploy := plan.TaskFunc("deploy-config",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.FileDeploy(ctx, client.FileDeployOpts{
Target: "_all",
ObjectName: "app.conf.tmpl",
Path: "/etc/app/config.yaml",
ContentType: "template",
Vars: map[string]any{"port": 8080},
})
if err != nil {
return nil, err
}

return &orchestrator.Result{
JobID: resp.Data.JobID,
Changed: resp.Data.Changed,
Data: orchestrator.StructToMap(resp.Data),
}, nil
},
})
)
deploy.DependsOn(upload)

// Step 3: Verify the deployed file.
verify := plan.Task("verify-status", &orchestrator.Op{
Operation: "file.status.get",
Target: "_all",
Params: map[string]any{"path": "/etc/app/config.yaml"},
})
verify := plan.TaskFunc("verify-status",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.FileStatus(
ctx, "_all", "/etc/app/config.yaml",
)
if err != nil {
return nil, err
}

return &orchestrator.Result{
JobID: resp.Data.JobID,
Changed: resp.Data.Changed,
Data: orchestrator.StructToMap(resp.Data),
}, nil
},
)
verify.DependsOn(deploy)
```

Expand Down
33 changes: 25 additions & 8 deletions docs/docs/sidebar/sdk/orchestrator/features/introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,28 @@ showing levels, parallelism, dependencies, and guards.
plan := orchestrator.NewPlan(client)

health := plan.TaskFunc("check-health", healthFn)
hostname := plan.Task("get-hostname", &orchestrator.Op{
Operation: "node.hostname.get",
Target: "_any",
})
hostname := plan.TaskFunc("get-hostname",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Hostname(ctx, "_any")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.HostnameResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)
hostname.DependsOn(health)

fmt.Println(plan.Explain())
Expand All @@ -33,12 +51,11 @@ Level 0:
check-health [fn]

Level 1:
get-hostname [op] <- check-health
get-hostname [fn] <- check-health
```

Tasks are annotated with their type (`fn` for functional tasks, `op` for
declarative operations), dependency edges (`<-`), and any active guards
(`only-if-changed`, `when`).
Tasks are annotated with their type (`fn` for functional tasks), dependency
edges (`<-`), and any active guards (`only-if-changed`, `when`).

## Levels

Expand Down
34 changes: 25 additions & 9 deletions docs/docs/sidebar/sdk/orchestrator/features/only-if-changed.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,34 @@ every dependency completed with `Changed: false`, the task is skipped and the
`OnSkip` hook fires with reason `"no dependencies changed"`.

```go
deploy := plan.Task("deploy-config", &orchestrator.Op{
Operation: "file.deploy.execute",
Target: "_any",
Params: map[string]any{
"object_name": "app.conf",
"path": "/etc/app/app.conf",
"content_type": "text/plain",
deploy := plan.TaskFunc("deploy-config",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.FileDeploy(ctx, client.FileDeployOpts{
Target: "_any",
ObjectName: "app.conf",
Path: "/etc/app/app.conf",
ContentType: "text/plain",
})
if err != nil {
return nil, err
}

return &orchestrator.Result{
JobID: resp.Data.JobID,
Changed: resp.Data.Changed,
Data: orchestrator.StructToMap(resp.Data),
}, nil
},
})
)

restart := plan.TaskFunc("restart-service",
func(_ context.Context, _ *client.Client) (*orchestrator.Result, error) {
func(
_ context.Context,
_ *client.Client,
) (*orchestrator.Result, error) {
fmt.Println("Restarting service...")
return &orchestrator.Result{Changed: true}, nil
},
Expand Down
87 changes: 75 additions & 12 deletions docs/docs/sidebar/sdk/orchestrator/features/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,88 @@ don't depend on each other are automatically parallelized.

```go
health := plan.TaskFunc("check-health",
func(ctx context.Context, c *client.Client) (*orchestrator.Result, error) {
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
_, err := c.Health.Liveness(ctx)
return &orchestrator.Result{Changed: false}, err
},
)

// Three tasks at the same level — all depend on health,
// so the engine runs them in parallel.
for _, op := range []struct{ name, operation string }{
{"get-hostname", "node.hostname.get"},
{"get-disk", "node.disk.get"},
{"get-memory", "node.memory.get"},
} {
t := plan.Task(op.name, &orchestrator.Op{
Operation: op.operation,
Target: "_any",
})
t.DependsOn(health)
}
getHostname := plan.TaskFunc("get-hostname",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Hostname(ctx, "_any")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.HostnameResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)
getHostname.DependsOn(health)

getDisk := plan.TaskFunc("get-disk",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Disk(ctx, "_any")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.DiskResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)
getDisk.DependsOn(health)

getMemory := plan.TaskFunc("get-memory",
func(
ctx context.Context,
c *client.Client,
) (*orchestrator.Result, error) {
resp, err := c.Node.Memory(ctx, "_any")
if err != nil {
return nil, err
}

return orchestrator.CollectionResult(
resp.Data,
func(r client.MemoryResult) orchestrator.HostResult {
return orchestrator.HostResult{
Hostname: r.Hostname,
Changed: r.Changed,
Error: r.Error,
}
},
), nil
},
)
getMemory.DependsOn(health)
```

## Example
Expand Down
Loading
Loading