Skip to content

Commit

Permalink
Merge pull request #958 from SiaFoundation/chris/dismiss-all-alerts
Browse files Browse the repository at this point in the history
Add pagination to alerts endpoint and add another endpoint to dismiss all alerts at once
  • Loading branch information
ChrisSchinnerl authored Feb 16, 2024
2 parents 074cbef + b22e96e commit 025df16
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
34 changes: 33 additions & 1 deletion alerts/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type (
Alerter interface {
RegisterAlert(_ context.Context, a Alert) error
DismissAlerts(_ context.Context, ids ...types.Hash256) error
DismissAllAlerts(_ context.Context) error
}

// Severity indicates the severity of an alert.
Expand All @@ -63,6 +64,11 @@ type (
alerts map[types.Hash256]Alert
webhookBroadcaster webhooks.Broadcaster
}

AlertsOpts struct {
Offset int
Limit int
}
)

// String implements the fmt.Stringer interface.
Expand Down Expand Up @@ -130,6 +136,17 @@ func (m *Manager) RegisterAlert(ctx context.Context, alert Alert) error {
})
}

// DismissAllAlerts implements the Alerter interface.
func (m *Manager) DismissAllAlerts(ctx context.Context) error {
m.mu.Lock()
toDismiss := make([]types.Hash256, 0, len(m.alerts))
for alertID := range m.alerts {
toDismiss = append(toDismiss, alertID)
}
m.mu.Unlock()
return m.DismissAlerts(ctx, toDismiss...)
}

// DismissAlerts implements the Alerter interface.
func (m *Manager) DismissAlerts(ctx context.Context, ids ...types.Hash256) error {
var dismissed []types.Hash256
Expand Down Expand Up @@ -159,17 +176,27 @@ func (m *Manager) DismissAlerts(ctx context.Context, ids ...types.Hash256) error
}

// Active returns the host's active alerts.
func (m *Manager) Active() []Alert {
func (m *Manager) Active(offset, limit int) []Alert {
m.mu.Lock()
defer m.mu.Unlock()

if offset >= len(m.alerts) {
return nil
} else if limit == -1 {
limit = len(m.alerts)
}

alerts := make([]Alert, 0, len(m.alerts))
for _, a := range m.alerts {
alerts = append(alerts, a)
}
sort.Slice(alerts, func(i, j int) bool {
return alerts[i].Timestamp.After(alerts[j].Timestamp)
})
alerts = alerts[offset:]
if limit < len(alerts) {
alerts = alerts[:limit]
}
return alerts
}

Expand Down Expand Up @@ -213,6 +240,11 @@ func (a *originAlerter) RegisterAlert(ctx context.Context, alert Alert) error {
return a.alerter.RegisterAlert(ctx, alert)
}

// DismissAllAlerts implements the Alerter interface.
func (a *originAlerter) DismissAllAlerts(ctx context.Context) error {
return a.alerter.DismissAllAlerts(ctx)
}

// DismissAlerts implements the Alerter interface.
func (a *originAlerter) DismissAlerts(ctx context.Context, ids ...types.Hash256) error {
return a.alerter.DismissAlerts(ctx, ids...)
Expand Down
24 changes: 19 additions & 5 deletions bus/bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,10 @@ func (b *bus) Handler() http.Handler {
"POST /account/:id/requiressync": b.accountsRequiresSyncHandlerPOST,
"POST /account/:id/resetdrift": b.accountsResetDriftHandlerPOST,

"GET /alerts": b.handleGETAlerts,
"POST /alerts/dismiss": b.handlePOSTAlertsDismiss,
"POST /alerts/register": b.handlePOSTAlertsRegister,
"GET /alerts": b.handleGETAlerts,
"POST /alerts/dismiss": b.handlePOSTAlertsDismiss,
"POST /alerts/dismissall": b.handlePOSTAlertsDismissAll,
"POST /alerts/register": b.handlePOSTAlertsRegister,

"GET /autopilots": b.autopilotsListHandlerGET,
"GET /autopilot/:id": b.autopilotsHandlerGET,
Expand Down Expand Up @@ -1715,8 +1716,17 @@ func (b *bus) gougingParams(ctx context.Context) (api.GougingParams, error) {
}, nil
}

func (b *bus) handleGETAlerts(c jape.Context) {
c.Encode(b.alertMgr.Active())
func (b *bus) handleGETAlerts(jc jape.Context) {
offset, limit := 0, -1
if jc.DecodeForm("offset", &offset) != nil {
return
} else if jc.DecodeForm("limit", &limit) != nil {
return
} else if offset < 0 {
jc.Error(errors.New("offset must be non-negative"), http.StatusBadRequest)
return
}
jc.Encode(b.alertMgr.Active(offset, limit))
}

func (b *bus) handlePOSTAlertsDismiss(jc jape.Context) {
Expand All @@ -1727,6 +1737,10 @@ func (b *bus) handlePOSTAlertsDismiss(jc jape.Context) {
jc.Check("failed to dismiss alerts", b.alertMgr.DismissAlerts(jc.Request.Context(), ids...))
}

func (b *bus) handlePOSTAlertsDismissAll(jc jape.Context) {
jc.Check("failed to dismiss alerts", b.alertMgr.DismissAllAlerts(jc.Request.Context()))
}

func (b *bus) handlePOSTAlertsRegister(jc jape.Context) {
var alert alerts.Alert
if jc.Decode(&alert) != nil {
Expand Down
18 changes: 16 additions & 2 deletions bus/client/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@ package client

import (
"context"
"fmt"
"net/url"

"go.sia.tech/core/types"
"go.sia.tech/renterd/alerts"
)

// Alerts fetches the active alerts from the bus.
func (c *Client) Alerts() (alerts []alerts.Alert, err error) {
err = c.c.GET("/alerts", &alerts)
func (c *Client) Alerts(opts alerts.AlertsOpts) (alerts []alerts.Alert, err error) {
values := url.Values{}
if opts.Offset != 0 {
values.Set("offset", fmt.Sprint(opts.Offset))
}
if opts.Limit != 0 {
values.Set("limit", fmt.Sprint(opts.Limit))
}
err = c.c.GET("/alerts?"+values.Encode(), &alerts)
return
}

// DismissAllAlerts dimisses all alerts.
func (c *Client) DismissAllAlerts(ctx context.Context) error {
return c.c.WithContext(ctx).POST("/alerts/dismissall", nil, nil)
}

// DismissAlerts dimisses the alerts with the given IDs.
func (c *Client) DismissAlerts(ctx context.Context, ids ...types.Hash256) error {
return c.c.WithContext(ctx).POST("/alerts/dismiss", ids, nil)
Expand Down
36 changes: 35 additions & 1 deletion internal/testing/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1920,7 +1920,7 @@ func TestAlerts(t *testing.T) {
tt.OK(b.RegisterAlert(context.Background(), alert))
findAlert := func(id types.Hash256) *alerts.Alert {
t.Helper()
alerts, err := b.Alerts()
alerts, err := b.Alerts(alerts.AlertsOpts{})
tt.OK(err)
for _, alert := range alerts {
if alert.ID == id {
Expand All @@ -1943,6 +1943,40 @@ func TestAlerts(t *testing.T) {
if foundAlert != nil {
t.Fatal("alert found")
}

// register 2 alerts
alert2 := alert
alert2.ID = frand.Entropy256()
alert2.Timestamp = time.Now().Add(time.Second)
tt.OK(b.RegisterAlert(context.Background(), alert))
tt.OK(b.RegisterAlert(context.Background(), alert2))
if foundAlert := findAlert(alert.ID); foundAlert == nil {
t.Fatal("alert not found")
} else if foundAlert := findAlert(alert2.ID); foundAlert == nil {
t.Fatal("alert not found")
}

// try to find with offset = 1
foundAlerts, err := b.Alerts(alerts.AlertsOpts{Offset: 1})
tt.OK(err)
if len(foundAlerts) != 1 || foundAlerts[0].ID != alert.ID {
t.Fatal("wrong alert")
}

// try to find with limit = 1
foundAlerts, err = b.Alerts(alerts.AlertsOpts{Limit: 1})
tt.OK(err)
if len(foundAlerts) != 1 || foundAlerts[0].ID != alert2.ID {
t.Fatal("wrong alert")
}

// dismiss all
tt.OK(b.DismissAllAlerts(context.Background()))
foundAlerts, err = b.Alerts(alerts.AlertsOpts{})
tt.OK(err)
if len(foundAlerts) != 0 {
t.Fatal("expected 0 alerts", len(foundAlerts))
}
}

func TestMultipartUploads(t *testing.T) {
Expand Down

0 comments on commit 025df16

Please sign in to comment.