Skip to content

Commit 8d6d4b0

Browse files
toepkerdtoepkerd-zzpeterzhuamazon
authored
PPL Alerting: Adding PPL Related Models (#940) (#955)
* PPL Alerting: Adding PPL Related Models * post load test optimization * adding constructor for bwc * extra tests and cleanup * removing mention of SQL in names, generalizing query and queryResults fields in Alert, add extra serde tests * removing experimental flag from PPLTrigger * random PPL Monitor for tests now always includes at least 1 trigger * improving serde tests and added comment about base ppl query * small cleanup * minor comments adjustment * removing notifications ID validation check --------- Signed-off-by: Dennis Toepker <toepkerd@amazon.com> Co-authored-by: Dennis Toepker <toepkerd@amazon.com> Co-authored-by: Peter Zhu <zhujiaxi@amazon.com>
1 parent c52c03d commit 8d6d4b0

18 files changed

Lines changed: 962 additions & 29 deletions

File tree

src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.opensearch.commons.alerting.model
22

3+
import org.opensearch.Version
34
import org.opensearch.common.lucene.uid.Versions
45
import org.opensearch.commons.alerting.alerts.AlertError
6+
import org.opensearch.commons.alerting.model.Monitor.Companion.suppressWarning
57
import org.opensearch.commons.alerting.util.IndexUtils.Companion.NO_SCHEMA_VERSION
68
import org.opensearch.commons.alerting.util.instant
79
import org.opensearch.commons.alerting.util.optionalTimeField
@@ -43,7 +45,12 @@ data class Alert(
4345
val aggregationResultBucket: AggregationResultBucket? = null,
4446
val executionId: String? = null,
4547
val associatedAlertIds: List<String>,
46-
val clusters: List<String>? = null
48+
val clusters: List<String>? = null,
49+
// these fields are specifically used for PPL Monitors
50+
// for now, Query-Level Monitors can support including
51+
// query results in Alerts by using these fields
52+
val query: String? = null,
53+
val queryResults: List<Map<String, Any?>> = listOf()
4754
) : Writeable, ToXContent {
4855

4956
init {
@@ -54,6 +61,64 @@ data class Alert(
5461
}
5562
}
5663

64+
constructor(
65+
id: String = NO_ID,
66+
version: Long = NO_VERSION,
67+
schemaVersion: Int = NO_SCHEMA_VERSION,
68+
monitorId: String,
69+
workflowId: String,
70+
workflowName: String,
71+
monitorName: String,
72+
monitorVersion: Long,
73+
monitorUser: User?,
74+
triggerId: String,
75+
triggerName: String,
76+
findingIds: List<String>,
77+
relatedDocIds: List<String>,
78+
state: State,
79+
startTime: Instant,
80+
endTime: Instant? = null,
81+
lastNotificationTime: Instant? = null,
82+
acknowledgedTime: Instant? = null,
83+
errorMessage: String? = null,
84+
errorHistory: List<AlertError>,
85+
severity: String,
86+
actionExecutionResults: List<ActionExecutionResult>,
87+
aggregationResultBucket: AggregationResultBucket? = null,
88+
executionId: String? = null,
89+
associatedAlertIds: List<String>,
90+
clusters: List<String>? = null
91+
) : this (
92+
id = id,
93+
version = version,
94+
schemaVersion = schemaVersion,
95+
monitorId = monitorId,
96+
workflowId = workflowId,
97+
workflowName = workflowName,
98+
monitorName = monitorName,
99+
monitorVersion = monitorVersion,
100+
monitorUser = monitorUser,
101+
triggerId = triggerId,
102+
triggerName = triggerName,
103+
findingIds = findingIds,
104+
relatedDocIds = relatedDocIds,
105+
state = state,
106+
startTime = startTime,
107+
endTime = endTime,
108+
lastNotificationTime = lastNotificationTime,
109+
acknowledgedTime = acknowledgedTime,
110+
errorMessage = errorMessage,
111+
errorHistory = errorHistory,
112+
severity = severity,
113+
actionExecutionResults = actionExecutionResults,
114+
aggregationResultBucket = aggregationResultBucket,
115+
executionId = executionId,
116+
associatedAlertIds = associatedAlertIds,
117+
clusters = clusters,
118+
query = null,
119+
queryResults = listOf()
120+
)
121+
57122
constructor(
58123
startTime: Instant,
59124
lastNotificationTime: Instant?,
@@ -87,12 +152,16 @@ data class Alert(
87152
workflowId = workflow.id,
88153
workflowName = workflow.name,
89154
associatedAlertIds = associatedAlertIds,
90-
clusters = clusters
155+
clusters = clusters,
156+
query = null,
157+
queryResults = listOf()
91158
)
92159

160+
// constructor for Alerts from QueryLevelMonitorRunner
161+
// this monitor runner runs Query-level, Cluster Metrics, and PPL Monitors
93162
constructor(
94163
monitor: Monitor,
95-
trigger: QueryLevelTrigger,
164+
trigger: Trigger,
96165
startTime: Instant,
97166
lastNotificationTime: Instant?,
98167
state: State = State.ACTIVE,
@@ -102,7 +171,9 @@ data class Alert(
102171
schemaVersion: Int = NO_SCHEMA_VERSION,
103172
executionId: String? = null,
104173
workflowId: String? = null,
105-
clusters: List<String>? = null
174+
clusters: List<String>? = null,
175+
query: String? = null,
176+
queryResults: List<Map<String, Any?>> = listOf()
106177
) : this(
107178
monitorId = monitor.id,
108179
monitorName = monitor.name,
@@ -125,7 +196,9 @@ data class Alert(
125196
workflowId = workflowId ?: "",
126197
workflowName = "",
127198
associatedAlertIds = emptyList(),
128-
clusters = clusters
199+
clusters = clusters,
200+
query = query,
201+
queryResults = queryResults
129202
)
130203

131204
constructor(
@@ -164,7 +237,9 @@ data class Alert(
164237
workflowId = workflowId ?: "",
165238
workflowName = "",
166239
associatedAlertIds = emptyList(),
167-
clusters = clusters
240+
clusters = clusters,
241+
query = null,
242+
queryResults = listOf()
168243
)
169244

170245
constructor(
@@ -204,7 +279,9 @@ data class Alert(
204279
workflowId = workflowId ?: "",
205280
workflowName = "",
206281
associatedAlertIds = emptyList(),
207-
clusters = clusters
282+
clusters = clusters,
283+
query = null,
284+
queryResults = listOf()
208285
)
209286

210287
constructor(
@@ -246,7 +323,9 @@ data class Alert(
246323
workflowId = workflowId ?: "",
247324
workflowName = "",
248325
associatedAlertIds = emptyList(),
249-
clusters = clusters
326+
clusters = clusters,
327+
query = null,
328+
queryResults = listOf()
250329
)
251330

252331
constructor(
@@ -285,7 +364,9 @@ data class Alert(
285364
workflowId = workflowId ?: "",
286365
executionId = executionId,
287366
associatedAlertIds = emptyList(),
288-
clusters = clusters
367+
clusters = clusters,
368+
query = null,
369+
queryResults = listOf()
289370
)
290371

291372
enum class State {
@@ -329,7 +410,17 @@ data class Alert(
329410
aggregationResultBucket = if (sin.readBoolean()) AggregationResultBucket(sin) else null,
330411
executionId = sin.readOptionalString(),
331412
associatedAlertIds = sin.readStringList(),
332-
clusters = sin.readOptionalStringList()
413+
clusters = sin.readOptionalStringList(),
414+
query = if (sin.version.onOrAfter(Version.V_3_5_0)) {
415+
sin.readOptionalString()
416+
} else {
417+
null
418+
},
419+
queryResults = if (sin.version.onOrAfter(Version.V_3_5_0)) {
420+
sin.readList { input -> suppressWarning(input.readMap()) }
421+
} else {
422+
listOf()
423+
}
333424
)
334425

335426
fun isAcknowledged(): Boolean = (state == State.ACKNOWLEDGED)
@@ -368,6 +459,12 @@ data class Alert(
368459
out.writeOptionalString(executionId)
369460
out.writeStringCollection(associatedAlertIds)
370461
out.writeOptionalStringArray(clusters?.toTypedArray())
462+
if (out.version.onOrAfter(Version.V_3_5_0)) {
463+
out.writeOptionalString(query)
464+
out.writeCollection(queryResults) { output, map ->
465+
output.writeMap(map)
466+
}
467+
}
371468
}
372469

373470
companion object {
@@ -399,6 +496,8 @@ data class Alert(
399496
const val BUCKET_KEYS = AggregationResultBucket.BUCKET_KEYS
400497
const val PARENTS_BUCKET_PATH = AggregationResultBucket.PARENTS_BUCKET_PATH
401498
const val CLUSTERS_FIELD = "clusters"
499+
const val QUERY_FIELD = "query"
500+
const val QUERY_RESULTS_FIELD = "query_results"
402501
const val NO_ID = ""
403502
const val NO_VERSION = Versions.NOT_FOUND
404503

@@ -430,6 +529,8 @@ data class Alert(
430529
var aggAlertBucket: AggregationResultBucket? = null
431530
val associatedAlertIds = mutableListOf<String>()
432531
val clusters = mutableListOf<String>()
532+
var query: String? = null
533+
var queryResults: List<Map<String, Any?>> = listOf()
433534
ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp)
434535
while (xcp.nextToken() != XContentParser.Token.END_OBJECT) {
435536
val fieldName = xcp.currentName()
@@ -505,6 +606,19 @@ data class Alert(
505606
clusters.add(xcp.text())
506607
}
507608
}
609+
QUERY_FIELD -> {
610+
if (xcp.currentToken() != XContentParser.Token.VALUE_NULL) {
611+
query = xcp.text()
612+
}
613+
}
614+
QUERY_RESULTS_FIELD -> {
615+
ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp)
616+
val resultsList = mutableListOf<Map<String, Any?>>()
617+
while (xcp.nextToken() != XContentParser.Token.END_ARRAY) {
618+
resultsList.add(xcp.map())
619+
}
620+
queryResults = resultsList
621+
}
508622
}
509623
}
510624

@@ -534,7 +648,9 @@ data class Alert(
534648
workflowId = workflowId,
535649
workflowName = workflowName,
536650
associatedAlertIds = associatedAlertIds,
537-
clusters = if (clusters.size > 0) clusters else null
651+
clusters = if (clusters.size > 0) clusters else null,
652+
query = query,
653+
queryResults = queryResults
538654
)
539655
}
540656

@@ -587,6 +703,11 @@ data class Alert(
587703

588704
if (!clusters.isNullOrEmpty()) builder.field(CLUSTERS_FIELD, clusters.toTypedArray())
589705

706+
if (!query.isNullOrEmpty()) builder.field(QUERY_FIELD, query)
707+
if (queryResults.isNotEmpty()) {
708+
builder.field(QUERY_RESULTS_FIELD, queryResults.toTypedArray())
709+
}
710+
590711
builder.endObject()
591712
return builder
592713
}
@@ -611,7 +732,9 @@ data class Alert(
611732
PARENTS_BUCKET_PATH to aggregationResultBucket?.parentBucketPath,
612733
FINDING_IDS to findingIds.joinToString(","),
613734
RELATED_DOC_IDS to relatedDocIds.joinToString(","),
614-
CLUSTERS_FIELD to clusters?.joinToString(",")
735+
CLUSTERS_FIELD to clusters?.joinToString(","),
736+
QUERY_FIELD to query,
737+
QUERY_RESULTS_FIELD to queryResults
615738
)
616739
}
617740
}

src/main/kotlin/org/opensearch/commons/alerting/model/BucketLevelTrigger.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ data class BucketLevelTrigger(
6363
out.writeCollection(actions)
6464
}
6565

66-
fun asTemplateArg(): Map<String, Any> {
66+
override fun asTemplateArg(): Map<String, Any> {
6767
return mapOf(
6868
ID_FIELD to id,
6969
NAME_FIELD to name,

src/main/kotlin/org/opensearch/commons/alerting/model/ChainedAlertTrigger.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ data class ChainedAlertTrigger(
5555
}
5656

5757
/** Returns a representation of the trigger suitable for passing into painless and mustache scripts. */
58-
fun asTemplateArg(): Map<String, Any> {
58+
override fun asTemplateArg(): Map<String, Any> {
5959
return mapOf(
6060
ID_FIELD to id,
6161
NAME_FIELD to name,

src/main/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTrigger.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ data class DocumentLevelTrigger(
5555
}
5656

5757
/** Returns a representation of the trigger suitable for passing into painless and mustache scripts. */
58-
fun asTemplateArg(): Map<String, Any> {
58+
override fun asTemplateArg(): Map<String, Any> {
5959
return mapOf(
6060
ID_FIELD to id,
6161
NAME_FIELD to name,

src/main/kotlin/org/opensearch/commons/alerting/model/Input.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.opensearch.commons.alerting.model
22

33
import org.opensearch.commons.alerting.model.ClusterMetricsInput.Companion.URI_FIELD
44
import org.opensearch.commons.alerting.model.DocLevelMonitorInput.Companion.DOC_LEVEL_INPUT_FIELD
5+
import org.opensearch.commons.alerting.model.PPLInput.Companion.PPL_INPUT_FIELD
56
import org.opensearch.commons.alerting.model.SearchInput.Companion.SEARCH_FIELD
67
import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput
78
import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput.Companion.REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD
@@ -20,7 +21,8 @@ interface Input : BaseModel {
2021
CLUSTER_METRICS_INPUT(URI_FIELD),
2122
SEARCH_INPUT(SEARCH_FIELD),
2223
REMOTE_MONITOR_INPUT(REMOTE_MONITOR_INPUT_FIELD),
23-
REMOTE_DOC_LEVEL_MONITOR_INPUT(REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD);
24+
REMOTE_DOC_LEVEL_MONITOR_INPUT(REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD),
25+
PPL_INPUT(PPL_INPUT_FIELD);
2426

2527
override fun toString(): String {
2628
return value
@@ -42,8 +44,10 @@ interface Input : BaseModel {
4244
DocLevelMonitorInput.parse(xcp)
4345
} else if (xcp.currentName() == Type.REMOTE_MONITOR_INPUT.value) {
4446
RemoteMonitorInput.parse(xcp)
45-
} else {
47+
} else if (xcp.currentName() == Type.REMOTE_DOC_LEVEL_MONITOR_INPUT.value) {
4648
RemoteDocLevelMonitorInput.parse(xcp)
49+
} else {
50+
PPLInput.parseInner(xcp)
4751
}
4852
XContentParserUtils.ensureExpectedToken(XContentParser.Token.END_OBJECT, xcp.nextToken(), xcp)
4953
return input
@@ -58,6 +62,7 @@ interface Input : BaseModel {
5862
Type.SEARCH_INPUT -> SearchInput(sin)
5963
Type.REMOTE_MONITOR_INPUT -> RemoteMonitorInput(sin)
6064
Type.REMOTE_DOC_LEVEL_MONITOR_INPUT -> RemoteDocLevelMonitorInput(sin)
65+
Type.PPL_INPUT -> PPLInput(sin)
6166
// This shouldn't be reachable but ensuring exhaustiveness as Kotlin warns
6267
// enum can be null in Java
6368
else -> throw IllegalStateException("Unexpected input [$type] when reading Trigger")

0 commit comments

Comments
 (0)