Skip to content

feat(cozy-lib): add TLS certificate helper - #2485

Closed
Arsolitt (Arsolitt) wants to merge 18 commits into
mainfrom
feat/cozy-lib-tls
Closed

Arsolitt (Arsolitt) wants to merge 18 commits into
mainfrom
feat/cozy-lib-tls

Conversation

@Arsolitt

@Arsolitt Arsolitt (Arsolitt) commented Apr 24, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Add shared TLS certificate helpers to cozy-lib for reuse across managed service packages.

Two new template helpers in _tls.tpl:

  • cozy-lib.tls.certificate — renders a cert-manager Certificate CR with configurable issuer,
    DNS SANs, duration, key usages, and sensible defaults (RSA-2048, rotationPolicy: Always,
    1-year duration, server auth usage)
  • cozy-lib.tls.secretName — resolves a user-provided TLS secret name or generates a
    <release>-<suffix> default; suffix is only required when secretName is not provided

Key design decisions:

  • All string outputs are quoted for YAML safety
  • Input validation with actionable error messages for all required fields
  • Type guards on list parameters (dnsNames, extraDnsNames) to catch string-instead-of-list mistakes
  • issuerRef.kind is enum-validated (Issuer or ClusterIssuer only)
  • Private key is hardcoded to RSA-2048 with rotationPolicy: Always (not configurable via this helper)
  • Default usages deliberately set to ["server auth"] instead of cert-manager's default

Also updates the library README to enumerate all helper categories and renames the test chart
from quota to cozy-lib-tests.

Screenshots

N/A — no UI changes.

Release note

feat(cozy-lib): add shared TLS certificate helpers for managed services

Summary by CodeRabbit

  • New Features

    • Added TLS certificate helpers for cert‑manager: certificate rendering, secret-name generation, input validation, and sensible defaults (issuer, DNS, usages, key settings).
  • Documentation

    • README rewritten with a cozy-lib introduction, a categorized list of available template helpers, and note that configuration is supplied via template arguments (no values.yaml parameters).
  • Tests

    • Extensive test suites exercising happy paths, defaults, and many validation/error scenarios for TLS helpers.

@coderabbitai

coderabbitai Bot commented Apr 24, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds two TLS helper templates to the cozy-lib Helm library (certificate and secretName), updates the library README, and introduces a comprehensive suite of Helm unit tests and test templates validating certificate rendering and input validation.

Changes

TLS Certificate Management Module

Layer / File(s) Summary
Library docs
packages/library/cozy-lib/README.md
Replaces the removed Parameters section with an intro describing cozy-lib as a shared Helm library chart, enumerating included _*.tpl helper templates, and stating there are no configurable values.yaml parameters.
New helpers / API
packages/library/cozy-lib/templates/_tls.tpl
Adds cozy-lib.tls.certificate (validates required inputs: Release, name, secretName, dnsNames, issuerRef; enforces list types for dnsNames, extraDnsNames, usages; restricts issuerRef.kind to Issuer or ClusterIssuer; hardcodes issuerRef.group: cert-manager.io; renders a cert-manager.io/v1 Certificate with defaults for duration/renewBefore, RSA-2048 privateKey, rotationPolicy: Always, default usages: ["server auth"], and concatenated DNS lists) and cozy-lib.tls.secretName (returns provided .secretName or constructs {{ .Release.Name }}-{{ .suffix }}; errors clearly when required inputs are missing).
Test chart metadata
packages/tests/cozy-lib-tests/Chart.yaml
Renames chart name from quota to cozy-lib-tests.
Test templates (integration/usage)
packages/tests/cozy-lib-tests/templates/tests/tls.yaml
When .Values.tls is set, builds $certArgs (defaulting secretName, extraDnsNames, usages, and conditionally adding duration/renewBefore) and includes cozy-lib.tls.certificate.
Test templates (validation guards)
packages/tests/cozy-lib-tests/templates/tests/tls-missing-release.yaml, .../tls-missing-secretname.yaml, .../tls-secretname-*.yaml
Adds test-only templates that invoke helpers with missing Release, missing secretName, or missing suffix to exercise validation and secretName generation behavior.
Test suites / assertions
packages/tests/cozy-lib-tests/tests/tls*.yaml
Adds multiple unittest suites: tls_test.yaml (extensive positive and negative assertions covering Certificate fields, defaults, overrides, usages, dns list handling, YAML quoting, and many validation failures) and separate suites for secretName and Release/suffix validation.
Test values
packages/tests/cozy-lib-tests/tests/tls_values.yaml
Provides TLS test values used by positive rendering tests (name, secretName, dnsNames, issuerRef) and empty _cluster/_namespace placeholders.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I stitched certs and names beneath moon's light,

Helpers guard inputs and set defaults just right,
Secrets find their names, errors softly scoped,
Tests hop around every path we hoped,
Cozy templates sleep—infra dreams take flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding a TLS certificate helper to cozy-lib. It is concise, directly related to the primary changeset, and provides meaningful context.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cozy-lib-tls

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@Arsolitt Arsolitt (Arsolitt) self-assigned this Apr 24, 2026
@github-actions github-actions Bot added area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) kind/feature Categorizes issue or PR as related to a new feature labels Apr 30, 2026
Arsolitt (Arsolitt) added a commit that referenced this pull request Apr 30, 2026
Incorporate the latest cozy-lib extraDnsNames changes from the upstream
branch so that PR #2485 can be merged before PR #2540 without conflicts.

Signed-off-by: Arsolitt <[email protected]>
Add helm-unittest tests for the upcoming cozy-lib.tls.certificate helper.
Tests cover: required fields, dnsNames, issuerRef, labels, default and
custom duration/renewBefore, default and custom usages, secretName
defaulting, and both ClusterIssuer and Issuer kinds.

Signed-off-by: Arsolitt <[email protected]>
Signed-off-by: Arsolitt <[email protected]>
Add cozy-lib.tls.certificate template that renders a cert-manager
Certificate CR for services needing TLS. This provides a shared
foundation so each chart does not duplicate cert-manager boilerplate.

Also adds cozy-lib.tls.secretName helper for resolving a TLS secret
name from user input or generating one from the release name.

Signed-off-by: Arsolitt <[email protected]>
Signed-off-by: Arsolitt <[email protected]>
… validation

Add missing space in renewBefore parameter description and add fail guards
for missing Release and suffix in the cozy-lib.tls.secretName helper.

Signed-off-by: Arsolitt <[email protected]>
…ML safety

Add optional extraDnsNames parameter to the TLS certificate helper so
consumers can append user-supplied custom domains to the SAN list.
Pipe all dnsNames (and extraDnsNames) through | quote to ensure
wildcard entries like *.example.com are properly YAML-quoted.

Assisted-By: Claude <[email protected]>
Signed-off-by: Arsolitt <[email protected]>
Add missing test cases for name and issuerRef validation guards.
Quote usages list items for consistency with dnsNames quoting.
Fix doc comment referencing test-internal tls.yaml wrapper.

Signed-off-by: Arsolitt <[email protected]>
Quote the hardcoded "server auth" default for consistency with the
range path that uses `| quote`. Rename test chart from "quota" to
"cozy-lib-tests" to reflect its actual scope.

Signed-off-by: Arsolitt <[email protected]>
Move the suffix validation inside the else branch of secretName
helper so callers providing an explicit secretName are not forced
to also supply an unused suffix parameter.

Signed-off-by: Arsolitt <[email protected]>
…mplates

Set metadata.namespace from Release.Namespace so Certificate CRs
land in the correct namespace. Add privateKey.rotationPolicy: Always
for forward secrecy on renewal. Remove duplicate test template by
reusing the existing suffix-missing fixture for both test scenarios.

Signed-off-by: Arsolitt <[email protected]>
…group field

Clarify in doc comments that empty usages list falls back to default,
duration values are passed through without validation, and issuerRef.group
is always hardcoded to cert-manager.io. Add test for empty usages list.

Signed-off-by: Arsolitt <[email protected]>
Add | quote to all string scalar fields in Certificate CR output for
defensive YAML safety. Document hardcoded privateKey parameters and
extraDnsNames nil behavior. Replace empty README stub with library
description.

Signed-off-by: Arsolitt <[email protected]>
Quote duration, renewBefore, and label values for full consistency.
Add kindIs slice check to reject string dnsNames that would silently
iterate over individual characters via range.

Signed-off-by: Arsolitt <[email protected]>
Add kindIs slice check for extraDnsNames to match dnsNames guard.
Quote the hardcoded group field for full output consistency.
Update README to enumerate all helper categories in the library.

Signed-off-by: Arsolitt <[email protected]>
@github-actions github-actions Bot added the size/XL This PR changes 500-999 lines, ignoring generated files label May 4, 2026
@Arsolitt Arsolitt (Arsolitt) added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress label May 4, 2026
@Arsolitt
Arsolitt (Arsolitt) marked this pull request as ready for review May 4, 2026 11:15
@dosubot dosubot Bot added size:L and removed size/XL This PR changes 500-999 lines, ignoring generated files labels May 4, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 significantly enhances the cozy-lib Helm library by introducing shared TLS certificate management capabilities. It provides new template helpers that centralize and simplify the creation of cert-manager Certificate resources and the resolution of TLS secret names. This ensures consistent, validated, and reusable TLS configurations across various managed services, reducing manual effort and potential configuration mistakes.

Highlights

  • New TLS Certificate Helpers: Introduced two new Helm template helpers in cozy-lib for streamlined TLS certificate management: cozy-lib.tls.certificate for rendering cert-manager Certificate Custom Resources, and cozy-lib.tls.secretName for resolving or generating TLS secret names.
  • Robust Input Validation: Implemented comprehensive input validation for the new TLS helpers, including checks for all required fields, type guards for list parameters like DNS names, and enum validation for issuerRef.kind to ensure correct usage and prevent common configuration errors.
  • Standardized TLS Configuration Defaults: Standardized key TLS certificate configurations within the cozy-lib.tls.certificate helper, setting sensible defaults for the private key algorithm (RSA-2048), rotation policy (Always), certificate duration (1 year), and key usages (server auth).
  • Documentation and Testing Updates: Updated the cozy-lib README to reflect the new TLS helper category and provide an overview of all available helper categories. The test chart was renamed from quota to cozy-lib-tests, and extensive test suites were added to validate the functionality and error handling of the new TLS helpers.

🧠 New Feature in Public Preview: You can now enable Memory 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 Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize 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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. 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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@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.

🧹 Nitpick comments (1)
packages/library/cozy-lib/templates/_tls.tpl (1)

78-85: ⚡ Quick win

Add a kindIs "slice" type guard for usages, consistent with dnsNames/extraDnsNames.

Both dnsNames (line 44) and extraDnsNames (line 47) have explicit guards that surface a clear error when a string is passed instead of a list. usages has no such guard — a caller passing usages: "server auth" (string) passes the truthiness check on line 79, then hits range .usages which fails with a cryptic Go template error ("can't iterate over …") rather than an actionable message.

