Documentation
¶
Index ¶
- Variables
- func CheckPort(port int) (bool, error)
- func ComposeServiceNames(composeFilePaths []string) (map[string]struct{}, error)
- func DeclaredHostPorts(ctx context.Context, workdir string, composeFiles ...string) ([]int, map[int]string, error)
- func DefaultCommand(service string) []string
- func DefaultPortServiceNames() map[int]string
- func Exec(ctx context.Context, container string, cmd []string, opts ExecOptions) error
- func GetContainerLogs(ctx context.Context, name string, tail int) (string, error)
- func GetHealthStatus(ctx context.Context, name string) (string, error)
- func OwnedHostPorts(ctx context.Context, workdir string, composeFiles ...string) (map[int]bool, error)
- func RemoveOrphans(ctx context.Context, orphans []OrphanContainer) []error
- func RunPostStartCleanup(ctx context.Context, projectName string) error
- func RunPreStartCleanup(ctx context.Context, projectName string) error
- type Compose
- func (c *Compose) ComposeConfig(ctx context.Context, workdir string) error
- func (c *Compose) ComposeDown(ctx context.Context, workdir string, opts DownOptions) error
- func (c *Compose) ComposePs(ctx context.Context, workdir string) ([]ContainerInfo, error)
- func (c *Compose) ComposePull(ctx context.Context, workdir string) error
- func (c *Compose) ComposePullService(ctx context.Context, workdir, service string) error
- func (c *Compose) ComposeRestart(ctx context.Context, workdir string, services ...string) error
- func (c *Compose) ComposeScale(ctx context.Context, workdir, service string, replicas int) error
- func (c *Compose) ComposeStop(ctx context.Context, workdir string, services ...string) error
- func (c *Compose) ComposeUp(ctx context.Context, workdir string, services ...string) error
- func (c *Compose) ComposeUpNoDeps(ctx context.Context, workdir string, services ...string) error
- func (c *Compose) Run(ctx context.Context, workdir string, args ...string) error
- type Container
- type ContainerInfo
- type DownOptions
- type ExecOptions
- type Executor
- type Mount
- type OrphanContainer
- type PortConflict
Constants ¶
This section is empty.
Variables ¶
var ReservedPorts = []int{
80, 443, 5432, 8080, 4000, 6379,
9000, 9001, 7700, 3021, 1025, 8025,
3008, 5000,
}
ReservedPorts lists ports used by the nSelf stack that must be checked for conflicts before starting services.
Functions ¶
func CheckPort ¶
CheckPort probes a single TCP port on localhost using DialTimeout. It returns true when the port is in use (connection succeeded) and false when the port is available (connection refused or timed out).
func ComposeServiceNames ¶ added in v1.3.6
ComposeServiceNames reads every compose file in composeFilePaths (the base docker-compose.yml plus any plugin compose fragments — the same file set `docker compose -f ... -f ...` is invoked with, see build.ReadComposeManifest) and returns the union of all service names they define. This is the "desired state" DetectOrphans compares live containers against.
Inputs: composeFilePaths — absolute paths to YAML files; missing files are
skipped (best-effort — a plugin fragment can be removed from disk independently of the manifest that references it).
Outputs: the union of service keys across all readable files, and the
first hard parse error encountered (a file that exists but is not valid YAML is a real problem, not a missing-file gap).
Constraints: pure I/O + YAML parsing, no docker daemon access.
func DeclaredHostPorts ¶ added in v1.3.4
func DeclaredHostPorts(ctx context.Context, workdir string, composeFiles ...string) ([]int, map[int]string, error)
DeclaredHostPorts returns the host ports the given compose files publish, together with a port -> service name map for error messages.
This exists because checking a fixed list of default ports is wrong for any stack that was configured off the defaults. A second nSelf project on one host must move its ports (POSTGRES_PORT=5433, HASURA_PORT=8181, ...); with a fixed list it is then blocked by the first project holding 5432 and 8080, ports it was never going to bind. That is what kept a staging stack down.
An error here means "could not determine", never "binds nothing". The caller must fall back to a conservative default list rather than check nothing.
func DefaultCommand ¶
DefaultCommand returns the default shell or client command for a given service. Recognized services get their native CLI; everything else falls back to /bin/sh.
func DefaultPortServiceNames ¶ added in v1.3.4
DefaultPortServiceNames maps the ReservedPorts defaults to human names. Used only on the fallback path, when the published ports could not be read from compose; the normal path names services from the compose config itself, so a project on custom ports still gets "Port 8181 (hasura)" and not "unknown service".
func Exec ¶
Exec runs a command inside a running container, attaching stdin/stdout/stderr for interactive sessions.
func GetContainerLogs ¶
GetContainerLogs shells out to `docker logs --tail N` and returns the combined output.
func GetHealthStatus ¶
GetHealthStatus returns the health status of a container: "healthy", "unhealthy", "starting", "none" (no healthcheck configured), or "not_found".
func OwnedHostPorts ¶ added in v1.0.2
func OwnedHostPorts(ctx context.Context, workdir string, composeFiles ...string) (map[int]bool, error)
OwnedHostPorts queries the running compose stack in workdir and returns the set of host-side port numbers currently bound by nself's own containers. These ports should not be reported as conflicts on startup — they are already owned by nself and will be reused by docker compose up.
If the query fails (e.g. no containers are running yet, Docker daemon unreachable) the function returns an empty set and a nil error so that the caller falls back to treating all in-use ports as conflicts.
func RemoveOrphans ¶ added in v1.3.6
func RemoveOrphans(ctx context.Context, orphans []OrphanContainer) []error
RemoveOrphans force-removes each orphan container by ID (docker rm -f — same primitive cleanup.go's forceRemoveContainer uses for init/zombie containers) and returns one error per failed removal. Never called unless the caller opted in (e.g. `nself build --remove-orphans`); detection alone never removes anything.
func RunPostStartCleanup ¶
RunPostStartCleanup runs the full post-startup cleanup sequence:
- Init container cleanup, repeated 3 times with 500ms delays (handles race conditions where containers are still being marked as exited).
- Zombie container cleanup for containers stuck in "created" state.
func RunPreStartCleanup ¶ added in v1.2.2
RunPreStartCleanup removes stale artifacts that would break `docker compose up`: hash-prefixed rename-leftover containers (which hold ports and shadow clean container names). Run before compose up.
Types ¶
type Compose ¶
type Compose struct {
// DockerPath overrides the docker binary location. Empty uses PATH lookup.
DockerPath string
// ComposeFiles lists compose file paths passed as -f flags.
// Order matters: first file is the base, subsequent files extend/override.
// If empty, no -f flags are added and Docker uses its default discovery.
ComposeFiles []string
// EnvFiles lists env file paths passed as --env-file flags for ${VAR}
// interpolation (secrets are env-templated out of the generated compose
// YAML — see internal/build secret templating). Order matters: later
// files win on conflict. If empty, Docker Compose falls back to its
// default .env discovery.
EnvFiles []string
}
Compose shells out to `docker compose` for lifecycle operations.
func NewCompose ¶
NewCompose returns a Compose instance configured with the given compose files. If no files are provided, Docker Compose uses its default file discovery.
func (*Compose) ComposeConfig ¶
ComposeConfig runs `docker compose config` in the given workdir to validate the compose configuration. Returns nil on success.
func (*Compose) ComposeDown ¶
ComposeDown runs `docker compose down` with the given options in the given workdir.
func (*Compose) ComposePs ¶
ComposePs runs `docker compose ps --format json` and parses the output into a slice of ContainerInfo.
Docker Compose v2 changed its JSON output format across versions:
- v2.20 and earlier: one JSON object per stdout line (NDJSON)
- v2.21 and later: a single JSON array containing all objects
Both formats are handled: the raw output is first attempted as a JSON array; if that fails (or yields nothing), the output is re-scanned line by line as NDJSON. This makes the function version-agnostic.
func (*Compose) ComposePull ¶
ComposePull runs `docker compose pull` in the given workdir, pulling images for all services defined in the compose file.
func (*Compose) ComposePullService ¶ added in v1.1.3
ComposePullService runs `docker compose pull <service>` in the given workdir, pulling a fresh image only for the named service.
func (*Compose) ComposeRestart ¶
ComposeRestart runs `docker compose restart [services...]` in the given workdir.
func (*Compose) ComposeScale ¶ added in v1.1.3
ComposeScale runs `docker compose up -d --scale <service>=<n>` in the given workdir, adjusting the replica count for the named service.
func (*Compose) ComposeStop ¶ added in v1.1.3
ComposeStop runs `docker compose stop [services...]` in the given workdir. Unlike ComposeDown, this stops containers without removing them, preserving their state for a subsequent ComposeUp.
func (*Compose) ComposeUp ¶
ComposeUp runs `docker compose up -d [services...]` in the given workdir.
func (*Compose) ComposeUpNoDeps ¶ added in v1.3.0
ComposeUpNoDeps runs `docker compose up -d --no-deps [services...]`.
This is the primitive that means "make these containers match the compose file": Compose recreates a container whose definition changed and leaves an unchanged one alone. `restart` does neither — it bounces the existing container and never re-reads the file. --no-deps keeps the operation scoped to the named services instead of pulling their dependencies up with them.
func (*Compose) Run ¶
Run executes a docker command with the given arguments, setting the working directory and inheriting the context for cancellation. Output is forwarded to os.Stdout/os.Stderr via goroutine-owned pipes rather than direct fd inheritance.
Direct fd inheritance (cmd.Stdout = os.Stdout) passes the test binary's stdout file descriptor to docker compose and all its children. The docker daemon, which is a separate long-lived process, may hold that fd open via IPC even after the docker compose process is killed — causing "*** Test I/O incomplete N s after exiting" in the Go test harness. Piped copying breaks the inheritance chain: the subprocess gets the write end of a pipe; our goroutine owns the read end and copies to os.Stdout. When the subprocess exits (or is killed), it closes the write end, the goroutine finishes, and the test binary's stdout fd is never held by any docker process.
WaitDelay gives the subprocess up to 5 seconds to drain pending I/O after the context is cancelled and the process is killed.
Setpgid places docker compose in its own process group so SIGKILL from the Cancel hook propagates to all spawned child processes, not just the top-level docker process.
type Container ¶
type Container struct {
ID string
Name string
Service string
State string // running, exited, paused, etc.
Health string // healthy, unhealthy, starting, none
Ports []string
}
Container represents a running or stopped container from ComposePs.
type ContainerInfo ¶
type ContainerInfo struct {
ID string
Name string
Service string // Docker Compose service name (e.g. "postgres", "hasura")
Image string
State string
Health string
Ports []string
Env map[string]string
Labels map[string]string
CreatedAt string
StartedAt string
Mounts []Mount
}
ContainerInfo holds detailed inspection data for a single container.
func InspectContainer ¶
func InspectContainer(ctx context.Context, name string) (*ContainerInfo, error)
InspectContainer shells out to `docker inspect` and returns parsed ContainerInfo.
type DownOptions ¶
type DownOptions struct {
RemoveVolumes bool
RemoveOrphans bool
Timeout int // seconds; 0 uses Docker default
}
DownOptions configures behavior for ComposeDown.
type ExecOptions ¶
ExecOptions configures behavior for container exec operations.
type Executor ¶
type Executor interface {
ComposeUp(ctx context.Context, services ...string) error
ComposeDown(ctx context.Context, opts DownOptions) error
ComposePs(ctx context.Context) ([]Container, error)
ComposeLogs(ctx context.Context, service string, follow bool, tail int) (io.ReadCloser, error)
Inspect(ctx context.Context, container string) (*ContainerInfo, error)
Exec(ctx context.Context, container string, cmd []string) error
}
Executor abstracts Docker Compose and container operations for testability.
type Mount ¶
type Mount struct {
Source string
Destination string
Type string // bind, volume, tmpfs
ReadOnly bool
}
Mount represents a container volume mount.
type OrphanContainer ¶ added in v1.3.6
type OrphanContainer struct {
ID string
Name string
Service string // may be empty if the container somehow lacks the label
State string
}
OrphanContainer describes a running-or-stopped container that belongs to this project (by compose project label) but has no matching service in the freshly generated compose files.
func DetectOrphans ¶ added in v1.3.6
func DetectOrphans(ctx context.Context, projectName string, defined map[string]struct{}) ([]OrphanContainer, error)
DetectOrphans lists every container belonging to projectName (by compose project label — see the package-level scoping note above) and returns the ones whose service is not present in defined. Returns an error only when the docker CLI itself could not be run (daemon unreachable, binary missing); callers should treat that as advisory-only and skip reporting rather than failing the caller's own command (a `nself build` run in a docker-less CI image must still succeed).
type PortConflict ¶
PortConflict records whether a specific port is already in use.
func CheckAllPorts ¶
func CheckAllPorts(ports []int) ([]PortConflict, error)
CheckAllPorts probes every port in the slice and returns only the entries where the port is already in use. A nil slice means no conflicts were found.
func CheckAllPortsFiltered ¶ added in v1.0.2
func CheckAllPortsFiltered(ctx context.Context, portList []int, workdir string, composeFiles ...string) ([]PortConflict, error)
CheckAllPortsFiltered probes every port in the slice and returns only entries where the port is in use by a process that is NOT part of nself's own running compose stack. This prevents false positives where nself's own containers (e.g. postgres bound to 127.0.0.1:5432) are reported as conflicts on restart.
workdir and composeFiles are forwarded to OwnedHostPorts. If the compose query fails the function falls back to unfiltered behaviour (all in-use ports are conflicts) so startup never silently succeeds with a real conflict.