Skip to content

render: Ignore the observed copy of the XR in --observed-resources - #256

Open
Bham06 wants to merge 1 commit into
crossplane:mainfrom
Bham06:fix-render-observed-xr
Open

render: Ignore the observed copy of the XR in --observed-resources#256
Bham06 wants to merge 1 commit into
crossplane:mainfrom
Bham06:fix-render-observed-xr

Conversation

@Bham06

@Bham06 Bham06 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Description of your changes

crossplane render emits the composite resource as its first output document.
When that output is fed back in via --observed-resources — the iterative
workflow the issue describes — the file contains a UID-less copy of the XR.
Observed resources are keyed by GVK, namespace and name, so that copy
overwrites the real XR in the engine's store and blanks its UID. The ownership
check then matches nothing, and every composed resource is silently dropped
from the output.

The XR is already supplied as the positional argument, so this drops the
redundant observed copy when building the render request.

This is the conservative fix discussed on the issue. @adamwg suggested that
rather than ignoring the observed XR we could merge it into the explicitly
supplied one, so an iterative run could feed back status fields set by a
previous render. That seems useful, but it is a behaviour change layered on top
of a currently-silent data loss bug, so I would suggest landing this first and
treating the merge semantics as a follow-up — happy to pick that up.

Covered by TestFilterObservedXR: the observed copy is dropped for both
namespaced and cluster-scoped XRs, a resource sharing the XR's name and
namespace but not its kind is kept, and the no-copy and empty-input cases are
left untouched.

Fixes #47

I have:

  • Read and followed Crossplane's contribution process.
  • Run ./nix.sh flake check to ensure this PR is ready for review. Ran go build ./..., go vet ./cmd/crossplane/render/..., gofmt, and go test ./cmd/crossplane/render/... locally, all clean; relied on CI for the full flake check.
  • Added or updated unit tests. (Added TestFilterObservedXR.)
  • Linked a PR or a docs tracking issue to document this change. Restores the documented behaviour of --observed-resources; no docs change needed.
  • Added backport release-x.y labels to auto-backport this PR. Left to maintainer discretion, though this may be worth backporting since it is silent data loss.

@Bham06
Bham06 requested review from a team, jcogilvie and tampakrap as code owners August 8, 2026 09:41
@Bham06
Bham06 requested review from negz and removed request for a team August 8, 2026 09:41
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The render conversion now removes an observed resource that matches the supplied composite resource by GVK, namespace, and name. Tests cover namespaced and cluster-scoped resources, unrelated resources, and empty input.

Changes

Observed XR filtering

Layer / File(s) Summary
Composite resource filter
cmd/crossplane/render/convert.go, cmd/crossplane/render/convert_test.go
filterObservedXR removes matching composite resources and preserves other observed resources. Table-driven tests cover resource identity and empty input.
Conversion path integration
cmd/crossplane/render/convert.go
BuildCompositeRequest filters observed resources before protobuf conversion.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: jcogilvie


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Breaking Changes ❌ Error cmd/crossplane/render/convert.go now drops XR-matching observed resources, removing prior behavior; no breaking-change label appears in the PR context. Add the breaking-change label, or revise the change so --observed-resources retains the previous behavior.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The 66-character title clearly describes ignoring the duplicate observed XR and stays under the 72-character limit.
Description check ✅ Passed The description explains the duplicate XR problem, the fix, testing, and issue context.
Linked Issues check ✅ Passed The implementation and tests satisfy issue #47 by filtering the matching observed XR before request conversion.
Out of Scope Changes check ✅ Passed The changes are limited to filtering duplicate observed XRs and adding focused tests for the linked issue.
Feature Gate Requirement ✅ Passed The change is a scoped bug fix in CLI input conversion, not a new experimental feature; HEAD changes no apis/** files and adds no ungated feature.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cmd/crossplane/render/convert_test.go (1)

60-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the required args and reason fields.

Thanks for adding coverage for namespaced and cluster-scoped XRs. The table defines xr and observed directly and omits a reason field. Nest the inputs in an args field, retain want, and add a reason for each case. Include the reason in failure output.

As per path instructions, **/*_test.go must use an args/want pattern and include reason fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/crossplane/render/convert_test.go` around lines 60 - 64, Update the test
table in the relevant conversion test to use an args field containing xr and
observed, retain want, and add a reason field to every case. Pass each case’s
reason into failure output so assertion messages identify the scenario,
following the required args/want test pattern.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cmd/crossplane/render/convert_test.go`:
- Around line 60-64: Update the test table in the relevant conversion test to
use an args field containing xr and observed, retain want, and add a reason
field to every case. Pass each case’s reason into failure output so assertion
messages identify the scenario, following the required args/want test pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8bd22eb3-3840-4cd6-aa17-eaf846038a7b

📥 Commits

Reviewing files that changed from the base of the PR and between d267815 and 4b343cd.

📒 Files selected for processing (2)
  • cmd/crossplane/render/convert.go
  • cmd/crossplane/render/convert_test.go

A render run emits the composite resource as its first output document, so
an --observed-resources file produced by a previous run usually contains a
UID-less copy of the XR. Keyed by GVK, namespace and name, that copy
overwrites the real XR in the engine's store and blanks its UID, which makes
the ownership check drop every composed resource.

The XR is already supplied as the positional argument, so drop the redundant
copy when building the render request.

Fixes crossplane#47

Signed-off-by: Bham06 <heu319@student.bham.ac.uk>
@Bham06
Bham06 force-pushed the fix-render-observed-xr branch from 4b343cd to 1566195 Compare August 10, 2026 08:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cmd/crossplane/render/convert_test.go (1)

110-120: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add namespace and API-version identity cases.

Thanks for covering exact matches, cluster-scoped resources, empty input, and kind mismatches. The table does not verify that namespace and apiVersion are also part of the match. A filter that ignores either field could pass the current tests and remove a valid observed resource. Could you add one case for each mismatch?

The production contract compares GVK, namespace, and name, so each non-match should remain in the result.

Suggested additions
+		"KeepsSameNameDifferentNamespace": {
+			reason: "A resource with the XR's GVK and name in another namespace should be kept.",
+			args: args{
+				xr: composite("example.org/v1", "XR", "default", "my-xr"),
+				observed: []composed.Unstructured{
+					observed("example.org/v1", "XR", "other", "my-xr"),
+				},
+			},
+			want: []string{"example.org/v1/XR/other/my-xr"},
+		},
+		"KeepsSameNameDifferentAPIVersion": {
+			reason: "A resource with the same group, kind, namespace and name in another API version should be kept.",
+			args: args{
+				xr: composite("example.org/v1", "XR", "default", "my-xr"),
+				observed: []composed.Unstructured{
+					observed("example.org/v2", "XR", "default", "my-xr"),
+				},
+			},
+			want: []string{"example.org/v2/XR/default/my-xr"},
+		},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/crossplane/render/convert_test.go` around lines 110 - 120, Add
table-driven cases in the test covering observed resources with the same GVK and
name but a different namespace, and with the same kind, namespace, and name but
a different apiVersion; update the expected results so each remains retained.
Anchor the additions in the existing test table around
“KeepsSameNameDifferentKind” and preserve the contract that matching requires
apiVersion, kind, namespace, and name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cmd/crossplane/render/convert_test.go`:
- Around line 110-120: Add table-driven cases in the test covering observed
resources with the same GVK and name but a different namespace, and with the
same kind, namespace, and name but a different apiVersion; update the expected
results so each remains retained. Anchor the additions in the existing test
table around “KeepsSameNameDifferentKind” and preserve the contract that
matching requires apiVersion, kind, namespace, and name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 181d48f1-801e-4016-80b0-ed9c74c98258

📥 Commits

Reviewing files that changed from the base of the PR and between 4b343cd and 1566195.

📒 Files selected for processing (1)
  • cmd/crossplane/render/convert_test.go

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.

crossplane render --observed-resources drops observed resources when the file also contains the XR

1 participant