🛡️ Proposed fix
+{{- if and .usages (not (kindIs "slice" .usages)) -}}
+{{-   fail "ERROR: \"usages\" must be a list for cozy-lib.tls.certificate. Got a string — wrap it in a list." -}}
+{{- end -}}
   usages:
     {{- if .usages }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/library/cozy-lib/templates/_tls.tpl` around lines 78 - 85, Add the
same type-guard used for dnsNames and extraDnsNames to usages: ensure you check
kindIs "slice" before ranging over .usages and surface a clear error when a
string is provided; update the template logic around usages (the usages block
that currently does "if .usages" then "range .usages") to mirror the
dnsNames/extraDnsNames pattern so callers passing usages: "server auth" get a
descriptive error instead of the Go template iteration failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/library/cozy-lib/templates/_tls.tpl`:
- Around line 78-85: Add the same type-guard used for dnsNames and extraDnsNames
to usages: ensure you check kindIs "slice" before ranging over .usages and
surface a clear error when a string is provided; update the template logic
around usages (the usages block that currently does "if .usages" then "range
.usages") to mirror the dnsNames/extraDnsNames pattern so callers passing
usages: "server auth" get a descriptive error instead of the Go template
iteration failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5905dad6-0a31-42b4-9d2d-35ffebad3875

📥 Commits

Reviewing files that changed from the base of the PR and between 5786afe and 6b633b1.

📒 Files selected for processing (17)
  • packages/library/cozy-lib/README.md
  • packages/library/cozy-lib/templates/_tls.tpl
  • packages/tests/cozy-lib-tests/Chart.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-missing-release.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-missing-secretname.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname-missing-release.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname-missing-suffix.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls.yaml
  • packages/tests/cozy-lib-tests/tests/tls_missing_release_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_missing_secretname_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_missing_release_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_missing_suffix_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_no_suffix_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_values.yaml

@Arsolitt

Copy link
Copy Markdown
Contributor Author

/gemini review

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.

Observations on the helper extraction — not gating, leaving approval for another reviewer.

Substantive helper extraction with thorough test coverage — helm unittest from packages/tests/cozy-lib-tests passes 34/34 across 8 suites locally, covering both the certificate-render happy path and every validation branch (missing Release, missing secretName, slice guards on dnsNames, missing suffix when secretName not provided). The 16-commit history reflects honest iteration on review feedback rather than a single take-it-or-leave-it patch — quoted scalars across all string outputs, enum-validation on issuerRef.kind, the "suffix is only required when secretName is not provided" carve-out in cozy-lib.tls.secretName, and the explicit kindIs "slice" guards on dnsNames / extraDnsNames are all the right shapes.

Design choices that look correct:

  • Hardcoding privateKey: { algorithm: RSA, size: 2048, rotationPolicy: Always } and exposing nothing about it. Documented as deliberate. Anyone who needs a different algo or rotation behavior should write their own Certificate CR — keeping this helper opinionated on a single sane default avoids the Helm-template combinatorial explosion that would otherwise creep in.
  • Defaulting usages: ["server auth"] instead of cert-manager's ["digital signature", "key encipherment"]. Explicitly documented as a deviation. For the cozystack use case (TLS server certs for managed services), server auth is what reviewers actually want; cert-manager's default is for general-purpose certs. Right call.
  • group: "cert-manager.io" hardcoded on the issuerRef. There is exactly one valid group, so exposing it as a parameter would be cosmetic surface area without a use case.
  • cozy-lib.tls.secretName returning the user-provided name verbatim or generating <release>-<suffix> — this is the right primitive for the conditional render pattern (mount existing secret if user-provided, otherwise emit a Certificate CR that will create one).

One outstanding finding from the latest CodeRabbit pass that's worth folding in either here or in a follow-up:

  • usages is the only list-typed parameter without a kindIs "slice" guard (compare dnsNames at line 44-46 and extraDnsNames at line 47-49). A user who passes usages: "server auth" (string instead of single-element list) currently hits Helm's generic "range can't iterate over string" error, which is much harder to action than the bespoke "must be a list ... wrap it in a list" message the other two parameters produce. Trivial consistency fix:
{{- if and .usages (not (kindIs "slice" .usages)) -}}
{{-   fail "ERROR: \"usages\" must be a list for cozy-lib.tls.certificate. Got a string — wrap it in a list." -}}
{{- end -}}

Two non-blocking observations for future iterations:

  • privateKey.encoding is unset, so cert-manager defaults it to PKCS#1. Most modern consumers expect PKCS#8, and cert-manager itself recommends PKCS#8 for new deployments. If a consumer chart hits this, it's worth promoting encoding: PKCS8 to the helper (still hardcoded, no parameter). Out of scope here.
  • The Certificate CR's metadata.namespace comes from .Release.Namespace. If a chart ever needs to render a Certificate into a namespace different from the release namespace (cross-tenant cert sharing, control-plane cert mirrored into a tenant ns, etc.), the helper currently has no escape hatch. Today no consumer needs this — all current managed-service charts render into their own release namespace — so worth flagging as a known constraint rather than a fix.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces TLS certificate and secret name helpers to the cozy-lib library chart, featuring robust input validation and a comprehensive test suite. The changes also include a README for the library and a rename of the testing chart to cozy-lib-tests. Feedback was provided to incorporate the trim function into the secret name helpers to prevent potential issues caused by accidental whitespace.

Comment on lines +116 to +117
{{- if not (empty .secretName) -}}
{{- .secretName -}}

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.

low

The helper returns the secretName directly without quoting. While the caller might quote it, it's safer to ensure the output of this helper is consistent with other string-returning helpers in the library. However, since this is often used in assignments like {{- $tlsSecret := include ... }}, returning the raw string is generally preferred for flexibility.

One minor improvement: using trim to ensure no accidental whitespace is included in the returned secret name.

{{- if not (empty .secretName) -}}
{{-   .secretName | trim -}}
{{- else -}}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The {{- ... -}} delimiters already strip surrounding template whitespace. For value-level whitespace in secretName, Kubernetes name validation ([a-z0-9]([-a-z0-9]*[a-z0-9])?) catches it at apply time with a clear error. Adding | trim would silently fix a misconfigured value instead of surfacing the error — we'd rather let the API server reject invalid names so the user knows their values.yaml needs correcting. Both Release.Name (Helm-managed DNS subdomain) and user-provided secretName (Kubernetes resource name) cannot legitimately contain whitespace.

{{- if not .suffix -}}
{{- fail "ERROR: \"suffix\" is required for cozy-lib.tls.secretName when secretName is not provided. Provide a suffix for the generated secret name (e.g. \"tls\", \"server-tls\")." -}}
{{- end -}}
{{- printf "%s-%s" .Release.Name .suffix -}}

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.

low

Similar to the user-provided secretName, the generated name should be trimmed to avoid issues if Release.Name or suffix contain leading/trailing spaces (though unlikely for Release.Name).

{{-   printf "%s-%s" .Release.Name .suffix | trim -}}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reasoning as above — Release.Name is always a valid DNS subdomain provided by Helm, and suffix is a string literal from the calling template. The {{- ... -}} delimiters handle template whitespace, and Kubernetes name validation covers the (theoretical) value-level case. Silently trimming would mask configuration errors.

Address review feedback from lexfrei on _tls.tpl:
Add type guard for usages to match dnsNames/extraDnsNames pattern,
producing an actionable error instead of Helm's generic range error.

Signed-off-by: Arsolitt <[email protected]>
@github-actions github-actions Bot added size/XL This PR changes 500-999 lines, ignoring generated files and removed size:L labels May 4, 2026
@Arsolitt

Copy link
Copy Markdown
Contributor Author

Good catch — added a kindIs "slice" guard for usages in 4a42159, matching the existing pattern for dnsNames and extraDnsNames. A user passing usages: "server auth" now gets the actionable "must be a list — wrap it in a list" message instead of Helm's generic range error.

On the non-blocking notes: privateKey.encoding (PKCS#1 vs PKCS#8) and the Release.Namespace constraint are noted as known limitations for future iterations — both are documented in the helper's doc comment.

@Arsolitt

Copy link
Copy Markdown
Contributor Author

CodeRabbit (@coderabbitai) full review

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@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.

🧹 Nitpick comments (3)
packages/library/cozy-lib/templates/_tls.tpl (2)

67-72: ⚡ Quick win

Consider adding an optional labels parameter for consumer-injected labels

The PR description lists "configurable labels" as a feature, but the template's Parameters block has no labels entry and the metadata section only auto-generates app.kubernetes.io/instance and app.kubernetes.io/managed-by from Release. Callers cannot attach custom labels (e.g., for NetworkPolicy selectors or monitoring alerts) to the Certificate CR without wrapping the helper.

♻️ Suggested approach
 metadata:
   name: {{ .name | quote }}
   namespace: {{ .Release.Namespace | quote }}
   labels:
     app.kubernetes.io/instance: {{ .Release.Name | quote }}
     app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
+    {{- if .labels }}
+    {{- if not (kindIs "map" .labels) }}
+    {{- fail "ERROR: \"labels\" must be a map for cozy-lib.tls.certificate." }}
+    {{- end }}
+    {{- range $k, $v := .labels }}
+    {{ $k }}: {{ $v | quote }}
+    {{- end }}
+    {{- end }}

And document it in the Parameters block:

+  - labels      (optional) - map of additional labels to add to the Certificate CR metadata
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/library/cozy-lib/templates/_tls.tpl` around lines 67 - 72, Add an
optional consumer-provided labels parameter so callers can inject arbitrary
labels into the generated Certificate metadata: add a new parameter (e.g.,
"labels" or ".Values.labels") to the template's Parameters block and merge it
into the metadata.labels map alongside the existing app.kubernetes.io/instance
and app.kubernetes.io/managed-by entries (ensuring nil/empty checks so no extra
keys when not provided). Update the template to iterate/merge the provided
labels into metadata and document the new parameter in the Parameters block so
consumers can pass custom labels for NetworkPolicy/monitoring selectors.

115-127: ⚡ Quick win

Release is required even when secretName is supplied

The Release guard at line 116 fires unconditionally before the secretName check at line 119. A caller who provides a static secretName and omits Release will receive an error even though Release.Name is only needed in the generated-name branch (line 125). Reordering makes Release truly optional when a static secretName is given.

♻️ Suggested reorder
 {{- define "cozy-lib.tls.secretName" -}}
-{{- if not .Release -}}
-{{-   fail "ERROR: \"Release\" is required for cozy-lib.tls.secretName. Pass the Helm release object via the context dict." -}}
-{{- end -}}
 {{- if not (empty .secretName) -}}
 {{-   .secretName -}}
 {{- else -}}
+{{-   if not .Release -}}
+{{-     fail "ERROR: \"Release\" is required for cozy-lib.tls.secretName. Pass the Helm release object via the context dict." -}}
+{{-   end -}}
 {{-   if not .suffix -}}
 {{-     fail "ERROR: \"suffix\" is required for cozy-lib.tls.secretName when secretName is not provided. ..." -}}
 {{-   end -}}
 {{-   printf "%s-%s" .Release.Name .suffix -}}
 {{- end -}}
 {{- end }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/library/cozy-lib/templates/_tls.tpl` around lines 115 - 127, The
guard for .Release in the template cozy-lib.tls.secretName runs too early and
blocks callers who provided a static .secretName; change the logic to first
check if .secretName is non-empty and return it, and only when .secretName is
empty validate .suffix and require .Release (so you can safely use .Release.Name
to construct the generated name). Ensure you still fail with the existing
messages when .secretName is empty and .suffix or .Release is missing.
packages/tests/cozy-lib-tests/tests/tls_test.yaml (1)

1-521: 💤 Low value

LGTM — comprehensive coverage across all configurable parameters and validation guards.

One minor asymmetry: dnsNames, extraDnsNames, and usages all have dedicated "errors when X is a string instead of a list" tests (lines 464–520), but there is no equivalent test for passing issuerRef as a string (e.g., issuerRef: "my-issuer"). In practice, a string issuerRef would likely produce the "issuerRef.kind is required" error rather than a type-specific message, so the failure is still actionable; this is a nice-to-have for completeness.

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

In `@packages/tests/cozy-lib-tests/tests/tls_test.yaml` around lines 1 - 521, Add
a test case to tls_test.yaml that checks behavior when issuerRef is provided as
a string (e.g., issuerRef: "my-issuer"); create a new "it: errors when issuerRef
is a string instead of a dict" test similar to the existing string-type tests
for dnsNames/extraDnsNames/usages, set tls with name/dnsNames and issuerRef as a
string, and assert a failedTemplate with the expected validation error (either
the existing 'ERROR: "issuerRef.kind" is required...' message or a clearer
'ERROR: "issuerRef" must be a dict...' if you prefer to make the validation
stricter).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/library/cozy-lib/templates/_tls.tpl`:
- Around line 67-72: Add an optional consumer-provided labels parameter so
callers can inject arbitrary labels into the generated Certificate metadata: add
a new parameter (e.g., "labels" or ".Values.labels") to the template's
Parameters block and merge it into the metadata.labels map alongside the
existing app.kubernetes.io/instance and app.kubernetes.io/managed-by entries
(ensuring nil/empty checks so no extra keys when not provided). Update the
template to iterate/merge the provided labels into metadata and document the new
parameter in the Parameters block so consumers can pass custom labels for
NetworkPolicy/monitoring selectors.
- Around line 115-127: The guard for .Release in the template
cozy-lib.tls.secretName runs too early and blocks callers who provided a static
.secretName; change the logic to first check if .secretName is non-empty and
return it, and only when .secretName is empty validate .suffix and require
.Release (so you can safely use .Release.Name to construct the generated name).
Ensure you still fail with the existing messages when .secretName is empty and
.suffix or .Release is missing.

In `@packages/tests/cozy-lib-tests/tests/tls_test.yaml`:
- Around line 1-521: Add a test case to tls_test.yaml that checks behavior when
issuerRef is provided as a string (e.g., issuerRef: "my-issuer"); create a new
"it: errors when issuerRef is a string instead of a dict" test similar to the
existing string-type tests for dnsNames/extraDnsNames/usages, set tls with
name/dnsNames and issuerRef as a string, and assert a failedTemplate with the
expected validation error (either the existing 'ERROR: "issuerRef.kind" is
required...' message or a clearer 'ERROR: "issuerRef" must be a dict...' if you
prefer to make the validation stricter).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0fe9da0f-727b-426b-b3d0-49b7a7e6c1eb

📥 Commits

Reviewing files that changed from the base of the PR and between 5786afe and 4a42159.

📒 Files selected for processing (17)
  • packages/library/cozy-lib/README.md
  • packages/library/cozy-lib/templates/_tls.tpl
  • packages/tests/cozy-lib-tests/Chart.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-missing-release.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-missing-secretname.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname-missing-release.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname-missing-suffix.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls-secretname.yaml
  • packages/tests/cozy-lib-tests/templates/tests/tls.yaml
  • packages/tests/cozy-lib-tests/tests/tls_missing_release_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_missing_secretname_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_missing_release_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_missing_suffix_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_no_suffix_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_secretname_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_test.yaml
  • packages/tests/cozy-lib-tests/tests/tls_values.yaml

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.

Verified the usages kindIs "slice" guard in 4a4215956 — the new branch in _tls.tpl:50-52 mirrors the existing dnsNames / extraDnsNames guards, the tls_test.yaml test case ("errors when usages is a string") pins the contract, and helm unittest is now 35/35 passing across 8 suites locally. Thanks for that.

One note before the substance: in #2485 (comment) you stated that privateKey.encoding (PKCS#1 vs PKCS#8) and the Release.Namespace constraint are "documented in the helper's doc comment." I re-read _tls.tpl:1-30 after 4a4215956 and neither is mentioned — line 28 calls out RSA-2048 and rotationPolicy: Always as the hardcoded private-key constraints, but says nothing about encoding, and the docstring's parameter list says nothing about the namespace coming verbatim from Release.Namespace with no override path. The notes exist in the review thread but not in the file. Both items are below as blockers/observations rather than as accepted limitations.

Blockers

B1: helper hardcodes privateKey.encoding to cert-manager's default (PKCS#1) for a permanent cross-service TLS primitive

File: packages/library/cozy-lib/templates/_tls.tpl:74-77

Issue: The helper sets privateKey: { algorithm: RSA, size: 2048, rotationPolicy: Always } and leaves encoding unset. cert-manager defaults unset privateKey.encoding to PKCS1. Cert-manager's own documentation (https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificatePrivateKey) lists PKCS8 as the recommended encoding for modern deployments, and several Kubernetes-ecosystem TLS clients (Go's crypto/tls since 1.18 expects either; many Java/Python TLS libraries built against modern OpenSSL prefer PKCS#8) read PKCS#8 keys without an extra conversion step, while PKCS#1 needs openssl pkcs8 -topk8 for compatibility.

For a one-off chart this would be cosmetic. For a shared library helper that the PR description bills as the anchor for all future managed-service TLS in cozystack (cozy-lib.tls.certificate), the PKCS#1 default ossifies into the contract for every consumer that follows. By the time the second or third managed service plugs into this helper and someone hits a TLS-handshake-fails-against-Go-1.22-client bug, the helper has shipped the implicit promise.

This is materially the same shape as the kindIs "slice" guards already added to dnsNames / extraDnsNames / usages — defaults that bake in an opinion the helper should hold strongly because callers can't easily override it.

Fix: Add encoding: PKCS8 alongside algorithm: RSA and size: 2048 in the privateKey block (keeping it hardcoded — this is opinionation by design, not a parameter to expose). Update the docstring at _tls.tpl:28 to read Private key is hardcoded to RSA-2048 PKCS#8 with rotationPolicy: Always; these are not configurable via this helper. Add a test that asserts spec.privateKey.encoding == "PKCS8" so the contract is pinned.

B2: helper's scope (leaf TLS certs only) is not documented and will mislead consumers attempting CA certs

File: packages/library/cozy-lib/templates/_tls.tpl:1-30 (docstring) and packages/library/cozy-lib/README.md

Issue: The helper is built around defaults that are correct only for leaf TLS certs: usages: ["server auth"], rotationPolicy: Always, RSA-2048, 1-year duration, no commonName, no isCA, no subject parameter. The repo already contains six hand-rolled CA Certificate CRs (packages/system/cozystack-api/templates/certmanager.yaml, packages/system/kubeovn-webhook/templates/certmanager.yaml, packages/system/lineage-controller-webhook/templates/certmanager.yaml, packages/system/linstor/templates/linstor-internal-tls.yaml, packages/extra/etcd/templates/etcd-cluster.yaml — two CAs there) using the opposite shape: isCA: true, commonName, usages: [signing, key encipherment, cert sign], rotationPolicy: Never, RSA-4096, 5–10 year duration, subject.organizations for etcd. None of those can migrate to this helper, and that's intentional.

Nothing in _tls.tpl's docstring or cozy-lib/README.md says so. A future contributor reading "shared TLS certificate helpers for managed services" will reasonably try cozy-lib.tls.certificate for a CA cert, hit the missing-isCA / wrong-usages / wrong-rotationPolicy / wrong-key-size wall, and either fork the helper or land a half-broken Certificate CR.

Fix: Add an explicit "Scope" line near the top of the docstring (around _tls.tpl:5) and the corresponding README section:

Scope: leaf (end-entity) TLS certificates only. For CA certificates that
need `isCA: true`, `commonName`, signing/key-encipherment/cert-sign
usages, `rotationPolicy: Never`, RSA-4096, or `subject` fields, write the
Certificate CR by hand — see `packages/system/{cozystack-api,kubeovn-webhook,
lineage-controller-webhook,linstor}/templates/` for the established pattern.

No test required — this is documentation-only.

Non-blocking observations

  1. Release.Namespace is hardcoded with no override path. _tls.tpl:66 renders metadata.namespace: {{ .Release.Namespace | quote }} with no parameter to override. Today no consumer needs cross-namespace rendering, but a one-line entry in the docstring ("metadata.namespace is Release.Namespace; cross-namespace rendering is not supported — caller must use a separate Helm release") would save the next maintainer the same investigation. Trivial.

  2. secretName is required, no default-to-name. Forces every caller to repeat (default .name .secretName) — templates/tests/tls.yaml:2 itself demonstrates this boilerplate. The docstring acknowledges "a caller-level wrapper can default this to name" as deliberate design, but defaulting secretName to name inside the helper would remove the repeated boilerplate without losing any expressiveness. Optional design call, not a defect.

  3. Hardcoded labels on the Certificate CR. _tls.tpl:67-69 emits only app.kubernetes.io/instance and app.kubernetes.io/managed-by labels. Consumers cannot inject app.kubernetes.io/name, app.kubernetes.io/component, monitoring/team labels, or NetworkPolicy selectors without wrapping the helper. CodeRabbit flagged the same on #2485 (comment) (coderabbitai 2026-05-04T13:51:22Z). For a shared helper this is the kind of extensibility-tax that bites the third or fourth consumer; an optional labels dict parameter (with kindIs "map" guard, mirroring the slice guards) would future-proof it. Not blocking — current behavior is consistent and tested.

  4. README does not mention the cert-manager CRDs prerequisite. cozy-lib/README.md documents the helper categories but does not state that consumers' clusters must already have cert-manager installed for any of cozy-lib.tls.* to work. One sentence ("cert-manager CRDs must be present in the cluster — these helpers do not install or vendor cert-manager itself") would prevent consumer charts from shipping into clusters that 404 on Certificate.cert-manager.io. Trivial.

  5. Em-dash — in error messages. _tls.tpl:46, :49, :52 use the U+2014 em-dash; every other tpl in packages/library/cozy-lib/templates/ is ASCII-only. Cosmetic; switch to -- for consistency. Won't fail any test.

  6. duration: 8760h (1 year) is on the long side. The doc points out this is a deliberate departure from cert-manager's 90-day default. With renewBefore: 720h (30 days) the rotation cadence is quarterly-ish, fine for most operators. Compliance postures requiring shorter (e.g., 90-day commercial) would need to override duration per consumer; the override path is exposed and tested, so this is just worth a doc nudge alongside the scope clarification in B2.

Verification

  • helm unittest packages/tests/cozy-lib-tests → 35/35 pass after 4a4215956 (was 34 prior; the new usages slice-guard test brought it to 35).
  • Validation paths fully covered: not Release / not name / not secretName / not dnsNames / dnsNames not slice / extraDnsNames not slice / usages not slice / not issuerRef / not issuerRef.name / not issuerRef.kind / invalid issuerRef.kind enum / secretName helper not Release / secretName helper not suffix (only when secretName empty). Every branch has a corresponding failedTemplate assertion.
  • cozy-lib-tests chart symlink at packages/tests/cozy-lib-tests/charts/cozy-lib resolves to packages/library/cozy-lib; tests pick up the latest helper without a vendoring step.
  • Chart rename quota → cozy-lib-tests has no external consumers — grep is clean across packages, scripts, hack, .github.

…ssages

Address review feedback from lexfrei:
- Set privateKey.encoding to PKCS8 for modern TLS compatibility
- Document helper scope as leaf certs only (not CA certificates)
- Add namespace and cert-manager prerequisite documentation
- Normalize em-dashes to double-dashes in error messages for ASCII consistency

Signed-off-by: Arsolitt <[email protected]>
@dosubot dosubot Bot added size:L and removed size/XL This PR changes 500-999 lines, ignoring generated files labels May 4, 2026
@Arsolitt

Copy link
Copy Markdown
Contributor Author

Addressed both blockers in b67e7736:

B1: Added encoding: PKCS8 to the privateKey block (hardcoded alongside algorithm/size/rotationPolicy). Doc comment updated to mention PKCS#8. Test pins spec.privateKey.encoding == "PKCS8".

B2: Added scope documentation at the top of _tls.tpl — "leaf (end-entity) TLS certificates only" with a note that CA certificates should be hand-crafted. README TLS entry also updated with "(leaf/end-entity certs only)" and a new Prerequisites section noting the cert-manager CRDs requirement.

Also folded in the non-blocking items: namespace doc note ("metadata.namespace is always Release.Namespace; cross-namespace rendering is not supported"), cert-manager prerequisite in README, and normalized em-dashes to -- for ASCII consistency with the rest of the library.

Apologies for the inaccuracy in my earlier reply about encoding/namespace being documented — they weren't at that point. They are now.

@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.

🧹 Nitpick comments (1)
packages/tests/cozy-lib-tests/tests/tls_test.yaml (1)

44-56: 💤 Low value

Consider adding hasDocuments: count: 1 to standalone positive test cases.

The first test case (lines 15–16) asserts hasDocuments: count: 1, but the remaining positive test cases that render a document don't repeat that assertion. If the template were ever modified to conditionally emit multiple documents, those tests would silently pass on the first document while the regression goes unnoticed. Adding the assertion to each positive case makes them independently self-validating.

Compare with the "uses default duration when duration is an empty string" test (line 402–403) which does include hasDocuments: count: 1 — making it the more robust pattern.

♻️ Example for the "sets dnsNames correctly" test (same pattern for all affected cases)
 asserts:
+  - hasDocuments:
+      count: 1
   - equal:
       path: spec.dnsNames

Also applies to: 58-74, 76-89, 91-103, 132-143, 171-191, 194-211, 214-234, 335-358, 361-382, 446-465

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

In `@packages/tests/cozy-lib-tests/tests/tls_test.yaml` around lines 44 - 56, Add
an explicit document-count assertion to each standalone positive test case that
renders a document (e.g., the "sets dnsNames correctly" test and the other
listed tests) by inserting a top-level hasDocuments: count: 1 entry in the test
YAML alongside values/release before the asserts block; ensure every positive
case that currently asserts on spec.* also includes hasDocuments: count: 1 so
each test independently verifies exactly one rendered document.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/tests/cozy-lib-tests/tests/tls_test.yaml`:
- Around line 44-56: Add an explicit document-count assertion to each standalone
positive test case that renders a document (e.g., the "sets dnsNames correctly"
test and the other listed tests) by inserting a top-level hasDocuments: count: 1
entry in the test YAML alongside values/release before the asserts block; ensure
every positive case that currently asserts on spec.* also includes hasDocuments:
count: 1 so each test independently verifies exactly one rendered document.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2bfdaf59-1868-49b3-b1ab-06ff93dfb6ef

📥 Commits

Reviewing files that changed from the base of the PR and between 4a42159 and b67e773.

📒 Files selected for processing (3)
  • packages/library/cozy-lib/README.md
  • packages/library/cozy-lib/templates/_tls.tpl
  • packages/tests/cozy-lib-tests/tests/tls_test.yaml
✅ Files skipped from review due to trivial changes (2)
  • packages/library/cozy-lib/templates/_tls.tpl
  • packages/library/cozy-lib/README.md

Comment on lines +82 to +85
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather default to a more modern algorithm. RSA support can be left as an opt-in for legacy applications.

@lexfrei

Copy link
Copy Markdown
Contributor

Holding pending rework: this should be promoted from a helper file in cozy-lib/templates/ to its own subchart (or sibling library chart) rather than landing as a flat helper inside cozy-lib. Promoting it gives the TLS surface area its own version lifecycle, lets consumers opt into it independently of unrelated cozy-lib helpers (RBAC, quota, etc.), and avoids coupling the release cadence of one helper to the rest of the library.

Marking do-not-merge/hold until that restructure lands; the prior CHANGES_REQUESTED review on the PKCS encoding default and the leaf-only scope docstring still applies on top.

@lexfrei Aleksei Sviridkin (lexfrei) added the do-not-merge/hold Indicates that a PR should not merge because someone has issued /hold label May 5, 2026
@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files and removed size:L labels May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) do-not-merge/hold Indicates that a PR should not merge because someone has issued /hold do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants