Open the adapter through dispatch¶
This page is for a developer embedding the adapter in a program built on go/dispatch.
Register it¶
Importing the package registers the adapter under the name slurm (the constant slurm.Name),
together with its two transports and the slurm.Placement and slurm.Resources extensions. A
blank import is enough:
Import it with a name when you also want its types:
The package is slurm whatever the module path ends in.
Open a cluster¶
dispatch.Open finds the adapter by name, hands it Config.Adapter as JSON, and composes the core
over the Backend it returns. Config.Adapter can be a slurm.Config, a map, or raw JSON:
cluster, err := dispatch.Open(ctx, slurm.Name, dispatch.Config{
Adapter: slurm.Config{
Transport: slurm.TransportSSH,
SSH: &slurm.SSH{Host: "login.example.org", User: "ada"},
Defaults: slurm.Defaults{Partition: "gpu"},
},
Store: store, // a records.Records, from go/dispatch
})
The settings are decoded strictly, so a mistake in them is an error from Open, naming the key.
Open runs preflight, so it needs the Slurm host to be reachable, and it can take a while the
first time. Read a preflight result says what happens in it.
To pass no variables at all, set Environment.Pass to an empty, non-nil slice
([]string{}); a nil slice means the default, PATH.
Open it read-only¶
A tool that only looks, such as a status command, can open the cluster without a record store:
cluster, err := dispatch.Open(ctx, slurm.Name, dispatch.Config{
Adapter: settings,
ReadOnly: true,
})
On a read-only backend every writing call returns dispatch.ErrReadOnly before running any command,
and preflight submits no probe. It reads the cached probe result if there is one. Without a
preflight.cacheDir holding a fresh result, Hold and AccountingHistory are unknown, so
Inspect has no sacct to fall back on and a job that has left squeue is dispatch.ErrNotFound.
Run a writable backend with the same cache directory first.
Build a Backend for a supervisor¶
A host that runs a supervisor needs the Backend itself:
Or build it without the registry, which also lets you choose options:
backend, err := slurm.New(ctx, slurm.Config{ /* ... */ },
slurm.ReadOnly(),
slurm.WithClock(clock.Now),
slurm.WithExecutor(myExecutor),
)
| Option | Does |
|---|---|
slurm.ReadOnly() |
writing calls return ErrReadOnly; no probe is submitted |
slurm.WithExecutor(e) |
runs scripts through e instead of the configured transport. The backend closes e |
slurm.WithClock(now) |
reads the caller's time from now: for Find's horizon, preflight's clock offset, StopJob's bound |
*slurm.Backend implements dispatch.Backend, Holder, Requeuer, CessationSignal,
OutputReader and MultiFinder. On a cluster with no known landing bound (an AuthType other
than auth/munge or auth/slurm), dispatch.Open and dispatch.NewBackend hand out
b.DispatchBackend() instead: the same backend without Holder and Requeuer, because it declares
a zero CallBound, which dispatch allows only without them. Don't type-assert *slurm.Backend on
what they return; call DispatchBackend yourself if you build with slurm.New and pass the
backend to a supervisor. It does not implement Watcher, so a supervisor must poll it at
least as often as the shortest PolledLoss window; see
Capabilities and declarations. Call Close when you are done. It closes
the transport; a shared ssh connection stays up until its ControlPersist runs out.
Describe a job¶
A job's Slurm-specific needs ride as extensions on the JobSpec:
spec := dispatch.JobSpec{
Command: []string{"torchrun", "train.py"},
Nodes: 2,
Extensions: dispatch.Extensions{
slurm.Placement{Partition: "gpu", Account: "research"},
slurm.Resources{GPUType: "h100", CPUsPerTask: 8, Exclusive: true},
},
}
How a job becomes an sbatch submission lists what each field does.
Validate settings in your own configuration¶
slurm.Schema() returns the settings' JSON Schema as bytes. A host that keeps its configuration in
a file can mount the schema at the adapter's section and have it checked there, before Open runs.
The schema is advisory: Open decodes strictly with or without it.
Passing the schema does not mean Open will accept the settings. The schema does not check the ssh
values' characters or output.dir's % and \, which the adapter refuses later; the
configuration reference lists the differences. A registered
transport name fails the schema but passes Open.
Test without a cluster¶
The slurmtest package replays recorded transcripts in place of a Slurm host. slurmtest.New
takes a list of Exchange values, each naming a fragment of the script it expects (Key) and the
transport.Result to return, and returns an Executor that answers scripts in order. An
unexpected script is an ErrUnexpectedScript. Pass it to slurm.New with slurm.WithExecutor.
A Key is matched against the script after slurmtest.Normalize has replaced each random nonce
with slurmtest.Nonce, and a Result may carry Nonce too. slurmtest.Jobs renders Slurm's job
JSON for a release (Release2511 or Release2605) from a few Job fields, and Reply, Fail,
Absent and Wrapped build results; Wrapped takes a Call per deadline-wrapped call, and
CallStatus renders one status line.
Preflight is a fixed sequence of scripts, so a transcript for slurm.New has to answer all of them.
The adapter's own tests, in preflight_test.go, show the sequence.
For a real cluster, run the conformance suite against sind.