Skip to content

Commit

Permalink
Merge branch 'main' into rename-cw
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhuie19 authored Nov 27, 2024
2 parents 88c820d + 75cf18c commit 5acf288
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 76 deletions.
72 changes: 66 additions & 6 deletions observability-lib/api/notification-policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,78 @@ func policyExist(parent alerting.NotificationPolicy, newNotificationPolicy alert
return false
}

func updateInPlace(parent *alerting.NotificationPolicy, newNotificationPolicy alerting.NotificationPolicy) bool {
for key, notificationPolicy := range parent.Routes {
matchersEqual := false
if notificationPolicy.ObjectMatchers != nil {
matchersEqual = objectMatchersEqual(*notificationPolicy.ObjectMatchers, *newNotificationPolicy.ObjectMatchers)
}
receiversEqual := reflect.DeepEqual(notificationPolicy.Receiver, newNotificationPolicy.Receiver)
if matchersEqual && receiversEqual {
parent.Routes[key] = newNotificationPolicy
return true
}
if notificationPolicy.Routes != nil {
policyExist(notificationPolicy, newNotificationPolicy)
}
}
return false
}

func deleteInPlace(parent *alerting.NotificationPolicy, newNotificationPolicy alerting.NotificationPolicy) bool {
for key, notificationPolicy := range parent.Routes {
matchersEqual := false
if notificationPolicy.ObjectMatchers != nil {
matchersEqual = objectMatchersEqual(*notificationPolicy.ObjectMatchers, *newNotificationPolicy.ObjectMatchers)
}
receiversEqual := reflect.DeepEqual(notificationPolicy.Receiver, newNotificationPolicy.Receiver)
if matchersEqual && receiversEqual {
parent.Routes = append(parent.Routes[:key], parent.Routes[key+1:]...)
return true
}
if notificationPolicy.Routes != nil {
policyExist(notificationPolicy, newNotificationPolicy)
}
}
return false
}

// DeleteNestedPolicy Delete Nested Policy from Notification Policy Tree
func (c *Client) DeleteNestedPolicy(newNotificationPolicy alerting.NotificationPolicy) error {
notificationPolicyTreeResponse, _, err := c.GetNotificationPolicy()
notificationPolicyTree := alerting.NotificationPolicy(notificationPolicyTreeResponse)

if err != nil {
return err
}
if policyExist(notificationPolicyTree, newNotificationPolicy) {
deleteInPlace(&notificationPolicyTree, newNotificationPolicy)
} else {
return fmt.Errorf("policy not found")
}
_, _, errPutNotificationPolicy := c.PutNotificationPolicy(notificationPolicyTree)
if errPutNotificationPolicy != nil {
return errPutNotificationPolicy
}
return nil
}

// AddNestedPolicy Add Nested Policy to Notification Policy Tree
func (c *Client) AddNestedPolicy(newNotificationPolicy alerting.NotificationPolicy) error {
notificationPolicyTree, _, err := c.GetNotificationPolicy()
notificationPolicyTreeResponse, _, err := c.GetNotificationPolicy()
notificationPolicyTree := alerting.NotificationPolicy(notificationPolicyTreeResponse)

if err != nil {
return err
}
if !policyExist(alerting.NotificationPolicy(notificationPolicyTree), newNotificationPolicy) {
if !policyExist(notificationPolicyTree, newNotificationPolicy) {
notificationPolicyTree.Routes = append(notificationPolicyTree.Routes, newNotificationPolicy)
_, _, errPutNotificationPolicy := c.PutNotificationPolicy(alerting.NotificationPolicy(notificationPolicyTree))
if errPutNotificationPolicy != nil {
return errPutNotificationPolicy
}
} else {
updateInPlace(&notificationPolicyTree, newNotificationPolicy)
}
_, _, errPutNotificationPolicy := c.PutNotificationPolicy(notificationPolicyTree)
if errPutNotificationPolicy != nil {
return errPutNotificationPolicy
}
return nil
}
Expand Down
33 changes: 33 additions & 0 deletions observability-lib/api/rule-group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package api

import (
"fmt"

"github.com/go-resty/resty/v2"
"github.com/grafana/grafana-foundation-sdk/go/alerting"
)

type UpdateAlertRuleGroupResponse struct{}

// UpdateAlertRuleGroup Update a specific alert rule group
func (c *Client) UpdateAlertRuleGroup(folderUID string, alertRuleGroup alerting.RuleGroup) (UpdateAlertRuleGroupResponse, *resty.Response, error) {
var grafanaResp UpdateAlertRuleGroupResponse

resp, err := c.resty.R().
SetHeader("Content-Type", "application/json").
SetHeader("X-Disable-Provenance", "true").
SetBody(alertRuleGroup).
SetResult(&grafanaResp).
Put(fmt.Sprintf("/api/v1/provisioning/folder/%s/rule-groups/%s", folderUID, *alertRuleGroup.Title))

if err != nil {
return UpdateAlertRuleGroupResponse{}, resp, fmt.Errorf("error making API request: %w", err)
}

statusCode := resp.StatusCode()
if statusCode != 200 {
return UpdateAlertRuleGroupResponse{}, resp, fmt.Errorf("error updating alert rule group, received unexpected status code %d: %s", statusCode, resp.String())
}

return grafanaResp, resp, nil
}
16 changes: 16 additions & 0 deletions observability-lib/api/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ func (c *Client) GetAlertRulesByDashboardUID(dashboardUID string) (GetAllAlertRu
return alerts, nil
}

// GetAlertRulesByFolderUIDAndGroupName Get alert rules by folder UID and GroupName
func (c *Client) GetAlertRulesByFolderUIDAndGroupName(folderUID string, ruleGroupName string) (GetAllAlertRulesResponse, error) {
var alerts []alerting.Rule

alertsRule, _, err := c.GetAlertRules()
if err != nil {
return nil, err
}
for _, rule := range alertsRule {
if rule.FolderUID != "" && (rule.FolderUID == folderUID) && (rule.RuleGroup == ruleGroupName) {
alerts = append(alerts, rule)
}
}
return alerts, nil
}

// GetAlertRules Get all alert rules
func (c *Client) GetAlertRules() (GetAllAlertRulesResponse, *resty.Response, error) {
var grafanaResp GetAllAlertRulesResponse
Expand Down
1 change: 1 addition & 0 deletions observability-lib/dashboards/atlas-don/test-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,7 @@
"annotations": {}
},
"Alerts": null,
"AlertGroups": null,
"ContactPoints": null,
"NotificationPolicies": null
}
1 change: 1 addition & 0 deletions observability-lib/dashboards/capabilities/test-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@
"annotations": {}
},
"Alerts": null,
"AlertGroups": null,
"ContactPoints": null,
"NotificationPolicies": null
}
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@
"annotations": {}
},
"Alerts": null,
"AlertGroups": null,
"ContactPoints": null,
"NotificationPolicies": null
}
5 changes: 5 additions & 0 deletions observability-lib/dashboards/core-node/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ func NewDashboard(props *Props) (*grafana.Observability, error) {
AlertsTags: props.AlertsTags,
})

builder.AddAlertGroup(grafana.NewAlertGroup(&grafana.AlertGroupOptions{
Title: props.Name,
Interval: 60,
}))

if props.SlackChannel != "" && props.SlackWebhookURL != "" {
builder.AddContactPoint(grafana.NewContactPoint(&grafana.ContactPointOptions{
Name: "chainlink-slack",
Expand Down
6 changes: 6 additions & 0 deletions observability-lib/dashboards/core-node/test-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -6348,6 +6348,12 @@
"title": "Head Tracker Heads Received Rate"
}
],
"AlertGroups": [
{
"interval": 60,
"title": "Core Node Dashboard"
}
],
"ContactPoints": null,
"NotificationPolicies": null
}
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@
"annotations": {}
},
"Alerts": null,
"AlertGroups": null,
"ContactPoints": null,
"NotificationPolicies": null
}
1 change: 1 addition & 0 deletions observability-lib/dashboards/nop-ocr/test-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@
"annotations": {}
},
"Alerts": null,
"AlertGroups": null,
"ContactPoints": null,
"NotificationPolicies": null
}
15 changes: 15 additions & 0 deletions observability-lib/grafana/alerts-group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package grafana

import (
"github.com/grafana/grafana-foundation-sdk/go/alerting"
)

type AlertGroupOptions struct {
Title string
Interval alerting.Duration // duration in seconds
}

func NewAlertGroup(options *AlertGroupOptions) *alerting.RuleGroupBuilder {
return alerting.NewRuleGroupBuilder(options.Title).
Interval(options.Interval)
}
5 changes: 5 additions & 0 deletions observability-lib/grafana/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ type AlertOptions struct {
QueryRefCondition string
Condition []ConditionQuery
PanelTitle string
RuleGroupTitle string
}

func NewAlertRule(options *AlertOptions) *alerting.RuleBuilder {
Expand Down Expand Up @@ -207,6 +208,10 @@ func NewAlertRule(options *AlertOptions) *alerting.RuleBuilder {
Annotations(annotations).
Labels(options.Tags)

if options.RuleGroupTitle != "" {
rule.RuleGroup(options.RuleGroupTitle)
}

for _, query := range options.Query {
rule.WithQuery(newRuleQuery(query))
}
Expand Down
55 changes: 35 additions & 20 deletions observability-lib/grafana/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type Builder struct {
dashboardBuilder *dashboard.DashboardBuilder
alertsBuilder []*alerting.RuleBuilder
alertGroupsBuilder []*alerting.RuleGroupBuilder
contactPointsBuilder []*alerting.ContactPointBuilder
notificationPoliciesBuilder []*alerting.NotificationPolicyBuilder
panelCounter uint32
Expand Down Expand Up @@ -103,6 +104,10 @@ func (b *Builder) AddAlert(alerts ...*alerting.RuleBuilder) {
b.alertsBuilder = append(b.alertsBuilder, alerts...)
}

func (b *Builder) AddAlertGroup(alertGroups ...*alerting.RuleGroupBuilder) {
b.alertGroupsBuilder = append(b.alertGroupsBuilder, alertGroups...)
}

func (b *Builder) AddContactPoint(contactPoints ...*alerting.ContactPointBuilder) {
b.contactPointsBuilder = append(b.contactPointsBuilder, contactPoints...)
}
Expand All @@ -120,31 +125,41 @@ func (b *Builder) Build() (*Observability, error) {
return nil, errBuildDashboard
}
observability.Dashboard = &db
}

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

// 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)
// 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)
}
}
observability.Alerts = alerts

var alertGroups []alerting.RuleGroup
for _, alertGroupBuilder := range b.alertGroupsBuilder {
alertGroup, errBuildAlertGroup := alertGroupBuilder.Build()
if errBuildAlertGroup != nil {
return nil, errBuildAlertGroup
}
observability.Alerts = alerts
alertGroups = append(alertGroups, alertGroup)
}
observability.AlertGroups = alertGroups

var contactPoints []alerting.ContactPoint
for _, contactPointBuilder := range b.contactPointsBuilder {
Expand Down
37 changes: 36 additions & 1 deletion observability-lib/grafana/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,44 @@ func TestNewBuilder(t *testing.T) {
if err != nil {
t.Errorf("Error during build: %v", err)
}

require.NotEmpty(t, o.Dashboard)
require.NotEmpty(t, o.Alerts)
require.Len(t, o.Alerts, 1)
require.Empty(t, o.ContactPoints)
require.Empty(t, o.NotificationPolicies)
})

t.Run("NewBuilder builds only alerts", func(t *testing.T) {
builder := grafana.NewBuilder(&grafana.BuilderOptions{})
builder.AddAlert(grafana.NewAlertRule(&grafana.AlertOptions{
Title: "Alert Title",
}))

o, err := builder.Build()
if err != nil {
t.Errorf("Error during build: %v", err)
}
require.Empty(t, o.Dashboard)
require.NotEmpty(t, o.Alerts)
require.Len(t, o.Alerts, 1)
require.Empty(t, o.ContactPoints)
require.Empty(t, o.NotificationPolicies)
})

t.Run("NewBuilder builds an alert group", func(t *testing.T) {
builder := grafana.NewBuilder(&grafana.BuilderOptions{})
builder.AddAlertGroup(grafana.NewAlertGroup(&grafana.AlertGroupOptions{
Title: "Group Title",
Interval: 30, // duration in seconds
}))

o, err := builder.Build()
if err != nil {
t.Errorf("Error during build: %v", err)
}
require.Empty(t, o.Dashboard)
require.NotEmpty(t, o.AlertGroups)
require.Len(t, o.AlertGroups, 1)
require.Empty(t, o.ContactPoints)
require.Empty(t, o.NotificationPolicies)
})
Expand Down
Loading

0 comments on commit 5acf288

Please sign in to comment.