feat(tmpl): {{domain_name}} placeholder — materialize on diff/apply, parameterize on snapshot

Adds internal/tmpl with Materialize (template placeholder -> zone name) and
Parameterize (zone name -> placeholder, the inverse used by the
template-from-zone snapshot). service.resolve now materializes the template
against DomainRef.ZoneName before diffing, so one template can be reused
across domains. LoadDomainFull (source query + hand-edited sqlc output, since
sqlc is not installed) now also selects zone_name to populate it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
2026-07-05 13:41:18 +07:00
parent 135917216c
commit df895d8850
9 changed files with 158 additions and 11 deletions
+16 -3
View File
@@ -643,15 +643,17 @@ func TestZoneRecords_ReturnsProviderRecords(t *testing.T) {
// TestTemplateFromZone_SnapshotsManagedRecordsOnlyAndAttaches covers the
// snapshot-to-template flow: NS/SOA are read-only and must be excluded from
// the generated template, and the new template must be auto-attached to the
// domain (SetDomainTemplate) so check/apply become immediately available.
// the generated template, the new template must be auto-attached to the
// domain (SetDomainTemplate) so check/apply become immediately available,
// and the zone name must be parameterized to {{domain_name}} in names/values
// so the resulting template is reusable across domains (tmpl.Parameterize).
func TestTemplateFromZone_SnapshotsManagedRecordsOnlyAndAttaches(t *testing.T) {
a, ts := newTenantTestAPI()
domID := uuid.New()
ts.domains = []store.Domain{{ID: domID, ZoneName: "example.com", ZoneID: "z1"}}
a.Svc = &mockCheckApplier{zoneRecords: []model.Record{
{Type: model.A, Name: "a.example.com.", TTL: 300, Values: []string{"1.1.1.1"}},
{Type: model.TXT, Name: "a.example.com.", TTL: 300, Values: []string{"v=spf1 -all"}},
{Type: model.TXT, Name: "a.example.com.", TTL: 300, Values: []string{"v=spf1 a:mail.example.com -all"}},
{Type: model.NS, Name: "example.com.", TTL: 3600, Values: []string{"ns1.example.com."}},
{Type: model.SOA, Name: "example.com.", TTL: 3600, Values: []string{"ns1.example.com. admin.example.com. 1 2 3 4 5"}},
}}
@@ -674,6 +676,17 @@ func TestTemplateFromZone_SnapshotsManagedRecordsOnlyAndAttaches(t *testing.T) {
if r.Type == "NS" || r.Type == "SOA" {
t.Fatalf("read-only record type %s leaked into snapshot template", r.Type)
}
if strings.Contains(r.Name, "example.com") {
t.Fatalf("expected zone name parameterized to {{domain_name}} in record name, got %+v", r)
}
for _, v := range r.Values {
if strings.Contains(v, "example.com") {
t.Fatalf("expected zone name parameterized to {{domain_name}} in record value, got %+v", r)
}
}
}
if ts.createTemplate.Doc.Records[1].Values[0] != "v=spf1 a:mail.{{domain_name}} -all" {
t.Fatalf("expected SPF value parameterized, got %q", ts.createTemplate.Doc.Records[1].Values[0])
}
// SetDomainTemplate must have been called with the newly created template's id.
if ts.domains[0].TemplateID == nil || *ts.domains[0].TemplateID != ts.createTemplate.ID {