fix(harbor): preserve jobservice and trivy storageClass on upgrade - #2930
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses an issue where the Harbor jobservice PVC was created without an explicit storageClassName, causing it to default to the cluster's default storage class. Because PVC storage classes are immutable, this led to upgrade failures when the rendered chart configuration differed from the cluster-assigned default. The changes ensure the storageClass is correctly propagated while intelligently preserving existing PVC configurations to maintain compatibility during upgrades. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe Harbor Helm template now looks up existing jobservice and Trivy PVC storageClass values during render, reuses them when present, and conditionally emits the persistence block. A new helm-unittest suite covers propagation and omission cases. ChangesHarbor jobservice PVC storageClass fix
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request addresses issue #2368 in the Harbor package by preserving the existing storageClassName of the jobservice PVC during upgrades via a cluster lookup, falling back to the configured storageClass on fresh installs. It also adds a test target to the Makefile and introduces a comprehensive helm-unittest suite to verify this behavior. The review feedback suggests using the dig function when accessing nested fields on the looked-up PVC to prevent potential template rendering errors if the spec field is missing or nil.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| {{- $jobservicePvc := lookup "v1" "PersistentVolumeClaim" .Release.Namespace (printf "%s-jobservice" .Release.Name) }} | ||
| {{- $jobserviceStorageClass := .Values.storageClass }} | ||
| {{- if $jobservicePvc }} | ||
| {{- $jobserviceStorageClass = $jobservicePvc.spec.storageClassName | default "" }} |
There was a problem hiding this comment.
To prevent potential template rendering errors due to nil pointer dereferences, it is safer to use the dig function when navigating nested maps returned by lookup. If spec is somehow missing or nil in the returned resource, accessing .spec.storageClassName directly will cause a rendering failure. Using dig provides a safe fallback.
{{- $jobserviceStorageClass = dig "spec" "storageClassName" "" $jobservicePvc }}There was a problem hiding this comment.
Addressed in bdf520bf7 — the spec access is now guarded with $jobservicePvc.spec | default dict before hasKey, so a missing/nil spec can't panic. I kept hasKey rather than dig "spec" "storageClassName" "" because dig's default can't distinguish an explicit storageClassName: "" (which must round-trip as the upstream "-" sentinel) from an absent field (which must stay omitted) — both would collapse to "".
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@packages/apps/harbor/templates/harbor.yaml`:
- Around line 34-38: The issue is that the jobservice storage class lookup uses
`| default ""` which treats an explicitly set empty string (`""` in
`spec.storageClassName`) as falsy and collapses it, losing the explicit "no
dynamic provisioning" state. Fix this by using a conditional that checks whether
the `spec.storageClassName` field exists (not just whether it is truthy) so that
explicitly empty values are preserved as `"-"`. Apply this fix to both the
initial jobservice PVC lookup block around line 37 (where
`$jobserviceStorageClass` is assigned from
`$jobservicePvc.spec.storageClassName`) and to any other similar storage class
assignment blocks that reference the jobservice PVC (around lines 125-131),
ensuring that explicit empty strings are maintained across upgrades instead of
being omitted from the template output.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b6e2f09a-f8d5-4011-b33d-7cd1d6549790
📒 Files selected for processing (3)
packages/apps/harbor/Makefilepackages/apps/harbor/templates/harbor.yamlpackages/apps/harbor/tests/jobservice_storageclass_test.yaml
9e9cb99 to
f7e449e
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — approving. No blockers: the jobservice PVC storageClass is correctly threaded, and the lookup-preserve logic keeps the rendered manifest equal to the live object so the immutable-field rejection on upgrade is avoided.
Business context: Harbor's jobservice PVC was rendered without a storageClassName, so default-StorageClass admission stamped a class onto the live PVC that the rendered manifest never carried; the resulting drift against the immutable PVC field broke upgrades of the harbor system HelmRelease.
I verified the fix against the upstream subchart and the live-PVC semantics. The problem is real — upstream emits storageClassName only when jobLog.storageClass is set, and main never passed it through for jobservice. The common path is correct: an existing PVC's class is read back and re-rendered so the manifest matches the live object; a fresh install falls back to .Values.storageClass, and a fresh install with an empty storageClass self-heals on the next reconcile once lookup observes the admission-defaulted class.
Blockers: none.
Non-blocking:
-
Explicit-empty PVC class is not preserved. When an existing jobservice PVC has
spec.storageClassName: ""(upstream's-/ no-dynamic-provisioning state),lookup ... | default ""collapses to falsy and the{{- with $jobserviceStorageClass }}block is skipped, so the rendered manifest omitsstorageClassNameinstead of reproducing"". That deviates from the PR's own "keep rendered equal to live" invariant. Narrow in practice — a cluster with a default StorageClass never produces""— but it is the one input the preserve logic does not round-trip. Evidence:packages/apps/harbor/templates/harbor.yaml:37(| default "") and:127({{- with ... }}), against upstreampackages/system/harbor/charts/harbor/templates/jobservice/jobservice-pvc.yaml:25-31(-rendersstorageClassName: ""). -
Fixes #2368over-closes the issue. The linked issue covers both the jobservice PVC and the trivy StatefulSet VCT; this PR addresses only jobservice, while the trivy block still passes.Values.storageClassblindly into an immutable VCT. On merge the issue auto-closes with the trivy half unfixed. ConsiderRefs #2368(or a dedicated trivy follow-up) so the trivy immutability is not silently dropped. Evidence: issue #2368 body lists both PVCs;packages/apps/harbor/templates/harbor.yaml:132-138(trivy still uses.Values.storageClass, unchanged frommain). -
The upgrade path itself is not unit-tested. helm-unittest renders
lookupas empty, so all four new cases exercise only the fresh-install fallback; the preserve-existing-class branch — the actual upgrade-safety logic — is covered only by the manual server-side dry-run noted in the description. This is an inherent helm-unittest limitation, not a request to add a test. Evidence:packages/apps/harbor/tests/jobservice_storageclass_test.yaml(every case sets or omitsstorageClass; none mocks an existing PVC).
The cozy-harbor wrapper never passed storageClass to the jobservice jobLog PVC, so it was created without a storageClassName and the default-StorageClass admission stamped the cluster default. Because PVC storageClassName is immutable, this caused rendered-vs-live drift on upgrade. Preserve an existing PVC's class via lookup and fall back to the configured storageClass for fresh installs. The trivy StatefulSet's volumeClaimTemplate had the same latent drift -- .Values.storageClass was forwarded blindly into a VCT that cannot be patched at all -- so it gets the same lookup-preserve treatment, keyed off data-<release>-trivy-0. A live object pinned to an explicit "" (no dynamic provisioning) now round-trips through upstream's "-" sentinel instead of collapsing to an omitted storageClassName, and the spec lookup is guarded with `| default dict` so a PVC carrying no storageClassName key stays omitted. Adds a helm-unittest regression suite and wires a test target so make unit-tests stops skipping packages/apps/harbor. Fixes #2368 Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
bdf520b
f7e449e to
bdf520b
Compare
|
Aleksei Sviridkin (@lexfrei) thanks for the thorough review. Addressed in
One caveat for transparency: the server-side dry-run validation was jobservice-only — the trivy change applies the same proven pattern but wasn't separately dry-run'd. |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — propagates storageClass to the jobservice PVC and trivy VCT with a lookup-preserve guard, fixing the immutable-storageClassName drift from #2368.
Verified:
- PVC naming is correct:
harbor.fullnameOverride: {{ .Release.Name }}makes the upstream subchart name resources<release>-*, so<release>-jobserviceanddata-<release>-trivy-0are the right lookup keys. - The lookup-preserve logic is sound across all four states: fresh install (configured class), existing PVC (preserve live class), explicit "" (round-trips via the "-" sentinel), and no storageClassName key (stays omitted to match live).
| quoteis necessary, not cosmetic: an unquoted-sentinel would be ambiguous YAML; quoting yields"-".- lookup is an established, working pattern in this very chart (credentials/token-cert preservation already rely on it), which de-risks the Flux helm-controller concern.
Non-blocking:
- PR/commit body says it "wires a test: target so make unit-tests stops skipping packages/apps/harbor", but the
test:target and a tests/ suite already exist on main — harbor was already covered. The new suite does run; only the description is inaccurate. Worth correcting so git log stays accurate. - helm-unittest can't exercise the preserve branches (lookup returns nil there), so only the fresh-install fallback is covered in CI; the actual new behavior rests on the manual server-side dry-run. An e2e assertion would close the loop.
- db/redis still forward
.Values.storageClasswithout the lookup-preserve guard; if those PVCs can hit the same #2368 drift when storageClass is unset, they'd need the same treatment — confirm they're immune (operator-managed) or out of scope.
|
Successfully created backport PR for |
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin release-1.5
git worktree add -d .worktree/backport-2930-to-release-1.5 origin/release-1.5
cd .worktree/backport-2930-to-release-1.5
git switch --create backport-2930-to-release-1.5
git cherry-pick -x bdf520bf74a7ed17a62bfeaa669ed2eef7b45509 |
|
Successfully created backport PR for |
What this PR does
The
apps/harborchart never passedstorageClassto the jobservicejobLogPVC, so the upstream chart rendered it without astorageClassName. On first install the default-StorageClass admission then stamps the cluster default onto the live PVC. Because a PVC'sstorageClassNameis immutable, the rendered manifest (no class) and the live object (admission-defaulted class) drift apart, which surfaced as upgrade failures on theharbor-*-systemHelmRelease.This propagates
storageClassto the jobservice PVC, with one safeguard: alookupreads the live jobservice PVC and reuses its currentstorageClassNamewhen the PVC already exists, only falling back to the configuredstorageClassfor a fresh install. Keeping the rendered value equal to the live one means an existing PVC whose admission-defaulted class differs from the configuredstorageClassis preserved rather than triggering an immutable-field rejection on upgrade. A live PVC pinned to an explicit""(no dynamic provisioning) round-trips through the upstream"-"sentinel instead of collapsing to an omitted field, and the spec access is guarded so a PVC with nostorageClassNamekey stays omitted.The trivy
StatefulSet'svolumeClaimTemplatecarried the same latent drift —.Values.storageClasswas forwarded blindly into a VCT, which cannot be patched at all — so it now gets the identical lookup-preserve treatment, keyed offdata-<release>-trivy-0. This closes both halves of #2368.Also adds a helm-unittest regression suite and wires a
test:target somake unit-testsstops skippingpackages/apps/harbor.Validated with
helm templateacross storageClass/trivy permutations and the new unit tests. The jobservice preserve path was additionally confirmed with a server-side dry-run against a running harbor release:lookupresolved the live PVC's class, and withstorageClassoverridden to a different class the jobservice render stayed pinned to the live PVC's class — confirming the immutable-field rejection is avoided. The trivy change applies the same proven pattern. Note: underhelm template/helm-unittestlookupreturns empty, so the unit suite exercises only the fresh-install fallback; the preserve branches are covered by the livelookup.Fixes #2368
Release note
Summary by CodeRabbit
Bug Fixes
Tests