fix(operator-web): compose conversions through the storage version#1194
Merged
kimpenhaus merged 3 commits intoJul 12, 2026
Conversation
The conversion webhook only performed a single-hop conversion: it required a direct converter edge from an object's stored apiVersion to the requested desiredAPIVersion. Converters are registered hub-and-spoke (every served version converts to and from the storage version), so the API server's request to convert between two non-storage versions -- which happens whenever an object is persisted under a version that is no longer the storage version and read at a third served version -- had no direct edge and the object was silently dropped, surfacing as "conversion webhook ... returned 0 objects, expected N". Compose the conversion through intermediate versions with a breadth-first search over the registered converter graph, so a hub-and-spoke converter set is sufficient for 3+ served versions. A direct converter, when present, is still selected as the single-hop shortest path, so existing behaviour is unchanged. Closes dotnet#1193 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes conversion webhook behavior for CRDs with 3+ served versions by allowing multi-hop conversions (e.g., v2 → storage → v1) instead of requiring a direct registered converter edge, addressing unreadable resources when objects remain persisted in older stored versions.
Changes:
- Updated
ConversionWebhook<TEntity>.Convertto build and apply a shortest-path conversion chain (BFS) across registered converter edges. - Added unit tests covering composed conversions between non-storage versions, direct hops to/from storage, and unroutable source versions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/KubeOps.Operator.Web/Webhooks/Conversion/ConversionWebhook.cs |
Implements BFS-based conversion chaining across registered converter graph. |
test/KubeOps.Operator.Web.Test/Webhooks/Conversion/ConversionWebhook.Test.cs |
Adds regression tests for composed conversions and existing direct-hop behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
Author
|
@dotnet-policy-service agree |
… a request The conversion chain depends only on the source and desired apiVersions, and the desired version is constant for a request, so the chain is now memoised by source apiVersion. A LIST response typically converts many objects sharing the same stored version; this avoids repeating the breadth-first graph search (and the scans over the conversions list) for every object. Unroutable source versions are cached as null so they are searched only once as well. Adds a batch test covering a repeated source version and an unroutable one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reject conversion requests when an object has no valid apiVersion or no conversion path instead of returning an incomplete object list. Add ConversionWebhook test traits and coverage for invalid objects and partial batch responses.
Collaborator
|
@wasabii I added a follow-up commit that improves conversion error handling:
I could not push directly to the fork branch. It can be applied with: git fetch https://github.com/dotnet/dotnet-operator-sdk.git pr-1194
git cherry-pick 5f7acbc
git pushThanks 😄 |
This was referenced Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #1193.
The conversion webhook only performed a single-hop conversion:
ConversionWebhook<T>.Convertrequired a direct registered converter edge from an object's storedapiVersionto the request'sdesiredAPIVersion, and silently skipped any object it couldn't convert in one step.Converters are registered hub-and-spoke — every served version converts to and from the storage version, exactly as the docs prescribe. But the API server legitimately asks the webhook to convert between two non-storage versions: this happens whenever an object remains persisted under a version that is no longer the storage version (so
.status.storedVersionscontains more than the current storage version) and is then read at a third served version. There is no direct edge for that pair, so the object was dropped and the read failed with:This makes the affected resource unreadable at some versions (kubectl, informers, GC, Helm/Flux) until every stored object is migrated to the current storage version.
How
Convertnow composes the conversion through intermediate versions using a breadth-first search over the registered converter graph, so aA → storage → Bchain is used when there is no directA → Bconverter. A hub-and-spoke converter set is therefore sufficient for 3+ served versions, and the object is no longer silently dropped.A direct converter, when one exists, is found as the single-hop shortest path — so existing behaviour is unchanged.
Tests
Added
ConversionWebhookTest(5 cases) covering the hub-and-spoke set from the docs example:dotnet format --verify-no-changesand the fullKubeOps.Operator.Web.Testsuite pass.🤖 Generated with Claude Code