Skip to content

feat(PL-6288): surface reconcile status on joy resources - #34

Open
silphid wants to merge 1 commit into
masterfrom
feat/PL-6288/expose-resource-statuses
Open

feat(PL-6288): surface reconcile status on joy resources#34
silphid wants to merge 1 commit into
masterfrom
feat/PL-6288/expose-resource-statuses

Conversation

@silphid

@silphid silphid commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Related PRs — PL-6288

One of 3 coordinated PRs:

Merge order: joy is merged and released; this PR now depends on the tagged joy v0.99.0. infra can merge any time (no-ops until resources carry conditions).


What

Reconcile outcomes now live on each resource's status instead of only in the operator logs.

  • cmd/crd-gen: enable the /status subresource for Release, Environment, Catalog (Project keeps its no-op reconciler and gets none). Also emit the {{- if .Values.installCRDs -}} guard from the generator so task crd-gen output is reproducible — previously the guard had to be re-added by hand after every regen.
  • cmd/operator/status.go: a single generic writeStatus[T, PT] helper, driven by the shared v1alpha1.StatusObject[T] constraint, sets a Ready condition (ReconcileSuccess / ReconcileError) + observedGeneration via UpdateStatus. The write is idempotent — the controller re-enqueues on every update (including our own status writes), so it only calls UpdateStatus when the condition or observedGeneration actually changed, avoiding an infinite reconcile loop. A failed status write is logged, never fatal.
  • Reconcilers: each names its return error and defers writeStatus right after fetching the resource, capturing the outcome of every return path with no per-path edits.
  • Regenerated chart CRDs and the testchart golden files.
  • main_test: refetch resources before mutating them (with retry.RetryOnConflict), now that the operator writes a status subresource on them.

Health mapping (see infra PR)

Ready=True → Healthy · Ready=False → Degraded · no conditions yet → Progressing.

Dependency

Requires github.com/nestoca/joy v0.99.0, which carries the ResourceStatus / StatusObject API.

Testing

  • go build ./... and go vet ./cmd/... clean.
  • testchart run — all 4 chart cases pass (status subresource + schema added to the 3 CRDs; no-crds unchanged).
  • The kind/docker e2e suite (test-app) passes in CI.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added structured status support for Releases, Environments, and Catalogs, including observedGeneration, conditions, and enabled Kubernetes status subresources.
    • Resources now reliably report a readiness condition reflecting reconciliation success or failure.
  • Bug Fixes

    • Improved resilience to concurrent update conflicts during reconciliation.
    • Status updates are now consistently written on all reconciliation paths and avoid unnecessary status update loops.
  • Tests

    • Updated Helm and standard CRD expectation fixtures for the new status schema and subresource behavior.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 59 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 07a44d71-9c51-4553-a8a6-3ff7b1385f12

📥 Commits

Reviewing files that changed from the base of the PR and between 91135ab and d1e4960.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • chart/templates/crds.yaml
  • chart/tests/common-labels/expected.yaml
  • chart/tests/helm/expected.yaml
  • chart/tests/standard/expected.yaml
  • cmd/crd-gen/main.go
  • cmd/operator/main_test.go
  • cmd/operator/reconciler_catalog.go
  • cmd/operator/reconciler_environment.go
  • cmd/operator/reconciler_release.go
  • cmd/operator/status.go
  • go.mod
📝 Walkthrough

Walkthrough

Changes

The operator now generates status-enabled CRDs for Releases, Environments, and Catalogs, records reconciliation outcomes as Ready conditions, and updates reconciliation tests to tolerate resource-version conflicts.

CRD status reporting

Layer / File(s) Summary
CRD status generation
cmd/crd-gen/main.go
CRD generation is guarded by installCRDs; configured resources emit a status subresource.
Generated CRD schemas and fixtures
chart/templates/crds.yaml, chart/tests/*/expected.yaml
CRD schemas define observedGeneration and structured conditions for all three resources, with matching expected manifests.
Reconcile outcome status updates
cmd/operator/status.go, cmd/operator/reconciler_*.go, go.mod
Reconcilers defer status writes, and writeStatus records Ready success or error conditions while avoiding unchanged updates.
Conflict-tolerant reconciliation tests
cmd/operator/main_test.go
Resource mutations retry on conflicts, use the environment name for release namespacing, and clean up through the configured interface.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Suggested reviewers: davidmdm

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: surfacing reconciliation status on Joy resources.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/PL-6288/expose-resource-statuses

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.

@silphid
silphid force-pushed the feat/PL-6288/expose-resource-statuses branch from aee7872 to a0fca21 Compare July 30, 2026 13:32
@silphid silphid changed the title feat(PL6288): surface reconcile status on joy resources feat(PL-6288): surface reconcile status on joy resources Jul 30, 2026
@silphid
silphid force-pushed the feat/PL-6288/expose-resource-statuses branch 3 times, most recently from b9418ba to 03020e4 Compare July 30, 2026 16:06
@silphid
silphid marked this pull request as ready for review July 30, 2026 17:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cmd/operator/main_test.go (1)

600-610: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider asserting the Ready status condition somewhere in this test.

TestHappyReconciliations exercises the full reconcile flow for Catalog/Environment/Release but never asserts .Status.Conditions or .Status.ObservedGeneration, which is the core new behavior this PR introduces. Worth adding a check before the cleanup/delete calls to catch regressions in writeStatus.

🤖 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/operator/main_test.go` around lines 600 - 610, Before the cleanup deletes
in TestHappyReconciliations, fetch the reconciled Catalog, Environment, and
Release resources and assert their Ready status condition and ObservedGeneration
values. Use the existing Kubernetes interfaces and the applied resource
generations to verify writeStatus updated each object, while preserving the
current non-destructive cleanup assertions.
🤖 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/operator/main_test.go`:
- Around line 600-610: Before the cleanup deletes in TestHappyReconciliations,
fetch the reconciled Catalog, Environment, and Release resources and assert
their Ready status condition and ObservedGeneration values. Use the existing
Kubernetes interfaces and the applied resource generations to verify writeStatus
updated each object, while preserving the current non-destructive cleanup
assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 08e2192c-7ed4-457c-8bd3-b3c8120b98c8

📥 Commits

Reviewing files that changed from the base of the PR and between 352d2ea and 03020e4.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • chart/templates/crds.yaml
  • chart/tests/common-labels/expected.yaml
  • chart/tests/helm/expected.yaml
  • chart/tests/standard/expected.yaml
  • cmd/crd-gen/main.go
  • cmd/operator/main_test.go
  • cmd/operator/reconciler_catalog.go
  • cmd/operator/reconciler_environment.go
  • cmd/operator/reconciler_release.go
  • cmd/operator/status.go
  • go.mod

@silphid
silphid force-pushed the feat/PL-6288/expose-resource-statuses branch from 03020e4 to 91135ab Compare July 30, 2026 20:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cmd/operator/status.go (1)

24-64: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Panic during a reconcile would report a false "success" status.

Since retErr is only set via return ..., err statements, a panic in any of the three reconcilers after the defer writeStatus(...) registration leaves retErr == nil, so the deferred call would record ConditionTrue/"Successfully reconciled" despite the crash — potentially masking real failures from anyone consuming the status/health condition. This only matters if nothing upstream (the yoke ctrl.Funcs runtime) already recovers panics and turns them into errors before this point; please confirm. If not already handled upstream, consider recovering within the deferred closure and forcing a failure condition on panic.

🤖 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/operator/status.go` around lines 24 - 64, Verify whether the yoke
ctrl.Funcs runtime converts reconciler panics into errors before deferred status
handling; if not, update each reconciler’s deferred writeStatus closure to
recover panics and pass a failure error instead of the nil retErr, preserving
ConditionFalse/ReconcileError reporting for crashes.
🤖 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/operator/status.go`:
- Around line 24-64: Verify whether the yoke ctrl.Funcs runtime converts
reconciler panics into errors before deferred status handling; if not, update
each reconciler’s deferred writeStatus closure to recover panics and pass a
failure error instead of the nil retErr, preserving
ConditionFalse/ReconcileError reporting for crashes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 00cc42b0-e20d-4e8b-a144-00d2c4479d24

📥 Commits

Reviewing files that changed from the base of the PR and between 03020e4 and 91135ab.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • chart/templates/crds.yaml
  • chart/tests/common-labels/expected.yaml
  • chart/tests/helm/expected.yaml
  • chart/tests/standard/expected.yaml
  • cmd/crd-gen/main.go
  • cmd/operator/main_test.go
  • cmd/operator/reconciler_catalog.go
  • cmd/operator/reconciler_environment.go
  • cmd/operator/reconciler_release.go
  • cmd/operator/status.go
  • go.mod

@silphid
silphid force-pushed the feat/PL-6288/expose-resource-statuses branch from d4caa3f to d1e4960 Compare July 31, 2026 12:26
Comment thread cmd/operator/status.go
changed = true
}

// As the controller re-enqueues on every update, let's avoid an infinite reconcile loop.

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.

I actually don't think this is true. Yes for kubebuilder by default and controller-runtime. false for yoke/k8s/ctrl

Comment thread cmd/operator/status.go
changed := apimeta.SetStatusCondition(&status.Conditions, cond)
if status.ObservedGeneration != obj.GetGeneration() {
status.ObservedGeneration = obj.GetGeneration()
changed = true

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.

If observed generation has changed, I think we should exit early no?

Comment thread cmd/operator/status.go
cond.Reason = "ReconcileSuccess"
cond.Message = "Successfully reconciled"
}

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.

In my experience, before updating a status, it's important to refetch the resource from the API since, you need to check for the most recent generation, and then all of this need to be wrapper in a retry.

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.

Yes, exactly what David said above. ☝️
That is one of the tricky problems I encountered on infra-operator when adding SDM support.

@greiko
greiko removed their request for review August 6, 2026 16:54
Comment thread cmd/operator/status.go
cond.Reason = "ReconcileSuccess"
cond.Message = "Successfully reconciled"
}

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.

Yes, exactly what David said above. ☝️
That is one of the tricky problems I encountered on infra-operator when adding SDM support.

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.

3 participants