services -> domains
This commit is contained in:
@@ -8,55 +8,53 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/wild-cloud/wild-central/internal/config"
|
||||
"github.com/wild-cloud/wild-central/internal/domains"
|
||||
"github.com/wild-cloud/wild-central/internal/haproxy"
|
||||
"github.com/wild-cloud/wild-central/internal/services"
|
||||
)
|
||||
|
||||
// TestReconciliation_HAProxyConfigFromServices verifies that the reconciliation
|
||||
// logic correctly maps registered services to HAProxy configuration. This
|
||||
// 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_HAProxyConfigFromServices(t *testing.T) {
|
||||
func TestReconciliation_HAProxyConfigFromDomains(t *testing.T) {
|
||||
api, _ := setupTestAPI(t)
|
||||
|
||||
// Register a mix of services like a real deployment
|
||||
testServices := []services.Service{
|
||||
// Register a mix of domains like a real deployment
|
||||
testDomains := []domains.Domain{
|
||||
{
|
||||
Domain: "cloud.payne.io",
|
||||
DomainName: "cloud.payne.io",
|
||||
Source: "wild-cloud",
|
||||
Backend: services.Backend{Address: "192.168.8.240:443", Type: services.BackendTCPPassthrough},
|
||||
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
||||
Subdomains: true,
|
||||
Public: true,
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
Domain: "payne.io",
|
||||
DomainName: "payne.io",
|
||||
Source: "wild-cloud",
|
||||
Backend: services.Backend{Address: "192.168.8.240:443", Type: services.BackendTCPPassthrough},
|
||||
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
||||
Subdomains: false,
|
||||
Public: true,
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
Domain: "wild-cloud.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: services.Backend{Address: "127.0.0.1:5055", Type: services.BackendHTTP},
|
||||
// Public defaults to false
|
||||
DomainName: "wild-cloud.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: domains.Backend{Address: "127.0.0.1:5055", Type: domains.BackendHTTP},
|
||||
},
|
||||
{
|
||||
Domain: "my-api.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: services.Backend{Address: "192.168.8.60:9001", Type: services.BackendHTTP, Health: "/health"},
|
||||
// Public defaults to false
|
||||
DomainName: "my-api.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: domains.Backend{Address: "192.168.8.60:9001", Type: domains.BackendHTTP, Health: "/health"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, svc := range testServices {
|
||||
if err := api.services.Register(svc); err != nil {
|
||||
t.Fatalf("Register %s failed: %v", svc.Domain, err)
|
||||
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
|
||||
svcs, err := api.services.List()
|
||||
doms, err := api.domains.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
@@ -64,50 +62,50 @@ func TestReconciliation_HAProxyConfigFromServices(t *testing.T) {
|
||||
var instanceRoutes []haproxy.InstanceRoute
|
||||
var httpRoutes []haproxy.HTTPRoute
|
||||
|
||||
for _, svc := range svcs {
|
||||
switch svc.Backend.Type {
|
||||
case services.BackendTCPPassthrough:
|
||||
for _, dom := range doms {
|
||||
switch dom.Backend.Type {
|
||||
case domains.BackendTCPPassthrough:
|
||||
instanceRoutes = append(instanceRoutes, haproxy.InstanceRoute{
|
||||
Name: svc.Domain,
|
||||
Domain: svc.Domain,
|
||||
BackendIP: extractHost(svc.Backend.Address),
|
||||
Subdomains: svc.Subdomains,
|
||||
Name: dom.DomainName,
|
||||
Domain: dom.DomainName,
|
||||
BackendIP: extractHost(dom.Backend.Address),
|
||||
Subdomains: dom.Subdomains,
|
||||
})
|
||||
case services.BackendHTTP:
|
||||
case domains.BackendHTTP:
|
||||
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
||||
Name: svc.Domain,
|
||||
Domain: svc.Domain,
|
||||
Name: dom.DomainName,
|
||||
Domain: dom.DomainName,
|
||||
Routes: []haproxy.HTTPRouteBackend{{
|
||||
Backend: svc.Backend.Address,
|
||||
HealthPath: svc.Backend.Health,
|
||||
Backend: dom.Backend.Address,
|
||||
HealthPath: dom.Backend.Health,
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Central registers itself as a service; add it to the test data
|
||||
if err := api.services.Register(services.Service{
|
||||
Domain: "central.payne.io",
|
||||
Source: "central",
|
||||
Backend: services.Backend{Address: "127.0.0.1:5055", Type: services.BackendHTTP},
|
||||
TLS: services.TLSTerminate,
|
||||
// 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
|
||||
svcs, err = api.services.List()
|
||||
doms, err = api.domains.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
httpRoutes = nil
|
||||
for _, svc := range svcs {
|
||||
if svc.Backend.Type == services.BackendHTTP {
|
||||
for _, dom := range doms {
|
||||
if dom.Backend.Type == domains.BackendHTTP {
|
||||
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
||||
Name: svc.Domain,
|
||||
Domain: svc.Domain,
|
||||
Name: dom.DomainName,
|
||||
Domain: dom.DomainName,
|
||||
Routes: []haproxy.HTTPRouteBackend{{
|
||||
Backend: svc.Backend.Address,
|
||||
HealthPath: svc.Backend.Health,
|
||||
Backend: dom.Backend.Address,
|
||||
HealthPath: dom.Backend.Health,
|
||||
}},
|
||||
})
|
||||
}
|
||||
@@ -120,7 +118,7 @@ func TestReconciliation_HAProxyConfigFromServices(t *testing.T) {
|
||||
|
||||
// --- Assertions on HAProxy config ---
|
||||
|
||||
// L7 services have exact-match ACLs in the SNI frontend
|
||||
// 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)
|
||||
}
|
||||
@@ -128,12 +126,12 @@ func TestReconciliation_HAProxyConfigFromServices(t *testing.T) {
|
||||
t.Errorf("expected L7 exact-match ACL for wild-cloud.payne.io:\n%s", cfg)
|
||||
}
|
||||
|
||||
// L4 services with subdomains:true have wildcard ACLs
|
||||
// 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 services with subdomains:false have exact-match only
|
||||
// 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)
|
||||
}
|
||||
@@ -168,72 +166,71 @@ func TestReconciliation_HAProxyConfigFromServices(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestReconciliation_DnsmasqConfigFromServices verifies that reconciliation
|
||||
// correctly generates dnsmasq config from registered services, including
|
||||
// TestReconciliation_DnsmasqConfigFromDomains verifies that reconciliation
|
||||
// correctly generates dnsmasq config from registered domains, including
|
||||
// the critical reach-based local=/ directives.
|
||||
func TestReconciliation_DnsmasqConfigFromServices(t *testing.T) {
|
||||
func TestReconciliation_DnsmasqConfigFromDomains(t *testing.T) {
|
||||
api, _ := setupTestAPI(t)
|
||||
|
||||
centralIP := "192.168.8.151"
|
||||
|
||||
// Register services with different reach and backend types
|
||||
testServices := []services.Service{
|
||||
// Register domains with different reach and backend types
|
||||
testDomains := []domains.Domain{
|
||||
{
|
||||
// tcp-passthrough, public → DNS points to k8s LB IP, no local=/
|
||||
Domain: "cloud.payne.io",
|
||||
DomainName: "cloud.payne.io",
|
||||
Source: "wild-cloud",
|
||||
Backend: services.Backend{Address: "192.168.8.240:443", Type: services.BackendTCPPassthrough},
|
||||
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
||||
Subdomains: true,
|
||||
Public: true,
|
||||
Public: true,
|
||||
},
|
||||
{
|
||||
// http, internal → DNS points to Central IP, has local=/
|
||||
Domain: "my-api.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: services.Backend{Address: "192.168.8.60:9001", Type: services.BackendHTTP},
|
||||
// Public defaults to false
|
||||
DomainName: "my-api.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: domains.Backend{Address: "192.168.8.60:9001", Type: domains.BackendHTTP},
|
||||
},
|
||||
{
|
||||
// http, public → DNS points to Central IP, NO local=/
|
||||
Domain: "public-app.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: services.Backend{Address: "192.168.8.60:8080", Type: services.BackendHTTP},
|
||||
Public: true,
|
||||
DomainName: "public-app.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: domains.Backend{Address: "192.168.8.60:8080", Type: domains.BackendHTTP},
|
||||
Public: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, svc := range testServices {
|
||||
if err := api.services.Register(svc); err != nil {
|
||||
t.Fatalf("Register %s failed: %v", svc.Domain, err)
|
||||
for _, dom := range testDomains {
|
||||
if err := api.domains.Register(dom); err != nil {
|
||||
t.Fatalf("Register %s failed: %v", dom.DomainName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Build dnsmasq instance configs the same way reconcileNetworking does
|
||||
svcs, err := api.services.List()
|
||||
doms, err := api.domains.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
|
||||
globalCfg := &config.GlobalConfig{}
|
||||
globalCfg := &config.State{}
|
||||
var instanceConfigs []config.InstanceConfig
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Domain == "" {
|
||||
for _, dom := range doms {
|
||||
if dom.DomainName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
dnsIP := centralIP
|
||||
if svc.Backend.Type == services.BackendTCPPassthrough {
|
||||
dnsIP = extractHost(svc.Backend.Address)
|
||||
if dom.Backend.Type == domains.BackendTCPPassthrough {
|
||||
dnsIP = extractHost(dom.Backend.Address)
|
||||
}
|
||||
|
||||
ic := config.InstanceConfig{}
|
||||
ic.Cluster.LoadBalancerIp = dnsIP
|
||||
|
||||
if !svc.Public {
|
||||
ic.Cloud.InternalDomain = svc.Domain
|
||||
if !dom.Public {
|
||||
ic.Cloud.InternalDomain = dom.DomainName
|
||||
} else {
|
||||
ic.Cloud.Domain = svc.Domain
|
||||
ic.Cloud.Domain = dom.DomainName
|
||||
}
|
||||
|
||||
instanceConfigs = append(instanceConfigs, ic)
|
||||
@@ -243,69 +240,67 @@ func TestReconciliation_DnsmasqConfigFromServices(t *testing.T) {
|
||||
|
||||
// --- TCP passthrough (public) → backend IP ---
|
||||
if !strings.Contains(dnsmasqCfg, "address=/cloud.payne.io/192.168.8.240") {
|
||||
t.Errorf("tcp-passthrough service must point DNS to backend IP (192.168.8.240):\n%s", dnsmasqCfg)
|
||||
t.Errorf("tcp-passthrough domain must point DNS to backend IP (192.168.8.240):\n%s", dnsmasqCfg)
|
||||
}
|
||||
|
||||
// --- HTTP services → Central IP ---
|
||||
// --- HTTP domains → Central IP ---
|
||||
if !strings.Contains(dnsmasqCfg, "address=/my-api.payne.io/192.168.8.151") {
|
||||
t.Errorf("http/internal service must point DNS to Central IP (192.168.8.151):\n%s", dnsmasqCfg)
|
||||
t.Errorf("http/internal domain must point DNS to Central IP (192.168.8.151):\n%s", dnsmasqCfg)
|
||||
}
|
||||
if !strings.Contains(dnsmasqCfg, "address=/public-app.payne.io/192.168.8.151") {
|
||||
t.Errorf("http/public service must point DNS to Central IP (192.168.8.151):\n%s", dnsmasqCfg)
|
||||
t.Errorf("http/public domain must point DNS to Central IP (192.168.8.151):\n%s", dnsmasqCfg)
|
||||
}
|
||||
|
||||
// --- reach:internal → local=/ present ---
|
||||
// --- internal → local=/ present ---
|
||||
if !strings.Contains(dnsmasqCfg, "local=/my-api.payne.io/") {
|
||||
t.Errorf("reach:internal service must have local=/ entry:\n%s", dnsmasqCfg)
|
||||
t.Errorf("internal domain must have local=/ entry:\n%s", dnsmasqCfg)
|
||||
}
|
||||
|
||||
// --- reach:public → NO local=/ ---
|
||||
// --- public → NO local=/ ---
|
||||
if strings.Contains(dnsmasqCfg, "local=/cloud.payne.io/") {
|
||||
t.Errorf("reach:public service must NOT have local=/ entry:\n%s", dnsmasqCfg)
|
||||
t.Errorf("public domain must NOT have local=/ entry:\n%s", dnsmasqCfg)
|
||||
}
|
||||
if strings.Contains(dnsmasqCfg, "local=/public-app.payne.io/") {
|
||||
t.Errorf("reach:public service must NOT have local=/ entry:\n%s", dnsmasqCfg)
|
||||
t.Errorf("public domain must NOT have local=/ entry:\n%s", dnsmasqCfg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReconciliation_CentralDomainInHAProxy verifies that Central, registered
|
||||
// as a service, produces correct HAProxy L7 routes.
|
||||
// as a domain, produces correct HAProxy L7 routes.
|
||||
func TestReconciliation_CentralDomainInHAProxy(t *testing.T) {
|
||||
api, _ := setupTestAPI(t)
|
||||
|
||||
centralPort := 15055
|
||||
|
||||
// Register Central as a service (as EnsureCentralService does)
|
||||
if err := api.services.Register(services.Service{
|
||||
Domain: "central.payne.io",
|
||||
Source: "central",
|
||||
Backend: services.Backend{Address: fmt.Sprintf("127.0.0.1:%d", centralPort), Type: services.BackendHTTP},
|
||||
// Public defaults to false
|
||||
TLS: services.TLSTerminate,
|
||||
// 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 service
|
||||
if err := api.services.Register(services.Service{
|
||||
Domain: "my-app.payne.io",
|
||||
Source: "wild-works",
|
||||
Backend: services.Backend{Address: "192.168.8.60:9001", Type: services.BackendHTTP},
|
||||
// Public defaults to false
|
||||
// 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)
|
||||
}
|
||||
|
||||
svcs, _ := api.services.List()
|
||||
doms, _ := api.domains.List()
|
||||
|
||||
var httpRoutes []haproxy.HTTPRoute
|
||||
for _, svc := range svcs {
|
||||
if svc.Backend.Type == services.BackendHTTP {
|
||||
for _, dom := range doms {
|
||||
if dom.Backend.Type == domains.BackendHTTP {
|
||||
httpRoutes = append(httpRoutes, haproxy.HTTPRoute{
|
||||
Name: svc.Domain,
|
||||
Domain: svc.Domain,
|
||||
Name: dom.DomainName,
|
||||
Domain: dom.DomainName,
|
||||
Routes: []haproxy.HTTPRouteBackend{{
|
||||
Backend: svc.Backend.Address,
|
||||
Backend: dom.Backend.Address,
|
||||
}},
|
||||
})
|
||||
}
|
||||
@@ -330,39 +325,39 @@ func TestReconciliation_CentralDomainInHAProxy(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestReconciliation_TCPPassthroughDNSTarget verifies that tcp-passthrough
|
||||
// services get DNS pointing to the backend IP, not Central's IP.
|
||||
// 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.services.Register(services.Service{
|
||||
Domain: "cloud.payne.io",
|
||||
if err := api.domains.Register(domains.Domain{
|
||||
DomainName: "cloud.payne.io",
|
||||
Source: "wild-cloud",
|
||||
Backend: services.Backend{Address: "192.168.8.240:443", Type: services.BackendTCPPassthrough},
|
||||
Backend: domains.Backend{Address: "192.168.8.240:443", Type: domains.BackendTCPPassthrough},
|
||||
Subdomains: true,
|
||||
Public: true,
|
||||
Public: true,
|
||||
}); err != nil {
|
||||
t.Fatalf("Register failed: %v", err)
|
||||
}
|
||||
|
||||
svcs, _ := api.services.List()
|
||||
globalCfg := &config.GlobalConfig{}
|
||||
doms, _ := api.domains.List()
|
||||
globalCfg := &config.State{}
|
||||
|
||||
var instanceConfigs []config.InstanceConfig
|
||||
for _, svc := range svcs {
|
||||
for _, dom := range doms {
|
||||
dnsIP := centralIP
|
||||
if svc.Backend.Type == services.BackendTCPPassthrough {
|
||||
dnsIP = extractHost(svc.Backend.Address)
|
||||
if dom.Backend.Type == domains.BackendTCPPassthrough {
|
||||
dnsIP = extractHost(dom.Backend.Address)
|
||||
}
|
||||
|
||||
ic := config.InstanceConfig{}
|
||||
ic.Cluster.LoadBalancerIp = dnsIP
|
||||
if !svc.Public {
|
||||
ic.Cloud.InternalDomain = svc.Domain
|
||||
if !dom.Public {
|
||||
ic.Cloud.InternalDomain = dom.DomainName
|
||||
} else {
|
||||
ic.Cloud.Domain = svc.Domain
|
||||
ic.Cloud.Domain = dom.DomainName
|
||||
}
|
||||
instanceConfigs = append(instanceConfigs, ic)
|
||||
}
|
||||
@@ -378,6 +373,104 @@ func TestReconciliation_TCPPassthroughDNSTarget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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.InstanceRoute
|
||||
var httpRoutes []haproxy.HTTPRoute
|
||||
|
||||
for _, dom := range doms {
|
||||
switch dom.Backend.Type {
|
||||
case domains.BackendTCPPassthrough:
|
||||
instanceRoutes = append(instanceRoutes, haproxy.InstanceRoute{
|
||||
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 instanceConfigs []config.InstanceConfig
|
||||
|
||||
for _, dom := range doms {
|
||||
dnsIP := centralIP
|
||||
bt := dom.Backend.Type
|
||||
if bt == domains.BackendTCPPassthrough || bt == domains.BackendDNSOnly {
|
||||
dnsIP = extractHost(dom.Backend.Address)
|
||||
}
|
||||
|
||||
ic := config.InstanceConfig{}
|
||||
ic.Cluster.LoadBalancerIp = dnsIP
|
||||
if !dom.Public {
|
||||
ic.Cloud.InternalDomain = dom.DomainName
|
||||
} else {
|
||||
ic.Cloud.Domain = dom.DomainName
|
||||
}
|
||||
instanceConfigs = append(instanceConfigs, ic)
|
||||
}
|
||||
|
||||
dnsmasqCfg := api.dnsmasq.Generate(globalCfg, instanceConfigs)
|
||||
|
||||
// dns-only domain should resolve to its backend IP (192.168.8.222)
|
||||
if !strings.Contains(dnsmasqCfg, "address=/dev.payne.io/192.168.8.222") {
|
||||
t.Errorf("dns-only domain must point DNS to backend IP:\n%s", dnsmasqCfg)
|
||||
}
|
||||
// HTTP domain should resolve to Central IP
|
||||
if !strings.Contains(dnsmasqCfg, "address=/app.payne.io/192.168.8.151") {
|
||||
t.Errorf("http domain must point DNS to 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()
|
||||
@@ -388,85 +481,85 @@ func writeTestConfig(t *testing.T, dataDir, centralDomain string) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCentralService_RegistersWhenDomainConfigured(t *testing.T) {
|
||||
func TestEnsureCentralDomain_RegistersWhenDomainConfigured(t *testing.T) {
|
||||
api, dataDir := setupTestAPI(t)
|
||||
api.SetPort(15055)
|
||||
writeTestConfig(t, dataDir, "central.example.com")
|
||||
|
||||
api.EnsureCentralService()
|
||||
api.EnsureCentralDomain()
|
||||
|
||||
svc, err := api.services.Get("central.example.com")
|
||||
dom, err := api.domains.Get("central.example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("expected Central service to be registered: %v", err)
|
||||
t.Fatalf("expected Central domain to be registered: %v", err)
|
||||
}
|
||||
if svc.Source != "central" {
|
||||
t.Errorf("source = %q, want %q", svc.Source, "central")
|
||||
if dom.Source != "central" {
|
||||
t.Errorf("source = %q, want %q", dom.Source, "central")
|
||||
}
|
||||
if svc.Backend.Address != "127.0.0.1:15055" {
|
||||
t.Errorf("backend = %q, want %q", svc.Backend.Address, "127.0.0.1:15055")
|
||||
if dom.Backend.Address != "127.0.0.1:15055" {
|
||||
t.Errorf("backend = %q, want %q", dom.Backend.Address, "127.0.0.1:15055")
|
||||
}
|
||||
if svc.TLS != services.TLSTerminate {
|
||||
t.Errorf("tls = %q, want %q", svc.TLS, services.TLSTerminate)
|
||||
if dom.TLS != domains.TLSTerminate {
|
||||
t.Errorf("tls = %q, want %q", dom.TLS, domains.TLSTerminate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCentralService_Idempotent(t *testing.T) {
|
||||
func TestEnsureCentralDomain_Idempotent(t *testing.T) {
|
||||
api, dataDir := setupTestAPI(t)
|
||||
api.SetPort(15055)
|
||||
writeTestConfig(t, dataDir, "central.example.com")
|
||||
|
||||
api.EnsureCentralService()
|
||||
api.EnsureCentralService() // second call should be a no-op
|
||||
api.EnsureCentralDomain()
|
||||
api.EnsureCentralDomain() // second call should be a no-op
|
||||
|
||||
svcs, _ := api.services.List()
|
||||
doms, _ := api.domains.List()
|
||||
count := 0
|
||||
for _, s := range svcs {
|
||||
if s.Source == "central" {
|
||||
for _, d := range doms {
|
||||
if d.Source == "central" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("expected 1 central service, got %d", count)
|
||||
t.Errorf("expected 1 central domain, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCentralService_CleansUpOnDomainChange(t *testing.T) {
|
||||
func TestEnsureCentralDomain_CleansUpOnDomainChange(t *testing.T) {
|
||||
api, dataDir := setupTestAPI(t)
|
||||
api.SetPort(15055)
|
||||
|
||||
// Register with old domain
|
||||
writeTestConfig(t, dataDir, "old.example.com")
|
||||
api.EnsureCentralService()
|
||||
api.EnsureCentralDomain()
|
||||
|
||||
// Change domain
|
||||
writeTestConfig(t, dataDir, "new.example.com")
|
||||
api.EnsureCentralService()
|
||||
api.EnsureCentralDomain()
|
||||
|
||||
// Old domain should be gone
|
||||
if _, err := api.services.Get("old.example.com"); err == nil {
|
||||
t.Error("old Central service should have been deregistered")
|
||||
if _, err := api.domains.Get("old.example.com"); err == nil {
|
||||
t.Error("old Central domain should have been deregistered")
|
||||
}
|
||||
// New domain should exist
|
||||
svc, err := api.services.Get("new.example.com")
|
||||
dom, err := api.domains.Get("new.example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("new Central service should be registered: %v", err)
|
||||
t.Fatalf("new Central domain should be registered: %v", err)
|
||||
}
|
||||
if svc.Source != "central" {
|
||||
t.Errorf("source = %q, want %q", svc.Source, "central")
|
||||
if dom.Source != "central" {
|
||||
t.Errorf("source = %q, want %q", dom.Source, "central")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCentralService_NoDomainIsNoop(t *testing.T) {
|
||||
func TestEnsureCentralDomain_NoDomainIsNoop(t *testing.T) {
|
||||
api, _ := setupTestAPI(t)
|
||||
api.SetPort(15055)
|
||||
// Default config has no Central domain
|
||||
|
||||
api.EnsureCentralService()
|
||||
api.EnsureCentralDomain()
|
||||
|
||||
svcs, _ := api.services.List()
|
||||
for _, s := range svcs {
|
||||
if s.Source == "central" {
|
||||
t.Error("no Central service should be registered when domain is empty")
|
||||
doms, _ := api.domains.List()
|
||||
for _, d := range doms {
|
||||
if d.Source == "central" {
|
||||
t.Error("no Central domain should be registered when domain is empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user