iac

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 14, 2026 License: Apache-2.0 Imports: 39 Imported by: 0

Documentation

Overview

Package iac turns multi-document YAML resource files into a plan of changes against a control plane and applies it through the ordinary REST API, so per-resource authorization applies to every item.

Index

Constants

View Source
const (
	StatusApplied = "applied"
	StatusFailed  = "failed"
	StatusDenied  = "denied"
	StatusSkipped = "skipped"
	StatusNoop    = "noop"
)

Item outcomes.

View Source
const (
	// CodeItemFailed is the fallback for a failure with no more specific code.
	CodeItemFailed = "item_failed"
	// CodePermissionDenied means the token may not read or write the resource.
	CodePermissionDenied = "permission_denied"
	// CodeUnavailable means the control plane does not serve this resource.
	CodeUnavailable = "unavailable"
	// CodeReadFailed means the live state could not be read.
	CodeReadFailed = "read_failed"
	// CodeProtectedResource means the resource is protected from this change.
	CodeProtectedResource = "protected_resource"
	// CodeImmutableField means a declared field cannot change in place.
	CodeImmutableField = "immutable_field"
	// CodeSyncedExternally means the resource is owned by an outside source.
	CodeSyncedExternally = "synced_externally"
)

Plan item codes, set on Change.Code when an item cannot be planned.

View Source
const (
	CodeRefProjectMissing     = "ref_project_missing"
	CodeRefEnvironmentMissing = "ref_environment_missing"
	CodeRefAppMissing         = "ref_app_missing"
	CodeRefSecretMissing      = "ref_secret_missing"
)

Reference codes, for a declared resource that points at one that is absent.

View Source
const (
	CodeSchemaUnknownField       = "schema_unknown_field"
	CodeSchemaVersionUnsupported = "schema_version_unsupported"
	CodeOwnershipConflict        = "ownership_conflict"
	CodePlanStale                = "plan_stale"
	CodePlanHashVersion          = "plan_hash_version"
	CodeStackLocked              = "stack_locked"
	CodeStackSourceSync          = "stack_source_sync"
	CodePruneGuard               = "prune_guard"
	CodeApplyDisabled            = "apply_disabled"
	CodeInvalidRequest           = "invalid_request"
	CodeInvalidDocument          = "invalid_document"
	// CodeInvalidName means a resource name breaks the rule the API enforces at creation.
	CodeInvalidName = "invalid_name"
)

Document and request codes, set outside planning (schema, stacks, apply).

View Source
const (
	// CodeComposePathInvalid means spec.compose.path is not a clean relative path.
	CodeComposePathInvalid = "compose_path_invalid"
	// CodeComposeRefMissing means a referenced compose file was not sent with the request.
	CodeComposeRefMissing = "compose_ref_missing"
	// CodeComposeTooLarge means a compose file or the attachment total is over its cap.
	CodeComposeTooLarge = "compose_too_large"
	// CodeComposeTooManyFiles means the request holds more compose files than the cap.
	CodeComposeTooManyFiles = "compose_too_many_files"
	// CodeComposeModeConflict means an App sets both spec.compose and spec.service.
	CodeComposeModeConflict = "compose_mode_conflict"
	// CodeComposeUnsupportedKey means a compose file uses a key strict mode refuses, such as build or env_file.
	CodeComposeUnsupportedKey = "compose_unsupported_key"
	// CodeComposeRejected means a compose file breaks a strict guard rule, such as privileged or a host bind.
	CodeComposeRejected = "compose_rejected"
	// CodeComposeSecretLiteral means a compose file holds what looks like a literal secret.
	CodeComposeSecretLiteral = "compose_secret_literal"
	// CodeComposeVarMissing means a compose ${VAR} is not declared in spec.compose.env.
	CodeComposeVarMissing = "compose_var_missing"
	// CodeComposeNoDeployUnsupported means no_deploy was asked for a compose App, which deploys on save.
	CodeComposeNoDeployUnsupported = "compose_no_deploy_unsupported"
	// CodeComposeApplyFailed means a step after a compose deploy failed; a first deploy is removed again.
	CodeComposeApplyFailed = "compose_apply_failed"
)

Compose reference codes (ADR 081).

View Source
const (
	// CodeApprovalPending means a cloud plan would replace the saved engine plan of a pending approval.
	CodeApprovalPending = "approval_pending"
	// CodePlanSuperseded means a later plan replaced the saved engine plan an approval named.
	CodePlanSuperseded = "plan_superseded"
	// CodeInterrupted means the control plane stopped while the plan job ran.
	CodeInterrupted = "interrupted"
	// CodePlanJobNotFound means no plan job has this id for the stack, or it was pruned.
	CodePlanJobNotFound = "plan_job_not_found"
	// CodePlanTimeout means the plan job ran past cloud.engine_timeout_seconds.
	CodePlanTimeout = "plan_timeout"
)

Async cloud plan job and approval race codes.

View Source
const (
	// CodeSyncRepoUnreachable means the sync repository could not be fetched.
	CodeSyncRepoUnreachable = "sync_repo_unreachable"
	// CodeSyncPathInvalid means the sync path leaves the checkout or holds a symlink.
	CodeSyncPathInvalid = "sync_path_invalid"
	// CodeSyncLimitsExceeded means the repository or its files passed a sync limit.
	CodeSyncLimitsExceeded = "sync_limits_exceeded"
	// CodeSyncPlanFailed means the synced documents could not be planned.
	CodeSyncPlanFailed = "sync_plan_failed"
	// CodeSyncSecretInRepo means a synced document holds a literal secret value.
	CodeSyncSecretInRepo = "sync_secret_in_repo"
	// CodeSyncDiskLow means free disk was below stacks.sync_min_free_mb, so nothing was fetched.
	CodeSyncDiskLow = "sync_disk_low"
	// CodeSyncApplyFailed means a sync apply was refused or failed; the stack is unchanged.
	CodeSyncApplyFailed = "sync_apply_failed"
)

Git sync codes. They never carry repository content or credentials.

View Source
const (
	OpAdd    = "add"
	OpChange = "change"
	OpRemove = "remove"
	OpKeep   = "keep"
)

Field operations shown in a plan.

View Source
const (
	CodeApprovalRequired    = "approval_required"
	CodeDestroyNotConfirmed = "destroy_not_confirmed"
	CodeCloudPlanExpired    = "cloud_plan_expired"
	CodeStateDiverged       = "state_diverged"
)

Frozen codes of the cloud apply path.

View Source
const (
	StabilityStable = "stable"
	StabilityBeta   = "beta"
)

Kind stability levels published in the schema as x-stability.

View Source
const DefaultDriftPolicy = DriftWarn

DefaultDriftPolicy is warn so hand edits keep working until an owner opts in.

View Source
const HashV2Prefix = "v2:"

HashV2Prefix marks a plan hash computed by planHashV2.

View Source
const (
	LabelManagedBy = "managed-by"
)

Labels with a meaning of their own.

View Source
const MaxSyncIntervalSeconds = 86400

MaxSyncIntervalSeconds bounds a per stack interval to one day.

View Source
const MinSecretMatchLen = 4

MinSecretMatchLen is the shortest secret value matched by substring. Shorter values would match ordinary document text; plans warn about them instead.

View Source
const NodeTaintWeakenReason = "weakens a protected taint; allowed by allow_weaken"

NodeTaintWeakenReason starts the reason of a Node change that drops or loosens a protected taint under allow_weaken, so reviewers can find it.

View Source
const SchemaVersion = 1

SchemaVersion is the only resource document version this package accepts.

View Source
const SettingWeakenReason = "weakens a security protection; allowed by allow_weaken"

SettingWeakenReason starts the reason of every Setting change that weakens a protection under allow_weaken, so reviewers and the audit can find it.

View Source
const ShortSecretWarning = "secret value too short to be checked against the documents"

ShortSecretWarning is the plan warning for values under MinSecretMatchLen.

Variables

View Source
var (
	ErrStackPinned     = errors.New("stack is pinned; only a rollback to the pinned revision may apply")
	ErrStackLocked     = errors.New("stack is locked by another apply")
	ErrLedgerConflict  = errors.New("resource is owned by another stack")
	ErrPruneNotAllowed = errors.New("prune needs both the manifest request and the stack allow_prune setting")
)

Sentinel errors the store and API map to 409 or 422 responses.

AllCodes lists every frozen code, for docs and guard tests.

View Source
var ErrCloudUnavailable = errors.New("cloud provisioning is not configured")

ErrCloudUnavailable marks a planner error caused by missing configuration (a stored connection, an engine setting, a binary source); the item fails as unavailable.

View Source
var ErrPlanChanged = errors.New("the plan changed since it was reviewed; plan again")

ErrPlanChanged means live state moved between the plan a user reviewed and the apply that followed.

View Source
var ErrPlanHashVersion = errors.New(CodePlanHashVersion + ": the plan hash is from an older release; plan again")

ErrPlanHashVersion means the submitted hash has the old unkeyed shape and the install no longer accepts it; plan again to get a current hash.

Kinds lists every kind in the order changes are applied.

Functions

func Build

func Build(docs []*Document, opts Options) ([]*Resource, []Issue)

Build validates every document (schema first, then semantics) and turns them into canonical resources. It returns no resources when any issue exists, so nothing is planned from a partly valid input.

func CheckHashVersion

func CheckHashVersion(expected string) error

CheckHashVersion refuses a v1 shaped hash while the setting disallows it. It needs no plan, so a caller can run it before any short circuit.

func CheckPlanHash

func CheckPlanHash(expected string, plan Plan) error

CheckPlanHash compares a submitted hash with the recomputed plan. Empty means no pin. A v1 shaped hash passes only while the setting allows it.

func CleanComposePath

func CleanComposePath(p string) (string, error)

CleanComposePath validates a compose path and returns its cleaned form. It never touches the disk; callers refuse symlinks where they read files.

func CleanSyncPath

func CleanSyncPath(p string) (string, error)

CleanSyncPath returns the slash path under the repository root; "" is the root.

func CloudPlannerInstalled

func CloudPlannerInstalled() bool

CloudPlannerInstalled reports whether a planner and a policy checker are both set.

func ComposeCreated

func ComposeCreated(res []*Resource, out ApplyResult) []string

ComposeCreated lists the compose Apps an apply deployed for the first time, so a caller that cannot record their ownership can remove them.

func ComposeEnvDigests

func ComposeEnvDigests(files []Source, project string) map[string]string

ComposeEnvDigests returns the env digest of every compose App the files declare, so a stack can record what its last applied revision set.

func ComposeRefPaths

func ComposeRefPaths(files []Source) []string

ComposeRefPaths lists the cleaned compose paths the documents reference.

func ComposeRefsEnabled

func ComposeRefsEnabled() bool

ComposeRefsEnabled reports the live iac.compose_refs gate.

func IsCloudKind

func IsCloudKind(k Kind) bool

IsCloudKind reports whether a kind is provisioned through the cloud engine.

func IsDenied

func IsDenied(err error) bool

IsDenied reports whether err is an authorization failure.

func IsFeatureOff

func IsFeatureOff(err error) bool

IsFeatureOff reports whether err is the 404 a gated endpoint answers while its beta feature is disabled.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err is a 404.

func IsSettingWeaken

func IsSettingWeaken(c Change) bool

IsSettingWeaken reports whether c is a Setting change that weakens a protection.

func IsUnavailable

func IsUnavailable(err error) bool

IsUnavailable reports whether err says the feature is not configured.

func KindStability

func KindStability(k Kind) string

KindStability returns the declared stability of a kind; an unlisted kind is stable.

func LeakedSecret

func LeakedSecret(docs []byte, secrets map[string]string) (string, bool)

LeakedSecret returns the name of a secret whose value appears in docs, so a revision can be refused before it is stored. Values under MinSecretMatchLen are skipped.

func LedgerConflicts

func LedgerConflicts(p Plan) []string

LedgerConflicts returns the keys a plan refused for ownership reasons.

func LooksSecret

func LooksSecret(key, value string) bool

LooksSecret is the literal detector resolveLiteral applies to spec env, exported so compose attachment scans share it instead of copying patterns.

func MassPruneBlocked

func MassPruneBlocked(p Plan) bool

MassPruneBlocked reports whether the guard held back any delete.

func ParseDocuments

func ParseDocuments(sources []Source) ([]*Document, []Issue)

ParseDocuments splits sources into documents and decodes each envelope. It reports YAML syntax errors and missing kind or name, nothing deeper; Validate does the schema and semantic checks.

func PlanHashV1

func PlanHashV1(p Plan) string

PlanHashV1 recomputes the v1 hash of a plan's changes.

func PlanValueMax

func PlanValueMax() int

PlanValueMax is the longest before or after value a plan shows.

func RemoveCompose

func RemoveCompose(ctx context.Context, d Doer, name string) error

RemoveCompose tears a compose App down through its delete path. It never asks for volumes=true, so named volumes are kept.

func SchemaJSON

func SchemaJSON() ([]byte, error)

SchemaJSON returns the published JSON Schema: the resource envelope schema with the app.yaml schema definitions merged in unchanged, except that an env value may also be a { secretRef } or a { from: { database, field } } object.

func SetCloudPlanner

func SetCloudPlanner(p CloudPlanner)

SetCloudPlanner installs the planner; nil restores the unconfigured failure.

func SetCloudPolicy

func SetCloudPolicy(c PolicyChecker)

SetCloudPolicy installs the policy checker; nil disables the extra checks.

func SetComposeSource

func SetComposeSource(fn func() ComposeLimits)

SetComposeSource makes the gate and caps follow runtime settings.

func SetPlanHashKey

func SetPlanHashKey(key []byte)

SetPlanHashKey sets the HMAC key for plan hashes; the caller derives it from the server master key so hashes are per install.

func SetPlanValueSource

func SetPlanValueSource(fn func() int)

SetPlanValueSource makes PlanValueMax follow a live setting.

func SetPruneGuardSource

func SetPruneGuardSource(fn func() PruneGuard)

SetPruneGuardSource makes new ledgers follow a live setting.

func SetV1HashAcceptedSource

func SetV1HashAcceptedSource(fn func() bool)

SetV1HashAcceptedSource follows the iac.v1_hash_accepted setting live; unset means v1 hashes are refused.

func SplitComposeRefs

func SplitComposeRefs(files []Source) (docs []Source, attachments map[string][]byte, issues []Issue)

SplitComposeRefs separates compose files referenced by App documents from resource documents, so a compose file in a stack folder is never parsed as a document. Source names are paths relative to the document set root.

func StrictComposeCheck

func StrictComposeCheck(data []byte, file string, declared map[string]string) (*compose.File, error)

StrictComposeCheck runs the strict parser, the secret literal scan and the declared variable check; errors carry codes, names and lines, never values.

func ValidateStackLevel

func ValidateStackLevel(scope StackLevel, project, environment string) error

ValidateStackLevel checks the project and environment fit the scope.

func ValidateStackName

func ValidateStackName(name string) error

ValidateStackName checks the stack name shape.

func WithAllowWeaken

func WithAllowWeaken(ctx context.Context) context.Context

WithAllowWeaken lets the Setting plan confirm a weakening change. Only the API sets it, after it has checked the caller is a human root session.

func WithCloudReport

func WithCloudReport(ctx context.Context, rep *CloudReport) context.Context

WithCloudReport makes an apply record its engine runs into rep.

func WithLedger

