Help us shape the future of Go! Take the 2026 Go Developer Survey

diffprocessor

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: Apache-2.0 Imports: 46 Imported by: 0

Documentation

Overview

Package diffprocessor contains the logic to calculate and render diffs.

Index

Constants

View Source
const (
	// ExitCodeSuccess indicates no diff and no errors.
	ExitCodeSuccess = 0

	// ExitCodeToolError indicates a tool execution error (e.g., kube access, internal failure).
	ExitCodeToolError = 1

	// ExitCodeSchemaValidation indicates a schema validation error.
	ExitCodeSchemaValidation = 2

	// ExitCodeDiffDetected indicates that differences were detected.
	ExitCodeDiffDetected = 3

	// ExitCodeInterrupted indicates the run was cancelled by SIGINT (Ctrl+C) or SIGTERM. It is 130 by
	// the shell convention for SIGINT (128 + 2); the signal handler does not report which signal
	// arrived, so SIGTERM uses the same code.
	ExitCodeInterrupted = 130
)

Exit codes for crossplane-diff CLI.

View Source
const (
	// LabelComposite identifies the root composite resource that owns a composed resource.
	LabelComposite = "crossplane.io/composite"
	// LabelClaimName identifies the claim that triggered creation of the composite.
	LabelClaimName = "crossplane.io/claim-name"
	// LabelClaimNamespace identifies the namespace of the claim.
	LabelClaimNamespace = "crossplane.io/claim-namespace"
)

Crossplane label keys used for resource identity and ownership.

View Source
const CleanupTimeout = 30 * time.Second

CleanupTimeout bounds how long releasing the processor's Docker resources may take. Cleanup is attempted on a context detached from the run's cancellation (see CleanupDetached), so this is what stops a slow or hung Docker daemon from blocking a run indefinitely.

View Source
const DefaultMaxRenderIterations = 20

DefaultMaxRenderIterations is the default maximum render iterations.

View Source
const EnvDockerNetwork = "CROSSPLANE_DIFF_DOCKER_NETWORK"

EnvDockerNetwork is the environment variable that specifies which Docker network the crossplane-render container and function containers should join. NewEngineRenderFn reads this once and routes the value through render.EngineFlags.CrossplaneDockerNetwork; the upstream docker engine then both runs the render container on that network and annotates fns to join it at Setup time. This is needed when crossplane-diff runs inside a Docker container (e.g. a GitHub Actions container job).

View Source
const EnvMaxRecvMessageSize = "MAX_RECV_MESSAGE_SIZE"

EnvMaxRecvMessageSize is the container env var crossplane-diff injects to raise a function's gRPC max receive size. It is intentionally unprefixed and function-agnostic, mirroring the SDK-wide TLS_SERVER_CERTS_DIR convention shared by crossplane composition functions: functions that bind it honor it, functions that don't simply ignore it.

View Source
const FieldErrorTypeAdmission = "admission"

FieldErrorTypeAdmission is the FieldValidationError.Type for a rejection issued by the apiserver's admission chain rather than by local schema validation. It deliberately sits alongside upstream's "schema" / "cel" / "unknownField" / "defaulting" values in the same field, because to a consumer asking "why won't the cluster accept this?" it is the same kind of answer.

View Source
const MinCrossplaneRenderVersion = "v2.3.4"

MinCrossplaneRenderVersion is the minimum crossplane render version that crossplane-diff supports. Renders against older versions silently drop cluster-observed composed resources (their controller-ref UIDs no longer match the XR the way this tool assembles them), producing incorrect diffs — see crossplane-contrib/crossplane-diff#399. The value corresponds to the upstream release that preserves a non-empty input XR UID and validates observed resources (crossplane/crossplane#7544).

Variables

View Source
var ErrInterrupted = errors.New("run interrupted by signal; results are incomplete")

ErrInterrupted is the error an interrupted run reports, in errors[] and on stderr, in place of the cancelled calls the interruption caused.

Functions

func CleanupDetached added in v0.11.0

func CleanupDetached(ctx context.Context, c Cleaner, logger logging.Logger)

CleanupDetached releases c's resources on a context derived from ctx that keeps ctx's values but not its cancellation or deadline, bounded instead by CleanupTimeout. Cleanup must still run when the run's own context has expired (--timeout), since Docker calls on a dead context fail and leave containers running; and it must not hang on a slow or wedged Docker daemon. A failure is logged, not returned: releasing resources is best-effort and must not change the run's result.

This is also what lets cleanup finish after Ctrl+C or SIGTERM: the first signal cancels the run's context (see the command layer's run context) rather than killing the process. A second signal exits immediately and may leave containers behind, which the next run reclaims (see dockerrun).

func CopyLabels added in v0.5.0

func CopyLabels(source, target *un.Unstructured, keys ...string)

CopyLabels copies specified labels from source to target if they exist in source. This is a no-op if source has no labels or if none of the specified keys exist.

func DetermineExitCode added in v0.6.0

func DetermineExitCode(err error, hasDiffs bool) int

DetermineExitCode determines the appropriate exit code based on the error and diff status. Priority: interrupted (130) > tool error (1) > schema validation error (2) > diff detected (3) > success (0). An interruption outranks everything because the other errors of an interrupted run are mostly the cancelled calls it caused.

func Interrupted added in v0.11.0

func Interrupted(ctx context.Context) bool

Interrupted reports whether the run context ctx was cancelled by a signal. The run context is a timeout context on the signal context, so an expired --timeout yields context.DeadlineExceeded and a signal yields context.Canceled. The run's own cancel also yields Canceled, but only fires once the run is over, so callers must evaluate this before it.

func IsSchemaValidationError added in v0.6.0

func IsSchemaValidationError(err error) bool

IsSchemaValidationError checks if any error in the chain is a schema validation error.

func NewOutputError added in v0.8.0

func NewOutputError(resourceID string, err error) dt.OutputError

NewOutputError builds a structured-output entry for err, tagged with resourceID. When err contains a *SchemaValidationError that carries either a pkgvalidate.ValidationResult or pre-built Failures, the returned OutputError also exposes a typed per-resource breakdown via ValidationFailures so machine consumers don't need to parse Message. Non-validation errors return an OutputError with only ResourceID and Message populated.

Explicit Failures win over Result. The two are never both set in practice — one comes from pkg/validate, the other from an apiserver rejection — but preferring the explicit list means a caller that sets it cannot have it silently dropped.

func NormalizeRenderVersion added in v0.11.0

func NormalizeRenderVersion(version string) string

NormalizeRenderVersion returns version with a leading "v" when it parses as a semantic version without one, and version unchanged otherwise.

ValidateMinRenderVersion deliberately accepts a bare "2.3.4", but upstream formats the pinned version into the image tag verbatim (…/crossplane:<version>) and publishes only v-prefixed tags, so without this the bare form validates and then dies at pull time as an unpullable image. The original string is prefixed rather than reformatted from the parsed value, so prerelease and build metadata survive byte-for-byte. Tags that are not semantic versions ("stable", "main") are real, pullable tags and are left alone.

func UncomparableRenderImageWarning added in v0.11.0

func UncomparableRenderImageWarning(image string) string

UncomparableRenderImageWarning returns advisory text when image is a reference whose version cannot be compared against MinCrossplaneRenderVersion, or "" for an empty image (no override was requested) or one that ValidateMinRenderImage could check. Rendering with an image below the minimum silently drops cluster-observed composed resources, which shrinks the rendered set and surfaces as spurious removals, so an unverifiable reference is worth saying out loud even though it cannot be rejected.

func ValidateMinRenderImage added in v0.11.0

func ValidateMinRenderImage(image string) error

ValidateMinRenderImage returns an error if the tag of a full crossplane render image reference parses as a semantic version older than MinCrossplaneRenderVersion.

A reference whose version is not comparable — pinned by digest, tagged with a floating name like "stable", or carrying no tag at all — is accepted: refusing it would break the mirrored and air-gapped registries --crossplane-image exists to serve, and a digest is opaque by construction. Those references are reported by UncomparableRenderImageWarning instead, so the floor stays visible without becoming a false positive.

func ValidateMinRenderVersion added in v0.9.0

func ValidateMinRenderVersion(version string) error

ValidateMinRenderVersion returns an error if version parses to a semantic version older than MinCrossplaneRenderVersion, or if it cannot be parsed as a semantic version at all. A leading "v" is accepted (and optional — see NormalizeRenderVersion, which is what makes the bare form usable rather than merely accepted). It is intended for validating an explicitly pinned --crossplane-version; a full --crossplane-image reference goes through ValidateMinRenderImage instead, and the floating :stable default carries no comparable version at all.

