[platform] add hr reconciler - #870
Conversation
|
""" WalkthroughThis update introduces a new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Controller Manager
participant TenantHelmReconciler
participant Kubernetes API
participant HelmRelease (tenant-root)
participant Child Namespace
Controller Manager->>TenantHelmReconciler: Start reconciliation loop
TenantHelmReconciler->>Kubernetes API: List HelmReleases (filter: name starts with "tenant-")
loop For each HelmRelease
TenantHelmReconciler->>HelmRelease (tenant-root): Check status and conditions
alt Is "tenant-root" in "tenant-root" namespace
TenantHelmReconciler->>Kubernetes API: Annotate "tenant-root" namespace with host value
end
TenantHelmReconciler->>Child Namespace: List HelmReleases in child namespace
TenantHelmReconciler->>Child Namespace: Patch HelmRelease if digest differs
end
Possibly related PRs
Suggested labels
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (13)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (12)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
2f028da to
799a59e
Compare
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (6)
packages/system/keycloak/templates/ingress.yaml (1)
8-15: DRY up tenant-root lookup across templates.
This tenant-root lookup block is repeated in multiple charts. Consider extracting it into a helper function in_helpers.tpl(e.g.,{{ include "cozystack.tenantRootHost" . }}) to reduce duplication and improve maintainability.packages/extra/info/templates/kubeconfig.yaml (1)
15-22: Extract tenant-root lookup into a helper.
This snippet recurs across multiple templates. Move it into a function in_helpers.tpl(e.g.,tenantRootHost) and call it in each chart to keep templates DRY.packages/system/keycloak/templates/sts.yaml (1)
10-17: Consolidate repeated tenant-root lookup.
The same tenant-root lookup logic appears in multiple templates. Extract it into a shared helper in_helpers.tplto improve maintainability and reduce duplication.cmd/cozystack-controller/main.go (1)
193-199: Incorrect controller name in log message
setupLog.Error(err, "unable to create controller", "controller", "Workload")will mis-lead operators when theTenantHelmReconcilerfails because the structured field still says"Workload".
Use the actual controller name to avoid confusion during troubleshooting.- setupLog.Error(err, "unable to create controller", "controller", "Workload") + setupLog.Error(err, "unable to create controller", "controller", "TenantHelm")internal/controller/tenant_helm_reconciler.go (2)
67-70: Incorrect field in log:namespaceis set to HelmRelease name
logger.Error(err, ... "namespace", hr.Name)passes the name instead of the namespace, making logs misleading.- logger.Error(err, "unable to list HelmReleases in namespace", "namespace", hr.Name) + logger.Error(err, "unable to list HelmReleases in namespace", "namespace", childNamespace)
127-152: Namespace update should use patch & incoming context
annotateTenantRootNsfetches andUpdates the Namespace with a full object write.
UsingPatchavoids clobbering concurrent updates and honours server-side apply semantics; alsocontext.TODO()loses cancellation.Consider:
- if err := c.Get(context.TODO(), client.ObjectKey{Name: "tenant-root"}, &ns); err != nil { + if err := c.Get(ctx, client.ObjectKey{Name: "tenant-root"}, &ns); err != nil { ... - if err := c.Update(context.TODO(), &ns); err != nil { + if err := c.Patch(ctx, &ns, client.MergeFrom(original)); err != nil {(Where
ctxis passed down andoriginalis a deep copy made right afterGet.)This reduces race risk with other controllers or
kubectledits.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.gitignore(2 hunks)cmd/cozystack-controller/main.go(3 hunks)internal/controller/tenant_helm_reconciler.go(1 hunks)packages/core/platform/templates/apps.yaml(1 hunks)packages/extra/info/Chart.yaml(1 hunks)packages/extra/info/templates/kubeconfig.yaml(1 hunks)packages/extra/ingress/Chart.yaml(1 hunks)packages/extra/ingress/templates/dashboard.yaml(1 hunks)packages/extra/versions_map(1 hunks)packages/system/cozystack-controller/templates/rbac.yaml(1 hunks)packages/system/keycloak-configure/templates/configure-kk.yaml(1 hunks)packages/system/keycloak/templates/ingress.yaml(1 hunks)packages/system/keycloak/templates/sts.yaml(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Build and Test
🔇 Additional comments (7)
.gitignore (2)
4-4: Ignore VSCode workspace settings
Adding.vscodeto the ignore list aligns with existing IDE-specific ignores (.idea) and prevents committing editor configurations.
79-79: Enhance.DS_Storeignore pattern
Using**/.DS_Storeensures macOS metadata files are ignored in nested directories as well, improving repository cleanliness.packages/extra/info/Chart.yaml (1)
6-6: Bump chart version forinfopackage
The version field has been correctly incremented to1.0.1to reflect the new template logic.packages/extra/ingress/Chart.yaml (1)
6-6: Bump chart version foringresspackage
The version field has been correctly updated to1.5.1to coordinate with the tenant HelmRelease host override changes.packages/extra/versions_map (2)
14-15: Updateinfopackage commit references
Lockedinfo 1.0.0to93bdf411and addedinfo 1.0.1pointing atHEADto match the Chart version bump.
21-22: Updateingresspackage commit references
Lockedingress 1.5.0to93bdf411and addedingress 1.5.1pointing atHEADin line with the chart version increment.packages/system/cozystack-controller/templates/rbac.yaml (1)
12-14: Approve RBAC addition for HelmRelease resources.
The new rule correctly grantsget,list,watch,patch, andupdateverbs onhelmreleasesin thehelm.toolkit.fluxcd.ioAPI group, which is required for the TenantHelmReconciler to manage HelmRelease resources.
| {{- $tenantRoot := list }} | ||
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | ||
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | ||
| {{- end }} | ||
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | ||
| {{- $host = $tenantRoot.spec.values.host }} | ||
| {{- else }} | ||
| {{- end }} |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix initialization of $tenantRoot to avoid runtime errors.
Initialising $tenantRoot with list means the and guard may still evaluate truthy for an empty list and then error on .spec. Replace with:
- {{- $tenantRoot := list }}
+ {{- $tenantRoot := nil }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {{- $tenantRoot := list }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- else }} | |
| {{- end }} | |
| {{- $tenantRoot := nil }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- else }} | |
| {{- end }} |
There was a problem hiding this comment.
| {{- $tenantRoot := list }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- else }} | |
| {{- end }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot := lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- end }} | |
| {{- end }} |
There was a problem hiding this comment.
Timofei Larkin (@lllamnyp) Your suggestion is excellent and actually improves upon my original recommendation! By moving the variable declaration inside the API version check and combining the conditionals, you've:
- Eliminated the potential runtime error from an empty list
- Improved variable scoping (only declaring where needed)
- Removed the unnecessary empty
elseclause - Created more concise and readable code
This approach is more idiomatic for Go templates and produces cleaner code overall. Nice improvement!
| {{- $tenantRoot := list }} | ||
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | ||
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | ||
| {{- end }} | ||
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | ||
| {{- $host = $tenantRoot.spec.values.host }} | ||
| {{- else }} | ||
| {{- end }} |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Possible template panic when CRD is absent
When the HelmRelease CRD is not installed, $tenantRoot remains a non-nil list, making
$tenantRoot.spec illegal and the template renders with:
template: ... invalid type for index: slice
A minimal, defensive rewrite prevents the panic and removes the now-useless else block:
-{{- $tenantRoot := list }}
-{{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }}
-{{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }}
-{{- end }}
-{{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }}
-{{- $host = $tenantRoot.spec.values.host }}
-{{- else }}
-{{- end }}
+{{- $tenantRoot := dict }}
+{{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }}
+ {{- $tenantRoot = (lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root") | default dict }}
+{{- end }}
+{{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }}
+ {{- $host = $tenantRoot.spec.values.host }}
+{{- end }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {{- $tenantRoot := list }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- else }} | |
| {{- end }} | |
| {{- $tenantRoot := dict }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = (lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root") | default dict }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- end }} |
| {{- $tenantRoot := list }} | ||
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | ||
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | ||
| {{- end }} | ||
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | ||
| {{- $host = $tenantRoot.spec.values.host }} | ||
| {{- else }} | ||
| {{- end }} |
There was a problem hiding this comment.
No need to initialize the variable. lookup already returns nil or the content of the resource. You could just do this:
| {{- $tenantRoot := list }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- end }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- else }} | |
| {{- end }} | |
| {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} | |
| {{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} | |
| {{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} | |
| {{- $host = $tenantRoot.spec.values.host }} | |
| {{- end }} | |
| {{- end }} |
799a59e to
d489421
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
internal/controller/tenant_helm_reconciler.go (3)
45-50:⚠️ Potential issue
hr.Status.History[0]still indexed before the length check (duplicate of earlier review)The first access at line 45 precedes the
len(hr.Status.History)guard on line 49, so the panic risk reported in the previous review remains.Suggested quick fix (move the guard before the access and drop the redundant second check):
- if hr.Status.History[0].Status != "deployed" { - return ctrl.Result{}, nil - } - - if len(hr.Status.History) == 0 { + if len(hr.Status.History) == 0 { logger.Info("no history in HelmRelease status", "name", hr.Name) return ctrl.Result{}, nil } - if hr.Status.History[0].Status != "deployed" { + if hr.Status.History[0].Status != "deployed" { return ctrl.Result{}, nil }
61-66:⚠️ Potential issuePossible nil-pointer dereference of
hr.Spec.Values(duplicate of earlier review)
hr.Spec.Valuesis a pointer; if it’snil, the dereference*hr.Spec.Valueswill panic.- err := annotateTenantRootNs(*hr.Spec.Values, r.Client) + if hr.Spec.Values == nil { + logger.Info("tenant-root HelmRelease has no .spec.values; skipping namespace annotation") + } else if err := annotateTenantRootNs(*hr.Spec.Values, r.Client); err != nil { + logger.Error(err, "cant annotate tenant-root ns") + return ctrl.Result{}, err + }
117-128: 🛠️ Refactor suggestion
getChildNamespacestill duplicates tenant name for dedicated namespaces (duplicate of earlier review)For a HelmRelease
tenant-fooliving in namespacetenant-foo, the function returnstenant-foo-foo, breaking look-ups.- default: - // 3) tenant in a dedicated namespace - return fmt.Sprintf("%s-%s", currentNamespace, tenantName) + default: + // 3) tenant in its own namespace – return namespace unchanged + return currentNamespace
🧹 Nitpick comments (2)
internal/controller/tenant_helm_reconciler.go (2)
71-73: Incorrect namespace in error log
hr.Nameis logged wherechildNamespace(orhr.Namespace) is intended, which can mislead troubleshooting.- logger.Error(err, "unable to list HelmReleases in namespace", "namespace", hr.Name) + logger.Error(err, "unable to list HelmReleases in namespace", "namespace", childNamespace)
131-156: Use caller context instead ofcontext.TODO()and prefer JSON unmarshalMinor but useful clean-ups:
context.TODO()discards cancellation/deadline from the Reconcile context.- Since
values.Rawis already JSON,json.Unmarshalavoids YAML quirks.-func annotateTenantRootNs(values apiextensionsv1.JSON, c client.Client) error { +func annotateTenantRootNs(ctx context.Context, values apiextensionsv1.JSON, c client.Client) error { @@ - if err := yaml.Unmarshal(values.Raw, &data); err != nil { + if err := json.Unmarshal(values.Raw, &data); err != nil { @@ - if err := c.Get(context.TODO(), client.ObjectKey{Name: "tenant-root"}, &ns); err != nil { + if err := c.Get(ctx, client.ObjectKey{Name: "tenant-root"}, &ns); err != nil { @@ - if err := c.Update(context.TODO(), &ns); err != nil { + if err := c.Update(ctx, &ns); err != nil {Caller adjustment:
- err := annotateTenantRootNs(*hr.Spec.Values, r.Client) + err := annotateTenantRootNs(ctx, *hr.Spec.Values, r.Client)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.gitignore(2 hunks)cmd/cozystack-controller/main.go(3 hunks)internal/controller/tenant_helm_reconciler.go(1 hunks)packages/core/platform/templates/apps.yaml(1 hunks)packages/extra/info/Chart.yaml(1 hunks)packages/extra/info/templates/kubeconfig.yaml(1 hunks)packages/extra/ingress/Chart.yaml(1 hunks)packages/extra/ingress/templates/dashboard.yaml(1 hunks)packages/extra/versions_map(1 hunks)packages/system/cozystack-controller/templates/rbac.yaml(1 hunks)packages/system/keycloak-configure/templates/configure-kk.yaml(1 hunks)packages/system/keycloak/templates/ingress.yaml(1 hunks)packages/system/keycloak/templates/sts.yaml(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/core/platform/templates/apps.yaml
🚧 Files skipped from review as they are similar to previous changes (11)
- packages/extra/info/Chart.yaml
- packages/extra/ingress/templates/dashboard.yaml
- packages/system/keycloak/templates/ingress.yaml
- .gitignore
- packages/system/cozystack-controller/templates/rbac.yaml
- packages/extra/ingress/Chart.yaml
- packages/system/keycloak-configure/templates/configure-kk.yaml
- packages/extra/versions_map
- cmd/cozystack-controller/main.go
- packages/system/keycloak/templates/sts.yaml
- packages/extra/info/templates/kubeconfig.yaml
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Build and Test
🔇 Additional comments (1)
internal/controller/tenant_helm_reconciler.go (1)
41-43:⚠️ Potential issueUnsafe access to
hr.Status.Conditions[0]without length check
hr.Status.Conditions[0]is dereferenced before verifying thatlen(hr.Status.Conditions) > 0. When a HelmRelease has no conditions yet (first reconcile, install error, etc.) the controller will panic.- if len(hr.Status.Conditions) == 0 || hr.Status.Conditions[0].Type != "Ready" { + if len(hr.Status.Conditions) == 0 { + return ctrl.Result{}, nil + } + + cond := hr.Status.Conditions[0] + if cond.Type != "Ready" || cond.Status != metav1.ConditionTrue { return ctrl.Result{}, nil }(The additional
Status == Trueguard prevents premature exits while the release is progressing.)Likely an incorrect or invalid review comment.
d489421 to
ddfaf9c
Compare
Signed-off-by: kklinch0 <kklinch0@gmail.com>
ddfaf9c to
f61a781
Compare
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new controller to synchronize tenant HelmReleases and propagate configuration changes. - Added dynamic host value overrides in multiple Helm templates by conditionally retrieving values from the "tenant-root" HelmRelease. - Updated RBAC permissions to allow management of HelmRelease resources. - **Improvements** - Added support for Helm v2 API integration. - Enhanced HelmRelease reconciliation logic and configuration propagation for tenant environments. - **Bug Fixes** - Fixed periodic reconciliation for the "tenant-root" HelmRelease by setting its interval to zero. - **Version Updates** - Incremented version numbers for the "info" and "ingress" packages. - **Chores** - Updated version mappings and commit references. - Improved .gitignore to exclude the .vscode directory. <!-- end of auto-generated comment: release notes by coderabbit.ai --> (cherry picked from commit dca732c) Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Version Updates
Chores