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.

  1. Resolve the source ResolveManifestUrl dispatches on scheme: obj:// to the object store, http(s) to a tarball fetch, empty scheme to a local file. Archive paths untar, find manifest.yaml, and set the working directory.
  2. Parse the manifest Unmarshal the top-level data, hierarchy, and overrides, plus the ccm block with pre_message, post_message, fail_on_error, resources, and resources_jet_file.
  3. Resolve Hiera hiera.ResolveYaml consumes hierarchy.order, merge, and overrides, returning the resolved data and validation rules. Overriding data is deep-merged on top, then the rules are enforced.
  4. Publish data mgr.SetData stores the resolved data and the template environment is built from it, so resource fields can reference Data.
  5. Produce the resource list Either the inline ccm.resources, or, if resources_jet_file is set, the rendered output of a Jet template. Multi-name blocks are flattened in place, preserving order.
  6. Parse and template each resource NewValidatedResourcePropertiesFromYaml builds typed properties per type, resolves templates, and validates.
  7. Validate against the schema Last, the resolved payload is checked against schemas/manifest.json, substituting placeholders for still-deferred template fields. NO_SCHEMA_VALIDATION=1 bypasses this.
Sourceobj · http · fileParseHiera+ overridesResource listValidateExecute · sessionBuild resourceApply / checkRecord event+ publishnext resource · declaration orderfail_on_error: stop
Resolve once, then loop the resources in declaration order, recording each event before the next runs.

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.