mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-22 17:41:26 +00:00
feat(config): pull out resolution into package and add tests
also adds a string method for pretty config printing
This commit is contained in:
3 files changed
+126
-39
No files matched your search
+8
-39
@@ -88,40 +88,9 @@ func main() {
|
||||
log.Fatalf("failed to load config: %v", err)
|
||||
}
|
||||
|
||||
telemetryResolution := cfg.Telemetry.Resolution.Duration
|
||||
if telemetryResolution == 0 {
|
||||
telemetryResolution = time.Hour
|
||||
}
|
||||
telemetryRetention := cfg.Telemetry.Retention.Duration
|
||||
if telemetryRetention == 0 {
|
||||
telemetryRetention = 28 * 24 * time.Hour // 4 weeks
|
||||
}
|
||||
packetRetention := cfg.Packets.Retention.Duration
|
||||
if packetRetention == 0 {
|
||||
packetRetention = 30 * 24 * time.Hour // 30 days
|
||||
}
|
||||
resolved := config.Resolve(cfg)
|
||||
|
||||
maxConnsPerIP := cfg.WebSocket.MaxConnectionsPerIP
|
||||
if maxConnsPerIP == 0 {
|
||||
maxConnsPerIP = 5
|
||||
}
|
||||
|
||||
// resolve background intervals with defaults
|
||||
viewRefreshInterval := cfg.Background.ViewRefresh.Duration
|
||||
if viewRefreshInterval == 0 {
|
||||
viewRefreshInterval = time.Hour
|
||||
}
|
||||
reconfirmInterval := cfg.Background.Reconfirm.Duration
|
||||
if reconfirmInterval == 0 {
|
||||
reconfirmInterval = time.Hour
|
||||
}
|
||||
cleanupInterval := cfg.Background.Cleanup.Duration
|
||||
if cleanupInterval == 0 {
|
||||
cleanupInterval = time.Hour
|
||||
}
|
||||
|
||||
log.Printf("config: loaded — telemetryResolution=%s telemetryRetention=%s packetRetention=%s maxConnsPerIP=%d viewRefresh=%s reconfirm=%s cleanup=%s",
|
||||
telemetryResolution, telemetryRetention, packetRetention, maxConnsPerIP, viewRefreshInterval, reconfirmInterval, cleanupInterval)
|
||||
log.Printf("config: loaded — %s", resolved)
|
||||
|
||||
// ── Hub ──────────────────────────────────────────────────────────────────
|
||||
h := hub.New()
|
||||
@@ -238,7 +207,7 @@ func main() {
|
||||
URL: getEnv("MQTT_BROKER_1_URL"),
|
||||
Username: getEnv("MQTT_BROKER_1_USERNAME"),
|
||||
Password: getEnv("MQTT_BROKER_1_PASSWORD"),
|
||||
TelemetryResolution: telemetryResolution,
|
||||
TelemetryResolution: resolved.TelemetryResolution,
|
||||
AllowedIATAs: allowedIATAs,
|
||||
},
|
||||
store,
|
||||
@@ -253,7 +222,7 @@ func main() {
|
||||
URL: getEnv("MQTT_BROKER_2_URL"),
|
||||
Username: getEnv("MQTT_BROKER_2_USERNAME"),
|
||||
Password: getEnv("MQTT_BROKER_2_PASSWORD"),
|
||||
TelemetryResolution: telemetryResolution,
|
||||
TelemetryResolution: resolved.TelemetryResolution,
|
||||
AllowedIATAs: allowedIATAs,
|
||||
},
|
||||
store,
|
||||
@@ -271,14 +240,14 @@ func main() {
|
||||
go broker2.Start(ctx)
|
||||
|
||||
scheduler := background.New([]background.Task{
|
||||
background.ViewRefreshTask(store, viewRefreshInterval),
|
||||
background.CleanupTask(store, telemetryRetention, packetRetention, cleanupInterval),
|
||||
background.ReconfirmTask(store, reconfirmInterval),
|
||||
background.ViewRefreshTask(store, resolved.ViewRefreshInterval),
|
||||
background.CleanupTask(store, resolved.TelemetryRetention, resolved.PacketRetention, resolved.CleanupInterval),
|
||||
background.ReconfirmTask(store, resolved.ReconfirmInterval),
|
||||
})
|
||||
go scheduler.Start(ctx)
|
||||
|
||||
// ── HTTP server ──────────────────────────────────────────────────────────
|
||||
r := router.New(h, reader, []*ingest.Worker{broker1, broker2}, maxConnsPerIP, cfg.CORS)
|
||||
r := router.New(h, reader, []*ingest.Worker{broker1, broker2}, resolved.MaxConnsPerIP, cfg.CORS)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -27,6 +28,17 @@ type Config struct {
|
||||
Background BackgroundConfig `yaml:"background"`
|
||||
}
|
||||
|
||||
// ResolvedConfig holds all runtime configuration with defaults applied.
|
||||
type ResolvedConfig struct {
|
||||
TelemetryResolution time.Duration
|
||||
TelemetryRetention time.Duration
|
||||
PacketRetention time.Duration
|
||||
MaxConnsPerIP int
|
||||
ViewRefreshInterval time.Duration
|
||||
ReconfirmInterval time.Duration
|
||||
CleanupInterval time.Duration
|
||||
}
|
||||
|
||||
// BackgroundConfig controls the intervals for background maintenance tasks.
|
||||
type BackgroundConfig struct {
|
||||
// ViewRefresh is how often materialized views are refreshed.
|
||||
@@ -227,3 +239,46 @@ func Load(path string) (*Config, error) {
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Resolve returns a ResolvedConfig with defaults applied for any zero values.
|
||||
func Resolve(cfg *Config) ResolvedConfig {
|
||||
r := ResolvedConfig{
|
||||
TelemetryResolution: cfg.Telemetry.Resolution.Duration,
|
||||
TelemetryRetention: cfg.Telemetry.Retention.Duration,
|
||||
PacketRetention: cfg.Packets.Retention.Duration,
|
||||
MaxConnsPerIP: cfg.WebSocket.MaxConnectionsPerIP,
|
||||
ViewRefreshInterval: cfg.Background.ViewRefresh.Duration,
|
||||
ReconfirmInterval: cfg.Background.Reconfirm.Duration,
|
||||
CleanupInterval: cfg.Background.Cleanup.Duration,
|
||||
}
|
||||
if r.TelemetryResolution == 0 {
|
||||
r.TelemetryResolution = time.Hour
|
||||
}
|
||||
if r.TelemetryRetention == 0 {
|
||||
r.TelemetryRetention = 28 * 24 * time.Hour
|
||||
}
|
||||
if r.PacketRetention == 0 {
|
||||
r.PacketRetention = 30 * 24 * time.Hour
|
||||
}
|
||||
if r.MaxConnsPerIP == 0 {
|
||||
r.MaxConnsPerIP = 5
|
||||
}
|
||||
if r.ViewRefreshInterval == 0 {
|
||||
r.ViewRefreshInterval = time.Hour
|
||||
}
|
||||
if r.ReconfirmInterval == 0 {
|
||||
r.ReconfirmInterval = time.Hour
|
||||
}
|
||||
if r.CleanupInterval == 0 {
|
||||
r.CleanupInterval = time.Hour
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ResolvedConfig) String() string {
|
||||
return fmt.Sprintf(
|
||||
"telemetryResolution=%s telemetryRetention=%s packetRetention=%s maxConnsPerIP=%d viewRefresh=%s reconfirm=%s cleanup=%s",
|
||||
r.TelemetryResolution, r.TelemetryRetention, r.PacketRetention,
|
||||
r.MaxConnsPerIP, r.ViewRefreshInterval, r.ReconfirmInterval, r.CleanupInterval,
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,9 @@ package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoad_FileNotFound(t *testing.T) {
|
||||
@@ -69,3 +71,64 @@ func TestLoad_InvalidYAML(t *testing.T) {
|
||||
t.Fatal("expected error for invalid YAML, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolve_Defaults(t *testing.T) {
|
||||
r := Resolve(&Config{})
|
||||
if r.TelemetryResolution != time.Hour {
|
||||
t.Errorf("expected TelemetryResolution 1h, got %v", r.TelemetryResolution)
|
||||
}
|
||||
if r.TelemetryRetention != 28*24*time.Hour {
|
||||
t.Errorf("expected TelemetryRetention 672h, got %v", r.TelemetryRetention)
|
||||
}
|
||||
if r.PacketRetention != 30*24*time.Hour {
|
||||
t.Errorf("expected PacketRetention 720h, got %v", r.PacketRetention)
|
||||
}
|
||||
if r.MaxConnsPerIP != 5 {
|
||||
t.Errorf("expected MaxConnsPerIP 5, got %d", r.MaxConnsPerIP)
|
||||
}
|
||||
if r.ViewRefreshInterval != time.Hour {
|
||||
t.Errorf("expected ViewRefreshInterval 1h, got %v", r.ViewRefreshInterval)
|
||||
}
|
||||
if r.ReconfirmInterval != time.Hour {
|
||||
t.Errorf("expected ReconfirmInterval 1h, got %v", r.ReconfirmInterval)
|
||||
}
|
||||
if r.CleanupInterval != time.Hour {
|
||||
t.Errorf("expected CleanupInterval 1h, got %v", r.CleanupInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolve_ExplicitValues(t *testing.T) {
|
||||
cfg := &Config{}
|
||||
cfg.Telemetry.Resolution.Duration = 30 * time.Minute
|
||||
cfg.Telemetry.Retention.Duration = 14 * 24 * time.Hour
|
||||
cfg.Packets.Retention.Duration = 7 * 24 * time.Hour
|
||||
cfg.WebSocket.MaxConnectionsPerIP = 10
|
||||
cfg.Background.ViewRefresh.Duration = 2 * time.Hour
|
||||
cfg.Background.Reconfirm.Duration = 3 * time.Hour
|
||||
cfg.Background.Cleanup.Duration = 4 * time.Hour
|
||||
|
||||
r := Resolve(cfg)
|
||||
if r.TelemetryResolution != 30*time.Minute {
|
||||
t.Errorf("expected 30m, got %v", r.TelemetryResolution)
|
||||
}
|
||||
if r.MaxConnsPerIP != 10 {
|
||||
t.Errorf("expected 10, got %d", r.MaxConnsPerIP)
|
||||
}
|
||||
if r.ViewRefreshInterval != 2*time.Hour {
|
||||
t.Errorf("expected 2h, got %v", r.ViewRefreshInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvedConfig_String(t *testing.T) {
|
||||
r := Resolve(&Config{})
|
||||
s := r.String()
|
||||
if s == "" {
|
||||
t.Error("expected non-empty string")
|
||||
}
|
||||
if !strings.Contains(s, "telemetryResolution=") {
|
||||
t.Error("expected telemetryResolution in string")
|
||||
}
|
||||
if !strings.Contains(s, "maxConnsPerIP=") {
|
||||
t.Error("expected maxConnsPerIP in string")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user