kernel

package
v1.0.0-alpha.23 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package kernel exposes the OPM runtime as a single struct, Kernel.

Kernel owns its *cue.Context and its *schema.Cache for its entire lifetime. Construction is New plus two options, WithSchemaLoader and WithRegistry; the kernel exposes no injection slot that no kernel operation reads. Downstream binaries (CLI, controller, Crossplane function) construct one Kernel per goroutine and call methods on it instead of importing the individual loader / module / compile / validate packages.

Goroutine safety

A single Kernel is NOT safe for concurrent use across its own method calls. The owned *cue.Context is driven single-threaded — sharing one Kernel between goroutines can cause data races inside CUE evaluation. Callers that need concurrency MUST construct one Kernel per goroutine.

A *materialize.MaterializedPlatform is owned by the Kernel that built it and is NOT safe to render against from several goroutines at once, whether through one Kernel or many. Kernel.Compile fills each transformer's #transform (FillPath of #moduleInstance, #component and #context), and filling a value is a write to its evaluation state, not a read. Measured against the real catalog (enhancement 0019, experiment 06): 2321 race-detector reports rendering concurrently against one shared platform, 1540 with the platform fully pre-evaluated first. No wrong output was observed; the behaviour is undefined. The earlier "materialize once, render concurrently, no mutex" contract is retracted, and ADR-002 records the supersession.

Until the shares-nothing render model of enhancement 0019 lands (D8: one CUE build per render, in a context that does not outlive the render), a consumer that renders from several goroutines MUST either serialize every use of a materialized platform behind one mutex, or give each goroutine its own Kernel and its own Kernel.Materialize call.

One-Kernel-per-goroutine example

func renderAll(ctx context.Context, paths []string) error {
    var wg sync.WaitGroup
    errs := make(chan error, len(paths))
    for _, p := range paths {
        wg.Add(1)
        go func(path string) {
            defer wg.Done()
            k := kernel.New() // one Kernel per goroutine
            if _, _, err := k.LoadModulePackage(ctx, path, loaderfile.LoadOptions{}); err != nil {
                errs <- err
            }
        }(p)
    }
    wg.Wait()
    close(errs)
    for err := range errs {
        if err != nil {
            return err
        }
    }
    return nil
}

Rendering from several goroutines

Serialize the render path, or materialize per goroutine. The mutex form is the cheaper stopgap while a platform is expensive to materialize; it holds the Kernel that built the platform and the platform itself behind one lock:

var renderMu sync.Mutex // guards k0 and shared together

func renderOne(ctx context.Context, k0 *kernel.Kernel, shared *materialize.MaterializedPlatform, inst *module.Instance) error {
    renderMu.Lock()
    defer renderMu.Unlock()
    _, err := k0.Compile(ctx, kernel.CompileInput{
        ModuleInstance: inst,
        Platform:       shared,
        RuntimeName:    "opm-operator",
    })
    return err
}

The per-goroutine form costs one Materialize (registry I/O) per goroutine and shares nothing, which is the model enhancement 0019 D8 makes the only one.

Phase methods

The kernel exposes two phase-explicit methods that mirror the OPM pipeline. Each accepts a phase-specific input struct and returns a phase-appropriate result:

  • Kernel.Match — component / transformer pairing. Returns *MatchPlan without executing any transformer.
  • Kernel.Compile — full pipeline (Match + Execute). Returns *CompileResult containing rendered values plus provenance. This is the terminal output and the verb every frontend's "apply" / "render" subcommand wants.

Both phases consume the instance as processed: Kernel.ProcessModuleInstance is the validated entry point — it validates user values against the module's `#config` schema (via Kernel.ValidateConfig) and fills them before either phase runs. A caller wanting a dry run calls Match for the pairing diagnosis, or Compile and discards the rendered slice.

Configuration validation

Three primitives form the validation surface:

