rendercontext

package
v0.6.0-rc.1 Latest Latest
Warning

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

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

README

pkg/controller/rendercontext

Builds the template rendering context shared by every code path that renders HAProxy config.

Overview

The renderer, validation-test runner, and benchmarks use this builder. Admission delegates to the renderer through the proposal pipeline. Callers select stores, capabilities, and render mode through options; the builder supplies their shared context structure.

The builder also produces a *FileRegistry (templates can register dynamically generated auxiliary files via this) and supports a StoreWrapper adapter that gives Scriggo templates the List / Fetch / GetSingle methods on top of stores.Store instances.

Quick Start

import "github.com/haproxy-haptic/HAPTIC/pkg/controller/rendercontext"

builder := rendercontext.NewBuilder(
    ctx,
    cfg,
    pathResolver,
    logger,
    rendercontext.WithStores(storeMap),
    rendercontext.WithHAProxyPodStore(haproxyPodStore),
    rendercontext.WithHTTPFetcher(httpWrapper),
    rendercontext.WithCurrentConfig(currentConfig),
)

res := builder.Build()
// res.Context is map[string]any ready to pass to engine.Render
// res.FileRegistry and the other output collectors are also available
// res.Err(ctx) reports cancellation or deferred resource-input failures

ctx, cfg, pathResolver, and logger are required positional arguments. The context cancels API-backed store reads when the render ends. Everything else is supplied through functional options. Defaults depend on the option: for example, capabilities default to false and render mode defaults to reconcile; http is omitted without a fetcher.

Store methods remain value-only for Scriggo, but they record read failures, ambiguous GetSingle results, and typed conversion failures in the returned BuildResult. Rendering callers must check res.Err(ctx) before accepting output.

Context Keys

The context map produced by Build() carries the keys templates rely on:

Key Type Source
resources Schema-derived struct of resource stores WithStores and typed-resource options
controller map[string]ResourceStore containing haproxy_pods WithHAProxyPodStore
templateSnippets []string (sorted) cfg.TemplateSnippets keys
fileRegistry *FileRegistry always present
statusPatchCollector *templating.StatusPatchCollector Always present; also exposed as BuildResult.StatusPatchCollector
pathResolver *templating.PathResolver required
dataplane config.DataplaneConfig from cfg.Dataplane
shared *templating.SharedContext always present (per-render cache)
capabilities map[string]any CapabilitiesToMap of the supplied value; all false when omitted
runtimeEnvironment *templating.RuntimeEnvironment always present (GOMAXPROCS and friends)
currentConfig *renderplan.CurrentConfig WithCurrentConfig (optional; omitted when nil to dodge a Scriggo nil-pointer-initializer panic)
http templating.HTTPFetcher WithHTTPFetcher (optional)
extraContext map[string]any cfg.TemplatingSettings.ExtraContext (always set, possibly empty; top-level keys are also merged into the root context via MergeExtraContextInto)

Adding a new context key means updating Build() plus the pkg/templating/globals.go declarations (so Scriggo knows the type at compile time).

See Also

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package rendercontext provides a centralized builder for template rendering contexts.

This package consolidates the previously duplicated context creation logic from renderer, testrunner, benchmark, and dryrunvalidator into a single, reusable builder.

Usage:

builder := rendercontext.NewBuilder(
    ctx,
    cfg,
    pathResolver,
    logger,
    rendercontext.WithStores(stores),
)
res := builder.Build()
ctx := res.Context

Index

Constants

This section is empty.

Variables

View Source
var ErrDerivedResourceResolverConfigured = errors.New("derived resource resolver is already configured")

ErrDerivedResourceResolverConfigured means a view already has a resolver.

View Source
var ErrDerivedResourceStale = errors.New("derived resource source does not match")

ErrDerivedResourceStale means a transformation no longer matches its exact source value.

View Source
var ErrDerivedResourceViewFrozen = errors.New("derived resource view is frozen")

ErrDerivedResourceViewFrozen means a sealed view rejected a transformation.

Functions

func BuildIncrementalResourcesValueWithViews

func BuildIncrementalResourcesValueWithViews(
	ctx context.Context,
	resourceStores map[string]stores.Store,
	typedTypes map[string]reflect.Type,
	watchedNames []string,
	indexByFor func(name string) []string,
	lazyFor func(name string) bool,
	apiVersionFor func(name string) string,
	logger *slog.Logger,
	resourceErrors *ResourceErrorCollector,
	snapshotView StoreSnapshotView,
	derivedResources *DerivedResourceView,
	memoizeSnapshotView bool,
) any

BuildIncrementalResourcesValueWithViews binds every resource call to the active component execution environment.

func BuildResourcesValue

func BuildResourcesValue(
	ctx context.Context,
	resourceStores map[string]stores.Store,
	typedTypes map[string]reflect.Type,
	watchedNames []string,
	indexByFor func(name string) []string,
	lazyFor func(name string) bool,
	apiVersionFor func(name string) string,
	logger *slog.Logger,
) any

BuildResourcesValue constructs the typed-struct `resources` runtime value the engine binds at template-run time. The shape matches typebootstrap.BuildEngineDeclarations exactly:

*struct{ <PascalCased-name> *innerStore; … }

with one field per watched resource. The inner store's List / Fetch / GetSingle closures return typed pointers when a generated type is available, untyped any otherwise — the OUTER struct shape stays the same either way, so the runtime value keeps binding cleanly against the engine declaration regardless of whether typebootstrap produced types for any given resource.

Production is fail-closed on typebootstrap failures (Bootstrap aborts iteration startup; helpers.BuildAdditionalDeclarations panics on nil Result), so every production engine declares the typed struct and this function always produces a typed value. There is no map fallback — callers that bypass the typed-engine path (a unit test constructing templating.New(...) directly without BuildAdditionalDeclarations) must build their own map[string]templating.ResourceStore aligned with the engine's default declaration in registerScriggoRuntimeVars; do not call BuildResourcesValue from that path.

Inputs:

  • ctx: lifetime for API-backed store reads performed by the returned closures.
  • resourceStores: per-name stores.Store for the resources the local controller has live watchers for. Looked up by name; a watched-resource name with no entry gets a struct field whose closures collapse to empty results. Names in this map that are NOT in watchedNames are silently ignored (e.g. the auto- injected haproxy_pods store, which belongs in controller["haproxy_pods"], not `resources`).
  • typedTypes: per-name generated reflect.Type from typebootstrap. Looked up by name; an unset entry yields an untyped-closure field for that resource. Names not in watchedNames are ignored.
  • watchedNames: every watched-resource name from the config — SOLE iteration source. Must mirror what typebootstrap.BuildEngineDeclarations iterated when it built the engine-side declaration; any field-list drift trips Scriggo's "must have type assignable to struct {...}" bind- time panic.
  • indexByFor: returns the per-resource IndexBy slice the watcher used to build the underlying store; forwarded to the StoreWrapper so per-render snapshot indices align with the live store state.
  • lazyFor: returns whether the per-resource wrapper should use LazySnapshot mode. True when the WatchedResource config has `store: on-demand` (CachedStore-backed) — see StoreWrapper's LazySnapshot field documentation for the semantics. A nil callback or one that always returns false keeps the historical eager-snapshot behaviour for every resource.

func BuildResourcesValueWithViews

func BuildResourcesValueWithViews(
	ctx context.Context,
	resourceStores map[string]stores.Store,
	typedTypes map[string]reflect.Type,
	watchedNames []string,
	indexByFor func(name string) []string,
	lazyFor func(name string) bool,
	apiVersionFor func(name string) string,
	logger *slog.Logger,
	resourceErrors *ResourceErrorCollector,
	snapshotView StoreSnapshotView,
	derivedResources *DerivedResourceView,
	memoizeSnapshotView bool,
) any

BuildResourcesValueWithViews applies transaction-pinned reads and a shared derived view.

func CapabilitiesToMap

func CapabilitiesToMap(caps *dataplane.Capabilities) map[string]any

CapabilitiesToMap converts a Capabilities struct to a template-friendly map. The map uses snake_case keys matching the Capabilities struct field names (e.g., "supports_crt_list" for SupportsCrtList) for consistency with template conventions.

func DetachExtraContext

func DetachExtraContext(extraContext map[string]any) (map[string]any, error)

DetachExtraContext returns a recursively isolated template value tree.

func MergeAuxiliaryFiles

func MergeAuxiliaryFiles(static, dynamic *dataplane.AuxiliaryFiles) (*dataplane.AuxiliaryFiles, error)

MergeAuxiliaryFiles combines static and dynamically registered files without mutating either input. Conflicting definitions of one storage object fail.

func MergeExtraContextInto

func MergeExtraContextInto(renderCtx map[string]any, cfg *config.Config)

MergeExtraContextInto merges the extraContext variables from the config into the provided template context.

This allows templates to access custom variables directly (e.g., {{ debug.enabled }}) instead of wrapping them in a "config" object.

The extraContext key is always populated (with an empty map if nil) to prevent nil pointer dereferences in templates that use extraContext | dig("key") | fallback(default).

func PathResolverFromValidationPaths

func PathResolverFromValidationPaths(vp *dataplane.ValidationPaths) *templating.PathResolver

PathResolverFromValidationPaths creates a PathResolver from ValidationPaths.

This is a convenience function for creating PathResolvers in contexts where ValidationPaths is available (testrunner, dryrunvalidator, benchmark). The TempDir from ValidationPaths becomes the BaseDir used with "default-path origin" in HAProxy's global section to resolve relative paths.

func SeparateHAProxyPodStore

func SeparateHAProxyPodStore(storeMap map[string]stores.Store) (resourceStores map[string]stores.Store, haproxyPodStore stores.Store)

SeparateHAProxyPodStore separates the haproxy-pods store from resource stores.

This is needed because haproxy-pods goes in the controller namespace (controller.haproxy_pods), not in the resources namespace. This function extracts haproxy-pods and returns both the filtered resource stores and the haproxy pod store separately.

Returns:

  • resourceStores: All stores except haproxy-pods
  • haproxyPodStore: The haproxy-pods store, or nil if not present

func SortSnippetNames

func SortSnippetNames(snippets map[string]config.TemplateSnippet) []string

SortSnippetNames sorts template snippet names alphabetically. Returns a slice of snippet names in sorted order.

Snippet ordering is controlled by encoding priority in the snippet name (e.g., "features-050-ssl" for priority 50). This is required because render_glob sorts templates alphabetically.

Types

type AssemblyReuse

type AssemblyReuse struct {
	Reused         int
	Rebuilt        int
	FallbackReason string
}

AssemblyReuse reports how much of the previous assembly a render kept. An empty FallbackReason means the whole-document reuse path ran; any other value names why the render had to rescan and rebuild the assembly from scratch.

type BuildResult

type BuildResult struct {
	Context                   map[string]any
	FileRegistry              *FileRegistry
	PlanRegistry              *PlanRegistry
	StatusPatchCollector      *templating.StatusPatchCollector
	RenderedResourceCollector *templating.RenderedResourceCollector
	EventCollector            *templating.EventCollector
	ResourceErrors            *ResourceErrorCollector
	DerivedResources          *DerivedResourceView
	// contains filtered or unexported fields
}

BuildResult is the context and render-scoped collectors returned from Build().

func (*BuildResult) Err

func (r *BuildResult) Err(ctx context.Context) error

Err returns cancellation first, then any resource failures recorded during the render.

func (*BuildResult) MaterializeUsedPreviousOutputs

func (r *BuildResult) MaterializeUsedPreviousOutputs(useCurrentConfig, useCurrentFiles bool) error

MaterializeUsedPreviousOutputs installs only compiled-used lazy prior outputs.

func (*BuildResult) PreviousOutputSources

func (r *BuildResult) PreviousOutputSources() (CurrentConfigSource, CurrentAuxFilesSource)

PreviousOutputSources returns the attempt-owned immutable source roots.

type Builder

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

Builder constructs template rendering contexts with consistent structure. Use NewBuilder() to create a builder and functional options to configure it.

func NewBuilder

func NewBuilder(ctx context.Context, cfg *config.Config, pathResolver *templating.PathResolver, logger *slog.Logger, opts ...Option) *Builder

NewBuilder creates a new context builder with required dependencies.

Parameters:

  • ctx: Lifetime for store reads performed while rendering
  • cfg: Controller configuration (required)
  • pathResolver: Path resolver for file paths (required)
  • logger: Structured logger (required)
  • opts: Optional configuration via functional options

func (*Builder) Build

func (b *Builder) Build() *BuildResult

type CurrentAuxFilesSource

type CurrentAuxFilesSource interface {
	ValidateAuthentication() error
	SameRoot(CurrentAuxFilesSource) (bool, error)
	MaterializeCurrentAuxFiles() (map[string]string, error)
}

CurrentAuxFilesSource binds lazily materialized currentFiles to an exact root.

type CurrentConfigSource

type CurrentConfigSource interface {
	ValidateAuthentication() error
	SameRoot(CurrentConfigSource) (bool, error)
	MaterializeCurrentConfig() (*renderplan.CurrentConfig, error)
}

CurrentConfigSource binds a lazily materialized currentConfig to an exact root.

type DerivedResource

type DerivedResource struct {
	Identity DerivedResourceIdentity `json:"identity"`
	Source   []byte                  `json:"source"`
	Value    []byte                  `json:"value"`
}

DerivedResource is an immutable transformation suitable for graph storage and replay.

type DerivedResourceIdentity

type DerivedResourceIdentity struct {
	Resource  string `json:"resource"`
	Namespace string `json:"namespace,omitempty"`
	Name      string `json:"name"`
}

DerivedResourceIdentity is the stable owner of one transformed resource.

type DerivedResourceResolver

type DerivedResourceResolver interface {
	ResolveDerivedResource(DerivedResourceIdentity) (DerivedResource, bool, error)
}

DerivedResourceResolver looks up one exact transformed resource.

type DerivedResourceResolverFunc

type DerivedResourceResolverFunc func(DerivedResourceIdentity) (DerivedResource, bool, error)

DerivedResourceResolverFunc adapts a function to DerivedResourceResolver.

func (DerivedResourceResolverFunc) ResolveDerivedResource

func (f DerivedResourceResolverFunc) ResolveDerivedResource(
	identity DerivedResourceIdentity,
) (DerivedResource, bool, error)

ResolveDerivedResource calls f for identity.

type DerivedResourceView

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

DerivedResourceView overlays immutable transformed values on one render's store snapshots.

func NewDerivedResourceView

func NewDerivedResourceView() *DerivedResourceView

NewDerivedResourceView creates an empty render-local derived resource view.

func NewDerivedResourceViewWithResolver

func NewDerivedResourceViewWithResolver(resolver DerivedResourceResolver) *DerivedResourceView

NewDerivedResourceViewWithResolver creates a view with lazy exact-identity lookups.

func (*DerivedResourceView) Bind

func (v *DerivedResourceView) Bind(resource string, exposed, source any) error

Bind records the exact store source behind a typed or otherwise materialized value.

func (*DerivedResourceView) Derivations

func (v *DerivedResourceView) Derivations() []DerivedResource

Derivations returns a stable, detached snapshot for graph persistence.

func (*DerivedResourceView) DeriveResource

func (v *DerivedResourceView) DeriveResource(resource string, item any, path string, value any) (any, error)

func (*DerivedResourceView) ExactCycleProtocolState

func (v *DerivedResourceView) ExactCycleProtocolState() (templating.ExactCycleProtocolState, error)

ExactCycleProtocolState authenticates the empty frozen view and its resolver authority.

func (*DerivedResourceView) Freeze

func (v *DerivedResourceView) Freeze() []DerivedResource

Freeze seals the transformation entries and returns their detached stable snapshot.

func (*DerivedResourceView) Project

func (v *DerivedResourceView) Project(resource string, items []any) ([]any, error)

Project replaces matching source values with their derived values.

func (*DerivedResourceView) Replay

func (v *DerivedResourceView) Replay(entry *DerivedResource) error

Replay installs one previously recorded transformation after validating its exact values.

func (*DerivedResourceView) SetResolver

func (v *DerivedResourceView) SetResolver(resolver DerivedResourceResolver) error

SetResolver installs the view's one lazy exact-identity resolver.

type DirectBoundResourceMaterialization

type DirectBoundResourceMaterialization struct {
	ResourceType string
	Operation    DirectBoundResourceOperation
	ElementType  reflect.Type
	ReturnType   reflect.Type
	// contains filtered or unexported fields
}

DirectBoundResourceMaterialization describes one authenticated bound declaration.

func (DirectBoundResourceMaterialization) Authenticate

func (d DirectBoundResourceMaterialization) Authenticate() error

Authenticate validates the exact request and declared result shape.

type DirectBoundResourceMaterializationRequest

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

DirectBoundResourceMaterializationRequest binds a declaration to its typed builder.

func (*DirectBoundResourceMaterializationRequest) Describe

Describe authenticates and returns the immutable public declaration.

func (*DirectBoundResourceMaterializationRequest) Materialize

Materialize returns a cached typed frame for one exact source projection.

func (*DirectBoundResourceMaterializationRequest) MaterializeUncached

func (r *DirectBoundResourceMaterializationRequest) MaterializeUncached(
	ctx context.Context,
	items []any,
	keys []string,
) (reflect.Value, error)

MaterializeUncached preserves exact projection semantics when sharing is unsafe.

type DirectBoundResourceMaterializationView

type DirectBoundResourceMaterializationView interface {
	MaterializeDirectBoundResource(
		context.Context,
		DirectBoundStoreInvocation,
		*DirectBoundResourceMaterializationRequest,
		stores.Store,
		[]string,
	) (reflect.Value, error)
}

DirectBoundResourceMaterializationView returns a typed frame after exact observation.

type DirectBoundResourceOperation

type DirectBoundResourceOperation uint8

DirectBoundResourceOperation identifies the exact declared result shape.

const (
	DirectBoundResourceList DirectBoundResourceOperation = iota + 1
	DirectBoundResourceFetch
	DirectBoundResourceGetSingle
)

type DirectBoundResourceProjection

type DirectBoundResourceProjection interface {
	AuthenticateDirectBoundResourceProjection(resourceType string) error
	ProjectDirectBoundResourceProjection(
		ctx context.Context,
		resourceType string,
		elementType reflect.Type,
	) ([]reflect.Value, error)
}

DirectBoundResourceProjection authenticates one exact immutable source projection.

type DirectBoundResourceResultProjection

type DirectBoundResourceResultProjection interface {
	ProjectDirectBoundResourceResult(
		ctx context.Context,
		resourceType string,
		operation DirectBoundResourceOperation,
		elementType reflect.Type,
		returnType reflect.Type,
	) (reflect.Value, *templating.IncrementalImmutableCertificate, bool, error)
}

DirectBoundResourceResultProjection returns an authenticated final result directly.

type DirectBoundStoreInvocation

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

DirectBoundStoreInvocation authenticates one allocation-free bound store call.

func NewDirectBoundStoreInvocation

func NewDirectBoundStoreInvocation(
	lease templating.IncrementalResourceInvocationLease,
	slot int,
	generation uint64,
) (DirectBoundStoreInvocation, error)

NewDirectBoundStoreInvocation creates an exact invocation token.

func (DirectBoundStoreInvocation) Generation

func (i DirectBoundStoreInvocation) Generation() uint64

Generation returns the slot generation held by this call.

func (DirectBoundStoreInvocation) Lease

Lease returns the exact item lease authenticated by the snapshot view.

func (DirectBoundStoreInvocation) Slot

Slot returns the preallocated execution slot held by this call.

type DirectBoundStoreSnapshotView

type DirectBoundStoreSnapshotView interface {
	BeginDirectBoundStoreInvocation(
		context.Context,
		templating.IncrementalResourceInvocationLease,
	) (DirectBoundStoreInvocation, error)
	EndDirectBoundStoreInvocation(DirectBoundStoreInvocation) error
	ListDirectBound(
		context.Context,
		DirectBoundStoreInvocation,
		string,
		stores.Store,
	) ([]any, error)
	GetDirectBound(
		context.Context,
		DirectBoundStoreInvocation,
		string,
		stores.Store,
		...string,
	) ([]any, error)
}

DirectBoundStoreSnapshotView serves an authenticated call without a derived context.

type DocumentPlanTransition

type DocumentPlanTransition struct {
	Document      rendercontent.Document
	DocumentDelta *rendercontent.DocumentDelta
	Plan          *renderplan.Snapshot
	PlanDelta     *renderplan.Delta
}

DocumentPlanTransition is one authenticated document-and-plan publication candidate.

type FileRegistry

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

FileRegistry allows templates to dynamically register auxiliary files (certs, maps, general files) during rendering. This is used for cases where file content comes from dynamic sources (e.g., certificates from secrets) rather than pre-declared templates.

Usage in templates:

{% set ca_content = secret.data["ca.crt"] | b64decode %}
{% set ca_path = file_registry.Register("cert", "my-backend-ca.pem", ca_content) %}
server backend:443 ssl ca-file {{ ca_path }} verify required

The Registry method is called on the FileRegistry object in the template rendering context, not as a standalone filter.

func NewFileRegistry

func NewFileRegistry(pathResolver *templating.PathResolver) *FileRegistry

NewFileRegistry creates a new FileRegistry with the given path resolver. The path resolver is used to compute full paths for registered files, ensuring they match the paths used by pathResolver.GetPath() method.

func (*FileRegistry) ExactCycleProtocolState

func (r *FileRegistry) ExactCycleProtocolState() (templating.ExactCycleProtocolState, error)

ExactCycleProtocolState authenticates the empty registry and its path semantics.

func (*FileRegistry) GetFiles

func (r *FileRegistry) GetFiles() *dataplane.AuxiliaryFiles

GetFiles converts all registered files to dataplane AuxiliaryFiles structure. This is called by the renderer after template rendering completes to merge dynamic files with pre-declared auxiliary files.

func (*FileRegistry) Register

func (r *FileRegistry) Register(args ...any) (string, error)

Register registers a new auxiliary file to be created and returns its predicted path. This method is called from templates as file_registry.Register(type, filename, content) or file_registry.Register("file", filename, content, reloadOnPush).

Parameters:

  • fileType: "cert", "map", "file", "crt-list", or "ca-file"
  • filename: Base filename (e.g., "ca.pem", "domains.map", "certificate-list.txt")
  • content: File content as a string
  • reloadOnPush: optional, "file" only. Defaults to true. Pass false for a file a sidecar owns and watches itself (the spoa-hub TOML), so a content change deploys without reloading HAProxy — see the CRD's files[].reloadOnPush.

Returns:

  • Predicted absolute path where the file will be located
  • Error if validation fails or content conflict detected

Conflict Detection:

  • If the same filename is registered multiple times with different content or a different reloadOnPush, returns error
  • If the same filename is registered identically, no error (idempotent)

type MainDocumentRender

type MainDocumentRender struct {
	Document     rendercontent.Document
	Sections     []renderplan.Section
	IncludeStats []templating.IncludeStats

	// Reuse reports how much of the previous assembly this render kept, and
	// why it could not keep all of it.
	Reuse AssemblyReuse
}

MainDocumentRender retains the assembled configuration without materializing it.

func RenderMainDocument

func RenderMainDocument(
	ctx context.Context,
	engine templating.Engine,
	renderCtx map[string]any,
	registry *PlanRegistry,
	profiling bool,
	cacheSessions ...*RenderCacheSession,
) (MainDocumentRender, error)

RenderMainDocument renders and assembles haproxy.cfg as an authenticated document.

type MainRender

type MainRender struct {
	// Config is the assembled configuration — what HAProxy, the consistency
	// check and every downstream consumer see.
	Config string

	// Document is the authenticated assembled configuration root.
	Document rendercontent.Document

	// Sections partition Config in emission order.
	Sections []renderplan.Section

	// IncludeStats is per-snippet profiling, non-nil only when the engine was
	// built with profiling and profiling was requested.
	IncludeStats []templating.IncludeStats
}

MainRender is the result of rendering and assembling haproxy.cfg.

func RenderMain

func RenderMain(
	ctx context.Context,
	engine templating.Engine,
	renderCtx map[string]any,
	registry *PlanRegistry,
	profiling bool,
	cacheSessions ...*RenderCacheSession,
) (MainRender, error)

RenderMain renders haproxy.cfg and assembles the sections templates declared while rendering it. Every render site goes through it, so a config that reaches HAProxy always has a plan behind it and the token invariants are checked the same way everywhere.

type Option

type Option func(*Builder)

Option configures a Builder.

func WithAdmissionSubject

func WithAdmissionSubject(store, namespace, name string) Option

WithAdmissionSubject identifies one watched-resource alias under admission review, exposed to templates as the `admissionSubject` map global. Route-scoped template checks use it to hard-fail only when the violating route belongs to the resource being admitted; violations on other, already-present resources degrade to warn-and-fail-closed-per-route so one bad existing object can never block an unrelated admission. Resource-agnostic by construction: the subject is the store name plus object identity, never a kind-specific shape. When unset (reconcile renders, config-proposal renders, bulk overlays), Build() emits an empty map so templates can dig() it unconditionally.

func WithAdmissionSubjectStores

func WithAdmissionSubjectStores(aliases []string, namespace, name string) Option

WithAdmissionSubjectStores identifies one admitted object across every watched-resource alias whose contents the request changes.

func WithCapabilities

func WithCapabilities(caps dataplane.Capabilities) Option

WithCapabilities sets the HAProxy version capabilities exposed to templates under the top-level "capabilities" key. The production renderer always injects this key, so validation and benchmark contexts must too — otherwise a template branching on `capabilities.supports_crt_list` (and similar) behaves differently between `controller validate` and production, defeating the purpose of pre-flight validation. When unset, Build() still populates "capabilities" with an all-false map (zero-value Capabilities), matching a no-capability HAProxy rather than omitting the key.

func WithCurrentAuxFiles

func WithCurrentAuxFiles(files map[string]string) Option

WithCurrentAuxFiles sets the authoritative auxiliary baseline exposed as currentFiles.

func WithCurrentAuxFilesSource

func WithCurrentAuxFilesSource(source CurrentAuxFilesSource) Option

WithCurrentAuxFilesSource defers projection until a template must execute.

func WithCurrentConfig

func WithCurrentConfig(cfg *renderplan.CurrentConfig) Option

WithCurrentConfig sets the servers of the running config for templates. This enables slot-aware server assignment and other config-aware features. If nil, templates receive nil currentConfig (first deployment case).

func WithCurrentConfigSource

func WithCurrentConfigSource(source CurrentConfigSource) Option

WithCurrentConfigSource defers projection until a template must execute.

func WithDetachedExtraContext

func WithDetachedExtraContext(extraContext map[string]any) Option

WithDetachedExtraContext supplies a caller-owned extra-context snapshot. The caller keeps it isolated from template mutation: see RenderService.attemptExtraContext.

func WithHAProxyPodStore

func WithHAProxyPodStore(store stores.Store) Option

WithHAProxyPodStore sets the HAProxy pod store for controller.haproxy_pods. This enables templates to access HAProxy pod count for calculations.

func WithHTTPFetcher

func WithHTTPFetcher(fetcher templating.HTTPFetcher) Option

WithHTTPFetcher sets the HTTP fetcher for http.Fetch() calls in templates. Pass nil to disable HTTP fetching capability.

func WithImmutableCertificates

func WithImmutableCertificates(certificates ...*templating.IncrementalImmutableCertificate) Option

WithImmutableCertificates guards already-certified inputs without walking them again in every render.

func WithPlanMemo

func WithPlanMemo(memo *PlanMemo) Option

WithPlanMemo reuses plan derivations across renders.

func WithPlanTokenAuthority

func WithPlanTokenAuthority(authority *PlanTokenAuthority) Option

WithPlanTokenAuthority retains authenticated plan placeholders across renders.

func WithRenderMode

func WithRenderMode(mode RenderMode) Option

WithRenderMode sets the RenderMode exposed to templates under "renderMode". When unset, Build() defaults to RenderModeReconcile — the lenient mode — so a caller that forgets to set it degrades to warn-and-proceed rather than failing a live render.

func WithRuntimeEnvironment

func WithRuntimeEnvironment(environment templating.RuntimeEnvironment) Option

WithRuntimeEnvironment supplies the runtime values exposed to templates.

func WithStores

func WithStores(storeMap map[string]stores.Store) Option

WithStores sets the resource stores for the template context. Each store is wrapped in a StoreWrapper to provide template-friendly methods.

func WithTypedResources

func WithTypedResources(types map[string]reflect.Type) Option

WithTypedResources supplies the per-resource generated Go types produced by typebootstrap (pkg/controller/typebootstrap). When set, Build emits one *additional* top-level context entry per supplied type: the resource's name maps to a *[]*<generated-struct> value populated by wrapping each item of the matching store's snapshot through typegen.WrapInto.

