Skip to content

fix: Get config hash for deployer download-cache key (Cachebacked Repository refactoring)#2058

Merged
frewilhelm merged 36 commits into
open-component-model:mainfrom
frewilhelm:get-config-hash
Mar 30, 2026
Merged

fix: Get config hash for deployer download-cache key (Cachebacked Repository refactoring)#2058
frewilhelm merged 36 commits into
open-component-model:mainfrom
frewilhelm:get-config-hash

Conversation

@frewilhelm

Copy link
Copy Markdown
Contributor

What this PR does / why we need it

While trying to get the config hash to use it as download-cache-key I noticed that the configuration loading in the NewCacheBackedRepository could also be done in the reconciliation loops of the controllers instead. Additionally, this make the client in the Resolver struct not necessary anymore. In conclusion, everything get a bit leaner.

Which issue(s) this PR fixes

Subtask of open-component-model/ocm-project#963

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
@github-actions github-actions Bot added the kind/bugfix Bug label Mar 24, 2026
@frewilhelm

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@github-actions github-actions Bot added the size/m Medium label Mar 24, 2026
@coderabbitai

coderabbitai Bot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Resolver API simplified: NewResolver no longer accepts a Kubernetes client. Controllers now load a concrete *configuration.Configuration early in Reconcile and pass it into resolution/repository flows. Deployer reconcile was refactored into helper steps, finalizers are added earlier, and download/cache and matching logic were adjusted.

Changes

Cohort / File(s) Summary
Resolution service & tests
kubernetes/controller/internal/resolution/service.go, kubernetes/controller/internal/resolution/service_test.go, kubernetes/controller/internal/resolution/doc.go
Removed client.Reader from NewResolver and Resolver struct. RepositoryOptions now carries Configuration *configuration.Configuration (removed OCMConfigurations and Namespace). Repo creation uses provided Configuration directly; tests/docs updated to preload configs.
Controller bootstrap
kubernetes/controller/cmd/main.go
Updated resolution.NewResolver invocation to new signature (removed mgr.GetClient() argument).
Component controller & tests
kubernetes/controller/internal/controller/component/...
Load concrete configuration.Configuration early in Reconcile and pass as Configuration: cfg to repository options; change not-ready reason to GetConfigurationFailedReason. Tests updated to new resolver signature.
Deployer controller & tests
kubernetes/controller/internal/controller/deployer/...
Add apply/watch finalizers early with immediate requeue; Reconcile split into helpers (reconcileDeployment, resolveResource, resolveConfiguration, createCacheBackedRepository, resolveComponentAndMatchResource); change resource matching and cache-key composition; DownloadResourceWithOCM now takes resolved repo/descriptor/cfg and only downloads+decodes; error handling moved to caller; tests updated (resolver signature, shared download cache, nil-digest path, relaxed cache assertions, eventuals).
Resource controller & tests
kubernetes/controller/internal/controller/resource/...
Load Configuration once per Reconcile and pass into repository/verification paths (remove OCMConfigurations/Namespace usage); adjust not-ready reasons and verification flows. Tests updated to new resolver signature.
Repository controller & tests
kubernetes/controller/internal/controller/repository/...
validate now preloads Configuration via configuration.LoadConfigurations(...) and supplies it to repository options; error wrapping adjusted. Suite tests updated for resolver signature.
Test suites across controllers
kubernetes/controller/internal/controller/*/suite_test.go
Multiple suites updated to construct resolver as resolution.NewResolver(&resolutionLogger, workerPool, pm) (removed k8sClient). Some suites add/init a shared downloadCache variable for tests.
Unit/integration tests adjustments
various *_test.go under controllers/deployer/resource/component/...
Resolver signature changes propagated; several tests changed to use Eventually for k8s list checks, adjusted not-ready reasons, added nil-digest test coverage, and relaxed some cache-hit assertions.
API constants
kubernetes/controller/api/v1alpha1/condition_types.go
Renamed failure reason constant: ConfigureContextFailedReasonGetConfigurationFailedReason.

Sequence Diagram(s)

sequenceDiagram
    participant Controller as Reconciler
    participant ConfigSvc as configuration\nLoader
    participant ResolutionSvc as resolution\nService
    participant Repo as CacheBacked\nRepository

    rect rgba(200,150,100,0.5)
    Note over Controller: Old Flow
    Controller->>ResolutionSvc: Request repository\n(OCMConfigurations, Namespace)
    ResolutionSvc->>ConfigSvc: LoadConfigurations(ctx, client, ns, ocmConfigs)
    ConfigSvc-->>ResolutionSvc: Configuration
    ResolutionSvc->>Repo: Create with cfg
    Repo-->>Controller: Repository
    end

    rect rgba(100,150,200,0.5)
    Note over Controller: New Flow
    Controller->>ConfigSvc: LoadConfigurations(ctx, client, ns, ocmConfigs)
    ConfigSvc-->>Controller: Configuration
    Controller->>ResolutionSvc: Request repository\n(Configuration)
    ResolutionSvc->>Repo: Create with cfg
    Repo-->>Controller: Repository
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Skarlso
  • fabianburth
  • jakobmoellerdev

Poem

🐇 I hopped through Go and changed the route,
Took out the client and preloaded truth,
Finalizers set with a bounding spring,
Caches hum quiet while reconcilers sing,
Tests tumble in — a celebratory hop!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: obtaining a config hash for the deployer download-cache key and refactoring the cache-backed repository initialization.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation (obtaining config hash for download-cache key) and the architectural changes (moving configuration loading to controllers, removing client from Resolver).

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
kubernetes/controller/internal/resolution/service.go (1)

53-57: Make the preload state explicit.

Configuration: nil now covers both “there are no OCM configs” and “the caller never materialized them”. Because repository creation still continues in both cases, a missed preload silently drops credentials/path matchers and the config hash from the cache key. A small wrapper/helper around LoadConfigurations would make this much harder to misuse later.

Also applies to: 71-71

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@kubernetes/controller/internal/resolution/service.go` around lines 53 - 57,
RepositoryOptions currently uses Configuration *configuration.Configuration
where nil ambiguously means either "no configs exist" or "caller didn't call
configuration.LoadConfigurations"; add an explicit preload indicator/wrapper
(e.g., a new type PreloadedConfiguration { Config *configuration.Configuration;
Loaded bool } or a constructor NewPreloadedConfigurationFromLoader that calls
configuration.LoadConfigurations and returns an error) and replace the raw
*Configuration field with this wrapper in RepositoryOptions and callers; update
CacheBackedRepository creation (and any callers that set RepositoryOptions) to
require/validate Loaded == true (or return an error) so missing preload cannot
be silent and the cache key includes config presence reliably.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@kubernetes/controller/internal/controller/component/component_controller.go`:
- Around line 244-249: The error branch after calling LoadConfigurations
currently marks the component not ready with
v1alpha1.GetComponentVersionFailedReason; update it to use the
configuration-specific failure reason constant (e.g.,
v1alpha1.GetConfigurationsFailedReason) so configuration load errors are
reported correctly; change the call to status.MarkNotReady(r.EventRecorder,
component, v1alpha1.GetConfigurationsFailedReason, err.Error()) while keeping
the existing error return unchanged (the affected symbols: LoadConfigurations,
status.MarkNotReady, v1alpha1.GetComponentVersionFailedReason).

In `@kubernetes/controller/internal/controller/deployer/deployer_controller.go`:
- Around line 344-358: The cache key used for DownloadCache.Load (variable key)
in the no-digest branch omits repository and verification inputs so a cache hit
can return manifests that were produced from a different
resource.Status.Component.RepositorySpec or a different set of verifications
used by getEffectiveComponentDescriptor; change the key composition (or move the
DownloadCache.Load call) so it includes resource.Status.Component.RepositorySpec
and the verification inputs passed into getEffectiveComponentDescriptor (or
perform the effective descriptor resolution/verification before calling
DownloadCache.Load) so DownloadResourceWithOCM is only skipped when
repo+verification+config+component+version+resourceIdentity all match.

In
`@kubernetes/controller/internal/controller/repository/repository_controller.go`:
- Around line 172-175: The config-loading error from
configuration.LoadConfigurations (called with ctx, r.Client,
ocmRepo.GetNamespace(), configs) must be distinguished from repository failures:
detect and return a specific configure-context error (for example wrap/return
fmt.Errorf("configure context failed: %w", err) or a sentinel
ConfigureContextError) instead of the generic validate error; then update
Reconcile's error-to-reason mapping to check for that configure-specific error
(or error type) and map it to ConfigureContextFailedReason while leaving other
validate errors mapped to GetRepositoryFailedReason. Ensure references:
configuration.LoadConfigurations, validate, Reconcile,
GetRepositoryFailedReason, ConfigureContextFailedReason, ocmRepo.GetNamespace(),
configs, and r.Client.

In `@kubernetes/controller/internal/resolution/doc.go`:
- Line 10: The example call to resolution.NewResolver is using a value `logger`
but the function expects `*logr.Logger`; update the example to pass a pointer to
the logger (or ensure the declared `logger` is a `*logr.Logger`) so the call to
`resolution.NewResolver(logger, wp, pluginManager)` type-checks—locate the
example line with `resolution.NewResolver` in the doc and change the argument to
the pointer form (or adjust the logger declaration) to match the NewResolver
signature.

---

Nitpick comments:
In `@kubernetes/controller/internal/resolution/service.go`:
- Around line 53-57: RepositoryOptions currently uses Configuration
*configuration.Configuration where nil ambiguously means either "no configs
exist" or "caller didn't call configuration.LoadConfigurations"; add an explicit
preload indicator/wrapper (e.g., a new type PreloadedConfiguration { Config
*configuration.Configuration; Loaded bool } or a constructor
NewPreloadedConfigurationFromLoader that calls configuration.LoadConfigurations
and returns an error) and replace the raw *Configuration field with this wrapper
in RepositoryOptions and callers; update CacheBackedRepository creation (and any
callers that set RepositoryOptions) to require/validate Loaded == true (or
return an error) so missing preload cannot be silent and the cache key includes
config presence reliably.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ef8c313-6085-42ec-9e27-31b94a04ec3f

📥 Commits

Reviewing files that changed from the base of the PR and between 220c7f9 and b3c0115.

📒 Files selected for processing (13)
  • kubernetes/controller/cmd/main.go
  • kubernetes/controller/internal/controller/component/component_controller.go
  • kubernetes/controller/internal/controller/component/component_controller_unit_test.go
  • kubernetes/controller/internal/controller/component/suite_test.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller.go
  • kubernetes/controller/internal/controller/deployer/suite_test.go
  • kubernetes/controller/internal/controller/repository/repository_controller.go
  • kubernetes/controller/internal/controller/repository/suite_test.go
  • kubernetes/controller/internal/controller/resource/resource_controller.go
  • kubernetes/controller/internal/controller/resource/suite_test.go
  • kubernetes/controller/internal/resolution/doc.go
  • kubernetes/controller/internal/resolution/service.go
  • kubernetes/controller/internal/resolution/service_test.go

Comment thread kubernetes/controller/internal/controller/deployer/deployer_controller.go Outdated
Comment thread kubernetes/controller/internal/resolution/doc.go
Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
@frewilhelm frewilhelm marked this pull request as ready for review March 25, 2026 13:46
@frewilhelm frewilhelm requested a review from a team as a code owner March 25, 2026 13:46
@frewilhelm

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread kubernetes/controller/internal/controller/deployer/deployer_controller.go Outdated

@fabianburth fabianburth left a comment

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.

Besides the reading from status instead of cache - which you already agreed you will address, looks good to me!

@frewilhelm

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
kubernetes/controller/internal/controller/deployer/deployer_controller_test.go (1)

820-825: Add a regression for the Digest == nil cache-key fallback.

These specs still set ResourceInfo.Digest, so they only cover the digest-key path. The bugfix in this PR is the fallback cfg.Hash/component:version/resourceIdentity branch; without a Digest: nil case plus a config change, that path can regress unnoticed.

Also applies to: 1060-1063, 1077-1080

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@kubernetes/controller/internal/controller/deployer/deployer_controller_test.go`
around lines 820 - 825, Add a regression test covering the Digest == nil
cache-key fallback by modifying the existing specs that currently set
ResourceInfo.Digest so they instead set ResourceInfo.Digest = nil (or remove it)
and adjust the test config to force the fallback path (the
cfg.Hash/component:version/resourceIdentity branch); specifically update the
tests that reference getEffectiveComponentDescriptor behavior and the
verifiedHit assertion so they exercise the non-digest branch and verify cache
hits still occur (apply same change to the other two similar specs referenced
near the verifiedHit assertions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@kubernetes/controller/internal/controller/deployer/deployer_controller.go`:
- Around line 350-356: The code dereferences
resource.Status.Component.RepositorySpec.Raw causing a nil-pointer panic; before
creating repoSpec or calling ocmruntime.NewScheme(...).Decode, add a guard that
checks resource.Status != nil, resource.Status.Component != nil and
resource.Status.Component.RepositorySpec != nil (and that RepositorySpec.Raw is
non-empty), and if any are missing call
status.MarkNotReady(r.GetEventRecorder(), deployer,
deliveryv1alpha1.GetRepositoryFailedReason, "repository spec missing or
incomplete") and return ctrl.Result{} (no error) so the reconciler surfaces a
NotReady state instead of panicking; update the logic around repoSpec /
ocmruntime.NewScheme.Decode to only run when the guard passes.

---

Nitpick comments:
In
`@kubernetes/controller/internal/controller/deployer/deployer_controller_test.go`:
- Around line 820-825: Add a regression test covering the Digest == nil
cache-key fallback by modifying the existing specs that currently set
ResourceInfo.Digest so they instead set ResourceInfo.Digest = nil (or remove it)
and adjust the test config to force the fallback path (the
cfg.Hash/component:version/resourceIdentity branch); specifically update the
tests that reference getEffectiveComponentDescriptor behavior and the
verifiedHit assertion so they exercise the non-digest branch and verify cache
hits still occur (apply same change to the other two similar specs referenced
near the verifiedHit assertions).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e6fe93e-86c8-4e86-93f1-a2e2bd7724d8

📥 Commits

Reviewing files that changed from the base of the PR and between 4d9e991 and ed7f4b4.

📒 Files selected for processing (3)
  • kubernetes/controller/internal/controller/deployer/deployer_controller.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller_rgd_test.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller_test.go

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kubernetes/controller/internal/controller/deployer/deployer_controller.go (1)

335-339: ⚠️ Potential issue | 🟡 Minor

Bug: Wrapping a nil error.

At line 338, the err variable is nil because the error case was already handled and returned at lines 322-333. The fmt.Errorf will produce confusing output like "failed to get ready resource: %!w(<nil>)".

🐛 Proposed fix
 	if resource.Status.Resource == nil {
 		status.MarkNotReady(r.EventRecorder, deployer, deliveryv1alpha1.ResourceIsNotAvailable, "resource is empty in status")
 
-		return ctrl.Result{}, fmt.Errorf("failed to get ready resource: %w", err)
+		return ctrl.Result{}, fmt.Errorf("resource is empty in status")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@kubernetes/controller/internal/controller/deployer/deployer_controller.go`
around lines 335 - 339, The check for resource.Status.Resource == nil logs the
condition but then returns fmt.Errorf("failed to get ready resource: %w", err)
which wraps a nil err; replace that return with a concrete error value (e.g.
errors.New or fmt.Errorf without %w) that describes the missing resource so you
don't wrap nil. Update the block around status.MarkNotReady, deployer, and
deliveryv1alpha1.ResourceIsNotAvailable to return ctrl.Result{} and a clear
non-nil error message like "failed to get ready resource:
resource.Status.Resource is nil".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@kubernetes/controller/internal/controller/deployer/deployer_controller.go`:
- Around line 335-339: The check for resource.Status.Resource == nil logs the
condition but then returns fmt.Errorf("failed to get ready resource: %w", err)
which wraps a nil err; replace that return with a concrete error value (e.g.
errors.New or fmt.Errorf without %w) that describes the missing resource so you
don't wrap nil. Update the block around status.MarkNotReady, deployer, and
deliveryv1alpha1.ResourceIsNotAvailable to return ctrl.Result{} and a clear
non-nil error message like "failed to get ready resource:
resource.Status.Resource is nil".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65b4bfd8-6b1a-4d2c-9b71-b2c2517e5499

📥 Commits

Reviewing files that changed from the base of the PR and between ed7f4b4 and a4d54b2.

📒 Files selected for processing (8)
  • kubernetes/controller/cmd/main.go
  • kubernetes/controller/internal/controller/component/component_controller.go
  • kubernetes/controller/internal/controller/component/component_controller_unit_test.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller_test.go
  • kubernetes/controller/internal/controller/repository/repository_controller.go
  • kubernetes/controller/internal/controller/resource/resource_controller.go
  • kubernetes/controller/internal/resolution/service_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • kubernetes/controller/internal/controller/repository/repository_controller.go
  • kubernetes/controller/internal/controller/component/component_controller_unit_test.go
  • kubernetes/controller/internal/controller/deployer/deployer_controller_test.go
  • kubernetes/controller/internal/controller/resource/resource_controller.go

jakobmoellerdev and others added 3 commits March 26, 2026 16:18
Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
@github-actions github-actions Bot added the size/l Large label Mar 26, 2026
@frewilhelm

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
@frewilhelm

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@frewilhelm frewilhelm requested a review from fabianburth March 27, 2026 13:27
@frewilhelm frewilhelm enabled auto-merge (squash) March 30, 2026 06:49
Signed-off-by: Frederic Wilhelm <frederic.wilhelm@sap.com>
@frewilhelm frewilhelm requested a review from Skarlso March 30, 2026 08:23

@Skarlso Skarlso left a comment

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.

Okay, sounds good. :)

@frewilhelm frewilhelm merged commit 5da31c0 into open-component-model:main Mar 30, 2026
18 checks passed
@frewilhelm frewilhelm deleted the get-config-hash branch March 30, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants