Keep agent install handoff on canonical config

Contract-Neutral: The first-session agent install handoff keeps the existing API and setup contracts while binding runtime handlers to the Router-owned canonical config through startup and monitor reloads.
This commit is contained in:
rcourtman
2026-07-20 08:19:00 +01:00
parent c73e78aeea
commit 1153be4770
3 changed files with 40 additions and 0 deletions
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
)
func decodeHostAgentInstallResponse(t *testing.T, rec *httptest.ResponseRecorder) AgentInstallCommandResponse {
@@ -101,3 +102,39 @@ func TestHandleAgentInstallCommand_HostOmitsTokenWhenAuthOptional(t *testing.T)
t.Fatalf("expected optional-auth host install to avoid persisting API tokens")
}
}
func TestRouterSetupHandoffUsesCanonicalRuntimeConfig(t *testing.T) {
dataDir := t.TempDir()
runtimeConfig := &config.Config{DataPath: dataDir, ConfigPath: dataDir}
monitorConfig := &config.Config{DataPath: dataDir, ConfigPath: dataDir}
monitor, err := monitoring.New(monitorConfig)
if err != nil {
t.Fatalf("monitoring.New: %v", err)
}
t.Cleanup(monitor.Stop)
router := NewRouter(runtimeConfig, monitor, nil, nil, func() error { return nil }, "1.0.0")
t.Cleanup(router.shutdownBackgroundWorkers)
// The first-session security setup updates the Router-owned config after
// routes have already been wired. The agent-install handoff must observe
// that same canonical config instead of the monitor's startup snapshot.
config.Mu.Lock()
runtimeConfig.AuthUser = "admin"
runtimeConfig.AuthPass = "hashed-password"
config.Mu.Unlock()
body := []byte(`{"type":"host","enableCommands":false}`)
req := httptest.NewRequest(http.MethodPost, "/api/agent-install-command", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.configHandlers.HandleAgentInstallCommand(rec, req)
resp := decodeHostAgentInstallResponse(t, rec)
if resp.Token == "" || resp.Record == nil {
t.Fatalf("expected setup handoff to create a scoped install token")
}
if got := router.configHandlers.getConfig(req.Context()); got != runtimeConfig {
t.Fatalf("config handler uses config %#v, want Router config %#v", got, runtimeConfig)
}
}
+2
View File
@@ -419,6 +419,7 @@ func (r *Router) setupRoutes() {
if r.monitor != nil {
r.configHandlers.SetMonitor(r.monitor)
}
r.configHandlers.SetConfig(r.config)
r.configHandlers.SetMockModeChangeHook(r.syncPlatformSupplementalProviders)
r.trueNASHandlers = &TrueNASHandlers{
getPersistence: r.configHandlers.getPersistence,
@@ -1474,6 +1475,7 @@ func (r *Router) SetMonitor(m *monitoring.Monitor) {
}
if r.configHandlers != nil {
r.configHandlers.SetMonitor(m)
r.configHandlers.SetConfig(r.config)
}
if r.notificationHandlers != nil {
r.notificationHandlers.SetMonitor(NewNotificationMonitorWrapper(m))
+1
View File
@@ -86,6 +86,7 @@ func (r *Router) SetMultiTenantMonitor(mtm *monitoring.MultiTenantMonitor) {
r.mtMonitor = mtm
if r.configHandlers != nil {
r.configHandlers.SetMultiTenantMonitor(mtm)
r.configHandlers.SetConfig(r.config)
}
if r.alertHandlers != nil {
r.alertHandlers.SetMultiTenantMonitor(mtm)