func WithLedger(ctx context.Context, l *Ledger) context.Context

WithLedger makes Load and the planner enforce l. It is a server-side value; nothing a client sends can set it.

func WithStackScope

func WithStackScope(ctx context.Context, s StackScope) context.Context

WithStackScope returns ctx carrying the stack apply scope.

Types

type Action

type Action string

Action is what a plan item does.

const (
	ActionCreate Action = "create"
	ActionUpdate Action = "update"
	ActionDelete Action = "delete"
	ActionNoop   Action = "noop"
	ActionError  Action = "error"
)

Plan actions.

type ApplyResult

type ApplyResult struct {
	Plan    Plan         `json:"plan"`
	Results []ItemResult `json:"results"`
	Applied int          `json:"applied"`
	Failed  int          `json:"failed"`
	Skipped int          `json:"skipped"`
}

ApplyResult is the outcome of an apply.

func Apply

func Apply(ctx context.Context, d Doer, res []*Resource, opts Options, expectedHash string) (ApplyResult, error)

Apply plans and executes resources through doer. Validation and reading happen before any write; a plan holding an error item applies nothing unless ContinueOnError is set. Items run in dependency order and each goes through the ordinary endpoint, so a denied write is reported on its own item and the rest proceed only when ContinueOnError is set.

func (ApplyResult) OK

func (r ApplyResult) OK() bool

OK reports whether every item that had to change did.

type Change

type Change struct {
	Kind     Kind          `json:"kind"`
	Name     string        `json:"name"`
	Scope    string        `json:"scope,omitempty"`
	Action   Action        `json:"action"`
	Fields   []FieldChange `json:"fields,omitempty"`
	Kept     []FieldChange `json:"kept,omitempty"`
	Warnings []string      `json:"warnings,omitempty"`
	// Services are display only compose preview rows; the plan hash never reads them.
	Services []ComposeServiceRow `json:"services,omitempty"`
	Reason   string              `json:"reason,omitempty"`
	// Code is the frozen machine code of a failed item; empty otherwise.
	Code string `json:"code,omitempty"`
	// DependsOn lists the resources this item reads from, applied first.
	DependsOn []string `json:"dependsOn,omitempty"`
	Denied    bool     `json:"denied,omitempty"`
	// LiveID is the live identifier a ledger records; it never leaves the process.
	LiveID string `json:"-"`
	File   string `json:"file,omitempty"`
	Line   int    `json:"line,omitempty"`
	// CloudPlanHash names the sealed engine plan a cloud item runs; approval binds to it.
	CloudPlanHash string `json:"cloudPlanHash,omitempty"`
	// ComposeDigest covers a compose App's file bytes and inputs; it never leaves the process.
	ComposeDigest string `json:"-"`
}

Change is one line of a plan.

func (Change) Key

func (c Change) Key() string

Key is the change's stable identity.

type CloudApplier

type CloudApplier interface {
	ApplyCloud(ctx context.Context, req CloudApplyRequest) (CloudApplyResult, error)
}

CloudApplier runs a saved engine plan; a planner with an engine implements it.

type CloudApplyRequest

type CloudApplyRequest struct {
	Stack    string
	Target   CloudResource
	Group    []CloudResource
	PlanHash string
	Destroy  bool
}

CloudApplyRequest asks the engine to run one approved saved plan of a stack.

type CloudApplyResult

type CloudApplyResult struct {
	Diverged     bool
	QuarantineID int64
}

CloudApplyResult reports a state the engine could not store; it never carries state.

type CloudApproval

type CloudApproval struct {
	Pins           map[string]Change
	ConfirmDestroy bool
}

CloudApproval is the approved plan an apply runs. Pins map an item key to the approved change, so apply never re-plans and so never replaces the saved engine plan.

type CloudAttr

type CloudAttr struct {
	Path      string
	Old, New  string
	Sensitive bool
}

CloudAttr is one attribute of a proposed cloud change.

type CloudChange

type CloudChange struct {
	Address string
	Type    string
	Action  string
}

CloudChange is one entry handed to a PolicyChecker.

type CloudDiff

type CloudDiff struct {
	Action string
	// Type is the provider resource type, used by policy checks only.
	Type  string
	Attrs []CloudAttr
	// PlanHash is the sealed engine plan this diff came from.
	PlanHash string
}

CloudDiff is a planner answer: action is create, update, delete or noop.

type CloudOutcome

type CloudOutcome struct {
	Diff CloudDiff
	Err  error
}

CloudOutcome is the answer for one document of a stack plan.

type CloudPlanner

type CloudPlanner interface {
	PlanCloud(ctx context.Context, r CloudResource) (CloudDiff, error)
}

CloudPlanner computes the proposed change for a cloud document. The engine implements it; with none set every cloud item fails loudly.

type CloudReadiness

type CloudReadiness interface {
	CloudReady() bool
}

CloudReadiness is optional on a planner: false means it has no engine yet, so even record-only kinds such as CloudAccount must not report unchanged.

type CloudRefusal

type CloudRefusal struct {
	Code    string
	Address string
	Message string
}

CloudRefusal is a policy verdict with a frozen code. An empty Address applies to every delete item (the destroy cap).

type CloudReport

type CloudReport struct {
	// contains filtered or unexported fields
}

CloudReport collects the engine runs of one apply.

func (*CloudReport) Runs

func (r *CloudReport) Runs() []CloudRun

Runs returns a copy of the recorded runs.

type CloudResource

type CloudResource struct {
	Kind Kind
	Name string
	Spec map[string]any
}

CloudResource is what a planner is asked about, taken from a document.

type CloudRun

type CloudRun struct {
	PlanHash     string
	Items        []string
	Diverged     bool
	QuarantineID int64
	Failed       bool
}

CloudRun is one engine run of an apply, for events: names and ids only.

type CloudStackPlanner

type CloudStackPlanner interface {
	PlanCloudStack(ctx context.Context, stack string, rs []CloudResource) []CloudOutcome
}

CloudStackPlanner plans every cloud document of one stack together, so documents that refer to each other land in one module. The outcomes line up with the input order.

type ComposeBody

type ComposeBody struct {
	Data []byte
	Env  map[string]string
}

ComposeBody is sent as the raw compose file instead of JSON. Env feeds ${VAR} interpolation on the server and never reaches the stored revision.

type ComposeLimits

type ComposeLimits struct {
	Enabled       bool
	MaxFileBytes  int
	MaxFiles      int
	MaxTotalBytes int
}

ComposeLimits are the live gate and caps for compose references.

type ComposeServiceRow

type ComposeServiceRow struct {
	Service string `json:"service"`
	Action  Action `json:"action"`
	Image   string `json:"image,omitempty"`
}

ComposeServiceRow is one display only preview row of a compose App.

type CoverageRow

type CoverageRow struct {
	Kind    Kind
	Feature string
	Status  CoverageStatus
	// Code is the frozen machine code of the rejection, empty when the
	// rejection is a plain builder message identified by Reason.
	Code string
	// Reason is the rejection text a user sees (a substring of it).
	Reason string
	Probe  string
}

CoverageRow is one feature of a kind. A not-yet row names the rejection a user sees when they try it; Probe is a minimal document that triggers it.

func CoverageMatrix

func CoverageMatrix() []CoverageRow

CoverageMatrix returns a copy of the matrix, ordered by kind then row.

type CoverageStatus

type CoverageStatus string

CoverageStatus tells whether a product feature can be written in files.

const (
	CoverageInFiles CoverageStatus = "in_files"
	CoverageNotYet  CoverageStatus = "not_in_files_yet"
)

Coverage statuses of a matrix row.

type Document

type Document struct {
	File     string
	Line     int
	Kind     Kind
	Name     string
	Labels   map[string]string
	Root     *yaml.Node
	SpecNode *yaml.Node
}

Document is one parsed YAML document with its envelope decoded.

func FilterProject

func FilterProject(docs []*Document, project string) []*Document

FilterProject keeps the documents that belong to one project: the project itself, its environments, apps and databases, the domains, load balancers, pipelines and alert rules of those apps, and the tags those apps use.

type Doer

type Doer interface {
	Do(ctx context.Context, method, path string, body, out any) error
}

Doer performs one JSON request against the control plane REST API. The CLI implements it over HTTP with the caller's token; the server implements it in process with the caller's own credentials, so per resource authorization applies to every call either way.

type DriftPolicy

type DriftPolicy string

DriftPolicy says what happens when live state diverges from the stack.

const (
	DriftWarn DriftPolicy = "warn"
	DriftLock DriftPolicy = "lock"
)

Drift policies; revert is deliberately not offered.

func ParseDriftPolicy

func ParseDriftPolicy(s string) (DriftPolicy, error)

ParseDriftPolicy maps an empty value to the default and rejects unknown ones.

type ExportFile

type ExportFile struct {
	Name    string `json:"name"`
	Kind    Kind   `json:"kind"`
	Content string `json:"content"`
}

ExportFile is one exported document.

type ExportOptions

type ExportOptions struct {
	Project string
	App     string
	// Database and Node each export that one resource alone.
	Database string
	Node     string
	// Settings exports only the saved, non-sensitive runtime settings.
	Settings bool
	// IncludeEnvValues writes literal env values; when false every value
	// becomes a ${{ env.NAME }} placeholder.
	IncludeEnvValues bool
}

ExportOptions select what to export.

type ExportResult

type ExportResult struct {
	Files    []ExportFile `json:"files"`
	Warnings []string     `json:"warnings,omitempty"`
}

ExportResult is the exported documents in stable order plus warnings for live settings the documents cannot express.

func Export

func Export(ctx context.Context, d Doer, opts ExportOptions) (ExportResult, error)

Export reads live state and renders it as documents. Output is stable: no timestamps or generated ids, sorted keys and a fixed document order.

func (ExportResult) Join

func (r ExportResult) Join() string

Join concatenates exported files into one multi-document stream.

type FieldChange

type FieldChange struct {
	Path string `json:"path"`
	Op   string `json:"op"`
	Old  string `json:"old,omitempty"`
	New  string `json:"new,omitempty"`
}

FieldChange is one leaf difference. Values under env are never shown.

type GitMeta

type GitMeta struct {
	Repo     string `json:"git_repo,omitempty"`
	Ref      string `json:"git_ref,omitempty"`
	SHA      string `json:"git_sha,omitempty"`
	CIRunURL string `json:"ci_run_url,omitempty"`
}

GitMeta links a revision to the commit and CI run that produced it.

type Issue

type Issue struct {
	File    string `json:"file,omitempty"`
	Path    string `json:"path,omitempty"`
	Line    int    `json:"line"`
	Message string `json:"message"`
	// Code is a frozen machine code when the issue has one.
	Code string `json:"code,omitempty"`
}

Issue is one validation problem, located by file, line and dotted path.

func ComposeStrictIssues

func ComposeStrictIssues(file string, err error) []Issue

ComposeStrictIssues turns a strict check error into coded issues. An error that is not a strict refusal gets a fixed message, since parser text can quote content.

func SyncSecretIssue

func SyncSecretIssue(issues []Issue) (Issue, bool)

SyncSecretIssue returns the first issue that refused a literal secret, so a sync can report its file and line without the value.

func (Issue) String

func (i Issue) String() string

type ItemResult

type ItemResult struct {
	Change
	Status string `json:"status"`
	Error  string `json:"error,omitempty"`
}

ItemResult is one plan item's outcome.

type Kind

type Kind string

Kind names a resource document type.

const (
	KindProject       Kind = "Project"
	KindEnvironment   Kind = "Environment"
	KindTag           Kind = "Tag"
	KindCloudAccount  Kind = "CloudAccount"
	KindCloudNetwork  Kind = "CloudNetwork"
	KindCloudInstance Kind = "CloudInstance"
	KindCloudBucket   Kind = "CloudBucket"
	KindNode          Kind = "Node"
	KindSetting       Kind = "Setting"
	KindDatabase      Kind = "Database"
	KindApp           Kind = "App"
	KindDomain        Kind = "Domain"
	KindLoadBalancer  Kind = "LoadBalancer"
	KindPipeline      Kind = "Pipeline"
	KindAlertRule     Kind = "AlertRule"
)

Resource kinds, in dependency order.

const KindStack Kind = "Stack"

KindStack is the optional stack document. It is not in Kinds because it configures the apply instead of describing a resource.

type Ledger

type Ledger struct {
	StackID   string
	StackName string
	// Scope is the stack scope; empty means project.
	Scope StackLevel
	// ScopeProject is the stack's own project; an admin bound it to the stack,
	// so declaring it claims it without an explicit adopt.
	ScopeProject string
	// Owned maps resource keys this stack owns to their entries.
	Owned map[string]LedgerEntry
	// Foreign maps keys owned by some other stack to that stack's id.
	Foreign map[string]string
	// Adopt lets the stack claim existing resources no stack owns.
	Adopt bool
	// AllowMassPrune confirms a prune above the guard thresholds.
	AllowMassPrune bool
	Guard          PruneGuard
}

Ledger is the stack ownership view a plan is checked against (ADR 042). With a ledger, ownership and prune come from it alone, never from tags.

func NewInstallLedger

func NewInstallLedger(stackID, stackName string, owned []LedgerEntry) *Ledger

NewInstallLedger builds the view for an install stack, which may own only Node and Setting documents and has no project to claim.

func NewLedger

func NewLedger(stackID, stackName, scopeProject string, owned []LedgerEntry) *Ledger

NewLedger builds the view for one stack from its own ledger rows.

type LedgerEntry

type LedgerEntry struct {
	StackID       string `json:"stack_id"`
	Kind          Kind   `json:"kind"`
	Key           string `json:"key"`
	LiveID        string `json:"live_id"`
	SinceRevision int    `json:"since_revision"`
}

LedgerEntry records the single stack that owns a resource. Key is the resourceKey (kind/scope/name), unique per kind across all stacks.

type LedgerRef

type LedgerRef struct {
	Kind Kind
	Key  string
}

LedgerRef is one (kind, key) whose owner a caller must look up.

func LedgerRefs

func LedgerRefs(res []*Resource) []LedgerRef

LedgerRefs lists every key the plan checks ownership of: each resource plus the parent app of Domain, LoadBalancer, Pipeline and AlertRule documents.

type LedgerUpdate

type LedgerUpdate struct {
	Claim   []LedgerEntry
	Release []LedgerEntry
}

LedgerUpdate is what one apply changes in the ledger.

func LedgerChanges

func LedgerChanges(stackID string, rev int, res []*Resource, out ApplyResult) LedgerUpdate

LedgerChanges derives claims and releases from an apply. Only declared resources are claimed, and nothing is claimed from a plan refused for errors.

type Options

type Options struct {
	// Vars resolve ${{ env.NAME }} placeholders.
	Vars map[string]string
	// Source names this apply source; apps it creates carry a managed-by
	// tag and only those are ever pruned.
	Source string
	// Secrets maps "NAME" or "app/NAME" to a value to store at apply time.
	// Values never appear in a plan, result or export.
	Secrets map[string]string
	// Project limits prune to managed apps of this project, matching the
	// project filter applied to the files.
	Project string
	// BelowRoot is set by a caller whose token is under the root tier; job
	// writes then fail the plan instead of stopping partway.
	BelowRoot bool
	// Prune deletes managed resources absent from the files.
	Prune bool
	// NoDeploy skips the restart that applies an env change to a running app.
	NoDeploy bool
	// ContinueOnError keeps applying after an item fails.
	ContinueOnError bool
	// Cloud is the approved cloud plan; without it every cloud write is refused.
	Cloud *CloudApproval
	// Attachments holds referenced compose files by cleaned relative path.
	Attachments map[string][]byte
	// ComposeEnvApplied maps a compose App to the env digest its stack last
	// applied, since a compose revision never stores env values.
	ComposeEnvApplied map[string]string
	// ConfirmDestroy is set server side when the caller typed the stack name;
	// a prune never removes a compose App without it.
	ConfirmDestroy bool
}

Options carry apply-time inputs that never live in a resource file.

type Plan

type Plan struct {
	Source  string   `json:"source,omitempty"`
	Prune   bool     `json:"prune,omitempty"`
	Changes []Change `json:"changes"`
	Summary Summary  `json:"summary"`
	Hash    string   `json:"hash"`
	// Warnings are plan-wide notes; the hash never reads them.
	Warnings []string `json:"warnings,omitempty"`
}

Plan is the ordered set of changes needed to reach the desired state.

func PlanFor

func PlanFor(st *State, res []*Resource, opts Options) Plan

PlanFor computes the plan for resources against live state.

func PlanForContext

func PlanForContext(ctx context.Context, st *State, res []*Resource, opts Options) Plan

PlanForContext is PlanFor with a context that cancels or times out the cloud engine calls.

func (Plan) Pending

func (p Plan) Pending() bool

Pending reports whether applying would change anything.

type PolicyChecker

type PolicyChecker interface {
	CheckCloud(changes []CloudChange) []CloudRefusal
}

PolicyChecker judges a whole cloud change set (cost, IAM, protected delete, destroy cap).

type PruneGuard

type PruneGuard struct {
	MaxPercent int
	MaxCount   int
}

PruneGuard bounds how much of a stack one prune may delete without an explicit confirmation.

func PruneGuardFromEnv

func PruneGuardFromEnv() PruneGuard

PruneGuardFromEnv reads APP_STACK_PRUNE_MAX_PERCENT and APP_STACK_PRUNE_MAX_COUNT; zero is valid and means any prune needs the flag.

type Resource

type Resource struct {
	Kind   Kind
	Name   string
	Scope  string
	Doc    *Document
	Fields map[string]any
	// SecretRefs are app env names backed by the secrets store.
	SecretRefs []string
	// ExistingSecrets are SecretRefs that must already be in the store.
	ExistingSecrets []string
	Warnings        []string
	// ComposeDigest is set for compose Apps and feeds the plan hash.
	ComposeDigest string
	// contains filtered or unexported fields
}

Resource is one desired object in canonical form, ready to compare with live state.

func (*Resource) Key

func (r *Resource) Key() string

Key is the stable identity used in plans: kind plus scope plus name.

type Revision

type Revision struct {
	StackID        string         `json:"stack_id"`
	Number         int            `json:"number"`
	Status         RevisionStatus `json:"status"`
	PlanHash       string         `json:"plan_hash"`
	PlanJSON       []byte         `json:"plan,omitempty"`
	RenderedDocs   []byte         `json:"rendered_docs,omitempty"`
	RollbackOf     int            `json:"rollback_of,omitempty"`
	IdempotencyKey string         `json:"idempotency_key,omitempty"`
	Actor          string         `json:"actor"`
	TokenID        string         `json:"token_id,omitempty"`
	Git            GitMeta        `json:"git"`
	CreatedAt      time.Time      `json:"created_at"`
	FinishedAt     *time.Time     `json:"finished_at,omitempty"`
	ResultJSON     []byte         `json:"result,omitempty"`
}

Revision is one numbered apply of a stack. RenderedDocs holds documents after env substitution only; secret values are never part of it.

func (*Revision) Validate

func (r *Revision) Validate() error

Validate checks a revision before it is first stored.

type RevisionStatus

type RevisionStatus string

RevisionStatus is the lifecycle of one stack apply.

const (
	RevisionPendingApproval RevisionStatus = "pending_approval"
	RevisionApplying        RevisionStatus = "applying"
	RevisionApplied         RevisionStatus = "applied"
	RevisionPartial         RevisionStatus = "partial"
	RevisionFailed          RevisionStatus = "failed"
	RevisionSuperseded      RevisionStatus = "superseded"
)

Revision statuses, in lifecycle order.

func (RevisionStatus) CanTransition

func (s RevisionStatus) CanTransition(next RevisionStatus) bool

CanTransition reports whether a revision may move from s to next.

func (RevisionStatus) Terminal

func (s RevisionStatus) Terminal() bool

Terminal statuses never change again, except applied becoming superseded.

func (RevisionStatus) Valid

func (s RevisionStatus) Valid() bool

Valid reports whether s is a known status.

type Source

type Source struct {
	Name string
	Data []byte
}

Source is one input file, or stdin, before parsing.

type Stack

type Stack struct {
	ID              string      `json:"id"`
	Name            string      `json:"name"`
	ProjectID       string      `json:"project_id"`
	EnvironmentID   string      `json:"environment_id,omitempty"`
	Scope           StackLevel  `json:"scope,omitempty"`
	AllowPrune      bool        `json:"allow_prune"`
	DriftPolicy     DriftPolicy `json:"drift_policy"`
	PinnedRevision  int         `json:"pinned_revision,omitempty"` // 0 means not pinned
	RequireApproval bool        `json:"require_approval"`
	CreatedAt       time.Time   `json:"created_at"`
	CreatedBy       string      `json:"created_by"`
}

