Skip to content

Commit c911cfa

Browse files
PPL Alerting: Create and Update Monitor V2 (#1961)
* PPL Alerting: Create and Update Monitor V2 Signed-off-by: Dennis Toepker <[email protected]> * addressing PR comments Signed-off-by: Dennis Toepker <[email protected]> * removing AlertingV1Utils Signed-off-by: Dennis Toepker <[email protected]> --------- Signed-off-by: Dennis Toepker <[email protected]> Co-authored-by: Dennis Toepker <[email protected]>
1 parent 497dea8 commit c911cfa

File tree

20 files changed

+2044
-25
lines changed

20 files changed

+2044
-25
lines changed

alerting/build.gradle

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ dependencies {
151151
// Needed for integ tests
152152
zipArchive group: 'org.opensearch.plugin', name:'opensearch-notifications-core', version: "${opensearch_build}"
153153
zipArchive group: 'org.opensearch.plugin', name:'notifications', version: "${opensearch_build}"
154+
zipArchive group: 'org.opensearch.plugin', name:'opensearch-job-scheduler', version: "${opensearch_build}"
155+
zipArchive group: 'org.opensearch.plugin', name:'opensearch-sql-plugin', version: "${opensearch_build}"
154156

155157
// Needed for security tests
156158
if (securityEnabled) {
@@ -168,7 +170,10 @@ dependencies {
168170
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:${kotlin_version}"
169171
implementation "org.jetbrains:annotations:13.0"
170172

173+
// SQL/PPL plugin dependencies are included in alerting-core
171174
api project(":alerting-core")
175+
implementation 'org.json:json:20240303'
176+
172177
implementation "com.github.seancfoley:ipaddress:5.4.1"
173178
implementation project(path: ":alerting-spi", configuration: 'shadow')
174179

@@ -246,6 +251,28 @@ testClusters.integTest {
246251
}
247252
}))
248253

254+
plugin(provider({
255+
new RegularFile() {
256+
@Override
257+
File getAsFile() {
258+
return configurations.zipArchive.asFileTree.matching {
259+
include '**/opensearch-job-scheduler*'
260+
}.singleFile
261+
}
262+
}
263+
}))
264+
265+
plugin(provider({
266+
new RegularFile() {
267+
@Override
268+
File getAsFile() {
269+
return configurations.zipArchive.asFileTree.matching {
270+
include '**/opensearch-sql-plugin*'
271+
}.singleFile
272+
}
273+
}
274+
}))
275+
249276
if (securityEnabled) {
250277
plugin(provider({
251278
new RegularFile() {

alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.opensearch.alerting.action.GetEmailGroupAction
1414
import org.opensearch.alerting.action.GetRemoteIndexesAction
1515
import org.opensearch.alerting.action.SearchEmailAccountAction
1616
import org.opensearch.alerting.action.SearchEmailGroupAction
17+
import org.opensearch.alerting.actionv2.IndexMonitorV2Action
1718
import org.opensearch.alerting.alerts.AlertIndices
1819
import org.opensearch.alerting.alerts.AlertIndices.Companion.ALL_ALERT_INDEX_PATTERN
1920
import org.opensearch.alerting.comments.CommentsIndices
@@ -27,6 +28,7 @@ import org.opensearch.alerting.core.resthandler.RestScheduledJobStatsHandler
2728
import org.opensearch.alerting.core.schedule.JobScheduler
2829
import org.opensearch.alerting.core.settings.LegacyOpenDistroScheduledJobSettings
2930
import org.opensearch.alerting.core.settings.ScheduledJobSettings
31+
import org.opensearch.alerting.modelv2.MonitorV2
3032
import org.opensearch.alerting.remote.monitors.RemoteMonitorRegistry
3133
import org.opensearch.alerting.resthandler.RestAcknowledgeAlertAction
3234
import org.opensearch.alerting.resthandler.RestAcknowledgeChainedAlertAction
@@ -51,6 +53,7 @@ import org.opensearch.alerting.resthandler.RestSearchAlertingCommentAction
5153
import org.opensearch.alerting.resthandler.RestSearchEmailAccountAction
5254
import org.opensearch.alerting.resthandler.RestSearchEmailGroupAction
5355
import org.opensearch.alerting.resthandler.RestSearchMonitorAction
56+
import org.opensearch.alerting.resthandlerv2.RestIndexMonitorV2Action
5457
import org.opensearch.alerting.script.TriggerScript
5558
import org.opensearch.alerting.service.DeleteMonitorService
5659
import org.opensearch.alerting.settings.AlertingSettings
@@ -83,6 +86,7 @@ import org.opensearch.alerting.transport.TransportSearchAlertingCommentAction
8386
import org.opensearch.alerting.transport.TransportSearchEmailAccountAction
8487
import org.opensearch.alerting.transport.TransportSearchEmailGroupAction
8588
import org.opensearch.alerting.transport.TransportSearchMonitorAction
89+
import org.opensearch.alerting.transportv2.TransportIndexMonitorV2Action
8690
import org.opensearch.alerting.util.DocLevelMonitorQueries
8791
import org.opensearch.alerting.util.destinationmigration.DestinationMigrationCoordinator
8892
import org.opensearch.cluster.metadata.IndexNameExpressionResolver
@@ -157,6 +161,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
157161
@JvmField val OPEN_SEARCH_DASHBOARDS_USER_AGENT = "OpenSearch-Dashboards"
158162
@JvmField val UI_METADATA_EXCLUDE = arrayOf("monitor.${Monitor.UI_METADATA_FIELD}")
159163
@JvmField val MONITOR_BASE_URI = "/_plugins/_alerting/monitors"
164+
@JvmField val MONITOR_V2_BASE_URI = "/_plugins/_alerting/v2/monitors"
160165
@JvmField val WORKFLOW_BASE_URI = "/_plugins/_alerting/workflows"
161166
@JvmField val REMOTE_BASE_URI = "/_plugins/_alerting/remote"
162167
@JvmField val DESTINATION_BASE_URI = "/_plugins/_alerting/destinations"
@@ -169,7 +174,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
169174
@JvmField val FINDING_BASE_URI = "/_plugins/_alerting/findings"
170175
@JvmField val COMMENTS_BASE_URI = "/_plugins/_alerting/comments"
171176

172-
@JvmField val ALERTING_JOB_TYPES = listOf("monitor", "workflow")
177+
@JvmField val ALERTING_JOB_TYPES = listOf("monitor", "workflow", "monitor_v2")
173178
}
174179

175180
lateinit var runner: MonitorRunnerService
@@ -194,6 +199,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
194199
nodesInCluster: Supplier<DiscoveryNodes>
195200
): List<RestHandler> {
196201
return listOf(
202+
// Alerting V1
197203
RestGetMonitorAction(),
198204
RestDeleteMonitorAction(),
199205
RestIndexMonitorAction(),
@@ -218,11 +224,15 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
218224
RestIndexAlertingCommentAction(),
219225
RestSearchAlertingCommentAction(),
220226
RestDeleteAlertingCommentAction(),
227+
228+
// Alerting V2
229+
RestIndexMonitorV2Action(),
221230
)
222231
}
223232

224233
override fun getActions(): List<ActionPlugin.ActionHandler<out ActionRequest, out ActionResponse>> {
225234
return listOf(
235+
// Alerting V1
226236
ActionPlugin.ActionHandler(ScheduledJobsStatsAction.INSTANCE, ScheduledJobsStatsTransportAction::class.java),
227237
ActionPlugin.ActionHandler(AlertingActions.INDEX_MONITOR_ACTION_TYPE, TransportIndexMonitorAction::class.java),
228238
ActionPlugin.ActionHandler(AlertingActions.GET_MONITOR_ACTION_TYPE, TransportGetMonitorAction::class.java),
@@ -249,13 +259,17 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
249259
ActionPlugin.ActionHandler(AlertingActions.DELETE_COMMENT_ACTION_TYPE, TransportDeleteAlertingCommentAction::class.java),
250260
ActionPlugin.ActionHandler(ExecuteWorkflowAction.INSTANCE, TransportExecuteWorkflowAction::class.java),
251261
ActionPlugin.ActionHandler(GetRemoteIndexesAction.INSTANCE, TransportGetRemoteIndexesAction::class.java),
252-
ActionPlugin.ActionHandler(DocLevelMonitorFanOutAction.INSTANCE, TransportDocLevelMonitorFanOutAction::class.java)
262+
ActionPlugin.ActionHandler(DocLevelMonitorFanOutAction.INSTANCE, TransportDocLevelMonitorFanOutAction::class.java),
263+
264+
// Alerting V2
265+
ActionPlugin.ActionHandler(IndexMonitorV2Action.INSTANCE, TransportIndexMonitorV2Action::class.java),
253266
)
254267
}
255268

256269
override fun getNamedXContent(): List<NamedXContentRegistry.Entry> {
257270
return listOf(
258271
Monitor.XCONTENT_REGISTRY,
272+
MonitorV2.XCONTENT_REGISTRY,
259273
SearchInput.XCONTENT_REGISTRY,
260274
DocLevelMonitorInput.XCONTENT_REGISTRY,
261275
QueryLevelTrigger.XCONTENT_REGISTRY,
@@ -431,7 +445,22 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
431445
AlertingSettings.COMMENTS_HISTORY_RETENTION_PERIOD,
432446
AlertingSettings.COMMENTS_MAX_CONTENT_SIZE,
433447
AlertingSettings.MAX_COMMENTS_PER_ALERT,
434-
AlertingSettings.MAX_COMMENTS_PER_NOTIFICATION
448+
AlertingSettings.MAX_COMMENTS_PER_NOTIFICATION,
449+
AlertingSettings.ALERT_V2_HISTORY_ENABLED,
450+
AlertingSettings.ALERT_V2_HISTORY_ROLLOVER_PERIOD,
451+
AlertingSettings.ALERT_V2_HISTORY_INDEX_MAX_AGE,
452+
AlertingSettings.ALERT_V2_HISTORY_MAX_DOCS,
453+
AlertingSettings.ALERT_V2_HISTORY_RETENTION_PERIOD,
454+
AlertingSettings.ALERTING_V2_MAX_MONITORS,
455+
AlertingSettings.ALERTING_V2_MAX_THROTTLE_DURATION,
456+
AlertingSettings.ALERTING_V2_MAX_EXPIRE_DURATION,
457+
AlertingSettings.ALERTING_V2_MAX_LOOK_BACK_WINDOW,
458+
AlertingSettings.ALERTING_V2_MAX_QUERY_LENGTH,
459+
AlertingSettings.ALERTING_V2_QUERY_RESULTS_MAX_DATAROWS,
460+
AlertingSettings.ALERT_V2_QUERY_RESULTS_MAX_SIZE,
461+
AlertingSettings.ALERT_V2_PER_RESULT_TRIGGER_MAX_ALERTS,
462+
AlertingSettings.NOTIFICATION_SUBJECT_SOURCE_MAX_LENGTH,
463+
AlertingSettings.NOTIFICATION_MESSAGE_SOURCE_MAX_LENGTH
435464
)
436465
}
437466

@@ -449,7 +478,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R
449478
return listOf(
450479
SystemIndexDescriptor(ALL_ALERT_INDEX_PATTERN, "Alerting Plugin system index pattern"),
451480
SystemIndexDescriptor(SCHEDULED_JOBS_INDEX, "Alerting Plugin Configuration index"),
452-
SystemIndexDescriptor(ALL_COMMENTS_INDEX_PATTERN, "Alerting Comments system index pattern")
481+
SystemIndexDescriptor(ALL_COMMENTS_INDEX_PATTERN, "Alerting Comments system index pattern"),
453482
)
454483
}
455484

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.alerting
7+
8+
import org.apache.lucene.search.TotalHits
9+
import org.apache.lucene.search.TotalHits.Relation
10+
import org.opensearch.action.search.SearchResponse
11+
import org.opensearch.action.search.ShardSearchFailure
12+
import org.opensearch.alerting.modelv2.MonitorV2
13+
import org.opensearch.commons.alerting.model.Monitor
14+
import org.opensearch.commons.alerting.model.ScheduledJob
15+
import org.opensearch.commons.alerting.model.Workflow
16+
import org.opensearch.index.IndexNotFoundException
17+
import org.opensearch.search.SearchHits
18+
import org.opensearch.search.aggregations.InternalAggregations
19+
import org.opensearch.search.internal.InternalSearchResponse
20+
import org.opensearch.search.profile.SearchProfileShardResults
21+
import org.opensearch.search.suggest.Suggest
22+
import org.opensearch.transport.RemoteTransportException
23+
import java.util.Collections
24+
25+
object AlertingV2Utils {
26+
// Validates that the given scheduled job is a Monitor
27+
// returns the exception to pass into actionListener.onFailure if not.
28+
fun validateMonitorV1(scheduledJob: ScheduledJob): Exception? {
29+
if (scheduledJob is MonitorV2) {
30+
return IllegalStateException("The ID given corresponds to a V2 Monitor, but a V1 Monitor was expected")
31+
} else if (scheduledJob !is Monitor && scheduledJob !is Workflow) {
32+
return IllegalStateException("The ID given corresponds to a scheduled job of unknown type: ${scheduledJob.javaClass.name}")
33+
}
34+
return null
35+
}
36+
37+
// Validates that the given scheduled job is a MonitorV2
38+
// returns the exception to pass into actionListener.onFailure if not.
39+
fun validateMonitorV2(scheduledJob: ScheduledJob): Exception? {
40+
if (scheduledJob is Monitor || scheduledJob is Workflow) {
41+
return IllegalStateException("The ID given corresponds to a V1 Monitor, but a V2 Monitor was expected")
42+
} else if (scheduledJob !is MonitorV2) {
43+
return IllegalStateException("The ID given corresponds to a scheduled job of unknown type: ${scheduledJob.javaClass.name}")
44+
}
45+
return null
46+
}
47+
48+
// Checks if the exception is caused by an IndexNotFoundException (directly or nested).
49+
// Used in Get and Search monitor functionalities to determine whether a "no results"
50+
// response should be returned
51+
fun isIndexNotFoundException(e: Exception): Boolean {
52+
if (e is IndexNotFoundException) {
53+
return true
54+
}
55+
if (e is RemoteTransportException) {
56+
val cause = e.cause
57+
if (cause is IndexNotFoundException) {
58+
return true
59+
}
60+
}
61+
return false
62+
}
63+
64+
// Used in Get and Search monitor functionalities to return a "no results" response
65+
fun getEmptySearchResponse(): SearchResponse {
66+
val internalSearchResponse = InternalSearchResponse(
67+
SearchHits(emptyArray(), TotalHits(0L, Relation.EQUAL_TO), 0.0f),
68+
InternalAggregations.from(Collections.emptyList()),
69+
Suggest(Collections.emptyList()),
70+
SearchProfileShardResults(Collections.emptyMap()),
71+
false,
72+
false,
73+
0
74+
)
75+
76+
return SearchResponse(
77+
internalSearchResponse,
78+
"",
79+
0,
80+
0,
81+
0,
82+
0,
83+
ShardSearchFailure.EMPTY_ARRAY,
84+
SearchResponse.Clusters.EMPTY
85+
)
86+
}
87+
}

0 commit comments

Comments
 (0)