Ctrl K
ComunidadGuías…

adguardhome/dnsrewrite_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package adguardhome

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"reflect"
	"sync"
	"testing"

	"sigs.k8s.io/external-dns/endpoint"
	"sigs.k8s.io/external-dns/plan"
)

type rewriteTestAPI struct {
	t      *testing.T
	mu     sync.Mutex
	rules  []string
	writes int
}

func (a *rewriteTestAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.mu.Lock()
	defer a.mu.Unlock()

	w.Header().Set("Content-Type", "application/json")
	switch r.URL.Path {
	case "/control/status":
		if err := json.NewEncoder(w).Encode(struct{}{}); err != nil {
			a.t.Errorf("encode status: %v", err)
		}
	case "/control/filtering/status":
		if err := json.NewEncoder(w).Encode(filteringStatus{UserRules: a.rules}); err != nil {
			a.t.Errorf("encode filtering status: %v", err)
		}
	case "/control/filtering/set_rules":
		var body setRules
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		a.rules = body.Rules
		a.writes++
	default:
		http.NotFound(w, r)
	}
}

func (a *rewriteTestAPI) snapshot() ([]string, int) {
	a.mu.Lock()
	defer a.mu.Unlock()
	return append([]string(nil), a.rules...), a.writes
}

func setupRewriteTest(t *testing.T, rules []string, flag string) *rewriteTestAPI {
	t.Helper()
	a := &rewriteTestAPI{t: t, rules: append([]string(nil), rules...)}
	s := httptest.NewServer(a)
	t.Cleanup(s.Close)
	t.Setenv(envURL, s.URL)
	t.Setenv(envUser, "test-user")
	t.Setenv(envPassword, "test-password")
	t.Setenv(envManagedBy, "test")
	t.Setenv("ADGUARD_HOME_USE_DNSREWRITE", flag)
	return a
}

func newInitializedTestProvider(dryRun bool) (*AdguardHomeProvider, error) {
	p, err := NewAdguardHomeProvider(context.Background(), dryRun)
	if err != nil {
		return nil, err
	}
	if err = p.Initialize(context.Background()); err != nil {
		return nil, err
	}
	return p, nil
}

