Documentation
¶
Overview ¶
Package build drives container image builds through BuildKit's Go client (github.com/moby/buildkit/client), never by shelling out to `docker build` or the `docker` CLI, matching the same "no CLI shelling" rule the node communication layer follows. It accepts plain Dockerfiles via the dockerfile.v0 frontend, per the declarative app spec's build config, and Railpack-detected sources via their own solve path (railpack.go).
A build runs against the BuildKit instance embedded in whichever Docker Engine is local to the process running it, and Router (router.go) decides which process that is: this one, or a node an operator marked build-capable, reached over the agent transport.
Index ¶
- Constants
- Variables
- func AppFromTag(tag string) string
- func AutoMemoryCapMB(hostMB int, g MemGuard) int
- func DetectBuildFramework(dir string) string
- func EffectiveMemoryMB(setting, hostMB int, g MemGuard) int
- func EmitCacheWarning(progress func(ProgressEvent), msg string)
- func EnsureWithin(root, target string) error
- func FrameworkLabel(provider string) (string, bool)
- func FrameworkMemoryFloorMB(framework string) int
- func HostPlatform() string
- func LowMemoryWarning(framework string, hostMB int) string
- func MemoryKillMessage(limitMB, hostMB int) string
- func NodeHeapMB(capMB, percent int) int
- func SelectBuildNode(nodes []NodeInfo) string
- func SetNetworkResolver(fn func(context.Context) (string, error))
- func SlogProgress(log *slog.Logger) func(ProgressEvent)
- func StaticMaxBytesFromEnv(lookup func(string) (string, bool)) int64
- func TarContext(ctx context.Context, dir string, w io.Writer) error
- func UntarConfined(r io.Reader, destDir string, maxBytes int64) error
- func UntarContext(ctx context.Context, r io.Reader, dir string) error
- func ValidatePublicRepoURL(repoURL string) error
- type Attestations
- type Builder
- type CacheConfig
- type CgroupProvisioner
- type Client
- func (c *Client) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
- func (c *Client) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
- func (c *Client) Close() error
- func (c *Client) SolveRemote(ctx context.Context, req RemoteRequest, out io.Writer, ...) (*Result, error)
- type Clock
- type DetectRequest
- type DetectResult
- type ImageExtractor
- type ImageSizer
- type Limiter
- func (l *Limiter) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
- func (l *Limiter) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
- func (l *Limiter) Load() []NodeLoad
- func (l *Limiter) Queued() (waiting, running int)
- type LimiterOption
- func WithCgroupProvisioner(c CgroupProvisioner) LimiterOption
- func WithImageSizer(s ImageSizer) LimiterOption
- func WithLimiterClock(c Clock) LimiterOption
- func WithLimiterLogger(lg *slog.Logger) LimiterOption
- func WithMemoryGuard(g MemGuard, hostMB func() int) LimiterOption
- func WithNodeResolver(n NodeResolver) LimiterOption
- func WithRunRecorder(r RunRecorder) LimiterOption
- type Limits
- type LimitsSource
- type MemGuard
- type NetworkFacts
- type NetworkMode
- type NodeBuilder
- type NodeInfo
- type NodeLoad
- type NodeResolver
- type NodeSource
- type Option
- type ProgressEvent
- type RailpackRequest
- type RemoteKind
- type RemoteRequest
- type Request
- type Result
- type Router
- func (r *Router) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
- func (r *Router) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
- func (r *Router) ImageSize(ctx context.Context, tag string) int64
- func (r *Router) SelectedNode(ctx context.Context) string
- type RouterOption
- type Run
- type RunRecorder
- type RunStatus
- type S3Cache
- type SpeedCacheState
- type SpeedFinding
- type SpeedReport
- type Trend
- type UnsupportedProviderError
Constants ¶
const ( CacheModeMin = "min" CacheModeMax = "max" )
Cache export modes accepted by BuildKit's s3 backend.
const ( EnvStaticMaxMB = "APP_STATIC_BUILD_MAX_MB" DefaultStaticMaxMB = 2048 )
Environment override and default for the size cap on an extracted static build.
const ( EnvBuildTimeout = "APP_BUILD_TIMEOUT" EnvBuildMaxConcurrent = "APP_BUILD_MAX_CONCURRENT" EnvBuildMemoryMB = "APP_BUILD_MEMORY_MB" EnvBuildCPUMillis = "APP_BUILD_CPU_MILLIS" EnvBuildCgroupRoot = "APP_BUILD_CGROUP_ROOT" DefaultBuildTimeout = 30 * time.Minute DefaultBuildMaxConcurrent = 2 )
Environment overrides and defaults for build limits. Zero disables a limit.
const ( EnvBuildMemoryPercent = "APP_BUILD_MEMORY_PERCENT" EnvBuildMemoryReserve = "APP_BUILD_MEMORY_RESERVE_MB" EnvBuildNodeHeapPct = "APP_BUILD_NODE_HEAP_PERCENT" DefaultMemoryPercent = 60 DefaultMemoryReserveMB = 512 DefaultNodeHeapPercent = 75 // BuildMemoryFloorMB is the smallest auto cap worth setting; BuildMemoryMinMB is the registry minimum. BuildMemoryFloorMB = 512 BuildMemoryMinMB = 256 )
Env seeds and defaults for the build memory guard. The cap itself is the builds.memory_limit_mb runtime setting (APP_BUILD_MEMORY_MB seeds it).
const ( FrameworkNextJS = "nextjs" FrameworkOther = "other" )
Framework build memory floors in MiB of host RAM. Documented guidance, not measured: no build on a real host has been timed for these numbers yet.
const ( SpeedCacheDisabled = "cache_disabled" SpeedCacheUnconfigured = "cache_unconfigured" SpeedLowCacheHit = "low_cache_hit" SpeedSlowerRecently = "slower_recently" )
Speed finding codes. The dashboard translates these.
const ( SpeedFixAuto = "auto" SpeedFixGuided = "guided" SpeedFixManual = "manual" )
Fix kinds: auto is applied by the control plane, guided needs one action elsewhere, manual is a repo change.
const EnvAttest = "APP_BUILD_ATTEST"
EnvAttest turns BuildKit SBOM and provenance attestations on for Dockerfile builds.
const EnvBuildNetwork = "APP_BUILD_NETWORK"
EnvBuildNetwork selects the network policy for RUN steps of a build.
const KeyBuildNetwork = "security.build_network"
KeyBuildNetwork is the runtime setting key.
Variables ¶
var ( ErrRailpackSourceDirRequired = errors.New("build: railpack: source dir is required") ErrRailpackTagRequired = errors.New("build: railpack: tag is required") )
ErrRailpackSourceDirRequired and ErrRailpackTagRequired are returned by Validate for the two required fields, matching Request.Validate's own "distinguishable sentinel error" shape for the Dockerfile path.
var ( ErrContextDirRequired = errors.New("build: context dir is required") ErrTagRequired = errors.New("build: tag is required") )
ErrContextDirRequired and ErrTagRequired are returned by Validate for the two required fields, so callers can match on them if they need to distinguish the failure reason without parsing the error string.
var ErrBuildEngineUnsupported = errors.New("build: source builds (Dockerfile, Railpack) are not supported on Podman: no BuildKit is reachable through its socket. " +
"Podman source builds are planned via a managed buildkitd. Image deploys work on Podman today, so deploy a prebuilt image or run the build on a Docker node")
ErrBuildEngineUnsupported means the container engine cannot host a source build. The text carries "no buildkit" so failure classification maps it to build_engine_unsupported.
var ErrBuildMemoryLimit = errors.New("build exceeded its memory limit")
ErrBuildMemoryLimit marks a build killed for exceeding its memory limit.
var ErrBuildTimeout = errors.New("build timed out")
ErrBuildTimeout is the cause of a build stopped by its time limit.
var ErrCacheRegistryRefRequired = errors.New("build: cache registry insecure flag set without a registry ref")
ErrCacheRegistryRefRequired is returned by CacheConfig.entries when RegistryInsecure is set but RegistryRef is empty: an insecure flag with nothing to apply it to is almost certainly a caller mistake (e.g. a typo'd env var name that leaves RegistryRef unset), not a meaningful configuration, so this fails loudly at construction time rather than silently doing nothing with the flag.
var ErrExtractTooLarge = errors.New("build: extracted output exceeds the size limit")
ErrExtractTooLarge is returned when an image directory exceeds the size cap.
var ErrNetworkFrontend = errors.New("build: a custom Dockerfile frontend (# syntax= or BUILDKIT_SYNTAX) is refused while security.build_network is not open; remove it or use the stock docker/dockerfile frontend")
ErrNetworkFrontend refuses a custom Dockerfile frontend: force-network-mode is only a hint to the stock frontend, a foreign one can return RUN steps with the default network.
var ErrNetworkUnenforceable = errors.New("build: security.build_network=block-private cannot be enforced on this build path yet; set it to open or none")
ErrNetworkUnenforceable fails a build instead of running it with a policy that would silently not apply.
var ErrNoBuildNodeAvailable = errors.New("build: no build-capable node is currently online")
ErrNoBuildNodeAvailable is no longer returned by SelectBuildNode: the build-node-routing initiative made falling back to the local node the default instead (SelectBuildNode's own doc comment). Kept as an exported sentinel in case anything outside this package still references it.
var ErrRemoteDockerfileOutsideContext = errors.New("build: a dispatched build's dockerfile must live inside its build context")
ErrRemoteDockerfileOutsideContext is returned when a build's Dockerfile sits outside its own build context. A local build can read it anyway, since both are paths on one filesystem; a dispatched one cannot, because only the context is shipped to the build node.
var ErrRemoteSecretsUnsupported = errors.New("build: secret mounts (private package registries) are not supported on remote build nodes yet; build on the control plane or remove the package registry")
ErrRemoteSecretsUnsupported is returned when a build with secret mounts would run on a remote build node, which does not receive secrets yet.
var ErrRepoTooLarge = errors.New("build: detect: repository is too large to inspect")
ErrRepoTooLarge is returned by Detect when the cloned tree exceeds detectMaxRepoBytes.
var ErrRepoURLSchemeNotAllowed = errors.New("repo_url must use http or https")
ErrRepoURLSchemeNotAllowed is returned by ValidatePublicRepoURL for a repoURL whose scheme is not http/https.
var ErrUnsafeContextPath = errors.New("build: build context entry escapes the destination directory")
ErrUnsafeContextPath is returned by UntarContext for an entry whose name escapes the destination directory. A build context arrives over the agent transport from an authenticated control plane, but an extractor that trusts entry names is a file-overwrite primitive regardless of who is on the other end.
Functions ¶
func AppFromTag ¶
AppFromTag maps an image tag like "reg.io/org/web:abc" to the app key "web".
func AutoMemoryCapMB ¶
AutoMemoryCapMB is min(max(host*percent, floor), host-reserve), never below the registry minimum. Unknown host memory (0) yields 0, meaning no cap.
func DetectBuildFramework ¶
DetectBuildFramework reports FrameworkNextJS when dir's package.json depends on next, else FrameworkOther.
func EffectiveMemoryMB ¶
EffectiveMemoryMB turns the setting into a cap: a positive value is used as is, 0 means auto.
func EmitCacheWarning ¶ added in v0.2.0
func EmitCacheWarning(progress func(ProgressEvent), msg string)
EmitCacheWarning reports that the remote build cache was skipped.
func EnsureWithin ¶
EnsureWithin returns an error when target, after resolving symlinks, is not inside root. Paths that do not exist yet are checked through their nearest existing parent, so a missing root is not an error.
func FrameworkLabel ¶ added in v0.2.0
FrameworkLabel returns provider's human-readable name and true, or ("", false) if provider is empty or outside supportedRailpackProviders.
func FrameworkMemoryFloorMB ¶
FrameworkMemoryFloorMB returns the host memory below which a build of the framework is likely to run out of memory.
func HostPlatform ¶
func HostPlatform() string
HostPlatform is the OS/arch BuildKit builds for: the daemon's own platform, since no cross-platform target is requested.
func LowMemoryWarning ¶
LowMemoryWarning returns a one line warning when the host is under the framework's floor, else "".
func MemoryKillMessage ¶
MemoryKillMessage explains an out of memory build kill: what was set, what the host has, what to do.
func NodeHeapMB ¶
NodeHeapMB is the V8 old space size that leaves room for the rest of a Node build inside the cap.
func SelectBuildNode ¶
SelectBuildNode picks which node a build should run on, given every known node. AcceptsBuildWorkloads is a routing preference, not a hard requirement: a build is never failed just because the node an operator would rather use is unavailable right now.
- No node in nodes has AcceptsBuildWorkloads set: returns "" (this control plane's own local node), matching the "" == local convention migrations/0009_node_placement.sql established for service and database placement. This is also what a single-node deployment sees unchanged.
- At least one node has AcceptsBuildWorkloads set and is Online: the Online one with the lexicographically smallest ID is selected, a deterministic, easy-to-reason-about tie-break rather than anything load-aware; real load-based scheduling is explicitly out of scope, this project's stated non-goal against building a scheduler with bin-packing, affinity rules, or autoscaling in v1, and this is the build-node equivalent of that same non-goal.
- At least one node has AcceptsBuildWorkloads set but none is Online: falls back to "" (local) rather than failing the build, since a build routing preference blipping offline should never be the reason a deploy fails.
func SetNetworkResolver ¶
SetNetworkResolver wires the platform settings value in at boot.
func SlogProgress ¶
func SlogProgress(log *slog.Logger) func(ProgressEvent)
SlogProgress adapts a *slog.Logger into a progress func, for callers that just want build progress logged rather than consumed some other way (an SSE stream, a test assertion). log defaults to slog.Default() if nil.
func StaticMaxBytesFromEnv ¶
StaticMaxBytesFromEnv reads the extraction cap in bytes; zero means unlimited.
func TarContext ¶ added in v0.2.0
TarContext writes dir's contents to w as a tar stream: regular files, directories, and symlinks, with their permission bits. Anything else (sockets, devices, named pipes) is skipped, since no build context meaningfully needs one and Docker's own context upload skips them too.
func UntarConfined ¶
UntarConfined unpacks regular files and directories of r under destDir, skipping links and special files, and fails past maxBytes (0 is unlimited).
func UntarContext ¶ added in v0.2.0
UntarContext extracts a TarContext stream into dir, which must already exist. Entry names that escape dir fail the whole extraction with ErrUnsafeContextPath rather than being skipped: a context that cannot be reproduced faithfully must not be built. Every write goes through an os.Root, so even a symlink the checks below miss cannot redirect a write outside dir.
func ValidatePublicRepoURL ¶ added in v0.2.0
ValidatePublicRepoURL rejects any scheme other than http/https, the same restriction internal/api/git_branches.go always enforced for a caller-supplied repo_url (that file's own doc comment on errRepoURLSchemeNotAllowed explains why: go-git registers a "file" transport unconditionally, and an unrestricted URL would let a caller read local files on this host or probe internal network reachability). Detect and internal/api/build_detect.go share this exact check since both accept the same caller-controlled repo_url shape.
Types ¶
type Attestations ¶ added in v0.2.0
type Attestations struct {
// SBOM is the bare SPDX or CycloneDX document, empty when none was produced.
SBOM []byte
// Provenance is the SLSA provenance predicate, empty when none was produced.
Provenance []byte
// Incomplete is true when scanning the image stream failed part way, so a
// missing SBOM or provenance may be a read error rather than "none produced".
Incomplete bool
// ScanError says why the stream scan stopped, empty when it finished.
ScanError string
}
Attestations are the in-toto predicates BuildKit attached to a build.
type Builder ¶
type Builder interface {
Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
}
Builder is the build surface the Limiter wraps and itself provides.
type CacheConfig ¶
type CacheConfig struct {
// Dir enables BuildKit's "local" cache backend: a directory on this
// process's own disk. Only ever useful to the one build node that
// owns that disk, which is exactly Phase 1's scoped-down cache
// (client.go's WithCacheDir).
Dir string
// RegistryRef enables BuildKit's "registry" cache backend: an image
// reference (e.g. "registry.example.com/levelrail/build-cache:app")
// that cache blobs are pushed to and pulled from using the same
// registry credentials Docker itself already has configured. This
// is the actual "remote cache... shared across dedicated build
// nodes" the build design calls for: any build
// node with network access to the registry gets the same cache,
// unlike Dir.
RegistryRef string
// RegistryInsecure allows the registry backend to talk plain HTTP
// or accept a self-signed certificate, matching BuildKit's own
// "insecure" cache attribute. Only meaningful when RegistryRef is
// set; ignored otherwise.
RegistryInsecure bool
// S3 enables BuildKit's s3 cache backend for one build.
S3 *S3Cache
}
CacheConfig describes every BuildKit cache import/export backend a Client should use. The zero value disables caching entirely, matching WithCacheDir's pre-existing "empty disables cache import/export entirely" behavior.
func (CacheConfig) String ¶
func (c CacheConfig) String() string
String renders c for logging, never including credentials (this struct never holds any: registry auth comes from Docker's own configured credential store, the same as every other registry operation in this codebase, per ensureImage in internal/docker).
type CgroupProvisioner ¶
type CgroupProvisioner interface {
Provision(id string, memoryMB, cpuMillis int) (parent string, cleanup func(), err error)
}
CgroupProvisioner creates a resource-capped cgroup for one build. parent is the value BuildKit takes as its cgroup parent; cleanup removes the cgroup.
func NewCgroupProvisioner ¶
func NewCgroupProvisioner(root string) CgroupProvisioner
NewCgroupProvisioner returns a cgroup v2 provisioner rooted at root, or at /sys/fs/cgroup when root is empty.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client drives builds against the BuildKit instance embedded in a local Docker Engine daemon.
Docker Desktop (and any dockerd using the default "docker" buildx driver, as opposed to the "docker-container" driver) does not expose a bare buildkitd gRPC endpoint of its own. BuildKit runs inside dockerd and is only reachable by hijacking the daemon's HTTP connection at the /grpc endpoint, exactly the way `docker buildx build` reaches it when using the default driver. That is what dockerbuildkit.ClientOpts wires up below, using the same *dockerclient.Client already used to talk to the Engine API elsewhere in this codebase (see internal/docker).
This hijacking approach is the one that actually works in this environment; a standalone buildkitd endpoint is not an option here.
func NewClient ¶
NewClient builds a Client that reaches BuildKit through docker's hijacked connection, and reuses the same Docker Engine API client to load the finished image back into the local image store.
docker must already be a working *dockerclient.Client (e.g. from dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation())). NewClient does not create one itself so callers control the daemon connection lifecycle.
func (*Client) Build ¶
func (c *Client) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
Build runs req end to end: solves the Dockerfile with BuildKit, streams the resulting image as a tar into the local Docker Engine's /images/load endpoint, and waits for both to finish. It never shells out to the docker CLI.
progress, if non-nil, receives every build progress update as it happens, including the docker-image-load phase after BuildKit's own solve finishes (that phase isn't part of BuildKit's SolveStatus stream, but is relayed through the same callback for one unified progress feed). Pass SlogProgress(logger) to just log them, or nil to discard them entirely.
func (*Client) BuildRailpack ¶
func (c *Client) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
BuildRailpack runs req end to end: generates a Railpack build plan for req.SourceDir, rejects it unless Railpack detected a supported provider (supportedRailpackProviders), converts the plan to a BuildKit LLB definition, solves it, and streams the resulting image into the local Docker Engine's /images/load endpoint, exactly like Build does for a Dockerfile. It never shells out to the railpack or docker CLI.
progress behaves exactly as it does for Build: pass SlogProgress(logger) to log every update, or nil to discard them.
func (*Client) Close ¶
Close releases the underlying BuildKit connection. It does not close the Docker Engine API client passed to NewClient, since the caller owns that client's lifecycle.
func (*Client) SolveRemote ¶ added in v0.2.0
func (c *Client) SolveRemote(ctx context.Context, req RemoteRequest, out io.Writer, progress func(ProgressEvent)) (*Result, error)
SolveRemote runs a build dispatched from a control plane against this node's own BuildKit and writes the resulting docker-save tar to out, rather than loading it into this node's image store: the image is wanted by whoever asked for the build, not by the node that ran it.
req.ContextDir must already hold the unpacked build context.
type DetectRequest ¶ added in v0.2.0
type DetectRequest struct {
// RepoURL is the git remote to shallow-clone. Required, and must pass
// ValidatePublicRepoURL.
RepoURL string
// Ref is the branch to check out. Empty means the remote's default
// branch (HEAD).
Ref string
}
DetectRequest is Detect's input: a git source to inspect, never built or pushed anywhere.
type DetectResult ¶ added in v0.2.0
type DetectResult struct {
// Provider is Railpack's own raw provider id (e.g. "node", "golang"),
// empty if Railpack detected nothing buildable at all.
Provider string
// FrameworkName is FrameworkLabel(Provider)'s human-readable name,
// empty if Provider is empty or not in supportedRailpackProviders:
// either way, the caller's own contract is "fall back to manual build
// type selection" for an empty FrameworkName, not an error.
FrameworkName string
// Port is the detected container port the app listens on, or 0 if
// detection failed or no port flag was found. For Node, parsed from
// scripts.start (or preview/serve); for Dockerfile, from EXPOSE.
Port int
// PortSource describes where Port came from (e.g. "from scripts.start
// --port 4173"), empty if Port is 0.
PortSource string
// FrameworkID is the matched internal/frameworks preset id, empty when
// no preset matched.
FrameworkID string
}
DetectResult is Detect's output.
func Detect ¶ added in v0.2.0
func Detect(ctx context.Context, req DetectRequest) (*DetectResult, error)
Detect shallow-clones req.RepoURL at req.Ref into a temporary directory and runs Railpack's own provider detection against it, no build, no image, no BuildKit connection: the same pure core.GenerateBuildPlan call generateRailpackPlan (railpack.go) already uses for a real build, which only ever reads files under the checkout (package.json, go.mod, pom.xml, and the like) to decide a provider, so this never executes anything the checkout itself contains.
Bounded against an untrusted repoURL three ways: a hard timeout (detectCloneTimeout), a shallow, single-branch clone (Depth: 1), and a post-clone size check (detectMaxRepoBytes). None of these are a build's own real safety net (the reconciler and BuildKit's own resource limits still apply once a build is actually triggered); they exist only because this pre-flight check runs synchronously inside an HTTP request the wizard is waiting on.
type ImageExtractor ¶
type ImageExtractor struct {
// contains filtered or unexported fields
}
ImageExtractor copies a directory out of a built image into a host directory, then deletes the image. It never starts the image.
func NewImageExtractor ¶
func NewImageExtractor(docker *dockerclient.Client, maxBytes int64) *ImageExtractor
NewImageExtractor wraps docker; maxBytes caps the extracted size (0 is unlimited).
func (*ImageExtractor) ExtractDir ¶
func (e *ImageExtractor) ExtractDir(ctx context.Context, tag, srcPath, destDir string) error
ExtractDir copies srcPath of image tag into destDir and removes the image.
type ImageSizer ¶
ImageSizer reports a built image's size in bytes, 0 when unknown.
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter queues builds past the per-node concurrency limit, stops builds past the time limit, and records each outcome with its cache hit counts.
func NewLimiter ¶
func NewLimiter(inner Builder, limits LimitsSource, opts ...LimiterOption) *Limiter
NewLimiter wraps inner. limits is read once per build.
func (*Limiter) Build ¶
func (l *Limiter) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
Build implements the deploy pipeline's ImageBuilder.
func (*Limiter) BuildRailpack ¶
func (l *Limiter) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
BuildRailpack implements the deploy pipeline's ImageBuilder.
type LimiterOption ¶
type LimiterOption func(*Limiter)
LimiterOption configures a Limiter.
func WithCgroupProvisioner ¶
func WithCgroupProvisioner(c CgroupProvisioner) LimiterOption
WithCgroupProvisioner enforces the CPU and memory limits through a cgroup per build.
func WithImageSizer ¶
func WithImageSizer(s ImageSizer) LimiterOption
WithImageSizer records the size of every built image.
func WithLimiterClock ¶
func WithLimiterClock(c Clock) LimiterOption
WithLimiterClock overrides the clock, for tests.
func WithLimiterLogger ¶
func WithLimiterLogger(lg *slog.Logger) LimiterOption
WithLimiterLogger overrides the logger.
func WithMemoryGuard ¶
func WithMemoryGuard(g MemGuard, hostMB func() int) LimiterOption
WithMemoryGuard sets the node heap share and a host memory reader (MiB) used for warnings and OOM messages.
func WithNodeResolver ¶
func WithNodeResolver(n NodeResolver) LimiterOption
WithNodeResolver makes the concurrency limit apply per node instead of globally.
func WithRunRecorder ¶
func WithRunRecorder(r RunRecorder) LimiterOption
WithRunRecorder records every finished build.
type Limits ¶
Limits caps builds. A zero field means unlimited. MemoryMB and CPUMillis (1000 = one core) are per build; the rest are control-plane wide.
type LimitsSource ¶
LimitsSource resolves the limits for the next build of app, so a settings change applies without a restart.
type NetworkFacts ¶
type NetworkFacts struct {
Mode NetworkMode
Enforced bool
Summary string
NotProven string
DocsAnchor string
}
NetworkFacts states what a build's RUN steps can reach under mode. The open wording names what the code does not restrict, not what was probed.
type NetworkMode ¶
type NetworkMode string
NetworkMode is how much network a build's RUN steps get.
const ( // NetworkOpen leaves RUN steps with whatever the engine's BuildKit gives // them. It is the default and the behaviour before the knob existed. NetworkOpen NetworkMode = "open" // NetworkNone runs every RUN step without a network interface. NetworkNone NetworkMode = "none" // NetworkBlockPrivate is reserved for blocking metadata and private // ranges while keeping the internet. No build path can enforce it yet. NetworkBlockPrivate NetworkMode = "block-private" )
func ParseNetworkMode ¶
func ParseNetworkMode(raw string) (NetworkMode, error)
ParseNetworkMode reads APP_BUILD_NETWORK. Empty is open; an unknown value is an error so a typo on a security setting never degrades to open.
func ResolveForDispatch ¶
func ResolveForDispatch(ctx context.Context) (NetworkMode, error)
ResolveForDispatch is the mode a build sent to a node must carry.
func (NetworkMode) Facts ¶
func (m NetworkMode) Facts() NetworkFacts
Facts describes the effective policy for the doctor and docs.
type NodeBuilder ¶ added in v0.2.0
type NodeBuilder interface {
BuildOnNode(ctx context.Context, nodeID string, req RemoteRequest, image io.Writer, progress func(ProgressEvent)) (*Result, error)
}
NodeBuilder runs a build on one specific node and streams the result back: progress as it happens, and the docker-save image tar into image. internal/agent's BuildDispatcher satisfies this over the agent transport; nothing in this package knows how a node is reached.
type NodeInfo ¶
type NodeInfo struct {
// ID is the node's identifier, matching store.Node.ID.
ID string
// AcceptsBuildWorkloads mirrors store.Node.AcceptsBuildWorkloads
// (migrations/0010_node_workloads.sql): whether this node has opted
// in to running build work at all.
AcceptsBuildWorkloads bool
// Online reports whether this node is currently reachable through
// the agent transport, i.e. whether
// agent.Registry.Get for this node's ID currently succeeds. A
// build-capable node the control plane cannot currently reach is
// not a usable candidate, the same reasoning resolveNodeTransport
// (cmd/levelrail/main.go) already applies to service/database
// placement.
Online bool
}
NodeInfo is the minimal node shape SelectBuildNode needs. Deliberately not store.Node: internal/build has no dependency on internal/store today, and build routing only ever cares about three of a node's fields, the same narrow, consumer-defined interface convention every other package boundary in this codebase already follows (e.g. internal/deploy's ImageBuilder/ServiceStore/SecretChecker).
type NodeResolver ¶
NodeResolver names the node the next build will run on, "" for the local node.
type NodeSource ¶ added in v0.2.0
NodeSource reports every node the control plane currently knows about, with whichever of them are reachable right now marked Online. Consulted per build rather than once at startup, so marking a node build-capable, or a build node dropping offline, takes effect on the next build instead of the next control-plane restart.
type Option ¶
type Option func(*Client)
Option configures optional Client behavior.
func WithCacheDir ¶
WithCacheDir enables BuildKit's local cache backend, importing from and exporting to dir on every build. Empty (the default) disables cache import/export entirely, not an empty-but-present cache.
This was the build design's "remote cache" goal scoped honestly for Phase 1's single-node target, before WithCacheRegistry was added: a cache genuinely shared across dedicated build nodes needs a registry or object-store backend and dedicated build nodes to share it with. WithCacheDir is still useful on its own (fast incremental rebuilds on one node with no registry round trip) and composes with WithCacheRegistry rather than being replaced by it: both may be set on the same Client, see CacheConfig.
func WithCacheRegistry ¶
WithCacheRegistry enables BuildKit's registry cache backend (CacheConfig.RegistryRef): this is the actual remote cache the build design asks for, shared by every build node with network access to ref's registry, unlike WithCacheDir's per-machine directory. Empty ref disables the registry backend, the same "empty disables this backend" convention WithCacheDir already establishes.
func WithCacheRegistryInsecure ¶
func WithCacheRegistryInsecure() Option
WithCacheRegistryInsecure allows the registry cache backend (WithCacheRegistry) to talk plain HTTP or accept a self-signed certificate. Only meaningful combined with WithCacheRegistry; NewClient returns ErrCacheRegistryRefRequired if this is set without a registry ref configured, rather than silently ignoring it.
type ProgressEvent ¶
type ProgressEvent struct {
// Step is the build step's name, e.g. "[2/4] RUN go build".
Step string
// Cached is true when this step was skipped because BuildKit's cache
// (see WithCacheDir) already had the result.
Cached bool
// Completed is true once Step finished, successfully or not.
Completed bool
// Error is non-empty when Step failed.
Error string
// Log is a raw output line from the step (stdout/stderr from the
// build), empty for step-lifecycle events.
Log string
// Stream is "stdout" or "stderr" when Log is non-empty, and empty
// for a step-lifecycle event (Log == ""), since those never came
// from either stream. Added for internal/deploylog.Recorder, so a
// persisted/live-streamed build log line can carry the same
// stdout/stderr distinction web/src/hooks/useDeployLogStream.ts's
// contract already expects (its own doc comment: "distinguishing
// stdout/stderr is useful for highlighting failed build steps").
// Populated from BuildKit's own VertexLog.Stream (see
// relayProgress), 1/2 being BuildKit's stdout/stderr convention; the
// docker-image-load phase (loadImage) has no equivalent stream
// signal from the Engine API, so it always reports "stdout".
Stream string
// CacheWarning is set when the remote build cache was skipped or failed and
// the build carried on without it. Log carries the same text.
CacheWarning string
}
ProgressEvent is one structured build progress update, deliberately decoupled from BuildKit's own SolveStatus wire type so callers, a future SSE handler in particular (the build design's build log streaming requirement), don't need to import moby/buildkit/client just to format a build log line for a browser.
type RailpackRequest ¶
type RailpackRequest struct {
// SourceDir is the app's source root Railpack inspects to detect its
// provider and generate a build plan, and the build context BuildKit
// reads from. Required.
SourceDir string
// Tag is the image reference given to the built image, e.g.
// "levelrail/thesvg:abc1234". Required.
Tag string
// CgroupParent places every build step in a pre-sized cgroup. Set by the Limiter; local builds only.
CgroupParent string
// NodeHeapMB, when positive, sets NODE_OPTIONS=--max-old-space-size for Node builds that did not set NODE_OPTIONS.
NodeHeapMB int
// Network is the RUN step policy; the Client fills it from APP_BUILD_NETWORK.
Network NetworkMode
// Env is build-time env, as Railpack's own --env does. Callers pass only
// public-prefix literals; it reaches every build step as an env var.
Env map[string]string
}
RailpackRequest describes a single Railpack-detected build: no Dockerfile, no user-authored build steps, just a source directory Railpack inspects itself to determine how to build it.
func (RailpackRequest) Validate ¶
func (r RailpackRequest) Validate() error
Validate checks the request is well-formed enough to attempt a build. Like Request.Validate, it does not check the filesystem: a missing source directory surfaces later, from Railpack's own app.NewApp call, which already produces a clear error for that case.
type RemoteKind ¶ added in v0.2.0
type RemoteKind string
RemoteKind selects which solve path a dispatched build runs, since a Dockerfile build and a Railpack build are genuinely different solves (see railpack.go) rather than one solve with a flag.
const ( RemoteKindDockerfile RemoteKind = "dockerfile" RemoteKindRailpack RemoteKind = "railpack" )
RemoteKindDockerfile and RemoteKindRailpack are the two dispatchable build kinds, matching internal/deploy's own two building build types.
type RemoteRequest ¶ added in v0.2.0
type RemoteRequest struct {
Kind RemoteKind
// ContextDir is the build context root on whichever side currently
// holds it: the control plane's checkout when dispatching, the build
// node's unpacked copy when running.
ContextDir string
// DockerfilePath is relative to ContextDir, since the control plane's
// own absolute paths mean nothing on the build node. Empty means the
// context root's own Dockerfile. Ignored for RemoteKindRailpack.
DockerfilePath string
Tag string
Target string
BuildArgs map[string]string
NoCache bool
// Cache is the registry cache backend the build node should import
// from and export to. CacheConfig.Dir is never carried here: it names
// a directory on the dispatching side's own disk.
Cache CacheConfig
// Network is the control plane's resolved build network mode. The node
// runs the stricter of it and its own setting, never a looser one.
Network NetworkMode
}
RemoteRequest is one build dispatched to another node: everything the build node needs that is not the context's bytes.
func NewRemoteRailpackRequest ¶ added in v0.2.0
func NewRemoteRailpackRequest(req RailpackRequest, cache CacheConfig) (RemoteRequest, error)
NewRemoteRailpackRequest turns a local Railpack build into a dispatchable one.
func NewRemoteRequest ¶ added in v0.2.0
func NewRemoteRequest(req Request, cache CacheConfig) (RemoteRequest, error)
NewRemoteRequest turns a local Dockerfile build into a dispatchable one, re-rooting DockerfilePath relative to the context.
type Request ¶
type Request struct {
// ContextDir is the build context root, matching `docker build <dir>`.
// Required.
ContextDir string
// DockerfilePath is the path to the Dockerfile. It may live outside
// ContextDir. Defaults to "<ContextDir>/Dockerfile" when empty.
DockerfilePath string
// RootDir, when set, is the checkout the build must stay inside:
// ContextDir and the Dockerfile must resolve within it after symlinks.
RootDir string
// Tag is the image reference given to the built image, e.g.
// "levelrail-spike:latest". Required.
Tag string
// Target is an optional multi-stage build target name.
Target string
// BuildArgs are passed through as build-time --build-arg equivalents.
BuildArgs map[string]string
// NoCache disables BuildKit's cache for this build.
NoCache bool
// Attest asks BuildKit for an SBOM and minimal provenance attestation.
Attest bool
// S3Cache adds BuildKit's s3 cache backend for this build. Nil means none.
S3Cache *S3Cache
// Secrets are BuildKit secret mounts by id, readable in a Dockerfile
// with RUN --mount=type=secret,id=<id>. They never enter an image layer
// or the build cache key. Local builds only: a remote build node does
// not receive them.
Secrets map[string][]byte
// CgroupParent places the build's processes in a pre-sized cgroup so
// CPU and memory limits hold. Set by the Limiter; local builds only.
CgroupParent string
// Network is the RUN step policy; the Client fills it from APP_BUILD_NETWORK.
Network NetworkMode
}
Request describes a single Dockerfile build. It is deliberately the pure, unit-testable input to newSolveOpt: nothing in this file touches a filesystem, a network, or a daemon, so Validate can be exercised with plain table-driven tests that need no live BuildKit connection.
type Result ¶
type Result struct {
Tag string
Duration time.Duration
// ExporterResponse is BuildKit's raw exporter metadata (e.g. the
// resulting image ID), passed through for callers that need it.
ExporterResponse map[string]string
// Attestations holds the SBOM and provenance of a build run with Request.Attest.
Attestations Attestations
}
Result is what a successful build produced.
type Router ¶ added in v0.2.0
type Router struct {
// contains filtered or unexported fields
}
Router is the build entry point the deploy pipeline calls: it picks a build node (SelectBuildNode, node.go) and either builds locally or dispatches, with the same method set and the same results either way.
func NewRouter ¶ added in v0.2.0
func NewRouter(local *Client, nodes NodeSource, remote NodeBuilder, opts ...RouterOption) *Router
NewRouter builds a Router over local. nodes and remote may both be nil, which pins every build to local: that is the single-node default, not a degraded mode.
func (*Router) Build ¶ added in v0.2.0
func (r *Router) Build(ctx context.Context, req Request, progress func(ProgressEvent)) (*Result, error)
Build implements internal/deploy.ImageBuilder.
func (*Router) BuildRailpack ¶ added in v0.2.0
func (r *Router) BuildRailpack(ctx context.Context, req RailpackRequest, progress func(ProgressEvent)) (*Result, error)
BuildRailpack implements internal/deploy.ImageBuilder.
type RouterOption ¶ added in v0.2.0
type RouterOption func(*Router)
RouterOption configures optional Router behavior.
func WithRouterLogger ¶ added in v0.2.0
func WithRouterLogger(logger *slog.Logger) RouterOption
WithRouterLogger overrides the logger a dispatch decision is reported on. Defaults to slog.Default().
type Run ¶
type Run struct {
App string `json:"app"`
Tag string `json:"tag"`
StartedAt time.Time `json:"started_at"`
Duration time.Duration `json:"-"`
QueueWait time.Duration `json:"-"`
Status RunStatus `json:"status"`
CachedSteps int `json:"cached_steps"`
TotalSteps int `json:"total_steps"`
Error string `json:"error,omitempty"`
Node string `json:"node,omitempty"`
Platform string `json:"platform,omitempty"`
ImageBytes int64 `json:"image_bytes,omitempty"`
}
Run is one finished build as the Limiter saw it.
type RunRecorder ¶
RunRecorder persists one finished build.
type S3Cache ¶ added in v0.2.0
type S3Cache struct {
Region string
Bucket string
Prefix string
Endpoint string
PathStyle bool
AccessKeyID string
SecretAccessKey string
Mode string
}
S3Cache configures BuildKit's s3 cache backend. It carries credentials, so it must never be logged or serialised; String is safe.
type SpeedCacheState ¶
SpeedCacheState is what the control plane knows about the app's remote cache setting.
type SpeedFinding ¶
type SpeedFinding struct {
Code string `json:"code"`
Severity string `json:"severity"`
FixKind string `json:"fix_kind"`
TargetID string `json:"target_id,omitempty"`
}
SpeedFinding is one suggestion to make builds faster.
type SpeedReport ¶
type SpeedReport struct {
Builds int `json:"builds"`
WarmP50Seconds float64 `json:"warm_p50_seconds"`
ColdP50Seconds float64 `json:"cold_p50_seconds"`
WarmKnown bool `json:"warm_known"`
TimeSavedSeconds float64 `json:"time_saved_seconds"`
Findings []SpeedFinding `json:"findings"`
}
SpeedReport is the build speed summary and advice for one app.
func AdviseSpeed ¶
func AdviseSpeed(runs []Run, cache SpeedCacheState) SpeedReport
AdviseSpeed analyses runs (newest first) and returns time saved by cache and fix suggestions.
type Trend ¶
type Trend struct {
Builds int `json:"builds"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
TimedOut int `json:"timed_out"`
Canceled int `json:"canceled"`
Skewed int `json:"skewed"`
P50Seconds float64 `json:"p50_seconds"`
P95Seconds float64 `json:"p95_seconds"`
MinSeconds float64 `json:"min_seconds"`
MaxSeconds float64 `json:"max_seconds"`
// CacheHitRatio is cached steps over total steps across successful builds,
// valid only when CacheKnown is true.
CacheHitRatio float64 `json:"cache_hit_ratio"`
CacheKnown bool `json:"cache_known"`
}
Trend summarises the last N builds of one app. Percentiles cover successful builds only; failed, timed out and canceled builds are counted, not timed.
func ComputeTrend ¶
ComputeTrend folds runs into a Trend. A successful run with a negative duration (clock skew) is counted in Skewed and left out of the timings.
type UnsupportedProviderError ¶
type UnsupportedProviderError struct {
// Provider is the provider name Railpack detected, or "" if it
// detected none at all.
Provider string
}
UnsupportedProviderError is returned when Railpack's own detection (core.BuildResult.DetectedProviders) resolved to a provider outside this slice's scope (supportedRailpackProviders), including the empty string for "no provider matched at all". It is a distinct type, not a plain fmt.Errorf, so callers (internal/deploy in particular) can errors.As it to produce a caller-appropriate message rather than a generic build failure, the same "fail loudly, not silently" pattern deploy.go's validateEnv already establishes for unsupported env resolution.
func (*UnsupportedProviderError) Detail ¶
func (e *UnsupportedProviderError) Detail() string
Detail is the message without the package prefix, for callers that add their own (internal/deploy).
func (*UnsupportedProviderError) Error ¶
func (e *UnsupportedProviderError) Error() string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package buildkitd supervises the managed rootless buildkitd container that serves source builds on engines without an embedded BuildKit (ADR 046).
|
Package buildkitd supervises the managed rootless buildkitd container that serves source builds on engines without an embedded BuildKit (ADR 046). |