Help us shape the future of Go! Take the 2026 Go Developer Survey

planner

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package planner turns a spec plus host policy plus adapter capabilities into a bundle plan, or into a plan-time error.

Everything the planner does is side-effect-free. That boundary is what makes a plan-time failure meaningful rather than a label on a mid-deploy crash: steps 1-7 of the deployment pipeline create nothing. See design 05 §3.

Package planner turns a spec plus policy plus adapter capabilities into a plan, or into a readable plan-time error.

Everything here is side-effect-free. That boundary is what makes a plan-time failure meaningful rather than a label on a mid-deploy crash: steps 1-7 of the deployment pipeline create nothing, clone nothing, and start nothing, so the console can call Plan on every spec edit without consequence.

If a check needs a side effect to run, it belongs after the boundary and it is not a plan-time check.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChooseDelivery

func ChooseDelivery(runtimeRef string, rc api.RuntimeCapabilities, builderRef string, bc api.BuilderCapabilities, reg InstallRegistry) (api.ImageDelivery, error)

ChooseDelivery decides how a built image reaches the runtime, from data (R-254): the runtime's ImageDelivery, the builder's SupportsPush, and whether the install has a registry.

Import when the runtime takes one and the install does not send every build through its registry; otherwise the registry, when the runtime pulls from one, one is configured and the builder can push. Anything else is a plan-time refusal that names what is missing, rather than a failure after the build has run.

Types

type Allocation

type Allocation struct {
	CPUMillis   int
	MemoryBytes int64
	DiskBytes   int64

	// LogBytes is the sum of every app's log cap on this runtime (R-224).
	//
	// Committed, not measured. O-16 resolved to bounding what an install
	// promises rather than watching what accumulates: if every app's logs are
	// capped and the caps sum under the budget, the total cannot exceed it.
	// Measuring would mean acting after the disk was already filling, and the
	// only remedy then is recreating containers — which the reconciler may not
	// do because an unrelated app turned chatty.
	LogBytes int64
}

Allocation is what other apps already hold, for the capacity check.

type Allocations

type Allocations interface {
	// AllocatedOn returns what is already committed on a runtime adapter,
	// excluding the app being planned — replanning an app must not count its
	// own current allocation against itself.
	AllocatedOn(ctx context.Context, adapterRef, excludeAppID string) (Allocation, error)
}

Allocations reports current commitments per runtime adapter.

type ImageReader

type ImageReader interface {
	Inspect(ctx context.Context, appID, reference string, want oci.Platform) (oci.Inspection, error)
}

ImageReader reads an app's image from its registry with the app's own credential (oci.Images).

type InstallRegistries

type InstallRegistries interface {
	CurrentRegistry(ctx context.Context) (InstallRegistry, error)
}

InstallRegistries reads the install's registry as it is now: it can be changed from the console while Pando runs (*imageregistry.Service).

func FixedRegistry

func FixedRegistry(r InstallRegistry) InstallRegistries

FixedRegistry answers InstallRegistries with r every time.

type InstallRegistry

type InstallRegistry interface {
	Configured() bool
	Always() bool
	Host() string
}

InstallRegistry is what the planner asks of the install's image registry adapter (issue #72, PR 5; R-252): whether there is one, and whether builds always go through it — its capabilities, read through *imageregistry.Registry, which satisfies this, a nil one included.

type Inventory

type Inventory interface {
	// LiveApps lists the live apps f admits. Every app a candidate policy
	// would block must be among them; others may be too.
	LiveApps(ctx context.Context, f InventoryFilter) ([]InventoryApp, error)
}

Inventory is every live app, with what a policy preview needs to judge it.

An interface because the planner does not import state (design 03 §9 keeps that boundary in one direction) and because previewing a policy against a list is the only thing this needs — not a store.

type InventoryApp

type InventoryApp struct {
	AppID string
	Name  string

	// Spec is the revision the app is pinned to — what its next deploy would
	// actually try to do, not its newest draft.
	Spec *spec.AppSpec

	// AnonymousGrant is whether anyone on the internet can reach this app
	// without signing in (R-075, R-077), and AnonymousPasscode whether they
	// need its passcode to (R-075a).
	AnonymousGrant    bool
	AnonymousPasscode bool
}

InventoryApp is one running app as a policy sees it.

type InventoryFilter

type InventoryFilter struct {
	// Every live app with a source: a source allowlist is in force.
	All bool

	// Apps shared with anyone on the internet; WithoutPasscode narrows that to
	// those shared without a passcode.
	Anonymous                bool
	AnonymousWithoutPasscode bool

	// EgressRuntimes are the runtimes that cannot enforce egress rules. An app
	// on one is named when the candidate restricts every app's egress
	// (EgressRestrictsAll), or when the app has egress settings of its own.
	EgressRuntimes     []string
	EgressRestrictsAll bool

	// OwnEgress names every app with egress settings of its own: the
	// candidate forbids loosening, and only an app's own settings loosen.
	OwnEgress bool

	// Runtimes and Builders are adapter isolation classes. An app is named
	// when its runtime's class is below the higher of its own runtime floor
	// and MinRuntime, or its builder's is below the higher of its own build
	// floor and MinBuild.
	Runtimes   map[string]spec.IsolationClass
	MinRuntime spec.IsolationClass
	Builders   map[string]spec.IsolationClass
	MinBuild   spec.IsolationClass
}

InventoryFilter is which live apps a candidate policy could block, worked out from the policy and the adapters before any app is read (issue #72).

A preview runs while an administrator edits the policy form, and reading every app's pinned spec for it is twenty thousand specs a keystroke. Most of a policy cannot block most apps: no allowlist blocks no source, sharing allowed blocks no public app, a runtime that meets the floor fails no app's floor unless the app asks for more. Each field below names apps one check could fail; an app no field names passes every check, so reading only the named ones gives the same answer as reading them all. The checks themselves still run on every app read — the filter only decides which are read, never what is reported.

func (InventoryFilter) Empty

func (f InventoryFilter) Empty() bool

Empty reports whether f names no app at all.

type Plan

type Plan struct {
	AppID  string            `json:"app_id"`
	Bundle api.BundlePlan    `json:"-"`
	Notes  []string          `json:"notes,omitempty"`
	Checks map[string]string `json:"checks"`

	// Egress is the rules the app would run with, merged, with where each
	// part came from and what every loosening needs (R-188).
	Egress policy.EffectiveEgress `json:"egress"`
}

Plan is the result of a successful plan.

type Planner

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

Planner produces bundle plans.

func New

func New(registry *api.Registry, pol *policy.Evaluator, allocations Allocations) *Planner

func (*Planner) Check

func (p *Planner) Check(ctx context.Context, s *spec.AppSpec) (*Plan, error)

Check runs steps 1-7 and returns the plan, or the first blocking error.

The order matches design 05 §3 exactly, and it matters: the source allowlist is checked before anything would clone, capabilities before capacity, and capacity last because it is the only check whose answer depends on other apps.

func (*Planner) Egress

Egress resolves an app's egress rules against host policy as it stands (R-182). The deploy runner asks this too, so what the plan showed is what the runtime is given.

func (*Planner) PreviewPolicy

func (p *Planner) PreviewPolicy(ctx context.Context, candidate policy.Document) ([]PolicyViolation, error)

PreviewPolicy reports which apps a candidate policy would block (design 05 §3).

O-10 resolved this to "report now, block on next deploy": saving a policy touches no running app, and each violating app fails at step 2 the next time it deploys. That is the right behavior and a bad experience on its own — an admin tightening a policy is entitled to know it will block four apps before they save, because finding out one deploy at a time is how a policy gets rolled back in anger.

Side-effect free, and not only incidentally: this runs the same plan-time checks the deploy will run, against a policy that is not saved yet. Nothing here may write, and the checks it reuses are the pre-boundary ones for exactly that reason.

func (*Planner) WithImages

func (p *Planner) WithImages(images ImageReader) *Planner

WithImages enables the plan-time checks on an image app's image: that it has a build for the runtime's platform, and which of the paths it keeps data in the spec leaves without storage (issue #41).

func (*Planner) WithInstallRegistry

func (p *Planner) WithInstallRegistry(r InstallRegistries) *Planner

WithInstallRegistry tells the planner about the install's image registry. Without it the planner knows of none, and a runtime that only pulls from a registry is refused.

func (*Planner) WithInventory

func (p *Planner) WithInventory(inv Inventory) *Planner

WithInventory enables policy preview.

type PolicyViolation

type PolicyViolation struct {
	AppID   string `json:"app_id"`
	AppName string `json:"app_name"`

	// Code is the error the app's next deploy would fail with, so the console
	// can show the same wording the developer will eventually see.
	Code    string `json:"code"`
	Message string `json:"message"`
	Remedy  string `json:"remedy,omitempty"`
}

PolicyViolation is one app a candidate policy would block.

Jump to

Keyboard shortcuts

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