The typed entries coexist with the existing map-keyed resources["<name>"] access — chart templates can adopt the typed shape per snippet without breaking templates that still use the untyped path. The two access paths may load their snapshots a few microseconds apart; templates are expected to use ONE shape or the OTHER for a given resource within a single render (mixing wouldn't compile anyway — the untyped variable is `any`).

Resources whose names appear in `types` but for which no store is registered (via WithStores) are silently skipped. That keeps the option safe to use even when typebootstrap successfully generated a type for a resource the local controller doesn't happen to watch — a common case in tests.

type PlanMemo

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

PlanMemo carries plan derivations across a render service's renders: each map file's parsed entries while its text is unchanged, and the prepared snapshot's backends as plan records while the snapshot is the same. Both are shared, never mutated: renderplan copies them into its snapshot and the deploy side clones before editing.

func NewPlanMemo

func NewPlanMemo() *PlanMemo

NewPlanMemo creates an empty memo shared by one render service.

type PlanRegistry

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

PlanRegistry collects the structure templates declare about the config they emit and assembles the final config from the placeholder tokens they got back. One per render; every method is safe for the sharded render's goroutines.

Templates reach it as the `planRegistry` global (templating.PlanRegistrar).

func NewPlanRegistry

func NewPlanRegistry(paths *templating.PathResolver) *PlanRegistry

NewPlanRegistry creates a registry with a fresh random nonce. paths may be nil, in which case file names are used unresolved.

func NewPlanRegistryWithAuthority

func NewPlanRegistryWithAuthority(
	paths *templating.PathResolver,
	authority *PlanTokenAuthority,
) (*PlanRegistry, error)

NewPlanRegistryWithAuthority reuses one sealed placeholder namespace.

func (*PlanRegistry) Assemble

func (r *PlanRegistry) Assemble(ctx context.Context, rendered string, post PostProcessFunc) (string, []renderplan.Section, error)

Assemble replaces every token line in the rendered output with the section text registered for it and returns the final config plus its ordered sections. Text between tokens becomes a core section, so the sections partition the config: concatenating them reproduces it byte for byte, which Assemble asserts before returning.

With no registered section the scan is a single search for the render's nonce and the config is returned unchanged.

func (*PlanRegistry) AssembleDocument

AssembleDocument splices registered sections without materializing one contiguous config.

func (*PlanRegistry) AssembleWithBatch

func (r *PlanRegistry) AssembleWithBatch(
	ctx context.Context,
	rendered string,
	post PostProcessFunc,
	postBatch PostProcessBatchFunc,
) (string, []renderplan.Section, error)

AssembleWithBatch uses one ordered call to post-process all section bodies.

func (*PlanRegistry) AttachPreparedPlan

func (r *PlanRegistry) AttachPreparedPlan(snapshot *PreparedPlanSnapshot) error

AttachPreparedPlan makes an authenticated immutable declaration set visible to this render.

func (*PlanRegistry) Backend

func (r *PlanRegistry) Backend(record map[string]any, text string) (string, error)

Backend records a backend as data and registers the section text emitted from it, so a consumer never has to read the text back to know what changed.

func (*PlanRegistry) ExactCycleProtocolState

func (r *PlanRegistry) ExactCycleProtocolState() (templating.ExactCycleProtocolState, error)

ExactCycleProtocolState authenticates the registry's pre-root declaration state.

func (*PlanRegistry) Fragment

func (r *PlanRegistry) Fragment(name string, value templating.TextFragment) (string, error)

Fragment implements templating.PlanRegistrar. Bypassing the template writer is the point: at chart scale the per-route rules are megabytes the root template would otherwise re-emit every render, already memoised.

func (*PlanRegistry) MapMeta

func (r *PlanRegistry) MapMeta(path string, ordered bool) error

MapMeta declares whether entry order matters for a map file. Maps are ordered unless a template says otherwise, so a third-party map file gets the safe treatment by default.

func (*PlanRegistry) MapPath

func (r *PlanRegistry) MapPath(name string) (string, error)

MapPath is the base-relative path a map file is referenced by in the config (`maps/host.map`), which is also its runtime name.

func (*PlanRegistry) Plan

func (r *PlanRegistry) Plan(config string, aux *dataplane.AuxiliaryFiles) (*renderplan.Plan, error)

Plan bundles the assembled sections with the recorded backends, the profiles, the entries of every map file and the file set, and computes the plan ID. Call it after Assemble; without it the plan carries no sections. Every path in the plan is the base-relative string the config references the file by.

func (*PlanRegistry) PlanDocument

PlanDocument builds exact plan metadata without materializing the assembled config.

func (*PlanRegistry) PlanWithCache

func (r *PlanRegistry) PlanWithCache(
	config string,
	aux *dataplane.AuxiliaryFiles,
	session *RenderCacheSession,
) (*renderplan.Plan, error)

PlanWithCache reuses a plan only for exact authenticated render inputs.

func (*PlanRegistry) PlanWithCacheIdentity

func (r *PlanRegistry) PlanWithCacheIdentity(
	config string,
	aux *dataplane.AuxiliaryFiles,
	session *RenderCacheSession,
) (*renderplan.Plan, *RenderPlanIdentity, error)

PlanWithCacheIdentity returns the exact cache generation that produced plan.

func (*PlanRegistry) PreparedBackendToken

func (r *PlanRegistry) PreparedBackendToken(name string) (string, error)

PreparedBackendToken returns this render's token for an attached backend declaration.

func (*PlanRegistry) PreparedBackendTokenIn

func (r *PlanRegistry) PreparedBackendTokenIn(snapshot *PreparedPlanSnapshot, name string) (string, error)

PreparedBackendTokenIn returns this render's token for a backend that snapshot declares. A memo built for an earlier generation of the attached plan materializes against that generation's snapshot.

func (*PlanRegistry) PreparedPlanTokenAuthority

func (r *PlanRegistry) PreparedPlanTokenAuthority() (*PlanTokenAuthority, error)

PreparedPlanTokenAuthority returns the authenticated namespace used by prepared plan output.

func (*PlanRegistry) Profile

func (r *PlanRegistry) Profile(record map[string]any) (string, error)

Profile content-addresses one named `defaults haptic-be-<hash> from haptic-base` from the shape values a backend shares with every backend of the same shape, registers its section text, and returns the name. Identical bodies yield the same name and one section by construction, so the sharded render produces a stable section set no matter which goroutine emits it first.

func (*PlanRegistry) ProfileGroup

func (r *PlanRegistry) ProfileGroup() string

ProfileGroup returns the token line the assembler replaces with every registered profile section, sorted by name.

func (*PlanRegistry) RegisterPreparedBackend

func (r *PlanRegistry) RegisterPreparedBackend(prepared *PreparedPlanBackend) (string, error)

RegisterPreparedBackend replays a validated backend declaration.

func (*PlanRegistry) RegisterPreparedProfile

func (r *PlanRegistry) RegisterPreparedProfile(prepared PreparedPlanProfile) (string, error)

RegisterPreparedProfile replays a validated profile declaration.

func (*PlanRegistry) Section

func (r *PlanRegistry) Section(kind, name, text string) (string, error)

Section registers section text and returns the token line that stands in for it until assembly. Re-registering identical text is a no-op — sharded rendering legitimately emits the same profile from several goroutines — while different text under the same name is an error.

type PlanTokenAuthority

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

PlanTokenAuthority makes placeholders unguessable within one immutable renderer.

func NewPlanTokenAuthority

func NewPlanTokenAuthority() *PlanTokenAuthority

NewPlanTokenAuthority creates an authenticated placeholder namespace.

type PostProcessBatchFunc

type PostProcessBatchFunc func(ctx context.Context, texts []string) ([]string, error)

PostProcessBatchFunc returns one normalised text per input in the same order.

type PostProcessFunc

type PostProcessFunc func(ctx context.Context, text string) (string, error)

PostProcessFunc normalises a section body the way the surrounding config was normalised — the main template's own post-processor chain.

type PreparedPlanBackend

type PreparedPlanBackend struct {
	Backend  renderplan.Backend `json:"backend"`
	Body     []string           `json:"body,omitempty"`
	Comments []string           `json:"comments,omitempty"`
	Text     string             `json:"text"`
	Digest   string             `json:"digest"`
}

PreparedPlanBackend is a canonical backend declaration safe to cache.

func PreparePlanBackend

func PreparePlanBackend(record map[string]any, text string) (PreparedPlanBackend, error)

PreparePlanBackend validates and detaches a template backend declaration.

func (*PreparedPlanBackend) Clone

Clone returns an independently owned declaration.

func (*PreparedPlanBackend) Validate

func (p *PreparedPlanBackend) Validate() error

Validate rejects noncanonical or corrupted declaration data.

type PreparedPlanProfile

type PreparedPlanProfile struct {
	Name   string   `json:"name"`
	Body   []string `json:"body,omitempty"`
	Text   string   `json:"text"`
	Digest string   `json:"digest"`
}

PreparedPlanProfile is a canonical profile declaration safe to cache.

func PreparePlanProfile

func PreparePlanProfile(record map[string]any) (PreparedPlanProfile, error)

PreparePlanProfile validates and detaches a template profile record.

func (PreparedPlanProfile) Clone

Clone returns an independently owned declaration.

func (PreparedPlanProfile) Validate

func (p PreparedPlanProfile) Validate() error

Validate rejects noncanonical or corrupted declaration data.

type PreparedPlanSnapshot

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

PreparedPlanSnapshot is an immutable set of validated plan declarations.

func NewPreparedPlanSnapshot

func NewPreparedPlanSnapshot() *PreparedPlanSnapshot

NewPreparedPlanSnapshot creates an authenticated empty declaration set.

func NewPreparedPlanSnapshotFromDeclarations

func NewPreparedPlanSnapshotFromDeclarations(
	profiles []PreparedPlanProfile,
	backends []*PreparedPlanBackend,
) (*PreparedPlanSnapshot, error)

NewPreparedPlanSnapshotFromDeclarations creates an authenticated snapshot from validated declarations.

func (*PreparedPlanSnapshot) ValidateAuthentication

func (s *PreparedPlanSnapshot) ValidateAuthentication() error

ValidateAuthentication rejects an unsealed or root-substituted snapshot.

func (*PreparedPlanSnapshot) WithBackend

WithBackend returns a snapshot containing backend.

func (*PreparedPlanSnapshot) WithProfile

WithProfile returns a snapshot containing profile.

func (*PreparedPlanSnapshot) WithoutBackend

func (s *PreparedPlanSnapshot) WithoutBackend(name string) (*PreparedPlanSnapshot, error)

WithoutBackend returns a snapshot without name.

func (*PreparedPlanSnapshot) WithoutProfile

func (s *PreparedPlanSnapshot) WithoutProfile(name string) (*PreparedPlanSnapshot, error)

WithoutProfile returns a snapshot without name.

type PreparedRenderCachePublication

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

PreparedRenderCachePublication is fully validated and publishes without failure.

func (*PreparedRenderCachePublication) Occurrence

func (p *PreparedRenderCachePublication) Occurrence() (uint64, error)

Occurrence returns the render occurrence that owns this publication.

func (*PreparedRenderCachePublication) ValidateAuthentication

func (p *PreparedRenderCachePublication) ValidateAuthentication() error

ValidateAuthentication rejects copied, foreign, or structurally invalid publications.

type RenderCacheSession

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

RenderCacheSession owns one render's private document, assembly, and plan candidate.

func (*RenderCacheSession) Prepare

Prepare validates the complete private candidate. Admission sessions return nil.

func (*RenderCacheSession) ReuseBase

func (s *RenderCacheSession) ReuseBase() error

ReuseBase carries the authenticated document, assembly, and plan roots into this occurrence without rebuilding their resource-scaled compatibility data.

type RenderDocumentCache

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

RenderDocumentCache retains the last authenticated root for exact structural reuse.

func NewRenderDocumentCache

func NewRenderDocumentCache(engine templating.Engine) (*RenderDocumentCache, error)

NewRenderDocumentCache binds structural output reuse to one exact engine.

func (*RenderDocumentCache) Begin

Begin captures one exact committed cache occurrence for a render attempt. Occurrence zero creates a read-only session, as used by admission.

func (*RenderDocumentCache) ValidatePublication

func (c *RenderDocumentCache) ValidatePublication(
	publication *PreparedRenderCachePublication,
	occurrence uint64,
) error

ValidatePublication verifies that publication belongs to this cache and occurrence.

type RenderMode

type RenderMode string

RenderMode tells conflict-style template checks whether this render is an admission dry-run — a proposed change being validated, where the check should fail loud so the webhook denies it — or a normal reconcile render of existing live state, where the check should warn (via recordEvent) and let the render proceed so one already-present bad resource can't block the whole fleet's config. It is exposed to templates as the `renderMode` string global.

const (
	// RenderModeReconcile is the live reconcile render: lenient (warn, never
	// abort). It is the safe default when unset.
	RenderModeReconcile RenderMode = "reconcile"
	// RenderModeAdmission is a webhook / proposed-change validation render:
	// strict (fail, so admission denies the change).
	RenderModeAdmission RenderMode = "admission"
)

type RenderPlanIdentity

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

RenderPlanIdentity proves exact reuse of one immutable plan-cache generation.

func (*RenderPlanIdentity) SameRoot

func (i *RenderPlanIdentity) SameRoot(other *RenderPlanIdentity) (bool, error)

SameRoot reports whether two identities name the same exact plan generation.

func (*RenderPlanIdentity) ValidateAuthentication

func (i *RenderPlanIdentity) ValidateAuthentication() error

ValidateAuthentication rejects copied or substituted identities.

type ResourceErrorCollector

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

ResourceErrorCollector records failures hidden by template-facing value-only APIs.

func NewResourceErrorCollector

func NewResourceErrorCollector() *ResourceErrorCollector

NewResourceErrorCollector creates an empty collector for one render.

func (*ResourceErrorCollector) Err

func (c *ResourceErrorCollector) Err() error

Err returns all recorded errors in deterministic order.

func (*ResourceErrorCollector) Record

func (c *ResourceErrorCollector) Record(err error)

Record adds an error, deduplicating repeated reads of the same failed input.

type ResourceInputError

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

ResourceInputError reports a watched-resource failure that invalidates the render.

func (*ResourceInputError) Error

func (e *ResourceInputError) Error() string

func (*ResourceInputError) Unwrap

func (e *ResourceInputError) Unwrap() error

Unwrap exposes the store, index, or materialization failure.

type ResourceItemCache

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

ResourceItemCache reuses immutable typed resource projections within one render.

func NewResourceItemCache

func NewResourceItemCache() *ResourceItemCache

NewResourceItemCache returns an empty render-scoped cache.

func (*ResourceItemCache) Revoke

func (c *ResourceItemCache) Revoke()

Revoke drops all render-only resource materializations.

type SelectiveDerivedResourceResolver

type SelectiveDerivedResourceResolver interface {
	DerivedResourceSupported(string) bool
}

SelectiveDerivedResourceResolver identifies resources for which resolution can succeed.

type StoreLookupKeyNormalizer

type StoreLookupKeyNormalizer interface {
	NormalizeLookupKeys(resourceType string, keys []any) ([]string, error)
}

StoreLookupKeyNormalizer replaces legacy lookup-key coercion for a snapshot view.

type StoreLookupKeySource

type StoreLookupKeySource interface {
	Len() int
	Value(index int) any
}

StoreLookupKeySource exposes synchronous lookup arguments without materializing a slice.

type StoreLookupKeySourceNormalizer

type StoreLookupKeySourceNormalizer interface {
	NormalizeLookupKeySource(resourceType string, keys StoreLookupKeySource) ([]string, error)
}

StoreLookupKeySourceNormalizer normalizes a non-retainable lookup-key source synchronously.

type StoreLookupReflectKeySource

type StoreLookupReflectKeySource interface {
	StoreLookupKeySource
	ReflectValue(index int) reflect.Value
}

StoreLookupReflectKeySource preserves an argument's reflection value without boxing it.

type StoreSnapshotView

type StoreSnapshotView interface {
	List(resourceType string, store stores.Store) ([]any, error)
	Get(resourceType string, store stores.Store, keys ...string) ([]any, error)
}

StoreSnapshotView supplies transaction-pinned reads for incremental components.

type StoreWrapper

type StoreWrapper struct {
	Store        stores.Store
	ResourceType string
	Logger       *slog.Logger

	// IndexBy mirrors the JSONPath expressions the underlying store uses
	// to index resources. Required for snapshot-served Fetch/GetSingle.
	IndexBy []string

	// LazySnapshot defers the eager Store.List() until List() is
	// actually called. Set when the underlying store is a CachedStore
	// (WatchedResources[name].Store == "on-demand"). See type doc.
	LazySnapshot bool

	SnapshotView        StoreSnapshotView
	DerivedView         *DerivedResourceView
	MemoizeSnapshotView bool
	// contains filtered or unexported fields
}

StoreWrapper exposes one immutable resource view to a template render. Lazy snapshots load exact and prefix reads on demand; List still resolves the complete store because cache warmth isn't a semantic input.

func (*StoreWrapper) CloneWithSnapshotView

func (w *StoreWrapper) CloneWithSnapshotView(view StoreSnapshotView, memoize bool) *StoreWrapper

CloneWithSnapshotView returns an empty per-render cache over the supplied view.

func (*StoreWrapper) CloneWithSnapshotViewContext

func (w *StoreWrapper) CloneWithSnapshotViewContext(
	ctx context.Context,
	view StoreSnapshotView,
	memoize bool,
) *StoreWrapper

CloneWithSnapshotViewContext returns an empty per-render cache with a new read context.

func (*StoreWrapper) Fetch

func (w *StoreWrapper) Fetch(keys ...any) (result []any)

Fetch performs O(1) indexed lookup over the per-render snapshot using the provided keys.

This method enables efficient lookups in templates and supports non-unique index keys by returning all resources matching the provided keys:

{% for endpoint_slice in resources.endpoints.Fetch(service_name) %}
  {{ endpoint_slice.metadata.name }}
{% endfor %}

The keys must match the index configuration for the resource type. For example, if EndpointSlices are indexed by service name:

index_by: ["metadata.labels['kubernetes.io/service-name']"]

Then you can look them up with:

resources.endpoints.Fetch("my-service")

This will return ALL EndpointSlices for that service (typically multiple).

Accepts any arguments for template compatibility.

A failed read returns an empty slice to the template and fails the render at its next phase boundary.

func (*StoreWrapper) GetSingle

func (w *StoreWrapper) GetSingle(keys ...any) (result any)

GetSingle performs O(1) indexed lookup over the per-render snapshot and expects exactly one matching resource.

This method is useful when you know the index keys uniquely identify a resource:

{% set ingress = resources.ingresses.GetSingle("default", "my-ingress") %}
{% if ingress %}
  {{ ingress.metadata.name }}
{% endif %}

{# Cross-namespace reference #}
{% set ref = "namespace/name".split("/") %}
{% set secret = resources.secrets.GetSingle(ref[0], ref[1]) %}

Accepts any arguments for template compatibility.

Returns:

  • nil if no resources match (this is NOT an error - allows templates to check existence)
  • The single matching resource if exactly one matches
  • nil if multiple resources match; the ambiguity then fails the render

A failed read returns nil to the template and fails the render at its next phase boundary.

func (*StoreWrapper) List

func (w *StoreWrapper) List() (result []any)

List returns all resources from the per-render snapshot.

A lazy List resolves the complete pinned store once.

Jump to

Keyboard shortcuts

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