Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix get alerts api to also return chained alerts with default params. and skip audit state alerts #1020

Merged
merged 3 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ object MonitorMetadataService :
monitor: Monitor,
createWithRunContext: Boolean = true,
skipIndex: Boolean = false,
workflowMetadataId: String? = null,
workflowMetadataId: String? = null
): Pair<MonitorMetadata, Boolean> {
try {
val created = true
Expand Down Expand Up @@ -154,7 +154,7 @@ object MonitorMetadataService :
XContentType.JSON
)
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.nextToken(), xcp)
MonitorMetadata.parse(xcp)
MonitorMetadata.parse(xcp, getResponse.id, getResponse.seqNo, getResponse.primaryTerm)
} else {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class TransportGetAlertsAction @Inject constructor(
clusterService: ClusterService,
actionFilters: ActionFilters,
val settings: Settings,
val xContentRegistry: NamedXContentRegistry
val xContentRegistry: NamedXContentRegistry,
) : HandledTransportAction<ActionRequest, GetAlertsResponse>(
AlertingActions.GET_ALERTS_ACTION_NAME,
transportService,
Expand All @@ -77,7 +77,7 @@ class TransportGetAlertsAction @Inject constructor(
override fun doExecute(
task: Task,
request: ActionRequest,
actionListener: ActionListener<GetAlertsResponse>
actionListener: ActionListener<GetAlertsResponse>,
) {
val getAlertsRequest = request as? GetAlertsRequest
?: recreateObject(request) { GetAlertsRequest(it) }
Expand All @@ -97,7 +97,15 @@ class TransportGetAlertsAction @Inject constructor(
queryBuilder.filter(QueryBuilders.termQuery("severity", getAlertsRequest.severityLevel))
}

if (getAlertsRequest.alertState != "ALL") {
if (getAlertsRequest.alertState == "ALL") {
// alerting dashboards expects chained alerts and individually executed monitors' alerts to be returned from this api
// when invoked with state=ALL. They require that audit alerts are NOT returned in this page
// and only be shown in "associated alerts" field under get workflow_alerts API.
// But if the API is called with query_params: state=AUDIT,monitor_id=<123>,workflow_id=<abc>, this api
// will return audit alerts generated by delegate monitor <123> in workflow <abc>
Comment on lines +104 to +105
Copy link
Member

@lezzago lezzago Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are still letting this API call get audit alerts, it should be included in ALL. It should not be just for the dashboard use case.

Otherwise, we should not let users see audit alerts through this API and only from the workflow alerts API

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not supporting it in ALL is temporary until the front end can fix it, then I am fine with going with this logic as long as we have an open github issue about it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AUDIT alerts is a state designed to ållow us to create events which we don't want to get users notified about. That's why they rely on chained alerts to be actionable(notifications/acknowledgement etc)

When we return alerts in the api it would clutter the response.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created issue to discuss further and reconcile with front end #1021

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make a call on if we want audit alerts here or not now.

QueryBuilders.boolQuery()
.filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Alert.STATE_FIELD, Alert.State.AUDIT.name)))
} else {
queryBuilder.filter(QueryBuilders.termQuery("state", getAlertsRequest.alertState))
}

Expand All @@ -107,16 +115,23 @@ class TransportGetAlertsAction @Inject constructor(

if (getAlertsRequest.monitorId != null) {
queryBuilder.filter(QueryBuilders.termQuery("monitor_id", getAlertsRequest.monitorId))
if (getAlertsRequest.workflowIds.isNullOrEmpty()) {
val noWorkflowIdQuery = QueryBuilders.boolQuery()
.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(Alert.WORKFLOW_ID_FIELD)))
.should(QueryBuilders.termsQuery(Alert.WORKFLOW_ID_FIELD, ""))
queryBuilder.must(noWorkflowIdQuery)
}
} else if (getAlertsRequest.monitorIds.isNullOrEmpty() == false) {
queryBuilder.filter(QueryBuilders.termsQuery("monitor_id", getAlertsRequest.monitorIds))
if (getAlertsRequest.workflowIds.isNullOrEmpty()) {
val noWorkflowIdQuery = QueryBuilders.boolQuery()
.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(Alert.WORKFLOW_ID_FIELD)))
.should(QueryBuilders.termsQuery(Alert.WORKFLOW_ID_FIELD, ""))
queryBuilder.must(noWorkflowIdQuery)
}
}
if (getAlertsRequest.workflowIds.isNullOrEmpty() == false) {
queryBuilder.must(QueryBuilders.termsQuery("workflow_id", getAlertsRequest.workflowIds))
} else {
val noWorklfowIdQuery = QueryBuilders.boolQuery()
.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(Alert.WORKFLOW_ID_FIELD)))
.should(QueryBuilders.termsQuery(Alert.WORKFLOW_ID_FIELD, ""))
queryBuilder.must(noWorklfowIdQuery)
}
if (!tableProp.searchString.isNullOrBlank()) {
queryBuilder
Expand Down Expand Up @@ -196,7 +211,7 @@ class TransportGetAlertsAction @Inject constructor(
alertIndex: String,
searchSourceBuilder: SearchSourceBuilder,
actionListener: ActionListener<GetAlertsResponse>,
user: User?
user: User?,
) {
// user is null when: 1/ security is disabled. 2/when user is super-admin.
if (user == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ class TransportGetWorkflowAlertsAction @Inject constructor(
queryBuilder.filter(QueryBuilders.termQuery("severity", getWorkflowAlertsRequest.severityLevel))
}

if (getWorkflowAlertsRequest.alertState != "ALL") {
queryBuilder.filter(QueryBuilders.termQuery("state", getWorkflowAlertsRequest.alertState))
if (getWorkflowAlertsRequest.alertState == "ALL") {
QueryBuilders.boolQuery()
.filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Alert.STATE_FIELD, Alert.State.AUDIT.name)))
} else {
queryBuilder.filter(QueryBuilders.termQuery(Alert.STATE_FIELD, getWorkflowAlertsRequest.alertState))
}

if (getWorkflowAlertsRequest.alertIds.isNullOrEmpty() == false) {
Expand Down Expand Up @@ -148,7 +151,7 @@ class TransportGetWorkflowAlertsAction @Inject constructor(
}

fun resolveAlertsIndexName(getAlertsRequest: GetWorkflowAlertsRequest): String {
return if (getAlertsRequest.alertIndex.isNullOrEmpty()) AlertIndices.ALL_ALERT_INDEX_PATTERN
return if (getAlertsRequest.alertIndex.isNullOrEmpty()) AlertIndices.ALERT_INDEX
else getAlertsRequest.alertIndex!!
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ object CompositeWorkflowRunner : WorkflowRunner() {
.should(boolQuery().mustNot(existsQuery(Alert.ERROR_MESSAGE_FIELD)))
.should(termsQuery(Alert.ERROR_MESSAGE_FIELD, ""))
queryBuilder.must(noErrorQuery)
searchRequest.source().query(queryBuilder)
searchRequest.source().query(queryBuilder).size(9999)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to make sure we fetch as many alerts as we can
we don't intend to evaluate with just 10 alerts which the default if no size is mentioned

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be paginating through them then. Also isnt there a max of 25 delegate monitors to a workflow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's revisit this soon
#1022 and take a quick follow up.

val searchResponse: SearchResponse = monitorCtx.client!!.suspendUntil { monitorCtx.client!!.search(searchRequest, it) }
val alerts = searchResponse.hits.map { hit ->
val xcp = XContentHelper.createParser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3942,6 +3942,23 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
)
var chainedAlerts = res.alerts
Assert.assertTrue(chainedAlerts.size == 1)

// verify get alerts api with defaults set in query params returns only chained alerts and not audit alerts
val table = Table("asc", "id", null, 1, 0, "")
val getAlertsDefaultParamsResponse = client().execute(
AlertingActions.GET_ALERTS_ACTION_TYPE,
GetAlertsRequest(
table = table,
severityLevel = "ALL",
alertState = "ALL",
monitorId = null,
alertIndex = null,
monitorIds = null,
workflowIds = null,
alertIds = null
)
).get()
Assert.assertEquals(getAlertsDefaultParamsResponse.alerts.size, 1)
Assert.assertTrue(res.associatedAlerts.isEmpty())
verifyAcknowledgeChainedAlerts(chainedAlerts, workflowId, 1)
Assert.assertTrue(chainedAlerts[0].executionId == executeWorkflowResponse.workflowRunResult.executionId)
Expand Down Expand Up @@ -3985,6 +4002,20 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
notTriggerResult = triggerResults[notTrigger.id]
Assert.assertFalse(notTriggerResult!!.triggered)
Assert.assertTrue(andTriggerResult!!.triggered)
val getAuditAlertsForMonitor1 = client().execute(
AlertingActions.GET_ALERTS_ACTION_TYPE,
GetAlertsRequest(
table = table,
severityLevel = "ALL",
alertState = "AUDIT",
monitorId = monitorResponse.id,
alertIndex = null,
monitorIds = null,
workflowIds = listOf(workflowId),
alertIds = null
)
).get()
Assert.assertEquals(getAuditAlertsForMonitor1.alerts.size, 1)
res = getWorkflowAlerts(workflowId)
chainedAlerts = res.alerts
Assert.assertTrue(chainedAlerts.size == 1)
Expand Down Expand Up @@ -4022,6 +4053,10 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
assertAuditStateAlerts(monitorResponse2.id, alerts1)
assertFindings(monitorResponse2.id, customFindingsIndex2, 1, 1, listOf("2"))
verifyAcknowledgeChainedAlerts(chainedAlerts, workflowId, 1)
// test redundant executions of workflow dont query old data again to verify metadata updation works fine
val redundantExec = executeWorkflow(workflow)
Assert.assertFalse(redundantExec?.workflowRunResult!!.triggerResults[andTrigger.id]!!.triggered)
Assert.assertTrue(redundantExec.workflowRunResult.triggerResults[notTrigger.id]!!.triggered)
}

private fun getDelegateMonitorMetadataId(
Expand Down