Repository navigation
Sync toolchain, finalizers and chart with auth0-operator - #14
Merged
Merged
Conversation
Brings the toolchain in line with alethic/auth0-operator, which moved to .NET 10 and KubeOps 13 while this repository stayed on .NET 9 / KubeOps 9.11.8. Toolchain: - global.json to SDK 10.0.100, MSTest.Sdk 4.3.3, NoTargets 3.7.134 - operator and test projects to net10.0 - KubeOps.Operator/.Generator and kubeops.cli to 13.1.0, with KubeOpsCli pointed at the tool manifest - CI installs .NET 10 (the .NET 8 step stays because the GitVersion 5.x tool runs on it) and restores dotnet tools KubeOps 9 -> 13 API: - IEntityController and IEntityFinalizer moved under KubeOps.Abstractions.Reconciliation and now return ReconciliationResult<TEntity>. The EntityRequeue<TEntity> delegate is gone; the next pass is scheduled from the returned result instead, so every Requeue call becomes a Success/Failure with a requeue interval and controllers no longer take a requeue parameter. - A failed delete returns Failure so the finalizer stays attached and the entity is retried, matching the previous requeue behaviour. The finalizer identifiers written into metadata.finalizers are unchanged: KubeOps derives them from the finalizer class name and the algorithm is byte-identical between 9.11.8 and 13.1.0, so entities already carrying seq.k8s.datalust.co/v1alpha1*finalizer still resolve and can drain. KubernetesClient 17 -> 19 (pulled in by KubeOps 13) removes every generated model constructor, leaving property initializers only. All model construction is rewritten accordingly, and the two ListAsync calls now name the cancellationToken argument because v13 inserted a fieldSelector parameter ahead of it. Generated CRDs and RBAC are no longer committed, matching auth0-operator: config/ is gitignored and the chart project takes a ProjectReference on the operator so generation runs before the chart is packaged, failing the build if no CRDs were produced. Note that KubeOps 13 defaults ReconcileStrategy to ByGeneration, so metadata-only and status writes no longer retrigger reconciliation; periodic requeue still detects drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
Fixes the two compile errors from the previous CI run: a multi-line V1Secret/V1ObjectMeta construction in the ApiKey controller that the earlier sweep missed, now a property initializer like the rest. Version-independent finalizers: - Finalizer classes move to Finalizers/ and lose their version prefix (AlertFinalizer, not V1alpha1AlertFinalizer). KubeOps derives the identifier in metadata.finalizers from the class name, so naming them after the API version mints a new identifier on every version bump and strands the old one on live entities -- and the reconciler returns without removing a finalizer it cannot resolve, wedging the entity in Terminating forever. - Finalizers/Legacy holds a shim per retired identifier so entities carrying seq.k8s.datalust.co/v1alpha1*finalizer still drain. These are never attached and must never be deleted. - EntityFinalizers is the identifier table, deriving each string from the finalizer type so a class rename cannot silently desync it. - AutoAttachFinalizers is disabled operator-wide; V1alpha1Controller owns attachment, adding the current identifier and stripping retired ones on every live reconciliation. Program.cs binds the operator options before building the operator so it can set MaxParallelReconciliations (new, default 4, bounding the burst of Seq API calls on startup) and AutoAttachFinalizers. It stays on the generic host: the web host auth0-operator uses is there to serve conversion webhooks, which this operator has none of. The chart now ships CRDs as templates rather than from crds/, via a YamlDotNet rewrite task that injects the chart labels and annotations and wraps each document in a crds.enabled guard. Helm installs crds/ once and never updates it, so schema changes previously required applying the CRDs by hand. New values crds.enabled and crds.keep control installation and retention. The webhook caBundle rewriting auth0-operator does is deliberately omitted; there is no webhook, Service or TLS secret here to point at, and it has to be added alongside those. The dist project gains the PrePrepare/Prepare two-pass so the operator is compiled and then rebuilt in Release with resource generation on before anything is published; the KubeOps generator otherwise skips an incremental build and leaves config/ stale, which now means absent. CI actions move to current majors: ubuntu-24.04, checkout v7, setup-dotnet v6, upload-artifact v7, download-artifact v8, podman-login v2 and GitVersion 6 via gittools v4, clearing the Node 20 deprecation warnings. GitVersion 6 requires the config fix in the same commit: mode must be ContinuousDelivery per branch (6's ContinuousDeployment strips the prerelease label, so main would build clean release versions), tag becomes label, and increment is pinned on feature, pull-request, hotfix and unknown, which otherwise inherit develop's Minor. The image and chart push paths stay under the repository, not the owner, so published locations do not move. Container env uses ASPNETCORE_HTTP_PORTS instead of the deprecated ASPNETCORE_URLS. Both are inert while the operator runs on the generic host; this is the form to carry forward if it ever serves HTTP. Also drops the Microsoft.Extensions.Caching.Memory and System.Text.Json package references, which NU1510 flags as redundant on net10.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
The dist project failed to load with MSB4025: an XML comment cannot contain '--', and the two-pass comment used it as an em dash. GitVersion 6 also changed the default base version from 0.1.0 to 0.0.1, and this repository has no version tags, so every version derives from that default. The build reported 0.0.1-PullRequest14.138 where GitVersion 5 reported 0.1.0-PullRequest0014.136, which would walk the published image and chart versions backwards on main. Pin next-version to 0.1.0 to keep the version base where it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
TestHelm ran under ContinueOnError, so 'helm template' could report a broken chart without failing CI. That was tolerable while the CRDs were static files copied into crds/, but they are now rewritten into templates, and 'helm package' only tars templates without rendering them, so an unrenderable CRD template would ship and surface only at install time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
Gating on 'helm template' broke the build with MSB3077: helm exited 0 and the chart rendered correctly, but Exec reports any stderr output as an error, and --debug writes its progress there. Drop --debug and set IgnoreStandardErrorWarningFormat, which stops Exec inferring failure from output text while still failing the task on a non-zero exit. The render is now genuinely gated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
The CRDs now ship as chart templates so that helm upgrade applies schema changes, but a release installed before that change owns its CRDs outside Helm, and Helm refuses to adopt a resource whose live metadata does not name the release. That check reads the object in the cluster, not the rendered manifest, so the chart cannot annotate its way out of it. Document the label and annotation to apply once before upgrading, and the crds.enabled and crds.keep values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
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.
Brings this repository in line with
alethic/auth0-operator, which moved to .NET 10 and KubeOps 13 and reworked its finalizers, chart and build while this repository stayed on .NET 9 / KubeOps 9.11.8.Still deliberately not included, from the same review: the
.slnxmigration, the CI test job, compare-before-write (NeedsUpdate), owner references to the parent Instance,spec.policy/spec.init, deprecated-field events, and the whole Auth0 rate-limiting stack (token bucket, adaptive pacer, circuit breaker), which has no counterpart in Seq's API.Toolchain
global.jsonto SDK 10.0.100, MSTest.Sdk 4.3.3, NoTargets 3.7.134; both projects tonet10.0KubeOps.Operator/.Generatorandkubeops.clito 13.1.0, withKubeOpsClipointed at the tool manifestMicrosoft.Extensions.Caching.MemoryandSystem.Text.Jsonreferences NU1510 flags as redundant on net10.0ASPNETCORE_HTTP_PORTSinstead of the deprecatedASPNETCORE_URLS. Both are inert while the operator runs on the generic host; this is the form to carry forward if it ever serves HTTP.KubeOps 9 → 13 API
IEntityControllerandIEntityFinalizermoved underKubeOps.Abstractions.Reconciliationand now returnReconciliationResult<TEntity>. TheEntityRequeue<TEntity>delegate is gone — the next pass is scheduled from the returned result, so everyRequeuecall becomes aSuccess/Failurecarrying an interval and controllers no longer take a requeue parameter. A failed delete returnsFailureso the finalizer stays attached and the entity is retried, matching the previous behaviour.Program.csbinds the operator options before building the operator so it can setMaxParallelReconciliations(new, default 4) and disableAutoAttachFinalizers. It stays on the generic host: the web host auth0-operator uses is there to serve conversion webhooks, which this operator has none of.Version-independent finalizers
KubeOps derives the identifier in
metadata.finalizersfrom the finalizer's class name. Naming finalizers after the API version mints a new identifier on every version bump and strands the old one on live entities — and the reconciler inspectsFinalizers()[0]only and returns without removing it when nothing is registered under that identifier, wedging the entity inTerminatingforever. This is the failure mode that bit auth0-operator.Finalizers/and lose their version prefix (AlertFinalizer, notV1alpha1AlertFinalizer). These names must not change again.Finalizers/Legacy/holds a shim per retired identifier so entities carryingseq.k8s.datalust.co/v1alpha1*finalizerstill drain. Never delete one.EntityFinalizersis the identifier table, deriving each string from the finalizer type so a class rename cannot silently desync it.AutoAttachFinalizersis off operator-wide;V1alpha1Controller.ReconcileFinalizersadds the current identifier and strips retired ones on every live reconciliation.InstanceFinalizernow delegates to the controller like every other kind, where the old one was a bare no-op, so Instance deletion emits that controller's "unsupported operation" warning.KubernetesClient 17 → 19 (transitive)
KubernetesClient 19 removes every generated model constructor, leaving property initializers only, so all 28 model construction sites are rewritten. Two related traps:
IKubernetesClient.ListAsyncgained afieldSelectorparameter beforecancellationToken. Both existing calls passed it positionally, so the token would have bound to the wrong parameter. Both now name the argument.IntstrIntOrStringwas renamedIntOrString. The type is never named here and the implicitstring/intconversions survive.The property sets of all 35 k8s model types this repository references were diffed between 17.0.14 and 19.0.2: nothing was removed, so the constructor removal was the whole model-level break.
Chart
CRDs now ship as templates rather than from the chart's
crds/directory, via a YamlDotNet rewrite task that injects the chart labels and annotations and wraps each document in acrds.enabledguard. Helm installscrds/exactly once and never updates it, so schema changes previously had to be applied by hand. New valuescrds.enabledandcrds.keep.This requires a one-time migration on existing releases — Helm refuses to adopt a CRD whose live metadata does not name the release, and that check reads the cluster object, not the rendered manifest, so the chart cannot annotate its way out of it. The commands are documented in the README's new Upgrading section. Fresh installs need nothing.
The webhook
caBundlerewriting auth0-operator does is deliberately omitted: there is no webhook, Service or TLS secret here to point at, and it has to be added alongside those on the day a second API version lands.Generated CRDs and RBAC are no longer committed
src/Alethic.Seq.Operator/config/is gitignored, matching auth0-operator. Two things were needed to make that safe:dotnet tool restore, so the KubeOps CLI was absent and generation had been failing silently all along (the MSBuild target usesIgnoreExitCode/ContinueOnError).ProjectReferenceon the operator, and the dist project gains auth0-operator'sPrePrepare/Preparetwo-pass, so the operator is compiled and then rebuilt in Release with generation on before anything is published. The build fails with a clear message if no CRDs were produced.The chart's committed
templates/clusterrole.yamlis now the sole RBAC source of truth and must be synced by hand from the generatedoperator-role.yamlwhen[EntityRbac]changes.CI and versioning
Actions move to current majors (ubuntu-24.04, checkout v7, setup-dotnet v6, upload-artifact v7, download-artifact v8, podman-login v2, GitVersion 6 via gittools v4), clearing the Node 20 deprecation warnings.
helm templatenow gates the build, so a chart that does not render fails CI instead of shipping.GitVersion 6 required two config changes in the same commit:
mode: ContinuousDeliveryper branch. v6'sContinuousDeploymentstrips the prerelease label, somainwould have built clean release versions;tagbecomeslabel;incrementis pinned onfeature/pull-request/hotfix/unknown, which otherwise inheritdevelop's Minor.next-version: 0.1.0. This repository has no version tags, so every version derives from the configured base, and v6 changed that default from 0.1.0 to 0.0.1 — the first run under v6 produced0.0.1-PullRequest14.138, which would have walked the published image and chart versions backwards. Pinned to preserve the existing base.Image and chart push paths stay under
github.repositoryrather than moving torepository_owner, and the release job keeps its explicitpermissions:block, so published locations do not move.Behavioural notes
ReconcileStrategy, defaulting toByGeneration; 9.11.8 reconciled on every watch event. Metadata-only and status writes no longer retrigger reconciliation, which breaks the status-write feedback loop; periodic requeue still detects drift.MaxParallelReconciliationsbounds the burst of Seq API calls on startup.Testing
Not compiled locally. No .NET SDK was available and egress policy blocked every .NET SDK download host, so CI is the compiler for this change. In place of a build, each API was verified against source:
buehler/dotnet-operator-sdkdiffed atv13.1.0againstv9.11.8(including confirmingFinalizerNameis byte-identical across the two, so existing finalizer identifiers still resolve), andkubernetes-client/csharpplus bothKubernetesClientpackages diffed at 17.0.14 against 19.0.2. Project and workflow files are parsed locally with real XML and YAML parsers before each push.Unrelated pre-existing issue noticed and left alone:
src/dist-tests/dist-tests.msbuildprojstill listsIKVM.ByteCode.Teststargets from the template it was copied from, so thetestsartifact CI uploads is empty — confirmed by this PR's runs, where it is 247 bytes.🤖 Generated with Claude Code
https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs