Skip to content

feat(design): project and resource simulate#181

Open
haarchri wants to merge 2 commits into
crossplane:mainfrom
haarchri:feature/design-simulate
Open

feat(design): project and resource simulate#181
haarchri wants to merge 2 commits into
crossplane:mainfrom
haarchri:feature/design-simulate

Conversation

@haarchri

@haarchri haarchri commented Jul 5, 2026

Copy link
Copy Markdown
Member

Description of your changes

This proposes crossplane project simulate and crossplane resource simulate: a terraform plan style preview of what a Composition, embedded function, XR, MR, or provider version change would do, both to the cluster and to the cloud behind it.

Rendering and cluster diffing build on crossplane-contrib/crossplane-diff rather than reimplementing it, and the proposal is explicit that converging that work into the CLI happens together with its maintainers, who are proposed reviewers here. The cloud layer drives per-provider plan servers over gRPC, the way render drives function runtimes.

Companion proposal in crossplane/upjet#694 defines the PlanService protocol and the provider-side implementation.

Fixes #

I have:

Need help with this checklist? See the cheat sheet.

haarchri added 2 commits July 5, 2026 23:17
Signed-off-by: Christopher Haar <christopher.haar@upbound.io>
Signed-off-by: Christopher Haar <christopher.haar@upbound.io>
@haarchri haarchri requested review from a team, jcogilvie and tampakrap as code owners July 5, 2026 21:21
@haarchri haarchri requested review from phisco and removed request for a team July 5, 2026 21:21
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@haarchri, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6dd94d6f-1952-412b-aa19-6659d95de0ad

📥 Commits

Reviewing files that changed from the base of the PR and between 40c3db0 and 6c50415.

📒 Files selected for processing (1)
  • design/one-pager-cli-simulate.md

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@haarchri

haarchri commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

We demonstrated the end to end flow, the CLI driving provider plan servers against a live control plane, in the Crossplane community meeting: see the
recording from 18:20 to 39:15.

preview of the revision advance rather than of the apply, and counted
separately in the summary. crossplane-diff reached the same conclusion from
the other direction: its composition diffing excludes Manual-policy
composites by default behind an `--include-manual` flag. This keeps that

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason why we wouldn't keep the same nomenclature?

I went with manual for the flag because Manual is the enumeration value for the compositionUpdatePolicy.

1. **Discover.** Parse the project file, find the Compositions under the
project's API paths, and list the XRs on the target cluster whose type
each Composition composes. `--namespace` and `--name` scope the run.
2. **Prepare.** Build the project's embedded functions locally, exactly as

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a variant of this that pulls these from the live cluster (at least, any not overridden locally) -- unless you mean to scope resource simulate to include -f composition.yaml as well?

I don't think everyone is using projects (yet?) and the folks who aren't will likely still want to diff a composition.

review. The existing maintainers stay code owners of what they built, the
standalone repo enters maintenance with a pointer forward, and every
Crossplane user gets the preview by default: one binary, one release
train, one CI matrix.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing i am attached to that saves a bunch of time in diff development is the integration test suite with setup-envtest.

it's about 7 seconds to run a full IT through kong input -> machine output against a sort-of-fake cluster, versus minutes to spin up the e2e kind cluster. because it's so (relatively) quick, it covers a lot of surface area and will be valuable for keeping regressions to a minimum during the transition.

we'll probably want to port that over for any CLI commands that benefit from testing against a cluster. it could form a foundation of the e2e/IT testing work we've been meaning to tackle.

transplant of the repo, but a joint pass with its maintainers over what
simulate and a first-class diff experience actually need, moving those
pieces into crossplane/cli as shared internals, and leaving behind what the
standalone packaging forced them to build.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this. it seems pretty core to the crossplane experience. in fact my initial diff proposal was a PR to crossplane/crossplane.

fetches what functions ask for from the live cluster), the diff
calculator (rendered against live, including removal detection and nested
XRs), the server-side dry-run that keeps defaulting and webhook mutation
out of the diff, and the composition impact discovery behind

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these already build on c/cli: the impact discovery uses the xrmClient of crossplane trace. and we run all the XRs through crossplane validate.

this will probably mean promoting trace and validate to GA (or at least the libraries they build on)

and a first-class diff command share.
* **What gets simpler in the move.** Living outside the CLI forced
indirection the code no longer needs once it lives inside. The render
seam it uses to drive the CLI's engine from another binary collapses into

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the seam isn't really going to collapse. the real seam is the docker seam between internal render and the CLI itself. we mostly just call the methods that are provided.

* **What gets simpler in the move.** Living outside the CLI forced
indirection the code no longer needs once it lives inside. The render
seam it uses to drive the CLI's engine from another binary collapses into
a direct call, and its loading and validation glue merges with the CLI's

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've polished up the loading and validation logic a little bit in the process of building diff, and submitted the improvements upstream.

rendered counterpart means the change would remove it, and it enters the
plan as a delete instead of silently disappearing from the preview.

Printing then follows one rule: show users their own manifest, annotated.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will be a conscious choice to switch from kubectl diff style syntax to terraform plan syntax.

maybe we should see which way consumers would prefer.

since my use case is largely for CI, i tried to build diff to use an output format that would be compatible with markdown's diff interpreter syntax for code blocks, so line by line replacement:

- foo: bar
+ foo: baz

(@tampakrap), Adam Wolfe-Gordon (@adamwg)
* Status: Draft

## Background

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing i don't see mentioned here is claims. we have a lot of people still on crossplane v1's object model; any specific goal or non-goal about those?

diff does try pretty hard to work against legacy objects.

discovery. Both commands enter the tree with the CLI's `maturity:"alpha"`
tag and graduate the way every other command does.

### Running Plan Servers Like Render Runs Functions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing i found out the hard way is that diff is slow unless you keep containers alive across runs. every run might stand up a crossplane container, all function containers, now provider containers, and then tear them down. we should explicitly account for container orchestration: strong names, maybe even a top level --orphan-containers flag in case you need to run some things serially.

render and crossplane-diff already share. Functions that require extra
resources get them the way crossplane-diff resolves them today:
iteratively, fetching what the pipeline requests from the live cluster
and re-rendering until requirements stabilize, with an iteration cap.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"iteration cap" reminds me of failure modes. diff is designed to fail fast and not emit partial results (for a given resource's tree) to avoid giving a false sense of confidence. is that a design principle we want to adhere to, here, or do we think "best effort" instead?

we should also agree on an error contract for structured mode. diff writes both stderr and the structured payload OutputError but that's a relatively new change for structured mode.

The CLI treats provider packages the way render treats function packages: as
images it can run locally and talk to over gRPC.

From the project's `dependsOn` (or an explicit `--provider-images` override,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does a provider-images override work? how do i know which provider serves a given api group without a project? are we looking into the cluster for this (it's what diff would do)? and if we're querying the cluster anyway, shouldn't we just query it and keep the image override optional?

(i suppose we could also query the provider registry, but that seems like an added dependency we don't want)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants