Skip to content

feat(charts): centralise DNS-1123 subdomain validation in talm helper#158

Merged
Aleksei Sviridkin (lexfrei) merged 4 commits into
mainfrom
feat/dns1123-validation
May 9, 2026
Merged

feat(charts): centralise DNS-1123 subdomain validation in talm helper#158
Aleksei Sviridkin (lexfrei) merged 4 commits into
mainfrom
feat/dns1123-validation

Conversation

@lexfrei
Copy link
Copy Markdown
Contributor

@lexfrei Aleksei Sviridkin (lexfrei) commented May 9, 2026

Summary

Replace per-site inline regex validation of values.yaml: clusterName with a shared talm.validate.dns1123subdomain helper, apply it to clusterDomain (cozystack), and add Go-side validation of talm init --name against the same upstream Kubernetes contract.

Three input channels accept DNS-name values from the operator:

  1. talm init --name <X> — Go path. Now validated in PreRunE via k8s.io/apimachinery/pkg/util/validation.IsDNS1123Subdomain. Previously accepted anything; failure surfaced downstream as an opaque bundle-generator error.
  2. values.yaml: clusterName — Helm template path in cozystack and generic charts. Previously a per-site inline regex with a misleading "DNS-1123 label" message and no length cap. Now piped through talm.validate.dns1123subdomain which matches the same RFC 1123 subdomain regex as the upstream k8s validator, enforces total length ≤ 253 chars, and emits a precise message naming the field and quoting the offending value.
  3. values.yaml: clusterDomain (cozystack only) — previously unvalidated. Same helper, same contract.

Test coverage

  • pkg/commands/contract_init_validation_test.go — every shipped chart name passes; uppercase, underscore, leading/trailing dash, space, double dot, 254-char length all rejected with messages quoting the offending value and naming "DNS-1123 subdomain". Validator skipped under --encrypt / --decrypt / --update where --name is not required.
  • pkg/engine/contract_dns1123_test.go — helper accepts canonical happy paths (single label, multi-label, leading digit, single character); rejects every invalid shape with a message naming the field. Same negative set tested for clusterDomain. Default cozy.local still passes on cozystack. Generic chart shares one negative case to catch template-only regressions.

Notes

Per-label length (63 chars per RFC 1035) intentionally NOT enforced — upstream IsDNS1123Subdomain does not enforce it, and there is no Talos-side cap that aligns with a 63-char floor. Stay symmetric with Go-side validation.

Closes the follow-ups raised on #148.

Summary by CodeRabbit

  • New Features

    • Added DNS-1123 subdomain validation for cluster names and domains to ensure proper formatting.
  • Improvements

    • The init --name command now validates input upfront with detailed error messaging for invalid values.
    • Cluster domain values are now consistently validated across all chart templates.
  • Tests

    • Added comprehensive validation test coverage for cluster naming and domain constraints.

Review Change Stack

Replace per-site inline regex validation of values.yaml clusterName
with a shared talm.validate.dns1123subdomain helper, apply it to
clusterDomain (cozystack), and add Go-side validation of
`talm init --name` against the same upstream Kubernetes contract.

Three input channels accept DNS-name values from the operator:

  1. `talm init --name <X>` — Go code path. Now validated in
     PreRunE via k8s.io/apimachinery/pkg/util/validation
     .IsDNS1123Subdomain so the operator sees the precise upstream
     error (length, character class) before any file is written.
     Previously this flag accepted anything and the failure
     surfaced downstream as an opaque bundle-generator error.

  2. values.yaml clusterName — Helm template path in cozystack and
     generic charts. Previously a per-site inline regex with a
     misleading 'DNS-1123 label' message and no length cap. Now
     piped through talm.validate.dns1123subdomain which:
       - matches the same RFC 1123 subdomain regex as the upstream
         k8s validator (so chart and CLI agree byte-for-byte)
       - enforces total length <= 253 chars (was missing)
       - emits a precise message naming the field and quoting the
         offending value

  3. values.yaml clusterDomain (cozystack only) — previously
     unvalidated. Same helper, same contract.

Test coverage:

  - pkg/commands/contract_init_validation_test.go pins the Go-side
    contract: every shipped chart name passes; uppercase, underscore,
    leading/trailing dash, space, double dot, 254-char length all
    rejected with messages quoting the offending value and naming
    'DNS-1123 subdomain'. Validator skipped under --encrypt /
    --decrypt / --update where --name is not required.

  - pkg/engine/contract_dns1123_test.go pins the Helm template
    contract: helper accepts canonical happy paths (single label,
    multi-label, leading digit, single character); rejects every
    invalid shape with a message naming the field. Same negative
    set tested for clusterDomain. Default cozy.local still passes
    on cozystack. Generic chart shares one negative case to catch
    template-only regressions.

Per-label length (63 chars) intentionally NOT enforced — upstream
IsDNS1123Subdomain does not enforce it, and there is no
Talos-side cap that aligns with a 63-char floor. Stay symmetric
with Go-side validation.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Warning

Rate limit exceeded

@lexfrei has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 19 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f3ba45f6-3fcc-47fe-894f-a0e4da14d400

📥 Commits

Reviewing files that changed from the base of the PR and between 122194e and f36dd9b.

📒 Files selected for processing (2)
  • charts/talm/templates/_helpers.tpl
  • pkg/engine/contract_dns1123_test.go
📝 Walkthrough

Walkthrough

This PR introduces DNS-1123 subdomain validation for cluster names and domains across Helm templates and CLI commands. It adds a reusable Helm validation helper, updates chart templates to enforce validation, adds CLI-side validation for the talm init --name flag, and provides comprehensive test coverage verifying valid and invalid cases.

Changes

DNS-1123 Subdomain Validation

Layer / File(s) Summary
Helm Validation Helper
charts/talm/templates/_helpers.tpl
New talm.validate.dns1123subdomain helper validates DNS-1123 subdomains: enforces non-empty, max-length 253, and lowercase RFC 1123 pattern; fails template render with field-specific errors on violations.
Chart Integration
charts/cozystack/templates/_helpers.tpl, charts/generic/templates/_helpers.tpl
Cozystack and generic charts replace inline regex clusterName checks with the parameterized validator; cozystack also validates clusterDomain via the same helper, with quoted output.
CLI Validation
pkg/commands/init.go
talm init command's PreRunE validates --name flag using Kubernetes' validation.IsDNS1123Subdomain, failing early with detailed error messages for invalid cluster names.
Helm Template Tests
pkg/engine/contract_dns1123_test.go, pkg/engine/contract_cluster_test.go
Table-driven tests verify valid clusterName values render with quoted output, invalid shapes (uppercase, underscores, dashes, spaces, overly long) fail with specific error substrings; cozystack-specific tests validate clusterDomain and default cozy.local rendering.
CLI Command Tests
pkg/commands/contract_init_validation_test.go
Tests verify initCmd.PreRunE accepts valid DNS-1123 subdomains, rejects invalid ones with quoted-input error messages, and skips validation on --encrypt/--decrypt/--update modes when name is empty; includes test-isolation helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A validator hops in place,
DNS-1123 checks with grace—
Subdomain names now sanitized,
Chart and CLI harmonized,
Test cases hop from side to side,
Ensuring validation's tested wide!

🚥 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 accurately describes the primary change: centralizing DNS-1123 subdomain validation in a shared talm helper, which is the core architectural improvement across multiple chart templates and validation layers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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/dns1123-validation

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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

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 centralized DNS-1123 subdomain validation for cluster names and domains across Helm charts and the CLI. It adds a new Helm helper, talm.validate.dns1123subdomain, and integrates Go-side validation using the Kubernetes apimachinery package. Comprehensive contract tests are included for both CLI and chart rendering. Feedback suggests quoting the dnsDomain output for YAML safety and improving the descriptiveness of validation error messages for consistency.

Comment thread charts/cozystack/templates/_helpers.tpl Outdated
cni:
name: none
dnsDomain: {{ .Values.clusterDomain }}
dnsDomain: {{ include "talm.validate.dns1123subdomain" (dict "value" .Values.clusterDomain "field" "clusterDomain") }}
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.

medium

The dnsDomain value should be quoted to ensure it is always interpreted as a string by YAML parsers. This is particularly important for values that might otherwise be parsed as numbers (e.g., a subdomain consisting only of digits like 123.456). This change also brings consistency with the clusterName field on line 106. Note: If this change is applied, the test case TestContract_DNS1123_ClusterDomain_DefaultPasses in pkg/engine/contract_dns1123_test.go will need to be updated to expect the quoted value.

    dnsDomain: {{ include "talm.validate.dns1123subdomain" (dict "value" .Values.clusterDomain "field" "clusterDomain") | quote }}

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.

Right — leaving dnsDomain as a raw scalar leaves it open to numeric reparse on a digits-only subdomain. Piped through | quote for consistency with clusterName, and updated TestContract_DNS1123_ClusterDomain_DefaultPasses plus TestContract_Cluster_ClusterDomain_Cozystack to expect the quoted form. See ac9d64b.

Comment thread charts/talm/templates/_helpers.tpl Outdated
{{- $value := .value -}}
{{- $field := .field -}}
{{- if not $value -}}
{{- fail (printf "values.yaml: %s must be a non-empty DNS-1123 subdomain (lowercase alphanumeric, '-', and '.'; total max 253 chars)" $field) -}}
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.

medium

This error message is less descriptive than the one used for regex validation failures on line 516. For consistency and better user feedback, it should explicitly mention the requirements (lowercase, allowed characters, and start/end constraints) enforced by the DNS-1123 contract.

{{- fail (printf "values.yaml: %s must be a non-empty DNS-1123 subdomain (must be lowercase, only [a-z0-9-.], start and end with [a-z0-9], max 253 chars)" $field) -}}

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.

Agreed — the empty branch was less descriptive than the regex branch a few lines down. Aligned both messages on the same constraint list (lowercase, [a-z0-9-.], start/end with [a-z0-9], max 253 chars) so operators see the same rule regardless of which guard fires. See 122194e.

Address review feedback from gemini-code-assist on
charts/cozystack/templates/_helpers.tpl:101: dnsDomain was being
emitted as an unquoted scalar, leaving it open to YAML re-parse
into a number when the value happens to look numeric (e.g. a
subdomain made of digits). Pipe through | quote to lock it in as
a string and align with the clusterName convention.

Tests in pkg/engine/contract_{dns1123,cluster}_test.go updated to
expect the quoted form.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
Address review feedback from gemini-code-assist on
charts/talm/templates/_helpers.tpl:510: the empty-value branch
emitted a shorter, less specific error than the regex branch a
few lines down. Both should describe the same constraint set so
operators do not have to consult two messages to learn the rule.

Match the wording used by the regex branch — must be lowercase,
only [a-z0-9-.], start and end with [a-z0-9], max 253 chars.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
pkg/engine/contract_dns1123_test.go (1)

90-95: ⚡ Quick win

Pin quoted offending-value behavior in render-error assertions.

The contract comments require quoted offending values, but current rejection tests only assert field name + generic substring. Add explicit quoted-value checks so message precision is locked.

✅ Suggested test hardening
@@
 			if !strings.Contains(err.Error(), "clusterName") {
 				t.Errorf("error must name 'clusterName' field, got: %v", err)
 			}
+			if !strings.Contains(err.Error(), `"`+tc.clusterName+`"`) {
+				t.Errorf("error must quote offending clusterName, got: %v", err)
+			}
 			if !strings.Contains(err.Error(), tc.wantInError) {
 				t.Errorf("error must contain %q, got: %v", tc.wantInError, err)
 			}
@@
 			if !strings.Contains(err.Error(), "clusterDomain") {
 				t.Errorf("error must name 'clusterDomain' field, got: %v", err)
 			}
+			if !strings.Contains(err.Error(), `"`+tc.clusterDomain+`"`) {
+				t.Errorf("error must quote offending clusterDomain, got: %v", err)
+			}
 			if !strings.Contains(err.Error(), tc.wantInError) {
 				t.Errorf("error must contain %q, got: %v", tc.wantInError, err)
 			}

Also applies to: 126-131

🤖 Prompt for 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.

In `@pkg/engine/contract_dns1123_test.go` around lines 90 - 95, The test
assertions in contract_dns1123_test.go currently only check for the field name
and a generic substring (tc.wantInError); update the test to also assert that
the rendered error message includes the offending value wrapped in quotes (e.g.,
the quoted form of the invalid value) so the contract’s requirement for quoted
offending-values is enforced; locate the checks around the block that contains
strings.Contains(err.Error(), "clusterName") and the tc.wantInError assertion
(also duplicate at the later block around lines 126-131) and add an assertion
that err.Error() contains the quoted offending value (construct the quoted form
from the test case’s offending value variable) to harden the expectations.
🤖 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 `@charts/talm/templates/_helpers.tpl`:
- Around line 507-513: The template currently uses .value directly (symbols:
$value, $field, len, fail) which breaks for non-string YAML scalars; coerce
.value to a string first (e.g., via printf "%v") and then perform the emptiness
and length checks so len operates on a string and the falsy check doesn't
mis-handle numeric 0—update references to use the canonical string variable when
calling if not, len, and in the fail messages to ensure safe validation of
DNS-1123 subdomain values.

---

Nitpick comments:
In `@pkg/engine/contract_dns1123_test.go`:
- Around line 90-95: The test assertions in contract_dns1123_test.go currently
only check for the field name and a generic substring (tc.wantInError); update
the test to also assert that the rendered error message includes the offending
value wrapped in quotes (e.g., the quoted form of the invalid value) so the
contract’s requirement for quoted offending-values is enforced; locate the
checks around the block that contains strings.Contains(err.Error(),
"clusterName") and the tc.wantInError assertion (also duplicate at the later
block around lines 126-131) and add an assertion that err.Error() contains the
quoted offending value (construct the quoted form from the test case’s offending
value variable) to harden the expectations.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2581cb5-dc65-4925-8c73-ec0a65dd1eed

📥 Commits

Reviewing files that changed from the base of the PR and between a3edda7 and 122194e.

📒 Files selected for processing (7)
  • charts/cozystack/templates/_helpers.tpl
  • charts/generic/templates/_helpers.tpl
  • charts/talm/templates/_helpers.tpl
  • pkg/commands/contract_init_validation_test.go
  • pkg/commands/init.go
  • pkg/engine/contract_cluster_test.go
  • pkg/engine/contract_dns1123_test.go

Comment thread charts/talm/templates/_helpers.tpl Outdated
IvanHunters
IvanHunters previously approved these changes May 9, 2026
Address review feedback from coderabbitai on
charts/talm/templates/_helpers.tpl:513: an unquoted numeric YAML
scalar (e.g. `clusterName: 123` in values.yaml) is parsed by Helm
as an int, and Go templates' `len` panics with 'len of type int'
when it hits the first length check — bypassing the fail()
message entirely.

Reproduced locally before the fix:

  template: cozystack/charts/talm/templates/_helpers.tpl:512:11:
  executing "talm.validate.dns1123subdomain" at <len $value>:
  error calling len: len of type int

Apply the suggested coercion: render .value through `printf "%v"`
into a guaranteed-string form before any length / regex / equality
check. Replace the `if not $value` falsy check (which would treat
numeric 0 as empty) with an explicit `eq $value ""` after
coercion.

Tests in pkg/engine/contract_dns1123_test.go:
- ClusterName_NumericYAMLScalarCoerced: `clusterName: 123` no
  longer crashes the template; the value coerces to "123" which
  happens to be a valid DNS-1123 subdomain so render succeeds.
  The pin is specifically against the `len of type int` panic.
- ClusterName_InvalidNumericFailsCleanly: `clusterName: -1`
  coerces to "-1" which is not a valid DNS-1123 subdomain;
  validator emits the regular field-name + value error instead
  of a template panic.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
@lexfrei Aleksei Sviridkin (lexfrei) merged commit 8542a59 into main May 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants