Compare commits

..

2 Commits

Author SHA1 Message Date
KS Jannette
7f10bcb7de Added PermanentError type, IsPermanent() helper. Permanent errors are logged and not retried
Some checks are pending
check / check (push) Waiting to run
2026-03-05 00:00:09 -05:00
S Jannette
f91ca33752 Merge pull request #18 from kjannette/cleanup
general cleanup - removed old comments, enforced naming conventions etc
2026-03-04 23:32:01 -05:00
6 changed files with 51 additions and 6 deletions

View File

@@ -101,8 +101,12 @@ func SendDiscordNotification(webhookURL, message string, meta AlertMetadata) (bo
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
err := fmt.Errorf("discord webhook failed: HTTP %d", resp.StatusCode)
log.Printf("Discord webhook failed: HTTP %d", resp.StatusCode) log.Printf("Discord webhook failed: HTTP %d", resp.StatusCode)
return false, fmt.Errorf("discord webhook failed: HTTP %d", resp.StatusCode) if isPermanentStatusCode(resp.StatusCode) {
return false, &PermanentError{Err: err}
}
return false, err
} }
return true, nil return true, nil

View File

@@ -97,8 +97,12 @@ func SendEmailNotification(apiKey, fromAddress, toAddress, message string, meta
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
err := fmt.Errorf("resend API failed: HTTP %d", resp.StatusCode)
log.Printf("Resend API failed: HTTP %d", resp.StatusCode) log.Printf("Resend API failed: HTTP %d", resp.StatusCode)
return false, fmt.Errorf("resend API failed: HTTP %d", resp.StatusCode) if isPermanentStatusCode(resp.StatusCode) {
return false, &PermanentError{Err: err}
}
return false, err
} }
return true, nil return true, nil

View File

@@ -1,6 +1,10 @@
package notifications package notifications
import "context" import (
"context"
"errors"
"net/http"
)
// AlertMetadata holds context about the alert being sent. // AlertMetadata holds context about the alert being sent.
type AlertMetadata struct { type AlertMetadata struct {
@@ -13,3 +17,22 @@ type AlertMetadata struct {
type Notifier interface { type Notifier interface {
Send(ctx context.Context, message string, meta AlertMetadata) error Send(ctx context.Context, message string, meta AlertMetadata) error
} }
// PermanentError wraps errors that should not be retried (e.g. 401, 403, 404).
type PermanentError struct{ Err error }
func (e *PermanentError) Error() string { return e.Err.Error() }
func (e *PermanentError) Unwrap() error { return e.Err }
func IsPermanent(err error) bool {
var p *PermanentError
return errors.As(err, &p)
}
func isPermanentStatusCode(code int) bool {
return code == http.StatusUnauthorized ||
code == http.StatusForbidden ||
code == http.StatusNotFound ||
code == http.StatusMethodNotAllowed ||
code == http.StatusGone
}

View File

@@ -87,8 +87,12 @@ func SendSlackNotification(webhookURL, message string, meta AlertMetadata) (bool
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
err := fmt.Errorf("slack webhook failed: HTTP %d", resp.StatusCode)
log.Printf("Slack webhook failed: HTTP %d", resp.StatusCode) log.Printf("Slack webhook failed: HTTP %d", resp.StatusCode)
return false, fmt.Errorf("slack webhook failed: HTTP %d", resp.StatusCode) if isPermanentStatusCode(resp.StatusCode) {
return false, &PermanentError{Err: err}
}
return false, err
} }
return true, nil return true, nil

View File

@@ -63,8 +63,12 @@ func SendTelegramNotification(botToken, chatID, message string, meta AlertMetada
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
err := fmt.Errorf("telegram API failed: HTTP %d", resp.StatusCode)
log.Printf("Telegram API failed: HTTP %d", resp.StatusCode) log.Printf("Telegram API failed: HTTP %d", resp.StatusCode)
return false, fmt.Errorf("telegram API failed: HTTP %d", resp.StatusCode) if isPermanentStatusCode(resp.StatusCode) {
return false, &PermanentError{Err: err}
}
return false, err
} }
return true, nil return true, nil

View File

@@ -261,8 +261,14 @@ func sendWithRetry(ctx context.Context, n notifications.Notifier, message string
} }
if err := n.Send(ctx, message, meta); err != nil { if err := n.Send(ctx, message, meta); err != nil {
log.Printf("Notification attempt %d/%d failed: %v", attempt+1, notificationMaxRetries, err)
lastErr = err lastErr = err
if notifications.IsPermanent(err) {
log.Printf("Permanent notification failure, skipping retries: %v", err)
return err
}
log.Printf("Notification attempt %d/%d failed: %v", attempt+1, notificationMaxRetries, err)
continue continue
} }