Skip to content
This repository has been archived by the owner on Oct 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request #53 from meqif/add-support-for-baseline-and-outlie…
Browse files Browse the repository at this point in the history
…r-nrql-alert-conditions

Add support for baseline and outlier NRQL alert conditions
  • Loading branch information
paultyng authored May 21, 2019
2 parents d0f2e65 + 25b35b5 commit 6bd4f0f
Show file tree
Hide file tree
Showing 2 changed files with 299 additions and 8 deletions.
288 changes: 288 additions & 0 deletions api/alert_nrql_conditions_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package api

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"testing"
)
Expand Down Expand Up @@ -212,6 +214,274 @@ func TestCreateAlertNrqlCondition(t *testing.T) {
}
}

func TestCreateAlertNrqlStaticCondition(t *testing.T) {
c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
condition, err := extractAlertNrqlConditionFromRequest(r)
if err != nil {
t.Fatalf("Failed to parse request: %s", err)
}

if condition.Type != "static" {
t.Fatalf("Type different from expected value: expected 'static', got '%s'", condition.Type)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`
{
"nrql_condition":
{
"id": 12345,
"type": "static",
"name": "NRQL Condition",
"runbook_url": "https://example.com/runbook.md",
"enabled": true,
"terms": [
{
"duration": "10",
"operator": "below",
"priority": "critical",
"threshold": "2",
"time_function": "all"
}
],
"value_function": "single_value",
"nrql": {
"query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
"since_value": "5"
}
}
}
`))
}))

nrqlAlertConditionTerms := []AlertConditionTerm{
{
Duration: 10,
Operator: "below",
Priority: "critical",
Threshold: 2.0,
TimeFunction: "all",
},
}

nrqlAlertQuery := AlertNrqlQuery{
Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
SinceValue: "5",
}

nrqlAlertCondition := AlertNrqlCondition{
PolicyID: 123,
Name: "Test Condition",
Type: "static",
Enabled: true,
RunbookURL: "https://example.com/runbook.md",
Terms: nrqlAlertConditionTerms,
ValueFunction: "all",
Nrql: nrqlAlertQuery,
}

nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition)
if err != nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp == nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp.ID != 12345 {
t.Fatal("Condition ID was not parsed correctly")
}
if nrqlAlertConditionResp.Type != "static" {
t.Fatal("Type was not parsed correctly")
}
}

func TestCreateAlertNrqlBaselineCondition(t *testing.T) {
c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
condition, err := extractAlertNrqlConditionFromRequest(r)
if err != nil {
t.Fatalf("Failed to parse request: %s", err)
}

if condition.Type != "baseline" {
t.Fatalf("Type different from expected value: expected 'baseline', got '%s'", condition.Type)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`
{
"nrql_condition":
{
"id": 12345,
"type": "baseline",
"name": "NRQL Condition",
"runbook_url": "https://example.com/runbook.md",
"enabled": true,
"terms": [
{
"duration": "10",
"operator": "below",
"priority": "critical",
"threshold": "2",
"time_function": "all"
}
],
"value_function": "single_value",
"nrql": {
"query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
"since_value": "5"
}
}
}
`))
}))

nrqlAlertConditionTerms := []AlertConditionTerm{
{
Duration: 10,
Operator: "below",
Priority: "critical",
Threshold: 2.0,
TimeFunction: "all",
},
}

nrqlAlertQuery := AlertNrqlQuery{
Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
SinceValue: "5",
}

nrqlAlertCondition := AlertNrqlCondition{
PolicyID: 123,
Type: "baseline",
Name: "Test Condition",
Enabled: true,
RunbookURL: "https://example.com/runbook.md",
Terms: nrqlAlertConditionTerms,
ValueFunction: "all",
Nrql: nrqlAlertQuery,
}

nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition)
if err != nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp == nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp.ID != 12345 {
t.Fatal("Condition ID was not parsed correctly")
}
if nrqlAlertConditionResp.Type != "baseline" {
t.Fatal("Type was not parsed correctly")
}
}

