Phase D: Automated DNS registration via MCNS
Add DNSRegistrar that creates/updates/deletes A records in MCNS during deploy and stop. When a service has routes, the agent ensures an A record exists in the configured zone pointing to the node's address. On stop, the record is removed. - Add MCNSConfig to agent config (server_url, ca_cert, token_path, zone, node_addr) with defaults and env overrides - Add DNSRegistrar (internal/agent/dns.go): REST client for MCNS record CRUD, nil-receiver safe - Wire into deploy flow (EnsureRecord after route registration) - Wire into stop flow (RemoveRecord before container stop) - 7 new tests, make all passes with 0 issues Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,11 @@ func TestLoadAgentConfig(t *testing.T) {
|
||||
if cfg.Metacrypt.Issuer != "infra" {
|
||||
t.Fatalf("metacrypt.issuer default: got %q, want infra", cfg.Metacrypt.Issuer)
|
||||
}
|
||||
|
||||
// MCNS defaults when section is omitted.
|
||||
if cfg.MCNS.Zone != "svc.mcp.metacircular.net" {
|
||||
t.Fatalf("mcns.zone default: got %q, want svc.mcp.metacircular.net", cfg.MCNS.Zone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLIConfigValidation(t *testing.T) {
|
||||
@@ -521,6 +526,81 @@ node_name = "rift"
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMCNS(t *testing.T) {
|
||||
cfgStr := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
[mcns]
|
||||
server_url = "https://localhost:28443"
|
||||
ca_cert = "/srv/mcp/certs/metacircular-ca.pem"
|
||||
token_path = "/srv/mcp/metacrypt-token"
|
||||
zone = "custom.zone"
|
||||
node_addr = "10.0.0.1"
|
||||
`
|
||||
path := writeTempConfig(t, cfgStr)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.MCNS.ServerURL != "https://localhost:28443" {
|
||||
t.Fatalf("mcns.server_url: got %q", cfg.MCNS.ServerURL)
|
||||
}
|
||||
if cfg.MCNS.CACert != "/srv/mcp/certs/metacircular-ca.pem" {
|
||||
t.Fatalf("mcns.ca_cert: got %q", cfg.MCNS.CACert)
|
||||
}
|
||||
if cfg.MCNS.Zone != "custom.zone" {
|
||||
t.Fatalf("mcns.zone: got %q", cfg.MCNS.Zone)
|
||||
}
|
||||
if cfg.MCNS.NodeAddr != "10.0.0.1" {
|
||||
t.Fatalf("mcns.node_addr: got %q", cfg.MCNS.NodeAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMCNSEnvOverrides(t *testing.T) {
|
||||
minimal := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
`
|
||||
t.Setenv("MCP_AGENT_MCNS_SERVER_URL", "https://override:28443")
|
||||
t.Setenv("MCP_AGENT_MCNS_TOKEN_PATH", "/override/token")
|
||||
t.Setenv("MCP_AGENT_MCNS_NODE_ADDR", "10.0.0.99")
|
||||
|
||||
path := writeTempConfig(t, minimal)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.MCNS.ServerURL != "https://override:28443" {
|
||||
t.Fatalf("mcns.server_url: got %q", cfg.MCNS.ServerURL)
|
||||
}
|
||||
if cfg.MCNS.TokenPath != "/override/token" {
|
||||
t.Fatalf("mcns.token_path: got %q", cfg.MCNS.TokenPath)
|
||||
}
|
||||
if cfg.MCNS.NodeAddr != "10.0.0.99" {
|
||||
t.Fatalf("mcns.node_addr: got %q", cfg.MCNS.NodeAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
|
||||
Reference in New Issue
Block a user