Documentation
¶
Overview ¶
Package spec parses and validates the app.yaml file: the one declarative file a user writes in their repo, per the app spec design. Validation happens in two layers. Structural shape (required fields, enums, string patterns, unknown-key rejection) is checked against the embedded JSON Schema (schema/app.schema.json), kept as the single source of truth so CI and the deploy-time check share one definition, not two hand-maintained copies that drift. Rules the schema can't express (a field's validity depending on another field's value) are checked by Validate after parsing.
Index ¶
- Constants
- Variables
- func AddStaticHeaderLine(b *StaticHeaderBlock, line string, l StaticLimits) error
- func CompileStaticEntries(routes []StaticRule, blocks []StaticHeaderBlock, opts *StaticServeOptions, ...) ([]StaticRule, []StaticHeaderBlock, error)
- func ContextBranchPattern(name string) (string, bool)
- func DiscoverPath(dir string, brandedName string) (string, error)
- func HeaderNameProblem(side, name string) string
- func IsPendingImage(image string) bool
- func IsStaticRedirectStatus(s int) bool
- func IsToken(s string) bool
- func NormalizeRepoPath(field, p string) (string, error)
- func ParseStaticHeaders(src string, l StaticLimits) ([]StaticHeaderBlock, []StaticProblem)
- func ParseStaticRedirects(src string, l StaticLimits) ([]StaticRule, []StaticProblem)
- func SafeHeaderValue(v string) bool
- func SchemaJSON() ([]byte, error)
- func StaticSidecarPath(rootDir string) string
- func ValidateContextConfig(label, buildType string, c ContextOverrides) error
- func ValidateContextName(name string) error
- func ValidateDocument(data []byte) (*Spec, []Issue)
- func ValidateLabels(labels map[string]string) error
- func ValidateStaticBuild(name, buildType, command, publish, path string, args map[string]string) error
- func ValidateStaticHeaderBlock(b *StaticHeaderBlock, l StaticLimits) error
- func ValidateStaticOptions(o *StaticServeOptions) error
- func ValidateStaticRule(r *StaticRule, l StaticLimits) error
- func WriteStaticSidecar(rootDir string, rs *StaticRuleset) error
- type Autoscale
- type Backup
- type Budget
- type Build
- type Canary
- type ContextBuild
- type ContextConfig
- type ContextOverrides
- type Database
- type Egress
- type EgressAllow
- type EnvVar
- type ExecCommand
- type GPU
- type HeaderRule
- type Health
- type Hooks
- type Issue
- type Job
- type Probe
- type Resources
- type Route
- type Service
- type Spec
- type StaticHeaderBlock
- type StaticHeaderKV
- type StaticInput
- type StaticLimits
- type StaticOptions
- type StaticPattern
- type StaticProblem
- type StaticQueryMatch
- type StaticRule
- type StaticRuleset
- type StaticServeOptions
- type StatusCodes
- type VaultRef
- type Volume
Constants ¶
const ( ContextProduction = "production" ContextDeployPreview = "deploy-preview" ContextBranchDeploy = "branch-deploy" ContextBranchPrefix = "branch:" )
Deploy context names. Production is the app's own settings and has no stored row.
const ( HeaderSideRequest = "request" HeaderSideResponse = "response" )
Header sides for HeaderNameProblem.
const ( MaxLabels = 32 MaxLabelKeyLength = 255 MaxLabelValueLength = 4096 )
Sanity limits on custom labels: a genuine safety bound against unbounded container metadata growth, not a business threshold, so a fixed constant is appropriate here (the project's "no hardcoded thresholds" rule targets values a deployment might reasonably need to tune, like a retention window or a rate limit; a handful of operator hand-written monitoring/tooling labels per service isn't that). Docker itself doesn't publish an official label count/size limit, so these are chosen generously enough for real use (a monitoring agent or log shipper typically wants a handful of keys) while still bounding how much metadata one service can accumulate.
const ( BuildDockerfile = "dockerfile" BuildCompose = "compose" BuildRailpack = "railpack" BuildStatic = "static" BuildImage = "image" )
Build input types: the app spec's build.type values, matching the BuildKit-based build detection (Dockerfile, Compose, Railpack, static) plus one non-build type: a prebuilt image already sitting in a registry, deployed as-is with no build step at all (see Build.Image and internal/deploy's deployImage).
const ( StrategyRolling = "rolling" StrategyRecreate = "recreate" StrategyBlueGreen = "blue-green" // StrategyCanary saves the canary: policy; the stable promotion itself // still rolls out blue-green. StrategyCanary = "canary" )
Deploy strategies, part of the app spec. Blue-green is the effective default, since it's easier to get right than rolling with a single replica, applied by DefaultStrategy when Strategy is empty, not baked into the schema itself.
const ( EngineFake = "" // zero value only, never valid; see Validate EnginePostgres = "postgres" EngineRedis = "redis" EngineMySQL = "mysql" EngineMongoDB = "mongodb" EngineMariaDB = "mariadb" EngineKeyDB = "keydb" EngineClickHouse = "clickhouse" EngineDragonfly = "dragonfly" EngineValkey = "valkey" EngineMeilisearch = "meilisearch" EngineQdrant = "qdrant" EngineTypesense = "typesense" EngineMemcached = "memcached" )
Supported managed database engines: Postgres and Redis shipped as first-class resources in the initial release, MySQL, MongoDB, MariaDB, KeyDB, ClickHouse, and Dragonfly joined once internal/reconcile/database's controller grew matching engine cases.
const ( StaticSourceRedirects = "_redirects" StaticSourceHeaders = "_headers" StaticSeverityError = "error" StaticSeverityWarning = "warning" // StaticSidecarSuffix names the compiled rules file stored beside a published tree. StaticSidecarSuffix = ".routes.json" )
Static routing sources, severities and sidecar naming.
const ( StaticSlashOff = "off" StaticSlashAdd = "add" StaticSlashRemove = "remove" )
Trailing slash modes for StaticServeOptions.TrailingSlash.
const ( EnvStaticMaxRules = "APP_STATIC_ROUTES_MAX_RULES" EnvStaticMaxHeaderBlocks = "APP_STATIC_ROUTES_MAX_HEADER_BLOCKS" EnvStaticMaxFileBytes = "APP_STATIC_ROUTES_MAX_FILE_BYTES" )
Environment variables that tune the rule-count caps in StaticLimits.
const ( // TriggerModePush deploys on every push to the git source's // configured branch. The default, matching pre-trigger-mode // behavior. TriggerModePush = "push" // TriggerModeRelease deploys only on a tag ref push, or (GitHub // only) a "release" webhook event with action "published". TriggerModeRelease = "release" )
Git source deploy trigger modes (store.GitSource.TriggerMode): which pushes to a connected repo actually trigger a deploy.
const ( JobModeFresh = "fresh" JobModeExec = "exec" JobCatchUpSkip = "skip" JobCatchUpRunOnce = "run-once" )
Job modes and catch-up values accepted in app.yaml.
const DefaultReplicas = 1
DefaultReplicas is used when a service doesn't set replicas.
const EgressModeAllowlist = "allowlist"
EgressModeAllowlist is the only meaningful Egress.Mode value today; see Egress's own doc comment for why the block is opt-in rather than defaulting to deny-all.
const EnvBuildSecretsMax = "APP_BUILD_SECRETS_MAX"
EnvBuildSecretsMax overrides how many build.secrets one service may list.
const GPUAll = -1
GPUAll is GPU.Count's "every GPU on the node" value.
const InstanceLabelKey = ReservedLabelPrefix + "instance"
InstanceLabelKey is the Docker label key every container, network, and volume this control-plane instance creates gets stamped with, set to its own persistent instance ID (store.GetOrCreateInstanceID). Two separate control-plane instances (two installs, or a dev and a prod instance) can end up sharing one Docker daemon; without this, a brand-labeled or brand-namespaced resource alone doesn't say which instance created it, so one instance's stale-resource cleanup could mistake the other's live resource for its own garbage and remove it. Under ReservedLabelPrefix so no operator-supplied label can collide with or spoof it (ValidateLabels below already rejects that prefix).
const PendingImageTag = ":pending"
PendingImageTag is the tag an app is created with when its first image has not been built yet (the CLI's own git-build path, which must send some image for an app POST that requires one). It never names a real registry image, so the application reconciler treats it as "not built yet" rather than something to pull.
const ReservedLabelPrefix = "platform-reserved."
ReservedLabelPrefix is the Docker label key namespace no operator- supplied custom label may use, keeping it open for this platform's own bookkeeping labels (none exist yet: today a service's containers are found purely by name pattern, internal/reconcile/application's ContainerName/ownsContainer, not by label). It's a fixed, brand- independent string on purpose, not derived from brand.ShortName: brand.ShortName is runtime-configurable (APP_BRAND_SHORT_NAME), and a validation boundary keyed to a value that can change between deploys would make a previously-valid app.yaml unpredictably valid or invalid depending on unrelated brand config. It also has to hold for cmd/levelrail-cli, which validates a local app.yaml with no server connection and therefore no brand config to consult at all. This package stays decoupled from internal/brand for the same reason DiscoverPath (discover.go) does.
const StaticSourceSpec = "app.yaml"
StaticSourceSpec labels rules that come from app.yaml.
Variables ¶
var ( // StaticPlaceholderRe matches a :name placeholder in a rule target. StaticPlaceholderRe = regexp.MustCompile(`:([A-Za-z_][A-Za-z0-9_]*)`) )
Functions ¶
func AddStaticHeaderLine ¶
func AddStaticHeaderLine(b *StaticHeaderBlock, line string, l StaticLimits) error
AddStaticHeaderLine parses one "Name: value" or "! Name" line into b.
func CompileStaticEntries ¶
func CompileStaticEntries(routes []StaticRule, blocks []StaticHeaderBlock, opts *StaticServeOptions, l StaticLimits) ([]StaticRule, []StaticHeaderBlock, error)
CompileStaticEntries validates the app.yaml routes and header blocks and returns them with matchers prepared and Source set.
func ContextBranchPattern ¶
ContextBranchPattern returns the pattern of a "branch:<pattern>" context.
func DiscoverPath ¶
DiscoverPath finds the app spec file in dir, checking candidate filenames in order and returning the first one that exists. The spec file is discovered by a list of candidate filenames (app.yaml, deploy.yaml, plus the branded one) so a product rename does not break existing users later. The generic names are checked first for exactly that reason: if the product is ever renamed, brandedName changes, but a repo with an app.yaml already committed keeps working without the user touching anything.
brandedName is the caller-supplied branded filename stem (derived from brand.Brand, not imported here, so this package stays decoupled from internal/brand); pass "" to skip it, e.g. in tests.
func HeaderNameProblem ¶
HeaderNameProblem returns why name cannot be used on side, or "".
func IsPendingImage ¶ added in v0.2.0
IsPendingImage reports whether image is a PendingImageTag placeholder.
func IsStaticRedirectStatus ¶
IsStaticRedirectStatus reports whether s is a 3xx code.
func NormalizeRepoPath ¶ added in v0.2.0
NormalizeRepoPath cleans a repository-relative path typed by an operator. An empty result means the repository root. It rejects absolute paths, backslashes, NUL bytes and any ".." that would leave the repository.
func ParseStaticHeaders ¶
func ParseStaticHeaders(src string, l StaticLimits) ([]StaticHeaderBlock, []StaticProblem)
ParseStaticHeaders parses Netlify _headers syntax: a path line, then indented "Name: value" lines, or "! Name" to remove a header.
func ParseStaticRedirects ¶
func ParseStaticRedirects(src string, l StaticLimits) ([]StaticRule, []StaticProblem)
ParseStaticRedirects parses Netlify _redirects syntax: "from [key=val ...] to [status[!]]" per line. Bad lines are skipped and reported, never applied half understood.
func SafeHeaderValue ¶
SafeHeaderValue reports whether v can be written into a header without splitting it: no CR, LF or NUL, and no other control byte except tab.
func SchemaJSON ¶ added in v0.2.0
SchemaJSON returns the embedded app.yaml JSON Schema, for tooling that composes it into larger documents.
func StaticSidecarPath ¶
StaticSidecarPath is where the ruleset of the tree at rootDir is stored: beside the tree, so it is never served and follows the tree on rollback.
func ValidateContextConfig ¶
func ValidateContextConfig(label, buildType string, c ContextOverrides) error
ValidateContextConfig checks one context's overrides against the app's build type.
func ValidateContextName ¶
ValidateContextName checks that name is a settable context and its pattern is valid.
func ValidateDocument ¶ added in v0.2.0
ValidateDocument runs the same two layers Parse does (schema, then Validate's semantic rules) but never stops at the first problem: every schema violation comes back at once, located by path and line, for a live editor to annotate while the operator is still typing. The returned Spec is non-nil only when there are no issues. Deploy time still calls Parse directly; this exists only to give that same check friendlier, earlier feedback, not a second source of truth.
func ValidateLabels ¶
ValidateLabels checks labels against the rules above: reserved prefix, empty keys, and the sanity limits. Exported so internal/api's validateAppResource can run the identical check on API-sourced labels (which bypass Parse/Validate entirely, they're never described in app.yaml), the same "define once here, both layers call it" shape StrategyRolling/StrategyRecreate/StrategyBlueGreen already establish for the API layer to reference rather than re-derive.
func ValidateStaticBuild ¶
func ValidateStaticBuild(name, buildType, command, publish, path string, args map[string]string) error
ValidateStaticBuild checks command and publish for a build of buildType.
func ValidateStaticHeaderBlock ¶
func ValidateStaticHeaderBlock(b *StaticHeaderBlock, l StaticLimits) error
ValidateStaticHeaderBlock checks a block built from app.yaml and prepares it.
func ValidateStaticOptions ¶
func ValidateStaticOptions(o *StaticServeOptions) error
ValidateStaticOptions checks the serving switches.
func ValidateStaticRule ¶
func ValidateStaticRule(r *StaticRule, l StaticLimits) error
ValidateStaticRule checks a rule and prepares its matcher. It is the single gate for file rules and app.yaml rules alike.
func WriteStaticSidecar ¶
func WriteStaticSidecar(rootDir string, rs *StaticRuleset) error
WriteStaticSidecar stores rs beside the tree at rootDir, replacing any earlier one.
Types ¶
type Autoscale ¶
type Autoscale struct {
Min int `yaml:"min"`
Max int `yaml:"max"`
Metric string `yaml:"metric"`
Target float64 `yaml:"target"`
// Tolerances are percents of Target: scale up above it, scale down below it.
ScaleUpTolerance int `yaml:"scale_up_tolerance,omitempty"`
ScaleDownTolerance int `yaml:"scale_down_tolerance,omitempty"`
// Durations use Go syntax such as 90s or 10m.
ScaleUpCooldown string `yaml:"scale_up_cooldown,omitempty"`
ScaleDownCooldown string `yaml:"scale_down_cooldown,omitempty"`
Stabilization string `yaml:"stabilization,omitempty"`
MaxStepUp int `yaml:"max_step_up,omitempty"`
MaxStepDown int `yaml:"max_step_down,omitempty"`
}
Autoscale is a service's replica autoscaling policy (internal/autoscale). It owns the replica count, so it cannot be combined with replicas:.
type Budget ¶
type Budget struct {
// Egress is a size such as 500GiB or 2TB.
Egress string `yaml:"egress,omitempty"`
Cost float64 `yaml:"cost,omitempty"`
Action string `yaml:"action,omitempty"`
Thresholds []int `yaml:"thresholds,omitempty"`
ThrottleRPS int `yaml:"throttle_rps,omitempty"`
}
Budget is a service's monthly egress and cost guardrail (internal/spendcap).
type Build ¶
type Build struct {
Type string `yaml:"type"`
Path string `yaml:"path,omitempty"`
// BaseDirectory scopes the build context to a subdirectory of the
// repo, e.g. "apps/web" in a monorepo. Empty means the repo root.
// Meaningful for dockerfile, railpack, and static; not meaningful
// for image (nothing gets built) or compose (the compose file's own
// context: field already scopes each service).
BaseDirectory string `yaml:"baseDirectory,omitempty"`
// Command is the shell command that builds a static site, run in a
// sandboxed build container. Only meaningful for build.type: static.
Command string `yaml:"command,omitempty"`
// Publish is the directory the build writes the site to, relative to the
// base directory. Required with Command; the files in it are what is served.
Publish string `yaml:"publish,omitempty"`
// Image is a full registry reference (e.g.
// "ghcr.io/org/app:v1.2.3"), only meaningful for build.type: image:
// a CI pipeline (or anything else) already built and pushed this
// exact image, so there is nothing for this control plane to build,
// only to deploy as-is. See internal/deploy's deployImage.
Image string `yaml:"image,omitempty"`
// RegistryCredential names a store.RegistryCredential (by its Name,
// not ID: app.yaml is hand-written, an opaque ID would be hostile
// to author) to authenticate with when pulling Image from a private
// registry. Empty means an unauthenticated (public) pull.
RegistryCredential string `yaml:"registryCredential,omitempty"`
// Args are Dockerfile build-time ARG values (e.g. a base image
// version, a build-time feature flag), passed through as
// --build-arg equivalents to BuildKit. Only meaningful for
// build.type: dockerfile.
Args map[string]string `yaml:"args,omitempty"`
// Secrets names app secrets mounted as BuildKit secrets (id = name)
// for RUN --mount=type=secret. Only meaningful for dockerfile.
Secrets []string `yaml:"secrets,omitempty"`
}
Build describes how a service's image gets built.
func ResolveGitBuild ¶ added in v0.2.0
ResolveGitBuild turns a git source's persisted build settings into the Build a deploy runs. baseDirectory is the build context (empty is the repository root); buildPath is the Dockerfile (or static output directory) relative to the repository root, never to baseDirectory. The returned Build.Path is relative to the context, which is what the deploy pipeline joins onto it.
func (Build) RunsStaticBuild ¶
RunsStaticBuild reports whether the build compiles a static site in a build container, as opposed to serving committed files as-is.
type Canary ¶
type Canary struct {
Stages []int `yaml:"stages,omitempty"`
// Interval is how long each stage observes traffic, Go duration syntax.
Interval string `yaml:"interval,omitempty"`
MaxErrorRate float64 `yaml:"max_error_rate,omitempty"`
MaxP95Ratio float64 `yaml:"max_p95_ratio,omitempty"`
}
Canary is a service's staged canary policy (internal/canary). Unset fields fall back to the APP_CANARY_* defaults.
func (*Canary) Policy ¶
func (c *Canary) Policy(base canarypolicy.Policy) (canarypolicy.Policy, error)
Policy overlays the block on base and validates the result.
type ContextBuild ¶
type ContextBuild struct {
Command string `yaml:"command,omitempty"`
Publish string `yaml:"publish,omitempty"`
Args map[string]string `yaml:"args,omitempty"`
}
ContextBuild is the build subset a context may override.
type ContextConfig ¶
type ContextConfig struct {
Env map[string]string `yaml:"env,omitempty"`
Build ContextBuild `yaml:"build,omitempty"`
}
ContextConfig overrides env and build settings for one deploy context.
func (ContextConfig) DeployConfig ¶
func (c ContextConfig) DeployConfig() ContextOverrides
DeployConfig converts c to the resolver's form.
type ContextOverrides ¶
type ContextOverrides struct {
Env map[string]string
BuildCommand string
Publish string
BuildArgs map[string]string
}
ContextOverrides is the settings one context can override. Empty fields inherit.
func (ContextOverrides) Empty ¶
func (c ContextOverrides) Empty() bool
Empty reports whether c overrides nothing.
type Database ¶
type Database struct {
Engine string `yaml:"engine"`
Version string `yaml:"version,omitempty"`
Backup *Backup `yaml:"backup,omitempty"`
// EphemeralInPreviews opts this database into a disposable,
// preview-scoped instance of its own (a full container, its own
// volume, its own credentials) rather than sharing whatever this
// database resolves to in production: one per pull request, created
// alongside the preview and destroyed with it, with no restore path
// once torn down. Off by default, like every other opt-in toggle in
// this codebase; only meaningful for a database attached to an app
// with preview environments enabled (store.GitSource.PreviewEnabled).
EphemeralInPreviews bool `yaml:"ephemeralInPreviews,omitempty"`
// IsolatedInPreviews opts this database into an isolated credential
// per preview, created on this same existing database (no new
// container, no new volume) rather than the whole disposable
// instance EphemeralInPreviews creates: a Postgres role, or a Redis
// ACL user scoped to its own key prefix. Ignored when
// EphemeralInPreviews is also set, since an ephemeral instance
// already has its own credentials. Postgres and Redis only today.
// Off by default.
IsolatedInPreviews bool `yaml:"isolatedInPreviews,omitempty"`
}
Database is one entry under databases:.
type Egress ¶ added in v0.2.0
type Egress struct {
Mode string `yaml:"mode"`
Allow []EgressAllow `yaml:"allow,omitempty"`
}
Egress is a service's outbound network policy.
type EgressAllow ¶ added in v0.2.0
EgressAllow is one host+port pair a service's outbound traffic may reach when Egress.Mode is allowlist. Host is a bare hostname, resolved (and re-resolved on an interval, DNS records aren't static) by the egress sidecar rather than pinned to an IP at deploy time.
type EnvVar ¶
type EnvVar struct {
// Value is a literal value, set when the YAML node was a plain
// string scalar rather than a mapping.
Value string
// From references another resource's computed value, e.g.
// "postgres.main.url". Mutually exclusive with Value, Secret, and
// Vault.
From string
// Secret means the operator provides this at deploy time via
// the platform's own envelope-encrypted secret storage, never
// written to app.yaml or the git repo. Mutually exclusive with
// Vault: a given env var resolves its value from exactly one of
// the two secret sources, never both.
Secret bool
// Required, only meaningful alongside Secret: fail the deploy if no
// value has been provided, rather than starting the container with
// the variable unset.
Required bool
// Vault means this value is resolved live from an external
// HashiCorp Vault instance at container-create time
// (internal/reconcile/application's resolveEnv), never stored by
// this platform at all: the alternative to Secret above, not a
// variant of it. nil means not vault-backed.
Vault *VaultRef
}
EnvVar is one entry in a service's env block. app.yaml allows two shapes for the same field, per the app spec's own example, which shows both:
DATABASE_URL: { from: postgres.main.url }
API_KEY: { secret: true, required: true }
A plain string scalar is also accepted as shorthand for a literal value, since forcing every simple env var into object form would make the common case more verbose than the shape it's modeled on (Docker Compose's own env shorthand).
func (*EnvVar) UnmarshalYAML ¶
UnmarshalYAML implements the string-or-object union described on EnvVar, using yaml.v3's node-based Unmarshaler interface: the node's Kind tells a scalar apart from a mapping before deciding which shape to decode into, rather than trying one and falling back on error.
type ExecCommand ¶ added in v0.2.0
type ExecCommand []string
ExecCommand is health.*.exec: a list is run as argv, a string through /bin/sh -c.
func (*ExecCommand) UnmarshalJSON ¶ added in v0.2.0
func (e *ExecCommand) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts a shell string or an argv array.
func (*ExecCommand) UnmarshalYAML ¶ added in v0.2.0
func (e *ExecCommand) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML accepts a shell string or an argv list.
type GPU ¶ added in v0.2.0
type GPU struct {
Count int `yaml:"count,omitempty" json:"count,omitempty"`
Devices []string `yaml:"devices,omitempty" json:"devices,omitempty"`
}
GPU is resources.gpu: `all`, a count, or {count, devices} where devices are NVIDIA indexes or UUIDs (devices wins over count).
func (*GPU) UnmarshalJSON ¶ added in v0.2.0
UnmarshalJSON accepts "all", a number, or an object.
type HeaderRule ¶
type HeaderRule struct {
Path string `yaml:"path"`
Values map[string]string `yaml:"values,omitempty"`
Remove []string `yaml:"remove,omitempty"`
}
HeaderRule is one entry under a static service's headers:.
type Health ¶
type Health struct {
Readiness *Probe `yaml:"readiness,omitempty"`
Liveness *Probe `yaml:"liveness,omitempty"`
// ReadyTimeout overrides how long a fresh deploy waits for Readiness
// to pass before the deploy is marked ReadinessFailed (internal/
// reconcile/application.defaultReadyBudget, 60s, when unset). A slow
// cold start (a JVM app, a big migration, a slow external connection)
// can legitimately take longer than 60s without ever being unhealthy.
ReadyTimeout string `yaml:"readyTimeout,omitempty"`
}
Health holds a service's readiness and liveness probe configuration.
type Hooks ¶
type Hooks struct {
PreDeploy string `yaml:"preDeploy,omitempty"`
PostDeploy string `yaml:"postDeploy,omitempty"`
}
Hooks are the two deploy-lifecycle commands a service can declare. Both run via "sh -c" inside the newly created container (see internal/reconcile/application.Controller's own doc comment for the full timing and failure-handling contract): PreDeploy before the container's readiness probe and before any old container is retired, PostDeploy after the whole replica set has cut over. Either field may be set alone.
type Issue ¶ added in v0.2.0
type Issue struct {
Path string `json:"path"`
Line int `json:"line"`
Message string `json:"message"`
}
Issue is one problem found in an app.yaml document, located by dotted path and source line when known. Mirrors internal/pipeline.Issue's shape, the live-validation convention this codebase already follows for its own declarative YAML format.
type Job ¶ added in v0.2.0
type Job struct {
Name string `yaml:"name"`
Schedule string `yaml:"schedule"`
Command []string `yaml:"command"`
Timezone string `yaml:"timezone,omitempty"`
// Mode is "fresh" (default, a new container from the service's image
// and spec) or "exec" (inside the running container).
Mode string `yaml:"mode,omitempty"`
// Concurrency is "allow", "forbid" (default) or "replace".
Concurrency string `yaml:"concurrency,omitempty"`
Timeout string `yaml:"timeout,omitempty"`
Retries int `yaml:"retries,omitempty"`
RetryBackoff string `yaml:"retryBackoff,omitempty"`
CatchUp string `yaml:"catchUp,omitempty"`
CatchUpLookback string `yaml:"catchUpLookback,omitempty"`
CPU float64 `yaml:"cpu,omitempty"`
Memory string `yaml:"memory,omitempty"`
WarnAfter string `yaml:"warnAfter,omitempty"`
// Enabled defaults to true when omitted.
Enabled *bool `yaml:"enabled,omitempty"`
}
Job is one entry under a service's jobs:. Durations use Go syntax ("30s", "10m", "1h"); Memory the same "512Mi"/"1Gi" shape as resources.
type Probe ¶
type Probe struct {
Path string `yaml:"path,omitempty"`
Scheme string `yaml:"scheme,omitempty" json:"scheme,omitempty"`
Host string `yaml:"host,omitempty" json:"host,omitempty"`
TLSSkipVerify bool `yaml:"tls_skip_verify,omitempty" json:"tls_skip_verify,omitempty"`
FollowRedirects *bool `yaml:"follow_redirects,omitempty" json:"follow_redirects,omitempty"`
ExpectedStatus StatusCodes `yaml:"expected_status,omitempty" json:"expected_status,omitempty"`
Exec ExecCommand `yaml:"exec,omitempty" json:"exec,omitempty"`
Interval string `yaml:"interval,omitempty"`
Timeout string `yaml:"timeout,omitempty"`
Failures int `yaml:"failures,omitempty"`
}
Probe is a single health check: an HTTP(S) request to Path, or a command run inside the container when Exec is set.
type Resources ¶
type Resources struct {
Memory string `yaml:"memory,omitempty"`
CPU float64 `yaml:"cpu,omitempty"`
// SwapMemory caps memory plus swap combined, the same "512Mi"/"1Gi"
// shape as Memory (Docker's own MemorySwap semantics); only
// meaningful alongside Memory, see Validate.
SwapMemory string `yaml:"swapMemory,omitempty"`
// CPUSet pins the container to specific host CPUs, Docker's own
// cpuset-cpus format (e.g. "0-3" or "0,2").
CPUSet string `yaml:"cpuSet,omitempty"`
// GPU requests NVIDIA GPUs; the service is placed only on a node
// with a usable GPU. nil means no GPU.
GPU *GPU `yaml:"gpu,omitempty"`
}
Resources holds a service's resource limits.
type Route ¶
type Route struct {
From string `yaml:"from"`
To string `yaml:"to"`
Status int `yaml:"status,omitempty"`
Force bool `yaml:"force,omitempty"`
Query map[string]string `yaml:"query,omitempty"`
}
Route is one entry under a static service's routes:. Status defaults to 301.
type Service ¶
type Service struct {
Build Build `yaml:"build"`
// Image is shorthand for build: {type: image, image: ...}; Parse folds it
// into Build, so consumers only ever read Build.
Image string `yaml:"image,omitempty"`
Domains []string `yaml:"domains,omitempty"`
Port int `yaml:"port,omitempty"`
// HostPort pins the host-side port Docker binds Port to, 0 meaning
// auto-assign (the same zero-value-means-unset convention Port
// itself uses). Its storage home is store.DesiredService.HostPort
// (migrations/0056_service_host_port.sql).
HostPort int `yaml:"host_port,omitempty"`
// BindAddress picks which network interface Port (and HostPort, if
// pinned) binds to on the host: "private" (loopback only, the
// default when empty), "public" (every interface, an explicit opt-
// in), or a literal IP. See internal/bindaddr.Resolve for the exact
// rules; EffectiveBindAddress applies the default.
BindAddress string `yaml:"bind_address,omitempty"`
Health *Health `yaml:"health,omitempty"`
Resources *Resources `yaml:"resources,omitempty"`
Env map[string]EnvVar `yaml:"env,omitempty"`
Replicas int `yaml:"replicas,omitempty"`
Strategy string `yaml:"strategy,omitempty"`
// Labels are arbitrary operator-supplied Docker labels applied to the
// service's container at create time, an escape hatch for tooling
// this platform doesn't know about (a monitoring agent or log
// shipper that keys off container labels, a homegrown script, and
// so on). See ValidateLabels (labels.go) for what's rejected:
// notably, any key under ReservedLabelPrefix, kept open for this
// platform's own bookkeeping labels.
Labels map[string]string `yaml:"labels,omitempty"`
// Volumes are named Docker volumes or host-directory bind mounts this
// service's container mounts (see Volume's own doc comment for how
// the two are distinguished): named volumes were previously a
// database-only capability, bind mounts a compose-import-only one. A
// named volume's Name is a logical name scoped to this service, not a
// global Docker volume name (see internal/deploy's translation into
// store.ServiceVolume for the actual, platform-prefixed name); two
// services can each declare a volume named "data" without colliding.
Volumes []Volume `yaml:"volumes,omitempty"`
// Hooks are shell commands the reconciler runs inside this service's
// own container at defined points in a deploy (internal/reconcile/
// application's controller). Nil means neither is configured, the
// same "declarative, resolved before storing" shape Health/Resources
// already follow.
Hooks *Hooks `yaml:"hooks,omitempty"`
// Command overrides the image's own default CMD
// (store.DesiredService.Command), nil meaning the image's own
// default: the app.yaml equivalent of
// internal/compose.Service.Command, which this mirrors. A plain argv
// list, never shell-interpreted.
Command []string `yaml:"command,omitempty"`
// Egress restricts this service's outbound network traffic to an
// explicit host+port allowlist, enforced by
// internal/reconcile/application's egress sidecar. Nil (the default,
// and the only state every service had before this field existed)
// means today's open-egress behavior, unchanged: this is deliberately
// opt-in, not deny-by-default, since Levelrail runs arbitrary
// third-party apps for existing users rather than AX's own
// tightly-scoped agent sandboxes, and defaulting every existing app
// to deny-all egress on upgrade would be a breaking change.
Egress *Egress `yaml:"egress,omitempty"`
// LoadBalancer configures balancing across this service's replicas
// (internal/loadbalancer). Nil keeps the plain single-upstream route.
LoadBalancer *loadbalancer.Config `yaml:"loadbalancer,omitempty"`
// Budget caps this service's monthly egress and cost. Nil leaves any
// budget set from the dashboard or CLI alone.
Budget *Budget `yaml:"budget,omitempty"`
// Autoscale owns this service's replica count within min and max. Nil
// leaves any policy set from the dashboard or CLI alone.
Autoscale *Autoscale `yaml:"autoscale,omitempty"`
// Canary tunes staged canary releases (strategy: canary). Nil uses the
// APP_CANARY_* defaults.
Canary *Canary `yaml:"canary,omitempty"`
// Contexts overrides env and build settings for deploy-preview,
// branch-deploy or a "branch:<pattern>" context. Production is the
// service's own settings.
Contexts map[string]ContextConfig `yaml:"contexts,omitempty"`
// DependsOn names sibling services: keys (within this same Spec, or
// within the same compose file for a compose-expanded service, see
// internal/compose.ExpandBuildService) this service waits on before
// the reconciler starts its own containers: internal/reconcile/
// application.Controller does not create a container for this
// service until every named dependency has at least one running
// container. This matches real Docker Compose's own default
// depends_on: semantic (service_started, not service_healthy): a
// start-order guarantee, not a health-based wait. See Validate for
// the reference and cycle checks.
DependsOn []string `yaml:"dependsOn,omitempty"`
// Jobs are cron-scheduled commands run for this service, each in a
// fresh container of its image (or exec'd into the running one), kept
// in step with this list on every deploy. Nil leaves jobs created
// through the dashboard or CLI alone; an explicit empty list removes
// every spec-declared job.
Jobs []Job `yaml:"jobs,omitempty"`
// Routes, Headers and Static shape a build.type: static site: redirects
// and rewrites, response headers, and the SPA, clean URL and 404 options.
Routes []Route `yaml:"routes,omitempty"`
Headers []HeaderRule `yaml:"headers,omitempty"`
Static *StaticOptions `yaml:"static,omitempty"`
}
Service is one entry under services:.
func (*Service) EffectiveBindAddress ¶ added in v0.2.0
EffectiveBindAddress returns svc.BindAddress, or bindaddr.Default (private, loopback-only) if unset. Mirrors EffectiveStrategy/ EffectiveReplicas' own "resolve the default here, once" shape.
func (*Service) EffectiveReplicas ¶
EffectiveReplicas returns svc.Replicas, or DefaultReplicas if unset. Schema validation guarantees Replicas is never negative when set; zero means "not specified in app.yaml", not "zero replicas".
func (*Service) EffectiveStrategy ¶
EffectiveStrategy returns svc.Strategy, or the default (blue-green, since it's easier to get right than rolling with a single replica) if unset.
func (Service) StaticRoutingInput ¶
func (s Service) StaticRoutingInput() StaticInput
StaticRoutingInput converts the routing fields to the form the deploy compiles; the _redirects and _headers file texts are filled in by the caller.
type Spec ¶
type Spec struct {
Version int `yaml:"version"`
Services map[string]Service `yaml:"services"`
Databases map[string]Database `yaml:"databases,omitempty"`
}
Spec is a fully parsed, schema-valid app.yaml.
func Parse ¶
Parse validates raw app.yaml bytes against the embedded JSON Schema, then unmarshals into a Spec and runs the semantic checks the schema can't express. Both layers must pass for Parse to succeed; a caller never sees a Spec that's schema-valid but semantically broken, or vice versa.
func (*Spec) LoadBalancerFor ¶ added in v0.2.0
func (s *Spec) LoadBalancerFor(service string) (*loadbalancer.Config, error)
LoadBalancerFor returns a service's loadbalancer: block, or nil when the service has none.
func (*Spec) Validate ¶
Validate checks rules the JSON Schema can't express: values whose validity depends on another field, or on the rest of the document, not just their own shape. Parse always runs this after schema validation succeeds, callers building a Spec by hand (tests, future tooling) should call it too before trusting a Spec.
type StaticHeaderBlock ¶
type StaticHeaderBlock struct {
Source string `json:"source"`
Path string `json:"path"`
Set []StaticHeaderKV `json:"set,omitempty"`
Remove []string `json:"remove,omitempty"`
// contains filtered or unexported fields
}
StaticHeaderBlock applies Set and Remove to responses whose path matches Path.
func (*StaticHeaderBlock) Pattern ¶
func (b *StaticHeaderBlock) Pattern() *StaticPattern
Pattern returns the compiled Path matcher, nil until validated.
func (*StaticHeaderBlock) SetPattern ¶
func (b *StaticHeaderBlock) SetPattern(p *StaticPattern)
SetPattern stores the matcher compiled for Path.
type StaticHeaderKV ¶
StaticHeaderKV is one response header.
type StaticInput ¶
type StaticInput struct {
Redirects string
Headers string
// Routes and HeaderBlocks come from app.yaml and rank before the files.
Routes []StaticRule
HeaderBlocks []StaticHeaderBlock
Options StaticServeOptions
}
StaticInput is what a deploy hands to the static routes compiler.
type StaticLimits ¶
StaticLimits caps the size of a site's routing rules.
func DefaultStaticLimits ¶
func DefaultStaticLimits() StaticLimits
DefaultStaticLimits are the caps used when no environment override is set.
type StaticOptions ¶
type StaticOptions struct {
SPA bool `yaml:"spa,omitempty"`
CleanURLs bool `yaml:"cleanUrls,omitempty"`
TrailingSlash string `yaml:"trailingSlash,omitempty"`
NotFound string `yaml:"notFound,omitempty"`
}
StaticOptions is a static service's static: block.
type StaticPattern ¶
type StaticPattern struct {
// contains filtered or unexported fields
}
StaticPattern is a path pattern with :name segments and a trailing * splat, compiled to an RE2 expression so matching is linear in the path length.
func CompileStaticPattern ¶
func CompileStaticPattern(p string) (*StaticPattern, error)
CompileStaticPattern compiles a path pattern.
func ValidateStaticHeaderPath ¶
func ValidateStaticHeaderPath(p string, l StaticLimits) (*StaticPattern, error)
ValidateStaticHeaderPath checks a header block path and compiles it.
type StaticProblem ¶
type StaticProblem struct {
Source string `json:"source"`
Line int `json:"line,omitempty"`
Severity string `json:"severity"`
Message string `json:"message"`
}
StaticProblem is one validation finding. Line is 0 for spec rules.
type StaticQueryMatch ¶
StaticQueryMatch requires a query parameter; Value is a literal or ":name".
type StaticRule ¶
type StaticRule struct {
Source string `json:"source"`
From string `json:"from"`
To string `json:"to"`
Status int `json:"status"`
Force bool `json:"force,omitempty"`
Query []StaticQueryMatch `json:"query,omitempty"`
// contains filtered or unexported fields
}
StaticRule is one redirect, rewrite or status rule.
func (*StaticRule) Pattern ¶
func (r *StaticRule) Pattern() *StaticPattern
Pattern returns the compiled From matcher, nil until validated.
type StaticRuleset ¶
type StaticRuleset struct {
Version int `json:"version"`
Rules []StaticRule `json:"rules"`
Headers []StaticHeaderBlock `json:"headers"`
Options StaticServeOptions `json:"options"`
Problems []StaticProblem `json:"problems"`
}
StaticRuleset is everything compiled for one deploy.
func CompileStatic ¶
func CompileStatic(in StaticInput, l StaticLimits) (*StaticRuleset, error)
CompileStatic validates every source and returns the ruleset. Invalid file lines become problems and are skipped; an invalid app.yaml entry, a bad option or a cap breach is an error, so the deploy fails before anything is served.
func LoadStaticSidecar ¶
func LoadStaticSidecar(rootDir string) (*StaticRuleset, error)
LoadStaticSidecar reads the ruleset of the tree at rootDir. A tree published before routing rules existed has none and yields nil without an error.
func (*StaticRuleset) Prepare ¶
func (rs *StaticRuleset) Prepare() error
Prepare compiles the matchers of a ruleset read back from JSON.
type StaticServeOptions ¶
type StaticServeOptions struct {
SPA bool `json:"spa,omitempty"`
CleanURLs bool `json:"clean_urls,omitempty"`
TrailingSlash string `json:"trailing_slash,omitempty"`
NotFound string `json:"not_found,omitempty"`
}
StaticServeOptions are the static serving switches.
type StatusCodes ¶ added in v0.2.0
type StatusCodes string
StatusCodes is health.*.expected_status: "200-399", 204, or a list mixing codes and ranges. It is kept in its normalized string form.
func (*StatusCodes) UnmarshalJSON ¶ added in v0.2.0
func (s *StatusCodes) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts a string, a number, or an array of either.
func (*StatusCodes) UnmarshalYAML ¶ added in v0.2.0
func (s *StatusCodes) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML accepts a scalar or a sequence of scalars.
type VaultRef ¶ added in v0.2.0
VaultRef is app.yaml's { vault: { path, key } } env var shape: Path is the Vault KV secret's path, Key is the field name within that secret's data. Both required together, mirroring internal/store's own VaultEnvRef which this parses into (see internal/deploy's vaultEnvRefs).
type Volume ¶
type Volume struct {
Name string `yaml:"name,omitempty"`
HostPath string `yaml:"hostPath,omitempty"`
Path string `yaml:"path"`
ReadOnly bool `yaml:"readOnly,omitempty"`
}
Volume is one entry under a service's volumes:. Exactly one of Name (a named Docker volume) or HostPath (a bind mount of a real host directory, gated the same as internal/compose's own bind-mount support: see validateBindMountHostPath) is set; see Validate. ReadOnly is only meaningful alongside HostPath, matching store.ServiceBindMount.