Skip to content

Commit

Permalink
feat: support recursive option & add tests
Browse files Browse the repository at this point in the history
[skip ci]
  • Loading branch information
adityathebe committed Sep 9, 2024
1 parent 70d117b commit 70e1ddb
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 5 deletions.
29 changes: 24 additions & 5 deletions db/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"time"

extraClausePlugin "github.com/WinterYukky/gorm-extra-clause-plugin"
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
"github.com/flanksource/duty/context"
"github.com/flanksource/duty/models"
"github.com/flanksource/duty/query"
Expand All @@ -27,6 +29,7 @@ func PersistNotificationSilenceFromCRD(ctx context.Context, obj *v1.Notification
From: obj.Spec.From.Time,
Until: obj.Spec.Until.Time,
Source: models.SourceCRD,
Recursive: obj.Spec.Recursive,
NotificationSilenceResource: models.NotificationSilenceResource{
ConfigID: obj.Spec.ConfigID,
ComponentID: obj.Spec.ComponentID,
Expand Down Expand Up @@ -141,21 +144,36 @@ func NotificationSendSummary(ctx context.Context, id string, window time.Duratio
}

func GetMatchingNotificationSilencesCount(ctx context.Context, resources models.NotificationSilenceResource) (int64, error) {
query := ctx.DB().Model(&models.NotificationSilence{}).
Where(`"from" <= NOW()`).
Where("until >= NOW()").
Where("deleted_at IS NULL")
_ = ctx.DB().Use(extraClausePlugin.New())

query := ctx.DB().Debug().Model(&models.NotificationSilence{})

// Initialize with a false condition,
// if no resources are provided, the query won't return all records
orClauses := ctx.DB().Where("1 = 0")

if resources.ConfigID != nil {
orClauses = orClauses.Or("config_id = ?", *resources.ConfigID)

// recursive stuff
orClauses = orClauses.Or("(recursive = true AND path_cte.path LIKE '%' || config_id::TEXT || '%')")
query = query.Clauses(exclause.NewWith(
"path_cte",
ctx.DB().Select("path").Model(&models.ConfigItem{}).Where("id = ?", *resources.ConfigID),
))
query = query.Joins("CROSS JOIN path_cte")
}

if resources.ComponentID != nil {
orClauses = orClauses.Or("component_id = ?", *resources.ComponentID)

// recursive stuff
orClauses = orClauses.Or("(recursive = true AND path_cte.path LIKE '%' || component_id::TEXT || '%')")
query = query.Clauses(exclause.NewWith(
"path_cte",
ctx.DB().Select("path").Model(&models.Component{}).Where("id = ?", *resources.ComponentID),
))
query = query.Joins("CROSS JOIN path_cte")
}

if resources.CanaryID != nil {
Expand All @@ -165,10 +183,11 @@ func GetMatchingNotificationSilencesCount(ctx context.Context, resources models.
if resources.CheckID != nil {
orClauses = orClauses.Or("check_id = ?", *resources.CheckID)
}

query = query.Where(orClauses)

var count int64
err := query.Count(&count).Error
err := query.Count(&count).Where(`"from" <= NOW()`).Where("until >= NOW()").Where("deleted_at IS NULL").Error
if err != nil {
return 0, err
}
Expand Down
106 changes: 106 additions & 0 deletions db/notifications_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package db

import (
"time"

"github.com/flanksource/duty/models"
"github.com/flanksource/duty/tests/fixtures/dummy"
"github.com/google/uuid"
"github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/samber/lo"
)

var _ = ginkgo.Describe("Notification Silence", ginkgo.Ordered, func() {
var silences []models.NotificationSilence
ginkgo.BeforeAll(func() {
silences = []models.NotificationSilence{
{
ID: uuid.New(),
From: time.Now().Add(-time.Hour),
Until: time.Now().Add(time.Hour),
Source: models.SourceCRD,
NotificationSilenceResource: models.NotificationSilenceResource{
ConfigID: lo.ToPtr(dummy.EKSCluster.ID.String()),
},
},
{
ID: uuid.New(),
From: time.Now().Add(-time.Hour),
Until: time.Now().Add(time.Hour),
Source: models.SourceCRD,
Recursive: true,
NotificationSilenceResource: models.NotificationSilenceResource{
ConfigID: lo.ToPtr(dummy.LogisticsAPIDeployment.ID.String()),
},
},
{
ID: uuid.New(),
From: time.Now().Add(-time.Hour),
Until: time.Now().Add(time.Hour),
Source: models.SourceCRD,
Recursive: true,
NotificationSilenceResource: models.NotificationSilenceResource{
ComponentID: lo.ToPtr(dummy.Logistics.ID.String()),
},
},
}

err := DefaultContext.DB().Create(&silences).Error
Expect(err).To(BeNil())
})

ginkgo.Context("non recursive match", func() {
ginkgo.It("should match", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ConfigID: lo.ToPtr(dummy.EKSCluster.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(1)))
})

ginkgo.It("should not match", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ConfigID: lo.ToPtr(dummy.KubernetesCluster.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(0)))
})
})

ginkgo.Context("config recursive match", func() {
ginkgo.It("should match a child", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ConfigID: lo.ToPtr(dummy.LogisticsAPIReplicaSet.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(1)))
})

ginkgo.It("should match a grand child", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ConfigID: lo.ToPtr(dummy.LogisticsAPIPodConfig.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(1)))
})

ginkgo.It("should not match", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ConfigID: lo.ToPtr(dummy.LogisticsUIDeployment.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(0)))
})
})

ginkgo.Context("component recursive match", func() {
ginkgo.It("should match a child", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ComponentID: lo.ToPtr(dummy.LogisticsAPI.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(1)))
})

ginkgo.It("should match a grand child", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ComponentID: lo.ToPtr(dummy.LogisticsWorker.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(1)))
})

ginkgo.It("should not match", func() {
matched, err := GetMatchingNotificationSilencesCount(DefaultContext, models.NotificationSilenceResource{ComponentID: lo.ToPtr(dummy.ClusterComponent.ID.String())})
Expect(err).To(BeNil())
Expect(matched).To(Equal(int64(0)))
})
})
})
12 changes: 12 additions & 0 deletions db/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package db
import (
"testing"

"github.com/flanksource/duty/context"
"github.com/flanksource/duty/tests/setup"
ginkgo "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -11,3 +13,13 @@ func TestDB(t *testing.T) {
RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "DB")
}

var (
DefaultContext context.Context
)

var _ = ginkgo.BeforeSuite(func() {
DefaultContext = setup.BeforeSuiteFn()
})

var _ = ginkgo.AfterSuite(setup.AfterSuiteFn)

0 comments on commit 70e1ddb

Please sign in to comment.