func TestCreateAlertNrqlOutlierCondition(t *testing.T) {
c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
condition, err := extractAlertNrqlConditionFromRequest(r)
if err != nil {
t.Fatalf("Failed to parse request: %s", err)
}

if condition.Type != "outlier" {
t.Fatalf("Type different from expected value: expected 'outlier', got '%s'", condition.Type)
}
if condition.ExpectedGroups != 2 {
t.Fatalf("ExpectedGroups different from expected value: expected '2', got '%d'", condition.ExpectedGroups)
}
if condition.IgnoreOverlap != false {
t.Fatalf("IgnoreOverlap different from expected value: expected 'false', got '%t'", condition.IgnoreOverlap)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`
{
"nrql_condition":
{
"id": 12345,
"type": "outlier",
"name": "NRQL Condition",
"runbook_url": "https://example.com/runbook.md",
"enabled": true,
"terms": [
{
"duration": "10",
"operator": "below",
"priority": "critical",
"threshold": "2",
"time_function": "all"
}
],
"value_function": "single_value",
"expected_groups": 2,
"ignore_overlap": false,
"nrql": {
"query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
"since_value": "5"
}
}
}
`))
}))

nrqlAlertConditionTerms := []AlertConditionTerm{
{
Duration: 10,
Operator: "below",
Priority: "critical",
Threshold: 2.0,
TimeFunction: "all",
},
}

nrqlAlertQuery := AlertNrqlQuery{
Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'",
SinceValue: "5",
}

nrqlAlertCondition := AlertNrqlCondition{
PolicyID: 123,
Type: "outlier",
Name: "Test Condition",
Enabled: true,
RunbookURL: "https://example.com/runbook.md",
Terms: nrqlAlertConditionTerms,
ValueFunction: "all",
ExpectedGroups: 2,
IgnoreOverlap: false,
Nrql: nrqlAlertQuery,
}

nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition)
if err != nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp == nil {
t.Log(err)
t.Fatal("CreateAlertNrqlCondition error")
}
if nrqlAlertConditionResp.ID != 12345 {
t.Fatal("Condition ID was not parsed correctly")
}
if nrqlAlertConditionResp.Type != "outlier" {
t.Fatal("Type was not parsed correctly")
}
if nrqlAlertConditionResp.ExpectedGroups != 2 {
t.Fatal("Expected Groups was not parsed correctly")
}
if nrqlAlertConditionResp.IgnoreOverlap != false {
t.Fatal("Ignore Overlap was not parsed correctly")
}
}

func TestUpdateAlertNrqlCondition(t *testing.T) {
c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
Expand Down Expand Up @@ -301,3 +571,21 @@ func TestDeleteAlertNrqlCondition(t *testing.T) {
t.Fatal("DeleteAlertNrqlCondition error")
}
}

func extractAlertNrqlConditionFromRequest(r *http.Request) (*AlertNrqlCondition, error) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
defer r.Body.Close()

var query *struct {
Condition AlertNrqlCondition `json:"nrql_condition"`
}
err = json.Unmarshal(body, &query)
if err != nil {
return nil, err
}

return &query.Condition, nil
}
19 changes: 11 additions & 8 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,17 @@ type AlertNrqlQuery struct {

// AlertNrqlCondition represents a New Relic NRQL Alert condition.
type AlertNrqlCondition struct {
PolicyID int `json:"-"`
ID int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Enabled bool `json:"enabled"`
RunbookURL string `json:"runbook_url,omitempty"`
Terms []AlertConditionTerm `json:"terms,omitempty"`
ValueFunction string `json:"value_function,omitempty"`
Nrql AlertNrqlQuery `json:"nrql,omitempty"`
PolicyID int `json:"-"`
ID int `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Enabled bool `json:"enabled"`
RunbookURL string `json:"runbook_url,omitempty"`
Terms []AlertConditionTerm `json:"terms,omitempty"`
ValueFunction string `json:"value_function,omitempty"`
ExpectedGroups int `json:"expected_groups,omitempty"`
IgnoreOverlap bool `json:"ignore_overlap,omitempty"`
Nrql AlertNrqlQuery `json:"nrql,omitempty"`
}

// AlertPlugin represents a plugin to use with a Plugin alert condition.
Expand Down

0 comments on commit 6bd4f0f

Please sign in to comment.