Architecture: - Extract reconcileNetworking into internal/reconcile package with 7 consumer-side interfaces - Add locked modifyState helper to fix state.yaml read-modify-write race condition - Extract CrowdSec and VPN handler groups with interfaces documenting dependency surface - Replace raw map[string]any YAML manipulation with typed AddDHCPStaticLease/RemoveDHCPStaticLease - Extract Cloudflare API functions into cfClient struct, eliminating repeated auth boilerplate Complexity reduction: - haproxy.GenerateWithOpts: 50 → 6 (extracted 8 focused helpers) - reconcile.Reconcile: 42 → 11 (extracted buildRoutes, buildDNSEntries, writeHAProxyConfig) - AutheliaUpdateConfig: 25 → 10 (extracted enableAuthelia/disableAuthelia) Test coverage improvements: - reconcile: 4.5% → 56.8% (stub-based tests for route building, DNS entries, orchestration) - dnsfilter: 10.7% → 45.6% (Manager.Compile, AddList, ToggleList, custom entries) - config: 67.3% → 89.8% (DHCP static lease mutation tests)
562 lines
17 KiB
Go
562 lines
17 KiB
Go
package v1
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/wild-cloud/wild-central/internal/config"
|
|
"github.com/wild-cloud/wild-central/internal/dnsmasq"
|
|
"github.com/wild-cloud/wild-central/internal/domains"
|
|
"github.com/wild-cloud/wild-central/internal/haproxy"
|
|
)
|
|
|
|
// extractHost gets the host part from a host:port string (test helper)
|
|
func extractHost(addr string) string {
|
|
for i := len(addr) - 1; i >= 0; i-- {
|
|
if addr[i] == ':' {
|
|
return addr[:i]
|
|
}
|
|
}
|
|
return addr
|
|
}
|
|
|
|
// TestReconciliation_HAProxyConfigFromDomains verifies that the reconciliation
|
|
// logic correctly maps registered domains to HAProxy configuration. This
|
|
// replicates the route-building logic from reconcileNetworking() and asserts
|
|
// on the generated config without requiring haproxy or dnsmasq binaries.
|
|
func TestReconciliation_HAProxyConfigFromDomains(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
|
|
// Register a mix of domains like a real deployment
|
|
testDomains := []domains.Domain{
|
|
{
|
|
DomainName: "cloud.payne.io",
|
|
Source: "wild-cloud",
|
|
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
|
Subdomains: true,
|
|
Public: true,
|
|
},
|
|
{
|
|
DomainName: "payne.io",
|
|
Source: "wild-cloud",
|
|
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
|
Subdomains: false,
|
|
Public: true,
|
|
},
|
|
{
|
|
DomainName: "wild-cloud.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "127.0.0.1:5055", Type: domains.BackendHTTP},
|
|
},
|
|
{
|
|
DomainName: "my-api.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "192.168.8.60:9001", Type: domains.BackendHTTP, Health: "/health"},
|
|
},
|
|
}
|
|
|
|
for _, dom := range testDomains {
|
|
if err := api.domains.Register(dom); err != nil {
|
|
t.Fatalf("Register %s failed: %v", dom.DomainName, err)
|
|
}
|
|
}
|
|
|
|
// Replicate reconcileNetworking route-building logic
|
|
doms, err := api.domains.List()
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
var instanceRoutes []haproxy.L4Route
|
|
var httpRoutes []haproxy.HTTPRoute
|
|
|
|
for _, dom := range doms {
|
|
switch dom.Backend.Type {
|
|
case domains.BackendTCPPassthrough:
|
|
instanceRoutes = append(instanceRoutes, haproxy.L4Route{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
BackendIP: extractHost(dom.Backend.Address),
|
|
Subdomains: dom.Subdomains,
|
|
})
|
|
case domains.BackendHTTP:
|
|
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
Routes: []haproxy.HTTPRouteBackend{{
|
|
Backend: dom.Backend.Address,
|
|
HealthPath: dom.Backend.Health,
|
|
}},
|
|
})
|
|
}
|
|
}
|
|
|
|
// Central registers itself as a domain; add it to the test data
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "central.payne.io",
|
|
Source: "central",
|
|
Backend: domains.Backend{Address: "127.0.0.1:5055", Type: domains.BackendHTTP},
|
|
TLS: domains.TLSTerminate,
|
|
}); err != nil {
|
|
t.Fatalf("Register central failed: %v", err)
|
|
}
|
|
// Re-list to pick up Central
|
|
doms, err = api.domains.List()
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
httpRoutes = nil
|
|
for _, dom := range doms {
|
|
if dom.Backend.Type == domains.BackendHTTP {
|
|
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
Routes: []haproxy.HTTPRouteBackend{{
|
|
Backend: dom.Backend.Address,
|
|
HealthPath: dom.Backend.Health,
|
|
}},
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg := api.haproxy.GenerateWithOpts(instanceRoutes, nil, haproxy.GenerateOpts{
|
|
HTTPRoutes: httpRoutes,
|
|
CertsDir: "/etc/haproxy/certs/",
|
|
})
|
|
|
|
// --- Assertions on HAProxy config ---
|
|
|
|
// L7 domains have exact-match ACLs in the SNI frontend
|
|
if !strings.Contains(cfg, "acl is_l7_central_payne_io req_ssl_sni -m str central.payne.io") {
|
|
t.Errorf("expected L7 exact-match ACL for central.payne.io:\n%s", cfg)
|
|
}
|
|
if !strings.Contains(cfg, "acl is_l7_wild_cloud_payne_io req_ssl_sni -m str wild-cloud.payne.io") {
|
|
t.Errorf("expected L7 exact-match ACL for wild-cloud.payne.io:\n%s", cfg)
|
|
}
|
|
|
|
// L4 domains with subdomains:true have wildcard ACLs
|
|
if !strings.Contains(cfg, "req_ssl_sni -m end .cloud.payne.io") {
|
|
t.Errorf("expected L4 wildcard ACL for cloud.payne.io (subdomains:true):\n%s", cfg)
|
|
}
|
|
|
|
// L4 domains with subdomains:false have exact-match only
|
|
if !strings.Contains(cfg, "req_ssl_sni -m str payne.io") {
|
|
t.Errorf("expected L4 exact-match ACL for payne.io:\n%s", cfg)
|
|
}
|
|
if strings.Contains(cfg, "req_ssl_sni -m end .payne.io") {
|
|
t.Errorf("payne.io (subdomains:false) must NOT have wildcard ACL:\n%s", cfg)
|
|
}
|
|
|
|
// Central's config domain appears as an L7 route
|
|
if !strings.Contains(cfg, "hdr(host) -i central.payne.io") {
|
|
t.Errorf("expected Host header ACL for central.payne.io in L7 frontend:\n%s", cfg)
|
|
}
|
|
|
|
// L7 ACLs appear BEFORE L4 wildcard ACLs
|
|
l7Pos := strings.Index(cfg, "is_l7_central_payne_io")
|
|
l4WildcardPos := strings.Index(cfg, "req_ssl_sni -m end .cloud.payne.io")
|
|
if l7Pos > l4WildcardPos {
|
|
t.Errorf("L7 exact matches (pos %d) must appear before L4 wildcards (pos %d)", l7Pos, l4WildcardPos)
|
|
}
|
|
|
|
// L4 exact (payne.io) appears before L4 wildcard (*.cloud.payne.io)
|
|
l4ExactPos := strings.Index(cfg, "acl is_payne_io req_ssl_sni -m str payne.io")
|
|
if l4ExactPos < 0 {
|
|
t.Fatalf("expected L4 exact ACL for payne.io:\n%s", cfg)
|
|
}
|
|
if l4ExactPos > l4WildcardPos {
|
|
t.Errorf("L4 exact match (pos %d) must come before L4 wildcard (pos %d)", l4ExactPos, l4WildcardPos)
|
|
}
|
|
|
|
// Health check path is present for my-api
|
|
if !strings.Contains(cfg, "option httpchk GET /health") {
|
|
t.Errorf("expected health check for my-api:\n%s", cfg)
|
|
}
|
|
}
|
|
|
|
// TestReconciliation_DnsmasqConfigFromDomains verifies that reconciliation
|
|
// correctly generates dnsmasq config from registered domains. Wildcard
|
|
// domains (subdomains:true) get address=/, exact-match domains get
|
|
// host-record=. No local=/ directives — AAAA is handled by filter-AAAA.
|
|
func TestReconciliation_DnsmasqConfigFromDomains(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
|
|
centralIP := "192.168.8.151"
|
|
|
|
// Register domains with different backend types and subdomain settings
|
|
testDomains := []domains.Domain{
|
|
{
|
|
// tcp-passthrough, wildcard → address=/ with backend IP
|
|
DomainName: "cloud.payne.io",
|
|
Source: "wild-cloud",
|
|
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
|
Subdomains: true,
|
|
Public: true,
|
|
},
|
|
{
|
|
// http, exact → host-record= with Central IP
|
|
DomainName: "my-api.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "192.168.8.60:9001", Type: domains.BackendHTTP},
|
|
},
|
|
{
|
|
// http, exact → host-record= with Central IP
|
|
DomainName: "public-app.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "192.168.8.60:8080", Type: domains.BackendHTTP},
|
|
Public: true,
|
|
},
|
|
}
|
|
|
|
for _, dom := range testDomains {
|
|
if err := api.domains.Register(dom); err != nil {
|
|
t.Fatalf("Register %s failed: %v", dom.DomainName, err)
|
|
}
|
|
}
|
|
|
|
// Build dnsmasq entries the same way reconcileNetworking does
|
|
doms, err := api.domains.List()
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
globalCfg := &config.State{}
|
|
var dnsEntries []dnsmasq.DNSEntry
|
|
|
|
for _, dom := range doms {
|
|
if dom.DomainName == "" {
|
|
continue
|
|
}
|
|
|
|
dnsIP := centralIP
|
|
if dom.Backend.Type == domains.BackendTCPPassthrough {
|
|
dnsIP = extractHost(dom.Backend.Address)
|
|
}
|
|
|
|
dnsEntries = append(dnsEntries, dnsmasq.DNSEntry{
|
|
Domain: dom.DomainName,
|
|
IP: dnsIP,
|
|
Wildcard: dom.Subdomains,
|
|
})
|
|
}
|
|
|
|
dnsmasqCfg := api.dnsmasq.Generate(globalCfg, dnsEntries)
|
|
|
|
// --- Wildcard (subdomains:true) → address=/ with backend IP ---
|
|
if !strings.Contains(dnsmasqCfg, "address=/cloud.payne.io/192.168.8.240") {
|
|
t.Errorf("wildcard domain must use address=/ with backend IP:\n%s", dnsmasqCfg)
|
|
}
|
|
|
|
// --- Exact-match HTTP domains → host-record= with Central IP ---
|
|
if !strings.Contains(dnsmasqCfg, "host-record=my-api.payne.io,192.168.8.151") {
|
|
t.Errorf("exact-match http domain must use host-record= with Central IP:\n%s", dnsmasqCfg)
|
|
}
|
|
if !strings.Contains(dnsmasqCfg, "host-record=public-app.payne.io,192.168.8.151") {
|
|
t.Errorf("exact-match http domain must use host-record= with Central IP:\n%s", dnsmasqCfg)
|
|
}
|
|
|
|
// --- No local=/ directives (AAAA handled by filter-AAAA) ---
|
|
if strings.Contains(dnsmasqCfg, "local=/") {
|
|
t.Errorf("must not produce local=/ directives:\n%s", dnsmasqCfg)
|
|
}
|
|
|
|
// --- filter-AAAA present ---
|
|
if !strings.Contains(dnsmasqCfg, "filter-AAAA") {
|
|
t.Errorf("must include filter-AAAA directive:\n%s", dnsmasqCfg)
|
|
}
|
|
}
|
|
|
|
// TestReconciliation_CentralDomainInHAProxy verifies that Central, registered
|
|
// as a domain, produces correct HAProxy L7 routes.
|
|
func TestReconciliation_CentralDomainInHAProxy(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
|
|
centralPort := 15055
|
|
|
|
// Register Central as a domain (as EnsureCentralDomain does)
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "central.payne.io",
|
|
Source: "central",
|
|
Backend: domains.Backend{Address: fmt.Sprintf("127.0.0.1:%d", centralPort), Type: domains.BackendHTTP},
|
|
TLS: domains.TLSTerminate,
|
|
}); err != nil {
|
|
t.Fatalf("Register central failed: %v", err)
|
|
}
|
|
|
|
// Register another HTTP domain
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "my-app.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "192.168.8.60:9001", Type: domains.BackendHTTP},
|
|
}); err != nil {
|
|
t.Fatalf("Register failed: %v", err)
|
|
}
|
|
|
|
doms, _ := api.domains.List()
|
|
|
|
var httpRoutes []haproxy.HTTPRoute
|
|
for _, dom := range doms {
|
|
if dom.Backend.Type == domains.BackendHTTP {
|
|
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
Routes: []haproxy.HTTPRouteBackend{{
|
|
Backend: dom.Backend.Address,
|
|
}},
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg := api.haproxy.GenerateWithOpts(nil, nil, haproxy.GenerateOpts{
|
|
HTTPRoutes: httpRoutes,
|
|
})
|
|
|
|
// Central domain should be present as both SNI ACL and Host header ACL
|
|
if !strings.Contains(cfg, "req_ssl_sni -m str central.payne.io") {
|
|
t.Errorf("expected Central domain SNI ACL:\n%s", cfg)
|
|
}
|
|
if !strings.Contains(cfg, "hdr(host) -i central.payne.io") {
|
|
t.Errorf("expected Central domain Host ACL:\n%s", cfg)
|
|
}
|
|
|
|
// Central backend must use the configured port (15055)
|
|
if !strings.Contains(cfg, fmt.Sprintf("server s0 127.0.0.1:%d", centralPort)) {
|
|
t.Errorf("Central backend must use port %d:\n%s", centralPort, cfg)
|
|
}
|
|
}
|
|
|
|
// TestReconciliation_TCPPassthroughDNSTarget verifies that tcp-passthrough
|
|
// domains get DNS pointing to the backend IP, not Central's IP.
|
|
func TestReconciliation_TCPPassthroughDNSTarget(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
|
|
centralIP := "192.168.8.151"
|
|
|
|
// Register a k8s instance with tcp-passthrough
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "cloud.payne.io",
|
|
Source: "wild-cloud",
|
|
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
|
Subdomains: true,
|
|
Public: true,
|
|
}); err != nil {
|
|
t.Fatalf("Register failed: %v", err)
|
|
}
|
|
|
|
doms, _ := api.domains.List()
|
|
globalCfg := &config.State{}
|
|
|
|
var dnsEntries []dnsmasq.DNSEntry
|
|
for _, dom := range doms {
|
|
dnsIP := centralIP
|
|
if dom.Backend.Type == domains.BackendTCPPassthrough {
|
|
dnsIP = extractHost(dom.Backend.Address)
|
|
}
|
|
dnsEntries = append(dnsEntries, dnsmasq.DNSEntry{
|
|
Domain: dom.DomainName,
|
|
IP: dnsIP,
|
|
Wildcard: dom.Subdomains,
|
|
})
|
|
}
|
|
|
|
dnsmasqCfg := api.dnsmasq.Generate(globalCfg, dnsEntries)
|
|
|
|
// TCP passthrough with subdomains → address=/ to backend (k8s LB), NOT central
|
|
if !strings.Contains(dnsmasqCfg, "address=/cloud.payne.io/192.168.8.240") {
|
|
t.Errorf("wildcard tcp-passthrough DNS must use address=/ to backend IP 192.168.8.240:\n%s", dnsmasqCfg)
|
|
}
|
|
if strings.Contains(dnsmasqCfg, "host-record=cloud.payne.io,192.168.8.151") {
|
|
t.Errorf("wildcard tcp-passthrough DNS must NOT use host-record= to Central IP:\n%s", dnsmasqCfg)
|
|
}
|
|
}
|
|
|
|
// TestReconciliation_DNSOnlyHasNoDNSButNoProxy verifies that dns-only domains
|
|
// get DNS entries but no HAProxy routes.
|
|
func TestReconciliation_DNSOnlyNoGateway(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
|
|
centralIP := "192.168.8.151"
|
|
|
|
// Register a dns-only domain (e.g. for SSH access)
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "dev.payne.io",
|
|
Source: "manual",
|
|
Backend: domains.Backend{Address: "192.168.8.222", Type: domains.BackendDNSOnly},
|
|
Public: true,
|
|
}); err != nil {
|
|
t.Fatalf("Register failed: %v", err)
|
|
}
|
|
|
|
// Also register a normal HTTP domain to verify it still works
|
|
if err := api.domains.Register(domains.Domain{
|
|
DomainName: "app.payne.io",
|
|
Source: "wild-works",
|
|
Backend: domains.Backend{Address: "127.0.0.1:8080", Type: domains.BackendHTTP},
|
|
}); err != nil {
|
|
t.Fatalf("Register failed: %v", err)
|
|
}
|
|
|
|
doms, _ := api.domains.List()
|
|
|
|
// Build HAProxy routes — dns-only should be absent
|
|
var instanceRoutes []haproxy.L4Route
|
|
var httpRoutes []haproxy.HTTPRoute
|
|
|
|
for _, dom := range doms {
|
|
switch dom.Backend.Type {
|
|
case domains.BackendTCPPassthrough:
|
|
instanceRoutes = append(instanceRoutes, haproxy.L4Route{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
BackendIP: extractHost(dom.Backend.Address),
|
|
})
|
|
case domains.BackendDNSOnly:
|
|
// dns-only: no gateway routing
|
|
case domains.BackendHTTP:
|
|
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
|
Name: dom.DomainName,
|
|
Domain: dom.DomainName,
|
|
Routes: []haproxy.HTTPRouteBackend{{Backend: dom.Backend.Address}},
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg := api.haproxy.GenerateWithOpts(instanceRoutes, nil, haproxy.GenerateOpts{
|
|
HTTPRoutes: httpRoutes,
|
|
CertsDir: "/etc/haproxy/certs/",
|
|
})
|
|
|
|
// dns-only domain must NOT appear in HAProxy config
|
|
if strings.Contains(cfg, "dev.payne.io") {
|
|
t.Errorf("dns-only domain must NOT appear in HAProxy config:\n%s", cfg)
|
|
}
|
|
// HTTP domain should appear
|
|
if !strings.Contains(cfg, "app.payne.io") {
|
|
t.Errorf("http domain should appear in HAProxy config:\n%s", cfg)
|
|
}
|
|
|
|
// Build DNS config — dns-only should point to its backend IP
|
|
globalCfg := &config.State{}
|
|
var dnsEntries []dnsmasq.DNSEntry
|
|
|
|
for _, dom := range doms {
|
|
dnsIP := centralIP
|
|
bt := dom.Backend.Type
|
|
if bt == domains.BackendTCPPassthrough || bt == domains.BackendDNSOnly {
|
|
dnsIP = extractHost(dom.Backend.Address)
|
|
}
|
|
dnsEntries = append(dnsEntries, dnsmasq.DNSEntry{
|
|
Domain: dom.DomainName,
|
|
IP: dnsIP,
|
|
Wildcard: dom.Subdomains,
|
|
})
|
|
}
|
|
|
|
dnsmasqCfg := api.dnsmasq.Generate(globalCfg, dnsEntries)
|
|
|
|
// dns-only domain (public, no subdomains) → host-record= with backend IP
|
|
if !strings.Contains(dnsmasqCfg, "host-record=dev.payne.io,192.168.8.222") {
|
|
t.Errorf("dns-only domain must use host-record= with backend IP:\n%s", dnsmasqCfg)
|
|
}
|
|
// HTTP domain (no subdomains) → host-record= with Central IP
|
|
if !strings.Contains(dnsmasqCfg, "host-record=app.payne.io,192.168.8.151") {
|
|
t.Errorf("http domain must use host-record= with Central IP:\n%s", dnsmasqCfg)
|
|
}
|
|
}
|
|
|
|
// writeTestConfig writes a minimal global config with the given Central domain.
|
|
func writeTestConfig(t *testing.T, dataDir, centralDomain string) {
|
|
t.Helper()
|
|
configPath := filepath.Join(dataDir, "state.yaml")
|
|
content := fmt.Sprintf("cloud:\n central:\n domain: %s\n", centralDomain)
|
|
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCentralDomain_RegistersWhenDomainConfigured(t *testing.T) {
|
|
api, dataDir := setupTestAPI(t)
|
|
api.SetPort(15055)
|
|
writeTestConfig(t, dataDir, "central.example.com")
|
|
|
|
api.EnsureCentralDomain()
|
|
|
|
dom, err := api.domains.Get("central.example.com")
|
|
if err != nil {
|
|
t.Fatalf("expected Central domain to be registered: %v", err)
|
|
}
|
|
if dom.Source != "central" {
|
|
t.Errorf("source = %q, want %q", dom.Source, "central")
|
|
}
|
|
if dom.Backend.Address != "127.0.0.1:15055" {
|
|
t.Errorf("backend = %q, want %q", dom.Backend.Address, "127.0.0.1:15055")
|
|
}
|
|
if dom.TLS != domains.TLSTerminate {
|
|
t.Errorf("tls = %q, want %q", dom.TLS, domains.TLSTerminate)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCentralDomain_Idempotent(t *testing.T) {
|
|
api, dataDir := setupTestAPI(t)
|
|
api.SetPort(15055)
|
|
writeTestConfig(t, dataDir, "central.example.com")
|
|
|
|
api.EnsureCentralDomain()
|
|
api.EnsureCentralDomain() // second call should be a no-op
|
|
|
|
doms, _ := api.domains.List()
|
|
count := 0
|
|
for _, d := range doms {
|
|
if d.Source == "central" {
|
|
count++
|
|
}
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("expected 1 central domain, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCentralDomain_CleansUpOnDomainChange(t *testing.T) {
|
|
api, dataDir := setupTestAPI(t)
|
|
api.SetPort(15055)
|
|
|
|
// Register with old domain
|
|
writeTestConfig(t, dataDir, "old.example.com")
|
|
api.EnsureCentralDomain()
|
|
|
|
// Change domain
|
|
writeTestConfig(t, dataDir, "new.example.com")
|
|
api.EnsureCentralDomain()
|
|
|
|
// Old domain should be gone
|
|
if _, err := api.domains.Get("old.example.com"); err == nil {
|
|
t.Error("old Central domain should have been deregistered")
|
|
}
|
|
// New domain should exist
|
|
dom, err := api.domains.Get("new.example.com")
|
|
if err != nil {
|
|
t.Fatalf("new Central domain should be registered: %v", err)
|
|
}
|
|
if dom.Source != "central" {
|
|
t.Errorf("source = %q, want %q", dom.Source, "central")
|
|
}
|
|
}
|
|
|
|
func TestEnsureCentralDomain_NoDomainIsNoop(t *testing.T) {
|
|
api, _ := setupTestAPI(t)
|
|
api.SetPort(15055)
|
|
// Default config has no Central domain
|
|
|
|
api.EnsureCentralDomain()
|
|
|
|
doms, _ := api.domains.List()
|
|
for _, d := range doms {
|
|
if d.Source == "central" {
|
|
t.Error("no Central domain should be registered when domain is empty")
|
|
}
|
|
}
|
|
}
|