Add semantic service snapshot

This commit is contained in:
Virgil 2026-04-04 02:21:56 +00:00
parent b4b1e5c930
commit 5968a4cc50
2 changed files with 108 additions and 7 deletions

View file

@ -95,6 +95,20 @@ type HealthResult struct {
TreeRoot string `json:"tree_root"`
}
// ServiceDescription is a semantic snapshot of the service state.
//
// snapshot := service.Describe()
// fmt.Println(snapshot.Status, snapshot.ZoneApex, snapshot.TreeRoot)
type ServiceDescription struct {
Status string `json:"status"`
Records int `json:"records"`
ZoneApex string `json:"zone_apex"`
TreeRoot string `json:"tree_root"`
DNSPort int `json:"dns_port"`
HTTPPort int `json:"http_port"`
RecordTTL string `json:"record_ttl"`
}
type Service struct {
mu sync.RWMutex
records map[string]NameRecords
@ -1104,6 +1118,48 @@ func (service *Service) Health() HealthResult {
}
}
// Describe returns a structured snapshot that is easier to inspect than String().
//
// snapshot := service.Describe()
// fmt.Printf("%+v\n", snapshot)
func (service *Service) Describe() ServiceDescription {
if service == nil {
return ServiceDescription{
Status: "not_ready",
}
}
service.pruneExpiredRecords()
service.mu.RLock()
defer service.mu.RUnlock()
treeRoot := service.treeRoot
if service.chainTreeRoot != "" {
treeRoot = service.chainTreeRoot
}
dnsPort := service.dnsPort
if dnsPort <= 0 {
dnsPort = DefaultDNSPort
}
httpPort := service.httpPort
if httpPort <= 0 {
httpPort = DefaultHTTPPort
}
return ServiceDescription{
Status: "ready",
Records: len(service.records),
ZoneApex: service.zoneApex,
TreeRoot: treeRoot,
DNSPort: dnsPort,
HTTPPort: httpPort,
RecordTTL: service.recordTTL.String(),
}
}
// ZoneApex returns the computed apex for the current record set.
//
// apex := service.ZoneApex()
@ -1402,14 +1458,16 @@ func NormalizeName(name string) string {
//
// fmt.Println(service)
func (service *Service) String() string {
service.mu.RLock()
defer service.mu.RUnlock()
snapshot := service.Describe()
return fmt.Sprintf(
"dns.Service{records=%d zone_apex=%q tree_root=%q}",
len(service.records),
service.zoneApex,
service.treeRoot,
"dns.Service{status=%q records=%d zone_apex=%q tree_root=%q dns_port=%d http_port=%d record_ttl=%q}",
snapshot.Status,
snapshot.Records,
snapshot.ZoneApex,
snapshot.TreeRoot,
snapshot.DNSPort,
snapshot.HTTPPort,
snapshot.RecordTTL,
)
}

View file

@ -617,6 +617,49 @@ func TestServiceLocalMutationClearsChainTreeRoot(t *testing.T) {
}
}
func TestServiceDescribeReturnsSemanticSnapshot(t *testing.T) {
service := NewService(ServiceOptions{
Records: map[string]NameRecords{
"gateway.charon.lthn": {
A: []string{"10.10.10.10"},
},
"node.charon.lthn": {
A: []string{"10.10.10.11"},
},
},
DNSPort: 1053,
HTTPPort: 5555,
})
snapshot := service.Describe()
if snapshot.Status != "ready" {
t.Fatalf("expected ready status, got %#v", snapshot.Status)
}
if snapshot.Records != 2 {
t.Fatalf("expected two cached records, got %#v", snapshot.Records)
}
if snapshot.ZoneApex != "charon.lthn" {
t.Fatalf("expected zone apex charon.lthn, got %#v", snapshot.ZoneApex)
}
if snapshot.TreeRoot == "" {
t.Fatal("expected tree root in service snapshot")
}
if snapshot.DNSPort != 1053 || snapshot.HTTPPort != 5555 {
t.Fatalf("expected configured ports in snapshot, got %#v", snapshot)
}
if snapshot.RecordTTL != "0s" {
t.Fatalf("expected zero TTL string for default service, got %#v", snapshot.RecordTTL)
}
description := service.String()
if !strings.Contains(description, "zone_apex=\"charon.lthn\"") {
t.Fatalf("expected string description to include zone apex, got %q", description)
}
if !strings.Contains(description, "dns_port=1053") || !strings.Contains(description, "http_port=5555") {
t.Fatalf("expected string description to include configured ports, got %q", description)
}
}
func TestServiceServeHTTPHealthReturnsJSON(t *testing.T) {
service := NewService(ServiceOptions{
Records: map[string]NameRecords{