docker

package
v1.0.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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

func CheckPort(port int) (bool, error)

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 DefaultCommand

func DefaultCommand(service string) []string

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 Exec

func Exec(ctx context.Context, container string, cmd []string, opts ExecOptions) error

Exec runs a command inside a running container, attaching stdin/stdout/stderr for interactive sessions.

func GetContainerLogs

func GetContainerLogs(ctx context.Context, name string, tail int) (string, error)

GetContainerLogs shells out to `docker logs --tail N` and returns the combined output.

func GetHealthStatus

func GetHealthStatus(ctx context.Context, name string) (string, error)

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 RunPostStartCleanup

func RunPostStartCleanup(ctx context.Context, projectName string) error

RunPostStartCleanup runs the full post-startup cleanup sequence:

  1. Init container cleanup, repeated 3 times with 500ms delays (handles race conditions where containers are still being marked as exited).
  2. Zombie container cleanup for containers stuck in "created" state.

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
}

Compose shells out to `docker compose` for lifecycle operations.

func NewCompose

func NewCompose(files ...string) *Compose

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

func (c *Compose) ComposeConfig(ctx context.Context, workdir string) error

ComposeConfig runs `docker compose config` in the given workdir to validate the compose configuration. Returns nil on success.

func (*Compose) ComposeDown

func (c *Compose) ComposeDown(ctx context.Context, workdir string, opts DownOptions) error

ComposeDown runs `docker compose down` with the given options in the given workdir.

func (*Compose) ComposePs

func (c *Compose) ComposePs(ctx context.Context, workdir string) ([]ContainerInfo, error)

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

func (c *Compose) ComposePull(ctx context.Context, workdir string) error

ComposePull runs `docker compose pull` in the given workdir.

func (*Compose) ComposeRestart

func (c *Compose) ComposeRestart(ctx context.Context, workdir string, services ...string) error

ComposeRestart runs `docker compose restart [services...]` in the given workdir.

func (*Compose) ComposeUp

func (c *Compose) ComposeUp(ctx context.Context, workdir string, services ...string) error

ComposeUp runs `docker compose up -d [services...]` in the given workdir.

func (*Compose) Run

func (c *Compose) Run(ctx context.Context, workdir string, args ...string) error

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

type ExecOptions struct {
	Interactive bool
	TTY         bool
	User        string
	WorkDir     string
	Env         []string
}

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 PortConflict

type PortConflict struct {
	Port  int
	InUse bool
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL