Documentation
¶
Overview ¶
Package templating provides template rendering capabilities using the Scriggo template engine.
This package offers a unified interface for compiling and rendering templates using Go template syntax via the Scriggo engine.
Templates are pre-compiled at initialization for optimal runtime performance and early detection of syntax errors.
Index ¶
- Constants
- Variables
- func FormatCompilationError(err error, templateName, templateContent string) string
- type CompilationError
- type Engine
- type FileRegistrar
- type FilterFunc
- type GlobalFunc
- type HTTPFetcher
- type IncludeStats
- type Options
- type PathResolver
- type PostProcessor
- type PostProcessorConfig
- type PostProcessorType
- type RegexReplaceProcessor
- type RenderError
- type RenderTimeoutError
- type RenderedResource
- type RenderedResourceCollector
- type ResourceStore
- type RuntimeEnvironment
- type ScriggoEngine
- func (e *ScriggoEngine) AppendTraces(other Engine)
- func (e *ScriggoEngine) ClearVMPool()
- func (e *ScriggoEngine) DisableFilterDebug()
- func (e *ScriggoEngine) DisableTracing()
- func (e *ScriggoEngine) EnableFilterDebug()
- func (e *ScriggoEngine) EnableTracing()
- func (e *ScriggoEngine) GetRawTemplate(templateName string) (string, error)
- func (e *ScriggoEngine) GetTraceOutput() string
- func (e *ScriggoEngine) HasTemplate(templateName string) bool
- func (e *ScriggoEngine) IsFilterDebugEnabled() bool
- func (e *ScriggoEngine) IsProfilingEnabled() bool
- func (e *ScriggoEngine) IsTracingEnabled() bool
- func (e *ScriggoEngine) Render(ctx context.Context, templateName string, templateContext map[string]any) (string, error)
- func (e *ScriggoEngine) RenderWithProfiling(ctx context.Context, templateName string, templateContext map[string]any) (string, []IncludeStats, error)
- func (e *ScriggoEngine) TemplateCount() int
- func (e *ScriggoEngine) TemplateNames() []string
- type SharedContext
- type StatusPatch
- type StatusPatchCollector
- type TemplateNotFoundError
- type TemplatePostProcessor
Constants ¶
const ( // FilterSortBy sorts items by JSONPath criteria. FilterSortBy = "sort_by" // FilterGlobMatch filters items by glob pattern. FilterGlobMatch = "glob_match" // FilterStrip removes leading/trailing whitespace. FilterStrip = "strip" // FilterTrim removes leading/trailing whitespace (alias for strip). FilterTrim = "trim" // FilterB64Decode decodes base64-encoded strings. FilterB64Decode = "b64decode" // FilterDebug outputs debug information for items. FilterDebug = "debug" // FilterIndent indents each line of a string by a specified amount. FilterIndent = "indent" )
Filter name constants for template engines.
const ( // FuncFail stops template execution with an error message. FuncFail = "fail" // FuncMerge merges two maps, returning a new map. // Values from the second map override values from the first. FuncMerge = "merge" // FuncKeys returns sorted keys from a map. FuncKeys = "keys" // FuncSortStrings sorts a []any slice as strings. // Useful when append() returns []any but you need sorted strings. FuncSortStrings = "sort_strings" // FuncSortInts sorts a []any slice of integer values numerically. // Useful for sorting port numbers, IDs, or any other integer keys // where lexicographic sort_strings ordering would be wrong // (e.g. "10" before "2"). Non-integer entries are coerced via // toint() and sort to the front (toint of a non-numeric value // returns 0). FuncSortInts = "sort_ints" // FuncStringsContains checks if a string contains a substring. FuncStringsContains = "strings_contains" // FuncStringsSplit splits a string by a separator. FuncStringsSplit = "strings_split" // FuncStringsTrim trims whitespace from a string. FuncStringsTrim = "strings_trim" // FuncStringsLower converts a string to lowercase. FuncStringsLower = "strings_lower" // FuncStringsReplace replaces all occurrences of old with new. FuncStringsReplace = "strings_replace" // FuncStringsSplitN splits a string by a separator with a maximum number of parts. FuncStringsSplitN = "strings_splitn" // FuncToString converts a value to string. FuncToString = "tostring" // FuncToInt converts a value to int. FuncToInt = "toint" // FuncToFloat converts a value to float64. FuncToFloat = "tofloat" // FuncCeil returns the ceiling of a float. FuncCeil = "ceil" // FuncSeq generates a sequence of integers from 0 to n-1. // Syntax: seq(n) returns []int{0, 1, 2, ..., n-1}. FuncSeq = "seq" // FuncRegexSearch checks if a string matches a regex pattern. FuncRegexSearch = "regex_search" // FuncIsDigit checks if a string contains only digits. FuncIsDigit = "isdigit" // FuncSanitizeRegex escapes regex special characters. FuncSanitizeRegex = "sanitize_regex" // FuncTitle converts a string to title case. FuncTitle = "title" // FuncIsNil checks if a value is nil, including typed nil pointers. // In Go, a typed nil pointer stored in an any is not equal to nil. // This function uses reflection to properly check for nil pointers. // Syntax: isNil(value) returns bool. FuncIsNil = "isNil" // FuncDig navigates nested maps/structures using a sequence of keys. // Returns nil if any key along the path is missing. // Ruby-style dig: obj | dig("metadata", "namespace") | fallback("") // Available in: Scriggo only. FuncDig = "dig" // FuncDigString fuses the very common dig + fallback + tostring chain // used at the chart's polymorphic-value boundaries (annotation lookups, // metadata extraction from any-typed values, etc.) into one filter. // Syntax: obj | dig_string(defaultStr, keys...) returns string. // Equivalent to: obj | dig(keys...) | fallback(defaultStr) | tostring(). // Available in: Scriggo only. FuncDigString = "dig_string" // FuncToStringSlice converts []any to []string. // Available in: Scriggo only. FuncToStringSlice = "toStringSlice" // FuncJoin joins a string slice with a separator. // Available in: Scriggo only. FuncJoin = "join" // FuncReplace replaces strings (alias for strings_replace). FuncReplace = "replace" // FuncCoalesce returns the first non-nil value, or the default if nil. // Workaround for Scriggo's default operator limitation with field access. FuncCoalesce = "coalesce" // FuncFallback returns the first non-nil value, or the fallback if nil. // Jinja2-style alias for coalesce, works well with pipe syntax (e.g., {{ value | fallback("default") }}). FuncFallback = "fallback" // FuncNamespace creates a mutable map for storing state in loops. FuncNamespace = "namespace" // FuncToSlice converts any value to []any for safe ranging. // Returns empty slice if input is nil, otherwise converts to []any. // Syntax: toSlice(value) - returns []any. FuncToSlice = "toSlice" // FuncToStrMap normalises any string-keyed map (map[string]string // from typegen-produced fields like metadata.labels / matchLabels, // or map[string]any from the untyped store path) into a uniform // map[string]string for template iteration. Returns nil for nil // input. Non-string values from a map[string]any input are // coerced via tostring(). // // Motivation: chart code that did `value.(map[string]any)` on a // label / matchLabels field panicked once the typed-watched- // resources path landed, because typegen builds those fields as // `map[string]string` (matching the K8s OpenAPI schema). A // shape-agnostic conversion at the call site keeps chart code // from having to branch per shape. // // Syntax: to_str_map(value) - returns map[string]string. FuncToStrMap = "to_str_map" // FuncAppendAny appends an item to a slice, handling any types. // Syntax: append(slice, item) - returns []any // If slice is nil, creates a new slice with the item. FuncAppendAny = "append" // FuncFirstSeen checks if a composite key is being seen for the first time. // Returns true on first occurrence, false on subsequent calls with same key. // Thread-safe for parallel template rendering. // Syntax: first_seen("prefix", key1, key2, ...) returns bool. FuncFirstSeen = "first_seen" // FuncSelectAttr filters items by attribute existence or value. // Jinja2-compatible filter for selecting items from a sequence. // Syntax: // selectattr(items, "attr") - items where attr is defined/truthy // selectattr(items, "attr", "eq", v) - items where attr equals v // selectattr(items, "attr", "ne", v) - items where attr not equals v // selectattr(items, "attr", "in", list) - items where attr is in list FuncSelectAttr = "selectattr" // FuncJoinKey joins multiple values into a composite key string with a separator. // Automatically converts all values to strings. // Syntax: join_key("_", val1, val2, ...) returns string. FuncJoinKey = "join_key" // FuncShardSlice divides a slice into N shards and returns the portion for a given shard index. // Used for parallel template rendering where work is split across goroutines. // Syntax: shard_slice(items, shardIndex, totalShards) returns []any. FuncShardSlice = "shard_slice" // FuncBasename extracts the filename from a path (like Unix basename command). // Useful for extracting sanitized filenames from paths returned by fileRegistry.Register(). // Syntax: basename(path) returns string. FuncBasename = "basename" // FuncStatusPatch registers a status patch for a Kubernetes resource during rendering. // Syntax: statusPatch(namespace, name, apiVersion, kind, variants). FuncStatusPatch = "statusPatch" // FuncCondition builds a metav1.Condition-compatible map. // Syntax: condition(type, status, reason, message, observedGeneration, lastTransitionTime). FuncCondition = "condition" // FuncTransitionTime determines the correct lastTransitionTime for a // metav1.Condition-shaped entry. Given the *existing* list of conditions // for the target field (e.g. .status.conditions, or // .status.listeners[i].conditions, or .status.parents[i].conditions — // the helper is resource-agnostic, the caller navigates to the list), // the existing lastTransitionTime is preserved if a condition with the // same type AND status is already present; otherwise the current time // is returned. Pass nil when no prior conditions exist (e.g. the // resource was just created) — the helper will treat every condition // as new. // // Syntax: transitionTime(existingConditions, conditionType, newStatus). FuncTransitionTime = "transitionTime" // FilterToJSON serializes any value to a JSON string. // Syntax: toJSON(value) or value | toJSON(). FilterToJSON = "toJSON" // FuncSemverGte checks if a semver version is >= a minimum version. // Compares major.minor (patch ignored). Returns false for unparseable versions. // Syntax: semver_gte(version, minVersion) returns bool. FuncSemverGte = "semver_gte" // FuncMakeGUID builds a HAProxy GUID from parts joined by ":". // Auto-truncates with a hash suffix if the result exceeds 127 characters. // Syntax: make_guid(parts...) returns string. FuncMakeGUID = "make_guid" )
Function name constants for template engines.
const EngineNameScriggo = "scriggo"
EngineNameScriggo is the canonical name of the (only) supported template engine. Used to validate the configured engine string in callers.
Variables ¶
var RenderContextContextKey = renderContextKey{}
RenderContextContextKey is exported for use in engine_scriggo.go.
Functions ¶
func FormatCompilationError ¶
Types ¶
type CompilationError ¶
type CompilationError struct {
// TemplateName is the name of the template that failed to compile
TemplateName string
// TemplateSnippet contains the first 200 characters of the template
TemplateSnippet string
// Cause is the underlying compilation error from the template engine
Cause error
}
CompilationError represents a template compilation failure. This error occurs during template initialization when the template syntax is invalid or contains unsupported constructs.
func NewCompilationError ¶
func NewCompilationError(templateName, templateContent string, cause error) *CompilationError
NewCompilationError creates a CompilationError for a template compilation failure.
func (*CompilationError) Error ¶
func (e *CompilationError) Error() string
Error implements the error interface.
func (*CompilationError) Unwrap ¶
func (e *CompilationError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type Engine ¶
type Engine interface {
// Render executes a template with the given context and returns the output.
// The ctx parameter is used for cancellation and timeout control.
// Returns RenderError if template execution fails, RenderTimeoutError if
// the context deadline is exceeded, or TemplateNotFoundError if the
// template doesn't exist.
Render(ctx context.Context, templateName string, templateContext map[string]any) (string, error)
// RenderWithProfiling renders a template and returns profiling statistics
// for included templates. Useful for performance debugging.
//
// Enable profiling via Options.Profiling at construction time.
// Returns nil stats when profiling is disabled.
//
// Stats are aggregated by template name — multiple renders of the same
// template are combined into a single IncludeStats entry with count > 1.
RenderWithProfiling(ctx context.Context, templateName string, templateContext map[string]any) (string, []IncludeStats, error)
// TemplateNames returns the names of all available templates, sorted alphabetically.
TemplateNames() []string
// HasTemplate checks if a template with the given name exists.
HasTemplate(templateName string) bool
// GetRawTemplate returns the original template string for the given name.
// Returns TemplateNotFoundError if the template doesn't exist.
GetRawTemplate(templateName string) (string, error)
// TemplateCount returns the number of templates in the engine.
TemplateCount() int
// EnableTracing enables template execution tracing for debugging.
EnableTracing()
// DisableTracing disables template execution tracing.
DisableTracing()
// IsTracingEnabled returns whether tracing is currently enabled.
IsTracingEnabled() bool
// GetTraceOutput returns accumulated trace output and clears the buffer.
GetTraceOutput() string
// AppendTraces appends traces from another engine to this engine's trace buffer.
// This is useful for aggregating traces from multiple worker engines.
AppendTraces(other Engine)
// EnableFilterDebug enables detailed filter operation logging.
EnableFilterDebug()
// DisableFilterDebug disables detailed filter operation logging.
DisableFilterDebug()
// IsFilterDebugEnabled returns whether filter debug logging is enabled.
IsFilterDebugEnabled() bool
// ClearVMPool releases pooled VMs to allow garbage collection.
// Call after rendering completes to reduce memory from parallel rendering spikes.
// No-op for engines that don't use VM pooling.
ClearVMPool()
}
Engine is the contract every template engine implementation satisfies. The methods are grouped visually by concern — rendering, introspection, tracing, filter debug, and resource management — but they all live on a single interface because no current caller needs a narrower view and every implementation ends up providing the full surface anyway.
type FileRegistrar ¶
FileRegistrar is an interface for dynamic file registration during template rendering. This interface is implemented by rendercontext.FileRegistry, allowing templates to register auxiliary files (certificates, maps, etc.) without creating import cycles.
The Register method signature matches the variadic calling convention used in templates:
file_registry.Register("cert", "filename.pem", "content...")
Arguments:
- args[0]: file type (string) - "cert", "map", "file", or "crt-list"
- args[1]: filename (string) - base filename
- args[2]: content (string) - file content
Returns:
- Predicted absolute path where the file will be located
- Error if validation fails or content conflict detected
type FilterFunc ¶
FilterFunc is a template filter function signature. Template filters transform values within template expressions:
{{ value | filter_name(arg1, arg2) }}
FilterFunc receives:
- in: The value being filtered (left side of | operator)
- args: Additional filter arguments from the template
Example:
func upperFilter(in any, args ...any) (any, error) {
str, ok := in.(string)
if !ok {
return nil, errors.New("upper filter requires string input")
}
return strings.ToUpper(str), nil
}
type GlobalFunc ¶
GlobalFunc is a template global function signature. Global functions are called directly in templates:
{{ function_name(arg1, arg2) }}
GlobalFunc receives variadic arguments from the template call.
Example:
func mergeFunc(args ...any) (any, error) {
if len(args) != 2 {
return nil, errors.New("merge requires exactly 2 arguments")
}
// ... merge logic
}
type HTTPFetcher ¶
type HTTPFetcher interface {
// Fetch fetches content from a URL with optional options and authentication.
// Arguments:
// - args[0]: URL (string, required)
// - args[1]: options (map, optional) - {"delay": "60s", "timeout": "30s", "retries": 3, "critical": true}
// - args[2]: auth (map, optional) - {"type": "bearer"|"basic", "token": "...", ...}
Fetch(args ...any) (any, error)
}
HTTPFetcher defines the interface for HTTP resource fetching accessible from templates. This interface enables the http.Fetch() method in Scriggo templates:
{% var content = http.Fetch("https://example.com/blocklist.txt") %}
{% var content = http.Fetch(url, map[string]any{"delay": "60s", "critical": true}) %}
Implementations are provided by pkg/controller/httpstore.HTTPStoreWrapper.
type IncludeStats ¶
type IncludeStats struct {
// Name is the name of the included/rendered template.
Name string
// Count is the number of times this template was rendered.
// Templates rendered multiple times (e.g., in loops) have count > 1.
Count int
// TotalMs is the cumulative time spent rendering this template in milliseconds.
// For templates rendered multiple times, this is the sum of all renders.
TotalMs float64
// AvgMs is the average time per render in milliseconds (TotalMs / Count).
AvgMs float64
// MaxMs is the maximum time for a single render in milliseconds.
MaxMs float64
}
IncludeStats represents timing statistics for template includes/renders. Used for performance profiling to identify slow templates.
type Options ¶
type Options struct {
// EntryPoints lists template names to compile explicitly; the remaining
// templates are snippets, discovered and compiled automatically when
// referenced via render/render_glob statements with inherit_context.
// nil means every template is an entry point.
EntryPoints []string
// Filters are custom filters merged over the built-in set (can be nil).
Filters map[string]FilterFunc
// Functions are custom global functions merged over the built-in set
// (can be nil).
Functions map[string]GlobalFunc
// PostProcessors configures per-template post-processing (can be nil).
PostProcessors map[string][]PostProcessorConfig
// Declarations registers domain-specific types with Scriggo (e.g.
// currentConfig for slot-aware server assignment). Use this when
// templates need access to types from other packages (can be nil).
Declarations map[string]any
// Profiling enables Scriggo's built-in profiler, which collects timing
// data for function calls, macros, and includes during execution
// (see RenderWithProfiling). Profiling adds minimal runtime overhead.
Profiling bool
}
Options configures a template engine. The zero value (or a nil *Options) compiles every template as an entry point, with no custom filters, functions, post-processors, type declarations, or profiling.
type PathResolver ¶
type PathResolver struct {
// BaseDir is the absolute base path for HAProxy auxiliary files.
// This is used with "default-path origin" in HAProxy's global section
// to resolve relative paths regardless of where the config file is located.
// Example: "/etc/haproxy"
BaseDir string
// MapsDir is the relative path to the HAProxy maps directory.
// Example: "maps"
MapsDir string
// SSLDir is the relative path to the HAProxy SSL certificates directory.
// Example: "ssl"
SSLDir string
// CRTListDir is the relative path to the HAProxy crt-list files directory.
// Example: "ssl"
CRTListDir string
// GeneralDir is the relative path to the HAProxy general files directory.
// Example: "files"
GeneralDir string
}
PathResolver resolves auxiliary file names to paths based on file type. This is used via the GetPath method in templates to construct paths for HAProxy auxiliary files (maps, SSL certificates, crt-list files, general files).
The paths are relative (maps/, ssl/, files/) and rely on HAProxy's "default-path origin <BaseDir>" directive to resolve to absolute locations. This enables the same rendered config to work for both local validation and DataPlane API deployment.
func (*PathResolver) GetBaseDir ¶
func (pr *PathResolver) GetBaseDir() string
GetBaseDir returns the BaseDir field for use in templates. This method exists because Scriggo runtime variables (declared with nil pointers) support method calls but may not support direct field access.
func (*PathResolver) GetPath ¶
func (pr *PathResolver) GetPath(args ...any) (any, error)
GetPath resolves a filename to a full path based on the file type.
This method is called from templates via the pathResolver context variable:
{{ pathResolver.GetPath("host.map", "map") }} → maps/host.map (relative) or /etc/haproxy/maps/host.map (absolute)
{{ pathResolver.GetPath("504.http", "file") }} → files/504.http (relative) or /etc/haproxy/general/504.http (absolute)
{{ pathResolver.GetPath("cert.pem", "cert") }} → ssl/cert.pem (relative) or /etc/haproxy/ssl/cert.pem (absolute)
{{ pathResolver.GetPath("certificate-list.txt", "crt-list") }} → ssl/certificate-list.txt (relative)
{{ pathResolver.GetPath("", "cert") }} → ssl (directory only)
Parameters:
- args[0]: filename (string) - The base filename (without directory path), or empty string for directory only
- args[1]: fileType (string) - File type: "map", "file", "cert", or "crt-list"
Returns:
- Path to the file (relative or absolute depending on PathResolver configuration)
- Error if argument count is wrong, arguments are not strings, file type is invalid, or path construction fails
Note: The pathResolver must be added to the rendering context for templates to access this method. Relative paths work with HAProxy's working directory resolution during validation.
type PostProcessor ¶
type PostProcessor interface {
// Process applies transformation to the input string.
// Returns the transformed output or an error if processing fails.
Process(input string) (string, error)
}
PostProcessor processes rendered template output before it is returned. Post-processors enable generic transformations like normalization, formatting, or cleanup that apply to the final rendered content.
Post-processors are applied in sequence after template rendering completes. Each processor receives the output of the previous processor (or the initial rendered template for the first processor).
func NewPostProcessor ¶
func NewPostProcessor(config PostProcessorConfig) (PostProcessor, error)
NewPostProcessor creates a post-processor instance from configuration.
Returns an error if:
- The processor type is unknown
- Required parameters are missing
- Parameters are invalid (e.g., invalid regex pattern)
type PostProcessorConfig ¶
type PostProcessorConfig struct {
// Type specifies which post-processor to use.
Type PostProcessorType `yaml:"type" json:"type"`
// Params contains type-specific configuration as key-value pairs.
// For regex_replace:
// - pattern: Regular expression pattern to match (required)
// - replace: Replacement string (required)
Params map[string]string `yaml:"params" json:"params"`
}
PostProcessorConfig defines configuration for a post-processor. The Type field determines which processor implementation to use, and Params contains type-specific configuration.
type PostProcessorType ¶
type PostProcessorType string
PostProcessorType identifies the type of post-processor.
const ( // PostProcessorTypeRegexReplace applies regex-based find/replace. PostProcessorTypeRegexReplace PostProcessorType = "regex_replace" // PostProcessorTypeTemplate applies a Scriggo template transformation. // The template receives the rendered output as the `input` variable (string) // and has access to all standard Scriggo builtins. PostProcessorTypeTemplate PostProcessorType = "template" )
type RegexReplaceProcessor ¶
type RegexReplaceProcessor struct {
// contains filtered or unexported fields
}
RegexReplaceProcessor applies regex-based find/replace to template output.
The processor operates line-by-line, applying the regex pattern to each line independently. This enables efficient processing of large outputs and supports line-anchored patterns like ^[ ]+ for indentation normalization.
Example usage for indentation normalization:
processor, err := NewRegexReplaceProcessor("^[ ]+", " ")
normalized, err := processor.Process(haproxyConfig)
This replaces any leading spaces with exactly 2 spaces per line.
func NewRegexReplaceProcessor ¶
func NewRegexReplaceProcessor(pattern, replace string) (*RegexReplaceProcessor, error)
NewRegexReplaceProcessor creates a new regex replace processor.
Parameters:
- pattern: Regular expression pattern to match (e.g., "^[ ]+" for leading spaces)
- replace: Replacement string (e.g., " " for 2-space indentation)
Returns an error if the regex pattern is invalid.
func (*RegexReplaceProcessor) Process ¶
func (p *RegexReplaceProcessor) Process(input string) (string, error)
Process applies the regex replacement to each line of the input.
The processor streams through input line-by-line using bufio.Scanner, avoiding intermediate allocations from strings.Split/Join. This reduces peak memory usage from ~2x input size to ~1x input size for large configs.
This line-by-line approach enables:
- Efficient processing of large files
- Line-anchored patterns (^ and $)
- Predictable behavior for indentation normalization
type RenderError ¶
type RenderError struct {
// TemplateName is the name of the template that failed to render
TemplateName string
// Cause is the underlying rendering error from the template engine
Cause error
}
RenderError represents a template rendering failure. This error occurs when a valid template fails during execution, typically due to missing context variables or runtime evaluation errors.
func NewRenderError ¶
func NewRenderError(templateName string, cause error) *RenderError
NewRenderError creates a RenderError for a template rendering failure.
func (*RenderError) Error ¶
func (e *RenderError) Error() string
Error implements the error interface.
func (*RenderError) Unwrap ¶
func (e *RenderError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type RenderTimeoutError ¶
type RenderTimeoutError struct {
// TemplateName is the name of the template that timed out
TemplateName string
// Cause is the underlying context error
Cause error
}
RenderTimeoutError represents a template rendering that exceeded the context deadline.
func (*RenderTimeoutError) Error ¶
func (e *RenderTimeoutError) Error() string
Error implements the error interface.
func (*RenderTimeoutError) Unwrap ¶
func (e *RenderTimeoutError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type RenderedResource ¶
type RenderedResource struct {
// APIVersion of the target resource (e.g., "v1", "gateway.networking.k8s.io/v1").
APIVersion string
// Kind of the target resource (e.g., "Service", "Secret", "ConfigMap").
Kind string
// Namespace of the target resource. Empty for cluster-scoped resources;
// the applier passes Namespace("") to the dynamic client which handles
// cluster-scoped types automatically.
Namespace string
// Name of the target resource.
Name string
// Object is the desired resource shape that will be sent verbatim to the
// API server via SSA. It must include `apiVersion`, `kind`, and
// `metadata.name` — the collector validates and injects those at
// Register time, so callers can omit them and pass only the deltas.
Object map[string]any
}
RenderedResource represents a full Kubernetes resource that templates declare during rendering as "I want this to exist on the cluster, owned by the controller". The applier consumes these and reconciles them via SSA (Server-Side Apply); resources owned by the controller but no longer declared in a render are pruned.
This mirrors the StatusPatch / StatusPatchCollector pattern in status_patch.go but for whole-resource lifecycle instead of status-only updates. The same resource-agnostic principle applies: the controller never names a specific resource kind in code — it just applies whatever the template emits. Templates decide *what* to emit; the controller is the generic vehicle.
Object holds the desired API resource as a map[string]any matching the shape of the corresponding Kubernetes resource (apiVersion / kind / metadata / spec / data / …). The applier marshals the map to JSON and sends it to the API server with PatchType=Apply and a fixed field manager.
type RenderedResourceCollector ¶
type RenderedResourceCollector struct {
// contains filtered or unexported fields
}
RenderedResourceCollector collects desired Kubernetes resources for the applier to reconcile. Filled by the renderer after rendering each entry in `spec.k8sResources`: every YAML document in the rendered output becomes one Register call. Thread-safe so the renderer can populate it from parallel template goroutines if needed (same lifecycle / same shape as StatusPatchCollector).
Created per render cycle. Multiple Register calls for the same key are last-write-wins on Object (the prior Object is replaced wholesale; the applier checksums the final object and skips unchanged SSA patches).
func NewRenderedResourceCollector ¶
func NewRenderedResourceCollector() *RenderedResourceCollector
NewRenderedResourceCollector creates an empty thread-safe collector.
func (*RenderedResourceCollector) Register ¶
func (c *RenderedResourceCollector) Register(apiVersion, kind, namespace, name string, object map[string]any) error
Register declares that a resource should exist on the cluster after this render lands. apiVersion / kind / namespace / name identify the target; `object` is the desired shape (without apiVersion / kind / metadata.name — those are injected so the SSA payload is well-formed regardless of which keys the template author included).
Repeated calls with the same (namespace, name, apiVersion, kind) replace the prior Object — last write wins. This keeps the semantics simple for templates that conditionally re-emit resources during a render. The applier's SHA-256 checksum cache prevents the resulting last-version from triggering a redundant API call when nothing actually changed across renders, so this last-write-wins doesn't risk hammering the API.
func (*RenderedResourceCollector) Resources ¶
func (c *RenderedResourceCollector) Resources() []RenderedResource
Resources returns a snapshot of all collected resources. Further Register calls do not affect the returned slice. Order is not guaranteed (Go map iteration); callers that need deterministic order should sort by key.
func (*RenderedResourceCollector) Validate ¶
func (c *RenderedResourceCollector) Validate() error
Validate runs lightweight sanity checks every collected resource must pass before the applier is allowed to send it. Returned errors include the offending key so template authors can locate the bad call site.
The check set is deliberately minimal — the API server will reject malformed payloads itself; we just want to catch obvious template mistakes (missing kind, missing metadata) before they reach the wire.
type ResourceStore ¶
type ResourceStore interface {
// List returns all resources from the store.
List() []any
// Fetch returns resources matching the given keys (typically namespace, name).
Fetch(keys ...any) []any
// GetSingle returns a single resource matching the keys, or nil if not found.
GetSingle(keys ...any) any
}
ResourceStore defines the interface for resource stores accessible from templates. This interface enables direct method calls in Scriggo templates:
{% for _, ing := range resources.ingresses.List() %}
{% var secret = resources.secrets.GetSingle(namespace, name) %}
{% for _, ep := range resources.endpoints.Fetch(serviceName) %}
Scriggo supports dot notation for map access, so `resources.ingresses` is equivalent to `resources["ingresses"]`.
Implementations are provided by pkg/controller/rendercontext.StoreWrapper.
type RuntimeEnvironment ¶
type RuntimeEnvironment struct {
// GOMAXPROCS is the maximum number of OS threads for parallel execution.
// Used by sharding logic to calculate optimal shard count.
GOMAXPROCS int
}
RuntimeEnvironment holds runtime information available to templates. This enables templates to adapt behavior based on the execution environment.
Templates access this via the runtimeEnvironment variable:
{%- var maxShards = runtimeEnvironment.GOMAXPROCS * 2 %}
Fields use exported names for direct template access.
type ScriggoEngine ¶
type ScriggoEngine struct {
// contains filtered or unexported fields
}
ScriggoEngine provides template compilation and rendering capabilities using Scriggo. It pre-compiles all templates at initialization for optimal runtime performance and early detection of syntax errors.
Scriggo uses Go template syntax, which is different from Jinja2:
- Loops: {% for x := range items %}...{% end %}
- Conditionals: {% if cond %}...{% else if other %}...{% end %}
- Variables: {{ .name }} or {{ name }} when in globals
This engine offers excellent performance and low memory usage with Go-style template syntax.
func New ¶
func New(templates map[string]string, opts *Options) (*ScriggoEngine, error)
New creates a Scriggo (Go template syntax) template engine.
Only opts.EntryPoints are compiled explicitly — or every template, when opts is nil or opts.EntryPoints is nil — so syntax errors are caught early. Templates not listed as entry points are snippets, compiled automatically when referenced via render/render_glob statements with inherit_context.
func (*ScriggoEngine) AppendTraces ¶
func (e *ScriggoEngine) AppendTraces(other Engine)
AppendTraces appends traces from another engine to this engine's trace buffer. This is useful for aggregating traces from multiple worker engines.
func (*ScriggoEngine) ClearVMPool ¶
func (e *ScriggoEngine) ClearVMPool()
ClearVMPool releases pooled Scriggo VMs to allow garbage collection. Call after rendering completes to reduce memory from parallel rendering spikes.
This is safe to call at any time - VMs currently in use are not affected (they're held by goroutines, not in the pool). Only pooled VMs waiting for reuse are released.
func (*ScriggoEngine) DisableFilterDebug ¶
func (e *ScriggoEngine) DisableFilterDebug()
DisableFilterDebug disables detailed filter operation logging.
func (*ScriggoEngine) DisableTracing ¶
func (e *ScriggoEngine) DisableTracing()
DisableTracing disables template execution tracing.
func (*ScriggoEngine) EnableFilterDebug ¶
func (e *ScriggoEngine) EnableFilterDebug()
EnableFilterDebug enables detailed filter operation logging.
func (*ScriggoEngine) EnableTracing ¶
func (e *ScriggoEngine) EnableTracing()
EnableTracing enables template execution tracing.
func (*ScriggoEngine) GetRawTemplate ¶
func (e *ScriggoEngine) GetRawTemplate(templateName string) (string, error)
GetRawTemplate returns the original template string for the given name.
func (*ScriggoEngine) GetTraceOutput ¶
func (e *ScriggoEngine) GetTraceOutput() string
GetTraceOutput returns accumulated trace output and clears the buffer.
func (*ScriggoEngine) HasTemplate ¶
func (e *ScriggoEngine) HasTemplate(templateName string) bool
HasTemplate checks if a template with the given name exists.
func (*ScriggoEngine) IsFilterDebugEnabled ¶
func (e *ScriggoEngine) IsFilterDebugEnabled() bool
IsFilterDebugEnabled returns true if filter debug logging is currently enabled.
func (*ScriggoEngine) IsProfilingEnabled ¶
func (e *ScriggoEngine) IsProfilingEnabled() bool
IsProfilingEnabled returns whether profiling is enabled for this engine.
func (*ScriggoEngine) IsTracingEnabled ¶
func (e *ScriggoEngine) IsTracingEnabled() bool
IsTracingEnabled returns whether tracing is currently enabled.
func (*ScriggoEngine) Render ¶
func (e *ScriggoEngine) Render(ctx context.Context, templateName string, templateContext map[string]any) (string, error)
Render executes a template with the given context and returns the output.
func (*ScriggoEngine) RenderWithProfiling ¶
func (e *ScriggoEngine) RenderWithProfiling(ctx context.Context, templateName string, templateContext map[string]any) (string, []IncludeStats, error)
RenderWithProfiling renders a template and returns profiling statistics.
When profiling is enabled (via Options.Profiling), this method returns aggregated include timing statistics. When profiling is disabled, returns nil for the stats slice.
func (*ScriggoEngine) TemplateCount ¶
func (e *ScriggoEngine) TemplateCount() int
TemplateCount returns the number of templates in the engine.
func (*ScriggoEngine) TemplateNames ¶
func (e *ScriggoEngine) TemplateNames() []string
TemplateNames returns the names of all available templates, sorted alphabetically.
type SharedContext ¶
type SharedContext struct {
// contains filtered or unexported fields
}
SharedContext provides thread-safe caching with compute-once semantics. It is used for sharing data between parallel template renders within a single reconciliation cycle.
The API is intentionally minimal to prevent race conditions:
- ComputeIfAbsent stores values atomically (uses singleflight)
- Get provides read-only access to existing values
- No Set method exists - prevents racy check-then-act patterns
- The wasComputed return value enables deduplication (FirstSeen pattern)
func NewSharedContext ¶
func NewSharedContext() *SharedContext
NewSharedContext creates a new thread-safe shared context.
func (*SharedContext) ComputeIfAbsent ¶
func (s *SharedContext) ComputeIfAbsent(key string, compute func() any) (any, bool)
ComputeIfAbsent returns the value for key, computing it if not present. Uses singleflight to ensure only one goroutine computes for a given key. Other goroutines waiting for the same key will receive the computed result.
Returns (value, wasComputed) where:
- value: the stored value (either existing or newly computed)
- wasComputed: true only for the goroutine that actually ran compute()
Use wasComputed for deduplication (FirstSeen pattern):
_, wasFirst := shared.ComputeIfAbsent("seen:"+key, func() any { return true })
if wasFirst {
// First occurrence
}
Use with nil fallback for read-only access:
val, _ := shared.ComputeIfAbsent("key", func() any { return nil })
IMPORTANT: The compute function is called WITHOUT holding the mutex, so it may safely call ComputeIfAbsent for other keys (nested/recursive calls).
func (*SharedContext) Get ¶
func (s *SharedContext) Get(key string) any
Get returns the value for key, or nil if not found. This is a read-only operation - use ComputeIfAbsent for initialization.
type StatusPatch ¶
type StatusPatch struct {
// Namespace of the target Kubernetes resource.
Namespace string
// Name of the target Kubernetes resource.
Name string
// APIVersion of the target resource (e.g., "networking.k8s.io/v1").
APIVersion string
// Kind of the target resource (e.g., "Service", "ConfigMap", or any watched CRD's Kind).
Kind string
// Variants maps pipeline phase names to desired status payloads.
// Keys are phase names: "rendered", "deployed", "renderFailed", "deployFailed".
// Values are the desired .status content for that phase.
Variants map[string]map[string]any
}
StatusPatch represents a status update to apply to a Kubernetes resource. Templates register patches via the statusPatch() function during rendering. Each patch targets a specific resource and contains outcome-keyed variants for different pipeline lifecycle phases.
type StatusPatchCollector ¶
type StatusPatchCollector struct {
// contains filtered or unexported fields
}
StatusPatchCollector collects status patches registered by templates during rendering. It is thread-safe for concurrent writes from parallel template goroutines. Created per render cycle (same lifecycle as FileRegistry).
func NewStatusPatchCollector ¶
func NewStatusPatchCollector() *StatusPatchCollector
NewStatusPatchCollector creates a new thread-safe collector.
func (*StatusPatchCollector) Patches ¶
func (c *StatusPatchCollector) Patches() []StatusPatch
Patches returns all collected status patches as a slice. The returned slice is a snapshot; further Register calls do not affect it.
func (*StatusPatchCollector) Register ¶
func (c *StatusPatchCollector) Register(namespace, name, apiVersion, kind string, variants map[string]map[string]any) error
Register registers a status patch for a Kubernetes resource. If a patch for the same resource already exists, the variant maps are merged (later calls override earlier ones for the same variant key).
The variants parameter maps phase names to status payloads:
- "rendered": applied after successful render
- "deployed": applied after successful deployment
- "renderFailed": applied when later render phases fail
- "deployFailed": applied when deployment fails
type TemplateNotFoundError ¶
type TemplateNotFoundError struct {
// TemplateName is the name of the requested template
TemplateName string
// AvailableTemplates lists all available template names
AvailableTemplates []string
}
TemplateNotFoundError represents a request for a non-existent template.
func NewTemplateNotFoundError ¶
func NewTemplateNotFoundError(templateName string, availableTemplates []string) *TemplateNotFoundError
NewTemplateNotFoundError creates a TemplateNotFoundError with the list of available templates.
func (*TemplateNotFoundError) Error ¶
func (e *TemplateNotFoundError) Error() string
Error implements the error interface.
type TemplatePostProcessor ¶
type TemplatePostProcessor struct {
// contains filtered or unexported fields
}
TemplatePostProcessor applies a Scriggo template transformation to the rendered output.
The template receives the rendered output as the `input` variable (string) and has access to all standard Scriggo builtins (regexp, replace, len, tostring, etc.). The template's output becomes the new rendered content.
This enables arbitrary post-processing logic using familiar template syntax, such as counting patterns, replacing placeholders with computed values, or validating the rendered output.
func NewTemplatePostProcessor ¶
func NewTemplatePostProcessor(source string, globals native.Declarations) (*TemplatePostProcessor, error)
NewTemplatePostProcessor creates a new template post-processor from Scriggo source.
The source is compiled during engine initialization with all standard globals plus an `input` variable (string) that receives the rendered output at processing time. Compilation errors are caught at init time (fail-fast), not at render time.
Parameters:
- source: Scriggo template source code
- globals: Engine globals to make available in the post-processor template
Returns an error if:
- The source is empty
- The template has syntax errors (compilation failure)
func (*TemplatePostProcessor) Process ¶
func (p *TemplatePostProcessor) Process(input string) (string, error)
Process applies the template transformation to the input string.
The input (previously rendered template output) is passed as the `input` variable. The template's output becomes the new rendered content.
Source Files
¶
- engine_interface.go
- engine_scriggo.go
- engine_scriggo_fs.go
- engine_scriggo_tracing.go
- error_formatter.go
- errors.go
- filter_names.go
- filters.go
- filters_collection.go
- filters_guid.go
- filters_navigation.go
- filters_scriggo.go
- filters_status.go
- filters_string.go
- filters_type.go
- filters_version.go
- func_types.go
- postprocessor.go
- postprocessor_regex.go
- postprocessor_template.go
- profiling.go
- rendered_resource.go
- shared_context.go
- sorting.go
- sorting_helpers.go
- status_patch.go
- types.go