Skip to content

feat(config): migrate to Talos v1.12 multi-document config format#116

Merged
lexfrei merged 6 commits intomainfrom
feat/talos-v1.12-multidoc-config
Apr 14, 2026
Merged

feat(config): migrate to Talos v1.12 multi-document config format#116
lexfrei merged 6 commits intomainfrom
feat/talos-v1.12-multidoc-config

Conversation

@lexfrei
Copy link
Copy Markdown
Contributor

@lexfrei lexfrei commented Apr 7, 2026

Summary

Add support for the Talos v1.12 multi-document configuration format. When TalosVersion is set to v1.12 or later (via Chart.yaml or --talos-version CLI flag), templates generate separate YAML documents instead of the deprecated monolithic machine.network and machine.registries fields.

Closes #100

Changes

Engine

  • Pass TalosVersion through the Helm values map into the template rendering context (available as .TalosVersion), avoiding global mutable state

Chart templates (cozystack + generic)

  • Split talos.config into shared sub-templates to eliminate duplication: talos.config.machine.common, talos.config.cluster, talos.config.network.legacy, talos.config.network.multidoc
  • Version dispatcher selects legacy or multi-doc format based on semverCompare
  • New v1.12 document types generated: HostnameConfig, ResolverConfig, LinkConfig, BondConfig, VLANConfig, RegistryMirrorConfig, Layer2VIPConfig

Bug fix

  • Fix nr_hugepages rendering inside with block (was producing empty string for non-zero values due to .Values being inaccessible when dot is rebound)

Backward compatibility

  • Legacy format is fully preserved when TalosVersion is empty or < v1.12
  • All legacy regression tests pass unchanged

Tests

  • isTalosConfigPatch and extractExtraDocuments tests for all 7 new document types
  • Offline rendering tests for both charts x both roles x both formats
  • Version edge cases: pre-release, two-component version strings
  • Concurrent render test with race detector
  • nr_hugepages rendering test for both legacy and multi-doc paths

Summary by CodeRabbit

  • Documentation

    • Clarified Talos config output differences: Talos < v1.12 emits a single YAML document; Talos ≥ v1.12 emits a multi-document stream. Notes added for conditional Layer2 VIP and registry-mirror emission.
  • Refactor

    • Chart rendering now branches between legacy and multi-document Talos formats, centralizing shared machine/cluster content and adapting network output per Talos version.
  • Tests

    • Expanded rendering tests, added Talos-version validation and concurrent/template-context checks.

@lexfrei lexfrei marked this pull request as ready for review April 7, 2026 23:16
@lexfrei lexfrei self-assigned this Apr 7, 2026
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 support for the Talos v1.12+ multi-document configuration format in the cozystack and generic charts, while maintaining legacy support for older versions. The rendering engine is updated to pass the Talos version into the template context, and comprehensive integration tests are added to verify both output formats. Review feedback identifies several schema mismatches in the new multi-document resources, including incorrect field names for BondConfig and VLANConfig, and incorrect list structures for ResolverConfig, LinkConfig, and RegistryMirrorConfig. Additionally, a safety check is needed for the lookup function in BondConfig generation to prevent template rendering errors.

Comment on lines +140 to +148
nameservers:
{{- $resolvers := include "talm.discovered.default_resolvers" . }}
{{- if $resolvers }}
{{- range fromJsonArray $resolvers }}
- address: {{ . | quote }}
{{- end }}
{{- else }}
[]
{{- end }}
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.

high

In Talos v1.12+, the ResolverConfig resource expects nameservers to be a list of strings (IP addresses), not a list of objects with an address key.

nameservers:
{{- $resolvers := include "talm.discovered.default_resolvers" . }}
{{- if $resolvers }}
{{- range fromJsonArray $resolvers }}
  - {{ . | quote }}
{{- end }}
{{- else }}
  []
{{- end }}

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 current format is correct per the Go type definitions.

ResolverConfigV1Alpha1 has ResolverNameservers []NameserverConfig with yaml tag nameservers,omitempty, where NameserverConfig has Address Addr with yaml tag address.

Source: resolver.go:63 and resolver.go:81

The official test data (testdata/resolverconfig.yaml) confirms the format:

nameservers:
  - address: 10.0.0.1
  - address: 2001:4860:4860::8888

