Skip to content

Commit 8e113bb

Browse files
committed
checkAndAlertForUnnoticedAlerts: fix (and test) stupid comparison operator bug
1 parent 4e69657 commit 8e113bb

2 files changed

Lines changed: 102 additions & 7 deletions

File tree

cmd/alertmanager/scheduled.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func handleCloudwatchScheduledEvent(ctx context.Context, now time.Time) error {
2020
return err
2121
}
2222

23-
if err := checkAndAlertForUnnoticedAlerts(ctx, app, now); err != nil {
23+
if err := checkAndAlertForUnnoticedAlerts(ctx, app, publishAlert, now); err != nil {
2424
return err
2525
}
2626

@@ -35,9 +35,16 @@ func handleCloudwatchScheduledEvent(ctx context.Context, now time.Time) error {
3535
return nil
3636
}
3737

38+
type alertDirectPublisherFn func(amstate.Alert) error
39+
3840
// check for old unnoticed alerts (not acked within 4 hours) and send an alarm to notify the operator,
3941
// keep sending every hour
40-
func checkAndAlertForUnnoticedAlerts(ctx context.Context, app *amstate.App, now time.Time) error {
42+
func checkAndAlertForUnnoticedAlerts(
43+
ctx context.Context,
44+
app *amstate.App,
45+
alertDirectPublisher alertDirectPublisherFn,
46+
now time.Time,
47+
) error {
4148
unnoticedAlerts := amstate.GetUnnoticedAlerts(app.State.ActiveAlerts(), now)
4249

4350
unnoticedAlertSubjects := []string{}
@@ -56,7 +63,7 @@ func checkAndAlertForUnnoticedAlerts(ctx context.Context, app *amstate.App, now
5663

5764
if err := app.Reader.TransactWrite(ctx, func() error {
5865
// only notify about unnoticed alerts once an hour
59-
if now.Sub(app.State.LastUnnoticedAlertsNotified()) >= 1*time.Hour {
66+
if now.Sub(app.State.LastUnnoticedAlertsNotified()) < 1*time.Hour {
6067
return errAlreadyNotified
6168
}
6269

@@ -79,7 +86,7 @@ func checkAndAlertForUnnoticedAlerts(ctx context.Context, app *amstate.App, now
7986
// skip ingestion to bypass rate limiting (this scheduled function is not invoked
8087
// too often) and deduplication. besides, we want to keep reminding the operator
8188
// to take care of this situation
82-
return publishAlert(amstate.Alert{
89+
return alertDirectPublisher(amstate.Alert{
8390
Subject: "Un-acked alerts",
8491
Details: details,
8592
Timestamp: now,

cmd/alertmanager/scheduled_test.go

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,103 @@
11
package main
22

33
import (
4+
"context"
5+
"encoding/json"
46
"fmt"
7+
"github.com/function61/eventhorizon/pkg/ehevent"
8+
"github.com/function61/eventhorizon/pkg/ehreader"
9+
"github.com/function61/eventhorizon/pkg/ehreader/ehreadertest"
510
"github.com/function61/gokit/assert"
11+
"github.com/function61/lambda-alertmanager/pkg/amdomain"
12+
"github.com/function61/lambda-alertmanager/pkg/amstate"
13+
"os"
614
"testing"
715
"time"
816
)
917

10-
func TestParseTtlSpec(t *testing.T) {
11-
now := time.Date(2019, 9, 7, 12, 0, 0, 0, time.UTC)
18+
var t0 = time.Date(2019, 9, 7, 12, 0, 0, 0, time.UTC)
19+
20+
func TestCheckAndAlertForUnnoticedAlerts(t *testing.T) {
21+
ctx := context.Background()
22+
23+
testStreamName := "/t-42/alertmanager"
24+
25+
// have an alert raised at T+0
26+
eventLog := ehreadertest.NewEventLog()
27+
eventLog.AppendE(
28+
testStreamName,
29+
amdomain.NewAlertRaised(
30+
"a14308bba82f",
31+
"The building is on fire",
32+
"Fire sensor in room 456 went off",
33+
ehevent.MetaSystemUser(t0)))
34+
35+
app, err := amstate.LoadUntilRealtime(
36+
ctx,
37+
ehreader.NewTenantCtxWithSnapshots(
38+
ehreader.TenantId("42"),
39+
eventLog,
40+
ehreader.NewInMemSnapshotStore()),
41+
nil)
42+
assert.Ok(t, err)
43+
44+
// lame hack to get ackLink() to produce URLs while in testing
45+
os.Setenv("API_ENDPOINT", "https://alertmanager.com/api")
46+
47+
unnoticedAlertAtT0Plus := func(plus time.Duration) string { // helper
48+
var publishedAlert *amstate.Alert
49+
50+
// capture maybe-published alert
51+
assert.Ok(t, checkAndAlertForUnnoticedAlerts(ctx, app, func(alert amstate.Alert) error {
52+
publishedAlert = &alert
1253

54+
return nil
55+
}, t0.Add(plus)))
56+
57+
publishedAlertAsJson, err := json.MarshalIndent(publishedAlert, "", " ")
58+
assert.Ok(t, err)
59+
60+
return string(publishedAlertAsJson)
61+
}
62+
63+
/* we want alert at 4 hour mark when alert is un-acked, then *every* hour again:
64+
65+
0:00 (an alert is raised)
66+
1:00
67+
2:00
68+
3:00
69+
4:00 => first "unnoticed" alert
70+
4:30
71+
5:00 => second "unnoticed" alert
72+
...
73+
*/
74+
assert.EqualString(t, unnoticedAlertAtT0Plus(0*time.Hour), "null")
75+
assert.EqualString(t, unnoticedAlertAtT0Plus(1*time.Hour), "null")
76+
assert.EqualString(t, unnoticedAlertAtT0Plus(2*time.Hour), "null")
77+
assert.EqualString(t, unnoticedAlertAtT0Plus(3*time.Hour), "null")
78+
assert.EqualString(t, unnoticedAlertAtT0Plus(4*time.Hour), `{
79+
"alert_key": "",
80+
"subject": "Un-acked alerts",
81+
"details": "There are 1 un-acked alert(s):\n\nThe building is on fire https://alertmanager.com/api/alerts/acknowledge?id=a14308bba82f\n\nGo take care of them!",
82+
"timestamp": "2019-09-07T16:00:00Z"
83+
}`)
84+
assert.EqualString(t, unnoticedAlertAtT0Plus(4*time.Hour+30*time.Minute), "null")
85+
assert.EqualString(t, unnoticedAlertAtT0Plus(5*time.Hour), `{
86+
"alert_key": "",
87+
"subject": "Un-acked alerts",
88+
"details": "There are 1 un-acked alert(s):\n\nThe building is on fire https://alertmanager.com/api/alerts/acknowledge?id=a14308bba82f\n\nGo take care of them!",
89+
"timestamp": "2019-09-07T17:00:00Z"
90+
}`)
91+
assert.EqualString(t, unnoticedAlertAtT0Plus(5*time.Hour+30*time.Minute), "null")
92+
assert.EqualString(t, unnoticedAlertAtT0Plus(6*time.Hour), `{
93+
"alert_key": "",
94+
"subject": "Un-acked alerts",
95+
"details": "There are 1 un-acked alert(s):\n\nThe building is on fire https://alertmanager.com/api/alerts/acknowledge?id=a14308bba82f\n\nGo take care of them!",
96+
"timestamp": "2019-09-07T18:00:00Z"
97+
}`)
98+
}
99+
100+
func TestParseTtlSpec(t *testing.T) {
13101
tcs := []struct {
14102
input string
15103
output string
@@ -51,7 +139,7 @@ func TestParseTtlSpec(t *testing.T) {
51139
for _, tc := range tcs {
52140
tc := tc // pin
53141
t.Run(tc.input, func(t *testing.T) {
54-
ttl, err := parseTtlSpec(tc.input, now)
142+
ttl, err := parseTtlSpec(tc.input, t0)
55143
var actual string
56144
if err != nil {
57145
actual = fmt.Sprintf("error: %v", err)

0 commit comments

Comments
 (0)