Types

type AnalyzeOn added in v0.11.0

type AnalyzeOn string

AnalyzeOn names the smallest composition change that triggers per-composite impact analysis. The values are ordered from least to most eager.

const (
	// AnalyzeOnSpecChange evaluates composites only when the composition's spec changes. The
	// cheapest setting, and a deliberate user judgement: a metadata-only change still produces a new
	// CompositionRevision, whose identity a composition template can observe through the XR's
	// compositionRevisionRef, so choosing this asserts that none of your compositions do that.
	AnalyzeOnSpecChange AnalyzeOn = "spec-change"

	// AnalyzeOnAnyChange evaluates composites whenever anything Crossplane hashes into the
	// composition's identity differs — labels and annotations as well as spec. The default, because
	// whether a metadata-only change reaches rendered output can only be settled by rendering.
	AnalyzeOnAnyChange AnalyzeOn = "any-change"

	// AnalyzeOnAlways evaluates composites even when the composition is identical, which creates no
	// revision and so cannot change anything. Useful as a pre-edit "is my cluster converged?"
	// baseline; note that any delta it reports is by construction not caused by this composition.
	AnalyzeOnAlways AnalyzeOn = "always"
)

type CRDDefaulter added in v0.11.0

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

CRDDefaulter implements Defaulter from the CRDs a SchemaClient knows.

func (*CRDDefaulter) Default added in v0.11.0

func (d *CRDDefaulter) Default(ctx context.Context, obj *un.Unstructured) (*un.Unstructured, string, error)

Default returns a copy of obj with its CRD's defaults applied, and the CRD's storage version when it is not obj's. What happens when no CRD can be found depends on the policy. A CRD that is found but does not define obj's apiVersion is an error under either policy: there is nothing to predict from, and returning obj as though defaulted would hide it.

type CachedFunctionProvider added in v0.5.0

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

CachedFunctionProvider lazy-loads functions and caches them by composition name. This is appropriate for the comp command, where many XRs use the same composition: the cluster is asked for a composition's functions once, however many XRs render it. The function containers themselves are started, reused across renders and removed by the render engine, which names them for the run that owns them (see EngineRenderFn).

func (*CachedFunctionProvider) Cleanup added in v0.5.0

Cleanup is a no-op for CachedFunctionProvider: the render engine owns the containers.

func (*CachedFunctionProvider) GetFunctionsForComposition added in v0.5.0

func (p *CachedFunctionProvider) GetFunctionsForComposition(comp *apiextensionsv1.Composition) ([]pkgv1.Function, error)

GetFunctionsForComposition fetches and caches functions on first call per composition.

type ChangeScope added in v0.11.0

type ChangeScope string

ChangeScope is how much of a composition differs from its in-cluster version, in terms of what Crossplane hashes into the composition's identity (see Composition.Hash()).

const (
	// ChangeScopeNone means nothing Crossplane hashes differs. No CompositionRevision is created, so
	// nothing can adopt anything.
	ChangeScopeNone ChangeScope = "none"

	// ChangeScopeMetadata means the labels or annotations differ but the spec does not. A new
	// CompositionRevision is still created, carrying an identical spec.
	ChangeScopeMetadata ChangeScope = "metadata"

	// ChangeScopeSpec means the spec differs.
	ChangeScopeSpec ChangeScope = "spec"
)

type Cleaner added in v0.11.0

type Cleaner interface {
	Cleanup(ctx context.Context) error
}

Cleaner is anything holding resources that CleanupDetached can release. DiffProcessor, CompDiffProcessor and FunctionProvider all embed it.

type CompDiffProcessor added in v0.2.0

type CompDiffProcessor interface {
	// DiffComposition processes composition changes and shows impact on existing XRs.
	// Returns (hasDiffs, error) where hasDiffs indicates if any differences were detected.
	// When `resources` is non-empty, impact analysis is restricted to the named composites:
	// each ref is resolved against every supplied composition's (XR GVK, claim GVK) pair via a
	// preflight pass. If any ref is relevant to no supplied composition, the call fails before
	// rendering any diffs (CLI input error). When `resources` is empty, behavior is unchanged.
	DiffComposition(ctx context.Context, compositions []*un.Unstructured, namespace string, resources []k8stypes.NamespacedName) (bool, error)
	Initialize(ctx context.Context) error
	// Cleaner releases any resources held by the processor (e.g., Docker containers).
	Cleaner
}

CompDiffProcessor defines the interface for composition diffing.

func NewCompDiffProcessor added in v0.2.0

func NewCompDiffProcessor(xrProc DiffProcessor, compositionClient xp.CompositionClient, opts ...ProcessorOption) CompDiffProcessor

NewCompDiffProcessor creates a new DefaultCompDiffProcessor.

type ComponentFactories

type ComponentFactories struct {
	// ResourceManager creates a ResourceManager
	ResourceManager func(client k8.ResourceClient, defClient xp.DefinitionClient, treeClient xp.ResourceTreeClient, logger logging.Logger) ResourceManager

	// SchemaValidator creates a SchemaValidator
	SchemaValidator func(schema k8.SchemaClient, resource k8.ResourceClient, def xp.DefinitionClient, logger logging.Logger) SchemaValidator

	// Defaulter creates a Defaulter with the given policy. The processor makes two: a strict one for the
	// XR it renders, and a lenient one the DiffCalculator uses to predict additions.
	Defaulter func(schema k8.SchemaClient, def xp.DefinitionClient, policy DefaultingPolicy) Defaulter

	// DiffCalculator creates a DiffCalculator
	DiffCalculator func(apply k8.ApplyClient, access k8.AccessChecker, tree xp.ResourceTreeClient, resourceManager ResourceManager, logger logging.Logger, diffOptions renderer.DiffOptions, dryRunOn DryRunOn, predictor Defaulter) DiffCalculator

	// DiffRenderer creates a DiffRenderer
	DiffRenderer func(logger logging.Logger, diffOptions renderer.DiffOptions) renderer.DiffRenderer

	// CompDiffRenderer creates a CompDiffRenderer for composition diffs
	CompDiffRenderer func(logger logging.Logger, diffRenderer renderer.DiffRenderer, opts renderer.DiffOptions) renderer.CompDiffRenderer

	// RequirementsProvider creates an ExtraResourceProvider
	RequirementsProvider func(res k8.ResourceClient, def xp.EnvironmentClient, logger logging.Logger) *RequirementsProvider

	// FunctionProvider creates a FunctionProvider
	FunctionProvider func(fnClient xp.FunctionClient, logger logging.Logger) FunctionProvider

	// InputValidator creates the InputValidator for one PerformDiff run
	InputValidator InputValidatorFactory
}

ComponentFactories contains factory functions for creating processor components.

type CompositeResolver added in v0.11.0

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

CompositeResolver computes the effective composite: what Crossplane would actually reconcile for a composite as supplied, given what the cluster already holds. It is computed once per composite, before the composition is resolved, so that composition resolution and render agree on which revision, composition and update policy apply.

func NewCompositeResolver added in v0.11.0

func NewCompositeResolver(resources ResourceManager, defs xp.DefinitionClient, logger logging.Logger) *CompositeResolver

NewCompositeResolver creates a CompositeResolver.

func (*CompositeResolver) Resolve added in v0.11.0

func (r *CompositeResolver) Resolve(ctx context.Context, authored *cmp.Unstructured, existing *un.Unstructured) (EffectiveComposite, error)

Resolve computes the effective composite for authored. existing is authored's cluster copy when the caller already has it (a nested XR, found among its parent's observed resources); otherwise it is nil and Resolve fetches it.

Fetch errors are fatal: a composite resolved without the cluster state it depends on would be rendered against the wrong revision, composition or observed resources, and the diff would be silently wrong.

type DefaultCompDiffProcessor added in v0.2.0

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

DefaultCompDiffProcessor implements CompDiffProcessor.

func (*DefaultCompDiffProcessor) Cleanup added in v0.7.0

Cleanup releases any resources held by the processor. Delegates to the underlying XR processor for cleanup.

func (*DefaultCompDiffProcessor) DiffComposition added in v0.2.0

func (p *DefaultCompDiffProcessor) DiffComposition(ctx context.Context, compositions []*un.Unstructured, namespace string, resources []k8stypes.NamespacedName) (bool, error)

DiffComposition processes composition changes and shows impact on existing XRs. Returns (hasDiffs, error) where hasDiffs indicates if any differences were detected.

func (*DefaultCompDiffProcessor) Initialize added in v0.2.0

func (p *DefaultCompDiffProcessor) Initialize(ctx context.Context) error