func TestDNSRewriteMigration(t *testing.T) {
	unmanaged := []string{
		"# User comment",
		"192.0.2.99 unmanaged.example.com",
		"192.0.2.98 other.example.com #$managed by external-dns;ref:other",
		"192.0.2.96 prefix-owner.example.com #$managed by external-dns;ref:test-other",
		"! 192.0.2.97 other-rewrite.example.com #$managed by external-dns;ref:other",
		"|other-rewrite.example.com|$dnsrewrite=NOERROR;A;192.0.2.97",
		`! $managed by external-dns;ref:other {"dnsName":"other-txt.example.com","targets":["$managed by external-dns;ref:test;labels=not-json"],"recordType":"TXT"}`,
		`|other-txt.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;\036managed\032by\032external-dns;ref:test;labels=not-json`,
	}
	legacy := append(append([]string(nil), unmanaged...),
		`192.0.2.10 app.example.com #$managed by external-dns;ref:test;labels={"owner":"test"}`,
		`192.0.2.11 app.example.com #$managed by external-dns;ref:test;labels={"owner":"test"}`,
		"# ownership txt.example.com $managed by external-dns;ref:test",
		"@@||app.example.com #$managed by external-dns;ref:test",
	)
	rewritten := append(append([]string(nil), unmanaged...),
		`! 192.0.2.10 app.example.com #$managed by external-dns;ref:test;labels={"owner":"test"}`,
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.10",
		`! 192.0.2.11 app.example.com #$managed by external-dns;ref:test;labels={"owner":"test"}`,
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.11",
		`! $managed by external-dns;ref:test {"dnsName":"txt.example.com","targets":["ownership"],"recordType":"TXT"}`,
		"|txt.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;ownership",
	)

	for _, tt := range []struct {
		name   string
		flag   string
		before []string
		after  []string
		writes int
	}{
		{name: "default remains legacy", before: legacy, after: legacy},
		{name: "explicit false remains legacy", flag: "false", before: legacy, after: legacy},
		{name: "migrate on startup without DNS changes", flag: "true", before: legacy, after: rewritten, writes: 1},
		{name: "already migrated", flag: "true", before: rewritten, after: rewritten},
		{name: "rollback", flag: "false", before: rewritten, after: legacy, writes: 1},
	} {
		t.Run(tt.name, func(t *testing.T) {
			a := setupRewriteTest(t, tt.before, tt.flag)
			p, err := newInitializedTestProvider(false)
			if err != nil {
				t.Fatal(err)
			}
			rules, writes := a.snapshot()
			if !reflect.DeepEqual(rules, tt.after) || writes != tt.writes {
				t.Fatalf("migration got rules %v, writes %d; want rules %v, writes %d", rules, writes, tt.after, tt.writes)
			}
			records, err := p.Records(context.Background())
			if err != nil {
				t.Fatal(err)
			}
			want := []*endpoint.Endpoint{
				{DNSName: "app.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"192.0.2.10", "192.0.2.11"}, Labels: endpoint.Labels{"owner": "test"}},
				{DNSName: "txt.example.com", RecordType: endpoint.RecordTypeTXT, Targets: endpoint.Targets{"ownership"}},
			}
			if !reflect.DeepEqual(records, want) {
				t.Fatalf("records got %+v, want %+v", records, want)
			}
			if err = p.ApplyChanges(context.Background(), &plan.Changes{}); err != nil {
				t.Fatal(err)
			}
			rules, _ = a.snapshot()
			if !reflect.DeepEqual(rules, tt.after) {
				t.Fatalf("reconciliation lost or changed rules: got %v, want %v", rules, tt.after)
			}
		})
	}
}

func TestDNSRewriteMixedSyntax(t *testing.T) {
	a := setupRewriteTest(t, []string{
		"192.0.2.10 app.example.com #$managed by external-dns;ref:test",
		"! 192.0.2.11 app.example.com #$managed by external-dns;ref:test",
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.11",
		"@@||app.example.com #$managed by external-dns;ref:test",
	}, "true")
	if _, err := newInitializedTestProvider(false); err != nil {
		t.Fatal(err)
	}
	want := []string{
		"! 192.0.2.10 app.example.com #$managed by external-dns;ref:test",
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.10",
		"! 192.0.2.11 app.example.com #$managed by external-dns;ref:test",
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.11",
	}
	if rules, writes := a.snapshot(); !reflect.DeepEqual(rules, want) || writes != 1 {
		t.Fatalf("mixed migration got %v, writes %d; want %v, writes 1", rules, writes, want)
	}
}

func TestDNSRewriteDefaultOwner(t *testing.T) {
	otherOwner := "192.0.2.99 other.example.com #$managed by external-dns;ref:other"
	a := setupRewriteTest(t, []string{
		otherOwner,
		"192.0.2.10 app.example.com #$managed by external-dns",
	}, "true")
	t.Setenv(envManagedBy, "")
	if _, err := newInitializedTestProvider(false); err != nil {
		t.Fatal(err)
	}
	want := []string{
		otherOwner,
		"! 192.0.2.10 app.example.com #$managed by external-dns",
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.10",
	}
	if rules, _ := a.snapshot(); !reflect.DeepEqual(rules, want) {
		t.Fatalf("default owner migration got %v, want %v", rules, want)
	}
}

func TestDNSRewriteApplyChanges(t *testing.T) {
	a := setupRewriteTest(t, nil, "true")
	p, err := newInitializedTestProvider(false)
	if err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()
	original := &endpoint.Endpoint{DNSName: "app.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"192.0.2.10", "192.0.2.11"}, Labels: endpoint.Labels{"owner": "test"}}
	if err = p.ApplyChanges(ctx, &plan.Changes{Create: []*endpoint.Endpoint{original}}); err != nil {
		t.Fatal(err)
	}
	updated := &endpoint.Endpoint{DNSName: "app.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"192.0.2.12"}, Labels: original.Labels}
	if err = p.ApplyChanges(ctx, &plan.Changes{UpdateOld: []*endpoint.Endpoint{original}, UpdateNew: []*endpoint.Endpoint{updated}}); err != nil {
		t.Fatal(err)
	}
	want := []string{
		`! 192.0.2.12 app.example.com #$managed by external-dns;ref:test;labels={"owner":"test"}`,
		"|app.example.com|$dnsrewrite=NOERROR;A;192.0.2.12",
	}
	if rules, _ := a.snapshot(); !reflect.DeepEqual(rules, want) {
		t.Fatalf("update got %v, want %v", rules, want)
	}
	if err = p.ApplyChanges(ctx, &plan.Changes{Delete: []*endpoint.Endpoint{updated}}); err != nil {
		t.Fatal(err)
	}
	if rules, _ := a.snapshot(); len(rules) != 0 {
		t.Fatalf("delete left rules %v", rules)
	}
}

func TestDNSRewriteInvalidFlag(t *testing.T) {
	a := setupRewriteTest(t, nil, "not-a-bool")
	if _, err := NewAdguardHomeProvider(context.Background(), false); err == nil {
		t.Fatal("expected invalid flag to fail startup")
	}
	if _, writes := a.snapshot(); writes != 0 {
		t.Fatal("invalid flag must not write rules")
	}
}

func TestDNSRewriteDryRun(t *testing.T) {
	original := []string{
		"192.0.2.10 app.example.com #$managed by external-dns;ref:test",
		"# ownership txt.example.com $managed by external-dns;ref:test",
	}
	a := setupRewriteTest(t, original, "true")
	p, err := newInitializedTestProvider(true)
	if err != nil {
		t.Fatal(err)
	}
	if err = p.ApplyChanges(context.Background(), &plan.Changes{}); err != nil {
		t.Fatal(err)
	}
	if rules, writes := a.snapshot(); !reflect.DeepEqual(rules, original) || writes != 0 {
		t.Fatalf("dry run changed rules: %v, writes %d", rules, writes)
	}
}

func TestDNSRewriteInvalidPair(t *testing.T) {
	for _, rules := range [][]string{
		{"! 192.0.2.10 app.example.com #$managed by external-dns;ref:test"},
		{"! 192.0.2.10 app.example.com #$managed by external-dns;ref:test", "|different.example.com|$dnsrewrite=NOERROR;A;192.0.2.10"},
		{`! $managed by external-dns;ref:test {"dnsName":"txt.example.com","targets":["token"],"recordType":"TXT"}`},
		{`! $managed by external-dns;ref:test {"dnsName":"txt.example.com","targets":["token"],"recordType":"TXT"}`, "|txt.example.com|$dnstype=TXT,dnsrewrite=NOERROR;TXT;different"},
		{`! $managed by external-dns;ref:test {"dnsName":"txt.example.com","targets":[],"recordType":"TXT"}`},
		{`! $managed by external-dns;ref:test {invalid-json}`},
	} {
		t.Run(rules[0], func(t *testing.T) {
			a := setupRewriteTest(t, rules, "true")
			p, err := newInitializedTestProvider(false)
			if err == nil {
				_, err = p.Records(context.Background())
			}
			if err == nil {
				t.Fatal("expected incomplete or mismatched metadata pair to fail")
			}
			if _, writes := a.snapshot(); writes != 0 {
				t.Fatal("invalid metadata pair must not overwrite rules")
			}
		})
	}
}