Comment on lines +160 to +196
{{- if $isBondInterface }}
{{- $link := lookup "links" "" $interfaceName }}
{{- $bondMaster := $link.spec.bondMaster }}
{{- $slaves := fromJsonArray (include "talm.discovered.bond_slaves" $link.spec.index) }}
---
apiVersion: v1alpha1
kind: BondConfig
name: {{ $interfaceName }}
links:
{{- range $slaves }}
- {{ . }}
{{- end }}
bondMode: {{ $bondMaster.mode }}
{{- if $bondMaster.xmitHashPolicy }}
xmitHashPolicy: {{ $bondMaster.xmitHashPolicy }}
{{- end }}
{{- if $bondMaster.lacpRate }}
lacpRate: {{ $bondMaster.lacpRate }}
{{- end }}
{{- if $bondMaster.miimon }}
miimon: {{ $bondMaster.miimon }}
{{- end }}
{{- if $bondMaster.updelay }}
updelay: {{ $bondMaster.updelay }}
{{- end }}
{{- if $bondMaster.downdelay }}
downdelay: {{ $bondMaster.downdelay }}
{{- end }}
{{- if not $isVlan }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
- address: {{ . }}
{{- end }}
routes:
- gateway: {{ include "talm.discovered.default_gateway" . }}
{{- end }}
{{- end }}
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.

high

There are several schema and safety issues in the BondConfig generation:

  1. The lookup function can return an empty map if the resource is not found. Accessing $link.spec without checking if it exists will cause a template rendering error.
  2. The field for bond mode is mode, not bondMode.
  3. The addresses field expects a list of strings.
  4. The routes field entries require a network field (e.g., 0.0.0.0/0).
{{- if $isBondInterface }}
{{- $link := lookup "links" "" $interfaceName }}
{{- if $link }}
{{- $bondMaster := $link.spec.bondMaster }}
{{- $slaves := fromJsonArray (include "talm.discovered.bond_slaves" $link.spec.index) }}
---
apiVersion: v1alpha1
kind: BondConfig
name: {{ $interfaceName }}
links:
{{- range $slaves }}
  - {{ . }}
{{- end }}
mode: {{ $bondMaster.mode }}
{{- if $bondMaster.xmitHashPolicy }}
xmitHashPolicy: {{ $bondMaster.xmitHashPolicy }}
{{- end }}
{{- if $bondMaster.lacpRate }}
lacpRate: {{ $bondMaster.lacpRate }}
{{- end }}
{{- if $bondMaster.miimon }}
miimon: {{ $bondMaster.miimon }}
{{- end }}
{{- if $bondMaster.updelay }}
updelay: {{ $bondMaster.updelay }}
{{- end }}
{{- if $bondMaster.downdelay }}
downdelay: {{ $bondMaster.downdelay }}
{{- end }}
{{- if not $isVlan }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
  - {{ . }}
{{- end }}
routes:
  - network: 0.0.0.0/0
    gateway: {{ include "talm.discovered.default_gateway" . }}
{{- end }}
{{- end }}
{{- end }}

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.

Schema claims are incorrect, but the lookup safety point is valid.

bondMode is correctBondConfigV1Alpha1 has BondMode *nethelpers.BondMode with yaml tag bondMode,omitempty (bond.go:96). Test data confirms: bondMode: 802.3ad.

addresses format is correctAddressConfig has AddressAddress netip.Prefix with yaml tag address (link.go:122). Format is - address: IP/prefix, not bare strings.

routes without destination is validRouteDestination has yaml tag destination,omitempty (NOT network). The field is optional: "If not specified, a default route will be created for the address family of the gateway." (link.go:133-135)

Lookup safety$isBondInterface calls talm.discovered.is_bond which already performs lookup "links" "" $interfaceName and checks $link.spec.kind == "bond". If it returned true, the resource is guaranteed to exist. Nevertheless, adding a guard is reasonable defensive coding. Will fix.

Comment on lines +199 to +209
apiVersion: v1alpha1
kind: VLANConfig
name: {{ $defaultLinkName }}
vlanID: {{ include "talm.discovered.vlan_id" $defaultLinkName }}
parent: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
- address: {{ . }}
{{- end }}
routes:
- gateway: {{ include "talm.discovered.default_gateway" . }}
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.

high

The VLANConfig resource has schema mismatches:

  1. The field name is vlanId, not vlanID.
  2. addresses should be a list of strings.
  3. routes entries require a network field.
kind: VLANConfig
name: {{ $defaultLinkName }}
vlanId: {{ include "talm.discovered.vlan_id" $defaultLinkName }}
parent: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
  - {{ . }}
{{- end }}
routes:
  - network: 0.0.0.0/0
    gateway: {{ include "talm.discovered.default_gateway" . }}

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.

All three claims are incorrect.

vlanID is correctVLANConfigV1Alpha1 has VLANIDConfig uint16 with yaml tag vlanID,omitempty (vlan.go:69). Test data confirms: vlanID: 2.

addresses format is correct — uses AddressConfig with address key, same as LinkConfig/BondConfig.

routes without destination is validRouteDestination is omitempty, not required.

Comment on lines +213 to +220
kind: LinkConfig
name: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
- address: {{ . }}
{{- end }}
routes:
- gateway: {{ include "talm.discovered.default_gateway" . }}
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.

high

The LinkConfig resource expects addresses to be a list of strings and routes to include a network field.

kind: LinkConfig
name: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
  - {{ . }}
{{- end }}
routes:
  - network: 0.0.0.0/0
    gateway: {{ include "talm.discovered.default_gateway" . }}

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.

Both claims are incorrect.

addresses uses AddressConfig with yaml tag address — format is - address: IP/prefix, not bare strings (link.go:98,122).

routesRouteDestination has yaml tag destination,omitempty. The destination field is optional, not required. There is no network field in RouteConfig. A route with only gateway creates a default route.

Comment on lines +225 to +227
kind: Layer2VIPConfig
name: {{ .Values.floatingIP | quote }}
link: {{ $interfaceName }}
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.

high

The Layer2VIPConfig resource uses the ip field for the VIP address, not the name field.

kind: Layer2VIPConfig
ip: {{ .Values.floatingIP | quote }}
link: {{ $interfaceName }}

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.

Incorrect. The VIP IP address is stored in the name field.

Layer2VIPConfigV1Alpha1 has MetaName string with yaml tag name (layer2_vip.go:65). The VIP() method parses this field as an IP address (layer2_vip.go:128-131). Validation at line 111 confirms MetaName must be a valid IP.

Test data confirms: name: 1.2.3.4. There is no ip field in this type.

Comment on lines +301 to +302
endpoints:
- url: https://mirror.gcr.io
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.

high

The RegistryMirrorConfig resource expects endpoints to be a list of strings (URLs), not a list of objects with a url key.

endpoints:
  - https://mirror.gcr.io

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.

Incorrect. Endpoints are objects, not strings.

RegistryMirrorConfigV1Alpha1 has RegistryEndpoints []RegistryEndpoint with yaml tag endpoints,omitempty. RegistryEndpoint has EndpointURL meta.URL with yaml tag url AND EndpointOverridePath *bool with yaml tag overridePath,omitempty (registry_mirror.go:69,87,90).

Endpoints must be objects because they support overridePath. Test data confirms:

endpoints:
  - url: https://my-private-registry.local:5000
  - url: http://my-harbor/v2/registry-k8s.io/
    overridePath: true

Comment on lines +63 to +71
nameservers:
{{- $resolvers := include "talm.discovered.default_resolvers" . }}
{{- if $resolvers }}
{{- range fromJsonArray $resolvers }}
- address: {{ . | quote }}
{{- end }}
{{- else }}
[]
{{- end }}
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.

high

The ResolverConfig resource expects nameservers to be a list of strings.

nameservers:
{{- $resolvers := include "talm.discovered.default_resolvers" . }}
{{- if $resolvers }}
{{- range fromJsonArray $resolvers }}
  - {{ . | quote }}
{{- end }}
{{- else }}
  []
{{- end }}

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 as above — the format is correct. NameserverConfig has Address Addr with yaml tag address. See resolver.go:63,81.

Comment on lines +83 to +119
{{- if $isBondInterface }}
{{- $link := lookup "links" "" $interfaceName }}
{{- $bondMaster := $link.spec.bondMaster }}
{{- $slaves := fromJsonArray (include "talm.discovered.bond_slaves" $link.spec.index) }}
---
apiVersion: v1alpha1
kind: BondConfig
name: {{ $interfaceName }}
links:
{{- range $slaves }}
- {{ . }}
{{- end }}
bondMode: {{ $bondMaster.mode }}
{{- if $bondMaster.xmitHashPolicy }}
xmitHashPolicy: {{ $bondMaster.xmitHashPolicy }}
{{- end }}
{{- if $bondMaster.lacpRate }}
lacpRate: {{ $bondMaster.lacpRate }}
{{- end }}
{{- if $bondMaster.miimon }}
miimon: {{ $bondMaster.miimon }}
{{- end }}
{{- if $bondMaster.updelay }}
updelay: {{ $bondMaster.updelay }}
{{- end }}
{{- if $bondMaster.downdelay }}
downdelay: {{ $bondMaster.downdelay }}
{{- end }}
{{- if not $isVlan }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
- address: {{ . }}
{{- end }}
routes:
- gateway: {{ include "talm.discovered.default_gateway" . }}
{{- end }}
{{- end }}
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.

high

The BondConfig generation has several schema and safety issues: missing lookup result check, incorrect mode field name, and incorrect addresses/routes structure.

{{- if $isBondInterface }}
{{- $link := lookup "links" "" $interfaceName }}
{{- if $link }}
{{- $bondMaster := $link.spec.bondMaster }}
{{- $slaves := fromJsonArray (include "talm.discovered.bond_slaves" $link.spec.index) }}
---
apiVersion: v1alpha1
kind: BondConfig
name: {{ $interfaceName }}
links:
{{- range $slaves }}
  - {{ . }}
{{- end }}
mode: {{ $bondMaster.mode }}
{{- if $bondMaster.xmitHashPolicy }}
xmitHashPolicy: {{ $bondMaster.xmitHashPolicy }}
{{- end }}
{{- if $bondMaster.lacpRate }}
lacpRate: {{ $bondMaster.lacpRate }}
{{- end }}
{{- if $bondMaster.miimon }}
miimon: {{ $bondMaster.miimon }}
{{- end }}
{{- if $bondMaster.updelay }}
updelay: {{ $bondMaster.updelay }}
{{- end }}
{{- if $bondMaster.downdelay }}
downdelay: {{ $bondMaster.downdelay }}
{{- end }}
{{- if not $isVlan }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
  - {{ . }}
{{- end }}
routes:
  - network: 0.0.0.0/0
    gateway: {{ include "talm.discovered.default_gateway" . }}
{{- end }}
{{- end }}
{{- end }}

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.

Schema claims are incorrect (see replies on the cozystack counterpart). The lookup safety point is valid though — adding a guard. Fixed.

Comment on lines +122 to +132
apiVersion: v1alpha1
kind: VLANConfig
name: {{ $defaultLinkName }}
vlanID: {{ include "talm.discovered.vlan_id" $defaultLinkName }}
parent: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
- address: {{ . }}
{{- end }}
routes:
- gateway: {{ include "talm.discovered.default_gateway" . }}
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.

high

The VLANConfig resource expects vlanId (not vlanID), a list of strings for addresses, and a network field in routes.

kind: VLANConfig
name: {{ $defaultLinkName }}
vlanId: {{ include "talm.discovered.vlan_id" $defaultLinkName }}
parent: {{ $interfaceName }}
addresses:
{{- range fromJsonArray (include "talm.discovered.default_addresses_by_gateway" .) }}
  - {{ . }}
{{- end }}
routes:
  - network: 0.0.0.0/0
    gateway: {{ include "talm.discovered.default_gateway" . }}

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.

All claims incorrect — see replies on the cozystack counterpart. vlanID is correct yaml tag, addresses uses AddressConfig objects, routes.destination is optional.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Templates and engine rendering were updated to support Talos v1.12+ multi-document config output, with version-gated template branches (multi-doc vs legacy), injected TalosVersion into template scope, engine-level TalosVersion validation, and extensive tests and README notes documenting format differences. Documentation only changed in README.md.

Changes

Cohort / File(s) Summary
Docs
README.md
Add note describing Talos config formatting differences by Talos version and conditional emission rules for multi-doc kinds (HostnameConfig, ResolverConfig, Link/Bond/VLAN, Layer2VIPConfig, RegistryMirrorConfig).
Chart: cozystack templates
charts/cozystack/templates/_helpers.tpl
Introduce version-gated talos.config entrypoint; extract machine.common, cluster, and split network rendering into network.multidoc and network.legacy; add multi-doc RegistryMirrorConfig; adjust hugepage rendering and VLAN/bond/vip logic.
Chart: generic templates
charts/generic/templates/_helpers.tpl
Same refactor as cozystack: add talos.config.machine.common, talos.config.cluster, talos.config.network.multidoc, talos.config.network.legacy, plus talos.config.multidoc/talos.config.legacy and conditional dispatch in talos.config.
Engine core
pkg/engine/engine.go
Validate opts.TalosVersion early (parse/fail on invalid), add TalosVersion into Helm root values, and simplify callsite to applyPatchesAndRenderConfig (signature change).
Helm integration
pkg/engine/helm/engine.go
Inject TalosVersion into per-template scope (next values) so {{ .TalosVersion }} is available to chart/dependency templates during recursive rendering.
Tests: engine/unit
pkg/engine/engine_test.go
Extend tests: assert isTalosConfigPatch excludes v1alpha1 multi-doc kinds; add multi-doc extra-document counting tests.
Tests: helm/unit
pkg/engine/helm/engine_test.go
Add tests ensuring .TalosVersion appears in template context, empty/absent cases, and concurrent render correctness; refactor repeat assertions.
Tests: rendering harness
pkg/engine/render_test.go
Add offline Helm render harness, many new legacy and multi-doc rendering tests for both charts (including topology lookup helpers), nr_hugepages checks, Talos version parsing error test, and assertion helpers.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI as User/CLI
participant Engine as CodeRabbit Engine
participant Helm as Helm Engine
participant Templates as Chart Templates
participant Output as Generated Talos Config
CLI->>Engine: render request with opts (including TalosVersion)
Engine->>Engine: parse/validate TalosVersion
Engine->>Helm: render chart (+ rootValues.TalosVersion)
Helm->>Templates: render templates (per-scope TalosVersion available)
alt TalosVersion >= 1.12
Templates->>Output: emit multi-document YAML (HostnameConfig, ResolverConfig, Link/Bond/VLAN, optional Layer2VIPConfig, RegistryMirrorConfig)
else TalosVersion < 1.12
Templates->>Output: emit legacy single-document machine.network/machine.registries YAML
end
Helm-->>Engine: rendered manifests
Engine-->>CLI: final rendered config/output

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

"I hopped through templates, careful and spry,
splitting docs like clouds in the sky.
Talos grows up, I nibble a test—
multi-doc carrots taste simply the best! 🥕🐇"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating Talos configuration to v1.12 multi-document format, which is the central objective across all modified files.
Linked Issues check ✅ Passed All coding objectives from issue #100 are met: multi-document config generation, version-based dispatching, new document types (HostnameConfig, LinkConfig, BondConfig, VLANConfig, RegistryMirrorConfig, Layer2VIPConfig), TalosVersion propagation through templates, backward compatibility, comprehensive test coverage, and documentation updates.
Out of Scope Changes check ✅ Passed All changes directly support the v1.12 migration objective: template refactoring for multi-doc formats, engine enhancements to pass TalosVersion context, comprehensive test additions, and README clarification. No unrelated modifications detected.

✏️ 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/talos-v1.12-multidoc-config

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

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

🧹 Nitpick comments (1)
pkg/engine/render_test.go (1)

298-323: Consider restoring LookupFunc after modification for test isolation.

This test and TestMultiDocCozystack_NrHugepages modify the package-level helmEngine.LookupFunc without restoring it. While safe for sequential tests, consider adding cleanup for future-proofing if tests are parallelized:

🔧 Suggested cleanup pattern
 func TestLegacyCozystack_NrHugepages(t *testing.T) {
 	// Test nr_hugepages is rendered correctly in legacy format
+	origLookup := helmEngine.LookupFunc
+	defer func() { helmEngine.LookupFunc = origLookup }()
+
 	helmEngine.LookupFunc = func(string, string, string) (map[string]any, error) {
 		return map[string]any{}, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/engine/render_test.go` around lines 298 - 323, This test (and
TestMultiDocCozystack_NrHugepages) mutates the package-level
helmEngine.LookupFunc without restoring it; save the original (e.g., orig :=
helmEngine.LookupFunc) before assigning the stub and defer restoring it (defer
func() { helmEngine.LookupFunc = orig }()) so the package state is reset after
TestLegacyCozystack_NrHugepages and the other test, ensuring isolation when
tests run in parallel.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/engine/render_test.go`:
- Around line 298-323: This test (and TestMultiDocCozystack_NrHugepages) mutates
the package-level helmEngine.LookupFunc without restoring it; save the original
(e.g., orig := helmEngine.LookupFunc) before assigning the stub and defer
restoring it (defer func() { helmEngine.LookupFunc = orig }()) so the package
state is reset after TestLegacyCozystack_NrHugepages and the other test,
ensuring isolation when tests run in parallel.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c75fd86c-523c-49ff-893e-105b7bf84efb

📥 Commits

Reviewing files that changed from the base of the PR and between 3fd6504 and 52eb508.

📒 Files selected for processing (8)
  • README.md
  • charts/cozystack/templates/_helpers.tpl
  • charts/generic/templates/_helpers.tpl
  • pkg/engine/engine.go
  • pkg/engine/engine_test.go
  • pkg/engine/helm/engine.go
  • pkg/engine/helm/engine_test.go
  • pkg/engine/render_test.go
✅ Files skipped from review due to trivial changes (3)
  • README.md
  • pkg/engine/engine_test.go
  • charts/cozystack/templates/_helpers.tpl

myasnikovdaniil

This comment was marked as duplicate.

Copy link
Copy Markdown
Contributor

@myasnikovdaniil myasnikovdaniil left a comment

Choose a reason for hiding this comment

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

Bug

Malformed TalosVersion causes opaque template crash

A non-semver TalosVersion (e.g. latest, foobar) passes the and .TalosVersion truthiness check but semverCompare returns an error, producing:

error calling semverCompare: invalid semantic version

The clean validation via ParseContractFromVersion in engine.go runs after template rendering, so it never gets a chance to produce a user-friendly error. Custom templates that don't use semverCompare reach the later validation and get a better message: invalid talos-version: error parsing version "vlatest".

Suggestion: Validate TalosVersion before eng.Render() — e.g. call semver.NewVersion(opts.TalosVersion) early in Render() and return a clean error.

Test coverage gaps

BondConfig and VLANConfig paths are never exercised

The offline LookupFunc always returns empty maps, so is_bond/is_vlan always evaluate to false. No test ever emits a BondConfig or VLANConfig document. The hasLinkConfig || hasBondConfig assertion always passes via LinkConfig. Consider adding tests with a custom LookupFunc returning mock bond/vlan resources.

Layer2VIPConfig is never positively asserted

The cozystack defaults include floatingIP: 192.168.100.10, so Layer2VIPConfig is present in the controlplane multi-doc output — but no test checks for it. All four Layer2VIPConfig references are assertNotContains. Adding assertContains(t, output, "kind: Layer2VIPConfig") to TestMultiDocCozystack_ControlPlane would close this gap.

Nits

README note could be more precise — lists all 7 document types as always-present, but BondConfig/VLANConfig/Layer2VIPConfig are conditional on topology, and RegistryMirrorConfig is cozystack-only. A qualifier like "depending on network topology and chart preset" would help.

Comment on talos.config.machine.common (cozystack) omits nodeLabels — says "type, kubelet, sysctls, kernel, certSANs, files, install" but the template also renders nodeLabels for controlplane nodes.

lexfrei added 4 commits April 14, 2026 16:53
Add support for the Talos v1.12 multi-document configuration format. When
TalosVersion is set to v1.12 or later (via Chart.yaml or --talos-version CLI
flag), templates generate separate YAML documents instead of the deprecated
monolithic machine.network and machine.registries fields.

Closes #100

Engine:
- Pass TalosVersion through the Helm values map into the template rendering
  context (available as .TalosVersion), avoiding global mutable state
- Remove unused ctx and chrt parameters from applyPatchesAndRenderConfig

Chart templates (cozystack + generic):
- Split talos.config into shared sub-templates to eliminate duplication:
  talos.config.machine.common, talos.config.cluster,
  talos.config.network.legacy, talos.config.network.multidoc
- Version dispatcher selects legacy or multi-doc format based on semverCompare
- New v1.12 document types generated: HostnameConfig, ResolverConfig,
  LinkConfig, BondConfig, VLANConfig, RegistryMirrorConfig, Layer2VIPConfig

Bug fix:
- Fix nr_hugepages rendering inside with block (was producing empty string
  for non-zero values due to .Values being inaccessible when dot is rebound)

Backward compatibility:
- Legacy format is fully preserved when TalosVersion is empty or < v1.12
- All legacy regression tests pass unchanged

Tests:
- isTalosConfigPatch and extractExtraDocuments tests for all 7 new document
  types
- Offline rendering tests for both charts x both roles x both formats
- Version edge cases: pre-release, two-component version strings
- Concurrent render test with race detector
- nr_hugepages rendering test for both legacy and multi-doc paths
- Regression test from #119 for offline lookup producing empty interface

Code style:
- Replace interface{} with any across engine package
- Use maps.Copy instead of range+assign loops
- Use range over int for simple counting loops
- Use strings.Repeat instead of += concatenation

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Call ParseContractFromVersion early in Render() so malformed values
(e.g. "latest", "foobar") surface a user-friendly "invalid
talos-version" error instead of the opaque "semverCompare: invalid
semantic version" that otherwise escapes from deep inside the Helm
engine template evaluation.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…onfig

- TestRenderInvalidTalosVersion: exercises the new early validation.
- TestMultiDocCozystack_BondTopology / TestMultiDocGeneric_BondTopology:
  assert BondConfig emission with mock links/routes/addresses lookups.
- TestMultiDocCozystack_VlanOnBondTopology /
  TestMultiDocGeneric_VlanOnBondTopology: assert BondConfig + VLANConfig
  combined emission when the default route goes through a VLAN.
- TestMultiDocCozystack_ControlPlane: add positive assert for
  Layer2VIPConfig since cozystack defaults set floatingIP.
- TestLegacy/MultiDocCozystack_NrHugepages: restore the package-level
  helmEngine.LookupFunc via t.Cleanup for test isolation.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
- README: clarify that not all multi-doc types are always present —
  HostnameConfig/ResolverConfig and one network interface document are
  always emitted, Layer2VIPConfig depends on floatingIP on controlplane,
  and RegistryMirrorConfig is cozystack-only.
- charts/cozystack _helpers.tpl: the talos.config.machine.common helper
  also renders nodeLabels for controlplane nodes — update the comment.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@lexfrei lexfrei force-pushed the feat/talos-v1.12-multidoc-config branch from 52eb508 to 7f9957b Compare April 14, 2026 14:02
@lexfrei
Copy link
Copy Markdown
Contributor Author

lexfrei commented Apr 14, 2026

Addressed review feedback. Force-pushed on rebase onto main.

Changes since last review

🐛 Bug — invalid TalosVersion crash
Render() now calls config.ParseContractFromVersion(opts.TalosVersion) before template evaluation. Values like latest or foobar now fail with invalid talos-version: error parsing version "vlatest" instead of the opaque semverCompare: invalid semantic version.

📊 Coverage — BondConfig / VLANConfig
Added four new tests with mock LookupFunc:

  • TestMultiDoc{Cozystack,Generic}_BondTopology — asserts BondConfig emission with slaves, bondMode, xmit hash/LACP, addresses, default route.
  • TestMultiDoc{Cozystack,Generic}_VlanOnBondTopology — asserts combined BondConfig + VLANConfig emission with vlanID, parent, addresses.

📊 Coverage — Layer2VIPConfig positive assert
TestMultiDocCozystack_ControlPlane now contains assertContains(output, "kind: Layer2VIPConfig") and assertContains(output, "192.168.100.10").

🧹 Test isolation
TestLegacyCozystack_NrHugepages and TestMultiDocCozystack_NrHugepages now restore helmEngine.LookupFunc via t.Cleanup.

📝 README
Clarified that only HostnameConfig, ResolverConfig, and one network interface document are always emitted; Layer2VIPConfig is controlplane-only when floatingIP is set; RegistryMirrorConfig is cozystack-only.

📝 Comment fix
talos.config.machine.common (cozystack) comment updated to include nodeLabels.

🔒 Lookup safety (already in place)
{{- if $link }} guard after lookup "links" "" $interfaceName in both _helpers.tpl — confirmed present on current HEAD.

Gemini feedback

All schema claims from the bot-review (bare-string addresses, mode/vlanId/ip/url-list/string-nameserver) have been verified against the siderolabs/talos Go types and test data — the existing YAML shape is correct. References are in the inline replies on the original comments.

Tests: go test -race ./... green, go vet clean, golangci-lint run — 0 issues.

@lexfrei lexfrei requested a review from myasnikovdaniil April 14, 2026 14:05
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: 3

🧹 Nitpick comments (1)
pkg/engine/render_test.go (1)

36-39: Restore the global lookup hook after each helper call.

renderChartTemplate overwrites helmEngine.LookupFunc and leaves the package-global in that state. That makes later tests order-dependent and blocks safe parallelization of this suite.

♻️ Suggested cleanup
 func renderChartTemplate(t *testing.T, chartPath string, templateFile string, talosVersion ...string) string {
 	t.Helper()
 
+	origLookup := helmEngine.LookupFunc
+	t.Cleanup(func() { helmEngine.LookupFunc = origLookup })
+
 	// Reset to offline mode
 	helmEngine.LookupFunc = func(string, string, string) (map[string]any, error) {
 		return map[string]any{}, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/engine/render_test.go` around lines 36 - 39, renderChartTemplate
currently overwrites the package-global helmEngine.LookupFunc and leaves it
modified, making tests order-dependent; fix this by saving the original value of
helmEngine.LookupFunc at the start of renderChartTemplate, then restore it
(preferably via defer) after the helper finishes so the global is returned to
its previous state; update renderChartTemplate to perform the assignment to
helmEngine.LookupFunc for the offline stub only for the duration of the call and
restore the saved original LookupFunc before returning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@charts/cozystack/templates/_helpers.tpl`:
- Around line 127-131: The multi-doc helper "talos.config.network.multidoc"
currently reconstructs network config for v1.12+ and ignores
talm.discovered.existing_interfaces_configuration; update the helper to preserve
that value by emitting it as a named document (e.g.,
"existing_interfaces_configuration") alongside the LinkConfig/BondConfig docs
when talm.discovered.existing_interfaces_configuration is present, or
alternatively add an explicit fast-fail validation at the top of
talos.config.network.multidoc that returns an error if
talm.discovered.existing_interfaces_configuration is set (to avoid silent data
loss when TalosVersion becomes v1.12+). Ensure the change references the helper
"talos.config.network.multidoc" and the variable
talm.discovered.existing_interfaces_configuration so callers get the preserved
data or a clear failure.
- Around line 224-230: The Layer2VIPConfig currently sets link to $interfaceName
which has been rewritten to the parent link in the VLAN path and moves the VIP
off the VLAN; update the template so Layer2VIPConfig.link uses the VLAN-emitted
interface variable $defaultLinkName instead of $interfaceName (keep the
surrounding condition using .Values.floatingIP and .MachineType and the resource
kind Layer2VIPConfig intact) so the VIP remains attached to the configured VLAN.

In `@charts/generic/templates/_helpers.tpl`:
- Around line 147-153: The Layer2VIPConfig block is attaching the VIP to
$interfaceName which has been rewritten to the parent link; replace the link
reference so the VIP stays on the VLAN by using $defaultLinkName instead of
$interfaceName in the link field of the Layer2VIPConfig (the conditional stays
the same: check .Values.floatingIP and .MachineType), ensuring the VIP is
emitted on the VLAN interface; update the template where Layer2VIPConfig's link:
is set to reference $defaultLinkName.

---

Nitpick comments:
In `@pkg/engine/render_test.go`:
- Around line 36-39: renderChartTemplate currently overwrites the package-global
helmEngine.LookupFunc and leaves it modified, making tests order-dependent; fix
this by saving the original value of helmEngine.LookupFunc at the start of
renderChartTemplate, then restore it (preferably via defer) after the helper
finishes so the global is returned to its previous state; update
renderChartTemplate to perform the assignment to helmEngine.LookupFunc for the
offline stub only for the duration of the call and restore the saved original
LookupFunc before returning.
🪄 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: c1b72b25-39ca-4df0-bed9-306a4d0524dc

📥 Commits

Reviewing files that changed from the base of the PR and between 52eb508 and 7f9957b.

📒 Files selected for processing (8)
  • README.md
  • charts/cozystack/templates/_helpers.tpl
  • charts/generic/templates/_helpers.tpl
  • pkg/engine/engine.go
  • pkg/engine/engine_test.go
  • pkg/engine/helm/engine.go
  • pkg/engine/helm/engine_test.go
  • pkg/engine/render_test.go
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/engine/helm/engine.go
  • pkg/engine/engine.go
  • pkg/engine/engine_test.go

Comment on lines +127 to +131
{{- define "talos.config.network.multidoc" }}
{{- /* Multi-doc format always reconstructs network config from discovery resources.
existing_interfaces_configuration is not used here because v1.12 nodes store
network config in separate documents (LinkConfig, BondConfig, etc.), not in
the legacy machine.network.interfaces field. */ -}}
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 14, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve existing_interfaces_configuration in the multi-doc branch.

The legacy path consumes talm.discovered.existing_interfaces_configuration, but the v1.12 path ignores it and always rebuilds from the default-route interface. That silently drops any non-default interface state as soon as TalosVersion flips to v1.12+. Please translate that helper output into named docs here, or fail fast when it is present.

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

In `@charts/cozystack/templates/_helpers.tpl` around lines 127 - 131, The
multi-doc helper "talos.config.network.multidoc" currently reconstructs network
config for v1.12+ and ignores talm.discovered.existing_interfaces_configuration;
update the helper to preserve that value by emitting it as a named document
(e.g., "existing_interfaces_configuration") alongside the LinkConfig/BondConfig
docs when talm.discovered.existing_interfaces_configuration is present, or
alternatively add an explicit fast-fail validation at the top of
talos.config.network.multidoc that returns an error if
talm.discovered.existing_interfaces_configuration is set (to avoid silent data
loss when TalosVersion becomes v1.12+). Ensure the change references the helper
"talos.config.network.multidoc" and the variable
talm.discovered.existing_interfaces_configuration so callers get the preserved
data or a clear failure.

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.

Intentional. talm.discovered.existing_interfaces_configuration emits a legacy machine.network.interfaces YAML fragment, which has no safe 1:1 translation to the v1.12 multi-doc resources (LinkConfig / BondConfig / VLANConfig / Layer2VIPConfig). Rather than silently producing a partial or incorrect translation, the multidoc path rebuilds the required documents from the discovery resources directly; the comment at the top of talos.config.network.multidoc documents this. Users who rely on custom legacy interface state should keep machine.talosVersion on v1.11.x until they migrate their overrides to the new resources.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@lexfrei, understood — that's a sound design rationale. Silently emitting a partial or incorrectly translated fragment would be far more dangerous than a clean rebuild from discovery resources, and the existing comment in the template documents the deliberate omission clearly. I'll note this for future reviews.

(ʕ •ᴥ•ʔ)≡


✏️ Learnings added
Learnt from: lexfrei
Repo: cozystack/talm PR: 116
File: charts/cozystack/templates/_helpers.tpl:127-131
Timestamp: 2026-04-14T15:31:55.570Z
Learning: In `charts/cozystack/templates/_helpers.tpl` and `charts/generic/templates/_helpers.tpl`, the `talos.config.network.multidoc` template intentionally ignores `talm.discovered.existing_interfaces_configuration`. That helper emits a legacy `machine.network.interfaces` YAML fragment that has no safe 1:1 translation to Talos v1.12 multi-doc resources (LinkConfig / BondConfig / VLANConfig / Layer2VIPConfig). The multidoc path rebuilds all required network documents directly from discovery resources instead. Users relying on custom legacy interface state must stay on Talos v1.11.x until they migrate their overrides to the new v1.12 resource types.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: lexfrei
Repo: cozystack/talm PR: 113
File: pkg/commands/apply.go:116-136
Timestamp: 2026-04-02T08:59:42.636Z
Learning: In the talm project (pkg/commands/apply.go and pkg/commands/template.go), when a node file (e.g. nodes/node0.yaml) is passed via `-f`, it serves only as a modeline container providing metadata (nodes, endpoints, template paths). The actual cluster configuration is produced entirely by Helm templates + values.yaml. The configFile is intentionally NOT passed as a patch to engine.Render. This matches templateWithFiles behavior in template.go and is equivalent to the two-step `talm template -f node.yaml | talm apply -f -` pattern.

lexfrei added 2 commits April 14, 2026 18:27
In the VLAN branch of talos.config.network.multidoc, $interfaceName is
rewritten to the parent link, while addresses/routes are emitted on the
VLAN link ($defaultLinkName). Using $interfaceName for Layer2VIPConfig.link
moved the VIP off the configured VLAN. Select $defaultLinkName when the
default link is a VLAN so the VIP stays attached to the VLAN interface,
matching the legacy path behavior.

Address review feedback from coderabbitai on charts/cozystack/templates/_helpers.tpl:229.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
… path

Mirror of the cozystack fix: when the default link is a VLAN,
$interfaceName is rewritten to the parent link while the VLAN
addresses/routes are emitted on $defaultLinkName. Use $defaultLinkName
for Layer2VIPConfig.link in that branch so the VIP stays on the VLAN.

Address review feedback from coderabbitai on charts/generic/templates/_helpers.tpl:152.

Assisted-By: Claude <noreply@anthropic.com>
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@charts/cozystack/templates/_helpers.tpl`:
- Around line 140-148: The template currently treats a non-empty JSON string
"[]" as truthy and ranges over fromJsonArray $resolvers which yields nothing,
leaving an empty "nameservers:" key; change the logic to parse the JSON into an
array first and check its length before rendering: call fromJsonArray once into
a variable (e.g. $arr := fromJsonArray $resolvers), then if gt (len $arr) 0
range $arr to render each "- address: {{ . | quote }}", else render an explicit
empty YAML list "  []". Ensure you reference the include
"talm.discovered.default_resolvers", the $resolvers variable, and the
fromJsonArray result variable (e.g. $arr) in the updated condition.
🪄 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: 40f39f20-84f4-4bcb-b68f-aca27e849ffb

📥 Commits

Reviewing files that changed from the base of the PR and between 7f9957b and 463bfb7.

📒 Files selected for processing (2)
  • charts/cozystack/templates/_helpers.tpl
  • charts/generic/templates/_helpers.tpl
🚧 Files skipped from review as they are similar to previous changes (1)
  • charts/generic/templates/_helpers.tpl

Comment on lines +140 to +148
nameservers:
{{- $resolvers := include "talm.discovered.default_resolvers" . }}
{{- if $resolvers }}
{{- range fromJsonArray $resolvers }}
- address: {{ . | quote }}
{{- end }}
{{- else }}
[]
{{- end }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor edge case: empty nameservers output.

If talm.discovered.default_resolvers returns an empty JSON array [], the condition if $resolvers is true (non-empty string), but range fromJsonArray $resolvers produces nothing, resulting in:

nameservers:

This is consistent with the generic template and likely acceptable, but verify Talos tolerates an empty nameservers: key without a value.

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

In `@charts/cozystack/templates/_helpers.tpl` around lines 140 - 148, The template
currently treats a non-empty JSON string "[]" as truthy and ranges over
fromJsonArray $resolvers which yields nothing, leaving an empty "nameservers:"
key; change the logic to parse the JSON into an array first and check its length
before rendering: call fromJsonArray once into a variable (e.g. $arr :=
fromJsonArray $resolvers), then if gt (len $arr) 0 range $arr to render each "-
address: {{ . | quote }}", else render an explicit empty YAML list "  []".
Ensure you reference the include "talm.discovered.default_resolvers", the
$resolvers variable, and the fromJsonArray result variable (e.g. $arr) in the
updated condition.

Copy link
Copy Markdown
Contributor

@myasnikovdaniil myasnikovdaniil left a comment

Choose a reason for hiding this comment

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

Re-reviewed. All items from the first review are addressed:

  • Malformed TalosVersion crash → fixed (early validation in Render())
  • BondConfig/VLANConfig test coverage → fixed (bondTopologyLookup + vlanOnBondTopologyLookup)
  • Layer2VIPConfig positive assertion → fixed
  • README note precision → fixed
  • machine.common comment → fixed
  • Layer2VIP attached to wrong link in VLAN path → fixed ($vipLinkName)

Tests pass clean, including with -race. Schema matches actual Talos Go types. LGTM.

@lexfrei lexfrei merged commit 00358fd into main Apr 14, 2026
4 checks passed
@lexfrei lexfrei deleted the feat/talos-v1.12-multidoc-config branch April 14, 2026 19:40
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.

feat: migrate to Talos v1.12 multi-document config format

3 participants