All three return CUE-native errors. Walk them via cuelang.org/go/cue/errors.Errors / cuelang.org/go/cue/errors.Positions, or print via cuelang.org/go/cue/errors.Print. Presentation belongs to the frontend — the kernel does not ship a formatter.

A caller holding a *module.Module or *module.Instance composes its ConfigSchema() accessor with the primitive it wants, e.g. k.ValidateConfig(m.ConfigSchema(), values).

Advanced: CueContext accessor

Kernel.CueContext returns the underlying *cue.Context for callers that need to build [cue.Value]s outside the kernel (typically tests). Values built with this context are safe to pass back into Kernel methods. Most callers should not need this.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompileInput

type CompileInput struct {
	// ModuleInstance supplies instance-level metadata and components.
	// Required.
	ModuleInstance *module.Instance

	// Platform is the materialized platform whose #composedTransformers and
	// #matchers index drive the matcher. Required. Callers MUST Materialize a
	// *platform.Platform before invoking these phases.
	Platform *materialize.MaterializedPlatform

	// RuntimeName identifies the runtime executing this compile (e.g.
	// "opm-cli", "opm-controller"). MUST be non-empty.
	RuntimeName string
}

CompileInput is the input for Kernel.Compile. The instance artifact is the sole module-side handle and is rendered as processed: values validation and filling happen in Kernel.ProcessModuleInstance, and Compile performs no validation pass of its own.

type CompileResult

type CompileResult = compile.CompileResult

CompileResult is the result of Kernel.Compile.

type Kernel

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

Kernel is the public anchor type for the OPM runtime. It owns a *cue.Context and a *schema.Cache for its lifetime.

Kernel is NOT safe for concurrent use across method calls — see the package documentation for the one-Kernel-per-goroutine pattern.

The Kernel owns exactly one *schema.Cache for its lifetime. Long- running consumers (operator, server) MUST keep the Kernel alive across operations to reuse the in-process schema cache; constructing a fresh Kernel per request pays the schema-fetch cost on every cold disk cache. The CUE module cache on disk is shared across Kernels.

func New

func New(opts ...Option) *Kernel

New constructs a Kernel with default dependencies and applies the supplied options. Defaults are:

New never returns nil. The returned Kernel is NOT safe for concurrent use across method calls.

New does NOT trigger a schema load. The first Kernel method that needs the schema invokes [Cache.Get] internally, which performs the fetch lazily.

func (*Kernel) AcquireModuleFromRegistry

func (k *Kernel) AcquireModuleFromRegistry(ctx context.Context, modPath, version string) (*module.Module, error)

AcquireModuleFromRegistry loads a #Module published in an OCI registry by its major-qualified path (e.g. "example.com/modules/hello@v0") and version (e.g. "v0.0.2"), using the kernel's *cue.Context and configured registry (set via WithRegistry, inheriting CUE_REGISTRY from the process environment when unset). It returns a decoded *module.Module whose staged source (module.Source) is populated, so the module can be reused as the main module of a follow-on build — notably by Kernel.SynthesizeInstance, which stages the instance inside the module's own root so the module's already-tidied cue.mod/module.cue drives transitive dependency resolution. A caller that wants only the raw module value reads Module.Package. See loaderregistry.LoadModulePackageWithSource.

func (*Kernel) Compile

func (k *Kernel) Compile(ctx context.Context, in CompileInput) (*CompileResult, error)

Compile runs the full pipeline (Match + Execute) and returns a *CompileResult containing rendered values, component summaries, unmatched FQNs, and warnings.

The instance is rendered as processed: values validation and filling happen in Kernel.ProcessModuleInstance, and Compile performs no validation pass of its own.

func (*Kernel) CueContext

func (k *Kernel) CueContext() *cue.Context

CueContext returns the *cue.Context owned by this Kernel.

Advanced: most callers do not need this. Use it only when building [cue.Value]s outside the kernel (typically tests or programmatic CUE construction). Values built with this context are safe to pass back into Kernel methods. The same *cue.Context is returned for the lifetime of the Kernel.

func (*Kernel) LoadInstancePackage

func (k *Kernel) LoadInstancePackage(_ context.Context, dirPath string, opts loaderfile.LoadOptions) (cue.Value, error)

LoadInstancePackage loads a #ModuleInstance CUE package from a directory using the kernel's *cue.Context. See loaderfile.LoadInstancePackage.

Was: LoadReleasePackage

func (*Kernel) LoadModulePackage

func (k *Kernel) LoadModulePackage(_ context.Context, dirPath string, opts loaderfile.LoadOptions) (cue.Value, error)

LoadModulePackage loads a module CUE package from a directory using the kernel's *cue.Context. See loaderfile.LoadModulePackage.

func (*Kernel) LoadPlatformPackage

func (k *Kernel) LoadPlatformPackage(_ context.Context, dirPath string, opts loaderfile.LoadOptions) (cue.Value, error)

LoadPlatformPackage loads a #Platform CUE package from a directory using the kernel's *cue.Context. See loaderfile.LoadPlatformPackage.

func (*Kernel) LoadSourceFromBytes

func (k *Kernel) LoadSourceFromBytes(origin, name string, b []byte) (Source, error)

LoadSourceFromBytes compiles b into a Source with cue.Filename(origin) baked into the resulting cue.Value, so that any subsequent validation error positions carry origin via token.Pos.Filename.

Returns an error if compilation fails (the returned Source is the zero value in that case).

func (*Kernel) LoadSourceFromFile

func (k *Kernel) LoadSourceFromFile(path string) (Source, error)

