Skip to content

Commit

Permalink
feat(alerts): Add support for prediction to NRQL alert conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
founddrama committed Feb 20, 2025
1 parent a221c9e commit 0cdeb2a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 6 deletions.
33 changes: 27 additions & 6 deletions pkg/alerts/nrql_conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,19 @@ var (
}
)

type NrqlConditionThresholdPrediction struct {
PredictBy int `json:"predictBy,omitempty"`
PreferPredictionViolation bool `json:"preferPredictionViolation,omitempty"`
}

// NrqlConditionTerm represents the a single term of a New Relic alert condition.
type NrqlConditionTerm struct {
Operator AlertsNRQLConditionTermsOperator `json:"operator,omitempty"`
Priority NrqlConditionPriority `json:"priority,omitempty"`
Threshold *float64 `json:"threshold"`
ThresholdDuration int `json:"thresholdDuration,omitempty"`
ThresholdOccurrences ThresholdOccurrence `json:"thresholdOccurrences,omitempty"`
Operator AlertsNRQLConditionTermsOperator `json:"operator,omitempty"`
Priority NrqlConditionPriority `json:"priority,omitempty"`
Threshold *float64 `json:"threshold"`
ThresholdDuration int `json:"thresholdDuration,omitempty"`
ThresholdOccurrences ThresholdOccurrence `json:"thresholdOccurrences,omitempty"`
Prediction *NrqlConditionThresholdPrediction `json:"prediction,omitempty"`
}

// NrqlConditionQuery represents the NRQL query object returned in a NerdGraph response object.
Expand Down Expand Up @@ -741,7 +747,7 @@ const (
closeViolationsOnExpiration
expirationDuration
openViolationOnExpiration
ignoreOnExpectedTermination
ignoreOnExpectedTermination
}
signal {
aggregationWindow
Expand All @@ -762,6 +768,17 @@ const (
}
`

graphqlFragmentNrqlStaticConditionFields = `
... on AlertsNrqlStaticCondition {
terms {
prediction {
predictBy
preferPredictionViolation
}
}
}
`

searchNrqlConditionsQuery = `
query($accountId: Int!, $searchCriteria: AlertsNrqlConditionsSearchCriteriaInput, $cursor: String) {
actor {
Expand All @@ -773,6 +790,7 @@ const (
nrqlConditions {` +
graphqlNrqlConditionStructFields +
graphqlFragmentNrqlBaselineConditionFields +
graphqlFragmentNrqlStaticConditionFields +
`} } } } } }`

getNrqlConditionQuery = `
Expand All @@ -783,6 +801,7 @@ const (
nrqlCondition(id: $id) {` +
graphqlNrqlConditionStructFields +
graphqlFragmentNrqlBaselineConditionFields +
graphqlFragmentNrqlStaticConditionFields +
`} } } } }`

// Baseline
Expand All @@ -806,12 +825,14 @@ const (
mutation($accountId: Int!, $policyId: ID!, $condition: AlertsNrqlConditionStaticInput!) {
alertsNrqlConditionStaticCreate(accountId: $accountId, policyId: $policyId, condition: $condition) {` +
graphqlNrqlConditionStructFields +
graphqlFragmentNrqlStaticConditionFields +
` } }`

// Static
updateNrqlConditionStaticMutation = `
mutation($accountId: Int!, $id: ID!, $condition: AlertsNrqlConditionUpdateStaticInput!) {
alertsNrqlConditionStaticUpdate(accountId: $accountId, id: $id, condition: $condition) {` +
graphqlNrqlConditionStructFields +
graphqlFragmentNrqlStaticConditionFields +
` } }`
)
79 changes: 79 additions & 0 deletions pkg/alerts/nrql_conditions_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
nrqlConditionBaseSlideBy = 30 // needed for setting pointer
nrqlConditionEvaluationDelay = 60 // needed for setting pointer
nrqlConditionTitleTemplate = "Title {{ createdAt }}" // needed for setting pointer
nrqlConditionPredictBy = 7200 // needed for setting pointer
nrqlConditionCreateBase = NrqlConditionCreateBase{
Description: "test description",
Enabled: true,
Expand Down Expand Up @@ -896,3 +897,81 @@ func TestIntegrationNrqlConditions_IgnoreOnExpectedTermination(t *testing.T) {
}
}()
}

func TestIntegrationNrqlConditions_Prediction(t *testing.T) {
t.Parallel()

testAccountID, err := mock.GetTestAccountID()
if err != nil {
t.Skipf("%s", err)
}

var nrqlConditionCreateWithPrediction = NrqlConditionCreateBase{
Enabled: true,
Name: fmt.Sprintf("test-nrql-condition-%s", testNrqlConditionRandomString),
Nrql: NrqlConditionCreateQuery{
Query: "SELECT rate(sum(apm.service.cpu.usertime.utilization), 1 second) * 100 as cpuUsage FROM Metric WHERE appName like 'Dummy App'",
DataAccountId: &testAccountID,
},
Terms: []NrqlConditionTerm{
{
Threshold: &nrqlConditionBaseThreshold,
ThresholdOccurrences: ThresholdOccurrences.AtLeastOnce,
ThresholdDuration: 600,
Operator: AlertsNRQLConditionTermsOperatorTypes.ABOVE,
Priority: NrqlConditionPriorities.Critical,
Prediction: &NrqlConditionThresholdPrediction{
PredictBy: nrqlConditionPredictBy,
},
},
},
ViolationTimeLimitSeconds: 3600,
Signal: &AlertsNrqlConditionCreateSignal{
AggregationWindow: &nrqlConditionBaseAggWindow,
FillOption: &AlertsFillOptionTypes.STATIC,
FillValue: &nrqlConditionBaseSignalFillValue,
EvaluationDelay: &nrqlConditionEvaluationDelay,
AggregationMethod: &nrqlConditionBaseAggMethod,
AggregationDelay: &nrqlConditionBaseAggDelay,
},
}

var (
randStr = mock.RandSeq(5)
createPredictionInput = NrqlConditionCreateInput{
NrqlConditionCreateBase: nrqlConditionCreateWithPrediction,
}
)

// Setup
client := newIntegrationTestClient(t)
testPolicy := AlertsPolicyInput{
IncidentPreference: AlertsIncidentPreferenceTypes.PER_POLICY,
Name: fmt.Sprintf("test-alert-policy-%s", randStr),
}
policy, err := client.CreatePolicyMutation(testAccountID, testPolicy)
require.NoError(t, err)

// Test: Create (static condition with forecast field)
createdStaticWithPrediction, err := client.CreateNrqlConditionStaticMutation(testAccountID, policy.ID, createPredictionInput)
require.NoError(t, err)
require.NotNil(t, createdStaticWithPrediction)
require.NotNil(t, createdStaticWithPrediction.ID)
require.NotNil(t, createdStaticWithPrediction.PolicyID)
require.NotNil(t, createdStaticWithPrediction.Terms[0].Prediction)
require.Equal(t, nrqlConditionPredictBy, createdStaticWithPrediction.Terms[0].Prediction.PredictBy)

// Test: Get (static condition with dataAccountId field)
readResult, err := client.GetNrqlConditionQuery(testAccountID, createdStaticWithPrediction.ID)
require.NoError(t, err)
require.NotNil(t, readResult)
require.Equal(t, nrqlConditionPredictBy, readResult.Terms[0].Prediction.PredictBy)

// Deferred teardown
defer func() {
_, err := client.DeletePolicyMutation(testAccountID, policy.ID)
if err != nil {
t.Logf("error cleaning up alert policy %s (%s): %s", policy.ID, policy.Name, err)
}
}()
}

0 comments on commit 0cdeb2a

Please sign in to comment.