Architecture Guide
cmd/sind/ CLI commands (cobra)
├── main.go Entry point
├── root.go Root command, --realm and -v flags (root-local, TraverseChildren)
├── context.go Dependency injection via context
├── logging.go Logger construction from -v verbosity
├── completion.go Shell completion for cluster/node names
├── nodeargs.go Node argument parsing
└── *.go One file per command group
pkg/cmdexec/ Command executor abstraction
├── exec.go Executor interface, OSExecutor, MockExecutor
├── logging.go LoggingExecutor (TRACE-level command logging)
├── recorder.go RecordingExecutor for integration tests
└── recording.go Recorded call types
pkg/docker/ Docker CLI wrapper
├── client.go Client type, run/exists helpers
├── container.go Container operations
├── network.go Network operations
├── volume.go Volume operations
└── image.go Image operations
pkg/cluster/ Cluster operations (orchestration)
├── create.go Cluster creation flow
├── delete.go Cluster deletion
├── get.go Listing clusters, nodes, networks, volumes
├── status.go Health status collection
├── worker.go Worker add/remove
├── power.go Power state operations
├── node.go Node initialization and setup
├── ssh.go SSH arg building, host key collection
├── logs.go Log command arg building
├── dns.go DNS record management
├── naming.go Resource naming conventions
└── preflight.go Pre-creation validation
pkg/config/ YAML configuration parsing and validation
pkg/log/ Context-based structured logging (slog)
pkg/mesh/ Global infrastructure (mesh network, DNS, SSH)
pkg/probe/ Node readiness probes
pkg/nodeset/ Nodeset expansion (worker-[0-3])
pkg/slurm/ Slurm config generation and version discovery
pkg/ssh/ SSH key generation and injection
cmd/sind → pkg/cluster → pkg/docker → pkg/cmdexec
→ pkg/config
→ pkg/log
→ pkg/mesh → pkg/docker
→ pkg/cmdexec
→ pkg/probe → pkg/docker
→ pkg/slurm → pkg/docker
→ pkg/ssh → pkg/docker
→ pkg/nodeset
The pkg/cmdexec package provides the executor abstraction at the bottom of the stack. pkg/docker wraps Docker CLI commands and pkg/mesh uses a separate executor for system commands (resolvectl, systemctl). The pkg/cluster package orchestrates everything.
-
Create the command file in
cmd/sind/(e.g.,mycommand.go) -
Define the cobra command with
Use,Short,Args, andRunE -
Wire it up in
root.goviacmd.AddCommand(newMyCommand()) -
Use context helpers to get the Docker client and mesh manager:
client := clientFrom(cmd.Context()) realm := realmFromFlag(cmd) meshMgr := meshMgrFrom(ctx, client, realm) -
Implement the operation in
pkg/cluster/(not incmd/sind/) -
Write tests for both the CLI layer and the cluster operation
The CLI layer should be thin — argument parsing, flag handling, and output formatting. Business logic belongs in pkg/cluster/.
- Add the method to
pkg/docker/client.go(or the appropriate resource file) - Follow the pattern: call
c.run()orc.runWithStdin(), parse output - Use strong types:
ContainerName,NetworkName,VolumeName, etc. - Write unit tests using
MockExecutor
All external commands go through the cmdexec.Executor interface (from pkg/cmdexec), making every operation testable:
type Executor interface {
Run(ctx context.Context, name string, args ...string) (stdout, stderr string, err error)
RunWithStdin(ctx context.Context, stdin io.Reader, name string, args ...string) (stdout, stderr string, err error)
}
pkg/docker uses an executor for Docker CLI calls. pkg/mesh uses a separate executor for system commands (resolvectl, systemctl, pkcheck). The CLI wraps the mesh executor in a LoggingExecutor to emit TRACE-level logs for system commands.
The CLI layer injects dependencies via Go context:
ctx = withClient(ctx, client)
ctx = withMeshManager(ctx, meshMgr)
ctx = sindlog.With(ctx, logger) // injected by PersistentPreRunE
Commands retrieve them with clientFrom(ctx) and meshMgrFrom(ctx, ...). The logger is injected automatically by the root command’s PersistentPreRunE based on the -v flag count.
The pkg/log package provides context-based logging via slog. All pkg/ code extracts the logger from context — never from slog.Default():
log := sindlog.From(ctx)
log.InfoContext(ctx, "creating cluster", "name", cfg.Name)
log.DebugContext(ctx, "waiting for node", "node", shortName)
log.Log(ctx, sindlog.LevelTrace, "docker", "cmd", strings.Join(args, " "))
When no logger is in the context (library use without the CLI), From returns a no-op logger. In errgroup goroutines, use gctx (not the outer ctx) for log calls.
All resource names are derived from cluster name and realm via functions in pkg/cluster/naming.go. Never hardcode resource name prefixes.