Documentation
¶
Overview ¶
Package docker turns Docker container metadata into desired NPM resource state and streams container lifecycle events.
Index ¶
- Constants
- Variables
- func DedupeNetworks(names []string) []string
- func EventFilters() filters.Args
- func IsEnabled(c Container, prefix string) bool
- func Managed(c Container, opts ParseOptions) (bool, string)
- func NewClient(host string) (*client.Client, error)
- func Scan(containers []Container, opts ParseOptions) (Snapshot, Result)
- type APIClient
- type Container
- func (c Container) ExposedPorts() []int
- func (c Container) GuessPort(preference []int) (int, bool)
- func (c Container) IPAddress(preferred []string, strict bool) string
- func (c Container) NetworkNames() []string
- func (c Container) OnAnyNetwork(names []string) bool
- func (c Container) PublishedPort(private int) (int, bool)
- func (c Container) ResolveHost(opts ParseOptions) string
- func (c Container) Running() bool
- type Event
- type InfoClient
- type Listener
- func (l *Listener) Close() error
- func (l *Listener) Containers(ctx context.Context) ([]Container, error)
- func (l *Listener) DaemonID(ctx context.Context) string
- func (l *Listener) DiscoverOwnNetworks(ctx context.Context) []string
- func (l *Listener) NetworksOfContainer(ctx context.Context, name string) []string
- func (l *Listener) Options() ParseOptions
- func (l *Listener) Ping(ctx context.Context) error
- func (l *Listener) SelfID() string
- func (l *Listener) SetSelfID(id string)
- func (l *Listener) Snapshot(ctx context.Context) (Snapshot, error)
- func (l *Listener) StreamStatus() StreamStatus
- func (l *Listener) Summarize(ctx context.Context) (Summary, error)
- func (l *Listener) Targets(ctx context.Context) ([]*Target, error)
- func (l *Listener) Watch(ctx context.Context, trigger chan<- Event) error
- type Network
- type OnStop
- type ParseOptions
- type PortBinding
- type Result
- type Snapshot
- type StreamStatus
- type Summary
- type Target
Constants ¶
const DefaultPrefix = "npm"
DefaultPrefix is the label namespace used when none is configured.
Variables ¶
var DefaultPortPreference = fields.DefaultPortPreference
DefaultPortPreference is the order in which an exposed port is picked when a container exposes several of them.
Functions ¶
func DedupeNetworks ¶
DedupeNetworks removes duplicates while keeping the first occurrence, so a preference list assembled from several sources stays readable in logs.
func EventFilters ¶
EventFilters returns the server-side filter set: only container lifecycle events that can change the desired configuration.
func Managed ¶
func Managed(c Container, opts ParseOptions) (bool, string)
Managed reports whether a container is synchronised, and why not when it is not.
With NPM_EXPOSED_BY_DEFAULT (the default) any container carrying at least one label of the namespace is managed and `npm.enable=false` opts out. With the flag turned off the historic behaviour applies: `npm.enable=true` is required.
func NewClient ¶
NewClient builds a Docker SDK client for the given host. Both "unix:///var/run/docker.sock" and "tcp://docker-socket-proxy:2375" work; the TCP form is the recommended, least-privilege setup.
func Scan ¶
func Scan(containers []Container, opts ParseOptions) (Snapshot, Result)
Scan parses every container into the snapshot one reconcile run works from.
A container whose labels could not be parsed is recorded as *protected*: its resources are still in NPM, the tool simply cannot tell what they should look like right now. Deleting them because of a typo in one label would turn a warning into an outage.
Types ¶
type APIClient ¶
type APIClient interface {
ContainerList(ctx context.Context, options container.ListOptions) ([]container.Summary, error)
Events(ctx context.Context, options events.ListOptions) (<-chan events.Message, <-chan error)
Ping(ctx context.Context) (types.Ping, error)
Close() error
}
APIClient is the slice of the Docker SDK this package needs. Keeping it narrow makes the listener trivially mockable in tests.
type Container ¶
type Container struct {
ID string
Name string
Labels map[string]string
Networks []Network
// Ports are the container's exposed and published ports, used to guess
// the upstream port when no label names one.
Ports []PortBinding
// State is the Docker state ("running", "exited", ...).
State string
}
Container is the subset of Docker container data the parser needs.
func (Container) ExposedPorts ¶
ExposedPorts returns the container's TCP ports, sorted, for error messages.
func (Container) GuessPort ¶
GuessPort returns the container port to forward to when no label names one.
A container that exposes exactly one TCP port needs no configuration at all; with several ports the preference list decides, and when none of them is listed the caller is told to be explicit rather than being handed a random port.
func (Container) IPAddress ¶
IPAddress returns the container's IPv4 address, preferring the given networks (in order). Without strict mode the remaining networks are used as a fallback, in a stable alphabetical order; with strict mode an empty string is returned instead, because an address NPM cannot route to is worse than no proxy host at all.
func (Container) NetworkNames ¶
NetworkNames returns the names of all networks the container is attached to.
func (Container) OnAnyNetwork ¶
OnAnyNetwork reports whether the container is attached to one of the given networks.
func (Container) PublishedPort ¶
PublishedPort returns the host port a container port is published on.
func (Container) ResolveHost ¶
func (c Container) ResolveHost(opts ParseOptions) string
ResolveHost returns the upstream host for the container: the container's IP address when resolution is enabled and an address is available, otherwise the container name (Docker's embedded DNS resolves it inside a shared user-defined network).
Only IPv4 addresses are used: NPM writes the value straight into `proxy_pass`, where a bare IPv6 address would be invalid.
type InfoClient ¶
InfoClient is the optional part of the Docker API used to identify the daemon. A socket proxy may well refuse /info, which is why it is separate: the sync instance id then falls back to the host name.
type Listener ¶
type Listener struct {
// contains filtered or unexported fields
}
Listener reads container state and lifecycle events from the Docker API.
func NewListener ¶
func NewListener(api APIClient, opts ParseOptions, log *slog.Logger) *Listener
NewListener wraps an APIClient. The ParseOptions carry the label prefix and the upstream-host resolution policy.
func (*Listener) Containers ¶
Containers returns all containers as parser input, including their network endpoints so the upstream IP can be resolved.
Stopped containers are included on purpose: "the container is gone" and "the container is stopped" are very different situations, and only the first one may ever remove a host (NPM_ON_STOP decides what the second one does).
func (*Listener) DaemonID ¶
DaemonID returns the id of the Docker daemon, which is the natural default for the sync instance id: it is stable across restarts and recreates of this container, and different for every Docker host.
When the endpoint does not expose /info - a filtered socket proxy may well deny it - the answer is deliberately empty rather than something like the host name. Inside a container the host name is the short container id, so it changes with every recreate, and a sync instance whose identity changes would consider all of its own resources to belong to somebody else. No identity at all is the safe answer: it means "manage every resource carrying our marker", which is what a single-instance setup wants anyway.
func (*Listener) DiscoverOwnNetworks ¶
DiscoverOwnNetworks returns the networks this process's own container is attached to. They are preferred when resolving upstream IPs, because a container reachable from here is reachable from NPM when both sit on the same network. Failures are not fatal: the caller falls back to the configured network or the first available address.
func (*Listener) NetworksOfContainer ¶
NetworksOfContainer returns the networks a named container is attached to. It is how NPM_CONTAINER_NAME turns into an upstream network preference without the operator having to name the network as well.
func (*Listener) Options ¶
func (l *Listener) Options() ParseOptions
Options returns the parse options in use.
func (*Listener) SetSelfID ¶
SetSelfID records the own container id so its events can be ignored. It is set automatically by DiscoverOwnNetworks.
func (*Listener) Snapshot ¶
Snapshot returns the desired NPM resources of all containers, plus the containers whose labels could not be parsed. Invalid definitions are logged and skipped so one broken label set cannot stall the whole sync - and their containers are marked protected so the broken definition cannot delete anything either.
func (*Listener) StreamStatus ¶
func (l *Listener) StreamStatus() StreamStatus
StreamStatus returns the health of the Docker event subscription.
func (*Listener) Targets ¶
Targets returns only the desired resources. It is the convenience form used by tests and the validate subcommand.
type OnStop ¶
type OnStop string
OnStop is the policy for a container that is stopped but still exists.
const ( // OnStopDisable disables the resources of a stopped container (default). OnStopDisable OnStop = "disable" // OnStopKeep leaves them untouched and serving. OnStopKeep OnStop = "keep" // OnStopDelete removes them, as if the container had been destroyed. OnStopDelete OnStop = "delete" )
The stop policies.
func ParseOnStop ¶
ParseOnStop resolves the NPM_ON_STOP setting.
type ParseOptions ¶
type ParseOptions struct {
// Prefix is the label namespace, e.g. "npm".
Prefix string
// ResolveIP makes the parser default the upstream host to the container's
// IP address instead of its name. Avoids 502s when NPM cannot resolve
// Docker's internal DNS.
ResolveIP bool
// PreferNetworks lists network names to try first when resolving the IP,
// typically the network NPM itself is attached to.
PreferNetworks []string
// StrictNetworks restricts IP resolution to PreferNetworks. Without it a
// container that is not attached to any of them falls back to an address
// from some other network - which NPM usually cannot reach, producing a
// proxy host that resolves to a dead upstream. Set whenever the operator
// named the network explicitly (NPM_NETWORK).
StrictNetworks bool
// Defaults are the effective field defaults (built-in, overridden by the
// NPM_<KIND>_<FIELD> and NPM_DEFAULT_<FIELD> environment variables).
Defaults *fields.Defaults
// ExposedByDefault manages every container that carries at least one
// label of the namespace, without requiring npm.enable=true
// (NPM_EXPOSED_BY_DEFAULT, default true).
ExposedByDefault bool
// StrictLabels skips a resource whose label set contains an unknown
// field instead of only warning about it.
StrictLabels bool
// PortPreference is the order in which an exposed port is picked when a
// container exposes several (NPM_PORT_PREFERENCE).
PortPreference []int
// SelfID is this process's own container: it is never managed.
SelfID string
// Offline parses labels without a Docker daemon behind them, as the
// `validate` subcommand does for a compose file: the container's address
// and exposed ports are simply not knowable, and their absence is not an
// error.
Offline bool
}
ParseOptions controls how labels are turned into targets.
type PortBinding ¶
type PortBinding struct {
// Private is the container-internal port (what EXPOSE declares).
Private int
// Public is the host port, 0 when the port is not published.
Public int
// Type is "tcp" or "udp".
Type string
}
PortBinding is one port of a container: the port inside the container and, when published, the port on the host.
type Result ¶
type Result struct {
Targets []*Target
Errors []error
Warnings []string
// Skipped marks a container whose resource was dropped because of an
// unknown label under STRICT_LABELS.
Skipped bool
}
Result is the outcome of parsing one or many containers. Warnings are problems that do not invalidate a resource (an unknown label, a domain no certificate covers); Errors are definitions that had to be dropped.
func Parse ¶
func Parse(c Container, opts ParseOptions) Result
Parse converts the labels of one container into its desired NPM resources. Containers that are not managed yield nothing. Invalid entries are reported individually so one broken definition cannot hide the valid ones.
func ParseAll ¶
func ParseAll(containers []Container, opts ParseOptions) Result
ParseAll converts a list of containers, collecting per-container problems instead of aborting the whole reconcile run.
type Snapshot ¶
type Snapshot struct {
// Targets are the resources the labels ask for.
Targets []*Target
// Protected lists containers whose labels could not be parsed, by name
// and by id. Their resources must not be deleted in this run: a typo in
// one label must never take a production host down.
Protected map[string]struct{}
// Summary counts how the containers were classified.
Summary Summary
// Containers is how many containers Docker reported at all. Zero is
// suspicious - a filtered socket proxy answering with an empty list looks
// exactly like "every container is gone".
Containers int
}
Snapshot is the desired state of one reconcile run, plus what the parser learned about the containers it read.
func (Snapshot) IsProtected ¶
IsProtected reports whether a container is shielded from orphan deletion.
type StreamStatus ¶
type StreamStatus struct {
// Connected reports whether the event stream is currently subscribed.
Connected bool
// Since is when the current state began.
Since time.Time
// LastError is why the stream dropped, if it did.
LastError string
}
StreamStatus describes the health of the Docker event subscription.
type Summary ¶
type Summary struct {
Managed int
OptedOut int
Unlabeled int
// Stopped counts managed containers that are not running.
Stopped int
}
Summary counts how the containers of one run were classified.
func Classify ¶
func Classify(containers []Container, opts ParseOptions) Summary
Classify counts managed, opted out and unlabelled containers for the start-up overview.
type Target ¶
type Target struct {
Kind npm.Kind
Index int
ContainerID string
ContainerName string
// Running mirrors the container state. A resource whose container is
// stopped is never reconfigured, only enabled or disabled (NPM_ON_STOP).
Running bool
// ExplicitPlus lists the NPMplus-only fields the labels actually set, so
// a warning against upstream NPM only appears when the user asked for
// something that flavour cannot do.
ExplicitPlus []string
// Shared across proxy, redirect and 404 hosts.
DomainNames []string
// Certificate is the unresolved certificate wish; the reconcile loop
// turns it into an id once it knows which certificates exist.
Certificate certs.Spec
// SSLForced is nil for "auto": forced as soon as a certificate is
// attached.
SSLForced *bool
HTTP2Support bool
HSTSEnabled bool
HSTSSubdomains bool
HTTP3Support bool
BlockExploits bool
AdvancedConfig string
Enabled bool
// Proxy hosts.
ForwardScheme string
ForwardHost string
ForwardPort int
Websockets bool
Caching bool
TrustForwardedProto bool
AccessListIDs []int
AccessListNames []string
AccessListType string
Locations []npm.Location
// Proxy hosts, NPMplus only.
NoIndex bool
CrowdsecAppsec bool
RequestBuffering bool
ResponseBuffering bool
UpstreamCompression bool
FancyIndex bool
XFrameOptions string
AuthRequest string
AuthRequestUpstream string
LocationConfig string
// Redirection hosts.
ForwardDomainName string
ForwardHTTPCode int
PreservePath bool
// Streams.
IncomingPort int
ForwardingHost string
ForwardingPort int
TCPForwarding bool
UDPForwarding bool
ProxyProtocol int
ProxyTLS bool
Description string
// Let's Encrypt (all kinds that carry a certificate).
LetsEncryptEmail string
LetsEncryptAgree bool
DNSChallenge bool
DNSProvider string
DNSCredentials string
PropagationSeconds int
}
Target is one desired NPM resource derived from a container. A single container can produce many targets through indexed labels (npm.proxy.domains, npm.proxy.1.domains, npm.1.stream.incoming_port, ...).
func (*Target) Complete ¶
Complete reports whether the target carries everything a create or update needs. It is false only for a stopped container, whose address and ports Docker no longer reports: such a resource is left exactly as it is and only enabled or disabled.
func (*Target) Describe ¶
Describe returns a log friendly identifier such as "web#0 proxy app.example.com".