From 38cdec790167eac2ffb331b5ee73ed20bf19ad7a Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Wed, 11 Sep 2024 17:15:31 +0545 Subject: [PATCH] chore: use datemath for notification silence --- db/notifications.go | 2 +- go.mod | 2 +- go.sum | 4 +- notification/controllers.go | 5 +- notification/silence.go | 55 +++++++++++++--------- notification/silence_test.go | 90 ++++++++++++++++++++++++++++++++++++ rbac/policy.go | 18 ++++---- 7 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 notification/silence_test.go diff --git a/db/notifications.go b/db/notifications.go index 6b0846d44..6ca642a96 100644 --- a/db/notifications.go +++ b/db/notifications.go @@ -117,7 +117,7 @@ func NotificationSendSummary(ctx context.Context, id string, window time.Duratio func GetMatchingNotificationSilencesCount(ctx context.Context, resources models.NotificationSilenceResource) (int64, error) { _ = ctx.DB().Use(extraClausePlugin.New()) - query := ctx.DB().Debug().Model(&models.NotificationSilence{}) + query := ctx.DB().Model(&models.NotificationSilence{}) // Initialize with a false condition, // if no resources are provided, the query won't return all records diff --git a/go.mod b/go.mod index de8dffbc2..a69efc1b8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/containrrr/shoutrrr v0.8.0 github.com/fergusstrange/embedded-postgres v1.25.0 // indirect github.com/flanksource/commons v1.29.10 - github.com/flanksource/duty v1.0.634 + github.com/flanksource/duty v1.0.637 github.com/flanksource/gomplate/v3 v3.24.30 github.com/flanksource/kopper v1.0.9 github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 diff --git a/go.sum b/go.sum index 76fb8b7a3..a14a95155 100644 --- a/go.sum +++ b/go.sum @@ -877,8 +877,8 @@ github.com/flanksource/artifacts v1.0.14 h1:Vv70bccsae0MwGaf/uSPp34J5V1/PyKfct9z github.com/flanksource/artifacts v1.0.14/go.mod h1:qHVCnQu5k50aWNJ5UhpcAKEl7pAzqUrFFKGSm147G70= github.com/flanksource/commons v1.29.10 h1:T/S95Pl8kASEFvQjQ7fJjTUqeVdhxQXg1vfkULTYFJQ= github.com/flanksource/commons v1.29.10/go.mod h1:iTbrXOSp3Spv570Nly97D/U9cQjLZoVlmWCXqWzsvRU= -github.com/flanksource/duty v1.0.634 h1:plZxB4f9nSmR/57G06ML9lwYbAJPmdpQnFmbQuUvaVs= -github.com/flanksource/duty v1.0.634/go.mod h1:Oj9zIX94CR2U+nmnt97gNLMrsBWILyIhIBeJynIIgqE= +github.com/flanksource/duty v1.0.637 h1:VmJJxSNyyyJ84KJOi1BtHN7zIldad7mZk/PD6Pf6J+o= +github.com/flanksource/duty v1.0.637/go.mod h1:Oj9zIX94CR2U+nmnt97gNLMrsBWILyIhIBeJynIIgqE= github.com/flanksource/gomplate/v3 v3.20.4/go.mod h1:27BNWhzzSjDed1z8YShO6W+z6G9oZXuxfNFGd/iGSdc= github.com/flanksource/gomplate/v3 v3.24.30 h1:6Y25KnAMX4iCuEXe1C8d1kB2PdV+zD1ZulZrv6DV14Q= github.com/flanksource/gomplate/v3 v3.24.30/go.mod h1:/lAM7+fkcCCfghCAjzdCqwgWxN5Ow8Sk6nkdFPjejCE= diff --git a/notification/controllers.go b/notification/controllers.go index b29d183aa..7c30e1cf3 100644 --- a/notification/controllers.go +++ b/notification/controllers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "net/http" + "github.com/flanksource/duty/api" "github.com/flanksource/duty/context" echoSrv "github.com/flanksource/incident-commander/echo" "github.com/flanksource/incident-commander/rbac" @@ -30,9 +31,9 @@ func RegisterRoutes(e *echo.Echo) { } if err := SaveNotificationSilence(ctx, req); err != nil { - return err + return api.WriteError(c, err) } return nil - }, rbac.Authorization(rbac.ObjectNotificationSilence, rbac.ActionCreate)) + }, rbac.Authorization(rbac.ObjectNotification, rbac.ActionCreate)) } diff --git a/notification/silence.go b/notification/silence.go index 975da177e..66b0d9e54 100644 --- a/notification/silence.go +++ b/notification/silence.go @@ -4,43 +4,51 @@ import ( "errors" "time" - "github.com/flanksource/commons/duration" + "github.com/flanksource/duty/api" "github.com/flanksource/duty/context" + "github.com/flanksource/duty/db" "github.com/flanksource/duty/models" "github.com/samber/lo" + "github.com/timberio/go-datemath" ) type SilenceSaveRequest struct { models.NotificationSilenceResource - From time.Time `json:"from"` - Until time.Time `json:"until"` - Duration string `json:"duration"` - Description string `json:"description"` + From string `json:"from"` + Until string `json:"until"` + Description string `json:"description"` + Recursive bool `json:"recursive"` + + from time.Time + until time.Time } func (t *SilenceSaveRequest) Validate() error { - if t.From.IsZero() { + if t.From == "" { return errors.New("`from` time is required") } - if t.Until.IsZero() { - if t.Duration == "" { - return errors.New("`until` or `duration` is required") - } + if t.Until == "" { + return errors.New("`until` is required") + } - if parsed, err := duration.ParseDuration(t.Duration); err != nil { - return err - } else { - t.Until = t.From.Add(time.Duration(parsed)) - } + if parsedTime, err := datemath.ParseAndEvaluate(t.From); err != nil { + return err + } else { + t.from = parsedTime } - if t.From.After(t.Until) { - return errors.New("`from` time must be before `until` time") + if parsedTime, err := datemath.ParseAndEvaluate(t.Until); err != nil { + return err + } else { + t.until = parsedTime + } + + if t.from.After(t.until) { + return errors.New("`from` time must be before `until") } - if t.NotificationSilenceResource.CanaryID == nil && t.NotificationSilenceResource.CheckID == nil && t.NotificationSilenceResource.ConfigID == nil && - t.NotificationSilenceResource.ComponentID == nil { + if t.NotificationSilenceResource.Empty() { return errors.New("at least one of `config_id`, `canary_id`, `check_id` or `component_id` is required") } @@ -49,19 +57,20 @@ func (t *SilenceSaveRequest) Validate() error { func SaveNotificationSilence(ctx context.Context, req SilenceSaveRequest) error { if err := req.Validate(); err != nil { - return err + return api.Errorf(api.EINVALID, err.Error()) } silence := models.NotificationSilence{ NotificationSilenceResource: req.NotificationSilenceResource, - From: req.From, - Until: req.Until, + From: req.from, + Until: req.until, Description: req.Description, + Recursive: req.Recursive, Source: models.SourceUI, CreatedBy: lo.ToPtr(ctx.User().ID), } - return ctx.DB().Create(&silence).Error + return db.ErrorDetails(ctx.DB().Create(&silence).Error) } func getSilencedResourceFromCelEnv(celEnv map[string]any) models.NotificationSilenceResource { diff --git a/notification/silence_test.go b/notification/silence_test.go new file mode 100644 index 000000000..78389a954 --- /dev/null +++ b/notification/silence_test.go @@ -0,0 +1,90 @@ +package notification + +import ( + "testing" + "time" + + "github.com/flanksource/duty/models" + "github.com/google/uuid" + "github.com/samber/lo" +) + +func TestSilenceSaveRequest_Validate(t *testing.T) { + type fields struct { + NotificationSilenceResource models.NotificationSilenceResource + From string + Until string + Description string + from time.Time + until time.Time + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "empty from", + fields: fields{ + NotificationSilenceResource: models.NotificationSilenceResource{}, + From: "", + Until: "now+2d", + }, + wantErr: true, + }, + { + name: "empty until", + fields: fields{ + NotificationSilenceResource: models.NotificationSilenceResource{}, + From: "now", + Until: "", + }, + wantErr: true, + }, + { + name: "empty resource", + fields: fields{ + NotificationSilenceResource: models.NotificationSilenceResource{}, + From: "now", + Until: "now+2d", + }, + wantErr: true, + }, + { + name: "valid", + fields: fields{ + NotificationSilenceResource: models.NotificationSilenceResource{ + ConfigID: lo.ToPtr(uuid.NewString()), + }, + From: "now", + Until: "now+2d", + }, + }, + { + name: "complete but invalid", + fields: fields{ + NotificationSilenceResource: models.NotificationSilenceResource{ + ConfigID: lo.ToPtr(uuid.NewString()), + }, + From: "now", + Until: "now-1m", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &SilenceSaveRequest{ + NotificationSilenceResource: tt.fields.NotificationSilenceResource, + From: tt.fields.From, + Until: tt.fields.Until, + Description: tt.fields.Description, + from: tt.fields.from, + until: tt.fields.until, + } + if err := tr.Validate(); (err != nil) != tt.wantErr { + t.Errorf("SilenceSaveRequest.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/rbac/policy.go b/rbac/policy.go index 3a9900b38..6113c4734 100644 --- a/rbac/policy.go +++ b/rbac/policy.go @@ -147,15 +147,15 @@ const ( RoleAgent = "agent" // Actions - ActionRead = "read" - ActionUpdate = "update" - ActionCreate = "create" - ActionDelete = "delete" - ActionRun = "run" - ActionApprove = "approve" - ActionAll = "*" - ActionCRUD = "create,read,update,delete" - + ActionRead = "read" + ActionUpdate = "update" + ActionCreate = "create" + ActionDelete = "delete" + ActionRun = "run" + ActionApprove = "approve" + ActionAll = "*" + ActionCRUD = "create,read,update,delete" + ObjectKubernetesProxy = "kubernetes-proxy" // Objects ObjectLogs = "logs" ObjectAgent = "agent"