Skip to content

Commit

Permalink
DocLevel Monitor - generate findings when 0 triggers (#856)
Browse files Browse the repository at this point in the history
Signed-off-by: Petar Dzepina <[email protected]>
  • Loading branch information
petardz authored May 2, 2023
1 parent 58d2b03 commit 4ba7d42
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,17 +215,27 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
val idQueryMap: Map<String, DocLevelQuery> = queries.associateBy { it.id }

val triggerResults = mutableMapOf<String, DocumentLevelTriggerRunResult>()
monitor.triggers.forEach {
triggerResults[it.id] = runForEachDocTrigger(
monitorCtx,
monitorResult,
it as DocumentLevelTrigger,
monitor,
idQueryMap,
docsToQueries,
queryToDocIds,
dryrun
)
// If there are no triggers defined, we still want to generate findings
if (monitor.triggers.isEmpty()) {
if (dryrun == false && monitor.id != Monitor.NO_ID) {
docsToQueries.forEach {
val triggeredQueries = it.value.map { queryId -> idQueryMap[queryId]!! }
createFindings(monitor, monitorCtx, triggeredQueries, it.key, true)
}
}
} else {
monitor.triggers.forEach {
triggerResults[it.id] = runForEachDocTrigger(
monitorCtx,
monitorResult,
it as DocumentLevelTrigger,
monitor,
idQueryMap,
docsToQueries,
queryToDocIds,
dryrun
)
}
}

// Don't update monitor if this is a test monitor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,65 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
assertEquals("Findings saved for test monitor", 4, findings.size)
}

fun `test execute monitor without triggers`() {
val docQuery = DocLevelQuery(query = "eventType:\"login\"", name = "3")

val docLevelInput = DocLevelMonitorInput(
"description", listOf(index), listOf(docQuery)
)
val customFindingsIndex = "custom_findings_index"
val customFindingsIndexPattern = "custom_findings_index-1"
val customQueryIndex = "custom_alerts_index"
var monitor = randomDocumentLevelMonitor(
inputs = listOf(docLevelInput),
triggers = listOf(),
dataSources = DataSources(
queryIndex = customQueryIndex,
findingsIndex = customFindingsIndex,
findingsIndexPattern = customFindingsIndexPattern
)
)
val monitorResponse = createMonitor(monitor)
assertFalse(monitorResponse?.id.isNullOrEmpty())

val testDoc = """{
"eventType" : "login"
}"""
indexDoc(index, "1", testDoc)

monitor = monitorResponse!!.monitor
val id = monitorResponse.id
// Execute dry run first and expect no alerts or findings
var executeMonitorResponse = executeMonitor(monitor, id, true)
Assert.assertEquals(executeMonitorResponse!!.monitorRunResult.monitorName, monitor.name)
Assert.assertEquals(executeMonitorResponse.monitorRunResult.triggerResults.size, 0)
searchAlerts(id)
var table = Table("asc", "id", null, 1, 0, "")
var getAlertsResponse = client()
.execute(AlertingActions.GET_ALERTS_ACTION_TYPE, GetAlertsRequest(table, "ALL", "ALL", null, null))
.get()
Assert.assertTrue(getAlertsResponse != null)
Assert.assertTrue(getAlertsResponse.alerts.isEmpty())
var findings = searchFindings(id, customFindingsIndex)
assertEquals("Findings saved for test monitor", 0, findings.size)

// Execute real run - expect findings, but no alerts
executeMonitorResponse = executeMonitor(monitor, id, false)

searchAlerts(id)
table = Table("asc", "id", null, 1, 0, "")
getAlertsResponse = client()
.execute(AlertingActions.GET_ALERTS_ACTION_TYPE, GetAlertsRequest(table, "ALL", "ALL", null, null))
.get()
Assert.assertTrue(getAlertsResponse != null)
Assert.assertTrue(getAlertsResponse.alerts.isEmpty())

findings = searchFindings(id, customFindingsIndex)
assertEquals("Findings saved for test monitor", 1, findings.size)
assertTrue("Findings saved for test monitor", findings[0].relatedDocIds.contains("1"))
assertEquals("Didn't match query", 1, findings[0].docLevelQueries.size)
}

fun `test execute monitor with custom query index`() {
val q1 = DocLevelQuery(query = "source.ip.v6.v1:12345", name = "3")
val q2 = DocLevelQuery(query = "source.ip.v6.v2:16645", name = "4")
Expand Down Expand Up @@ -1051,6 +1110,78 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
Assert.assertEquals(searchMonitorResponse.hits.hits.size, 1)
}

fun `test execute pre-existing monitor without triggers`() {
val request = CreateIndexRequest(SCHEDULED_JOBS_INDEX).mapping(ScheduledJobIndices.scheduledJobMappings())
.settings(Settings.builder().put("index.hidden", true).build())
client().admin().indices().create(request)
val monitorStringWithoutName = """
{
"monitor": {
"type": "monitor",
"schema_version": 0,
"name": "UayEuXpZtb",
"monitor_type": "doc_level_monitor",
"user": {
"name": "",
"backend_roles": [],
"roles": [],
"custom_attribute_names": [],
"user_requested_tenant": null
},
"enabled": true,
"enabled_time": 1662753436791,
"schedule": {
"period": {
"interval": 5,
"unit": "MINUTES"
}
},
"inputs": [{
"doc_level_input": {
"description": "description",
"indices": [
"$index"
],
"queries": [{
"id": "63efdcce-b5a1-49f4-a25f-6b5f9496a755",
"name": "3",
"query": "test_field:\"us-west-2\"",
"tags": []
}]
}
}],
"triggers": [],
"last_update_time": 1662753436791
}
}
""".trimIndent()
val monitorId = "abc"
indexDoc(SCHEDULED_JOBS_INDEX, monitorId, monitorStringWithoutName)
val getMonitorResponse = getMonitorResponse(monitorId)
Assert.assertNotNull(getMonitorResponse)
Assert.assertNotNull(getMonitorResponse.monitor)
val monitor = getMonitorResponse.monitor

val testTime = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now().truncatedTo(MILLIS))
val testDoc = """{
"message" : "This is an error from IAD region",
"test_strict_date_time" : "$testTime",
"test_field" : "us-west-2"
}"""
indexDoc(index, "1", testDoc)
var executeMonitorResponse = executeMonitor(monitor!!, monitorId, false)
Assert.assertNotNull(executeMonitorResponse)
if (executeMonitorResponse != null) {
Assert.assertNotNull(executeMonitorResponse.monitorRunResult.monitorName)
}
val alerts = searchAlerts(monitorId)
assertEquals(0, alerts.size)

val findings = searchFindings(monitorId)
assertEquals("Findings saved for test monitor", 1, findings.size)
assertTrue("Findings saved for test monitor", findings[0].relatedDocIds.contains("1"))
}

