discovery

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package discovery parses each stack's per-service podman-compose.yaml files and exposes the result as a structured model (services, targets, infra, volumes, networks, dependency graph). The result is the daemon's source of truth for everything operational — every later phase (supervision, env, logs, volumes) keys off these types.

The compose contract

A service's builds/podman-compose.yaml doubles as the daemon's service manifest. The conventions below are what make discovery work; they hold for every service repo, so service compose files don't re-document them:

  • Every cmd/<target> entrypoint has a compose mirror named <service>-<target> gated behind profiles: ["<target>"]. The profile keeps targets out of a plain `podman compose up`: the daemon runs targets itself (go-exec by default, the profiled container on demand) and orchestrates ordering through depends_on, never through Compose's profile cascade. It also prevents a containerized copy from binding the host port a locally-run target owns.
  • Compose services without a profile are infrastructure (databases, mailservers): always brought up, never run as targets.
  • A target with a healthcheck (compose or Dockerfile) is a long-runner; one without is a one-shot. One-shots run to completion on infra-up and must be idempotent — they re-run on every infra-up cycle.
  • Sibling services are never declared in each other's compose files. Each service describes only its own infra; cross-service connections resolve through daemon-allocated env (<SIBLING>_<VAR> references).
  • Constants (credentials, test keys) are inlined; values the daemon allocates or derives (ports, hosts, URLs, DSNs) appear as ${VAR} references the daemon fills.

Discovery is deliberately strict: contract violations surface as errors at daemon startup, so misconfigurations never become runtime surprises. The one place it is lenient is healthcheck classification — see the kind derivation in discovery.go for why.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RenderTopology

func RenderTopology(s *Service) string

RenderTopology returns an ASCII tree of the service's dependency graph, formatted for `a-novel run topology` output. One service per invocation; the daemon RPC handler concatenates per service when --service is omitted.

Layout puts infra first (it's the root), then one-shots (in compose-graph order), then long-runners, with each leaf annotated by current phase + (mode).

func ValidateEnvRefs

func ValidateEnvRefs(stacks []*Stack)

ValidateEnvRefs scans every compose env block in `disc` for ${VAR} references the daemon doesn't know how to fill, and appends a non-fatal warning to the stack's Errors list. Called at end of discovery so every parse error and validation warning land in one stream the daemon prints at startup.

What's "resolvable":

  • `*_PORT` → allocated by the daemon
  • `*_HOST`, `*_URL` → synthesized from `*_PORT` (same service)
  • `POSTGRES_DSN` → synthesized when POSTGRES_PORT allocated
  • `<SERVICE>_<VAR>` → cross-service reference, if <SERVICE> is a known service in this stack
  • Any key declared as a literal (no `${...}` references) in the SAME env block — that's a sibling constant

Anything else gets a warning: "compose service X references ${VAR} but the daemon can't fill it". The compose will still substitute with an empty string at runtime; this warning surfaces the gap.

Types

type DiscoveryError

type DiscoveryError struct {
	Service string // service name within the stack
	Path    string // file or directory path the error points at
	Reason  string // human-readable explanation
}

DiscoveryError is a non-fatal classification problem (orphan compose service, missing compose mirror for a cmd/X, etc.). Collected per stack and surfaced via Status so the user can fix them without preventing daemon startup. Truly fatal errors (can't read the stack path) bubble up as the function's error.

func (DiscoveryError) Error

func (e DiscoveryError) Error() string

type Infra

type Infra struct {
	Name           string // compose service name, e.g., "postgres-json-keys"
	Service        string // owning service name
	Stack          string // owning stack name
	Dockerfile     string // referenced Dockerfile (for build)
	HasHealthcheck bool
	DependsOn      []string
	Ports          []string
	Environment    map[string]string
	Volumes        []string // volume mounts the infra declares
}

Infra is a compose service with no profile assignment and no matching cmd/<name>/ directory.

type Service

type Service struct {
	Name        string // e.g., "service-json-keys"
	Stack       string // owning stack name
	Path        string // absolute path to the service directory
	ComposePath string // absolute path to its compose file
	Targets     []*Target
	Infra       []*Infra
	Volumes     []*Volume
	Networks    []string // network names declared at compose top-level
	// Dependency edges, one per compose `depends_on` entry. Each edge
	// connects two compose-service names (target or infra); resolution
	// to *Target/*Infra is done by ResolveDeps at the daemon level.
	DependsOn map[string][]string // compose-service-name → [list of names it depends on]
}

Service is one app/service-* directory, with everything we discovered about its compose file.

type Stack

type Stack struct {
	Name     string
	Path     string
	Default  bool
	Services []*Service
	// Errors collected during discovery for this stack. Non-fatal —
	// surfaced via Status / startup logs so the user can fix them without
	// the daemon refusing to start outright. Strict-failure cases (the
	// stack path doesn't exist at all) are returned as the function's
	// error, not added here.
	Errors []DiscoveryError
}

Stack is the discovered shape of one registered git checkout. It owns the services found under <path>/app/service-*.

func DiscoverStacks

func DiscoverStacks(stk []stacks.Stack) ([]*Stack, error)

DiscoverStacks parses every service in every registered stack. Returns one Stack per input, with Services populated and DiscoveryErrors collected per stack. A truly inaccessible stack path returns an error (no Stack entry for it); a stack with malformed individual services returns the Stack with the bad services represented in Errors.

type Target

type Target struct {
	Name        string     // e.g., "rest" (cmd directory name)
	ComposeName string     // e.g., "service-json-keys-rest" (compose service name)
	Service     string     // owning service name
	Stack       string     // owning stack name
	Profile     string     // compose profile name (same as Name by convention)
	Kind        TargetKind // OneShot | LongRunner
	CmdDir      string     // absolute path to cmd/<name>/
	Dockerfile  string     // absolute path to the build's Dockerfile (may be "")
	DependsOn   []string   // compose-service-names this target depends_on
	Ports       []string   // raw "${VAR}:N" port mappings (parsed by phase 4)
	Environment map[string]string
}

Target is a discovered Go cmd/<name>/ entry with its compose mirror.

func (*Target) ID

func (t *Target) ID() string

ID returns the canonical "<stack>/<service>/<target>" identifier the daemon uses to address this target across every API. Single rule matches runner.targetID and server.targetIDFor.

type TargetKind

type TargetKind int

TargetKind classifies a target as one-shot or long-runner.

const (
	TargetKindUnknown    TargetKind = iota
	TargetKindOneShot               // no HEALTHCHECK in compose OR Dockerfile
	TargetKindLongRunner            // has HEALTHCHECK in compose OR Dockerfile
)

func (TargetKind) String

func (k TargetKind) String() string

String renders the kind in the shape the proto enum uses.

type Volume

type Volume struct {
	Name    string // bare name, e.g., "json-keys-postgres-data"
	Service string
	Stack   string
}

Volume is a top-level compose `volumes:` entry, scoped to its service.

Jump to

Keyboard shortcuts

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