Resources & agents
Declaring a resource returns a handle; the outputs are reachable only
through Ready. The mechanics — the record, the phases, the ownership
tree — are in Concepts / Resources; this
page is the code surface.
Declaring agents
// The record waits for its agent to connect (a fresh VM brings the
// agent through user-data).
vmAgent := pipeline.NewAgent(ctx, "edge-1",
pipeline.WithLabels(map[string]string{"role": "edge"}))
// The machine already exists: the system's only touch is the ssh
// install. The key is a secret NAME; the host key is required —
// a control plane opening a root shell does not trust-on-first-use.
bareAgent := pipeline.NewAgentViaSSH(ctx, "bare-1", pipeline.SSHInstall{
Address: params.BareHost,
User: params.BareUser,
KeyRef: pipeline.Secret(ctx, "bare-ssh-key"),
HostKey: params.BareHostKey,
}, pipeline.WithLabels(map[string]string{"role": "edge"}))
vmAgent.CloudInit() renders the identity for a fresh VM's
user-data — how the agent gets onto a machine the pipeline is about to
create:
vm := k8slib.Resource(ctx, k8sClient, "vm-1", &compute.Instance{
...
Metadata: map[string]*string{"user-data": ptr(vmAgent.CloudInit())},
}, k8slib.WithResourceOption[compute.Instance](pipeline.Children(vmAgent)))
Declaration options
| Option | Meaning |
|---|---|
Parent(h) | the new resource dies with h instead of the run |
Children(h...) | the new resource claims existing resources it should own |
WithLabels(m) | labels on the record — selection by equality |
Need(name, WhereLabel(k,v), WhereIn(k, v...)) | a capability requirement; readiness waits for it |
Selection
edges, err := pipeline.SelectAgents(ctx,
pipeline.WithLabels(map[string]string{"role": "edge"}),
pipeline.Need("docker"))
A snapshot of the matching agents; the selection is foreign — no ownership taken.
Foreign resources
foreign := pipeline.AttachAgent(ctx, "edge-1", pipeline.Need("marker"))
baseline := pipeline.AttachArtifact(ctx, "baseline-report")
Attached resources read like your own; they cannot be a parent or a child.
Outliving the run
Long life is a transfer, not a sleep — the pipeline's stand always exists:
pipeline.ToStand(ctx, vm, pipeline.KeepFor(params.Keep)) // TTL bounds the stay
pipeline.ToStand(ctx, reportArtifact) // lives until an explicit delete
The workflow can return after the transfer completes; the machine stays up. Ownership transfer emits activity heartbeats while waiting for the resource entity, including during creation. Cancellation and activity deadlines still bound the wait, and command errors propagate to the caller.
Ready-to-use implementations are documented in Resource libraries.