From 39402c71681604991bb22205076431dcfc4c2b5a Mon Sep 17 00:00:00 2001 From: Spencer Heywood Date: Wed, 13 May 2026 12:36:06 -0600 Subject: [PATCH] spindle: support exposing docker socket --- docs/DOCS.md | 1 + spindle/config/config.go | 1 + spindle/engines/nixery/engine.go | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/DOCS.md b/docs/DOCS.md index cbb3fc8c..842cff21 100644 --- a/docs/DOCS.md +++ b/docs/DOCS.md @@ -967,6 +967,7 @@ Spindle is configured using environment variables. The following environment var - `SPINDLE_SERVER_DEV`: A boolean indicating whether the server is running in development mode (default: `false`). - `SPINDLE_SERVER_OWNER`: The DID of the owner (required). - `SPINDLE_SERVER_LOG_DIR`: The directory to store workflow logs (default: `"/var/log/spindle"`). +- `SPINDLE_SERVER_DOCKER_SOCKET`: Path to Docker socket to expose to invoked Spindle containers (default: `""`). - `SPINDLE_PIPELINES_NIXERY`: The Nixery URL (default: `"nixery.tangled.sh"`). - `SPINDLE_PIPELINES_WORKFLOW_TIMEOUT`: The default workflow timeout (default: `"5m"`). diff --git a/spindle/config/config.go b/spindle/config/config.go index ba17151b..da482b50 100644 --- a/spindle/config/config.go +++ b/spindle/config/config.go @@ -22,6 +22,7 @@ type Server struct { QueueSize int `env:"QUEUE_SIZE, default=100"` MaxJobCount int `env:"MAX_JOB_COUNT, default=2"` // max number of pipelines that run at a time MaxConcurrentWorkflows int `env:"MAX_CONCURRENT_WORKFLOWS, default=8"` // max number of workflow containers running at once (memory cap) + DockerSocket string `env:"DOCKER_SOCKET"` // path to a docker socket to expose to workflow containers } type Tap struct { diff --git a/spindle/engines/nixery/engine.go b/spindle/engines/nixery/engine.go index 0862bfb4..7bf02d2b 100644 --- a/spindle/engines/nixery/engine.go +++ b/spindle/engines/nixery/engine.go @@ -73,6 +73,7 @@ func (ss *setupSteps) addStep(step models.Step) { type addlFields struct { image string container string + mounts []mount.Mount } func (e *Engine) InitWorkflow(twf tangled.Pipeline_Workflow, tpl tangled.Pipeline) (*models.Workflow, error) { @@ -105,6 +106,14 @@ func (e *Engine) InitWorkflow(twf tangled.Pipeline_Workflow, tpl tangled.Pipelin swf.Environment = dwf.Environment addl.image = workflowImage(dwf.Dependencies, e.cfg.NixeryPipelines.Nixery) + if sock := e.cfg.Server.DockerSocket; sock != "" { + addl.mounts = append(addl.mounts, mount.Mount{ + Type: mount.TypeBind, + Source: sock, + Target: sock, + ReadOnly: false, + }) + } setup := &setupSteps{} setup.addStep(nixConfStep()) @@ -239,7 +248,7 @@ func (e *Engine) SetupWorkflow(ctx context.Context, wid models.WorkflowId, wf *m // TODO(winter): investigate whether environment variables passed here // get propagated to ContainerExec processes }, &container.HostConfig{ - Mounts: []mount.Mount{ + Mounts: append([]mount.Mount{ { Type: mount.TypeTmpfs, Target: "/tmp", @@ -251,7 +260,7 @@ func (e *Engine) SetupWorkflow(ctx context.Context, wid models.WorkflowId, wf *m }, }, }, - }, + }, addl.mounts...), ReadonlyRootfs: false, CapDrop: []string{"ALL"}, CapAdd: []string{"CAP_DAC_OVERRIDE", "CAP_CHOWN", "CAP_FOWNER", "CAP_SETUID", "CAP_SETGID"}, @@ -360,6 +369,9 @@ func (e *Engine) RunStep(ctx context.Context, wid models.WorkflowId, w *models.W envs.AddEnv("HOME", homeDir) existingPath := "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" envs.AddEnv("PATH", fmt.Sprintf("%s/.nix-profile/bin:/nix/var/nix/profiles/default/bin:%s", homeDir, existingPath)) + if sock := e.cfg.Server.DockerSocket; sock != "" { + envs.AddEnv("DOCKER_HOST", fmt.Sprintf("unix://%s", sock)) + } mkExecResp, err := e.docker.ContainerExecCreate(ctx, addl.container, container.ExecOptions{ Cmd: []string{"bash", "-c", step.Command()}, -- 2.51.2