refactor: Drop internal/declarative/v2 package#3358
Conversation
Relocates everything that lived in the v2 package into the layer it belongs to per ADR 004/005: - Reconciler + setup → internal/controller/manifest/ - Spec struct → internal/manifest/spec/ - Cached manifest parser → internal/manifest/parser/ - Resource transforms + render-side helpers → internal/service/manifest/render/ - ExistsStateCheck → internal/manifest/statecheck/ - ResourceList → internal/controller/manifest/ (private) Drops the Object abstraction (replaced by *v1beta2.Manifest), removes the generated mock_v2 package, consolidates duplicated constants onto shared.OwnedByFormat / shared.DefaultRemoteNamespace, lifts labelsremoval.NewManagedByLabelRemovalService construction up to cmd/main.go (ADR 002), turns SpecResolver into a consumer interface on the reconciler (ADR 001), and pins the manifestctrl import alias. Closes kyma-project#2745
Re-add the manifestctrl alias to the two integration suite_test.go files where the formatter dropped it, satisfying the importas linter rule (.golangci.yaml line 64). Update unit-test-coverage-lifecycle-manager.yaml: - internal/service/manifest/render: 96 -> 84.1 (testObj shim removal + Object guard deletion lowered the line count denominator) - internal/manifest/statecheck: 71 -> 66.1 (ExistsStateCheck added) - internal/controller/manifest: 4.5 (new package floor)
- Centralise resource ID format: ResourceList.objectID delegates to shared.Resource.ID() so the controller and api/shared cannot drift. - Add unit tests for ExistsStateCheck (success + IgnoreNotFound + error). - Add unit tests for CachedManifestParser (cache hit/miss, eviction, deep-copy isolation, error path). - Add behavioural tests for DockerImageLocalizationTransform. - Lowercase the two error sentinels with no external callers (errManagerInErrorState, errResourceSyncDiffInSameOCILayer). - Drop the pointer indirection in CachedManifestParser.Parse. - Update coverage YAML floors: - internal/service/manifest/render: 84.1 -> 92.5 - internal/manifest/statecheck: 66.1 -> 74.2 (above the original 71) - internal/manifest/parser: 17.6 (new) - internal/manifest/spec: 92.3 (new entry; tests already existed)
It only exercises exported API; drop the //nolint:testpackage marker.
There was a problem hiding this comment.
Pull request overview
This PR removes the internal/declarative/v2 package by relocating its responsibilities into more appropriate layers (controller/manifest, manifest/spec+parser+statecheck, and service/manifest/render), updating call sites, tests, and documentation accordingly.
Changes:
- Migrates reconciler/setup and related helper types into
internal/controller/manifest, removing the v2 “declarative” abstraction layer. - Introduces dedicated
internal/manifest/spec+internal/manifest/parser+internal/manifest/statecheckpackages to replace v2 equivalents. - Refactors manifest rendering transforms into
internal/service/manifest/renderto operate directly on*v1beta2.Manifest.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| unit-test-coverage-lifecycle-manager.yaml | Adds coverage thresholds for newly introduced/relocated packages. |
| tests/integration/controller/manifest/suite_test.go | Updates integration suite wiring to new manifest controller + parser/render/statecheck packages. |
| tests/integration/controller/manifest/oci_reg_secret/suite_test.go | Same as above for OCI registry secret suite; adjusts imports and reconciler construction. |
| tests/integration/controller/manifest/custom_resource_check/suite_test.go | Same as above for custom resource check suite; injects managed label removal service. |
| tests/e2e/utils_test.go | Switches env var constant reference from declarative v2 to render package. |
| internal/service/manifest/render/transform.go | Adds render-local transform and parser interfaces (new abstraction boundary). |
| internal/service/manifest/render/service.go | Updates render service to consume spec.Spec + new transform/parser interfaces. |
| internal/service/manifest/render/service_test.go | Updates render service unit tests to use spec.Spec and *v1beta2.Manifest in transforms. |
| internal/service/manifest/render/image_pull_secret_transform.go | Moves/renames transform into render package and drops old Object type assertion. |
| internal/service/manifest/render/image_pull_secret_transform_test.go | Updates tests to new render package APIs/constants. |
| internal/service/manifest/render/docker_image_localization_test.go | Adds focused unit tests for image localization transform behavior. |
| internal/service/manifest/render/default_transforms.go | Moves default transforms into render package and consolidates constants onto shared. |
| internal/service/manifest/render/default_transforms_test.go | Updates tests to new transform signatures and package paths. |
| internal/manifest/statecheck/exists_state_check.go | Moves ExistsStateCheck into dedicated statecheck package. |
| internal/manifest/statecheck/exists_state_check_test.go | Adds unit tests for ExistsStateCheck behavior. |
| internal/manifest/spec/spec.go | Introduces new spec.Spec type replacing declarative v2 Spec. |
| internal/manifest/spec/resolver.go | Updates resolver to return *spec.Spec instead of v2 Spec. |
| internal/manifest/spec/resolver_test.go | Updates resolver tests for new spec.Spec. |
| internal/manifest/parser/cached_manifest_parser.go | Adds cached manifest parser implementation (TTL-based) in new package. |
| internal/manifest/parser/cached_manifest_parser_test.go | Adds unit tests for caching/deep-copy/eviction/error behavior. |
| internal/declarative/v2/spec.go | Removes declarative v2 Spec/SpecResolver types (deleted). |
| internal/declarative/v2/resource.go | Removes declarative v2 ResourceList implementation (deleted). |
| internal/declarative/v2/object.go | Removes declarative v2 Object abstraction (deleted). |
| internal/declarative/v2/mock/object.go | Removes generated mock package (deleted). |
| internal/declarative/v2/inmemory_rendered.go | Removes v2 in-memory parser implementation (deleted). |
| internal/controller/manifest/setup.go | Updates setup to use new manifest controller interfaces and injected label removal service. |
| internal/controller/manifest/resource_list.go | Reintroduces ResourceList under controller/manifest with ID consistency via shared.Resource. |
| internal/controller/manifest/resource_list_test.go | Updates tests to new package path and enables t.Parallel(). |
| internal/controller/manifest/controller.go | Refactors reconciler, inlines collaborator interfaces, and adopts new spec/parser/render/statecheck boundaries. |
| internal/controller/manifest/controller_test.go | Updates pruneResource tests to use shared.DefaultRemoteNamespace. |
| docs/contributor/resources/02-manifest.md | Updates documentation reference from declarative library to render service. |
| docs/contributor/02-controllers.md | Updates controller documentation to describe the new reconciliation flow. |
| cmd/main.go | Updates manifest reconciler wiring: new parser, render composition, and injected managed-by label removal service. |
| cmd/composition/service/manifest/render/render.go | Updates render composition to use new parser package and render transforms. |
| .golangci.yaml | Pins manifestctrl alias for controller/manifest and removes declarative v2 alias entries. |
Comments suppressed due to low confidence (3)
internal/controller/manifest/controller.go:253
prepareReconcileno longer special-casesaccessmanager.ErrAccessSecretNotFound. This changes behavior vs the delete path (and the previous install path) and can leave Manifests stuck in error/requeue without clearing finalizers/metrics when the SKR access secret is gone. Consider routing this error throughcleanupManifest, same asprepareDeleteReconcile.
internal/service/manifest/render/image_pull_secret_transform.go:17- Typo in error message: "already exits" -> "already exists".
internal/manifest/statecheck/exists_state_check.go:14 - The doc comment says StateReady is reported once resources are "retrievable", but the implementation explicitly ignores NotFound errors (treating missing resources as ready). Updating the comment to match the behavior will avoid confusion for future callers.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ller Helpers in the install/delete pipeline previously returned (T, ctrl.Result, bool, error). Replace with (T, *stepResult) where *stepResult wraps the (Result, Err) the caller must return on a short-circuit. A nil pointer means continue to the next step. This avoids overloading error for non-failure short-circuits (ssaSpec, updateManifest, ErrDeletionNotFinished, unmanaged cleanup all requeue with Err == nil), and keeps the call sites two-valued without the errors.Is/As / zero-Result footguns of a structuredError approach.
c-pius
left a comment
There was a problem hiding this comment.
Thanks, this is a great step forward! Consider my comments optional and decide yourself if you want to apply something. I think we will anyway need to continue polishing this.
I am a bit uneasy about this change though. It was super hard to review this because so many things moved. Also, this is one of the most important parts in the code of KLM. We should quickly align with the team how we plan to release and test this in the live landscapes. Also we may need to be cautious when delivering this together with other high-priority features.
| // SyncedOCIRefAnnotation is the annotation key on a Manifest CR that records | ||
| // the OCI ref last successfully synced to the SKR. The reconciler uses it to | ||
| // detect re-renders triggered by an OCI ref change. | ||
| const SyncedOCIRefAnnotation = "sync-oci-ref" |
There was a problem hiding this comment.
I was this years old when I learned... :)
Rhetorical question, why is this an annotation and not status? 🥹
| // SKRClientCache holds Kubernetes clients for SKR clusters keyed by the | ||
| // manifest's cache key. | ||
| type SKRClientCache interface { | ||
| GetClient(key string) *skrclient.SKRClient | ||
| AddClient(key string, client *skrclient.SKRClient) | ||
| DeleteClient(key string) | ||
| } |
There was a problem hiding this comment.
As the next follow up we can try to use the same SKR client cache for Kyma and Manifest reconciler :)
Co-authored-by: Christoph Schwägerl <acc.pius@mailbox.org>
|
Holding until the regular KLM release was created Friday. We will then merge it and create an additional patch release to isolate this change. |
Drop the misleading "shared by install/delete" wording — these helpers aren't shared between the install and delete paths; each runs only in its own pipeline. Reframe as the preparatory steps that run before spec resolution / resource rendering.
8a4f6ff to
8dd2908
Compare
CachedManifestParser is consumed in exactly one place — Service in service.go. Move the interface declaration next to its sole consumer rather than keeping it in transform.go (which is named for, and now contains only, ResourceTransform). ResourceTransform stays in transform.go because multiple files in the package consume it.
Relocates the contents of
internal/declarative/v2into the layer each piece belongs to, then deletes the package.internal/controller/manifest/Spec→internal/manifest/spec/internal/manifest/parser/internal/service/manifest/render/ExistsStateCheck→internal/manifest/statecheck/ResourceList→ private tointernal/controller/manifest/Drops the
Objectabstraction (transforms now take*v1beta2.Manifestdirectly), removes the generatedmock_v2package, consolidates duplicated constants ontoshared.OwnedByFormat/shared.DefaultRemoteNamespace, liftslabelsremoval.NewManagedByLabelRemovalServiceto the composition root, turnsSpecResolverinto a consumer interface on the reconciler, inlines all collaborator interfaces alongside their consumer incontroller.go, and pins themanifestctrlimport alias in.golangci.yaml.Closes #2745