Files
hamlab/internal/compose/compose.go
Mitchell-JohnsonandClaude Opus 5.5 c27d9f0ada Stacks: one machine, one set of files; default home is <data dir>/stacks
The hub no longer probes a node's Docker on tcp :2375, holds compose files
for a node, or runs compose against another machine's Docker. hubStacks,
nodeDocker, taskOnHub and the hub-side /api/nodes/{node}/docker/... routes
are gone; the generic node proxy hands those requests to the node's own
Ham Lab. Stack.remote / held_by and compose.Spec.Remote are removed.

New stacks default to <data dir>/stacks on every machine. Folders with two
or more deployed stacks are still offered, after it, never as default.
The ~/stacks fallback is gone. A missing home is skipped without a sudo find.

A schedule saved earlier with a tcp target is refused with a message
instead of being run against this machine's Docker.

PORTABILITY.md 3.1, 3.2, 3.3 and 9.2 done; 3.5 checked, no change needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:53:10 -07:00

43 lines
1.5 KiB
Go

// Package compose runs docker compose the way a stack was deployed — which is not always
// simply "on this host". A stack deployed by an orchestrator running inside its own
// container records paths in that container's filesystem, so compose has to run in there.
// Both the console and the scheduled updates it writes must invoke it identically, or a
// redeploy from one would not match the other's labels. Compose always drives this
// machine's own Docker: a stack's files and its containers are on the same box.
package compose
import (
"context"
"os/exec"
"path/filepath"
)
// Spec is everything needed to run compose for one stack.
type Spec struct {
Project string // -p
Files []string // -f, in order
Via string // a container to run compose inside; "" is this host
Tool string // "docker" or "podman"
}
// Cmd builds the command. args are compose's own, e.g. "pull" or "up", "-d".
func Cmd(ctx context.Context, s Spec, args ...string) *exec.Cmd {
tool := s.Tool
if tool == "" {
tool = "docker"
}
base := []string{"compose", "-p", s.Project}
for _, f := range s.Files {
base = append(base, "-f", f)
}
if s.Via != "" {
// The CLI inside the orchestrator's container is its own `docker`, whatever this host runs.
return exec.CommandContext(ctx, tool, append(append([]string{"exec", s.Via, "docker"}, base...), args...)...)
}
cmd := exec.CommandContext(ctx, tool, append(base, args...)...)
if len(s.Files) > 0 {
cmd.Dir = filepath.Dir(s.Files[0])
}
return cmd
}