Repository navigation
Conversation
Callers can now detect a backend that predates `crossplane internal render` with errors.Is instead of matching stderr text. The user-visible message is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Verbatim copy of cmd/diff/{renderer,renderer/types,types,testutils} from
crossplane-contrib/crossplane-diff at 9fa7d31 with import paths rewritten,
gci import grouping, and named constants for repeated literals in
testutils/mock_builder.go (goconst). Adds gopsutil/v4 (used by the envtest
reaper) and bumps go.yaml.in/yaml/v3 to v3.0.5 and golang.org/x/sys to
v0.48.0, which it requires.
TestCRDsForXRD needs Crossplane's CRDs and fails until a CRD fetch exists.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Fetch Crossplane's cluster/crds at v2.4.1 (the apis/v2 version go.mod pins) as a fixed-output derivation and expose it to the test check and the test app as CROSSPLANE_CRDS_DIR. PinnedCrossplaneCRDsDir now reads that variable instead of an Earthly-populated cluster/gomod/crds. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Verbatim copy of cmd/diff/{ref,dockerrun,client/core} from
crossplane-contrib/crossplane-diff at 11e6fc3 with import paths rewritten.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Upstream replaced Info-level advisory logging with a dt.Advisories channel; resync renderer, renderer/types, testutils and dockerrun. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Verbatim copy of cmd/diff/client/kubernetes from crossplane-contrib/crossplane-diff at 9fa7d31 with import paths rewritten and gci import grouping. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Verbatim copy of cmd/diff/client/crossplane from crossplane-contrib/crossplane-diff at 9fa7d31 with import paths rewritten and gci import grouping. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Verbatim copy of cmd/diff/diffprocessor from crossplane-contrib/crossplane-diff at 9fa7d31 with import paths rewritten and gci import grouping. Adds github.com/Masterminds/semver v1.5.0, which crossplane-diff's render_version.go imports, and refreshes the nix vendor hash. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The cli's docker and local render engines now mark a backend that lacks `crossplane internal render` as render.ErrStaleRenderBackend, so the diff render engine checks that sentinel instead of defining its own and matching the error text. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Port crossplane-diff's AppContext as internal/diff/client.New and share resource simulate's rest.Config construction via kube.RESTConfig. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Lift render xr's loadFunctionsFromProject into project.LoadFunctions with no behavior change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
When spec.paths.apis is "/", project build walks the whole project and should skip the examples, functions and operations directories. Walk paths are rooted at "/" but the default excludes are relative, so the prefix check never matched and those directories were packaged. Anchor each exclude at "/" and match the directory itself or anything beneath it, so "examples" no longer matches "examples-foo" either. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add LoadCompositions, which returns the Compositions under a project's APIs path using the same walk and excludes as project build. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add ProjectFunctionProvider, a FunctionProvider that resolves pipeline steps naming a project function to that function and asks the wrapped (cluster) provider only for the remaining steps, so an embedded function that is not installed in the cluster no longer fails the lookup and an installed one no longer runs the stale cluster version. Returned functions are per-call copies, as CachedFunctionProvider's are. Overlaid functions carry no pull-policy annotation, so the docker runtime keeps its IfNotPresent default and daemon-loaded images run without a registry pull. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add `crossplane project simulate`, which previews how a project's Compositions would change the composite resources in the user's current Kubernetes context. It builds the project's functions, overlays them on the cluster's functions by name, and runs the diff engine's composition diff, printing the result with the engine's own renderers (-o diff|json|yaml). Exit status is 0 on success, 1 on a tool error, 2 when only schema validation failed, and 130 when interrupted. The timeout bounds the whole simulation because the engine has no per-composite deadline yet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add --exit-code to `crossplane project simulate`. With it, a simulation that succeeds and finds changes exits with status 3, so CI can gate on it, as with git diff --exit-code. Without it, changes still exit 0, because finding them is what a simulation is for. Tool, schema-validation and interrupt statuses take precedence either way. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
…harness Port TestDiffIntegration and TestCompDiffIntegration from crossplane-diff origin/main 6a140b6 (cmd/diff/diff_integration_test.go, diff_it_utils_test.go, main_test.go and the ~350-file testdata tree) into internal/diff/integration, verbatim case tables included. Every case is CLI args plus an expected exit code and output, funneled through one runSimulate harness function that wires this repo's ported engine the way crossplane-diff's comp and xr commands do; when crossplane project simulate grows this surface, the harness swaps to the real command in a single-function change. Cases the simulate surface cannot express yet are ported in full and skipped with greppable gap:/env: reasons rather than dropped. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Auto-fix gci/tagalign/prealloc; preallocate the harness's option slice; mark the five builders carried verbatim from crossplane-diff whose cases are gap-skipped as nolint:unused with the reason. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The gap/env guards sat after startIntegrationCluster, so a skipped case still booted envtest before skipping; move them ahead of cluster startup and thread the case's timeout in, since the args they inspect are built before the cluster too. Also skip when CROSSPLANE_CRDS_DIR is unset, so plain go test skips rather than fatals, and document the package in a README. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
TestDiffSingleResourceConcurrent starts its own envtest cluster, so it needs the same Docker/envtest/CRDS guards the runner applies; without them nix run .#test died in envtest.Start instead of skipping. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The flag-parity branch is merged, so --no-color, --ignore-paths, --function-credentials, --eventual-state, --analyze-on, --analyze-unchanged (deprecated; simulate's --analyze-on=always) and --dry-run-on all exist on project simulate. Remove the gap guard and wire --function-credentials through the harness, loading its Secrets at parse time the way both crossplane-diff's kong MapperValue and simulate's own flag do. 41 of 142 subtests now run and pass (was 23); the remaining skips are the xr-mode and repeated --resource gaps plus crossplane-diff's own upstream skip reasons. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Package the envtest control-plane binaries (kube-apiserver, etcd, kubectl) from the controller-tools envtest v1.30.3 release, the version crossplane-diff's Earthfile targets. Wire KUBEBUILDER_ASSETS into the sandboxed test check, where the render-engine cases still skip on the missing Docker daemon but the envtest cluster cases can run, and add an unsandboxed integration-test app plus a CI job that runs it on a runner's host Docker daemon. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The test app now runs the whole suite with envtest assets and Docker, as crossplane-diff's go-test target does, and writes a -coverpkg=./... coverage profile so integration coverage of internal/diff counts. CI's tests job runs the app outside the sandbox; the separate integration-test app and job are removed. The sandboxed check stays for flake check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
jcogilvie
force-pushed
the
jco-simulate-diff-port
branch
from
October 20, 2026 15:53
9dff52a to
881e787
Compare
This reverts commit 881e787. Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The diff integration tests need Docker and envtest assets, so they run in their own integration-test target rather than with the unit tests. The integration tag keeps go test ./... from compiling them; golangci-lint builds with the tag so they are still linted. An untagged doc.go keeps the package buildable without the tag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The ported renderers wrote every OutputError to stderr as "ERROR: Kind/name: ...", and the processors also returned the failure, which kong printed again as "crossplane: error: ...". A failing run reported each error twice in two formats. Stop the renderers printing errors and drop OutputError.FormatError, which nothing else used. Structured output keeps errors[] unchanged, and warnings still go to stderr when raised. The command's returned error is now the only place a human sees an error, so it must name each failure: PerformDiff already joined "unable to process resource Kind/name: ..." for each input, and DiffComposition now does the same under "impact analysis failed for N XR(s)" and "failed to process all compositions", in place of bare counts. The integration harness prints a returned error the way kong's FatalIfErrorf does, so the cases' stderr assertions see what a user sees. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Ports crossplane-diff#591. GenerateDiff, Options{IgnorePaths, ForVerdict} and
PathLastAppliedConfiguration move to internal/diff/compare, DiffCalculator
takes compare.Options, and renderer.DiffOptions keeps only presentation
fields. No behaviour change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
…lan.Result Renderer.Render now takes a plan.Result. Result gains Composites, Compositions with RevisionImpact, typed Errors, Warnings and Summary.Deferred; ResourcePlan gains Composite and DryRun. FieldChanges is exported for mappers. The plan renderer groups plans under an XR header when a composite is set; ungrouped output is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Port of crossplane-diff ad02f2b (#593). DiffProcessor.PerformDiff now returns a *dt.XRReport (groups, union errors, sorted advisories) and CompDiffProcessor.DiffComposition a *renderer.CompDiffOutput, instead of rendering and returning a has-diffs bool. project simulate builds the renderer from its flags and the kong context's writers, renders the returned report (even alongside an error, so partial results and structured errors still reach stdout), and derives the exit code from the error and the report's HasChanges. ProcessorConfig drops Colorize, Compact, MinimizeComposition, OutputFormat, OutputVersion, Stdout, Stderr, GetDiffOptions and the DiffRenderer and CompDiffRenderer factories with their options. A processor's default AdvisoryCollector is now collect-only; the command injects the stderr-writing one. Cleanup still runs before the advisories are read into the report, so a teardown advisory reaches warnings[]. The CLI keeps its divergences: errors are returned, naming each failing resource, rather than printed inline, so the report-error tests also assert on the returned error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Port of crossplane-diff 1271905 (#594). Rename internal/diff/renderer/types to internal/diff/result and move into it the comp report types (CompDiffOutput, CompositionDiff, RevisionImpact, XRImpact and their siblings) with their HasChanges verdicts, and the unverified-additions summary, exported as DryRunWarnings, XRDryRunWarnings and CompDryRunWarnings. The renderers import them from result, and diffprocessor no longer imports renderer. No behaviour change. The renderers keep the CLI's divergence: they still do not print errors inline to stderr. Anchor the .gitignore entries for Nix's result symlinks to the repository root, so they no longer ignore the new package. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Port of crossplane-diff ccd756f (#595). DiffComposition now always records the composites it filters out (Manual update policy, revision selector mismatch, being deleted) in ImpactAnalysis as filtered entries, not only in --resource mode. CompositionDiff.Scoped records that the composites were the ones --resource named, and the renderers list filtered entries one by one only when it is set; default discovery still reports them only through the affectedResources counters. Human output and discovery-mode structured output are unchanged. A Manual-policy filtered composite that names a revision carries FilterDetail "compositionRevisionRef: <name>" (the human line ignores it), and XRImpact.Pinned marks a composite --include-manual evaluated that would not adopt the resulting revision. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Merged
5 tasks done
jcogilvie
added a commit
to crossplane-contrib/crossplane-diff
that referenced
this pull request
Oct 21, 2026
The renderers wrote every OutputError to stderr as "ERROR: Kind/name: ...", and the processors also returned the failure, which main printed again as "Error: ...". A failing run reported each error twice in two formats. Stop the renderers printing errors and drop OutputError.FormatError. Structured output keeps errors[] unchanged, and warnings still go to stderr when raised. main prints every returned error, schema-validation ones included, since the renderers no longer print them. DiffComposition now names each failing resource in its returned error, as PerformDiff already did. The integration harness prints a returned error as main does so stderr assertions see what a user sees. Backport of crossplane/cli#436 (19817b2). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add FromXRReport and FromCompReport, which translate the diff engine's reports into plan.Result: one plan per resource diff (the XR's own included) grouped under its composite, typed errors with validation failures, deferred and skipped composites from filter reasons, and a Compositions entry with revision impact. Secret data is redacted and identical changes reached by several inputs are listed once. plan gains three omitempty fields the mapping needs: CompositeResult.Pinned, CompositionResult.ImpactAnalysisSkipped and Result.Notes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
jcogilvie
added a commit
to crossplane-contrib/crossplane-diff
that referenced
this pull request
Oct 21, 2026
The renderers wrote every OutputError to stderr as "ERROR: Kind/name: ...", and the processors also returned the failure, which main printed again as "Error: ...". A failing run reported each error twice in two formats. Stop the renderers printing errors and drop OutputError.FormatError. Structured output keeps errors[] unchanged, and warnings still go to stderr when raised. main prints every returned error, schema-validation ones included, since the renderers no longer print them. DiffComposition now names each failing resource in its returned error, as PerformDiff already did. The integration harness prints a returned error as main does so stderr assertions see what a user sees. Backport of crossplane/cli#436 (19817b2). Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The plan text renderer prints each Composition's revision impact as one line under its plan, and the result's Notes after the summary. A result with neither, like resource simulate's, renders as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
project simulate now prints a field-level plan by default. -o json and -o yaml emit plan.Result through the internal/simulate mapper, and -o diff keeps the engine's human renderer. The command picks the renderer with a small switch; the engine gains no factories. --output-version is hidden and temporary. Only when it is set do -o json|yaml emit crossplane-diff's v1/v2 structured schema, and it is rejected with -o plan or -o diff. The integration harness passes -o diff for the text cases and --output-version for the structured ones, so the existing cases still assert on the old output. New integration cases pin -o plan text and the -o json plan.Result shape for an xr run and a composition run. BREAKING CHANGE: the default output is now plan, not diff, and -o json|yaml emit plan.Result rather than crossplane-diff's schema. Pass -o diff for the previous default output. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Map keys that are not plain identifiers are written as ["key"] in FieldChange paths, and the plan text prints them whole as one level, so annotation and label keys like crossplane.io/composite no longer split into nested levels. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
A comp run analyses composites per composition, so with several compositions plan.Result could not say which composition a composite's impact belongs to. CompositeResult now carries Composition, set by simulate.FromCompReport and omitted for xr runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The integration cases' structured assertions read crossplane-diff's v1/v2 schema, so the harness had to pass --output-version. They now read plan.Result, which project simulate's -o json emits, behind the same builder API, and the harness no longer passes --output-version. WithOutputVersion is gone along with its two call sites. A field change whose old and new values are equal has no leaf FieldChange in plan.Result, so it is asserted as unchanged (no change at or under the path) rather than by value. The diffprocessor test for a failed composition renders its JSON through simulate.FromCompReport. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
WithKeepEqualDiffs names the composed resources whose diff the calculator keeps even when equal, and makes an unchanged composite's impact carry its diffs, so a caller can refine them further (for example, with provider diff servers). The predicate travels on compare.Options, so the calculator and its factory signatures are unchanged. Nil, the default, keeps nothing and leaves every output as it was: every HasChanges and renderer already skips equal diffs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
PlanDiffs plans the engine's resource diffs against the provider diff servers a Planner routes to, once per diff key. Removals stay the engine's, equal diffs are planned only when kept, and kinds no server serves keep their engine plan, never approximate. Planner.CanPlan reports whether a kind is routed. FromXRReport and FromCompReport take those plans, nil meaning none ran, and merge them by diff key: a provider error is an error plan, NotSupported marks the engine's plan approximate, a provider create, update or replace takes over the action and changes, and a kept-equal resource the provider finds unchanged is dropped. A composite one of them changes becomes changed. The not-run note stays only when plans is nil; the command picks NoteProviderPlanSkipped or NoteNoDiffServers when it wires this in. ProviderManaged is the predicate for the engine's WithKeepEqualDiffs: it follows the CRD to the ProviderRevision controlling it, the lookup RelevantProviderImages already did, now factored into one place. Changed is the verdict over a merged plan.Result. The command still passes nil. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
…r dependencies Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Add the generic, nil-default renderer hook DiffOptions.Replaced: a replaced diff's header reads "(requires replacement)" and every summary appends "(N requires replacement)". With a nil hook output is byte-identical. Add simulate.AnnotateXRReport and AnnotateCompReport, which annotate a copy of the engine report with provider plans as terraform does: a forcing field's + line ends " # forces replacement", computed fields print "(known after apply)" (split from an equal line, rewritten on a + line, or added under their deepest ancestor), and sensitive values print "(sensitive value)". ReplacedBy builds the Replaced hook, nil for no plans. An equal diff the provider would change renders as a modified drift block (driftDiff, pending confirmation). Nothing calls these yet. plan.FormatValue is exported for the drift block. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The plan.Result assertion helpers move from internal/diff/testutils to internal/simulate/simtest, and the integration tests (which run project simulate) import them from there. The diffprocessor test for a failed composition asserts on dt.CompDiffOutput instead of mapping it through simulate.FromCompReport. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
plan.Result serializes no object for a plan, so an expectation whose old and new values are equal can only check that nothing changed under the path. Say so where the check is made. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
The debug counts string prints replaced= only when a diff is replaced, so upstream's format is unchanged when there are none. The listed-change key now includes a plan's error, approximate flag and replace paths. driftDiff's comment is a plain doc comment. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Every ResourcePlan now serializes the objects it compares, following terraform's show -json change.before/after, so CI consumers can match a plan to its input and render their own diff: - before: the live object, absent for a create. - after: the desired object, absent for a delete. Project simulate uses the engine's Clean views, the same ones changes[] is diffed from. Resource simulate and the provider/fallback paths use the live and desired objects. A Secret's data and stringData values are redacted in both. ResourcePlan.Desired, which nothing read, is replaced. The result format version moves to 0.3. The integration assertion for an unchanged field now checks its value in before and after again, rather than only that nothing changed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of your changes
Adds an alpha
crossplane project simulate(#367) built on thecrossplane diffengine, ported from crossplane-diff intointernal/diff. It previews how a project's changed Compositions and embedded functions affect the XRs on a live cluster.Builds on #429 (
resource simulate, merged).Work in progress toward #367. Done so far:
Engine port (
internal/diff/**): renderer, types, ref, dockerrun, the core/kubernetes/crossplane clients and the diffprocessor, ported verbatim from crossplane-diff (import rewrites and gci grouping only), plus a client bundleclient.New.Integration tests (
internal/diff/integration): every case from crossplane-diff'sTestDiffIntegrationandTestCompDiffIntegrationat 6a140b6, run against envtest with the real Docker render engine. The case tables are verbatim. OnerunSimulatefunction translates each case's crossplane-diff arguments and runs the realproject simulatecommand through kong.gap: simulate has no repeated --resource scoping, and two carry upstream's own skips.grep -rn 'gap:'lists what is left to enable.Project glue:
project.LoadFunctions(lifted fromrender xr),project.LoadCompositions, an overlayFunctionProviderthat serves project functions by name and falls back to the cluster's, andkube.RESTConfig(now shared withresource simulate).project simulatecommand (alpha).-f/--inputis repeatable and picks the mode from what it's given:comp. XRs are found on the cluster, and the cluster's functions run.xr. Compositions and functions come from the cluster.compandxrdo.-c/--context, impersonation,-n,--name,--include-pinned,--max-iterations,--timeout,--max-concurrency,-o diff|json|yaml,--output-version, the render-backend version flags, and crossplane-diff's other common flags (--ignore-paths,--function-credentials,--dry-run-on,--analyze-on, ...).Engine boundary (tracking upstream): ported from crossplane-diff#591, #593, #594 and #595:
internal/diff/compare.XRReport,CompDiffOutput), and the command renders them.internal/diff/result.The CLI keeps its own error printing: errors print once, via kong, not inline.
Mapper:
internal/simulate.FromXRReportandFromCompReportturn engine reports intoplan.Result, with Secrets redacted and claims kept. It is not wired into the command yet.plan.Result: carries composite grouping, typed errors with validation failures, warnings, revision impact for each Composition, dry-run verification and a deferred count. The text renderer groups plans under their XR.resource simulate's output is unchanged, and its JSONformatVersionis now 0.2.internal/simulate:resource simulate's per-resource planning moves into a reusablePlanner(Plan,PlanWithLive) andRelevantProviderImages, soproject simulatecan plan composed MRs through the provider diff servers.Render: a typed
render.ErrStaleRenderBackend, so the engine can tell a stale render backend apart from other errors.Nix and CI: pinned Crossplane v2.4.1 CRDs and envtest v1.30.3 assets for the tests. The integration tests need Docker for the render engine, and the Nix sandbox has none. So they sit behind an
integrationbuild tag, the same way the existingcompilegatetests do, and lint still covers them. They run through their ownnix run .#integration-testapp andintegration-testsCI job, outside the sandbox. Unit tests andflake checkare unchanged. The integration job writes a-coverpkg=./...profile and uploads it to codecov under theintegrationflag. Codecov merges it with theunittestsupload for the same commit. A later e2e suite fits in as a third tag, app and job.Still to do for #367:
-o planas the default, withjson|yamlemittingplan.Resultthrough one mapper. Then drop--output-versionand crossplane-diff's v1/v2 schemas, and rewrite the structured IT assertions# forces replacementand(known after apply)in-o diffDecisions to review (hard to reverse, even under alpha):
-o planis the default.-o json|yamlemitplan.Result(formatVersion0.2), and-o diffis crossplane-diff's diff.--output-versionis hidden and temporary: it selects crossplane-diff's schema while the integration tests move over, and is then removed.plan.Result:pathis one string. Plain keys join with., list indexes are[N], and any other key is quoted terraform-style as["key"], e.g.metadata.annotations["crossplane.io/composite"]. This lets annotation and label keys round-trip, but json/yaml consumers must parse the grammar to split a path. An additivesegmentslist is an option if that's a burden. Paths from upjet's diff servers don't quote dotted TF map keys (e.g. atagskeya.b), which needs an upjet change.common.ExitStatusthatmainexits with after a run that returns no error. Errors still exit through kong'sFatalIfErrorf.-f/--input(its long form was--project-file),--name,--include-pinned,-c/--context.-fclassification: a single file that isn't a project file and holds no Compositions is treated as XRs. Each one is checked against the cluster's XRDs, as a claim and then as an XR, and anything else (a Configuration, a Provider, a ConfigMap) is rejected with its kind, name and what-faccepts.ERROR: Kind/name: .... kong prints the returned error once, ascrossplane: error: ..., and that error names each failing resource. This deviates from crossplane-diff, which printed both. Structured output'serrors[]is unchanged.diff complists Functions, Compositions and XRDs cluster-wide on startup. That conflicts with the issue's "no cluster-wide work beyond the scoped discovery"; it is flagged here, not fixed yet.Also worth a look:
fix(project):project buildwithspec.paths.apis: "/"no longer packages XRDs and Compositions fromexamples,functionsandoperations. It's a separate commit, easy to cherry-pick on its own.go.yaml.in/yaml/v3v3.0.5,golang.org/x/sysv0.48.0. The port's semver use moved to Masterminds v3.--timeoutcovers the whole run, not each XR.-f -) hasn't been tested end to end.I have:
./nix.sh flake checkto ensure this PR is ready for review.Addedbackport release-x.ylabels to auto-backport this PR.🤖 Generated with Claude Code