LoadSourceFromFile reads a values file from disk, compiles it via cuelang.org/go/cue/load.Instances (which populates cue.Filename automatically with the file's absolute path), and returns a Source whose Origin matches the absolute path so per-position diagnostics remain consistent with the file the user wrote.

Auto-unwrap: if the loaded value has a top-level `values:` field that exists and reports no error, that field is returned as the Source.Value. Otherwise the whole evaluated file value is returned.

Name defaults to the basename.

func (*Kernel) LoadSourceFromString

func (k *Kernel) LoadSourceFromString(origin, name, s string) (Source, error)

LoadSourceFromString is the [string]-input mirror of Kernel.LoadSourceFromBytes.

func (*Kernel) Match

func (k *Kernel) Match(_ context.Context, in MatchInput) (*MatchPlan, error)

Match produces a *MatchPlan describing matched and non-matched component / transformer pairs. It does NOT execute any transformer.

func (*Kernel) Materialize

Materialize realizes a #Platform's path-keyed catalog subscriptions into a sealed *materialize.MaterializedPlatform, delegating to opm/materialize with the kernel's configured registry (WithRegistry) and owned *cue.Context.

It performs registry I/O (version enumeration + OCI pulls) and is explicit and caller-driven: the kernel holds no materialize cache (Principle I). Long-running consumers that want memoization store the result keyed on an invalidation signal they own; short-lived ones rely on CUE's on-disk module cache.

The phase methods (Match, Compile) accept only the materialized form: callers MUST Materialize a *platform.Platform before invoking either phase.

func (*Kernel) NewInstanceFromValue

func (k *Kernel) NewInstanceFromValue(v cue.Value) (*module.Instance, error)

NewInstanceFromValue builds a typed *module.Instance from a raw cue.Value. See module.NewInstanceFromValue.

Was: NewReleaseFromValue

func (*Kernel) NewModuleFromValue

func (k *Kernel) NewModuleFromValue(v cue.Value) (*module.Module, error)

NewModuleFromValue builds a typed *module.Module from a raw cue.Value. See module.NewModuleFromValue.

func (*Kernel) NewPlatformFromValue

func (k *Kernel) NewPlatformFromValue(v cue.Value) (*platform.Platform, error)

NewPlatformFromValue builds a typed *platform.Platform from a raw cue.Value. See platform.NewPlatformFromValue.

func (*Kernel) ProcessModuleInstance

func (k *Kernel) ProcessModuleInstance(_ context.Context, spec cue.Value, mod module.Module, values cue.Value) (*module.Instance, error)

ProcessModuleInstance validates the supplied values, fills them into the instance spec, asserts the result is fully concrete, decodes instance metadata via opm/schema, and returns a constructed *module.Instance.

The instance spec carries its source #Module inside the CUE package; the schema for value validation is read from spec via schema.Module + schema.Config. mod is supplied for fallback diagnostics (instance name when metadata.name is not yet concrete) and is not retained on the returned Instance — the source module remains reachable through Instance.Package.

values is a single, pre-unified cue.Value — layering is performed by callers via Kernel.ValidateConfigDetailed before this call. The zero cue.Value is treated as "no values supplied": validation is skipped, no fill is performed, and the spec must already be concrete on every required field.

Was: ProcessModuleRelease

func (*Kernel) SchemaCache

func (k *Kernel) SchemaCache() *schema.Cache

SchemaCache returns the *schema.Cache owned by this Kernel. The same pointer is returned for the lifetime of the Kernel; callers MAY hold it across operations to ensure cache reuse.

Calling SchemaCache does NOT trigger a schema load. Only the first schema.Cache.Get invocation contacts CUE; the load is lazy and memoized.

Typical use: pass to synth.InstanceInput.SchemaCache before calling instance synthesis, or read schema.Cache.ResolvedVersion for diagnostics after a schema-touching operation has run.

func (*Kernel) SynthesizeInstance

func (k *Kernel) SynthesizeInstance(ctx context.Context, in synth.InstanceInput) (*module.Instance, error)

SynthesizeInstance builds a *module.Instance from typed in-memory inputs. This is the recommended entry point for callers that hold a Module and need a fully validated instance — it mirrors how Kernel.LoadInstancePackage is the recommended entry point for the file-driven path.

SynthesizeInstance chains synth.Instance (which unifies inputs against the version binding's #ModuleInstance schema and lets CUE derive uuid, components, auto-secrets, and standard labels) into Kernel.ProcessModuleInstance (which validates supplied values against the module's #config, fills them into the spec, enforces concreteness, and decodes instance metadata).

The Kernel's *cue.Context threads through both steps so the resulting *module.Instance.Package is reachable through cue lookups using the same runtime. Callers that explicitly want the helper-level primitive — for example, a test that wants the spec value before concreteness enforcement — should call synth.Instance directly with Kernel.CueContext and then invoke Kernel.ProcessModuleInstance themselves.

in.Values is passed through to Kernel.ProcessModuleInstance unchanged. The zero cue.Value means "no values supplied"; Kernel.ProcessModuleInstance then fails the concreteness check unless every #config field has a default. synth.Instance never falls back to Module.debugValues — frontends that want a debug-values overlay layer it on the caller side.

Was: SynthesizeRelease

func (*Kernel) SynthesizePlatform added in v0.3.0

func (k *Kernel) SynthesizePlatform(_ context.Context, in synth.PlatformInput) (*platform.Platform, error)

SynthesizePlatform builds a *platform.Platform from typed in-memory inputs. This is the recommended entry point for callers that hold platform configuration as typed data — an operator reconciling a Platform CRD spec, a CLI assembling subscriptions from flags — and is the synthesis peer of Kernel.LoadPlatformPackage on the file-driven path.

SynthesizePlatform chains synth.Platform (which unifies inputs against the resolved #Platform schema) into platform.NewPlatformFromValue (which decodes the platform metadata and stores the value as the platform's Package). It returns the pre-materialize *platform.Platform twin.

It does NOT call Kernel.Materialize: resolving the platform's #registry subscriptions into a *MaterializedPlatform performs registry I/O and stays an explicit, separate, caller-driven step (Principle I / design D14). The returned Package carries #registry as authored with #composedTransformers / #matchers unset.

The Kernel owns the schema cache; callers MUST NOT need to thread it through explicitly. If in.SchemaCache is set it is honored (a test may pin a different one), otherwise it defaults to the kernel-owned cache.

ctx is unused today — synthesis touches no I/O the caller could cancel (synth.Platform uses the Kernel's *cue.Context; NewPlatformFromValue takes none). It is part of the signature for parity with Kernel.SynthesizeInstance and so a future materialize-aware variant can honor cancellation without an API break. Keep it.

func (*Kernel) ValidateConfig

func (k *Kernel) ValidateConfig(schema cue.Value, values cue.Value) (cue.Value, error)

ValidateConfig is the kernel's primitive Tier-2 validation entry point. It unifies values with schema, runs the closed-schema disallowed-field walk, and asserts concreteness via cue.Concrete(true).

Returns the unified cue.Value on success and the zero value on failure. The returned error is the raw CUE error tree; walk it via cuelang.org/go/cue/errors.Errors / cuelang.org/go/cue/errors.Positions, or print it via cuelang.org/go/cue/errors.Print. Presentation is outside the kernel's contract — frontends own their own formatting.

values is a single, pre-merged cue.Value. Layered inputs flow through Kernel.ValidateConfigDetailed; partial-mode validation flows through Kernel.ValidateConfigPartial. Module-name framing is the caller's responsibility — wrap with fmt.Errorf if a context prefix is required.

The zero cue.Value is treated as "no values": ValidateConfig returns (zero, nil) without running any schema check.

func (*Kernel) ValidateConfigDetailed

func (k *Kernel) ValidateConfigDetailed(schema cue.Value, sources []Source, opts ...ValidateOption) (cue.Value, error)

ValidateConfigDetailed unifies an ordered slice of Source values, then validates the merged value against schema. Per-source attribution flows through token.Pos.Filename, populated from cue.Filename(Origin) at the time each Source.Value was compiled — see Kernel.LoadSourceFromFile, Kernel.LoadSourceFromBytes, and Kernel.LoadSourceFromString for constructors that bake the filename for you.

Without options the merged value must be concrete (every required field set). With Partial in opts, concreteness is not enforced; the merged value is checked only for type errors, constraint violations, and disallowed fields under closed schemas.

Returns the merged cue.Value on success and the zero value on failure. The returned error is the raw CUE error tree; walk it via cuelang.org/go/cue/errors.Errors and cuelang.org/go/cue/errors.Positions, or print it via cuelang.org/go/cue/errors.Print. Presentation is outside the kernel's contract — frontends own their own formatting.

Empty sources, a zero schema, or a merged value that does not exist all short-circuit to (zero, nil) — the "no values supplied" path documented across the kernel's validation surface.

func (*Kernel) ValidateConfigPartial

func (k *Kernel) ValidateConfigPartial(schema cue.Value, values cue.Value) (cue.Value, error)

ValidateConfigPartial validates partial values against schema without requiring every schema field to be concrete. It catches type errors, disallowed fields, and pattern/regex violations on fields that ARE set, but does NOT flag fields that are missing entirely.

Used for CLI lint subcommands, IDE/LSP live feedback, admission webhooks, and any callsite that intentionally validates a draft slice of the full configuration.

The zero cue.Value (no values) is treated as success.

type MatchInput

type MatchInput struct {
	// ModuleInstance supplies the components value via
	// [module.Instance.MatchComponents]. Required.
	ModuleInstance *module.Instance

	// Platform is the materialized platform whose #composedTransformers and
	// #matchers index drive the matcher. Required. Callers MUST Materialize a
	// *platform.Platform before invoking these phases.
	Platform *materialize.MaterializedPlatform
}

MatchInput is the input for Kernel.Match. The instance artifact is the sole module-side handle and is matched as processed: values validation and filling happen in Kernel.ProcessModuleInstance before either phase runs.

type MatchPlan

type MatchPlan = compile.MatchPlan

MatchPlan is the result of Kernel.Match.

type Option

type Option func(*Kernel)

Option configures a Kernel at construction time. Options compose via the functional-options pattern; new options can be added in MINOR instances without breaking existing call sites. The provided options are WithSchemaLoader and WithRegistry; the Kernel exposes no injection slot that no kernel operation reads.

func WithRegistry

func WithRegistry(registry string) Option

WithRegistry sets the OCI registry mapping (CUE_REGISTRY syntax, e.g. "opmodel.dev=ghcr.io/open-platform-model") used for catalog resolution during Kernel.Materialize. The materialize flow uses the same mapping when it resolves opmodel.dev/core for the schema.

Omitting this option (or passing an empty string) inherits CUE_REGISTRY from the process environment; the kernel applies no built-in default registry — the same stance as the schema loader. The mapping is never written back to the process environment; it is plumbed into the load configuration for the operation only.

func WithSchemaLoader

func WithSchemaLoader(l schema.Loader) Option

WithSchemaLoader configures the schema.Loader used to populate the kernel's *schema.Cache. Omitting this option defaults to a zero-value schema.OCILoader that resolves opmodel.dev/core@v2 via CUE_REGISTRY / CUE_CACHE_DIR from the process environment.

The Kernel wraps the supplied Loader in a fresh Cache; callers cannot inject a pre-built Cache. This guarantees one Kernel = one Cache, so no two Kernels accidentally share memoization. Multi-Kernel cache sharing is intentionally not exposed and may be added later as a non-breaking addition.

A nil Loader is ignored (the default OCILoader applies).

type Source

type Source struct {
	// Value is the raw values payload for this source.
	//
	// Value MUST have been compiled with [cue.Filename](Origin) for
	// per-source attribution to flow into errors. Use one of
	// [Kernel.LoadSourceFromFile], [Kernel.LoadSourceFromBytes], or
	// [Kernel.LoadSourceFromString] to construct a Source whose Value
	// satisfies this contract automatically. Hand-built Sources MUST set
	// the filename themselves when compiling.
	Value cue.Value

	// Name is the human-friendly label shown in UI. Examples:
	// "user values", "ConfigMap/foo", "instance overlay".
	Name string

	// Origin is the stable identifier for machine-readable correlation
	// (file path, K8s object reference, composition input key). It MUST
	// match the [cue.Filename] used when Value was compiled, so error
	// positions report Origin via [token.Pos.Filename].
	Origin string
}

Source is one labeled values input for Kernel.ValidateConfigDetailed.

A Source pairs a values payload with caller-supplied identity so that per-position diagnostics flowing out of CUE error trees carry the originating source's filename. The library does not invent a Go-typed wrapper around CUE's error attribution — instead, it relies on token.Pos.Filename, populated from cue.Filename at compile time.

type ValidateOption

type ValidateOption func(*validateConfig)

ValidateOption configures Kernel.ValidateConfigDetailed. Options compose via the functional-options pattern; new options can be added in MINOR instances without breaking existing call sites.

The type is named ValidateOption (rather than the more terse Option) to avoid collision with Option, the kernel-construction option type.

func Partial

func Partial() ValidateOption

Partial returns a ValidateOption that skips the concreteness check on the merged value passed to Kernel.ValidateConfigDetailed.

With Partial: type errors, constraint violations on fields that ARE set, and disallowed-field errors (under closed schemas) all still surface. Missing required fields do NOT surface — concrete validation is the only pass that flags them, and Partial disables it.

Use Partial for callers that intentionally validate incomplete drafts: CLI lint subcommands, IDE/LSP live feedback, admission webhooks that see only one of several values sources.

Jump to

Keyboard shortcuts

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