Fix backend test failures blocking release workflow
Three categories of fixes: 1. Goroutine leak causing 10-minute timeout: - Add defer mon.notificationMgr.Stop() in monitor_memory_test.go - Background goroutines from notification manager weren't being stopped 2. Database NULL column scanning errors: - Change LastError from string to *string in queue.go - Change PayloadBytes from int to *int in queue.go - SQL NULL values require pointer types in Go 3. SSRF protection blocking test servers: - Check allowlist for localhost before rejecting in notifications.go - Set PULSE_DATA_DIR to temp directory in tests - Add defer nm.Stop() calls to prevent goroutine leaks Fixes for preflight test failures in workflow run 19280879903.
This commit is contained in:
parent
92a5d74ba9
commit
b41f8a2ac4
4 changed files with 19 additions and 4 deletions
|
|
@ -193,6 +193,7 @@ func TestPollPVEInstanceUsesRRDMemUsedFallback(t *testing.T) {
|
||||||
lastAuthAttempt: make(map[string]time.Time),
|
lastAuthAttempt: make(map[string]time.Time),
|
||||||
}
|
}
|
||||||
defer mon.alertManager.Stop()
|
defer mon.alertManager.Stop()
|
||||||
|
defer mon.notificationMgr.Stop()
|
||||||
|
|
||||||
mon.pollPVEInstance(context.Background(), "test", client)
|
mon.pollPVEInstance(context.Background(), "test", client)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1999,9 +1999,17 @@ func (n *NotificationManager) ValidateWebhookURL(webhookURL string) error {
|
||||||
return fmt.Errorf("webhook URL missing hostname")
|
return fmt.Errorf("webhook URL missing hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block localhost and loopback addresses (SSRF protection)
|
// Block localhost and loopback addresses (SSRF protection) unless allowlisted
|
||||||
if host == "localhost" || host == "127.0.0.1" || host == "::1" || strings.HasPrefix(host, "127.") {
|
if host == "localhost" || host == "127.0.0.1" || host == "::1" || strings.HasPrefix(host, "127.") {
|
||||||
return fmt.Errorf("webhook URLs pointing to localhost are not allowed for security reasons")
|
// Check if localhost is in the allowlist
|
||||||
|
localhostIP := net.ParseIP("127.0.0.1")
|
||||||
|
if !n.isIPInAllowlist(localhostIP) {
|
||||||
|
return fmt.Errorf("webhook URLs pointing to localhost are not allowed for security reasons")
|
||||||
|
}
|
||||||
|
log.Debug().
|
||||||
|
Str("host", host).
|
||||||
|
Str("url", webhookURL).
|
||||||
|
Msg("Localhost webhook URL allowed via allowlist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block link-local addresses
|
// Block link-local addresses
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,10 @@ func TestSendGroupedAppriseInvokesExecutor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendGroupedAppriseHTTP(t *testing.T) {
|
func TestSendGroupedAppriseHTTP(t *testing.T) {
|
||||||
|
t.Setenv("PULSE_DATA_DIR", t.TempDir())
|
||||||
|
|
||||||
nm := NewNotificationManager("https://pulse.local")
|
nm := NewNotificationManager("https://pulse.local")
|
||||||
|
defer nm.Stop()
|
||||||
nm.SetGroupingWindow(0)
|
nm.SetGroupingWindow(0)
|
||||||
nm.SetEmailConfig(EmailConfig{Enabled: false})
|
nm.SetEmailConfig(EmailConfig{Enabled: false})
|
||||||
|
|
||||||
|
|
@ -563,7 +566,10 @@ func TestSendTestNotificationApprise(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTestNotificationAppriseHTTP(t *testing.T) {
|
func TestSendTestNotificationAppriseHTTP(t *testing.T) {
|
||||||
|
t.Setenv("PULSE_DATA_DIR", t.TempDir())
|
||||||
|
|
||||||
nm := NewNotificationManager("")
|
nm := NewNotificationManager("")
|
||||||
|
defer nm.Stop()
|
||||||
nm.SetEmailConfig(EmailConfig{Enabled: false})
|
nm.SetEmailConfig(EmailConfig{Enabled: false})
|
||||||
|
|
||||||
type apprisePayload struct {
|
type apprisePayload struct {
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ type QueuedNotification struct {
|
||||||
Attempts int `json:"attempts"`
|
Attempts int `json:"attempts"`
|
||||||
MaxAttempts int `json:"maxAttempts"`
|
MaxAttempts int `json:"maxAttempts"`
|
||||||
LastAttempt *time.Time `json:"lastAttempt,omitempty"`
|
LastAttempt *time.Time `json:"lastAttempt,omitempty"`
|
||||||
LastError string `json:"lastError,omitempty"`
|
LastError *string `json:"lastError,omitempty"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
NextRetryAt *time.Time `json:"nextRetryAt,omitempty"`
|
NextRetryAt *time.Time `json:"nextRetryAt,omitempty"`
|
||||||
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
||||||
PayloadBytes int `json:"payloadBytes,omitempty"`
|
PayloadBytes *int `json:"payloadBytes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotificationQueue manages persistent notification delivery with retries and DLQ
|
// NotificationQueue manages persistent notification delivery with retries and DLQ
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue