From 48a05fda1c6d58034d3e29186ca09ce3d09b7bbc Mon Sep 17 00:00:00 2001 From: Harivansh Rathi Date: Wed, 1 Apr 2026 14:52:37 +0000 Subject: [PATCH] Validate registration retry intervals. Reject register-only configs that leave the shared retry interval at the zero value so failed registration attempts cannot spin in a tight loop. Cover the regression in tests and make the register-only integration test explicit about its retry interval. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../nodeagent/app_integration_test.go | 17 +++++++++-------- .../node-agent/internal/nodeagent/app_test.go | 19 +++++++++++++++++++ apps/node-agent/internal/nodeagent/config.go | 4 ++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/apps/node-agent/internal/nodeagent/app_integration_test.go b/apps/node-agent/internal/nodeagent/app_integration_test.go index 46d1746..3d3694a 100644 --- a/apps/node-agent/internal/nodeagent/app_integration_test.go +++ b/apps/node-agent/internal/nodeagent/app_integration_test.go @@ -458,14 +458,15 @@ func TestAppRegistersWithoutControlPlaneTokenWhenUnset(t *testing.T) { exportPath := filepath.Join(t.TempDir(), "export") _, stop := startTestApp(t, Config{ - Port: "0", - ExportPath: exportPath, - MachineID: "nas-no-token", - DisplayName: "No Token NAS", - AgentVersion: "test-version", - ExportLabel: "register-only", - ControlPlaneURL: controlPlane.URL, - RegisterEnabled: true, + Port: "0", + ExportPath: exportPath, + MachineID: "nas-no-token", + DisplayName: "No Token NAS", + AgentVersion: "test-version", + ExportLabel: "register-only", + ControlPlaneURL: controlPlane.URL, + RegisterEnabled: true, + HeartbeatInterval: time.Second, }) defer stop() diff --git a/apps/node-agent/internal/nodeagent/app_test.go b/apps/node-agent/internal/nodeagent/app_test.go index f1d6bbe..bebd145 100644 --- a/apps/node-agent/internal/nodeagent/app_test.go +++ b/apps/node-agent/internal/nodeagent/app_test.go @@ -93,3 +93,22 @@ func TestNewRejectsRegistrationWithoutMachineID(t *testing.T) { t.Fatalf("error = %q, want missing-machine-id message", err.Error()) } } + +func TestNewRejectsRegistrationWithoutHeartbeatInterval(t *testing.T) { + t.Parallel() + + _, err := New(Config{ + ExportPath: t.TempDir(), + ListenAddress: defaultListenAddress(defaultPort), + MachineID: "nas-1", + ControlPlaneURL: "http://127.0.0.1:8081", + RegisterEnabled: true, + }, log.New(io.Discard, "", 0)) + if err == nil { + t.Fatal("expected missing registration retry interval to fail") + } + + if !strings.Contains(err.Error(), "BETTERNAS_NODE_HEARTBEAT_INTERVAL") { + t.Fatalf("error = %q, want missing-heartbeat-interval message", err.Error()) + } +} diff --git a/apps/node-agent/internal/nodeagent/config.go b/apps/node-agent/internal/nodeagent/config.go index 397a4a9..d114928 100644 --- a/apps/node-agent/internal/nodeagent/config.go +++ b/apps/node-agent/internal/nodeagent/config.go @@ -303,6 +303,10 @@ func validateRuntimeConfig(cfg Config) error { return fmt.Errorf("BETTERNAS_NODE_HEARTBEAT_INTERVAL must be greater than zero") } + if cfg.RegisterEnabled && cfg.HeartbeatInterval <= 0 { + return fmt.Errorf("BETTERNAS_NODE_HEARTBEAT_INTERVAL must be greater than zero when registration is enabled") + } + return nil }