Documentation
¶
Overview ¶
Package render evaluates a CUE module from a local directory against a curated set of inputs and returns the Kubernetes resources, readiness, and status it produces.
The contract with a module is intentionally narrow. The engine fills a top-level input field with the observed XR's spec, metadata, environment, and explicitly requested observed composed resources, then reads an author-keyed resources map (each entry an object plus an optional readiness hint) and an optional top-level status. Module authors never see Crossplane's request/response internals.
The engine itself is pure: a ModuleLoader port abstracts where the module bytes come from. Two adapters ship here — LocalLoader serves a fixed directory (offline for a self-contained module, or resolving dependencies through a registry via NewLocalLoader), and OCILoader fetches a module (and its transitive CUE dependencies) from an OCI registry per CUE_REGISTRY. The gRPC function and codegen live in other packages.
Index ¶
- func LoadModule(ctx context.Context, loader ModuleLoader, ref string) (cue.Value, func(), error)
- func ProjectSpec(spec map[string]any) map[string]any
- func UsesObservedResources(v cue.Value) (bool, error)
- type Engine
- type Inputs
- type Loaded
- type LocalLoader
- type Metadata
- type ModuleLoader
- type OCIConfig
- type OCILoader
- type Requirement
- type Resource
- type Result
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LoadModule ¶
LoadModule resolves ref through loader, builds the module's CUE value, and returns it alongside a cleanup func the caller must invoke when done. It is the single load path shared by the render engine and the author-time schema codegen, so both consume an identically-built module value: bytes come only from the ModuleLoader port, and a loader-supplied dependency registry (set by OCILoader, nil for LocalLoader) resolves transitive CUE dependencies.
The returned value is the raw built module (no inputs filled). On any error the cleanup has already run and the caller must not call it again.
func ProjectSpec ¶
ProjectSpec returns a shallow copy of an observed XR spec with the Crossplane-reserved keys removed, so a closed module [#Spec] does not conflict with machinery fields the author never declared. The input map is not mutated; a nil spec yields a nil result.
func UsesObservedResources ¶ added in v0.1.6
UsesObservedResources reports whether the module explicitly materializes out.input.observedResources as a regular field — the opt-in that makes the engine deliver observed composed resources. Tooling uses it to reject observed fixtures against modules that would silently ignore them.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine renders CUE modules into composed resources, readiness, and status.
func New ¶
func New(loader ModuleLoader) *Engine
New returns an Engine that loads modules with the given loader.
func (*Engine) Render ¶
Render loads the module at ref, fills its out.input field with in (projecting Crossplane-reserved keys out of the spec), and returns the composed resources, readiness, and status it produces. It errors if the module is missing, fails to evaluate, violates its #Spec, or leaves resources or status non-concrete.
A module nests its transform under a single top-level `out` field (out.input/out.resources/out.status) — the module contract. The schema definitions (#API/#Spec/#Status) stay top-level and are not read here.
type Inputs ¶
type Inputs struct {
// Spec is the observed composite resource's user spec. The engine projects
// out Crossplane-reserved keys before unifying it against the module #Spec.
Spec map[string]any `json:"spec,omitempty"`
// Metadata is the observed composite resource's identifying metadata.
Metadata Metadata `json:"metadata"`
// Environment is the merged EnvironmentConfig data from the pipeline context,
// empty when no environment was supplied.
Environment map[string]any `json:"environment,omitempty"`
// RequiredResources holds the cluster objects Crossplane fetched for the
// requirements this module emitted on a previous pass, keyed by the author's
// requirement name. An empty list means "requested but none found". omitempty
// keeps it off out.input before any requirement is delivered.
RequiredResources map[string][]map[string]any `json:"requiredResources,omitempty"`
// ObservedResources holds the composed Kubernetes objects Crossplane supplied
// on the current request, keyed by the author's stable resource name. The
// engine exposes it only when the loaded module declares observedResources as
// a regular field under out.input; optional or absent declarations are omitted.
ObservedResources map[string]map[string]any `json:"observedResources,omitempty"`
}
Inputs are the curated values a module sees under its top-level input field.
type Loaded ¶
type Loaded struct {
// Dir is the module root directory (containing cue.mod) to load.
Dir string
// Registry resolves the module's transitive CUE dependencies during load.
// It is nil for self-contained modules served from disk (LocalLoader), in
// which case the engine loads without a registry and dep-free modules render
// identically.
Registry modconfig.Registry
// Cleanup releases any resources the loader allocated. It is always non-nil
// and safe to call exactly once; a persistent cache returns a no-op.
Cleanup func()
}
Loaded is the result of resolving a module reference to local bytes: the directory the engine loads, an optional CUE registry for resolving the module's transitive dependencies at load time, and a cleanup func the caller must invoke when done.
type LocalLoader ¶
type LocalLoader struct {
// Dir is the module root directory (containing cue.mod) to serve.
Dir string
// contains filtered or unexported fields
}
LocalLoader serves a module from a fixed local directory, ignoring ref.
The zero value (LocalLoader{Dir: ...}) resolves no dependencies: it returns a nil registry, so a self-contained module loads fully offline. This is the primitive used by hermetic tests and offline development. Use NewLocalLoader to serve a module that imports OCI dependencies (e.g. the official cue.dev/x/k8s.io schema); it attaches a registry so those deps resolve at load time.
func NewLocalLoader ¶
func NewLocalLoader(dir string, cfg OCIConfig) (*LocalLoader, error)
NewLocalLoader serves the module in dir and resolves its transitive CUE dependencies through a registry built from cfg (central by default; CUE_REGISTRY for private or override registries). Use it for a local module that imports OCI dependencies; the zero-value LocalLoader stays offline.
type Metadata ¶
type Metadata struct {
// Name is the composite resource's name.
Name string `json:"name,omitempty"`
// Namespace is the composite resource's namespace.
Namespace string `json:"namespace,omitempty"`
}
Metadata is the subset of an XR's metadata exposed to a module.
type ModuleLoader ¶
type ModuleLoader interface {
// Load fetches the module identified by ref ("path@version") and returns a
// [Loaded] describing the local directory, an optional dependency registry,
// and a cleanup func the caller must invoke when done.
Load(ctx context.Context, ref string) (Loaded, error)
}
ModuleLoader fetches a CUE module by reference and exposes it as a local directory tree (rooted at the module's cue.mod) that the engine can load.
type OCIConfig ¶
type OCIConfig struct {
// Env provides environment variables (CUE_REGISTRY, CUE_CACHE_DIR, ...) used
// to build the registry clients. When nil, the process environment
// (os.Environ) is used. Injecting Env keeps resolution explicit and avoids
// relying on process-global state under parallel tests.
Env []string
// CacheDir is the writable directory used for both CUE's module cache and the
// loader's digest-keyed extraction cache. When set it overrides CUE_CACHE_DIR
// in Env, which makes caching work for a nonroot or read-only-root container.
// When empty, CUE_CACHE_DIR (or the OS user cache) is used.
CacheDir string
// Expect maps a module ref ("path@version") to the manifest digest the loader
// must observe after fetch. A mismatch fails the load. Refs absent from the
// map are not digest-checked. This is the runtime half of the schema<->runtime
// digest lock-step: CUE references modules by semver, not digest, so the
// expected digest is verified after fetch rather than referenced directly.
//
// Only the root module ref passed to Load is verified. Transitive dependency
// refs in this map are ignored: deps are resolved immutably by version through
// CUE's module cache, so a per-dep digest lock is out of scope here.
Expect map[string]string
}
OCIConfig configures an OCILoader.
type OCILoader ¶
type OCILoader struct {
// contains filtered or unexported fields
}
OCILoader loads CUE modules from an OCI registry using the CUE module registry protocol. It honors CUE_REGISTRY (including the "+insecure" suffix used for plain-HTTP localhost registries).
The root module is handled digest-aware: every Load re-resolves the tag to its current manifest digest and keys a loader-owned extraction cache by that digest, so changed content under the same tag yields a new digest, a cache miss, and a re-render. A ref->digest pointer lets a fresh process serve the last-known digest from cache when the registry is unreachable. Transitive dependencies resolve through CUE's own version-keyed module cache (correct, since versions are immutable) via the injected registry.
func NewOCILoader ¶
NewOCILoader builds an OCILoader from cfg. It resolves a writable cache dir, forces CUE_CACHE_DIR to it, and derives both a digest-aware registry client (for the root fetch) and a CUE registry (for transitive deps and the shared modcache) from the same configuration.
func (*OCILoader) Load ¶
Load fetches the module at ref from the OCI registry, verifies its manifest digest, extracts it (once) into a digest-keyed cache directory, and returns that directory together with the CUE registry for resolving transitive deps.
When the registry is reachable, the tag is always re-resolved to its current digest so republished content is re-fetched. When the registry is unreachable, the loader falls back to the last-known digest recorded for ref and serves it from cache. A non-existent ref (404/NAME_UNKNOWN/MANIFEST_UNKNOWN) propagates rather than falling back.
func (*OCILoader) ResolveDigest ¶
ResolveDigest resolves ref ("path@version") to its current OCI manifest digest ("sha256:...") by querying the same registry the loader fetches modules from. It is the publish-time half of the schema<->runtime digest lock-step: the author records this real, resolved digest in the Composition's cuefn input so the runtime loader (OCIConfig.Expect) can verify the module it later fetches has not drifted. A malformed ref, a non-existent module, or an unreachable registry surfaces as a clear error naming ref; no digest cache fallback is used because publish must observe the live digest.
type Requirement ¶ added in v0.1.1
type Requirement struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
MatchName string `json:"matchName,omitempty"`
MatchLabels map[string]string `json:"matchLabels,omitempty"`
Namespace string `json:"namespace,omitempty"`
}
Requirement is one entry of out.requirements: a selector the engine returns for Crossplane to fetch. Exactly one of MatchName/MatchLabels is set, enforced at render time by [readRequirements].
type Resource ¶
type Resource struct {
// Object is the rendered Kubernetes object as an unstructured map.
Object map[string]any
// Ready is the readiness the module assigned. An absent module hint maps to
// [resource.ReadyUnspecified].
Ready resource.Ready
}
Resource is a single composed resource produced by a module: a finished Kubernetes object plus the readiness the module assigned it.
type Result ¶
type Result struct {
// Resources holds the composed resources keyed by the author's map key
// verbatim (the Crossplane composed-resource name).
Resources map[string]Resource
// Status is the status the module returned, or nil when it returned none.
Status map[string]any
// Requirements holds the selectors the module emitted under out.requirements,
// keyed by requirement name. nil when the module declares none.
Requirements map[string]Requirement
}
Result is the decoded output of a render: the composed resources keyed by the author's stable name, and the optional status the module returned.