Files
DDI/agent/core/role.go
T
sir_melonchenandClaude Opus 5.5 3e957d7b33 feat(agent): command runner with stdin and optional role logger
The DNS role needs to feed nsupdate scripts on stdin and to log its
dynamic updates. command.InputRunner adds RunInput (errors never quote the
input); roles implementing core.LogSetter get the core's logger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 05:47:32 +02:00

128 lines
3.7 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package core
import (
"context"
"log/slog"
"net/netip"
"time"
"git.beugsiud.de/beug.k/DDI/internal/bundle"
)
// Kind identifies an agent role.
type Kind string
// Supported roles. The values equal the role in bundle manifests.
const (
KindDNS Kind = "dns"
KindDHCP Kind = "dhcp"
)
// Revision is a verified configuration revision stored by the core under
// <state_dir>/revisions/<Number>/. Roles must treat it as read-only.
type Revision struct {
Number uint64
// Dir holds the artifacts at their manifest paths (bundle.CheckPath
// guarantees they stay inside Dir).
Dir string
Files []bundle.File
}
// Role applies revisions to one managed service (BIND or Kea). The core
// drives it as follows (docs/ARCHITECTURE.md, section 4.2):
//
// 1. Validate(rev). An error means the revision is rejected; nothing on the
// host may have changed.
// 2. Activate(rev, prev). Makes rev the live configuration and reloads the
// service. An error triggers a rollback.
// 3. Health(&rev), retried for a while. A non-OK state triggers a rollback.
// 4. Rollback: Activate(lastGood, &rev) followed by Health(&lastGood).
//
// Files marked CreateOnly are written only if they do not exist and are never
// overwritten or removed. Activate removes files that prev had and rev does
// not (except CreateOnly ones).
type Role interface {
// Kind returns the role.
Kind() Kind
// ServiceVersion returns the managed service's version, e.g.
// "BIND 9.20.3" or "Kea 3.2.1".
ServiceVersion(ctx context.Context) (string, error)
// Validate checks rev without changing the live configuration.
Validate(ctx context.Context, rev Revision) error
// Activate installs rev and reloads the service. prev is the revision
// that was active before (nil if none).
Activate(ctx context.Context, rev Revision, prev *Revision) error
// Health checks the running service. expected is the revision that
// should be active (nil if none); the DNS role compares SOA serials.
Health(ctx context.Context, expected *Revision) Health
}
// HealthState summarizes a health check.
type HealthState int
// Health states (mirroring the protocol's HealthState).
const (
HealthUnknown HealthState = iota
HealthOK
HealthDegraded
HealthFailed
)
// Health is the result of Role.Health.
type Health struct {
State HealthState
Message string
Checks []HealthCheck
}
// HealthCheck is one named check within a Health result.
type HealthCheck struct {
Name string
State HealthState
Message string
}
// LeaseState mirrors the protocol's LeaseState.
type LeaseState int
// Lease states.
const (
LeaseUnknown LeaseState = iota
LeaseActive
LeaseExpired
LeaseReleased
LeaseDeclined
)
// Lease is one DHCP lease as seen by the managed service.
type Lease struct {
IP netip.Addr
MAC string // lowercase, colon separated
Hostname string
State LeaseState
Expires time.Time
Updated time.Time // last client transaction
SubnetID uint32
ClientID string
}
// LeaseSource is implemented by roles that can list leases (DHCP). The core
// polls it, computes changes and reports them to the server.
type LeaseSource interface {
Leases(ctx context.Context) ([]Lease, error)
}
// LogSetter is implemented by roles that log on their own (e.g. the DNS
// role's dynamic updates). The core passes its logger, which carries the
// agent ID and role, before it uses the role.
type LogSetter interface {
SetLogger(log *slog.Logger)
}
// RoleFactory creates a role from its section of the agent configuration
// file. decode unmarshals that section (YAML) into the role's own struct; an
// absent section decodes nothing, so roles must start from defaults.
type RoleFactory func(decode func(v any) error) (Role, error)