diff --git a/spindle/engines/nixery/engine.go b/spindle/engines/nixery/engine.go --- a/spindle/engines/nixery/engine.go +++ b/spindle/engines/nixery/engine.go @@ -35,9 +35,10 @@ type cleanupFunc func(context.Context) error type Engine struct { - docker client.APIClient - l *slog.Logger - cfg *config.Config + dockerMu sync.Mutex + docker client.APIClient + l *slog.Logger + cfg *config.Config slotter engine.WorkflowSlotter @@ -164,15 +165,9 @@ } func New(ctx context.Context, cfg *config.Config) (*Engine, error) { - dcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - return nil, err - } - l := log.FromContext(ctx).With("component", "spindle") e := &Engine{ - docker: dcli, l: l, cfg: cfg, slotter: engine.NewSemaphoreSlotter(cfg.NixeryPipelines.MaxConcurrentWorkflows), @@ -181,6 +176,22 @@ e.cleanup = make(map[string][]cleanupFunc) return e, nil +} + +func (e *Engine) ensureDocker() (client.APIClient, error) { + e.dockerMu.Lock() + defer e.dockerMu.Unlock() + + if e.docker != nil { + return e.docker, nil + } + + dcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, err + } + e.docker = dcli + return dcli, nil } func (e *Engine) AcquireWorkflowSlot( @@ -214,6 +225,10 @@ err = fmt.Errorf("Failed to setup container:\n%w", err) } }() + + if _, err := e.ensureDocker(); err != nil { + return err + } /// -------------------------NETWORK CREATION--------------------------------------- _, err = e.docker.NetworkCreate(ctx, networkName(wid), network.CreateOptions{ diff --git a/spindle/engines/nixery/engine_test.go b/spindle/engines/nixery/engine_test.go new file mode 100644 --- /dev/null +++ b/spindle/engines/nixery/engine_test.go @@ -0,0 +1,25 @@ +package nixery + +import ( + "context" + "testing" + + "tangled.org/core/spindle/config" +) + +func TestNewDefersDockerClientUntilWorkflowSetup(t *testing.T) { + t.Setenv("DOCKER_HOST", "tcp://127.0.0.1:2376") + t.Setenv("DOCKER_TLS_VERIFY", "1") + t.Setenv("DOCKER_CERT_PATH", t.TempDir()) + + e, err := New(context.Background(), &config.Config{}) + if err != nil { + t.Fatal(err) + } + if e.docker != nil { + t.Fatal("docker client initialized during engine initialization") + } + if _, err := e.ensureDocker(); err == nil { + t.Fatal("expected incomplete Docker TLS configuration to fail when first used") + } +}