Skip to content

Commit

Permalink
feat(observability-lib): builder to create independently grafana reso…
Browse files Browse the repository at this point in the history
…urces (#915)

* feat(observability-lib): builder to create independently grafana resources

* fix(observability-lib): notification policy matchers check
Atrax1 authored Nov 5, 2024

Verified

This commit was signed with the committer’s verified signature. The key has expired.
splattner Sebastian Plattner
1 parent e1b7c81 commit f1c901f
Showing 19 changed files with 287 additions and 120 deletions.
23 changes: 22 additions & 1 deletion observability-lib/api/notification-policy.go
Original file line number Diff line number Diff line change
@@ -8,11 +8,32 @@ import (
"github.com/grafana/grafana-foundation-sdk/go/alerting"
)

func objectMatchersEqual(a alerting.ObjectMatchers, b alerting.ObjectMatchers) bool {
if len(a) != len(b) {
return false
}

for i := range a {
foundMatch := false
for j := range b {
if reflect.DeepEqual(a[i], b[j]) {
foundMatch = true
break
}
}
if !foundMatch {
return false
}
}

return true
}

func policyExist(parent alerting.NotificationPolicy, newNotificationPolicy alerting.NotificationPolicy) bool {
for _, notificationPolicy := range parent.Routes {
matchersEqual := false
if notificationPolicy.ObjectMatchers != nil {
matchersEqual = reflect.DeepEqual(notificationPolicy.ObjectMatchers, newNotificationPolicy.ObjectMatchers)
matchersEqual = objectMatchersEqual(*notificationPolicy.ObjectMatchers, *newNotificationPolicy.ObjectMatchers)
}
receiversEqual := reflect.DeepEqual(notificationPolicy.Receiver, newNotificationPolicy.Receiver)
if matchersEqual && receiversEqual {
46 changes: 46 additions & 0 deletions observability-lib/api/notification-policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package api

import (
"testing"

"github.com/grafana/grafana-foundation-sdk/go/alerting"
"github.com/stretchr/testify/require"
)

func TestObjectMatchersEqual(t *testing.T) {
t.Run("returns true if the two object matchers are equal", func(t *testing.T) {
a := alerting.ObjectMatchers{{"team", "=", "chainlink"}}
b := alerting.ObjectMatchers{{"team", "=", "chainlink"}}

result := objectMatchersEqual(a, b)
require.True(t, result)
})

t.Run("returns true if the two object matchers with multiple matches are equal", func(t *testing.T) {
a := alerting.ObjectMatchers{
{"team", "=", "chainlink"},
{"severity", "=", "critical"},
}
b := alerting.ObjectMatchers{
{"severity", "=", "critical"},
{"team", "=", "chainlink"},
}

result := objectMatchersEqual(a, b)
require.True(t, result)
})

t.Run("returns false if the two object matchers with multiple matches are different", func(t *testing.T) {
a := alerting.ObjectMatchers{
{"team", "=", "chainlink"},
{"severity", "=", "critical"},
}
b := alerting.ObjectMatchers{
{"severity", "=", "warning"},
{"team", "=", "chainlink"},
}

result := objectMatchersEqual(a, b)
require.False(t, result)
})
}
2 changes: 1 addition & 1 deletion observability-lib/cmd/builder.go
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ type BuildOptions struct {
AlertsFilters string
}

func BuildDashboardWithType(options *BuildOptions) (*grafana.Dashboard, error) {
func BuildDashboardWithType(options *BuildOptions) (*grafana.Observability, error) {
switch options.TypeDashboard {
case TypeDashboardCoreNode:
return corenode.NewDashboard(&corenode.Props{
2 changes: 1 addition & 1 deletion observability-lib/dashboards/atlas-don/component.go
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import (
"github.com/smartcontractkit/chainlink-common/observability-lib/grafana"
)

func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
2 changes: 1 addition & 1 deletion observability-lib/dashboards/atlas-don/component_test.go
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "DON OCR Dashboard", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
2 changes: 1 addition & 1 deletion observability-lib/dashboards/capabilities/component.go
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ type Props struct {
}

// NewDashboard creates a Capabilities dashboard
func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "Capabilities Dashboard", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import (
"github.com/smartcontractkit/chainlink-common/observability-lib/grafana"
)

func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "Core Node Components Dashboard", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
2 changes: 1 addition & 1 deletion observability-lib/dashboards/core-node/component.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import (
)

// NewDashboard creates a DON dashboard for the given OCR version
func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
2 changes: 1 addition & 1 deletion observability-lib/dashboards/core-node/component_test.go
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "Core Node Dashboard", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
2 changes: 1 addition & 1 deletion observability-lib/dashboards/k8s-resources/component.go
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ type Props struct {
MetricsDataSource *grafana.DataSource // MetricsDataSource is the datasource for querying metrics
}

func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "K8s resources", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
2 changes: 1 addition & 1 deletion observability-lib/dashboards/nop-ocr/component.go
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ type Props struct {
OCRVersion string // OCRVersion is the version of the OCR (ocr, ocr2, ocr3)
}

func NewDashboard(props *Props) (*grafana.Dashboard, error) {
func NewDashboard(props *Props) (*grafana.Observability, error) {
if props.Name == "" {
return nil, fmt.Errorf("Name is required")
}
2 changes: 1 addition & 1 deletion observability-lib/dashboards/nop-ocr/component_test.go
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ func TestNewDashboard(t *testing.T) {
if err != nil {
t.Errorf("Error creating dashboard: %v", err)
}
require.IsType(t, grafana.Dashboard{}, *testDashboard)
require.IsType(t, grafana.Observability{}, *testDashboard)
require.Equal(t, "NOP OCR Dashboard", *testDashboard.Dashboard.Title)
json, errJSON := testDashboard.GenerateJSON()
if errJSON != nil {
82 changes: 46 additions & 36 deletions observability-lib/grafana/builder.go
Original file line number Diff line number Diff line change
@@ -29,16 +29,23 @@ type BuilderOptions struct {
}

func NewBuilder(options *BuilderOptions) *Builder {
if options.TimeZone == "" {
options.TimeZone = common.TimeZoneBrowser
}
builder := &Builder{}

builder := &Builder{
dashboardBuilder: dashboard.NewDashboardBuilder(options.Name).
Tags(options.Tags).
Refresh(options.Refresh).
Time(options.TimeFrom, options.TimeTo).
Timezone(options.TimeZone),
if options.Name != "" {
builder.dashboardBuilder = dashboard.NewDashboardBuilder(options.Name)
if options.Tags != nil {
builder.dashboardBuilder.Tags(options.Tags)
}
if options.Refresh != "" {
builder.dashboardBuilder.Refresh(options.Refresh)
}
if options.TimeFrom != "" && options.TimeTo != "" {
builder.dashboardBuilder.Time(options.TimeFrom, options.TimeTo)
}
if options.TimeZone == "" {
options.TimeZone = common.TimeZoneBrowser
}
builder.dashboardBuilder.Timezone(options.TimeZone)
}

if options.AlertsTags != nil {
@@ -104,33 +111,39 @@ func (b *Builder) AddNotificationPolicy(notificationPolicies ...*alerting.Notifi
b.notificationPoliciesBuilder = append(b.notificationPoliciesBuilder, notificationPolicies...)
}

func (b *Builder) Build() (*Dashboard, error) {
db, errBuildDashboard := b.dashboardBuilder.Build()
if errBuildDashboard != nil {
return nil, errBuildDashboard
}
func (b *Builder) Build() (*Observability, error) {
observability := Observability{}

var alerts []alerting.Rule
for _, alertBuilder := range b.alertsBuilder {
alert, errBuildAlert := alertBuilder.Build()
if errBuildAlert != nil {
return nil, errBuildAlert
if b.dashboardBuilder != nil {
db, errBuildDashboard := b.dashboardBuilder.Build()
if errBuildDashboard != nil {
return nil, errBuildDashboard
}
observability.Dashboard = &db

// Add common tags to alerts
if b.alertsTags != nil && len(b.alertsTags) > 0 {
tags := maps.Clone(b.alertsTags)
maps.Copy(tags, alert.Labels)
var alerts []alerting.Rule
for _, alertBuilder := range b.alertsBuilder {
alert, errBuildAlert := alertBuilder.Build()
if errBuildAlert != nil {
return nil, errBuildAlert
}

alertBuildWithTags := alertBuilder.Labels(tags)
alertWithTags, errBuildAlertWithTags := alertBuildWithTags.Build()
if errBuildAlertWithTags != nil {
return nil, errBuildAlertWithTags
// Add common tags to alerts
if b.alertsTags != nil && len(b.alertsTags) > 0 {
tags := maps.Clone(b.alertsTags)
maps.Copy(tags, alert.Labels)

alertBuildWithTags := alertBuilder.Labels(tags)
alertWithTags, errBuildAlertWithTags := alertBuildWithTags.Build()
if errBuildAlertWithTags != nil {
return nil, errBuildAlertWithTags
}
alerts = append(alerts, alertWithTags)
} else {
alerts = append(alerts, alert)
}
alerts = append(alerts, alertWithTags)
} else {
alerts = append(alerts, alert)
}
observability.Alerts = alerts
}

var contactPoints []alerting.ContactPoint
@@ -141,6 +154,7 @@ func (b *Builder) Build() (*Dashboard, error) {
}
contactPoints = append(contactPoints, contactPoint)
}
observability.ContactPoints = contactPoints

var notificationPolicies []alerting.NotificationPolicy
for _, notificationPolicyBuilder := range b.notificationPoliciesBuilder {
@@ -150,11 +164,7 @@ func (b *Builder) Build() (*Dashboard, error) {
}
notificationPolicies = append(notificationPolicies, notificationPolicy)
}
observability.NotificationPolicies = notificationPolicies

return &Dashboard{
Dashboard: &db,
Alerts: alerts,
ContactPoints: contactPoints,
NotificationPolicies: notificationPolicies,
}, nil
return &observability, nil
}
Loading

0 comments on commit f1c901f

Please sign in to comment.