Repository navigation
Conversation
9dff52a to
881e787
Compare
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>
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>
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>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
… errors Reject --max-concurrency=0 and bad --name/-n in Validate, skip malformed pipeline steps, join render errors with processing errors, note missing diff servers only when resources were considered, treat each -f value as one path, and update the help for pinned composites. 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>
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>
…sources 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>
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>
Docker credential helpers live on the host PATH, not in Nix. 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>
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>
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>
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. Provider diff servers then plan each composed managed resource.Builds on #429 (
resource simulate, merged).Engine port (
internal/diff/**): the renderer, types, ref, dockerrun, the core/kubernetes/crossplane clients and the diffprocessor, ported from crossplane-diff (import rewrites and gci grouping only), plus a client bundleclient.New. It tracks upstream through crossplane-diff#591, #593, #594, #595, #597 and #598:internal/diff/compare.XRReport,CompDiffOutput), and the command renders them.internal/diff/result.CLI-only engine additions, written to be upstreamable: an opt-in per-XR budget (
WithXRTimeout), whose clock pauses while shared work runs (OutsideXRDeadline), and an after-XR hook (WithAfterXRDiff).Integration tests (
internal/diff/integration): every case from crossplane-diff'sTestDiffIntegrationandTestCompDiffIntegration, run against envtest with the real Docker render engine. OnerunSimulatefunction translates each case's crossplane-diff arguments and runs the realproject simulatecommand through kong. Structured assertions checkplan.Result.simtest.Expect()/AssertStructured), since every mode emits the sameplan.Result. Each mode keeps its own matching rules, so no check got weaker or stricter.TestProjectSimulateIntegrationruns the command on real project files: malformed and empty projects, a project mixed with other inputs, a plain project that must match Composition mode's result, an embedded function that must win over a same-named cluster Function, and a provider dependency that must decide which diff server is used (through a test-onlySimulateHooks.Servers).CrossNamespaceResourceCollision,AddedResourceRetainsCompositionAuthoredStatus). Nogap:skips remain.nix run .#integration-test, or./nix.sh run .#integration-testin a container).Project glue:
project.LoadFunctions(lifted fromrender xr),project.LoadCompositionsandproject.LoadProviderDependencies.FunctionProviderthat serves project functions by name and falls back to the cluster's.project push.kube.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.-f: commas are part of the path; repeat-ffor more inputs.--name,-n,--max-concurrency,--max-iterations,--timeout) fail during parsing, before any function build or server start.-c/--context, impersonation,-n,--name(repeatable, comma-separated, acceptsns/name),--include-pinned,--max-iterations,--timeout,--max-concurrency,--cache-dir,-o plan|diff|json|yaml, the render-backend version flags, and crossplane-diff's other common flags (--ignore-paths,--function-credentials,--dry-run-on,--analyze-on, ...).Provider plans: each composed managed resource is planned by its provider's diff server, with
resource simulate'sPlanner, now shared ininternal/simulate. Unchanged provider-managed resources are planned too, so cloud drift shows.--skip-plan,--providers,--image-configsand--session-id. Planning is on by default.--providersoverrides. Otherwise project mode uses the project's provider dependencies, and Composition and XR modes start the cluster's providers on demand for the kinds actually rendered. A failed lookup for a kind skips that kind with one warning.approximate.Output:
-o planis the default terraform-style view.-o json|yamlemitplan.Result(formatVersion0.3) through one mapper (internal/simulate.FromXRReportandFromCompReport).-o diffis crossplane-diff's diff, plus inline# forces replacement,(known after apply)and a(requires replacement)header and summary count, but only when provider plans ran. Without them it is byte-identical to crossplane-diff.plan.Result: each plan carriesbefore(live) andafter(rendered), the cleaned objects the diff compares, with Secret values redacted. It also carries composite grouping, typed errors with validation failures, warnings, revision impact for each Composition, dry-run verification, a deferred count, and notes. The notes say when the provider layer was skipped or no diff server served the resources.resource simulateshares the model, so its JSON gainsbefore/afterand the same stripping.Render: a typed
render.ErrStaleRenderBackend, so the engine can tell a stale render backend apart from other errors.Pinned XRs: an XR on a Manual policy whose pinned revision calls a changed embedded function is rendered against that revision, with the project's functions, and reported with
pinned: trueand adetailnaming the revision. Otherwise pinned XRs are deferred, still withpinned: true.Nix and CI: pinned Crossplane v2.4.1 CRDs and envtest v1.30.3 assets (Linux and macOS, amd64 and arm64) for the tests. The integration tests need Docker for the render engine, and the Nix sandbox has none.
integrationbuild tag, the same way the existingcompilegatetests do, and lint still covers them.nix run .#integration-testapp andintegration-testsCI job, outside the sandbox. Unit tests andflake checkare unchanged. The app keeps the host PATH (nix inputs first) so Docker's credential helpers resolve.-coverpkg=./...profile to codecov under theintegrationflag, and codecov merges it with theunittestsupload for the same commit.Still to do for #367:
crossplane project simulateandcrossplane resource simulatedocs#1162, after this PR passes review.Decisions to review (hard to reverse, even under alpha):
project simulatewrites into the project. In project mode, simulate builds the embedded functions the same wayproject buildandrender xrdo. That generates language schemas into the project'sschemas/directory as a side effect. For a preview command, that may be unexpected: running it in CI or on a clean checkout leaves a modified working tree. Should simulate generate schemas into a temporary directory instead, leaving the project untouched? Or is writing them acceptable, sincerender xralready does it and the schemas are needed to build the functions anyway? If we change it, shouldrender xrchange with it?-o planis the default,-o json|yamlemitplan.Result, and crossplane-diff's v1/v2 JSON schemas are gone.plan.Resultfield shapes: every summary count is always present (deferredincluded); a composite'sreasonis a camelCase enum (manualPolicy,revisionSelectorMismatch,deleting,unknown), mapped explicitly from the engine's values so an upstream rename can't silently change the contract; an error's resource iserrors[].ref;pinnedis true for every XR on a Manual policy. Every input mode emits this one shape, butcompositionsandcompositesareomitempty, so an XR-mode result has nocompositionskey rather than[]. Do reviewers agree, or should both always be present so consumers see the same keys whatever-fis given?before/afterinplan.Result: they follow terraform'sshow -jsonand are null for a create or delete respectively. A resource whose only change is provider-side drift has no objects.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.--timeout(default 2m, must be > 0) is a per-XR budget covering the XR's render and its resources' provider plans.-f/--input,--name,--include-pinned,-c/--context,--skip-plan,--providers,--image-configs,--session-id.-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. 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. Structured output'serrors[]is unchanged.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.resource simulatechanges:internal/simulate, including fix(simulate): stop planning server-defaulted and late-initialized fields as removals in the approximate diff #441's CRD defaulting and Simulate development runtime #443's Development runtime, soproject simulate's provider plans get both.--timeout 0means no per-plan limit.go.yaml.in/yaml/v3v3.0.5,golang.org/x/sysv0.48.0. The port's semver use moved to Masterminds v3.-f -) hasn't been tested end to end.I have:
./nix.sh flake checkto ensure this PR is ready for review.crossplane project simulateandcrossplane resource simulatedocs#1162.Addedbackport release-x.ylabels to auto-backport this PR.🤖 Generated with Claude Code