Initialize loads required resources.

type DefaultDiffCalculator

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

DefaultDiffCalculator implements the DiffCalculator interface.

func (*DefaultDiffCalculator) CalculateDiff

func (c *DefaultDiffCalculator) CalculateDiff(ctx context.Context, composite *un.Unstructured, desired *un.Unstructured) (*dt.ResourceDiff, error)

CalculateDiff calculates the diff for a single resource.

func (*DefaultDiffCalculator) CalculateDiffs

CalculateDiffs computes all diffs including removals for the rendered resources. This is the primary method that most code should use.

func (*DefaultDiffCalculator) CalculateNonRemovalDiffs added in v0.5.0

CalculateNonRemovalDiffs computes diffs for modified/added resources and returns the set of rendered resource keys for removal detection.

TWO-PHASE DIFF ALGORITHM: This method implements Phase 1 of our two-phase diff calculation. The two-phase approach is necessary to correctly handle nested XRs (Composite Resources that themselves create other Composite Resources).

WHY TWO PHASES? When processing nested XRs, we must:

  1. Phase 1 (this method): Calculate diffs for all rendered resources (adds/modifications) and build a set of "rendered resource keys" that tracks what was generated
  2. Phase 2 (CalculateRemovedResourceDiffs): Compare cluster state against rendered resources to identify removals

The separation is critical because:

  • Nested XRs are processed recursively BETWEEN these phases
  • Nested XRs generate additional composed resources that must be added to the "rendered resources" set before removal detection
  • If we detected removals too early, we'd falsely identify nested XR resources as "to be removed" before they've been processed

EXAMPLE SCENARIO:

Parent XR renders: [Resource-A, NestedXR-B]
NestedXR-B renders: [Resource-C, Resource-D]

Without two phases:
  - We'd see cluster has [Resource-A, Resource-C, Resource-D] from prior render
  - We'd see new render has [Resource-A, NestedXR-B]
  - We'd INCORRECTLY mark Resource-C and Resource-D as removed

With two phases:
  Phase 1: Calculate diffs for [Resource-A, NestedXR-B], track as rendered
  Process nested: Recurse into NestedXR-B, add Resource-C and Resource-D to rendered set
  Phase 2: Now see [Resource-A, NestedXR-B, Resource-C, Resource-D] as rendered
           No false removal detection!

What it records as rendered is keyed for both of its consumers; see dtypes.RenderedResources. Each composed resource is recorded as desired by desired.CompositeResource, the composite render consumed (for a claim, its backing XR, not the claim xr is), because that is the composite that composes it.

Returns: (diffs map, rendered resources, error).

func (*DefaultDiffCalculator) CalculateRemovedResourceDiffs

func (c *DefaultDiffCalculator) CalculateRemovedResourceDiffs(ctx context.Context, xr *un.Unstructured, rendered dtypes.RenderedResources) (map[string]*dt.ResourceDiff, error)

CalculateRemovedResourceDiffs identifies the composed resources Crossplane would garbage collect, and calculates their removal diffs.

It applies Crossplane's own rule (crossplane v2.4.2, internal/controller/apiextensions/composite/ composition_functions.go). ExistingComposedResourceObserver.ObserveComposedResources observes each resource in a composite's resourceRefs, keyed by its composition resource name, except one another controller owns. DeletingComposedResourceGarbageCollector.GarbageCollectComposedResources then deletes each observed resource whose name is not among the names the composite's render desired, unless it has no controller reference. Matching is by composition resource name within the composite, never by API version or object name: a resource rendered at a new apiVersion is updated in place (#552).

The walk covers nested XRs' resources too, each matched against the composite it hangs from in the tree, which is the one that composed it. A composite that was not rendered, such as a nested XR being removed, desires nothing, so what it composed is removed with it.

func (*DefaultDiffCalculator) SetDiffOptions

func (c *DefaultDiffCalculator) SetDiffOptions(options renderer.DiffOptions)

SetDiffOptions updates the diff options used by the calculator.

type DefaultDiffProcessor

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

DefaultDiffProcessor implements DiffProcessor with modular components.

func (*DefaultDiffProcessor) Cleanup added in v0.7.0

func (p *DefaultDiffProcessor) Cleanup(ctx context.Context) error

Cleanup releases any resources held by the processor. This includes stopping and removing any Docker containers created for function execution.

func (*DefaultDiffProcessor) DiffSingleResource

func (p *DefaultDiffProcessor) DiffSingleResource(ctx context.Context, res *un.Unstructured, compositionProvider types.CompositionProvider) (map[string]*dt.ResourceDiff, error)

DiffSingleResource handles one resource at a time and returns its diffs. The compositionProvider function is called to obtain the composition to use for rendering. This is the public method for top-level XR diffing, which enables removal detection.

func (*DefaultDiffProcessor) Initialize

func (p *DefaultDiffProcessor) Initialize(ctx context.Context) error

Initialize loads required resources like CRDs and environment configs.

func (*DefaultDiffProcessor) PerformDiff

func (p *DefaultDiffProcessor) PerformDiff(ctx context.Context, resources []*un.Unstructured, compositionProvider types.CompositionProvider) (bool, error)

PerformDiff processes resources using a composition provider function. Returns (hasDiffs, error) where hasDiffs indicates if any differences were detected.

func (*DefaultDiffProcessor) ProcessNestedXRs added in v0.3.0

func (p *DefaultDiffProcessor) ProcessNestedXRs(
	ctx context.Context,
	composedResources []cpd.Unstructured,
	compositionProvider types.CompositionProvider,
	parentResourceID string,
	parentXR *cmp.Unstructured,
	observedResources []cpd.Unstructured,
	depth int,
) (map[string]*dt.ResourceDiff, types.RenderedResources, error)

ProcessNestedXRs recursively processes composed resources that are themselves XRs. It checks each composed resource to see if it's an XR, and if so, processes it through its own composition pipeline to get the full tree of diffs. It preserves the identity of existing nested XRs to ensure accurate diff calculation. observedResources should contain the observed resources from the parent XR's resource tree. depth is the nesting depth of composedResources themselves: 1 for the resources composed by the XR the user named, 2 for their children, and so on. MaxNestedDepth is therefore the number of levels of nesting permitted below the root, and descending past it is an error rather than a silent truncation of the tree.

func (*DefaultDiffProcessor) RenderToStableState added in v0.7.0

func (p *DefaultDiffProcessor) RenderToStableState(
	ctx context.Context,
	xr *cmp.Unstructured,
	comp *apiextensionsv1.Composition,
	fns []pkgv1.Function,
	resourceID string,
	observedResources []cpd.Unstructured,
	synthesizeReady bool,
) (render.CompositionOutputs, error)

RenderToStableState performs iterative rendering until stable state is reached. It handles requirements resolution (environment configs, etc.) and optionally synthesizes Ready status on composed resources between iterations.

When synthesizeReady is false (normal mode), the loop terminates once requirements are resolved. When synthesizeReady is true (eventual-state mode), the loop continues until every rendered composed resource is observed Ready and no new requirements appear, synthesizing Ready=True on the observed state between iterations to reveal stages gated on readiness (function-sequencer). Synthesized Ready reaches only the next render's observed state: observedResources itself, and the cluster state the diff is later computed against, are never modified.

func (*DefaultDiffProcessor) SanitizeXR

func (p *DefaultDiffProcessor) SanitizeXR(res *un.Unstructured, resourceID string) (*cmp.Unstructured, bool, error)

SanitizeXR makes an XR into a valid unstructured object that we can use in a dry-run apply.

type DefaultFunctionProvider added in v0.5.0

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

DefaultFunctionProvider fetches functions from the cluster on each call. This is appropriate for the xr command where each XR is processed independently.

func (*DefaultFunctionProvider) Cleanup added in v0.5.0

Cleanup is a no-op for DefaultFunctionProvider as it doesn't create any resources.

func (*DefaultFunctionProvider) GetFunctionsForComposition added in v0.5.0

func (p *DefaultFunctionProvider) GetFunctionsForComposition(comp *apiextensionsv1.Composition) ([]pkgv1.Function, error)

GetFunctionsForComposition fetches functions from the cluster.

type DefaultResourceManager

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

DefaultResourceManager implements ResourceManager interface.

func (*DefaultResourceManager) FetchCurrentObject

func (m *DefaultResourceManager) FetchCurrentObject(ctx context.Context, composite *un.Unstructured, desired *un.Unstructured) (*un.Unstructured, bool, error)

FetchCurrentObject retrieves the current state of the object from the cluster It returns the current object, a boolean indicating if it's a new object, and any error.

func (*DefaultResourceManager) FetchObservedResources added in v0.5.0

func (m *DefaultResourceManager) FetchObservedResources(ctx context.Context, xr *cmp.Unstructured) ([]cpd.Unstructured, error)

FetchObservedResources fetches the observed composed resources for the given XR. Returns a flat slice of composed resources suitable for RenderInputs.ObservedResources.

func (*DefaultResourceManager) UpdateOwnerRefs

func (m *DefaultResourceManager) UpdateOwnerRefs(ctx context.Context, parent *un.Unstructured, child *un.Unstructured)

UpdateOwnerRefs ensures all OwnerReferences have valid UIDs. It handles Claims and XRs differently according to Crossplane's ownership model: - Claims should never be controller owners of composed resources. - XRs should be controller owners and use their UID for matching references.

type DefaultSchemaValidator

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

DefaultSchemaValidator implements SchemaValidator interface.

func (*DefaultSchemaValidator) EnsureComposedResourceCRDs

func (v *DefaultSchemaValidator) EnsureComposedResourceCRDs(ctx context.Context, resources []*un.Unstructured) error

EnsureComposedResourceCRDs checks if we have all the CRDs needed for the cpd resources and fetches any missing ones from the cluster.

func (*DefaultSchemaValidator) GetCRDs

GetCRDs returns the current CRDs.

func (*DefaultSchemaValidator) LoadCRDs

func (v *DefaultSchemaValidator) LoadCRDs(ctx context.Context) error

LoadCRDs loads CRDs from the cluster.

func (*DefaultSchemaValidator) ValidateResources

func (v *DefaultSchemaValidator) ValidateResources(ctx context.Context, xr *un.Unstructured, composed []cpd.Unstructured) error

ValidateResources validates resources using schema validation.

func (*DefaultSchemaValidator) ValidateScopeConstraints

func (v *DefaultSchemaValidator) ValidateScopeConstraints(ctx context.Context, resource *un.Unstructured, expectedNamespace string, isClaimRoot bool) error

ValidateScopeConstraints validates that a resource has the appropriate namespace for its scope and that namespaced resources match the expected namespace (no cross-namespace refs).

type Defaulter added in v0.11.0

type Defaulter interface {
	// Default returns a copy of obj with its CRD's defaults applied, and the version that CRD stores
	// obj at when that is not obj's own version. The version is empty when there is no other: for a
	// built-in type, a resource whose CRD is not found, a single-version CRD, or a resource at its
	// CRD's storage version. obj itself is never modified.
	Default(ctx context.Context, obj *un.Unstructured) (defaulted *un.Unstructured, storageVersion string, err error)
}

Defaulter predicts the defaults the apiserver would apply to a resource from its CRD's schema.

Nothing it returns may be sent to the apiserver. Under server-side apply every field in a request claims ownership of that field, so a locally defaulted field would take a value away from whichever manager set it in the cluster and report a change that applying the manifest would not make (#503). It has exactly two uses: the XR that composition and render consume, which must carry the spec Crossplane would see (StrictDefaulting); and the predicted result of an addition that has no apiserver result (LenientDefaulting).

The prediction covers CRD `default:` values only. Mutating admission and admission plugins are not modelled at all. Nor is a multi-version CRD's storage version: the prediction defaults against the requested version only, while the cluster may also apply the storage version's defaults and convert the resource through it. That one cannot be predicted without the conversion, but it can be named, so Default reports the storage version whenever it differs from the requested one, and the unverified addition's warning says the diff may omit its defaults and conversion (#528). Nor does it prune fields the schema does not declare, as the apiserver would, but that never shows: schema validation rejects an undeclared field as an unknownField error before any prediction runs.

func NewDefaulter added in v0.11.0

func NewDefaulter(sc k8.SchemaClient, dc xp.DefinitionClient, policy DefaultingPolicy) Defaulter

NewDefaulter creates a CRDDefaulter with the given policy.

type DefaultingPolicy added in v0.11.0

type DefaultingPolicy int

DefaultingPolicy says what a Defaulter does with a resource it finds no CRD for.

const (
	// StrictDefaulting fails for a resource it finds no CRD for. It is the policy for the XR that render
	// consumes: rendering without the defaults Crossplane would see could produce a wrong diff, so a
	// missing XRD or CRD must not be passed over.
	StrictDefaulting DefaultingPolicy = iota

	// LenientDefaulting returns a resource it finds no CRD for as an unchanged copy: a built-in type,
	// which has no CRD, or a resource whose CRD cannot be found. It is the policy for predicting an
	// addition the apiserver did not see, which is best effort by nature; the schema validator, not
	// the Defaulter, is the gate on missing CRDs.
	LenientDefaulting
)

type DiffCalculator

type DiffCalculator interface {
	// CalculateDiff computes the diff for a single resource
	CalculateDiff(ctx context.Context, composite *un.Unstructured, desired *un.Unstructured) (*dt.ResourceDiff, error)

	// CalculateDiffs computes all diffs including removals for the rendered resources.
	// This is the primary method that most code should use.
	CalculateDiffs(ctx context.Context, xr *cmp.Unstructured, desired render.CompositionOutputs) (map[string]*dt.ResourceDiff, error)

	// CalculateNonRemovalDiffs computes diffs for modified/added resources and returns
	// what was rendered. This is used by nested XR processing.
	// Returns: (diffs map, rendered resources, error)
	CalculateNonRemovalDiffs(ctx context.Context, xr *cmp.Unstructured, desired render.CompositionOutputs) (map[string]*dt.ResourceDiff, dtypes.RenderedResources, error)

	// CalculateRemovedResourceDiffs identifies the composed resources in the cluster that Crossplane
	// would garbage collect, given what was rendered. This is called after nested XR processing is
	// complete.
	CalculateRemovedResourceDiffs(ctx context.Context, xr *un.Unstructured, rendered dtypes.RenderedResources) (map[string]*dt.ResourceDiff, error)
}

DiffCalculator calculates differences between resources.

func NewDiffCalculator

func NewDiffCalculator(apply k8.ApplyClient, access k8.AccessChecker, tree xp.ResourceTreeClient, resourceManager ResourceManager, logger logging.Logger, diffOptions renderer.DiffOptions, dryRunOn DryRunOn, predictor Defaulter) DiffCalculator

NewDiffCalculator creates a new DefaultDiffCalculator. predictor predicts CRD defaults for the additions that get no apiserver result; see dryRunCreateAddition. It should be lenient (LenientDefaulting): a prediction is best effort, and must not fail an addition for want of a CRD.

type DiffProcessor

type DiffProcessor interface {
	// PerformDiff processes resources using a composition provider function.
	// Returns (hasDiffs, error) where hasDiffs indicates if any differences were detected.
	PerformDiff(ctx context.Context, resources []*un.Unstructured, compositionProvider types.CompositionProvider) (bool, error)

	// DiffSingleResource processes a single resource and returns its diffs.
	DiffSingleResource(ctx context.Context, res *un.Unstructured, compositionProvider types.CompositionProvider) (map[string]*dt.ResourceDiff, error)

	// Initialize loads required resources like CRDs and environment configs
	Initialize(ctx context.Context) error

	// Cleaner releases any resources held by the processor (e.g., Docker containers).
	// Should be called when the processor is no longer needed.
	Cleaner
}

DiffProcessor interface for processing resources.

func NewDiffProcessor

func NewDiffProcessor(k8cs k8.Clients, xpcs xp.Clients, opts ...ProcessorOption) DiffProcessor

NewDiffProcessor creates a new DefaultDiffProcessor with the provided options.

type DryRunOn added in v0.11.0

type DryRunOn string

DryRunOn selects which resources are round-tripped through the apiserver to compute their post-apply form. Like AnalyzeOn it is a depth/cost knob: every resource dry-run costs one apiserver round-trip and requires a permission, and a resource left un-dry-run is marked with a DryRunInfo so "we did not check" stays distinguishable from "we checked and it matches".

const (
	// DryRunOnExisting dry-runs only resources that already exist in the cluster, leaving additions
	// as the render pipeline produced them plus locally predicted CRD defaults (see Defaulter). This
	// was the only behaviour before crossplane-diff#334. It needs no create permission, and costs one
	// round-trip fewer per added resource.
	DryRunOnExisting DryRunOn = "existing"

	// DryRunOnAll additionally dry-run creates added resources, so their diffs pick up
	// apiserver-side defaulting and mutating admission — neither of which the local Defaulter can
	// predict for a built-in type, and neither of which it can predict at all for admission. The
	// default. Where the create is not permitted, the resource degrades to rendered output plus
	// locally predicted CRD defaults, and says so rather than failing the run.
	DryRunOnAll DryRunOn = "all"
)

type EffectiveComposite added in v0.11.0

type EffectiveComposite struct {
	// Authored is the composite as supplied (see DefaultDiffProcessor.SanitizeXR). It is never
	// modified, because it is what gets validated and dry-run applied: under server-side apply every
	// field in a payload claims ownership of that field (#503). For a claim it is the claim, which is
	// defaulted only with its own CRD (by the apiserver, or by the lenient Defaulter for a predicted
	// addition), never its XR's: the two differ (crossplane-runtime pkg/xcrd schemas.go:75 and :209),
	// since only the XR's defaults compositionUpdatePolicy and only the claim's compositeDeletePolicy.
	Authored *cmp.Unstructured

	// Cluster is the cluster's copy of Authored, or nil if the cluster has none. For a claim it is the
	// claim.
	Cluster *un.Unstructured

	// Backing is the cluster's copy of a claim's backing XR. It is nil for an XR, and for a claim whose
	// backing XR does not exist yet.
	Backing *un.Unstructured

	// Effective is the XR Crossplane would compose, before CRD defaulting. For an XR, that is Authored
	// plus the fields Crossplane writes that Authored leaves out, taken from Cluster. For a claim it is
	// the backing XR (Backing, or one synthesized from the claim) with the claim's spec synced into it
	// the way Crossplane's claim syncer does. It is a fresh object the caller may modify.
	Effective *un.Unstructured

	// IsClaim reports whether Authored is a claim.
	IsClaim bool
}

EffectiveComposite is one composite seen the ways a diff needs it.

func (EffectiveComposite) Composed added in v0.11.0

func (c EffectiveComposite) Composed() *un.Unstructured

Composed returns the cluster object whose composed resources Effective observes: Backing for a claim, Cluster for an XR. It is nil when that object does not exist yet.

type EngineRenderFn added in v0.8.0

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

EngineRenderFn is the default RenderFn implementation. It lazily starts function runtimes on first use, reuses them across subsequent calls, and serializes concurrent renders with an internal mutex.

A single `xr` invocation can render against XRs from multiple compositions whose function pipelines overlap but aren't identical. Engine.Setup (crossplane/cli#159) accepts being called once per batch: the call that creates a new environment — if any — returns a real cleanup; calls that integrate fns into an environment that already exists (because a prior Setup call established it, or because the engine was pre-configured to use an externally-managed environment via --crossplane-docker-network) return a no-op cleanup, as do calls on engines with nothing to clean up (the local engine). We accumulate every returned cleanup in a slice and call them in LIFO order on Cleanup without coordinating which one is real, per the caller contract in the upstream Engine.Setup docstring.

func NewEngineRenderFn added in v0.8.0

func NewEngineRenderFn(log logging.Logger, binaryPath, version, image string) *EngineRenderFn

NewEngineRenderFn constructs an EngineRenderFn.

binaryPath, version, and image select the render backend via render.EngineFlags (they are mutually exclusive upstream, grouped under the "crossplane-selector" xor):

  • binaryPath (CrossplaneBinary): drives the upstream localRenderEngine against a local `crossplane` binary.
  • version (CrossplaneVersion): the docker engine pulls xpkg.crossplane.io/crossplane/crossplane:<version>.
  • image (CrossplaneImage): the docker engine pulls that full image ref.

When all three are empty the docker engine pulls xpkg.crossplane.io/crossplane/crossplane:stable. Both engines now capture stderr into the returned error and honour exit code 3 (partial-output-on- fatal — crossplane/crossplane#7455) per crossplane/cli#91, so we no longer wrap them. Our EngineRenderFn.Render still expects (rsp != nil, err != nil) on pipeline fatal and surfaces both — see the comment there.

The docker engine renders on a Docker network: by default one it creates in its first Setup and removes in that Setup's cleanup, labelled with the run's session. CROSSPLANE_DIFF_DOCKER_NETWORK, read once here, names the user's network instead, passed as render.EngineFlags.CrossplaneDockerNetwork (crossplane/cli#65): the engine then runs the crossplane-render container on it AND annotates each fn at Setup time so its container joins it too — closing both halves of the "crossplane-diff inside a container" case (crossplane/cli#75). The engine never creates or removes that network, and neither does the run: it carries no session label.

func (*EngineRenderFn) Cleanup added in v0.8.0

func (e *EngineRenderFn) Cleanup(ctx context.Context) error

Cleanup stops every function runtime started across the engine's lifetime, then has the run remove every container labelled with its session, run the cleanups accumulated from each engine.Setup call in LIFO order (removing the network the engine created, now that nothing is attached), and remove any of its networks still left. The run's own sweep also catches what upstream fails to remove. Idempotent and safe to call when Render was never invoked.

func (*EngineRenderFn) Render added in v0.8.0

Render performs one render. It is safe for concurrent use — calls are serialized internally. Setup runs only on invocations that introduce previously-unseen functions; renders whose fns are all already running skip straight to building the request. See the EngineRenderFn docstring for the per-batch Setup contract this relies on.

func (*EngineRenderFn) RenderImage added in v0.11.0

func (e *EngineRenderFn) RenderImage() string

RenderImage returns the fully-resolved render image reference the engine will pull, or "" when a local crossplane binary was selected instead. It exists so callers — and tests — can observe which backend a processor actually ended up with; the upstream Engine interface exposes no accessor for it, so before this the selector arguments vanished at the boundary and dropping them entirely was undetectable (crossplane-diff#488).

type EnvInjectingFunctionProvider added in v0.11.0

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

EnvInjectingFunctionProvider wraps another FunctionProvider and upserts a set of env pairs into each function's runtime-docker-env annotation before returning them. Used to raise the function gRPC max-recv-message-size so large XRs don't trip the function-sdk-go 4MB default under `crossplane render` (which, unlike the in-cluster runtime, does not apply DeploymentRuntimeConfig).

func (*EnvInjectingFunctionProvider) Cleanup added in v0.11.0

Cleanup delegates to the wrapped provider.

func (*EnvInjectingFunctionProvider) GetFunctionsForComposition added in v0.11.0

func (p *EnvInjectingFunctionProvider) GetFunctionsForComposition(comp *apiextensionsv1.Composition) ([]pkgv1.Function, error)

GetFunctionsForComposition delegates to the wrapped provider and upserts the configured env pairs into each function's runtime-docker-env annotation. The upsert is idempotent so repeated calls over a caching inner provider don't duplicate pairs.

type FunctionProvider added in v0.5.0

type FunctionProvider interface {
	// GetFunctionsForComposition returns the functions needed to render a composition.
	GetFunctionsForComposition(comp *apiextensionsv1.Composition) ([]pkgv1.Function, error)

	// Cleaner stops and removes any resources created during function execution.
	// For providers that don't create resources (like DefaultFunctionProvider), this is a no-op.
	Cleaner
}

FunctionProvider provides functions for rendering compositions. Different implementations can fetch functions on-demand or return cached functions.

func NewCachedFunctionProvider added in v0.5.0

func NewCachedFunctionProvider(fnClient xp.FunctionClient, logger logging.Logger) FunctionProvider

NewCachedFunctionProvider creates a new CachedFunctionProvider.

func NewDefaultFunctionProvider added in v0.5.0

func NewDefaultFunctionProvider(fnClient xp.FunctionClient, logger logging.Logger) FunctionProvider

NewDefaultFunctionProvider creates a new DefaultFunctionProvider.

func NewEnvInjectingFunctionProvider added in v0.11.0

func NewEnvInjectingFunctionProvider(inner FunctionProvider, envs map[string]string, logger logging.Logger) FunctionProvider

NewEnvInjectingFunctionProvider wraps inner, upserting the given env pairs into each returned function's runtime-docker-env annotation.

func NewRegistryOverrideFunctionProvider added in v0.8.0

func NewRegistryOverrideFunctionProvider(inner FunctionProvider, registry string, logger logging.Logger) FunctionProvider

NewRegistryOverrideFunctionProvider wraps inner, replacing the registry portion of every function package ref with the given registry.

type InputValidator added in v0.11.0

type InputValidator interface {
	// ToRender returns the inputs to render, in input order, with duplicates already dropped.
	// An input with a non-nil Err was rejected before rendering and must not be rendered.
	ToRender() []types.ValidatedInput
	// RecordRender records the render of ToRender()[i]: every resource key it produced
	// (including unchanged ones) and its error, if any.
	RecordRender(i int, rendered map[string]bool, err error)
	// Verdicts returns the final error for each group (indexed like ToRender()), or nil.
	// It applies stage 2 and the precedence rule: a managed-input verdict replaces that
	// group's own render failure.
	Verdicts(groups []dt.XRDiffGroup) []error
	// RenderOverlaps returns the stage-3 errors.
	RenderOverlaps(groups []dt.XRDiffGroup) []error
}

InputValidator validates one PerformDiff run's input set. It is created per run by an InputValidatorFactory, because it carries that run's state between stages.

func NewBundleInputValidator added in v0.11.0

func NewBundleInputValidator(logger logging.Logger, inputs []*un.Unstructured) InputValidator

NewBundleInputValidator returns the default InputValidator for one run over inputs. It has the InputValidatorFactory signature. Stage 1 (checkInputs) runs here, so duplicates are logged once.

type InputValidatorFactory added in v0.11.0

type InputValidatorFactory func(logger logging.Logger, inputs []*un.Unstructured) InputValidator

InputValidatorFactory creates the InputValidator for one run.

type ProcessorConfig

type ProcessorConfig struct {
	// Colorize determines whether to use colors in the diff output
	Colorize bool

	// Compact determines whether to show a compact diff format
	Compact bool

	// OutputFormat specifies the output format for diffs (diff, json, yaml)
	OutputFormat renderer.OutputFormat

	// OutputVersion specifies the schema version of structured (json, yaml) output
	OutputVersion renderer.OutputVersion

	// MaxNestedDepth is the maximum depth for recursive nested XR processing
	MaxNestedDepth int

	// IncludeManual determines whether to include XRs with Manual update policy in composition diffs
	IncludeManual bool

	// MinimizeComposition collapses composition changes to a single marker line per
	// composition, omitting the full YAML diff body. Human renderer only; structured
	// output always includes full compositionChanges.
	MinimizeComposition bool

	// Warnings is the source of non-fatal advisories to include in structured output. It is the same
	// *WarningLogger that Logger is set to when the CLI wires one up; keeping a typed handle avoids
	// type-asserting the Logger back to its concrete type at drain time. Nil is valid and means no
	// warnings are collected — the human-visible stderr line is emitted by the WarningLogger itself,
	// so leaving this unset only affects structured output.
	Warnings *WarningLogger

	// AnalyzeOn is the smallest composition change that triggers per-composite impact analysis.
	// Evaluating a composite costs one function render, so this is a cost knob — it never suppresses
	// a reported consequence. RevisionImpact is populated at every setting, and composites left
	// unevaluated are marked ImpactAnalysisSkipped so "we did not look" stays distinguishable from
	// "we looked and found nothing".
	//
	// Zero value means AnalyzeOnAnyChange, matching the CLI default.
	AnalyzeOn AnalyzeOn

	// DryRunOn selects which resources are verified against the apiserver. Zero value means
	// DryRunOnAll, matching the CLI default.
	DryRunOn DryRunOn

	// EventualState enables iterative simulation to show eventual state after all reconciliation
	// cycles complete. Useful with function-sequencer which hides later stage resources.
	EventualState bool

	// MaxRenderIterations is the maximum number of render iterations when resolving requirements
	// or simulating eventual state. Higher values may be needed for complex pipelines.
	MaxRenderIterations int

	// IgnorePaths is a list of paths to ignore when calculating diffs
	IgnorePaths []string

	// FunctionCredentials holds Secret credentials to pass to Functions during rendering
	FunctionCredentials []corev1.Secret

	// FunctionRegistryOverride overrides the registry in all function package refs.
	FunctionRegistryOverride string

	// MaxRecvMessageSize is the max gRPC message size (MB) for render function
	// containers. Zero leaves the function's own default (function-sdk-go uses
	// 4MB). When >0 it is injected as the appropriate container env var,
	//  so large XRs don't trip the default limit under render.
	MaxRecvMessageSize int

	// Stdout is the writer for diff output (defaults to os.Stdout)
	Stdout io.Writer

	// Stderr is the writer for error output (defaults to os.Stderr)
	Stderr io.Writer

	// Logger is the logger to use
	Logger logging.Logger

	// RenderFunc is the function to use for rendering resources. If left nil,
	// processors construct a default engine-backed RenderFn on initialization.
	RenderFunc RenderFn

	// CrossplaneRenderBinary, when non-empty, causes the default
	// engine-backed RenderFn to invoke a local `crossplane` binary at the
	// supplied path instead of the upstream docker engine. This is a test
	// affordance that gives integration tests a fast in-process render path;
	// production users should leave it empty so the docker engine pulls
	// xpkg.crossplane.io/crossplane/crossplane:stable. Ignored when
	// RenderFunc is set explicitly.
	CrossplaneRenderBinary string

	// CrossplaneVersion, when non-empty, causes the default engine-backed
	// RenderFn's docker engine to pull
	// xpkg.crossplane.io/crossplane/crossplane:<version> instead of :stable.
	// Mutually exclusive with CrossplaneImage and CrossplaneRenderBinary.
	// Ignored when RenderFunc is set explicitly.
	CrossplaneVersion string

	// CrossplaneImage, when non-empty, causes the default engine-backed
	// RenderFn's docker engine to pull this full image reference instead of
	// the default xpkg.crossplane.io/crossplane/crossplane:stable. Mutually
	// exclusive with CrossplaneVersion and CrossplaneRenderBinary. Ignored
	// when RenderFunc is set explicitly.
	CrossplaneImage string

	// Factories provide factory functions for creating components
	Factories ComponentFactories
}

ProcessorConfig contains configuration for the DiffProcessor.

func (*ProcessorConfig) GetDiffOptions

func (c *ProcessorConfig) GetDiffOptions() renderer.DiffOptions

GetDiffOptions returns DiffOptions based on the ProcessorConfig.

func (*ProcessorConfig) SetDefaultFactories

func (c *ProcessorConfig) SetDefaultFactories()

SetDefaultFactories sets default component factory functions if not already set.

type ProcessorOption

type ProcessorOption func(*ProcessorConfig)

ProcessorOption defines a function that can modify a ProcessorConfig.

func WithAnalyzeOn added in v0.11.0

func WithAnalyzeOn(analyzeOn AnalyzeOn) ProcessorOption

WithAnalyzeOn sets the smallest composition change that triggers per-composite impact analysis (see ProcessorConfig.AnalyzeOn). An empty value leaves the default in place.

func WithColorize

func WithColorize(colorize bool) ProcessorOption

WithColorize sets whether to use colors in diff output.

func WithCompact

func WithCompact(compact bool) ProcessorOption

WithCompact sets whether to use compact diff format.

func WithCrossplaneImage added in v0.9.0

func WithCrossplaneImage(image string) ProcessorOption

WithCrossplaneImage pins the full crossplane render image reference the default engine-backed RenderFn's docker engine pulls. See ProcessorConfig.CrossplaneImage. Mutually exclusive with WithCrossplaneVersion and WithCrossplaneRenderBinary.

func WithCrossplaneRenderBinary added in v0.8.0

func WithCrossplaneRenderBinary(path string) ProcessorOption

WithCrossplaneRenderBinary points the default engine-backed RenderFn at a local `crossplane` binary instead of the upstream docker engine. See ProcessorConfig.CrossplaneRenderBinary for the semantics — production callers should not use this; it exists for fast integration-test iteration.

func WithCrossplaneVersion added in v0.9.0

func WithCrossplaneVersion(version string) ProcessorOption

WithCrossplaneVersion pins the crossplane render version the default engine-backed RenderFn's docker engine pulls (…/crossplane:<version>). See ProcessorConfig.CrossplaneVersion. Mutually exclusive with WithCrossplaneImage and WithCrossplaneRenderBinary.

func WithDefaulterFactory added in v0.11.0

func WithDefaulterFactory(factory func(k8.SchemaClient, xp.DefinitionClient, DefaultingPolicy) Defaulter) ProcessorOption

WithDefaulterFactory sets the Defaulter factory function.

func WithDiffCalculatorFactory

WithDiffCalculatorFactory sets the DiffCalculator factory function.

func WithDiffRendererFactory

func WithDiffRendererFactory(factory func(logging.Logger, renderer.DiffOptions) renderer.DiffRenderer) ProcessorOption

WithDiffRendererFactory sets the DiffRenderer factory function.

func WithDryRunOn added in v0.11.0

func WithDryRunOn(dryRunOn DryRunOn) ProcessorOption

WithDryRunOn sets which resources are round-tripped through the apiserver. An empty value leaves the config's default in place, matching WithAnalyzeOn.

func WithEventualState added in v0.7.0

func WithEventualState(enabled bool) ProcessorOption

WithEventualState sets whether to show eventual state after all reconciliation cycles complete. When enabled, the processor runs an iterative simulation that synthesizes Ready status on rendered resources until no new resources appear. This is useful with function-sequencer which hides later stage resources until earlier stages become Ready.

func WithFunctionCredentials added in v0.6.0

func WithFunctionCredentials(creds []corev1.Secret) ProcessorOption

WithFunctionCredentials sets the credentials to pass to Functions during rendering.

func WithFunctionProviderFactory added in v0.5.0

func WithFunctionProviderFactory(factory func(xp.FunctionClient, logging.Logger) FunctionProvider) ProcessorOption

WithFunctionProviderFactory sets the FunctionProvider factory function.

func WithFunctionRegistryOverride added in v0.8.0

func WithFunctionRegistryOverride(registry string) ProcessorOption

WithFunctionRegistryOverride overrides the registry in all function package refs.

func WithIgnorePaths added in v0.5.0

func WithIgnorePaths(ignorePaths []string) ProcessorOption

WithIgnorePaths sets the paths to ignore when calculating diffs.

func WithIncludeManual added in v0.3.0

func WithIncludeManual(includeManual bool) ProcessorOption

WithIncludeManual sets whether to include XRs with Manual update policy in composition diffs.

func WithInputValidatorFactory added in v0.11.0

func WithInputValidatorFactory(factory InputValidatorFactory) ProcessorOption

WithInputValidatorFactory sets the InputValidator factory function, called once per PerformDiff run.

func WithLogger

func WithLogger(logger logging.Logger) ProcessorOption

WithLogger sets the logger for the processor.

func WithMaxNestedDepth added in v0.3.0

func WithMaxNestedDepth(depth int) ProcessorOption

WithMaxNestedDepth sets the maximum depth for recursive nested XR processing.

func WithMaxRecvMessageSize added in v0.11.0

func WithMaxRecvMessageSize(mb int) ProcessorOption

WithMaxRecvMessageSize sets the max gRPC message size (MB) injected into render function containers.

func WithMaxRenderIterations added in v0.7.0

func WithMaxRenderIterations(maxIterations int) ProcessorOption

WithMaxRenderIterations sets the maximum number of render iterations when resolving requirements or simulating eventual state.

func WithMinimizeComposition added in v0.9.0

func WithMinimizeComposition(minimize bool) ProcessorOption

WithMinimizeComposition sets whether to collapse composition changes to a single marker line.

func WithOutputFormat added in v0.6.0

func WithOutputFormat(format renderer.OutputFormat) ProcessorOption

WithOutputFormat sets the output format for diffs.

func WithOutputVersion added in v0.11.0

func WithOutputVersion(version renderer.OutputVersion) ProcessorOption

WithOutputVersion sets the schema version of structured output.

func WithRenderFunc

func WithRenderFunc(renderFn RenderFn) ProcessorOption

WithRenderFunc sets the render function for the processor.

func WithRequirementsProviderFactory

func WithRequirementsProviderFactory(factory func(k8.ResourceClient, xp.EnvironmentClient, logging.Logger) *RequirementsProvider) ProcessorOption

WithRequirementsProviderFactory sets the RequirementsProvider factory function.

func WithResourceManagerFactory

WithResourceManagerFactory sets the ResourceManager factory function.

func WithSchemaValidatorFactory

func WithSchemaValidatorFactory(factory func(k8.SchemaClient, k8.ResourceClient, xp.DefinitionClient, logging.Logger) SchemaValidator) ProcessorOption

WithSchemaValidatorFactory sets the SchemaValidator factory function.

func WithStderr added in v0.7.0

func WithStderr(w io.Writer) ProcessorOption

WithStderr sets the writer for error output.

func WithStdout added in v0.7.1

func WithStdout(w io.Writer) ProcessorOption

WithStdout sets the writer for diff output.

func WithWarnings added in v0.11.0

func WithWarnings(warnings *WarningLogger) ProcessorOption

WithWarnings sets the collector whose warnings are included in structured output. Pass the same *WarningLogger that was supplied to WithLogger.

type RegistryOverrideFunctionProvider added in v0.8.0

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

RegistryOverrideFunctionProvider wraps another FunctionProvider and replaces the registry in each function's package reference before returning them.

func (*RegistryOverrideFunctionProvider) Cleanup added in v0.8.0

Cleanup delegates to the wrapped provider.

func (*RegistryOverrideFunctionProvider) GetFunctionsForComposition added in v0.8.0

func (p *RegistryOverrideFunctionProvider) GetFunctionsForComposition(comp *apiextensionsv1.Composition) ([]pkgv1.Function, error)

GetFunctionsForComposition delegates to the wrapped provider and rewrites the registry portion of each returned function's package ref.

type RenderFn added in v0.8.0

RenderFn is the render abstraction injected into diff processors. Callers provide the Function CRs they already have; the implementation owns the engine and FunctionAddresses lifecycle.

type RenderInputs added in v0.8.0

type RenderInputs struct {
	CompositeResource   *ucomposite.Unstructured
	Composition         *apiextensionsv1.Composition
	Functions           []pkgv1.Function
	FunctionCredentials []corev1.Secret
	ObservedResources   []composed.Unstructured
	RequiredResources   []kunstructured.Unstructured
	RequiredSchemas     []spec3.OpenAPI

	// XRD is the CompositeResourceDefinition the binary should consider
	// when rendering. Set by RenderToStableState based on a defClient lookup
	// so the binary can pick the right composite.Schema (Legacy vs Modern)
	// for the input XR's GVK. The renderer then writes canonical fields at
	// the path the cluster CRD declares (spec.* for v1, spec.crossplane.*
	// for v2), making dry-run apply succeed and the rendered desired
	// comparable against cluster state. Optional; when nil the binary
	// falls back to SchemaModern.
	XRD *kunstructured.Unstructured
}

RenderInputs carries what the diff processor already holds. It deliberately omits FunctionAddrs — that's engine state, not caller state.

type RequirementsProvider

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

RequirementsProvider consolidates requirement processing with caching.

func NewRequirementsProvider

func NewRequirementsProvider(res k8.ResourceClient, env xp.EnvironmentClient, logger logging.Logger) *RequirementsProvider

NewRequirementsProvider creates a new provider with caching.

func (*RequirementsProvider) ClearCache

func (p *RequirementsProvider) ClearCache()

ClearCache clears all cached resources.

func (*RequirementsProvider) Initialize

func (p *RequirementsProvider) Initialize(ctx context.Context) error

Initialize pre-fetches resources like environment configs.

func (*RequirementsProvider) ResolveSelectors added in v0.8.0

func (p *RequirementsProvider) ResolveSelectors(ctx context.Context, selectors []*v1.ResourceSelector, xrNamespace string) ([]*un.Unstructured, error)

ResolveSelectors resolves a flat list of ResourceSelector entries into their backing resources. Checks the cache first; on a miss it defers to the per-selector fetcher and caches the result.

Matches the render.CompositionOutputs.RequiredResources shape introduced in upstream crossplane PR #7339.

type ResourceManager

type ResourceManager interface {
	// FetchCurrentObject retrieves the current state of an object from the cluster
	FetchCurrentObject(ctx context.Context, composite *un.Unstructured, desired *un.Unstructured) (*un.Unstructured, bool, error)

	// UpdateOwnerRefs ensures all OwnerReferences have valid UIDs
	UpdateOwnerRefs(ctx context.Context, parent *un.Unstructured, child *un.Unstructured)

	// FetchObservedResources fetches the observed composed resources for the given XR
	FetchObservedResources(ctx context.Context, xr *cmp.Unstructured) ([]cpd.Unstructured, error)
}

ResourceManager handles resource-related operations like fetching, updating owner refs, and identifying resources to be removed.

func NewResourceManager

func NewResourceManager(client k8.ResourceClient, defClient xp.DefinitionClient, treeClient xp.ResourceTreeClient, logger logging.Logger) ResourceManager

NewResourceManager creates a new DefaultResourceManager.

type SchemaValidationError added in v0.6.0

type SchemaValidationError struct {
	ResourceID string
	Message    string
	Err        error
	Result     *pkgvalidate.ValidationResult
	Failures   []dt.ResourceValidationFailure
}

SchemaValidationError indicates schema validation failed. Used to distinguish validation errors from other tool errors for exit code handling.

Result, when non-nil, carries the structured per-resource validation outcome that produced this error. Output renderers use it to surface typed FieldValidationError records under OutputError.ValidationFailures in JSON / YAML output. It is left nil for paths that fail validation without a *pkgvalidate.ValidationResult in hand — for example scope-validation errors raised after schema validation succeeded — so the absence of structured detail is observable rather than fabricated.

Failures is the same information for rejections that did not come from pkg/validate at all — specifically, a resource the apiserver itself refused during a dry-run (a validating admission webhook, a ResourceQuota, a missing namespace). Those are the same kind of fact as a schema rejection ("the cluster will not accept this resource"), just detected by the apiserver instead of locally, so they belong in the same exit-code tier and the same output field. They are carried separately rather than synthesised into a pkgvalidate.ValidationResult because that would mean inventing a FieldErrorType value upstream does not define; ResourceValidationFailure is explicitly ours to populate (see its doc in renderer/types).

func NewAdmissionRejectionError added in v0.11.0

func NewAdmissionRejectionError(rejectedDesc string, obj *un.Unstructured, err error) *SchemaValidationError

NewAdmissionRejectionError wraps an apiserver dry-run rejection of obj as a SchemaValidationError, so it lands in the same exit-code tier and the same structured-output field as a local schema rejection.

This is used for BOTH new and existing resources on purpose. Before this existed, a webhook rejection of an existing resource surfaced as a plain tool error (exit 1) while the identical rejection of an addition would have been a validation error (exit 2) — reporting the same cluster fact under two different exit codes depending only on whether the resource happened to exist already. See crossplane-diff#334.

rejectedDesc describes the REJECTED resource (the calculator's per-resource id, e.g. "Bucket/my-bucket"), not the input XR. It is folded into the message because OutputError.ResourceID identifies the input the user supplied, which for a composed resource is the XR — without this, a human would be told their XR was rejected with no way to tell which composed resource the cluster actually refused.

The inner SchemaValidationError deliberately gets an empty ResourceID: its Error() would otherwise prefix "schema validation error for <id>: ", which both duplicates the id that OutputError.FormatError already prints and mislabels an admission rejection as a schema problem.

func NewSchemaValidationError added in v0.6.0

func NewSchemaValidationError(resourceID, message string, err error) *SchemaValidationError

NewSchemaValidationError creates a new SchemaValidationError without a structured Result. Use WithResult on the returned value to attach one when the failure originated from pkg/validate.SchemaValidate.

func (*SchemaValidationError) Error added in v0.6.0

func (e *SchemaValidationError) Error() string

Error implements the error interface.

func (*SchemaValidationError) Unwrap added in v0.6.0

func (e *SchemaValidationError) Unwrap() error

Unwrap returns the wrapped error for errors.Is/As compatibility.

func (*SchemaValidationError) WithFailures added in v0.11.0

WithFailures attaches already-built per-resource failures for a rejection that did not originate from pkg/validate — an apiserver dry-run refusal. It is the sibling of WithResult; see the Failures field for why the two are separate. Returns the receiver for fluent chaining.

func (*SchemaValidationError) WithResult added in v0.8.0

WithResult attaches the structured *pkgvalidate.ValidationResult that produced this error so downstream renderers can emit typed per-resource failures alongside the human-readable Message. Returns the receiver for fluent chaining.

type SchemaValidator

type SchemaValidator interface {
	// ValidateResources validates resources using schema validation
	ValidateResources(ctx context.Context, xr *un.Unstructured, composed []cpd.Unstructured) error

	// EnsureComposedResourceCRDs ensures we have all required CRDs for validation
	EnsureComposedResourceCRDs(ctx context.Context, resources []*un.Unstructured) error

	// ValidateScopeConstraints validates that a resource has the appropriate namespace for its scope
	// and that namespaced resources match the expected namespace (no cross-namespace refs)
	ValidateScopeConstraints(ctx context.Context, resource *un.Unstructured, expectedNamespace string, isClaimRoot bool) error
}

SchemaValidator handles validation of resources against CRD schemas.

func NewSchemaValidator

func NewSchemaValidator(sClient k8.SchemaClient, rClient k8.ResourceClient, dClient xp.DefinitionClient, logger logging.Logger) SchemaValidator

NewSchemaValidator creates a new DefaultSchemaValidator.

type WarningLogger added in v0.11.0

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

WarningLogger is the advisory channel: a logging.Logger decorator that treats every Info call as a user-facing warning, writing it to stderr immediately and collecting it for structured output. Debug calls are forwarded to the wrapped logger unchanged.

Why the logger, and why Info

A warning has to be raisable from wherever it is discovered — a client, a resource manager, the render loop. The logging.Logger is the only cross-cutting dependency already threaded to all of those; every component factory in ProcessorConfig takes exactly one (a client, a converter, and a Logger), so a parallel "warning sink" parameter would mean churning every constructor, every factory signature, and every With*Factory option for no semantic gain.

There is no Warn to reach for: crossplane-runtime's logging.Logger has exactly two levels by design ("Crossplane prefers not to use go-logr because it desires a simpler API with only two levels ... Info and Debug"), and it defines Info as "messages that Crossplane operators are very likely to be concerned with when running Crossplane" against Debug's "when debugging Crossplane". So treating Info as the advisory level is not a convention invented here — it is the interface's documented meaning, which this type implements by giving those messages somewhere to go. What it does add is the second sink: the same message also lands in structured output.

The user-facing label is deliberately WARNING rather than INFO. An operator reading "INFO: applying this diff will assume ownership" would under-weight it; the upstream *level* and the word shown to a human are answering different questions.

Why it writes to stderr itself

Raising the wrapped logger's verbosity would surface warnings for humans, but in zap's development-mode format (timestamp, level, caller) and still nowhere in structured output. Owning the human rendering here means one mechanism produces both views — the same dual-emission contract OutputError follows — and the text stays fit for a CLI. Info is therefore NOT forwarded to the wrapped logger; otherwise a --verbose run would print every warning twice, once in each format.

Why identical warnings are collapsed

A warning is raised where the condition is discovered, and several of those sites sit inside loops the caller chose — resolveFunctionCredentials runs once per XR, so a single unfetchable secret on a composition affecting thirty XRs would otherwise produce thirty byte-identical WARNING lines and thirty identical warnings[] entries. A repeat that is identical in BOTH message and context carries no information the user has not already been given, so the sink keeps only the first. Warnings whose context differs in any pair are distinct occurrences and every one of them is kept: the ownership-theft advisory in resource_manager.go names the composed resource in its context and must fire once per resource. The consequence for a warning site is therefore a simple rule — put what distinguishes one occurrence from another in the context, which it should be doing regardless, since context is what a machine consumer reads.

A zero WarningLogger is not usable; construct one with NewWarningLogger.

func NewWarningLogger added in v0.11.0

func NewWarningLogger(logger logging.Logger, stderr io.Writer) *WarningLogger

NewWarningLogger wraps logger so that Info calls become user-facing warnings written to stderr and collected for structured output. Debug calls pass through to logger unchanged.

func (*WarningLogger) Debug added in v0.11.0

func (l *WarningLogger) Debug(msg string, keysAndValues ...any)

Debug forwards to the wrapped logger. Tracing is not advisory output.

func (*WarningLogger) Info added in v0.11.0

func (l *WarningLogger) Info(msg string, keysAndValues ...any)

Info records msg as a user-facing warning: written to stderr now, and collected for structured output. A warning identical to one already raised — same message and same context — is dropped from both channels; see the type comment. It is deliberately not forwarded to the wrapped logger.

func (*WarningLogger) Warnings added in v0.11.0

func (l *WarningLogger) Warnings() []dt.OutputWarning

Warnings returns the warnings collected so far, in emission order. The returned slice is a copy, so callers may retain it while more warnings are raised.

func (*WarningLogger) WithValues added in v0.11.0

func (l *WarningLogger) WithValues(keysAndValues ...any) logging.Logger

WithValues returns a logger that carries the supplied pairs, sharing this logger's collection point so derived loggers do not each accumulate their own separate warning list.

type XRDiffResult added in v0.5.0

type XRDiffResult struct {
	// Diffs contains the downstream resource diffs for this XR (keyed by resource ID).
	// Empty map means no changes detected.
	Diffs map[string]*dt.ResourceDiff
	// Error contains any error that occurred while processing this XR.
	// nil means processing was successful.
	Error error
}

XRDiffResult captures the result of processing a single XR against a composition.

func (*XRDiffResult) HasChanges added in v0.5.0

func (r *XRDiffResult) HasChanges() bool

HasChanges returns true if this XR has downstream resource changes.

func (*XRDiffResult) HasError added in v0.5.0

func (r *XRDiffResult) HasError() bool

HasError returns true if this XR encountered a processing error.

Jump to

Keyboard shortcuts

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