mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
Rule alerts go to the rule's own org
SendAlerts addressed every alert to `SELECT id FROM organizations LIMIT 1`, which is whichever org sorts first, not the rule's. That org's alertmanager has none of the rule's channels or route policies, so the alert sat on its default receiver and reached nobody, and that org's members could see it. Alerts now carry the rule's own org id. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -358,21 +358,10 @@ func (r *BaseRule) ActiveAlerts() []*ruletypes.Alert {
|
||||
return res
|
||||
}
|
||||
|
||||
// SendAlerts hands the rule's alerts that are due to notifyFunc, addressed to
|
||||
// the rule's own org: its alertmanager holds the channels and route policies
|
||||
// the rule's thresholds name.
|
||||
func (r *BaseRule) SendAlerts(ctx context.Context, ts time.Time, resendDelay time.Duration, interval time.Duration, notifyFunc NotifyFunc) {
|
||||
var orgID string
|
||||
err := r.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
NewSelect().
|
||||
Table("organizations").
|
||||
ColumnExpr("id").
|
||||
Limit(1).
|
||||
Scan(ctx, &orgID)
|
||||
if err != nil {
|
||||
r.logger.ErrorContext(ctx, "failed to get org ids", errors.Attr(err))
|
||||
return
|
||||
}
|
||||
|
||||
alerts := []*ruletypes.Alert{}
|
||||
r.ForEachActiveAlert(func(alert *ruletypes.Alert) {
|
||||
if alert.NeedsSending(ts, resendDelay) {
|
||||
@@ -386,7 +375,7 @@ func (r *BaseRule) SendAlerts(ctx context.Context, ts time.Time, resendDelay tim
|
||||
alerts = append(alerts, &anew)
|
||||
}
|
||||
})
|
||||
notifyFunc(ctx, orgID, alerts...)
|
||||
notifyFunc(ctx, r.orgID.StringValue(), alerts...)
|
||||
}
|
||||
|
||||
// withTenant scopes ctx to the rule's org when req reads event.log or
|
||||
|
||||
@@ -14,8 +14,6 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore/sqlstoretest"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore/telemetrystoretest"
|
||||
"github.com/hanzoai/o11y/pkg/types/alertmanagertypes"
|
||||
@@ -59,14 +57,6 @@ func TestManager_TestNotification_SendUnmatched_ThresholdRule(t *testing.T) {
|
||||
}).Return(nil).Times(tc.ExpectAlerts)
|
||||
}
|
||||
},
|
||||
SqlStoreHook: func(store sqlstore.SQLStore) {
|
||||
mockStore := store.(*sqlstoretest.Provider)
|
||||
// Mock the organizations query that SendAlerts makes
|
||||
// Bun generates: SELECT id FROM organizations LIMIT 1 (or SELECT "id" FROM "organizations" LIMIT 1)
|
||||
orgRows := mockStore.Mock().NewRows([]string{"id"}).AddRow(orgID.StringValue())
|
||||
// Match bun's generated query pattern - bun may quote identifiers
|
||||
mockStore.Mock().ExpectQuery("SELECT (.+) FROM (.+)organizations(.+) LIMIT (.+)").WillReturnRows(orgRows)
|
||||
},
|
||||
TelemetryStoreHook: func(store telemetrystore.TelemetryStore) {
|
||||
mockStore := store.(*telemetrystoretest.Provider)
|
||||
// Set up mock data for telemetry store
|
||||
@@ -172,12 +162,6 @@ func TestManager_TestNotification_SendUnmatched_PromRule(t *testing.T) {
|
||||
}).Return(nil).Times(tc.ExpectAlerts)
|
||||
}
|
||||
},
|
||||
SqlStoreHook: func(store sqlstore.SQLStore) {
|
||||
mockStore := store.(*sqlstoretest.Provider)
|
||||
// Mock the organizations query that SendAlerts makes
|
||||
orgRows := mockStore.Mock().NewRows([]string{"id"}).AddRow(orgID.StringValue())
|
||||
mockStore.Mock().ExpectQuery("SELECT (.+) FROM (.+)organizations(.+) LIMIT (.+)").WillReturnRows(orgRows)
|
||||
},
|
||||
TelemetryStoreHook: func(store telemetrystore.TelemetryStore) {
|
||||
mockStore := store.(*telemetrystoretest.Provider)
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/types/ruletypes"
|
||||
"github.com/hanzoai/o11y/pkg/valuer"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// A rule's alerts are addressed to the rule's own org, whichever org the
|
||||
// organizations table happens to list first.
|
||||
func TestSendAlertsAddressesTheRulesOrg(t *testing.T) {
|
||||
orgID := valuer.GenerateUUID()
|
||||
now := time.Now()
|
||||
r := &BaseRule{
|
||||
id: "rule-1",
|
||||
orgID: orgID,
|
||||
logger: instrumentationtest.New().Logger(),
|
||||
Active: map[uint64]*ruletypes.Alert{
|
||||
1: {State: ruletypes.StateFiring, FiredAt: now.Add(-time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
var got string
|
||||
var sent int
|
||||
r.SendAlerts(context.Background(), now, time.Minute, time.Minute, func(_ context.Context, org string, alerts ...*ruletypes.Alert) {
|
||||
got, sent = org, len(alerts)
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sent)
|
||||
require.Equal(t, orgID.StringValue(), got)
|
||||
}
|
||||
Reference in New Issue
Block a user