Documentation
¶
Overview ¶
Package synth builds OPM artifact CUE values from in-memory typed inputs by unifying caller-supplied identity (name, namespace, module reference, values, subscriptions, labels, annotations) against the schema definition resolved through the caller-supplied *schema.Cache.
It covers two of the three OPM artifacts:
- synth.Instance builds a #ModuleInstance from a *module.Module plus typed identity and values.
- synth.Platform builds a #Platform from typed identity and subscription inputs.
Synth is a peer of opm/helper/loader, not a subpackage of it. The loader tree (opm/helper/loader/file, opm/helper/loader/bytes) reads existing artifact bytes from a source — filesystem or byte buffer. Synthesis is creation from typed inputs: there is no file to parse, no bytes to decode. Co-locating synth under loader would conflate verbs and force every reader to ignore the package doc when interpreting the package path. Keeping them as peers makes each verb legible at a glance.
Recommended entry points: (*kernel.Kernel).SynthesizeInstance and (*kernel.Kernel).SynthesizePlatform. SynthesizeInstance chains synth.Instance into Kernel.ProcessModuleInstance so a caller building an instance from typed inputs gets a fully validated, concrete *module.Instance in one call. SynthesizePlatform chains synth.Platform into platform.NewPlatformFromValue and returns a typed pre-materialize *platform.Platform; it does NOT call Materialize (registry I/O stays an explicit, separate, caller-driven step). Call synth.Instance / synth.Platform directly only when there is no *Kernel on hand (rare; mostly unit-testing in tight loops).
Single-build construction (ADR-003): synth.Instance does NOT stitch an instance value together from separate CUE evaluations. It synthesizes a virtual CUE package — a fabricated cue.mod/module.cue (deps: the resolved core version + the module's path@version), an instance.cue that IMPORTS the module and writes `#module: <import>` plus caller metadata, and a values.cue rendered from the caller's Values via format.Node — and evaluates it in one build through the same loader build-and-shape-gate step LoadInstancePackage uses for on-disk packages. There is no cue.Scope/userModule trick and no Go-side FillPath(#config, Values) pre-merge: because the module enters the build by import (one #Image / #Secret closure), the schema's own `unifiedModule = #module & {#config: values}` performs the values merge in CUE. The synth and authored-Instance paths therefore share one mechanism, so a render bug surfaces in both or neither. See adr/003-single-build-cue- evaluation-invariant.md.
Schema source of truth: the synth helpers never reimplement derivations the CUE schema already owns (instance UUID stamping, components fan-out from #components, auto-secrets injection, standard label stamping, the #Subscription enable default). Every derived field flows through unification with the artifact definition obtained from in.SchemaCache.Get(ctx); the schema package itself is resolved by the Cache's underlying Loader (typically schema.OCILoader against CUE_REGISTRY).
Boundary scope: synth helpers live under opm/helper/ because they are opinionated frontend conveniences. Frontends MAY bypass them and unify against the schema themselves; nothing in synth is part of the kernel contract.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMissingModule is returned when InstanceInput.Module is nil, or carries // no decoded metadata identity (modulePath / version) to import by. ErrMissingModule = errors.New("synth.Instance: Module is required") // ErrMissingName is returned when InstanceInput.Name is empty. ErrMissingName = errors.New("synth.Instance: Name is required") // ErrMissingNamespace is returned when InstanceInput.Namespace is empty. ErrMissingNamespace = errors.New("synth.Instance: Namespace is required") // ErrMissingSchemaCache is returned when InstanceInput.SchemaCache is // nil. Callers typically pass kernel.SchemaCache() from their Kernel. ErrMissingSchemaCache = errors.New("synth.Instance: SchemaCache is required") // SchemaCache resolves but does not expose #ModuleInstance, or does not // surface a resolved version to derive the synthesized package's core import // major. ErrSchemaUnavailable = errors.New("synth.Instance: schema unavailable") // ErrMissingSource is returned when InstanceInput.Module carries no staged // registry source (Module.HasSource() is false). Instance constructs the // #ModuleInstance INSIDE the module's own staged source tree so the module's // already-tidied cue.mod/module.cue drives transitive resolution; it cannot // do so without that source. Callers acquire a source-carrying module via // Kernel.AcquireModuleFromRegistry. Instance never performs a registry fetch // of its own. ErrMissingSource = errors.New("synth.Instance: Module has no staged source; acquire it via Kernel.AcquireModuleFromRegistry") )
Sentinel errors. Frontends inspecting the failure surface match on these via errors.Is. Synthesis-time errors all wrap one of these so callers can distinguish "you forgot a required field" from "the schema is broken."
var ( // ErrMissingType is returned when PlatformInput.Type is empty. It has no // Instance counterpart (only #Platform carries a required type). ErrMissingType = errors.New("synth.Platform: Type is required") // ErrPlatformMissingName is returned when PlatformInput.Name is empty. ErrPlatformMissingName = errors.New("synth.Platform: Name is required") // ErrPlatformMissingSchemaCache is returned when PlatformInput.SchemaCache // is nil. Callers typically pass kernel.SchemaCache() from their Kernel. ErrPlatformMissingSchemaCache = errors.New("synth.Platform: SchemaCache is required") // SchemaCache resolves but does not expose #Platform. ErrPlatformSchemaUnavailable = errors.New("synth.Platform: schema unavailable") // ErrSubscriptionMissingVersion is returned when a SubscriptionSpec // carries an empty Version. Early validation at the write side (the only // in-repo producer of the subscription shape) beats a materialize-time // failure at the read side: the kernel resolves exactly the authored // version (0010 D14), so a versionless subscription can never materialize. ErrSubscriptionMissingVersion = errors.New("synth.Platform: subscription Version is required") )
Platform sentinel errors. These mirror the Instance set but carry synth.Platform: wording so error messages name the failing artifact. They are distinct package-level vars from the Instance sentinels (D4) — the instance sentinels stay untouched to avoid churn on synth.Instance callers.
Functions ¶
func Instance ¶
Instance builds a #ModuleInstance CUE value by synthesizing an in-memory CUE package INSIDE the acquired module's own staged source tree and evaluating it in a single build (ADR-003), through the same loader build-and-shape-gate path LoadInstancePackage uses for on-disk instance packages. The module's own (already-tidied at publish time) cue.mod/module.cue is the build's module file, so it — not a fabricated dep list — drives transitive dependency resolution. The synthesized package consists of an instance.cue overlaid under a reserved subdirectory of the module root (importing core and the module's own package, writing `#module: <import>` plus caller-supplied metadata) and — when Values is supplied — a values.cue rendered from InstanceInput.Values.
Because the module is the build's main module, its own package import resolves LOCALLY (no fabricated dependency, no registry round-trip for the module itself), and the module's transitive imports (core, catalog subpackages, …) resolve through the module's own cue.mod/module.cue. This is why a module that imports a catalog subpackage synthesizes correctly where the previous fabricated-{core, module}-deps approach failed (library#31). The module enters the build by import, so there is no closed-into-closed FillPath and no Go-side value pre-merge: the schema's own `let unifiedModule = #module & {#config: values}` performs the values merge in CUE.
Instance REQUIRES the module to carry staged source (Module.HasSource()); acquire it via Kernel.AcquireModuleFromRegistry. It never fetches from a registry itself.
The function does NOT validate values against #config and does NOT enforce concreteness. Both responsibilities live downstream in Kernel.ProcessModuleInstance, which the Kernel.SynthesizeInstance wrapper chains onto this call. See package doc for the recommended entry point.
The returned cue.Value carries every schema-derived field automatically: metadata.uuid is computed by uuid.SHA1, components is fanned from the unified module, opm-secrets is added when #Secret instances are present, and the standard module-instance.opmodel.dev/{name,uuid} labels are stamped. Instance stamps only the caller-supplied fields and lets CUE derive the rest.
Was: Release
func Platform ¶ added in v0.3.0
Platform builds a #Platform CUE value by unifying PlatformInput against the #Platform definition obtained from the caller-supplied SchemaCache. It is a peer of Instance.
Unlike Instance, Platform needs no userModule scope dance: #Platform has no nested closed-artifact input — all inputs are plain scalars, maps, and lists — so Platform renders a CUE source string and CompileStrings it with the resolved schema package as cue.Scope (to resolve #Platform / #Subscription references). See design D2.
The returned cue.Value carries the caller-supplied identity and subscription fields and leaves the kernel-filled materialization slots (#composedTransformers, #matchers) unset — those are populated later by Materialize, not by synthesis.
Types ¶
type InstanceInput ¶
type InstanceInput struct {
// Module is the source #Module the instance deploys. Required. Its
// metadata.modulePath / metadata.version identify the published module
// the synthesized package imports (Instance references the module by
// import, not by inlining its value).
Module *module.Module
// Name is the instance name (metadata.name). Required. Must satisfy the
// schema's #NameType regex; violations surface as a CUE unification error
// from Instance.
Name string
// Namespace is the target namespace (metadata.namespace). Required.
Namespace string
// SchemaCache supplies the OPM core schema used to unify against
// #ModuleInstance. REQUIRED. Typically the value of
// kernel.SchemaCache() from the caller's Kernel — passing the
// kernel's cache preserves the one-Cache-per-process invariant and
// avoids a duplicate schema fetch. Instance returns an error when
// this field is nil.
SchemaCache *schema.Cache
// Values is the caller-supplied configuration value unified against the
// module's #config. The zero cue.Value signals "no values supplied" — the
// schema's values path is left unfilled and concreteness is enforced
// downstream by Kernel.ProcessModuleInstance. Instance NEVER falls back to
// Module.debugValues; that is a frontend policy concern.
Values cue.Value
// Labels and Annotations layer over the schema's stamped
// module-instance.opmodel.dev/{name,uuid} labels. CUE unification merges
// caller-supplied entries with schema-stamped ones; caller-supplied keys
// MUST NOT collide with the schema's reserved keys.
Labels map[string]string
Annotations map[string]string
}
InstanceInput is the typed input carried into Instance. Required fields: Module, Name, Namespace, SchemaCache. Optional fields are filled into the instance only when present (non-nil / non-empty / non-zero); empty values do not displace schema-derived fields.
Was: ReleaseInput
type PlatformInput ¶ added in v0.3.0
type PlatformInput struct {
// Name is the platform name (metadata.name). Required. Must satisfy the
// schema's #NameType regex; violations surface as a CUE unification error
// from Platform.
Name string
// Type is the platform type discriminator (the top-level #Platform.type
// field). Required by the schema. Today it is an informational
// discriminator the matcher does not consult.
Type string
// SchemaCache supplies the OPM core schema used to unify against
// #Platform. REQUIRED. Typically the value of kernel.SchemaCache() from
// the caller's Kernel — passing the kernel's cache preserves the
// one-Cache-per-process invariant and avoids a duplicate schema fetch.
// Platform returns an error when this field is nil.
SchemaCache *schema.Cache
// Description, Labels, and Annotations layer onto metadata. Each is
// rendered only when non-empty.
Description string
Labels map[string]string
Annotations map[string]string
// Subscriptions is the optional typed registry of catalog subscriptions.
// The map key is the catalog's CUE module path (e.g.
// "opmodel.dev/catalogs/opm"); a key that violates #ModulePathType
// surfaces as a CUE unification error from Platform. An empty/nil map
// leaves #registry empty.
Subscriptions map[string]SubscriptionSpec
}
PlatformInput is the typed input carried into Platform. Required fields: Name, Type, SchemaCache. Optional fields are rendered into the platform only when present (non-nil / non-empty); empty values do not displace schema-derived fields. It is the #Platform peer of InstanceInput.
type SubscriptionSpec ¶ added in v0.3.0
type SubscriptionSpec struct {
// Enable maps onto #Subscription.enable. It is a pointer so an omitted
// value (nil) DEFERS to the schema's `*true` default rather than forcing
// `false`: nil → schema default, non-nil → the explicit bool. This mirrors
// the unset-vs-supplied distinction synth.Instance draws for its inputs.
Enable *bool
// Version maps onto #Subscription.version — the single build the
// subscription materializes (0010 D14: the platform file IS the
// resolution). REQUIRED: Platform returns an error naming the
// subscription path when it is empty, because the kernel reads this
// scalar — a versionless subscription would fail at materialize.
Version string
}
SubscriptionSpec is the typed form of a single #registry subscription.