The Apply Engine
The apply engine turns a manifest into a sequence of applied resources. It resolves the manifest from a file, an HTTP tarball, or a NATS object store; layers Hiera data and overrides over it; builds each resource; and runs them in declaration order, recording an event after each so later resources can see what earlier ones did.
Where it lives
resources/apply: the manifest parser and executor. resources/applyresource and
resources/applyresource/ccmmanifest: the apply meta-resource that lets one manifest apply
another. Key files: resources/apply/apply.go, resources/apply/jet.go,
resources/apply/validation.go, resources/applyresource/ccmmanifest/ccmmanifest.go.
Resolving a manifest
ResolveManifestReader (resources/apply/apply.go:395) is the core resolver. It runs before
any resource executes, and its ordering is precise: data is resolved and templates are
expanded before the schema is checked, so a runtime template does not fail a structural
validation.
- Resolve the source
ResolveManifestUrldispatches on scheme:obj://to the object store,http(s)to a tarball fetch, empty scheme to a local file. Archive paths untar, findmanifest.yaml, and set the working directory. - Parse the manifest Unmarshal the top-level
data,hierarchy, andoverrides, plus theccmblock withpre_message,post_message,fail_on_error,resources, andresources_jet_file. - Resolve Hiera
hiera.ResolveYamlconsumeshierarchy.order,merge, andoverrides, returning the resolved data and validation rules. Overriding data is deep-merged on top, then the rules are enforced. - Publish data
mgr.SetDatastores the resolved data and the template environment is built from it, so resource fields can referenceData. - Produce the resource list Either the inline
ccm.resources, or, ifresources_jet_fileis set, the rendered output of a Jet template. Multi-name blocks are flattened in place, preserving order. - Parse and template each resource
NewValidatedResourcePropertiesFromYamlbuilds typed properties per type, resolves templates, and validates. - Validate against the schema Last, the resolved payload is checked against
schemas/manifest.json, substituting placeholders for still-deferred template fields.NO_SCHEMA_VALIDATION=1bypasses this.
Execution and ordering
Execute (resources/apply/apply.go:603) opens a session, then iterates the resources. For
each it builds the concrete resource through the ResourceFactory, calls Apply or
Healthcheck, logs the result, records the event, and publishes any register_when_stable
entries. When fail_on_error is set, a failed resource stops the run after the current entry.
Load-bearing decision
There is no dependency graph and no topological sort. Resources run in manifest declaration
order. require is a fail-gate, not a scheduler: a resource whose required references failed
or were themselves skipped is skipped, not reordered. Authors must place producers before
consumers. This is the single most important invariant of the engine.
Cross-resource behavior is stateful through the session. Because Execute records each event
immediately, IsResourceFailed and ShouldRefresh inspect the last event for a reference, so
a later resource sees an earlier one’s change. This is again why declaration order matters.
Generating resources with Jet
When resources_jet_file is set instead of inline resources, jetParseManifestResources
(resources/apply/jet.go:19) renders a Jet template with the delimiters [[ and ]], chosen
so they do not collide with the {{ }} templating used for scalar fields. The resolved Hiera
Data is in scope, so a template can loop over Data.packages to emit a resource per item.
The rendered YAML is then parsed exactly like inline resources. Jet generates the resource
list; ordinary templating fills in individual fields.
Nested applies
The apply resource type lets one manifest apply another. Its sole provider,
ccmmanifest.Provider (resources/applyresource/ccmmanifest/ccmmanifest.go:31), snapshots the
manager’s noop, data, and working directory, runs the child inside the parent’s session so
events are shared, and restores the manager afterward.
Load-bearing decision
A nested apply can only strengthen execution, never relax it. Noop is turned on only if the
parent is not already noop and the child requests it; health-check-only is the OR of parent and
child. A child cannot escape the parent’s noop or health-check state. A parent can also forbid
nested applies entirely with WithDenyApplyResources unless the child sets AllowApply.
Recursion is bounded by DefaultMaxRecursionDepth = 10. Note that the depth guard is not fully
threaded through the apply-resource boundary today: Type.ApplyResource calls ApplyManifest
with a hardcoded depth of 0, so the guard is effectively aspirational for deeply nested
manifests. Two TODOs in the executor also flag that a resource-factory error currently aborts
the whole run rather than recording a failed event, and that resource dispatch should move into
the registry.
Next
Continue to The Agent to see how the engine is driven continuously on a timer.