Stack is the server-side stack object. Settings here are admin-owned; a manifest can request but never grant them.

func (*Stack) CheckPin

func (s *Stack) CheckPin(rollbackOf int) error

CheckPin returns ErrStackPinned unless the apply is a rollback to the pin. rollbackOf is 0 for an ordinary apply.

func (*Stack) PruneAllowed

func (s *Stack) PruneAllowed(manifestPrune bool) error

PruneAllowed needs both sides so a manifest cannot grant itself prune.

func (*Stack) Validate

func (s *Stack) Validate() error

Validate checks the fields a store must never persist wrong.

type StackDeleteMode

type StackDeleteMode string

StackDeleteMode picks what deleting a stack does to its resources.

const (
	// StackForget releases the ledger and leaves every resource in place.
	StackForget StackDeleteMode = "forget"
	// StackDestroy deletes ledger apps, and databases only with delete_data; never from CI.
	StackDestroy StackDeleteMode = "destroy"
)

func ParseStackDeleteMode

func ParseStackDeleteMode(s string) (StackDeleteMode, error)

ParseStackDeleteMode defaults to forget, the mode that cannot lose data.

type StackLevel

type StackLevel string

StackLevel says whether a stack belongs to one project or to the install.

const (
	LevelProject StackLevel = "project"
	LevelInstall StackLevel = "install"
)

Stack scopes; an empty scope means project.

func ParseStackLevel

func ParseStackLevel(s string) (StackLevel, error)

ParseStackLevel maps an empty value to project and rejects unknown ones.

type StackLock

type StackLock struct {
	StackID   string    `json:"stack_id"`
	Holder    string    `json:"holder"`
	Revision  int       `json:"revision"`
	ExpiresAt time.Time `json:"expires_at"`
}

StackLock is the per-stack lease. Its length comes from APP_STACK_LOCK_TTL.

func (StackLock) Expired

func (l StackLock) Expired(now time.Time) bool

Expired reports whether another holder may take the lease over.

type StackScope

type StackScope struct {
	StackID   string
	StackName string
	Revision  int
}

StackScope is the apply context a stack handler attaches to loopback sub-requests. IAM trusts only this server-side value, never a header.

func StackScopeFrom

func StackScopeFrom(ctx context.Context) (StackScope, bool)

StackScopeFrom returns the stack apply scope, if the request runs inside one.

type StackSpec

type StackSpec struct {
	Scope       StackLevel  `json:"scope,omitempty" yaml:"scope,omitempty"`
	Project     string      `json:"project,omitempty" yaml:"project,omitempty"`
	Environment string      `json:"environment,omitempty" yaml:"environment,omitempty"`
	Prune       bool        `json:"prune,omitempty" yaml:"prune,omitempty"`
	Drift       DriftPolicy `json:"drift,omitempty" yaml:"drift,omitempty"`
}

StackSpec is the spec of a kind: Stack document.

func (*StackSpec) Validate

func (s *StackSpec) Validate() error

Validate checks the spec and fills the default drift policy.

type StackSync

type StackSync struct {
	RepoURL string `json:"repo_url"`
	Branch  string `json:"branch"`
	Path    string `json:"path,omitempty"`
	// CredentialRef names a stored credential ("app:<name>"), never a secret value.
	CredentialRef   string `json:"credential_ref,omitempty"`
	IntervalSeconds int    `json:"interval_seconds,omitempty"`
	WebhookOnly     bool   `json:"webhook_only,omitempty"`
	Enabled         bool   `json:"enabled"`
	Apply           bool   `json:"apply"`
	Prune           bool   `json:"prune"`
	Deploy          bool   `json:"deploy"`
}

StackSync is the opt-in git source of a stack. Apply, prune and deploy on sync are separate flags, all off unless the PUT names each one.

func (StackSync) Validate

func (s StackSync) Validate() (string, error)

Validate returns the stable code and reason for the first invalid field. Paths that leave the checkout carry CodeSyncPathInvalid.

type State

type State struct {
	// contains filtered or unexported fields
}

State is the live control plane data a plan needs, read through the API with the caller's own permissions. A read the caller may not perform is recorded in denied instead of failing the whole plan.

func Load

func Load(ctx context.Context, d Doer, res []*Resource, opts Options) (*State, error)

Load reads the live state the resources and prune scope need.

func (*State) ManagedApps

func (st *State) ManagedApps() []string

ManagedApps lists the apps this apply owns: the ledger's apps for a stack, otherwise those carrying the source's managed-by tag.

type StatusError

type StatusError struct {
	Status  int
	Message string
}

StatusError is a non-2xx API answer.

func (*StatusError) Error

func (e *StatusError) Error() string

type Summary

type Summary struct {
	Create int `json:"create"`
	Update int `json:"update"`
	Delete int `json:"delete"`
	Noop   int `json:"noop"`
	Error  int `json:"error"`
}

Summary counts a plan's items by action.

Jump to

Keyboard shortcuts

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