fun `test execute monitor with empty source index`() {
val docQuery = DocLevelQuery(query = "test_field:\"us-west-2\"", name = "3")
val docLevelInput = DocLevelMonitorInput("description", listOf(index), listOf(docQuery))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import org.opensearch.commons.alerting.model.Alert.State.ACTIVE
import org.opensearch.commons.alerting.model.Alert.State.COMPLETED
import org.opensearch.commons.alerting.model.Alert.State.ERROR
import org.opensearch.commons.alerting.model.DataSources
import org.opensearch.commons.alerting.model.DocLevelMonitorInput
import org.opensearch.commons.alerting.model.DocLevelQuery
import org.opensearch.commons.alerting.model.IntervalSchedule
import org.opensearch.commons.alerting.model.Monitor
import org.opensearch.commons.alerting.model.SearchInput
Expand All @@ -50,7 +52,6 @@ import java.time.temporal.ChronoUnit
import java.time.temporal.ChronoUnit.DAYS
import java.time.temporal.ChronoUnit.MILLIS
import java.time.temporal.ChronoUnit.MINUTES
import kotlin.collections.HashMap

class MonitorRunnerServiceIT : AlertingRestTestCase() {

Expand Down Expand Up @@ -192,6 +193,31 @@ class MonitorRunnerServiceIT : AlertingRestTestCase() {
Assert.assertEquals(404, exception?.response?.statusLine?.statusCode)
}

fun `test execute doclevel monitor without triggers success`() {
// use a non-existent monitoid to trigger a 404.
val index = "foo"
createIndex(index, Settings.EMPTY)
val docQuery = DocLevelQuery(query = "test_field:\"us-west-2\"", name = "1")
val docLevelInput = DocLevelMonitorInput("description", listOf(index), listOf(docQuery))
val monitor = createMonitor(
randomDocumentLevelMonitor(
inputs = listOf(docLevelInput),
triggers = listOf()
)
)
val doc = """
{ "test_field": "us-west-2" }
""".trimIndent()
indexDoc(index, "1", doc)

val response = executeMonitor(monitor.id)
var output = entityAsMap(response)
assertEquals(monitor.name, output["monitor_name"])
assertTrue("Unexpected monitor error message", (output["error"] as String?).isNullOrEmpty())
assertTrue(searchFindings(monitor).size == 1)
assertTrue(searchAlerts(monitor).isEmpty())
}

fun `test acknowledged alert does not suppress subsequent errors`() {
val destinationId = createDestination().id

Expand Down

0 comments on commit 4ba7d42

Please sign in to comment.