adguardhome/txt_rewrite_test.go
Rama main · réplica de lectura del repositorio original.
package adguardhome
import (
"context"
"reflect"
"strings"
"testing"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
func TestTXTRewriteValues(t *testing.T) {
for _, tt := range []struct {
name string
target string
value string
}{
{name: "plain", target: "verification-token", value: "verification-token"},
{name: "spaces", target: "v=spf1 include:example.com -all", value: `v=spf1\032include:example.com\032-all`},
{name: "quoted ownership", target: `"heritage=external-dns,external-dns/owner=test"`, value: `heritage=external-dns\044external-dns/owner=test`},
{name: "reserved characters", target: `a,b$important "quoted" C:\path`, value: `a\044b\036important\032\034quoted\034\032C:\092path`},
{name: "quoted escapes", target: `"a\"b\\c\044\036"`, value: `a\034b\092c\044\036`},
{name: "trailing space", target: "token ", value: `token\032`},
{name: "label-like content", target: "token;labels=not-json", value: "token;labels=not-json"},
{name: "management marker in content", target: "$managed by external-dns", value: `\036managed\032by\032external-dns`},
{name: "metadata-like content", target: "$managed by external-dns;labels=not-json", value: `\036managed\032by\032external-dns;labels=not-json`},
{name: "unicode", target: "é", value: `\195\169`},
{name: "decimal byte", target: `"\255"`, value: `\255`},
{name: "empty", target: "", value: ""},
{name: "quoted empty", target: `""`, value: ""},
{name: "maximum length", target: strings.Repeat("x", 255), value: strings.Repeat("x", 255)},
} {
t.Run(tt.name, func(t *testing.T) {
c := &mockAdguardClient{}
p := &AdguardHomeProvider{client: c, ruleFormat: rewriteRuleFormat()}
e := &endpoint.Endpoint{DNSName: "txt.example.com", RecordType: endpoint.RecordTypeTXT, Targets: endpoint.Targets{tt.target}, Labels: endpoint.Labels{"owner": "test"}}
if err := p.ApplyChanges(context.Background(), &plan.Changes{Create: []*endpoint.Endpoint{e}}); err != nil {
t.Fatal(err)
}
if len(c.rules) != 2 || c.rules[1] != "|txt.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;"+tt.value {
t.Fatalf("unexpected rules: %v", c.rules)
}
records, err := p.Records(context.Background())
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(records, []*endpoint.Endpoint{e}) {
t.Fatalf("TXT endpoint changed: got %+v, want %+v", records, e)
}
before := append([]string(nil), c.rules...)
if err = p.ApplyChanges(context.Background(), &plan.Changes{}); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(c.rules, before) {
t.Fatalf("reconciliation changed TXT rules: %v", c.rules)
}
p.ruleFormat = legacyRuleFormat()
if err = p.migrateRules(context.Background()); err != nil {
t.Fatal(err)
}
records, err = p.Records(context.Background())
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(records, []*endpoint.Endpoint{e}) {
t.Fatalf("rollback changed TXT endpoint: got %+v, want %+v", records, e)
}
})
}
}
func TestTXTRewriteValidation(t *testing.T) {
for _, target := range []string{
strings.Repeat("x", 256),
strings.Repeat("é", 128),
`"unterminated`,
`"a" "b"`,
`"bad\999"`,
`"bad\256"`,
`"bad\"`,
"injected\n||example.com^",
} {
t.Run(target, func(t *testing.T) {
c := &mockAdguardClient{rules: []string{"# existing rule"}}
p := &AdguardHomeProvider{client: c, ruleFormat: rewriteRuleFormat()}
err := p.ApplyChanges(context.Background(), &plan.Changes{Create: []*endpoint.Endpoint{
{DNSName: "txt.example.com", RecordType: endpoint.RecordTypeTXT, Targets: endpoint.Targets{target}},
}})
if err == nil {
t.Fatal("expected invalid TXT value to fail")
}
if !reflect.DeepEqual(c.rules, []string{"# existing rule"}) {
t.Fatalf("invalid value overwrote rules: %v", c.rules)
}
})
}
}
func TestTXTRewriteMigrationAndRollback(t *testing.T) {
legacy := []string{
"# keep this comment",
`# "heritage=external-dns,external-dns/owner=test" app.example.com $managed by external-dns;ref:test;labels={"owner":"test"}`,
"# v=spf1 include:example.com -all app.example.com $managed by external-dns;ref:test",
}
a := setupRewriteTest(t, legacy, "true")
p, err := newInitializedTestProvider(false)
if err != nil {
t.Fatal(err)
}
rewritten, writes := a.snapshot()
if writes != 1 || len(rewritten) != 5 || rewritten[2] != `|app.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;heritage=external-dns\044external-dns/owner=test` || rewritten[4] != `|app.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;v=spf1\032include:example.com\032-all` {
t.Fatalf("TXT-only startup migration got rules %v, writes %d", rewritten, writes)
}
records, err := p.Records(context.Background())
if err != nil {
t.Fatal(err)
}
want := []*endpoint.Endpoint{{DNSName: "app.example.com", RecordType: endpoint.RecordTypeTXT, Targets: endpoint.Targets{`"heritage=external-dns,external-dns/owner=test"`, "v=spf1 include:example.com -all"}, Labels: endpoint.Labels{"owner": "test"}}}
if !reflect.DeepEqual(records, want) {
t.Fatalf("migrated TXT records got %+v, want %+v", records, want)
}
t.Setenv(envUseDNSRewrite, "false")
if _, err = newInitializedTestProvider(false); err != nil {
t.Fatal(err)
}
if rules, writes := a.snapshot(); !reflect.DeepEqual(rules, legacy) || writes != 2 {
t.Fatalf("rollback got rules %v, writes %d; want %v, writes 2", rules, writes, legacy)
}
}
func TestTXTRewriteSharesHostnameWithA(t *testing.T) {
for _, enabled := range []bool{false, true} {
name := "legacy"
if enabled {
name = "rewrite"
}
t.Run(name, func(t *testing.T) {
c := &mockAdguardClient{}
format := legacyRuleFormat()
if enabled {
format = rewriteRuleFormat()
}
p := &AdguardHomeProvider{client: c, ruleFormat: format}
ctx := context.Background()
a := &endpoint.Endpoint{DNSName: "app.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"192.0.2.10"}, Labels: endpoint.Labels{"kind": "address"}}
txt := &endpoint.Endpoint{DNSName: "app.example.com", RecordType: endpoint.RecordTypeTXT, Targets: endpoint.Targets{"first", "second"}, Labels: endpoint.Labels{"kind": "text"}}
if err := p.ApplyChanges(ctx, &plan.Changes{Create: []*endpoint.Endpoint{a, txt}}); err != nil {
t.Fatal(err)
}
records, err := p.Records(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(records, []*endpoint.Endpoint{a, txt}) {
t.Fatalf("A and TXT records merged: %+v", records)
}
updated := &endpoint.Endpoint{DNSName: txt.DNSName, RecordType: txt.RecordType, Targets: endpoint.Targets{"updated"}, Labels: txt.Labels}
if err = p.ApplyChanges(ctx, &plan.Changes{UpdateOld: []*endpoint.Endpoint{txt}, UpdateNew: []*endpoint.Endpoint{updated}}); err != nil {
t.Fatal(err)
}
if err = p.ApplyChanges(ctx, &plan.Changes{Delete: []*endpoint.Endpoint{updated}}); err != nil {
t.Fatal(err)
}
records, err = p.Records(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(records, []*endpoint.Endpoint{a}) {
t.Fatalf("deleting TXT changed A record: %+v", records)
}
})
}
}