Skip to content

test(e2e): add composed resource observation test (#6788)#7360

Open
boxcee-interview wants to merge 2 commits into
crossplane:mainfrom
boxcee-interview:main
Open

test(e2e): add composed resource observation test (#6788)#7360
boxcee-interview wants to merge 2 commits into
crossplane:mainfrom
boxcee-interview:main

Conversation

@boxcee-interview
Copy link
Copy Markdown

Closes #6788

What

Add an e2e test that verifies composed resource changes are detected and reflected in XR status within a tight timeout.

Why

Issue #6788 requested a test to verify that Crossplane detects changes to composed resources and reflects them in the XR status. This is important for testing the composition pipeline's observation mechanism.

How

The test uses function-python with require_resources to observe a ConfigMap and write its data to the XR status:

  1. Creates a ConfigMap with initial data
  2. Creates an XR that observes the ConfigMap via the composition pipeline
  3. Verifies the initial observation is reflected in XR status
  4. Modifies the ConfigMap directly using Server-Side Apply
  5. Verifies the XR status reflects the change within 30 seconds

Testing

The test follows the existing e2e test patterns in apiextensions_compositions_test.go, using the same setup/teardown structure as TestRequiredResources and TestCircuitBreaker.

Add an e2e test that verifies composed resource changes are detected
and reflected in XR status within a tight timeout. The test:

- Creates a ConfigMap with initial data
- Creates an XR that observes the ConfigMap via function-python
- Verifies the initial observation is reflected in XR status
- Modifies the ConfigMap directly using Server-Side Apply
- Verifies the XR status reflects the change within 30 seconds

Closes crossplane#6788

Signed-off-by: Moritz Schmitz von Hülst <[email protected]>
@boxcee-interview boxcee-interview requested a review from a team as a code owner May 7, 2026 07:39
@boxcee-interview boxcee-interview requested a review from adamwg May 7, 2026 07:39
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2dfd6ac-8a29-463f-9c0b-38103988444f

📥 Commits

Reviewing files that changed from the base of the PR and between 6cc29a0 and 540953b.

📒 Files selected for processing (3)
  • test/e2e/apiextensions_compositions_test.go
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/composition.yaml
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/definition.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/e2e/apiextensions_compositions_test.go
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/definition.yaml
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/composition.yaml

📝 Walkthrough

Walkthrough

This PR adds a comprehensive end-to-end test (TestComposedResourceObservation) that validates realtime composition observation for namespaced composite resources. The test creates a schema, function, composition, and resource instance to verify that composed resource changes propagate to XR status within tight timeouts.

Changes

Composed Resource Observation E2E Test

Layer / File(s) Summary
Resource Schema Definition
test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/definition.yaml
CompositeResourceDefinition declares a namespaced Test resource with spec.coolField (string) and status.observedData (object with string-valued properties) and resourceReceived boolean.
Function Resource
test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/functions.yaml
Function resource configures the crossplane-contrib/function-python package (v0.4.0) for Python-based composition scripts.
Composition with Observation Logic
test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/composition.yaml
Pipeline-mode Composition runs a Python script that observes a namespaced ConfigMap, detects resource receipt, extracts data.message, and updates composite status with observed data and result messaging.
Composite Resource Instance
test/e2e/manifests/apiextensions/composition/composed-resource-observation/xr.yaml
Test composite resource instance references the observe-composition-namespaced composition with spec.coolField = "test".
E2E Test Verification
test/e2e/apiextensions_compositions_test.go
TestComposedResourceObservation applies prerequisites, creates a ConfigMap, verifies initial observation in XR status, updates the ConfigMap via server-side apply, and asserts XR status reflects the change within a tight timeout to validate realtime composition for namespaced XRs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • phisco
  • negz
  • jbw976
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding an e2e test for composed resource observation, is within the 72 character limit, and directly relates to the changeset.
Description check ✅ Passed The description is well-related to the changeset, explaining the test's purpose, implementation approach, and how it validates composed resource change detection.
Linked Issues check ✅ Passed The PR fulfills the core objectives from #6788: adding an e2e test for namespaced XRs that verifies composed resource changes are detected and reflected in XR status within a timeout.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the e2e test implementation: test code, composition manifests, resource definitions, and example resources. No unrelated modifications present.
Breaking Changes ✅ Passed The breaking change check applies to files under 'apis/' or 'cmd/'. This PR only modifies test files under 'test/e2e/', which are outside the scope of this check.
Feature Gate Requirement ✅ Passed PR adds only test code and test fixtures in test/e2e/ without introducing new experimental features or changes to apis/**.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
test/e2e/apiextensions_compositions_test.go (2)

542-562: ⚡ Quick win

Same t.Errorft.Fatalf concern applies here; also, the modify patch drops the app: crossplane-e2e label.

Two small points:

  1. t.Errorf + return ctx (line 556) shares the same concern as the setup step — downstream assertions become noisy. t.Fatalf is cleaner.
  2. The creation patch (line 517) sets labels: {app: crossplane-e2e}. The modify patch here omits labels entirely. Because both patches are under the same FieldOwner, SSA will relinquish ownership of the app label and it will be removed. This is harmless for the test's purpose, but worth knowing — could you confirm this is intentional?
🛠️ Proposed fix for t.Fatalf
-			if err := cfg.Client().Resources().GetControllerRuntimeClient().Patch(ctx, cm, client.Apply, client.FieldOwner(FieldManager), client.ForceOwnership); err != nil {
-				t.Errorf("failed to modify ConfigMap: %v", err)
-				return ctx
-			}
+			if err := cfg.Client().Resources().GetControllerRuntimeClient().Patch(ctx, cm, client.Apply, client.FieldOwner(FieldManager), client.ForceOwnership); err != nil {
+				t.Fatalf("failed to modify ConfigMap: %v", err)
+			}
🤖 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 `@test/e2e/apiextensions_compositions_test.go` around lines 542 - 562, In the
Assess "ModifyConfigMapDirectly" closure, replace the t.Errorf call that returns
ctx with t.Fatalf to stop the test on patch failure (change the error path in
the closure using t.Fatalf). Also ensure the patch preserves the app label so
SSA doesn't remove it: when building the unstructured cm before calling
cfg.Client().Resources().GetControllerRuntimeClient().Patch(..., client.Apply,
client.FieldOwner(FieldManager), ...), set metadata.labels.app="crossplane-e2e"
(or fetch the existing resource first and modify only data.message) so the
FieldManager/Apply call does not drop the label; confirm if removing the label
was intentional and keep the label-preserving approach if not.

509-528: ⚡ Quick win

Consider using t.Fatalf instead of t.Errorf in the setup step.

t.Errorf marks the test failed but execution continues, so all downstream Assess steps will also run (and likely fail with confusing errors) even though the ConfigMap was never created. t.Fatalf immediately stops the goroutine and gives a cleaner failure signal. The existing ModifyConfigMapDirectly step at line 556 has the same pattern.

Contrast this with TestCircuitBreaker's inline step (line 387), which uses t.Logf because failures there are non-critical; here the ConfigMap must exist for the test to make sense.

🛠️ Proposed fix
-			if err := cfg.Client().Resources().GetControllerRuntimeClient().Patch(ctx, cm, client.Apply, client.FieldOwner(FieldManager), client.ForceOwnership); err != nil {
-				t.Errorf("failed to create ConfigMap: %v", err)
-				return ctx
-			}
+			if err := cfg.Client().Resources().GetControllerRuntimeClient().Patch(ctx, cm, client.Apply, client.FieldOwner(FieldManager), client.ForceOwnership); err != nil {
+				t.Fatalf("failed to create ConfigMap: %v", err)
+			}
🤖 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 `@test/e2e/apiextensions_compositions_test.go` around lines 509 - 528, Replace
the non-fatal test failure in the setup step by calling t.Fatalf instead of
t.Errorf inside the WithSetup("CreateConfigMap", func(ctx context.Context, t
*testing.T, cfg *envconf.Config) ...) so the test goroutine stops immediately
when the ConfigMap creation Patch fails; do the same change for the similar
setup helper used by ModifyConfigMapDirectly so that creation failures abort the
test rather than allowing downstream Assess steps to run.
🤖 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.

Inline comments:
In `@test/e2e/apiextensions_compositions_test.go`:
- Around line 536-538: The status schema in the CompositeResourceDefinition's
definition.yaml is missing the status.resourceReceived property so Kubernetes
prunes the field written by the composition (see composition.yaml usage); add
resourceReceived: type: boolean under status.properties in that definition.yaml
so status.resourceReceived is declared and persisted during reconciliation.

In
`@test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/composition.yaml`:
- Line 17: The YAML uses an invalid block scalar indicator "|\\" under the
script key which causes YAML parsers to fail; edit the composition.yaml entry
for the script key (the line starting with "script:") and remove the trailing
backslash so it reads "script: |" (i.e., replace "|\\" with a plain pipe),
preserving the existing indented block content below it.

---

Nitpick comments:
In `@test/e2e/apiextensions_compositions_test.go`:
- Around line 542-562: In the Assess "ModifyConfigMapDirectly" closure, replace
the t.Errorf call that returns ctx with t.Fatalf to stop the test on patch
failure (change the error path in the closure using t.Fatalf). Also ensure the
patch preserves the app label so SSA doesn't remove it: when building the
unstructured cm before calling
cfg.Client().Resources().GetControllerRuntimeClient().Patch(..., client.Apply,
client.FieldOwner(FieldManager), ...), set metadata.labels.app="crossplane-e2e"
(or fetch the existing resource first and modify only data.message) so the
FieldManager/Apply call does not drop the label; confirm if removing the label
was intentional and keep the label-preserving approach if not.
- Around line 509-528: Replace the non-fatal test failure in the setup step by
calling t.Fatalf instead of t.Errorf inside the WithSetup("CreateConfigMap",
func(ctx context.Context, t *testing.T, cfg *envconf.Config) ...) so the test
goroutine stops immediately when the ConfigMap creation Patch fails; do the same
change for the similar setup helper used by ModifyConfigMapDirectly so that
creation failures abort the test rather than allowing downstream Assess steps to
run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79709d10-fb60-41c8-9fce-7dd394d7c806

📥 Commits

Reviewing files that changed from the base of the PR and between 71e233b and 6cc29a0.

📒 Files selected for processing (5)
  • test/e2e/apiextensions_compositions_test.go
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/composition.yaml
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/definition.yaml
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/setup/functions.yaml
  • test/e2e/manifests/apiextensions/composition/composed-resource-observation/xr.yaml

Comment thread test/e2e/apiextensions_compositions_test.go
- Fix invalid YAML block scalar indicator (|\ -> |) in composition.yaml
- Add resourceReceived status field to definition.yaml schema
- Use t.Fatalf instead of t.Errorf in ConfigMap setup steps

Signed-off-by: Moritz Schmitz von Hülst <[email protected]>
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.

E2E tests don't validate realtime composition performance for namespaced XRs

1 participant