diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 31e21d66b..75f22834a 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -38,4 +38,5 @@ jobs: export SONATYPE_PASSWORD=$(aws secretsmanager get-secret-value --secret-id maven-snapshots-password --query SecretString --output text) echo "::add-mask::$SONATYPE_USERNAME" echo "::add-mask::$SONATYPE_PASSWORD" + ./gradlew publishShadowPublicationToSnapshotsRepository ./gradlew publishPluginZipPublicationToSnapshotsRepository diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml index ebc4c2cec..65d32abbb 100644 --- a/.github/workflows/test-workflow.yml +++ b/.github/workflows/test-workflow.yml @@ -96,7 +96,7 @@ jobs: java-version: ${{ matrix.java }} - name: Build and run with Gradle working-directory: ${{ env.WORKING_DIR }} - run: ./gradlew assemble integTest ${{ env.BUILD_ARGS }} + run: ./gradlew assemble ${{ env.BUILD_ARGS }} env: _JAVA_OPTIONS: ${{ matrix.os_java_options }} - name: Create Artifact Path diff --git a/alerting/build.gradle b/alerting/build.gradle index d0f240193..bb6b8e60e 100644 --- a/alerting/build.gradle +++ b/alerting/build.gradle @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin import java.util.concurrent.Callable import org.opensearch.gradle.test.RestIntegTestTask import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask @@ -109,6 +110,7 @@ dependencies { api project(":alerting-core") implementation "com.github.seancfoley:ipaddress:5.4.1" + implementation project(path: ":alerting-spi", configuration: 'shadow') testImplementation "org.antlr:antlr4-runtime:${versions.antlr4}" testImplementation "org.jetbrains.kotlin:kotlin-test:${kotlin_version}" diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/AlertService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/AlertService.kt index 722b315f3..6ab37e412 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/AlertService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/AlertService.kt @@ -18,10 +18,6 @@ import org.opensearch.action.search.SearchRequest import org.opensearch.action.search.SearchResponse import org.opensearch.action.support.WriteRequest import org.opensearch.alerting.alerts.AlertIndices -import org.opensearch.alerting.model.ActionRunResult -import org.opensearch.alerting.model.ChainedAlertTriggerRunResult -import org.opensearch.alerting.model.ClusterMetricsTriggerRunResult -import org.opensearch.alerting.model.QueryLevelTriggerRunResult import org.opensearch.alerting.opensearchapi.firstFailureOrNull import org.opensearch.alerting.opensearchapi.retry import org.opensearch.alerting.opensearchapi.suspendUntil @@ -32,7 +28,6 @@ import org.opensearch.alerting.util.CommentsUtils import org.opensearch.alerting.util.IndexUtils import org.opensearch.alerting.util.MAX_SEARCH_SIZE import org.opensearch.alerting.util.getBucketKeysHash -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.client.Client import org.opensearch.common.unit.TimeValue import org.opensearch.common.xcontent.LoggingDeprecationHandler @@ -41,14 +36,19 @@ import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.alerts.AlertError import org.opensearch.commons.alerting.model.ActionExecutionResult +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.AggregationResultBucket import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.ChainedAlertTriggerRunResult +import org.opensearch.commons.alerting.model.ClusterMetricsTriggerRunResult import org.opensearch.commons.alerting.model.DataSources import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.NoOpTrigger +import org.opensearch.commons.alerting.model.QueryLevelTriggerRunResult import org.opensearch.commons.alerting.model.Trigger import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.commons.alerting.model.action.AlertCategory import org.opensearch.core.action.ActionListener import org.opensearch.core.common.bytes.BytesReference diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt index bb25c0bf1..5f5f07cea 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt @@ -6,7 +6,6 @@ package org.opensearch.alerting import org.opensearch.action.ActionRequest -import org.opensearch.alerting.action.DocLevelMonitorFanOutAction import org.opensearch.alerting.action.ExecuteMonitorAction import org.opensearch.alerting.action.ExecuteWorkflowAction import org.opensearch.alerting.action.GetDestinationsAction @@ -26,6 +25,7 @@ import org.opensearch.alerting.core.resthandler.RestScheduledJobStatsHandler import org.opensearch.alerting.core.schedule.JobScheduler import org.opensearch.alerting.core.settings.LegacyOpenDistroScheduledJobSettings import org.opensearch.alerting.core.settings.ScheduledJobSettings +import org.opensearch.alerting.remote.monitors.RemoteMonitorRegistry import org.opensearch.alerting.resthandler.RestAcknowledgeAlertAction import org.opensearch.alerting.resthandler.RestAcknowledgeChainedAlertAction import org.opensearch.alerting.resthandler.RestDeleteAlertingCommentAction @@ -56,6 +56,7 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.DOC_LEVEL_MON import org.opensearch.alerting.settings.DestinationSettings import org.opensearch.alerting.settings.LegacyOpenDistroAlertingSettings import org.opensearch.alerting.settings.LegacyOpenDistroDestinationSettings +import org.opensearch.alerting.spi.RemoteMonitorRunnerExtension import org.opensearch.alerting.transport.TransportAcknowledgeAlertAction import org.opensearch.alerting.transport.TransportAcknowledgeChainedAlertAction import org.opensearch.alerting.transport.TransportDeleteAlertingCommentAction @@ -92,6 +93,7 @@ import org.opensearch.common.settings.Setting import org.opensearch.common.settings.Settings import org.opensearch.common.settings.SettingsFilter import org.opensearch.commons.alerting.action.AlertingActions +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutAction import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelectorExtAggregationBuilder import org.opensearch.commons.alerting.model.BucketLevelTrigger import org.opensearch.commons.alerting.model.ChainedAlertTrigger @@ -103,6 +105,7 @@ import org.opensearch.commons.alerting.model.QueryLevelTrigger import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.SearchInput import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorTrigger import org.opensearch.core.action.ActionResponse import org.opensearch.core.common.io.stream.NamedWriteableRegistry import org.opensearch.core.common.io.stream.StreamInput @@ -117,6 +120,7 @@ import org.opensearch.painless.spi.Whitelist import org.opensearch.painless.spi.WhitelistLoader import org.opensearch.percolator.PercolatorPluginExt import org.opensearch.plugins.ActionPlugin +import org.opensearch.plugins.ExtensiblePlugin import org.opensearch.plugins.ReloadablePlugin import org.opensearch.plugins.ScriptPlugin import org.opensearch.plugins.SearchPlugin @@ -180,6 +184,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R lateinit var alertIndices: AlertIndices lateinit var clusterService: ClusterService lateinit var destinationMigrationCoordinator: DestinationMigrationCoordinator + var monitorTypeToMonitorRunners: MutableMap = mutableMapOf() override fun getRestHandlers( settings: Settings, @@ -260,6 +265,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R ClusterMetricsInput.XCONTENT_REGISTRY, DocumentLevelTrigger.XCONTENT_REGISTRY, ChainedAlertTrigger.XCONTENT_REGISTRY, + RemoteMonitorTrigger.XCONTENT_REGISTRY, Workflow.XCONTENT_REGISTRY ) } @@ -301,6 +307,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R .registerLockService(lockService) .registerConsumers() .registerDestinationSettings() + .registerRemoteMonitors(monitorTypeToMonitorRunners) scheduledJobIndices = ScheduledJobIndices(client.admin(), clusterService) commentsIndices = CommentsIndices(environment.settings(), client, threadPool, clusterService) docLevelMonitorQueries = DocLevelMonitorQueries(client, clusterService) @@ -443,4 +450,20 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R ) ) } + + override fun loadExtensions(loader: ExtensiblePlugin.ExtensionLoader) { + for (monitorExtension in loader.loadExtensions(RemoteMonitorRunnerExtension::class.java)) { + val monitorTypesToMonitorRunners = monitorExtension.getMonitorTypesToMonitorRunners() + + for (monitorTypeToMonitorRunner in monitorTypesToMonitorRunners) { + val monitorType = monitorTypeToMonitorRunner.key + val monitorRunner = monitorTypeToMonitorRunner.value + + if (!this.monitorTypeToMonitorRunners.containsKey(monitorType)) { + val monitorRegistry = RemoteMonitorRegistry(monitorType, monitorRunner) + this.monitorTypeToMonitorRunners[monitorType] = monitorRegistry + } + } + } + } } diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/BucketLevelMonitorRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/BucketLevelMonitorRunner.kt index 0ef5e0abd..34a2b46d3 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/BucketLevelMonitorRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/BucketLevelMonitorRunner.kt @@ -12,11 +12,7 @@ import org.opensearch.action.index.IndexRequest import org.opensearch.action.search.SearchRequest import org.opensearch.action.search.SearchResponse import org.opensearch.action.support.WriteRequest -import org.opensearch.alerting.model.ActionRunResult import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.BucketLevelTriggerRunResult -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.alerting.opensearchapi.InjectorContextElement import org.opensearch.alerting.opensearchapi.convertToMap import org.opensearch.alerting.opensearchapi.retry @@ -31,17 +27,21 @@ import org.opensearch.alerting.util.getBucketKeysHash import org.opensearch.alerting.util.getCancelAfterTimeInterval import org.opensearch.alerting.util.getCombinedTriggerRunResult import org.opensearch.alerting.util.printsSampleDocData -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.client.Client import org.opensearch.common.unit.TimeValue import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentType +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult import org.opensearch.commons.alerting.model.Comment import org.opensearch.commons.alerting.model.Finding +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.SearchInput +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.commons.alerting.model.action.AlertCategory import org.opensearch.commons.alerting.model.action.PerAlertActionScope import org.opensearch.commons.alerting.model.action.PerExecutionActionScope diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/DocumentLevelMonitorRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/DocumentLevelMonitorRunner.kt index cc15ad71a..f28b03292 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/DocumentLevelMonitorRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/DocumentLevelMonitorRunner.kt @@ -10,24 +10,24 @@ import org.opensearch.ExceptionsHelper import org.opensearch.Version import org.opensearch.action.ActionListenerResponseHandler import org.opensearch.action.support.GroupedActionListener -import org.opensearch.alerting.action.DocLevelMonitorFanOutAction -import org.opensearch.alerting.action.DocLevelMonitorFanOutRequest -import org.opensearch.alerting.action.DocLevelMonitorFanOutResponse -import org.opensearch.alerting.model.ActionRunResult -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.IndexExecutionContext -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IndexUtils -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.cluster.metadata.IndexMetadata import org.opensearch.cluster.node.DiscoveryNode import org.opensearch.cluster.routing.ShardRouting import org.opensearch.cluster.service.ClusterService +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutAction +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutRequest +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.DocLevelQuery +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult +import org.opensearch.commons.alerting.model.IndexExecutionContext +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult +import org.opensearch.commons.alerting.model.WorkflowRunContext +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.common.breaker.CircuitBreakingException import org.opensearch.core.common.io.stream.Writeable @@ -436,7 +436,7 @@ class DocumentLevelMonitorRunner : MonitorRunner() { if (res.exception == null) { return null } else { - exceptions.add(res.exception) + exceptions.add(res.exception!!) } } return AlertingException.merge(*exceptions.toTypedArray()) @@ -501,9 +501,9 @@ class DocumentLevelMonitorRunner : MonitorRunner() { if (response.exception == null) { if (response.inputResults.error != null) { if (response.inputResults.error is AlertingException) { - errors.add(response.inputResults.error) + errors.add(response.inputResults.error as AlertingException) } else { - errors.add(AlertingException.wrap(response.inputResults.error) as AlertingException) + errors.add(AlertingException.wrap(response.inputResults.error as Exception) as AlertingException) } } val partialResult = response.inputResults.results diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/InputService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/InputService.kt index 48cd37e81..e7b107e2b 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/InputService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/InputService.kt @@ -10,8 +10,6 @@ import kotlinx.coroutines.withContext import org.apache.logging.log4j.LogManager import org.opensearch.action.search.SearchRequest import org.opensearch.action.search.SearchResponse -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.TriggerAfterKey import org.opensearch.alerting.opensearchapi.convertToMap import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings @@ -21,7 +19,6 @@ import org.opensearch.alerting.util.addUserBackendRolesFilter import org.opensearch.alerting.util.clusterMetricsMonitorHelpers.executeTransportAction import org.opensearch.alerting.util.clusterMetricsMonitorHelpers.toMap import org.opensearch.alerting.util.getRoleFilterEnabled -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.client.Client import org.opensearch.cluster.routing.Preference import org.opensearch.cluster.service.ClusterService @@ -30,8 +27,11 @@ import org.opensearch.common.settings.Settings import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.ClusterMetricsInput +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.SearchInput +import org.opensearch.commons.alerting.model.TriggerAfterKey +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput import org.opensearch.core.common.io.stream.NamedWriteableRegistry import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorMetadataService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorMetadataService.kt index da167ed49..dabd3069d 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorMetadataService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorMetadataService.kt @@ -25,10 +25,8 @@ import org.opensearch.action.get.GetResponse import org.opensearch.action.index.IndexRequest import org.opensearch.action.index.IndexResponse import org.opensearch.action.support.WriteRequest -import org.opensearch.alerting.model.MonitorMetadata import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IndexUtils import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -40,7 +38,10 @@ import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.ToXContent @@ -187,12 +188,14 @@ object MonitorMetadataService : suspend fun recreateRunContext(metadata: MonitorMetadata, monitor: Monitor): MonitorMetadata { try { - val monitorIndex = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + val monitorIndex = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR.value) (monitor.inputs[0] as DocLevelMonitorInput).indices[0] - } else null - val runContext = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + else if (monitor.monitorType.endsWith(Monitor.MonitorType.DOC_LEVEL_MONITOR.value)) + (monitor.inputs[0] as RemoteDocLevelMonitorInput).docLevelMonitorInput.indices[0] + else null + val runContext = if (monitor.monitorType.endsWith(Monitor.MonitorType.DOC_LEVEL_MONITOR.value)) createFullRunContext(monitorIndex, metadata.lastRunContext as MutableMap>) - } else null + else null return if (runContext != null) { metadata.copy( lastRunContext = runContext @@ -210,10 +213,12 @@ object MonitorMetadataService : createWithRunContext: Boolean, workflowMetadataId: String? = null, ): MonitorMetadata { - val monitorIndex = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) + val monitorIndex = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR.value) (monitor.inputs[0] as DocLevelMonitorInput).indices[0] + else if (monitor.monitorType.endsWith(Monitor.MonitorType.DOC_LEVEL_MONITOR.value)) + (monitor.inputs[0] as RemoteDocLevelMonitorInput).docLevelMonitorInput.indices[0] else null - val runContext = if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR && createWithRunContext) + val runContext = if (monitor.monitorType.endsWith(Monitor.MonitorType.DOC_LEVEL_MONITOR.value)) createFullRunContext(monitorIndex) else emptyMap() return MonitorMetadata( diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunner.kt index 5692e6120..c88c77a77 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunner.kt @@ -9,8 +9,6 @@ import org.opensearch.OpenSearchSecurityException import org.opensearch.alerting.action.GetDestinationsAction import org.opensearch.alerting.action.GetDestinationsRequest import org.opensearch.alerting.action.GetDestinationsResponse -import org.opensearch.alerting.model.ActionRunResult -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.alerting.model.destination.Destination import org.opensearch.alerting.opensearchapi.InjectorContextElement import org.opensearch.alerting.opensearchapi.suspendUntil @@ -24,10 +22,12 @@ import org.opensearch.alerting.util.destinationmigration.publishLegacyNotificati import org.opensearch.alerting.util.destinationmigration.sendNotification import org.opensearch.alerting.util.isAllowed import org.opensearch.alerting.util.isTestAction -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.client.node.NodeClient +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.Table +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.commons.alerting.model.action.Action import org.opensearch.commons.notifications.model.NotificationConfigInfo import org.opensearch.core.common.Strings diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerExecutionContext.kt index 459743f9d..523c2be3a 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerExecutionContext.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerExecutionContext.kt @@ -9,6 +9,7 @@ import org.opensearch.action.bulk.BackoffPolicy import org.opensearch.alerting.alerts.AlertIndices import org.opensearch.alerting.core.lock.LockService import org.opensearch.alerting.model.destination.DestinationContextFactory +import org.opensearch.alerting.remote.monitors.RemoteMonitorRegistry import org.opensearch.alerting.settings.AlertingSettings import org.opensearch.alerting.settings.DestinationSettings import org.opensearch.alerting.settings.LegacyOpenDistroDestinationSettings @@ -41,6 +42,7 @@ data class MonitorRunnerExecutionContext( var workflowService: WorkflowService? = null, var jvmStats: JvmStats? = null, var findingsToTriggeredQueries: Map>? = null, + var remoteMonitors: Map = mapOf(), @Volatile var retryPolicy: BackoffPolicy? = null, @Volatile var moveAlertsRetryPolicy: BackoffPolicy? = null, diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerService.kt index 3ddff1b03..e3147a2d9 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/MonitorRunnerService.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import org.apache.logging.log4j.LogManager +import org.opensearch.OpenSearchStatusException import org.opensearch.action.bulk.BackoffPolicy import org.opensearch.action.search.TransportSearchAction.SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING import org.opensearch.action.support.master.AcknowledgedResponse @@ -26,11 +27,11 @@ import org.opensearch.alerting.core.JobRunner import org.opensearch.alerting.core.ScheduledJobIndices import org.opensearch.alerting.core.lock.LockModel import org.opensearch.alerting.core.lock.LockService -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.model.WorkflowRunResult import org.opensearch.alerting.model.destination.DestinationContextFactory import org.opensearch.alerting.opensearchapi.retry import org.opensearch.alerting.opensearchapi.suspendUntil +import org.opensearch.alerting.remote.monitors.RemoteDocumentLevelMonitorRunner +import org.opensearch.alerting.remote.monitors.RemoteMonitorRegistry import org.opensearch.alerting.script.TriggerExecutionContext import org.opensearch.alerting.settings.AlertingSettings import org.opensearch.alerting.settings.AlertingSettings.Companion.ALERT_BACKOFF_COUNT @@ -59,11 +60,16 @@ import org.opensearch.common.settings.Settings import org.opensearch.common.unit.TimeValue import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.model.TriggerRunResult import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunResult import org.opensearch.commons.alerting.model.action.Action import org.opensearch.commons.alerting.util.isBucketLevelMonitor +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.core.action.ActionListener +import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.monitor.jvm.JvmStats import org.opensearch.script.Script @@ -156,6 +162,11 @@ object MonitorRunnerService : JobRunner, CoroutineScope, AbstractLifecycleCompon return this } + fun registerRemoteMonitors(monitorRegistry: Map): MonitorRunnerService { + this.monitorCtx.remoteMonitors = monitorRegistry + return this + } + // Must be called after registerClusterService and registerSettings in AlertingPlugin fun registerConsumers(): MonitorRunnerService { monitorCtx.retryPolicy = BackoffPolicy.constantBackoff( @@ -424,42 +435,75 @@ object MonitorRunnerService : JobRunner, CoroutineScope, AbstractLifecycleCompon } val monitor = job as Monitor val executionId = "${monitor.id}_${LocalDateTime.now(ZoneOffset.UTC)}_${UUID.randomUUID()}" - logger.info( - "Executing scheduled monitor - id: ${monitor.id}, type: ${monitor.monitorType.name}, periodStart: $periodStart, " + - "periodEnd: $periodEnd, dryrun: $dryrun, executionId: $executionId" - ) - val runResult = if (monitor.isBucketLevelMonitor()) { - BucketLevelMonitorRunner.runMonitor( - monitor, - monitorCtx, - periodStart, - periodEnd, - dryrun, - executionId = executionId, - transportService = transportService - ) - } else if (monitor.isDocLevelMonitor()) { - DocumentLevelMonitorRunner().runMonitor( - monitor, - monitorCtx, - periodStart, - periodEnd, - dryrun, - executionId = executionId, - transportService = transportService + + if (monitor.isMonitorOfStandardType()) { + logger.info( + "Executing scheduled monitor - id: ${monitor.id}, type: ${monitor.monitorType}, periodStart: $periodStart, " + + "periodEnd: $periodEnd, dryrun: $dryrun, executionId: $executionId" ) + val runResult = if (monitor.isBucketLevelMonitor()) { + BucketLevelMonitorRunner.runMonitor( + monitor, + monitorCtx, + periodStart, + periodEnd, + dryrun, + executionId = executionId, + transportService = transportService + ) + } else if (monitor.isDocLevelMonitor()) { + DocumentLevelMonitorRunner().runMonitor( + monitor, + monitorCtx, + periodStart, + periodEnd, + dryrun, + executionId = executionId, + transportService = transportService + ) + } else { + QueryLevelMonitorRunner.runMonitor( + monitor, + monitorCtx, + periodStart, + periodEnd, + dryrun, + executionId = executionId, + transportService = transportService + ) + } + return runResult } else { - QueryLevelMonitorRunner.runMonitor( - monitor, - monitorCtx, - periodStart, - periodEnd, - dryrun, - executionId = executionId, - transportService = transportService - ) + if (monitorCtx.remoteMonitors.containsKey(monitor.monitorType)) { + if (monitor.monitorType.endsWith(Monitor.MonitorType.DOC_LEVEL_MONITOR.value)) { + return RemoteDocumentLevelMonitorRunner().runMonitor( + monitor, + monitorCtx, + periodStart, + periodEnd, + dryrun, + executionId = executionId, + transportService = transportService + ) + } else { + return monitorCtx.remoteMonitors[monitor.monitorType]!!.monitorRunner.runMonitor( + monitor, + periodStart, + periodEnd, + dryrun, + executionId, + transportService + ) + } + } else { + return MonitorRunResult( + monitor.name, + periodStart, + periodEnd, + OpenSearchStatusException("Monitor Type ${monitor.monitorType} not known", RestStatus.BAD_REQUEST) + ) + } } - return runResult } // TODO: See if we can move below methods (or few of these) to a common utils diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/QueryLevelMonitorRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/QueryLevelMonitorRunner.kt index b00caec7d..3ed7a74ce 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/QueryLevelMonitorRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/QueryLevelMonitorRunner.kt @@ -7,18 +7,18 @@ package org.opensearch.alerting import org.apache.logging.log4j.LogManager import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.model.QueryLevelTriggerRunResult import org.opensearch.alerting.opensearchapi.InjectorContextElement import org.opensearch.alerting.opensearchapi.withClosableContext import org.opensearch.alerting.script.QueryLevelTriggerExecutionContext import org.opensearch.alerting.settings.AlertingSettings import org.opensearch.alerting.util.CommentsUtils import org.opensearch.alerting.util.isADMonitor -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.QueryLevelTrigger +import org.opensearch.commons.alerting.model.QueryLevelTriggerRunResult +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.transport.TransportService import java.time.Instant @@ -82,9 +82,9 @@ object QueryLevelMonitorRunner : MonitorRunner() { } val triggerCtx = QueryLevelTriggerExecutionContext(monitor, trigger as QueryLevelTrigger, monitorResult, currentAlertContext) val triggerResult = when (monitor.monitorType) { - Monitor.MonitorType.QUERY_LEVEL_MONITOR -> + Monitor.MonitorType.QUERY_LEVEL_MONITOR.value -> monitorCtx.triggerService!!.runQueryLevelTrigger(monitor, trigger, triggerCtx) - Monitor.MonitorType.CLUSTER_METRICS_MONITOR -> { + Monitor.MonitorType.CLUSTER_METRICS_MONITOR.value -> { val remoteMonitoringEnabled = monitorCtx.clusterService!!.clusterSettings.get(AlertingSettings.CROSS_CLUSTER_MONITORING_ENABLED) logger.debug("Remote monitoring enabled: {}", remoteMonitoringEnabled) diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/TriggerService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/TriggerService.kt index d1edf8fc1..e59b48eeb 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/TriggerService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/TriggerService.kt @@ -7,12 +7,6 @@ package org.opensearch.alerting import org.apache.logging.log4j.LogManager import org.opensearch.alerting.chainedAlertCondition.parsers.ChainedAlertExpressionParser -import org.opensearch.alerting.model.BucketLevelTriggerRunResult -import org.opensearch.alerting.model.ChainedAlertTriggerRunResult -import org.opensearch.alerting.model.ClusterMetricsTriggerRunResult -import org.opensearch.alerting.model.ClusterMetricsTriggerRunResult.ClusterTriggerResult -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.QueryLevelTriggerRunResult import org.opensearch.alerting.script.BucketLevelTriggerExecutionContext import org.opensearch.alerting.script.ChainedAlertTriggerExecutionContext import org.opensearch.alerting.script.QueryLevelTriggerExecutionContext @@ -20,19 +14,25 @@ import org.opensearch.alerting.script.TriggerScript import org.opensearch.alerting.triggercondition.parsers.TriggerExpressionParser import org.opensearch.alerting.util.CrossClusterMonitorUtils import org.opensearch.alerting.util.getBucketKeysHash -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.cluster.service.ClusterService import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelectorIndices.Fields.BUCKET_INDICES import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelectorIndices.Fields.PARENT_BUCKET_PATH import org.opensearch.commons.alerting.model.AggregationResultBucket import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult import org.opensearch.commons.alerting.model.ChainedAlertTrigger +import org.opensearch.commons.alerting.model.ChainedAlertTriggerRunResult +import org.opensearch.commons.alerting.model.ClusterMetricsTriggerRunResult +import org.opensearch.commons.alerting.model.ClusterMetricsTriggerRunResult.ClusterTriggerResult import org.opensearch.commons.alerting.model.DocLevelQuery import org.opensearch.commons.alerting.model.DocumentLevelTrigger +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.QueryLevelTrigger +import org.opensearch.commons.alerting.model.QueryLevelTriggerRunResult import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.script.Script import org.opensearch.script.ScriptService import org.opensearch.search.aggregations.Aggregation diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowMetadataService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowMetadataService.kt index 9dc4fbcdd..f56dd5fd7 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowMetadataService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowMetadataService.kt @@ -18,10 +18,8 @@ import org.opensearch.action.get.GetResponse import org.opensearch.action.index.IndexRequest import org.opensearch.action.index.IndexResponse import org.opensearch.action.support.WriteRequest -import org.opensearch.alerting.model.WorkflowMetadata import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.settings.Settings @@ -33,6 +31,8 @@ import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.CompositeInput import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowMetadata +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.ToXContent diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowService.kt index 04bd64b8d..32d7971f1 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/WorkflowService.kt @@ -12,13 +12,13 @@ import org.opensearch.action.admin.indices.exists.indices.IndicesExistsResponse import org.opensearch.action.search.SearchRequest import org.opensearch.action.search.SearchResponse import org.opensearch.alerting.opensearchapi.suspendUntil -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.Finding import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutAction.kt deleted file mode 100644 index c03d95942..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutAction.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.action - -import org.opensearch.action.ActionType - -class DocLevelMonitorFanOutAction private constructor() : ActionType(NAME, ::DocLevelMonitorFanOutResponse) { - companion object { - val INSTANCE = DocLevelMonitorFanOutAction() - const val NAME = "cluster:admin/opensearch/alerting/monitor/doclevel/fanout" - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequest.kt b/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequest.kt deleted file mode 100644 index 7b16b8961..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequest.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.action - -import org.opensearch.action.ActionRequest -import org.opensearch.action.ActionRequestValidationException -import org.opensearch.alerting.model.IndexExecutionContext -import org.opensearch.alerting.model.MonitorMetadata -import org.opensearch.alerting.workflow.WorkflowRunContext -import org.opensearch.commons.alerting.model.Monitor -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.index.shard.ShardId -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.ToXContentObject -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException - -class DocLevelMonitorFanOutRequest : ActionRequest, ToXContentObject { - val monitor: Monitor - val dryRun: Boolean - val monitorMetadata: MonitorMetadata - val executionId: String - val indexExecutionContext: IndexExecutionContext - val shardIds: List - val concreteIndicesSeenSoFar: List - val workflowRunContext: WorkflowRunContext? - - constructor( - monitor: Monitor, - dryRun: Boolean, - monitorMetadata: MonitorMetadata, - executionId: String, - indexExecutionContext: IndexExecutionContext, - shardIds: List, - concreteIndicesSeenSoFar: List, - workflowRunContext: WorkflowRunContext?, - ) : super() { - this.monitor = monitor - this.dryRun = dryRun - this.monitorMetadata = monitorMetadata - this.executionId = executionId - this.indexExecutionContext = indexExecutionContext - this.shardIds = shardIds - this.concreteIndicesSeenSoFar = concreteIndicesSeenSoFar - this.workflowRunContext = workflowRunContext - require(false == shardIds.isEmpty()) { } - } - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - monitor = Monitor.readFrom(sin)!!, - dryRun = sin.readBoolean(), - monitorMetadata = MonitorMetadata.readFrom(sin), - executionId = sin.readString(), - shardIds = sin.readList(::ShardId), - concreteIndicesSeenSoFar = sin.readStringList(), - workflowRunContext = if (sin.readBoolean()) { - WorkflowRunContext(sin) - } else null, - indexExecutionContext = IndexExecutionContext(sin) - ) - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - monitor.writeTo(out) - out.writeBoolean(dryRun) - monitorMetadata.writeTo(out) - out.writeString(executionId) - out.writeCollection(shardIds) - out.writeStringCollection(concreteIndicesSeenSoFar) - out.writeBoolean(workflowRunContext != null) - workflowRunContext?.writeTo(out) - indexExecutionContext.writeTo(out) - } - - override fun validate(): ActionRequestValidationException? { - var actionValidationException: ActionRequestValidationException? = null - if (shardIds.isEmpty()) { - actionValidationException = ActionRequestValidationException() - actionValidationException.addValidationError("shard_ids is null or empty") - } - return actionValidationException - } - - @Throws(IOException::class) - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - .field("monitor", monitor) - .field("dry_run", dryRun) - .field("execution_id", executionId) - .field("index_execution_context", indexExecutionContext) - .field("shard_ids", shardIds) - .field("concrete_indices", concreteIndicesSeenSoFar) - .field("workflow_run_context", workflowRunContext) - return builder.endObject() - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponse.kt b/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponse.kt deleted file mode 100644 index 8c74de356..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponse.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.action - -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.util.AlertingException -import org.opensearch.core.action.ActionResponse -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.ToXContentObject -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException - -class DocLevelMonitorFanOutResponse : ActionResponse, ToXContentObject { - val nodeId: String - val executionId: String - val monitorId: String - val lastRunContexts: MutableMap - val inputResults: InputRunResults - val triggerResults: Map - val exception: AlertingException? - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - nodeId = sin.readString(), - executionId = sin.readString(), - monitorId = sin.readString(), - lastRunContexts = sin.readMap()!! as MutableMap, - inputResults = InputRunResults.readFrom(sin), - triggerResults = suppressWarning(sin.readMap(StreamInput::readString, DocumentLevelTriggerRunResult::readFrom)), - exception = sin.readException() - ) - - constructor( - nodeId: String, - executionId: String, - monitorId: String, - lastRunContexts: MutableMap, - inputResults: InputRunResults = InputRunResults(), // partial, - triggerResults: Map = mapOf(), - exception: AlertingException? = null - ) : super() { - this.nodeId = nodeId - this.executionId = executionId - this.monitorId = monitorId - this.lastRunContexts = lastRunContexts - this.inputResults = inputResults - this.triggerResults = triggerResults - this.exception = exception - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeString(nodeId) - out.writeString(executionId) - out.writeString(monitorId) - out.writeMap(lastRunContexts) - inputResults.writeTo(out) - out.writeMap( - triggerResults, - StreamOutput::writeString, - { stream, stats -> stats.writeTo(stream) } - ) - out.writeException(exception) - } - - @Throws(IOException::class) - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - .field("node_id", nodeId) - .field("execution_id", executionId) - .field("monitor_id", monitorId) - .field("last_run_contexts", lastRunContexts) - .field("input_results", inputResults) - .field("trigger_results", triggerResults) - .field("exception", exception) - .endObject() - return builder - } - - companion object { - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): Map { - return map as Map - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteMonitorResponse.kt b/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteMonitorResponse.kt index 8d7a7c25a..96b15a278 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteMonitorResponse.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteMonitorResponse.kt @@ -5,7 +5,7 @@ package org.opensearch.alerting.action -import org.opensearch.alerting.model.MonitorRunResult +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.core.action.ActionResponse import org.opensearch.core.common.io.stream.StreamInput import org.opensearch.core.common.io.stream.StreamOutput diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteWorkflowResponse.kt b/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteWorkflowResponse.kt index 7312a9470..6875960c7 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteWorkflowResponse.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/action/ExecuteWorkflowResponse.kt @@ -5,7 +5,7 @@ package org.opensearch.alerting.action -import org.opensearch.alerting.model.WorkflowRunResult +import org.opensearch.commons.alerting.model.WorkflowRunResult import org.opensearch.core.action.ActionResponse import org.opensearch.core.common.io.stream.StreamInput import org.opensearch.core.common.io.stream.StreamOutput diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/alerts/AlertIndices.kt b/alerting/src/main/kotlin/org/opensearch/alerting/alerts/AlertIndices.kt index c4dd5b3b1..6acd25653 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/alerts/AlertIndices.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/alerts/AlertIndices.kt @@ -41,7 +41,6 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.FINDING_HISTO import org.opensearch.alerting.settings.AlertingSettings.Companion.FINDING_HISTORY_RETENTION_PERIOD import org.opensearch.alerting.settings.AlertingSettings.Companion.FINDING_HISTORY_ROLLOVER_PERIOD import org.opensearch.alerting.settings.AlertingSettings.Companion.REQUEST_TIMEOUT -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.CommentsUtils import org.opensearch.alerting.util.IndexUtils import org.opensearch.client.Client @@ -56,6 +55,7 @@ import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.DataSources +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.XContentParser diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/BucketLevelTriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/BucketLevelTriggerRunResult.kt deleted file mode 100644 index ffc302d98..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/BucketLevelTriggerRunResult.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.model.AggregationResultBucket -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException - -data class BucketLevelTriggerRunResult( - override var triggerName: String, - override var error: Exception? = null, - var aggregationResultBuckets: Map, - var actionResultsMap: MutableMap> = mutableMapOf() -) : TriggerRunResult(triggerName, error) { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - sin.readString(), - sin.readException() as Exception?, // error - sin.readMap(StreamInput::readString, ::AggregationResultBucket), - sin.readMap() as MutableMap> - ) - - override fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder - .field(AGG_RESULT_BUCKETS, aggregationResultBuckets) - .field(ACTIONS_RESULTS, actionResultsMap as Map) - } - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - override fun writeTo(out: StreamOutput) { - super.writeTo(out) - out.writeMap(aggregationResultBuckets, StreamOutput::writeString) { - valueOut: StreamOutput, aggResultBucket: AggregationResultBucket -> - aggResultBucket.writeTo(valueOut) - } - out.writeMap(actionResultsMap as Map) - } - - companion object { - const val AGG_RESULT_BUCKETS = "agg_result_buckets" - const val ACTIONS_RESULTS = "action_results" - - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): TriggerRunResult { - return BucketLevelTriggerRunResult(sin) - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/ChainedAlertTriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/ChainedAlertTriggerRunResult.kt deleted file mode 100644 index b95e533e9..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/ChainedAlertTriggerRunResult.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.alerts.AlertError -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.script.ScriptException -import java.io.IOException -import java.time.Instant - -data class ChainedAlertTriggerRunResult( - override var triggerName: String, - var triggered: Boolean, - override var error: Exception?, - var actionResults: MutableMap = mutableMapOf(), - val associatedAlertIds: Set, -) : TriggerRunResult(triggerName, error) { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - triggerName = sin.readString(), - error = sin.readException(), - triggered = sin.readBoolean(), - actionResults = sin.readMap() as MutableMap, - associatedAlertIds = sin.readStringList().toSet() - ) - - override fun alertError(): AlertError? { - if (error != null) { - return AlertError(Instant.now(), "Failed evaluating trigger:\n${error!!.userErrorMessage()}") - } - for (actionResult in actionResults.values) { - if (actionResult.error != null) { - return AlertError(Instant.now(), "Failed running action:\n${actionResult.error.userErrorMessage()}") - } - } - return null - } - - override fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - if (error is ScriptException) error = Exception((error as ScriptException).toJsonString(), error) - return builder - .field("triggered", triggered) - .field("action_results", actionResults as Map) - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - super.writeTo(out) - out.writeBoolean(triggered) - out.writeMap(actionResults as Map) - out.writeStringCollection(associatedAlertIds) - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): TriggerRunResult { - return ChainedAlertTriggerRunResult(sin) - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/ClusterMetricsTriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/ClusterMetricsTriggerRunResult.kt deleted file mode 100644 index a19de0637..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/ClusterMetricsTriggerRunResult.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.alerts.AlertError -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.ToXContentObject -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.script.ScriptException -import java.io.IOException -import java.time.Instant - -data class ClusterMetricsTriggerRunResult( - override var triggerName: String, - override var triggered: Boolean, - override var error: Exception?, - override var actionResults: MutableMap = mutableMapOf(), - var clusterTriggerResults: List = listOf() -) : QueryLevelTriggerRunResult( - triggerName = triggerName, - error = error, - triggered = triggered, - actionResults = actionResults -) { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - triggerName = sin.readString(), - error = sin.readException(), - triggered = sin.readBoolean(), - actionResults = sin.readMap() as MutableMap, - clusterTriggerResults = sin.readList((ClusterTriggerResult.Companion)::readFrom) - ) - - override fun alertError(): AlertError? { - if (error != null) { - return AlertError(Instant.now(), "Failed evaluating trigger:\n${error!!.userErrorMessage()}") - } - for (actionResult in actionResults.values) { - if (actionResult.error != null) { - return AlertError(Instant.now(), "Failed running action:\n${actionResult.error.userErrorMessage()}") - } - } - return null - } - - override fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - if (error is ScriptException) error = Exception((error as ScriptException).toJsonString(), error) - builder - .field(TRIGGERED_FIELD, triggered) - .field(ACTION_RESULTS_FIELD, actionResults as Map) - .startArray(CLUSTER_RESULTS_FIELD) - clusterTriggerResults.forEach { it.toXContent(builder, params) } - return builder.endArray() - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - super.writeTo(out) - out.writeBoolean(triggered) - out.writeMap(actionResults as Map) - clusterTriggerResults.forEach { it.writeTo(out) } - } - - companion object { - const val TRIGGERED_FIELD = "triggered" - const val ACTION_RESULTS_FIELD = "action_results" - const val CLUSTER_RESULTS_FIELD = "cluster_results" - } - - data class ClusterTriggerResult( - val cluster: String, - val triggered: Boolean, - ) : ToXContentObject, Writeable { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - cluster = sin.readString(), - triggered = sin.readBoolean() - ) - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder.startObject() - .startObject(cluster) - .field(TRIGGERED_FIELD, triggered) - .endObject() - .endObject() - } - - override fun writeTo(out: StreamOutput) { - out.writeString(cluster) - out.writeBoolean(triggered) - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): ClusterTriggerResult { - return ClusterTriggerResult(sin) - } - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/DocumentLevelTriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/DocumentLevelTriggerRunResult.kt deleted file mode 100644 index a89d89da7..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/DocumentLevelTriggerRunResult.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.script.ScriptException -import java.io.IOException - -data class DocumentLevelTriggerRunResult( - override var triggerName: String, - var triggeredDocs: List, - override var error: Exception?, - var actionResultsMap: MutableMap> = mutableMapOf() -) : TriggerRunResult(triggerName, error) { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - triggerName = sin.readString(), - error = sin.readException(), - triggeredDocs = sin.readStringList(), - actionResultsMap = readActionResults(sin) - ) - - override fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - if (error is ScriptException) error = Exception((error as ScriptException).toJsonString(), error) - return builder - .field("triggeredDocs", triggeredDocs as List) - .field("action_results", actionResultsMap as Map) - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - super.writeTo(out) - out.writeStringCollection(triggeredDocs) - out.writeInt(actionResultsMap.size) - actionResultsMap.forEach { (alert, actionResults) -> - out.writeString(alert) - out.writeInt(actionResults.size) - actionResults.forEach { (id, result) -> - out.writeString(id) - result.writeTo(out) - } - } - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): TriggerRunResult { - return DocumentLevelTriggerRunResult(sin) - } - - @JvmStatic - fun readActionResults(sin: StreamInput): MutableMap> { - val actionResultsMapReconstruct: MutableMap> = mutableMapOf() - val size = sin.readInt() - var idx = 0 - while (idx < size) { - val alert = sin.readString() - val actionResultsSize = sin.readInt() - val actionRunResultElem = mutableMapOf() - var i = 0 - while (i < actionResultsSize) { - val actionId = sin.readString() - val actionResult = ActionRunResult.readFrom(sin) - actionRunResultElem[actionId] = actionResult - ++i - } - actionResultsMapReconstruct[alert] = actionRunResultElem - ++idx - } - return actionResultsMapReconstruct - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/IndexExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/IndexExecutionContext.kt deleted file mode 100644 index b36460d96..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/IndexExecutionContext.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.model.DocLevelQuery -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException - -data class IndexExecutionContext( - val queries: List, - val lastRunContext: MutableMap, // previous execution - val updatedLastRunContext: MutableMap, // without sequence numbers - val indexName: String, - val concreteIndexName: String, - val updatedIndexNames: List, - val concreteIndexNames: List, - val conflictingFields: List, - val docIds: List? = emptyList(), -) : Writeable, ToXContent { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - queries = sin.readList { DocLevelQuery(sin) }, - lastRunContext = sin.readMap(), - updatedLastRunContext = sin.readMap(), - indexName = sin.readString(), - concreteIndexName = sin.readString(), - updatedIndexNames = sin.readStringList(), - concreteIndexNames = sin.readStringList(), - conflictingFields = sin.readStringList(), - docIds = sin.readOptionalStringList() - ) - - override fun writeTo(out: StreamOutput?) { - out!!.writeCollection(queries) - out.writeMap(lastRunContext) - out.writeMap(updatedLastRunContext) - out.writeString(indexName) - out.writeString(concreteIndexName) - out.writeStringCollection(updatedIndexNames) - out.writeStringCollection(concreteIndexNames) - out.writeStringCollection(conflictingFields) - out.writeOptionalStringCollection(docIds) - } - - override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder { - builder!!.startObject() - .field("queries", queries) - .field("last_run_context", lastRunContext) - .field("updated_last_run_context", updatedLastRunContext) - .field("index_name", indexName) - .field("concrete_index_name", concreteIndexName) - .field("udpated_index_names", updatedIndexNames) - .field("concrete_index_names", concreteIndexNames) - .field("conflicting_fields", conflictingFields) - .field("doc_ids", docIds) - .endObject() - return builder - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorMetadata.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorMetadata.kt deleted file mode 100644 index d1c5c240e..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorMetadata.kt +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.alerting.model.destination.Destination.Companion.NO_ID -import org.opensearch.commons.alerting.model.Monitor -import org.opensearch.commons.alerting.util.instant -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.core.xcontent.XContentParser -import org.opensearch.core.xcontent.XContentParserUtils -import org.opensearch.index.seqno.SequenceNumbers -import java.io.IOException -import java.time.Instant - -data class MonitorMetadata( - val id: String, - val seqNo: Long = SequenceNumbers.UNASSIGNED_SEQ_NO, - val primaryTerm: Long = SequenceNumbers.UNASSIGNED_PRIMARY_TERM, - val monitorId: String, - val lastActionExecutionTimes: List, - val lastRunContext: Map, - // Maps (sourceIndex + monitorId) --> concreteQueryIndex - val sourceToQueryIndexMapping: MutableMap = mutableMapOf() -) : Writeable, ToXContent { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - id = sin.readString(), - seqNo = sin.readLong(), - primaryTerm = sin.readLong(), - monitorId = sin.readString(), - lastActionExecutionTimes = sin.readList(ActionExecutionTime::readFrom), - lastRunContext = Monitor.suppressWarning(sin.readMap()), - sourceToQueryIndexMapping = sin.readMap() as MutableMap - ) - - override fun writeTo(out: StreamOutput) { - out.writeString(id) - out.writeLong(seqNo) - out.writeLong(primaryTerm) - out.writeString(monitorId) - out.writeCollection(lastActionExecutionTimes) - out.writeMap(lastRunContext) - out.writeMap(sourceToQueryIndexMapping as MutableMap) - } - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - if (params.paramAsBoolean("with_type", false)) builder.startObject(METADATA) - builder.field(MONITOR_ID_FIELD, monitorId) - .field(LAST_ACTION_EXECUTION_FIELD, lastActionExecutionTimes.toTypedArray()) - if (lastRunContext.isNotEmpty()) builder.field(LAST_RUN_CONTEXT_FIELD, lastRunContext) - if (sourceToQueryIndexMapping.isNotEmpty()) { - builder.field(SOURCE_TO_QUERY_INDEX_MAP_FIELD, sourceToQueryIndexMapping as MutableMap) - } - if (params.paramAsBoolean("with_type", false)) builder.endObject() - return builder.endObject() - } - - companion object { - const val METADATA = "metadata" - const val MONITOR_ID_FIELD = "monitor_id" - const val LAST_ACTION_EXECUTION_FIELD = "last_action_execution_times" - const val LAST_RUN_CONTEXT_FIELD = "last_run_context" - const val SOURCE_TO_QUERY_INDEX_MAP_FIELD = "source_to_query_index_mapping" - - @JvmStatic - @JvmOverloads - @Throws(IOException::class) - fun parse( - xcp: XContentParser, - id: String = NO_ID, - seqNo: Long = SequenceNumbers.UNASSIGNED_SEQ_NO, - primaryTerm: Long = SequenceNumbers.UNASSIGNED_PRIMARY_TERM - ): MonitorMetadata { - lateinit var monitorId: String - val lastActionExecutionTimes = mutableListOf() - var lastRunContext: Map = mapOf() - var sourceToQueryIndexMapping: MutableMap = mutableMapOf() - - XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp) - while (xcp.nextToken() != XContentParser.Token.END_OBJECT) { - val fieldName = xcp.currentName() - xcp.nextToken() - - when (fieldName) { - MONITOR_ID_FIELD -> monitorId = xcp.text() - LAST_ACTION_EXECUTION_FIELD -> { - XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp) - while (xcp.nextToken() != XContentParser.Token.END_ARRAY) { - lastActionExecutionTimes.add(ActionExecutionTime.parse(xcp)) - } - } - LAST_RUN_CONTEXT_FIELD -> lastRunContext = xcp.map() - SOURCE_TO_QUERY_INDEX_MAP_FIELD -> sourceToQueryIndexMapping = xcp.map() as MutableMap - } - } - - return MonitorMetadata( - if (id != NO_ID) id else "$monitorId-metadata", - seqNo = seqNo, - primaryTerm = primaryTerm, - monitorId = monitorId, - lastActionExecutionTimes = lastActionExecutionTimes, - lastRunContext = lastRunContext, - sourceToQueryIndexMapping = sourceToQueryIndexMapping - ) - } - - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): MonitorMetadata { - return MonitorMetadata(sin) - } - - /** workflowMetadataId is used as key for monitor metadata in the case when the workflow execution happens - so the monitor lastRunContext (in the case of doc level monitor) is not interfering with the monitor execution - WorkflowMetadataId will be either workflowId-metadata (when executing the workflow as it is scheduled) - or timestampWithUUID-metadata (when a workflow is executed in a dry-run mode) - In the case of temp workflow, doc level monitors must have lastRunContext created from scratch - That's why we are using workflowMetadataId - in order to ensure that the doc level monitor metadata is created from scratch - **/ - fun getId(monitor: Monitor, workflowMetadataId: String? = null): String { - return if (workflowMetadataId.isNullOrEmpty()) "${monitor.id}-metadata" - // WorkflowMetadataId already contains -metadata suffix - else "$workflowMetadataId-${monitor.id}-metadata" - } - } -} - -/** - * A value object containing action execution time. - */ -data class ActionExecutionTime( - val actionId: String, - val executionTime: Instant -) : Writeable, ToXContent { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - sin.readString(), // actionId - sin.readInstant() // executionTime - ) - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder.startObject() - .field(ACTION_ID_FIELD, actionId) - .field(EXECUTION_TIME_FIELD, executionTime) - .endObject() - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeString(actionId) - out.writeInstant(executionTime) - } - - companion object { - const val ACTION_ID_FIELD = "action_id" - const val EXECUTION_TIME_FIELD = "execution_time" - - @JvmStatic - @Throws(IOException::class) - fun parse(xcp: XContentParser): ActionExecutionTime { - lateinit var actionId: String - lateinit var executionTime: Instant - - XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp) - while (xcp.nextToken() != XContentParser.Token.END_OBJECT) { - val fieldName = xcp.currentName() - xcp.nextToken() - - when (fieldName) { - ACTION_ID_FIELD -> actionId = xcp.text() - EXECUTION_TIME_FIELD -> executionTime = xcp.instant()!! - } - } - - return ActionExecutionTime( - actionId, - executionTime - ) - } - - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): ActionExecutionTime { - return ActionExecutionTime(sin) - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorRunResult.kt deleted file mode 100644 index 07d839291..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/MonitorRunResult.kt +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.apache.logging.log4j.LogManager -import org.opensearch.OpenSearchException -import org.opensearch.commons.alerting.alerts.AlertError -import org.opensearch.commons.alerting.model.Trigger -import org.opensearch.commons.alerting.util.optionalTimeField -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.script.ScriptException -import java.io.IOException -import java.time.Instant - -data class MonitorRunResult( - val monitorName: String, - val periodStart: Instant, - val periodEnd: Instant, - val error: Exception? = null, - val inputResults: InputRunResults = InputRunResults(), - val triggerResults: Map = mapOf() -) : Writeable, ToXContent { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - sin.readString(), // monitorName - sin.readInstant(), // periodStart - sin.readInstant(), // periodEnd - sin.readException(), // error - InputRunResults.readFrom(sin), // inputResults - suppressWarning(sin.readMap()) as Map // triggerResults - ) - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder.startObject() - .field("monitor_name", monitorName) - .optionalTimeField("period_start", periodStart) - .optionalTimeField("period_end", periodEnd) - .field("error", error?.message) - .field("input_results", inputResults) - .field("trigger_results", triggerResults) - .endObject() - } - - /** Returns error information to store in the Alert. Currently it's just the stack trace but it can be more */ - fun alertError(): AlertError? { - if (error != null) { - return AlertError(Instant.now(), "Failed running monitor:\n${error.userErrorMessage()}") - } - - if (inputResults.error != null) { - return AlertError(Instant.now(), "Failed fetching inputs:\n${inputResults.error.userErrorMessage()}") - } - return null - } - - fun scriptContextError(trigger: Trigger): Exception? { - return error ?: inputResults.error ?: triggerResults[trigger.id]?.error - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): MonitorRunResult { - return MonitorRunResult(sin) - } - - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): Map { - return map as Map - } - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeString(monitorName) - out.writeInstant(periodStart) - out.writeInstant(periodEnd) - out.writeException(error) - inputResults.writeTo(out) - out.writeMap(triggerResults) - } -} - -data class InputRunResults( - val results: List> = listOf(), - val error: Exception? = null, - val aggTriggersAfterKey: MutableMap? = null -) : Writeable, ToXContent { - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder.startObject() - .field("results", results) - .field("error", error?.message) - .endObject() - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeVInt(results.size) - for (map in results) { - out.writeMap(map) - } - out.writeException(error) - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): InputRunResults { - val count = sin.readVInt() // count - val list = mutableListOf>() - for (i in 0 until count) { - list.add(suppressWarning(sin.readMap())) // result(map) - } - val error = sin.readException() // error - return InputRunResults(list, error) - } - - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): Map { - return map as Map - } - } - - fun afterKeysPresent(): Boolean { - aggTriggersAfterKey?.forEach { - if (it.value.afterKey != null && !it.value.lastPage) { - return true - } - } - return false - } -} - -data class TriggerAfterKey(val afterKey: Map?, val lastPage: Boolean) - -data class ActionRunResult( - val actionId: String, - val actionName: String, - val output: Map, - val throttled: Boolean = false, - val executionTime: Instant? = null, - val error: Exception? = null -) : Writeable, ToXContent { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - sin.readString(), // actionId - sin.readString(), // actionName - suppressWarning(sin.readMap()), // output - sin.readBoolean(), // throttled - sin.readOptionalInstant(), // executionTime - sin.readException() // error - ) - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - return builder.startObject() - .field("id", actionId) - .field("name", actionName) - .field("output", output) - .field("throttled", throttled) - .optionalTimeField("executionTime", executionTime) - .field("error", error?.message) - .endObject() - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeString(actionId) - out.writeString(actionName) - out.writeMap(output) - out.writeBoolean(throttled) - out.writeOptionalInstant(executionTime) - out.writeException(error) - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): ActionRunResult { - return ActionRunResult(sin) - } - - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): MutableMap { - return map as MutableMap - } - } -} - -private val logger = LogManager.getLogger(MonitorRunResult::class.java) - -/** Constructs an error message from an exception suitable for human consumption. */ -fun Throwable.userErrorMessage(): String { - return when { - this is ScriptException -> this.scriptStack.joinToString(separator = "\n", limit = 100) - this is OpenSearchException -> this.detailedMessage - this.message != null -> { - logger.info("Internal error: ${this.message}. See the opensearch.log for details", this) - this.message!! - } - else -> { - logger.info("Unknown Internal error. See the OpenSearch log for details.", this) - "Unknown Internal error. See the OpenSearch log for details." - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/QueryLevelTriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/QueryLevelTriggerRunResult.kt deleted file mode 100644 index 5917c1ecf..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/QueryLevelTriggerRunResult.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.alerts.AlertError -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.script.ScriptException -import java.io.IOException -import java.time.Instant - -open class QueryLevelTriggerRunResult( - override var triggerName: String, - open var triggered: Boolean, - override var error: Exception?, - open var actionResults: MutableMap = mutableMapOf() -) : TriggerRunResult(triggerName, error) { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - triggerName = sin.readString(), - error = sin.readException(), - triggered = sin.readBoolean(), - actionResults = sin.readMap() as MutableMap - ) - - override fun alertError(): AlertError? { - if (error != null) { - return AlertError(Instant.now(), "Failed evaluating trigger:\n${error!!.userErrorMessage()}") - } - for (actionResult in actionResults.values) { - if (actionResult.error != null) { - return AlertError(Instant.now(), "Failed running action:\n${actionResult.error.userErrorMessage()}") - } - } - return null - } - - override fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - if (error is ScriptException) error = Exception((error as ScriptException).toJsonString(), error) - return builder - .field("triggered", triggered) - .field("action_results", actionResults as Map) - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - super.writeTo(out) - out.writeBoolean(triggered) - out.writeMap(actionResults as Map) - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): TriggerRunResult { - return QueryLevelTriggerRunResult(sin) - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/TriggerRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/TriggerRunResult.kt deleted file mode 100644 index c3aec89f2..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/TriggerRunResult.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.alerts.AlertError -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException -import java.time.Instant - -abstract class TriggerRunResult( - open var triggerName: String, - open var error: Exception? = null -) : Writeable, ToXContent { - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - .field("name", triggerName) - - internalXContent(builder, params) - val msg = error?.message - - builder.field("error", msg) - .endObject() - return builder - } - - abstract fun internalXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder - - /** Returns error information to store in the Alert. Currently it's just the stack trace but it can be more */ - open fun alertError(): AlertError? { - if (error != null) { - return AlertError(Instant.now(), "Failed evaluating trigger:\n${error!!.userErrorMessage()}") - } - return null - } - - @Throws(IOException::class) - override fun writeTo(out: StreamOutput) { - out.writeString(triggerName) - out.writeException(error) - } - - companion object { - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): MutableMap { - return map as MutableMap - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowMetadata.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowMetadata.kt deleted file mode 100644 index 9ab7b43f8..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowMetadata.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.commons.alerting.util.instant -import org.opensearch.commons.alerting.util.optionalTimeField -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import org.opensearch.core.xcontent.XContentParser -import org.opensearch.core.xcontent.XContentParserUtils -import java.io.IOException -import java.time.Instant - -data class WorkflowMetadata( - val id: String, - val workflowId: String, - val monitorIds: List, - val latestRunTime: Instant, - val latestExecutionId: String -) : Writeable, ToXContent { - - @Throws(IOException::class) - constructor(sin: StreamInput) : this( - id = sin.readString(), - workflowId = sin.readString(), - monitorIds = sin.readStringList(), - latestRunTime = sin.readInstant(), - latestExecutionId = sin.readString() - ) - - override fun writeTo(out: StreamOutput) { - out.writeString(id) - out.writeString(workflowId) - out.writeStringCollection(monitorIds) - out.writeInstant(latestRunTime) - out.writeString(latestExecutionId) - } - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - if (params.paramAsBoolean("with_type", false)) builder.startObject(METADATA) - builder.field(WORKFLOW_ID_FIELD, workflowId) - .field(MONITOR_IDS_FIELD, monitorIds) - .optionalTimeField(LATEST_RUN_TIME, latestRunTime) - .field(LATEST_EXECUTION_ID, latestExecutionId) - if (params.paramAsBoolean("with_type", false)) builder.endObject() - return builder.endObject() - } - - companion object { - const val METADATA = "workflow_metadata" - const val WORKFLOW_ID_FIELD = "workflow_id" - const val MONITOR_IDS_FIELD = "monitor_ids" - const val LATEST_RUN_TIME = "latest_run_time" - const val LATEST_EXECUTION_ID = "latest_execution_id" - - @JvmStatic @JvmOverloads - @Throws(IOException::class) - fun parse(xcp: XContentParser): WorkflowMetadata { - lateinit var workflowId: String - var monitorIds = mutableListOf() - lateinit var latestRunTime: Instant - lateinit var latestExecutionId: String - - XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp) - while (xcp.nextToken() != XContentParser.Token.END_OBJECT) { - val fieldName = xcp.currentName() - xcp.nextToken() - - when (fieldName) { - WORKFLOW_ID_FIELD -> workflowId = xcp.text() - MONITOR_IDS_FIELD -> { - XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp) - while (xcp.nextToken() != XContentParser.Token.END_ARRAY) { - monitorIds.add(xcp.text()) - } - } - LATEST_RUN_TIME -> latestRunTime = xcp.instant()!! - LATEST_EXECUTION_ID -> latestExecutionId = xcp.text() - } - } - return WorkflowMetadata( - id = "$workflowId-metadata", - workflowId = workflowId, - monitorIds = monitorIds, - latestRunTime = latestRunTime, - latestExecutionId = latestExecutionId - ) - } - - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): WorkflowMetadata { - return WorkflowMetadata(sin) - } - - fun getId(workflowId: String? = null) = "$workflowId-metadata" - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowRunResult.kt b/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowRunResult.kt deleted file mode 100644 index cabdc6330..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/model/WorkflowRunResult.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.model - -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.XContentBuilder -import java.io.IOException -import java.lang.Exception -import java.time.Instant - -data class WorkflowRunResult( - val workflowId: String, - val workflowName: String, - val monitorRunResults: List> = mutableListOf(), - val executionStartTime: Instant, - var executionEndTime: Instant? = null, - val executionId: String, - val error: Exception? = null, - val triggerResults: Map = mapOf(), -) : Writeable, ToXContent { - - @Throws(IOException::class) - @Suppress("UNCHECKED_CAST") - constructor(sin: StreamInput) : this( - workflowId = sin.readString(), - workflowName = sin.readString(), - monitorRunResults = sin.readList> { s: StreamInput -> MonitorRunResult.readFrom(s) }, - executionStartTime = sin.readInstant(), - executionEndTime = sin.readOptionalInstant(), - executionId = sin.readString(), - error = sin.readException(), - triggerResults = suppressWarning(sin.readMap()) as Map - ) - - override fun writeTo(out: StreamOutput) { - out.writeString(workflowId) - out.writeString(workflowName) - out.writeList(monitorRunResults) - out.writeInstant(executionStartTime) - out.writeOptionalInstant(executionEndTime) - out.writeString(executionId) - out.writeException(error) - out.writeMap(triggerResults) - } - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { - builder.startObject() - builder.field("execution_id", executionId) - builder.field("workflow_name", workflowName) - builder.field("workflow_id", workflowId) - builder.field("trigger_results", triggerResults) - builder.startArray("monitor_run_results") - for (monitorResult in monitorRunResults) { - monitorResult.toXContent(builder, ToXContent.EMPTY_PARAMS) - } - builder.endArray() - .field("execution_start_time", executionStartTime) - .field("execution_end_time", executionEndTime) - .field("error", error?.message) - .endObject() - return builder - } - - companion object { - @JvmStatic - @Throws(IOException::class) - fun readFrom(sin: StreamInput): WorkflowRunResult { - return WorkflowRunResult(sin) - } - - @Suppress("UNCHECKED_CAST") - fun suppressWarning(map: MutableMap?): Map { - return map as Map - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteDocumentLevelMonitorRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteDocumentLevelMonitorRunner.kt new file mode 100644 index 000000000..c74b4e23c --- /dev/null +++ b/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteDocumentLevelMonitorRunner.kt @@ -0,0 +1,387 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.remote.monitors + +import org.apache.logging.log4j.LogManager +import org.opensearch.ExceptionsHelper +import org.opensearch.Version +import org.opensearch.alerting.MonitorMetadataService +import org.opensearch.alerting.MonitorRunner +import org.opensearch.alerting.MonitorRunnerExecutionContext +import org.opensearch.alerting.util.IndexUtils +import org.opensearch.cluster.metadata.IndexMetadata +import org.opensearch.cluster.node.DiscoveryNode +import org.opensearch.cluster.routing.ShardRouting +import org.opensearch.cluster.service.ClusterService +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse +import org.opensearch.commons.alerting.model.ActionRunResult +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult +import org.opensearch.commons.alerting.model.InputRunResults +import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult +import org.opensearch.commons.alerting.model.WorkflowRunContext +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.core.index.shard.ShardId +import org.opensearch.core.rest.RestStatus +import org.opensearch.index.seqno.SequenceNumbers +import org.opensearch.transport.TransportService +import java.io.IOException +import java.time.Instant + +class RemoteDocumentLevelMonitorRunner : MonitorRunner() { + private val logger = LogManager.getLogger(javaClass) + + override suspend fun runMonitor( + monitor: Monitor, + monitorCtx: MonitorRunnerExecutionContext, + periodStart: Instant, + periodEnd: Instant, + dryRun: Boolean, + workflowRunContext: WorkflowRunContext?, + executionId: String, + transportService: TransportService + ): MonitorRunResult<*> { + logger.debug("Remote Document-level-monitor is running ...") + val isTempMonitor = dryRun || monitor.id == Monitor.NO_ID + var monitorResult = MonitorRunResult(monitor.name, periodStart, periodEnd) + + try { + validate(monitor) + } catch (e: Exception) { + logger.error("Failed to start Document-level-monitor. Error: $e") + monitorResult = monitorResult.copy(error = AlertingException.wrap(e)) + } + + var (monitorMetadata, _) = MonitorMetadataService.getOrCreateMetadata( + monitor = monitor, + createWithRunContext = false, + skipIndex = isTempMonitor, + workflowRunContext?.workflowMetadataId + ) + logger.info(monitorMetadata.lastRunContext.toMutableMap().toString()) + val lastRunContext = if (monitorMetadata.lastRunContext.isNullOrEmpty()) mutableMapOf() + else monitorMetadata.lastRunContext.toMutableMap() as MutableMap> + val updatedLastRunContext = lastRunContext.toMutableMap() + + val remoteDocLevelMonitorInput = monitor.inputs[0] as RemoteDocLevelMonitorInput + val docLevelMonitorInput = remoteDocLevelMonitorInput.docLevelMonitorInput + var shards: Set = mutableSetOf() + var concreteIndices = listOf() + + // Resolve all passed indices to concrete indices + val allConcreteIndices = IndexUtils.resolveAllIndices( + docLevelMonitorInput.indices, + monitorCtx.clusterService!!, + monitorCtx.indexNameExpressionResolver!! + ) + // cleanup old indices that are not monitored anymore from the same monitor + val runContextKeys = updatedLastRunContext.keys.toMutableSet() + for (ind in runContextKeys) { + if (!allConcreteIndices.contains(ind)) { + updatedLastRunContext.remove(ind) + lastRunContext.remove(ind) + } + } + + try { + docLevelMonitorInput.indices.forEach { indexName -> + concreteIndices = IndexUtils.resolveAllIndices( + listOf(indexName), + monitorCtx.clusterService!!, + monitorCtx.indexNameExpressionResolver!! + ) + var lastWriteIndex: String? = null + if (IndexUtils.isAlias(indexName, monitorCtx.clusterService!!.state()) || + IndexUtils.isDataStream(indexName, monitorCtx.clusterService!!.state()) + ) { + lastWriteIndex = concreteIndices.find { lastRunContext.containsKey(it) } + if (lastWriteIndex != null) { + val lastWriteIndexCreationDate = + IndexUtils.getCreationDateForIndex(lastWriteIndex, monitorCtx.clusterService!!.state()) + concreteIndices = IndexUtils.getNewestIndicesByCreationDate( + concreteIndices, + monitorCtx.clusterService!!.state(), + lastWriteIndexCreationDate + ) + } + } + + concreteIndices.forEach { concreteIndexName -> + // Prepare lastRunContext for each index + val indexLastRunContext = lastRunContext.getOrPut(concreteIndexName) { + val isIndexCreatedRecently = createdRecently( + monitor, + periodStart, + periodEnd, + monitorCtx.clusterService!!.state().metadata.index(concreteIndexName) + ) + MonitorMetadataService.createRunContextForIndex(concreteIndexName, isIndexCreatedRecently) + } + + val indexUpdatedRunContext = initializeNewLastRunContext( + indexLastRunContext.toMutableMap(), + monitorCtx, + concreteIndexName + ) as MutableMap + if (IndexUtils.isAlias(indexName, monitorCtx.clusterService!!.state()) || + IndexUtils.isDataStream(indexName, monitorCtx.clusterService!!.state()) + ) { + if (concreteIndexName == IndexUtils.getWriteIndex( + indexName, + monitorCtx.clusterService!!.state() + ) + ) { + updatedLastRunContext.remove(lastWriteIndex) + updatedLastRunContext[concreteIndexName] = indexUpdatedRunContext + } + } else { + updatedLastRunContext[concreteIndexName] = indexUpdatedRunContext + } + } + + concreteIndices.forEach { + val shardCount = getShardsCount(monitorCtx.clusterService!!, it) + for (i in 0 until shardCount) { + shards = shards.plus("$it:$i") + } + } + } + + val nodeMap = getNodes(monitorCtx) + val nodeShardAssignments = distributeShards( + monitorCtx, + nodeMap.keys.toList(), + shards.toList() + ) + + val docLevelMonitorFanOutResponses = monitorCtx.remoteMonitors[monitor.monitorType]!!.monitorRunner.doFanOut( + monitorCtx.clusterService!!, + monitor, + monitorMetadata.copy(lastRunContext = lastRunContext), + executionId, + concreteIndices, + workflowRunContext, + dryRun, + transportService, + nodeMap, + nodeShardAssignments + ) + updateLastRunContextFromFanOutResponses(docLevelMonitorFanOutResponses, updatedLastRunContext) + val triggerResults = buildTriggerResults(docLevelMonitorFanOutResponses) + val inputRunResults = buildInputRunResults(docLevelMonitorFanOutResponses) + if (!isTempMonitor) { + MonitorMetadataService.upsertMetadata( + monitorMetadata.copy(lastRunContext = updatedLastRunContext), + true + ) + } + return monitorResult.copy(triggerResults = triggerResults, inputResults = inputRunResults) + } catch (e: Exception) { + logger.error("Failed running Document-level-monitor ${monitor.name}", e) + val errorMessage = ExceptionsHelper.detailedMessage(e) + monitorCtx.alertService!!.upsertMonitorErrorAlert(monitor, errorMessage, executionId, workflowRunContext) + val alertingException = AlertingException( + errorMessage, + RestStatus.INTERNAL_SERVER_ERROR, + e + ) + return monitorResult.copy(error = alertingException, inputResults = InputRunResults(emptyList(), alertingException)) + } + } + + private fun validate(monitor: Monitor) { + if (monitor.inputs.size > 1) { + throw IOException("Only one input is supported with remote document-level-monitor.") + } + + if (monitor.inputs[0].name() != RemoteDocLevelMonitorInput.REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD) { + throw IOException("Invalid input with remote document-level-monitor.") + } + + if ((monitor.inputs[0] as RemoteDocLevelMonitorInput).docLevelMonitorInput.indices.isEmpty()) { + throw IllegalArgumentException("DocLevelMonitorInput has no indices") + } + } + + private fun getNodes(monitorCtx: MonitorRunnerExecutionContext): Map { + return monitorCtx.clusterService!!.state().nodes.dataNodes.filter { it.value.version >= Version.CURRENT } + } + + private fun distributeShards( + monitorCtx: MonitorRunnerExecutionContext, + allNodes: List, + shards: List, + ): Map> { + val totalShards = shards.size + val numFanOutNodes = allNodes.size.coerceAtMost((totalShards + 1) / 2) + val totalNodes = monitorCtx.totalNodesFanOut.coerceAtMost(numFanOutNodes) + val shardsPerNode = totalShards / totalNodes + var shardsRemaining = totalShards % totalNodes + + val shardIdList = shards.map { + val index = it.split(":")[0] + val shardId = it.split(":")[1] + ShardId(monitorCtx.clusterService!!.state().metadata.index(index).index, shardId.toInt()) + } + val nodes = allNodes.subList(0, totalNodes) + + val nodeShardAssignments = mutableMapOf>() + var idx = 0 + for (node in nodes) { + val nodeShardAssignment = mutableSetOf() + for (i in 1..shardsPerNode) { + nodeShardAssignment.add(shardIdList[idx++]) + } + nodeShardAssignments[node] = nodeShardAssignment + } + + for (node in nodes) { + if (shardsRemaining == 0) { + break + } + nodeShardAssignments[node]!!.add(shardIdList[idx++]) + --shardsRemaining + } + return nodeShardAssignments + } + + private fun getShardsCount(clusterService: ClusterService, index: String): Int { + val allShards: List = clusterService!!.state().routingTable().allShards(index) + return allShards.filter { it.primary() }.size + } + + private fun updateLastRunContextFromFanOutResponses( + docLevelMonitorFanOutResponses: MutableList, + updatedLastRunContext: MutableMap>, + ) { + + // Prepare updatedLastRunContext for each index + for (indexName in updatedLastRunContext.keys) { + for (fanOutResponse in docLevelMonitorFanOutResponses) { + if (fanOutResponse.exception == null) { + // fanOutResponse.lastRunContexts //updatedContexts for relevant shards + val indexLastRunContext = updatedLastRunContext[indexName] as MutableMap + + if (fanOutResponse.lastRunContexts.contains(indexName)) { + (fanOutResponse.lastRunContexts[indexName] as Map).forEach { + + val seq_no = it.value.toString().toIntOrNull() + if ( + it.key != "shards_count" && + it.key != "index" && + seq_no != null && + seq_no >= 0 + ) { + indexLastRunContext[it.key] = seq_no + } + } + } + } + } + } + } + + private fun buildTriggerResults( + docLevelMonitorFanOutResponses: MutableList, + ): MutableMap { + val triggerResults = mutableMapOf() + val triggerErrorMap = mutableMapOf>() + for (res in docLevelMonitorFanOutResponses) { + if (res.exception == null) { + for (triggerId in res.triggerResults.keys) { + val documentLevelTriggerRunResult = res.triggerResults[triggerId] + if (documentLevelTriggerRunResult != null) { + if (false == triggerResults.contains(triggerId)) { + triggerResults[triggerId] = documentLevelTriggerRunResult + triggerErrorMap[triggerId] = if (documentLevelTriggerRunResult.error != null) { + val error = if (documentLevelTriggerRunResult.error is AlertingException) { + documentLevelTriggerRunResult.error as AlertingException + } else { + AlertingException.wrap(documentLevelTriggerRunResult.error!!) as AlertingException + } + mutableListOf(error) + } else { + mutableListOf() + } + } else { + val currVal = triggerResults[triggerId] + val newTriggeredDocs = mutableListOf() + newTriggeredDocs.addAll(currVal!!.triggeredDocs) + newTriggeredDocs.addAll(documentLevelTriggerRunResult.triggeredDocs) + val newActionResults = mutableMapOf>() + newActionResults.putAll(currVal.actionResultsMap) + newActionResults.putAll(documentLevelTriggerRunResult.actionResultsMap) + triggerResults[triggerId] = currVal.copy( + triggeredDocs = newTriggeredDocs, + actionResultsMap = newActionResults + ) + + if (documentLevelTriggerRunResult.error != null) { + triggerErrorMap[triggerId]!!.add(documentLevelTriggerRunResult.error as AlertingException) + } + } + } + } + } + } + + triggerErrorMap.forEach { triggerId, errorList -> + if (errorList.isNotEmpty()) { + triggerResults[triggerId]!!.error = AlertingException.merge(*errorList.toTypedArray()) + } + } + return triggerResults + } + + private fun buildInputRunResults(docLevelMonitorFanOutResponses: MutableList): InputRunResults { + val inputRunResults = mutableMapOf>() + val errors: MutableList = mutableListOf() + for (response in docLevelMonitorFanOutResponses) { + if (response.exception == null) { + if (response.inputResults.error != null) { + if (response.inputResults.error is AlertingException) { + errors.add(response.inputResults.error as AlertingException) + } else { + errors.add(AlertingException.wrap(response.inputResults.error as Exception) as AlertingException) + } + } + val partialResult = response.inputResults.results + for (result in partialResult) { + for (id in result.keys) { + inputRunResults.getOrPut(id) { mutableSetOf() }.addAll(result[id] as Collection) + } + } + } + } + return InputRunResults(listOf(inputRunResults), if (!errors.isEmpty()) AlertingException.merge(*errors.toTypedArray()) else null) + } + + private fun createdRecently( + monitor: Monitor, + periodStart: Instant, + periodEnd: Instant, + indexMetadata: IndexMetadata + ): Boolean { + val lastExecutionTime = if (periodStart == periodEnd) monitor.lastUpdateTime else periodStart + val indexCreationDate = indexMetadata.settings.get("index.creation_date")?.toLong() ?: 0L + return indexCreationDate > lastExecutionTime.toEpochMilli() + } + + private fun initializeNewLastRunContext( + lastRunContext: Map, + monitorCtx: MonitorRunnerExecutionContext, + index: String, + ): Map { + val count: Int = getShardsCount(monitorCtx.clusterService!!, index) + val updatedLastRunContext = lastRunContext.toMutableMap() + for (i: Int in 0 until count) { + val shard = i.toString() + updatedLastRunContext[shard] = SequenceNumbers.UNASSIGNED_SEQ_NO + } + return updatedLastRunContext + } +} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteMonitorRegistry.kt b/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteMonitorRegistry.kt new file mode 100644 index 000000000..527ee2ef2 --- /dev/null +++ b/alerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteMonitorRegistry.kt @@ -0,0 +1,13 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.remote.monitors + +import org.opensearch.alerting.spi.RemoteMonitorRunner + +/** + * Class to store monitorType to monitorRunner tuples. + */ +class RemoteMonitorRegistry(val monitorType: String, val monitorRunner: RemoteMonitorRunner) diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestExecuteMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestExecuteMonitorAction.kt index f9d88bc5b..86e93bd65 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestExecuteMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestExecuteMonitorAction.kt @@ -9,10 +9,10 @@ import org.apache.logging.log4j.LogManager import org.opensearch.alerting.AlertingPlugin import org.opensearch.alerting.action.ExecuteMonitorAction import org.opensearch.alerting.action.ExecuteMonitorRequest -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.node.NodeClient import org.opensearch.common.unit.TimeValue import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.xcontent.XContentParser.Token.START_OBJECT import org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken import org.opensearch.rest.BaseRestHandler diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexAlertingCommentAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexAlertingCommentAction.kt index 3edd64579..9dc2cb756 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexAlertingCommentAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexAlertingCommentAction.kt @@ -7,7 +7,6 @@ package org.opensearch.alerting.resthandler import org.apache.logging.log4j.LogManager import org.opensearch.alerting.AlertingPlugin -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IF_PRIMARY_TERM import org.opensearch.alerting.util.IF_SEQ_NO import org.opensearch.client.node.NodeClient @@ -16,6 +15,7 @@ import org.opensearch.commons.alerting.action.IndexCommentRequest import org.opensearch.commons.alerting.action.IndexCommentResponse import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Comment +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.ToXContent import org.opensearch.index.seqno.SequenceNumbers diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexMonitorAction.kt index 7dfde7cc5..e4b923e6e 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexMonitorAction.kt @@ -8,7 +8,6 @@ import org.apache.logging.log4j.LogManager import org.opensearch.action.support.WriteRequest import org.opensearch.alerting.AlertingPlugin import org.opensearch.alerting.alerts.AlertIndices -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IF_PRIMARY_TERM import org.opensearch.alerting.util.IF_SEQ_NO import org.opensearch.alerting.util.REFRESH @@ -22,6 +21,8 @@ import org.opensearch.commons.alerting.model.DocumentLevelTrigger import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.QueryLevelTrigger import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.commons.utils.getInvalidNameChars import org.opensearch.commons.utils.isValidName import org.opensearch.core.rest.RestStatus @@ -42,6 +43,7 @@ import org.opensearch.rest.RestResponse import org.opensearch.rest.action.RestResponseListener import java.io.IOException import java.time.Instant +import java.util.Locale private val log = LogManager.getLogger(RestIndexMonitorAction::class.java) @@ -101,33 +103,38 @@ class RestIndexMonitorAction : BaseRestHandler() { val monitorType = monitor.monitorType val triggers = monitor.triggers - when (monitorType) { - Monitor.MonitorType.QUERY_LEVEL_MONITOR -> { - triggers.forEach { - if (it !is QueryLevelTrigger) { - throw (IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for query level monitor")) + if (monitor.isMonitorOfStandardType()) { + when (Monitor.MonitorType.valueOf(monitor.monitorType.uppercase(Locale.ROOT))) { + Monitor.MonitorType.QUERY_LEVEL_MONITOR -> { + triggers.forEach { + if (it !is QueryLevelTrigger) { + throw (IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for query level monitor")) + } } } - } - Monitor.MonitorType.BUCKET_LEVEL_MONITOR -> { - triggers.forEach { - if (it !is BucketLevelTrigger) { - throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for bucket level monitor") + + Monitor.MonitorType.BUCKET_LEVEL_MONITOR -> { + triggers.forEach { + if (it !is BucketLevelTrigger) { + throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for bucket level monitor") + } } } - } - Monitor.MonitorType.CLUSTER_METRICS_MONITOR -> { - triggers.forEach { - if (it !is QueryLevelTrigger) { - throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for cluster metrics monitor") + + Monitor.MonitorType.CLUSTER_METRICS_MONITOR -> { + triggers.forEach { + if (it !is QueryLevelTrigger) { + throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for cluster metrics monitor") + } } } - } - Monitor.MonitorType.DOC_LEVEL_MONITOR -> { - validateDocLevelQueryName(monitor) - triggers.forEach { - if (it !is DocumentLevelTrigger) { - throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for document level monitor") + + Monitor.MonitorType.DOC_LEVEL_MONITOR -> { + validateDocLevelQueryName(monitor) + triggers.forEach { + if (it !is DocumentLevelTrigger) { + throw IllegalArgumentException("Illegal trigger type, ${it.javaClass.name}, for document level monitor") + } } } } diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexWorkflowAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexWorkflowAction.kt index d631ed710..e8837c3f4 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexWorkflowAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestIndexWorkflowAction.kt @@ -6,7 +6,6 @@ package org.opensearch.alerting.resthandler import org.opensearch.action.support.WriteRequest import org.opensearch.alerting.AlertingPlugin -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IF_PRIMARY_TERM import org.opensearch.alerting.util.IF_SEQ_NO import org.opensearch.alerting.util.REFRESH @@ -15,6 +14,7 @@ import org.opensearch.commons.alerting.action.AlertingActions import org.opensearch.commons.alerting.action.IndexWorkflowRequest import org.opensearch.commons.alerting.action.IndexWorkflowResponse import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.ToXContent import org.opensearch.core.xcontent.XContentParser diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/script/BucketLevelTriggerExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/script/BucketLevelTriggerExecutionContext.kt index 40250d088..fe6e382f8 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/script/BucketLevelTriggerExecutionContext.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/script/BucketLevelTriggerExecutionContext.kt @@ -7,10 +7,10 @@ package org.opensearch.alerting.script import org.apache.logging.log4j.LogManager import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.BucketLevelTriggerRunResult -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import java.time.Instant private val logger = LogManager.getLogger(BucketLevelTriggerExecutionContext::class.java) diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/script/ChainedAlertTriggerExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/script/ChainedAlertTriggerExecutionContext.kt index d4bf4cb59..d7357abeb 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/script/ChainedAlertTriggerExecutionContext.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/script/ChainedAlertTriggerExecutionContext.kt @@ -5,10 +5,10 @@ package org.opensearch.alerting.script -import org.opensearch.alerting.model.WorkflowRunResult import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.ChainedAlertTrigger import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunResult import java.time.Instant data class ChainedAlertTriggerExecutionContext( diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/script/QueryLevelTriggerExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/script/QueryLevelTriggerExecutionContext.kt index 2247bcb01..9d934ef3f 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/script/QueryLevelTriggerExecutionContext.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/script/QueryLevelTriggerExecutionContext.kt @@ -6,10 +6,10 @@ package org.opensearch.alerting.script import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.model.QueryLevelTriggerRunResult import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.QueryLevelTrigger +import org.opensearch.commons.alerting.model.QueryLevelTriggerRunResult import java.time.Instant data class QueryLevelTriggerExecutionContext( diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/script/TriggerExecutionContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/script/TriggerExecutionContext.kt index 1f5bd8be5..f66533c17 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/script/TriggerExecutionContext.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/script/TriggerExecutionContext.kt @@ -5,8 +5,8 @@ package org.opensearch.alerting.script -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.Trigger import java.time.Instant diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/service/DeleteMonitorService.kt b/alerting/src/main/kotlin/org/opensearch/alerting/service/DeleteMonitorService.kt index 2ba1fbd17..9d4357a8f 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/service/DeleteMonitorService.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/service/DeleteMonitorService.kt @@ -25,13 +25,13 @@ import org.opensearch.alerting.MonitorMetadataService import org.opensearch.alerting.core.lock.LockModel import org.opensearch.alerting.core.lock.LockService import org.opensearch.alerting.opensearchapi.suspendUntil -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_DELEGATE_PATH import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_MONITOR_PATH import org.opensearch.client.Client import org.opensearch.commons.alerting.action.DeleteMonitorResponse import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.index.query.QueryBuilders import org.opensearch.index.reindex.BulkByScrollResponse diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/SecureTransportAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/SecureTransportAction.kt index 402780212..a0155c9fc 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/SecureTransportAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/SecureTransportAction.kt @@ -8,10 +8,10 @@ package org.opensearch.alerting.transport import org.apache.logging.log4j.LogManager import org.opensearch.OpenSearchStatusException import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.commons.ConfigConstants +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeAlertAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeAlertAction.kt index a94a682d3..a640a157e 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeAlertAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeAlertAction.kt @@ -22,7 +22,6 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.action.update.UpdateRequest import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -38,6 +37,7 @@ import org.opensearch.commons.alerting.action.GetMonitorRequest import org.opensearch.commons.alerting.action.GetMonitorResponse import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.alerting.util.optionalTimeField import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeChainedAlertAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeChainedAlertAction.kt index 26da7f644..18dca2d2f 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeChainedAlertAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportAcknowledgeChainedAlertAction.kt @@ -26,7 +26,6 @@ import org.opensearch.action.support.WriteRequest import org.opensearch.action.update.UpdateRequest import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.ScheduledJobUtils import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -44,6 +43,7 @@ import org.opensearch.commons.alerting.model.CompositeInput import org.opensearch.commons.alerting.model.DataSources import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.alerting.util.optionalTimeField import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteAlertingCommentAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteAlertingCommentAction.kt index 87e6a03a1..f31510d52 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteAlertingCommentAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteAlertingCommentAction.kt @@ -19,7 +19,6 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.comments.CommentsIndices.Companion.ALL_COMMENTS_INDEX_PATTERN import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -31,6 +30,7 @@ import org.opensearch.commons.alerting.action.AlertingActions import org.opensearch.commons.alerting.action.DeleteCommentRequest import org.opensearch.commons.alerting.action.DeleteCommentResponse import org.opensearch.commons.alerting.model.Comment +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteMonitorAction.kt index 321c2e162..608f66dbf 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteMonitorAction.kt @@ -19,7 +19,6 @@ import org.opensearch.action.support.WriteRequest.RefreshPolicy import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.service.DeleteMonitorService import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -32,6 +31,7 @@ import org.opensearch.commons.alerting.action.DeleteMonitorRequest import org.opensearch.commons.alerting.action.DeleteMonitorResponse import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteWorkflowAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteWorkflowAction.kt index f5c062c88..8fb2533fb 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteWorkflowAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDeleteWorkflowAction.kt @@ -25,13 +25,10 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.action.support.WriteRequest.RefreshPolicy import org.opensearch.alerting.core.lock.LockModel import org.opensearch.alerting.core.lock.LockService -import org.opensearch.alerting.model.MonitorMetadata -import org.opensearch.alerting.model.WorkflowMetadata import org.opensearch.alerting.opensearchapi.addFilter import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.service.DeleteMonitorService import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_DELEGATE_PATH import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_MONITOR_PATH import org.opensearch.client.Client @@ -46,8 +43,11 @@ import org.opensearch.commons.alerting.action.DeleteWorkflowRequest import org.opensearch.commons.alerting.action.DeleteWorkflowResponse import org.opensearch.commons.alerting.model.CompositeInput import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowMetadata +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDocLevelMonitorFanOutAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDocLevelMonitorFanOutAction.kt index bd3d32ce7..4f24fe2d1 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDocLevelMonitorFanOutAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportDocLevelMonitorFanOutAction.kt @@ -28,22 +28,12 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.AlertService import org.opensearch.alerting.MonitorRunnerService import org.opensearch.alerting.TriggerService -import org.opensearch.alerting.action.DocLevelMonitorFanOutAction -import org.opensearch.alerting.action.DocLevelMonitorFanOutRequest -import org.opensearch.alerting.action.DocLevelMonitorFanOutResponse import org.opensearch.alerting.action.GetDestinationsAction import org.opensearch.alerting.action.GetDestinationsRequest import org.opensearch.alerting.action.GetDestinationsResponse -import org.opensearch.alerting.model.ActionRunResult import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.IndexExecutionContext -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.MonitorMetadata -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.alerting.model.destination.Destination import org.opensearch.alerting.model.destination.DestinationContextFactory -import org.opensearch.alerting.model.userErrorMessage import org.opensearch.alerting.opensearchapi.InjectorContextElement import org.opensearch.alerting.opensearchapi.convertToMap import org.opensearch.alerting.opensearchapi.suspendUntil @@ -61,7 +51,6 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.MAX_ACTIONABL import org.opensearch.alerting.settings.AlertingSettings.Companion.PERCOLATE_QUERY_DOCS_SIZE_MEMORY_PERCENTAGE_LIMIT import org.opensearch.alerting.settings.AlertingSettings.Companion.PERCOLATE_QUERY_MAX_NUM_DOCS_IN_MEMORY import org.opensearch.alerting.settings.DestinationSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.defaultToPerExecutionAction import org.opensearch.alerting.util.destinationmigration.NotificationActionConfigs import org.opensearch.alerting.util.destinationmigration.NotificationApiUtils @@ -73,7 +62,6 @@ import org.opensearch.alerting.util.isAllowed import org.opensearch.alerting.util.isTestAction import org.opensearch.alerting.util.parseSampleDocTags import org.opensearch.alerting.util.printsSampleDocData -import org.opensearch.alerting.workflow.WorkflowRunContext import org.opensearch.client.Client import org.opensearch.client.node.NodeClient import org.opensearch.cluster.routing.Preference @@ -83,18 +71,30 @@ import org.opensearch.common.settings.Settings import org.opensearch.common.xcontent.XContentFactory import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.AlertingPluginInterface +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutAction +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutRequest +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse import org.opensearch.commons.alerting.action.PublishFindingsRequest import org.opensearch.commons.alerting.action.SubscribeFindingsResponse import org.opensearch.commons.alerting.model.ActionExecutionResult +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.DocLevelQuery import org.opensearch.commons.alerting.model.DocumentLevelTrigger +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult import org.opensearch.commons.alerting.model.Finding +import org.opensearch.commons.alerting.model.IndexExecutionContext +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.Table +import org.opensearch.commons.alerting.model.WorkflowRunContext import org.opensearch.commons.alerting.model.action.Action import org.opensearch.commons.alerting.model.action.PerAlertActionScope +import org.opensearch.commons.alerting.model.userErrorMessage +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.alerting.util.string import org.opensearch.commons.notifications.model.NotificationConfigInfo import org.opensearch.core.action.ActionListener @@ -208,7 +208,7 @@ class TransportDocLevelMonitorFanOutAction try { val monitor = request.monitor var monitorResult = MonitorRunResult(monitor.name, Instant.now(), Instant.now()) - val updatedIndexNames = request.indexExecutionContext.updatedIndexNames + val updatedIndexNames = request.indexExecutionContext!!.updatedIndexNames val monitorMetadata = request.monitorMetadata val shardIds = request.shardIds val indexExecutionContext = request.indexExecutionContext @@ -251,7 +251,7 @@ class TransportDocLevelMonitorFanOutAction fetchShardDataAndMaybeExecutePercolateQueries( monitor, - indexExecutionContext, + indexExecutionContext!!, monitorMetadata, inputRunResults, docsToQueries, diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteMonitorAction.kt index cf07dd1b4..9b3c40881 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteMonitorAction.kt @@ -22,7 +22,6 @@ import org.opensearch.alerting.action.ExecuteMonitorAction import org.opensearch.alerting.action.ExecuteMonitorRequest import org.opensearch.alerting.action.ExecuteMonitorResponse import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DocLevelMonitorQueries import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -34,6 +33,8 @@ import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.ConfigConstants import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.commons.authuser.User import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus @@ -41,6 +42,7 @@ import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.tasks.Task import org.opensearch.transport.TransportService import java.time.Instant +import java.util.Locale private val log = LogManager.getLogger(TransportExecuteMonitorAction::class.java) private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO) @@ -83,7 +85,7 @@ class TransportExecuteMonitorAction @Inject constructor( } try { log.info( - "Executing monitor from API - id: ${monitor.id}, type: ${monitor.monitorType.name}, " + + "Executing monitor from API - id: ${monitor.id}, type: ${monitor.monitorType}, " + "periodStart: $periodStart, periodEnd: $periodEnd, dryrun: ${execMonitorRequest.dryrun}" ) val monitorRunResult = runner.runJob(monitor, periodStart, periodEnd, execMonitorRequest.dryrun, transportService) @@ -137,7 +139,10 @@ class TransportExecuteMonitorAction @Inject constructor( false -> (execMonitorRequest.monitor as Monitor).copy(user = user) } - if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + if ( + monitor.isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(monitor.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.DOC_LEVEL_MONITOR + ) { try { scope.launch { if (!docLevelMonitorQueries.docLevelQueryIndexExists(monitor.dataSources)) { diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteWorkflowAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteWorkflowAction.kt index 4749fef6f..a84f9b6e9 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteWorkflowAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportExecuteWorkflowAction.kt @@ -18,7 +18,6 @@ import org.opensearch.alerting.MonitorRunnerService import org.opensearch.alerting.action.ExecuteWorkflowAction import org.opensearch.alerting.action.ExecuteWorkflowRequest import org.opensearch.alerting.action.ExecuteWorkflowResponse -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.common.inject.Inject import org.opensearch.common.xcontent.LoggingDeprecationHandler @@ -27,6 +26,7 @@ import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.ConfigConstants import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetAlertsAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetAlertsAction.kt index 2745501b7..c59768ffa 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetAlertsAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetAlertsAction.kt @@ -20,7 +20,6 @@ import org.opensearch.alerting.alerts.AlertIndices import org.opensearch.alerting.opensearchapi.addFilter import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -34,6 +33,7 @@ import org.opensearch.commons.alerting.action.GetAlertsResponse import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetDestinationsAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetDestinationsAction.kt index 4036769ad..a0e1c63fb 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetDestinationsAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetDestinationsAction.kt @@ -16,7 +16,6 @@ import org.opensearch.alerting.action.GetDestinationsResponse import org.opensearch.alerting.model.destination.Destination import org.opensearch.alerting.opensearchapi.addFilter import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -24,6 +23,7 @@ import org.opensearch.common.settings.Settings import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.core.action.ActionListener import org.opensearch.core.common.Strings diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailAccountAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailAccountAction.kt index cae1f2298..465579d04 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailAccountAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailAccountAction.kt @@ -16,7 +16,6 @@ import org.opensearch.alerting.action.GetEmailAccountRequest import org.opensearch.alerting.action.GetEmailAccountResponse import org.opensearch.alerting.model.destination.email.EmailAccount import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DestinationType import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -26,6 +25,7 @@ import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.ScheduledJob.Companion.SCHEDULED_JOBS_INDEX +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailGroupAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailGroupAction.kt index 4bc51440c..ccd31fc60 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailGroupAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetEmailGroupAction.kt @@ -16,7 +16,6 @@ import org.opensearch.alerting.action.GetEmailGroupRequest import org.opensearch.alerting.action.GetEmailGroupResponse import org.opensearch.alerting.model.destination.email.EmailGroup import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DestinationType import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -26,6 +25,7 @@ import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.ScheduledJob.Companion.SCHEDULED_JOBS_INDEX +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetFindingsAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetFindingsAction.kt index 5955ea1f6..bc6890c00 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetFindingsAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetFindingsAction.kt @@ -21,7 +21,6 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.alerts.AlertIndices.Companion.ALL_FINDING_INDEX_PATTERN import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -36,6 +35,7 @@ import org.opensearch.commons.alerting.action.GetMonitorResponse import org.opensearch.commons.alerting.model.Finding import org.opensearch.commons.alerting.model.FindingDocument import org.opensearch.commons.alerting.model.FindingWithDocs +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener import org.opensearch.core.common.Strings diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetMonitorAction.kt index 3a6f090ec..8343108e6 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetMonitorAction.kt @@ -20,7 +20,6 @@ import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_DELEGATE_PATH import org.opensearch.alerting.util.ScheduledJobUtils.Companion.WORKFLOW_MONITOR_PATH import org.opensearch.client.Client @@ -37,6 +36,7 @@ import org.opensearch.commons.alerting.action.GetMonitorResponse.AssociatedWorkf import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetRemoteIndexesAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetRemoteIndexesAction.kt index 446c96649..7253162fa 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetRemoteIndexesAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetRemoteIndexesAction.kt @@ -28,12 +28,12 @@ import org.opensearch.alerting.action.GetRemoteIndexesResponse.ClusterIndexes.Cl import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings import org.opensearch.alerting.settings.AlertingSettings.Companion.CROSS_CLUSTER_MONITORING_ENABLED -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.CrossClusterMonitorUtils import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject import org.opensearch.common.settings.Settings +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAction.kt index c7bd42904..e04f6c4f5 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAction.kt @@ -12,7 +12,6 @@ import org.opensearch.action.get.GetResponse import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -25,6 +24,7 @@ import org.opensearch.commons.alerting.action.GetWorkflowRequest import org.opensearch.commons.alerting.action.GetWorkflowResponse import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAlertsAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAlertsAction.kt index 29bec8a1d..b42989bed 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAlertsAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportGetWorkflowAlertsAction.kt @@ -18,7 +18,6 @@ import org.opensearch.alerting.alerts.AlertIndices import org.opensearch.alerting.opensearchapi.addFilter import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -31,6 +30,7 @@ import org.opensearch.commons.alerting.action.GetAlertsRequest import org.opensearch.commons.alerting.action.GetWorkflowAlertsRequest import org.opensearch.commons.alerting.action.GetWorkflowAlertsResponse import org.opensearch.commons.alerting.model.Alert +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexAlertingCommentAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexAlertingCommentAction.kt index f26fa3528..43a3ed147 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexAlertingCommentAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexAlertingCommentAction.kt @@ -27,7 +27,6 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.ALERTING_COMM import org.opensearch.alerting.settings.AlertingSettings.Companion.COMMENTS_MAX_CONTENT_SIZE import org.opensearch.alerting.settings.AlertingSettings.Companion.INDEX_TIMEOUT import org.opensearch.alerting.settings.AlertingSettings.Companion.MAX_COMMENTS_PER_ALERT -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.CommentsUtils import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService @@ -42,6 +41,7 @@ import org.opensearch.commons.alerting.action.IndexCommentRequest import org.opensearch.commons.alerting.action.IndexCommentResponse import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Comment +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexMonitorAction.kt index 057977079..4b49cbd1d 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexMonitorAction.kt @@ -31,7 +31,6 @@ import org.opensearch.action.support.WriteRequest.RefreshPolicy import org.opensearch.action.support.master.AcknowledgedResponse import org.opensearch.alerting.MonitorMetadataService import org.opensearch.alerting.core.ScheduledJobIndices -import org.opensearch.alerting.model.MonitorMetadata import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.service.DeleteMonitorService import org.opensearch.alerting.settings.AlertingSettings @@ -40,7 +39,6 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.INDEX_TIMEOUT import org.opensearch.alerting.settings.AlertingSettings.Companion.MAX_ACTION_THROTTLE_VALUE import org.opensearch.alerting.settings.AlertingSettings.Companion.REQUEST_TIMEOUT import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DocLevelMonitorQueries import org.opensearch.alerting.util.IndexUtils import org.opensearch.alerting.util.addUserBackendRolesFilter @@ -61,9 +59,14 @@ import org.opensearch.commons.alerting.action.IndexMonitorResponse import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.DocLevelMonitorInput.Companion.DOC_LEVEL_INPUT_FIELD import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.ScheduledJob.Companion.SCHEDULED_JOBS_INDEX import org.opensearch.commons.alerting.model.SearchInput +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput.Companion.REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener @@ -81,6 +84,7 @@ import org.opensearch.tasks.Task import org.opensearch.transport.TransportService import java.io.IOException import java.time.Duration +import java.util.Locale private val log = LogManager.getLogger(TransportIndexMonitorAction::class.java) private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO) @@ -192,10 +196,15 @@ class TransportIndexMonitorAction @Inject constructor( ) { val indices = mutableListOf() // todo: for doc level alerting: check if index is present before monitor is created. - val searchInputs = request.monitor.inputs.filter { it.name() == SearchInput.SEARCH_FIELD || it.name() == DOC_LEVEL_INPUT_FIELD } + val searchInputs = request.monitor.inputs.filter { + it.name() == SearchInput.SEARCH_FIELD || + it.name() == DOC_LEVEL_INPUT_FIELD || + it.name() == REMOTE_DOC_LEVEL_MONITOR_INPUT_FIELD + } searchInputs.forEach { val inputIndices = if (it.name() == SearchInput.SEARCH_FIELD) (it as SearchInput).indices - else (it as DocLevelMonitorInput).indices + else if (it.name() == DOC_LEVEL_INPUT_FIELD) (it as DocLevelMonitorInput).indices + else (it as RemoteDocLevelMonitorInput).docLevelMonitorInput.indices indices.addAll(inputIndices) } val updatedIndices = indices.map { index -> @@ -536,7 +545,11 @@ class TransportIndexMonitorAction @Inject constructor( throw t } try { - if (request.monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + if ( + request.monitor.isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(request.monitor.monitorType.uppercase(Locale.ROOT)) == + Monitor.MonitorType.DOC_LEVEL_MONITOR + ) { indexDocLevelMonitorQueries(request.monitor, indexResponse.id, metadata, request.refreshPolicy) } // When inserting queries in queryIndex we could update sourceToQueryIndexMapping @@ -700,7 +713,11 @@ class TransportIndexMonitorAction @Inject constructor( val (metadata, created) = MonitorMetadataService.getOrCreateMetadata(request.monitor) // Recreate runContext if metadata exists // Delete and insert all queries from/to queryIndex - if (created == false && currentMonitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + + if (!created && + currentMonitor.isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(currentMonitor.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.DOC_LEVEL_MONITOR + ) { updatedMetadata = MonitorMetadataService.recreateRunContext(metadata, currentMonitor) client.suspendUntil { DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE) diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexWorkflowAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexWorkflowAction.kt index 26d834fe6..0538aa207 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexWorkflowAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportIndexWorkflowAction.kt @@ -41,7 +41,6 @@ import org.opensearch.alerting.settings.AlertingSettings.Companion.INDEX_TIMEOUT import org.opensearch.alerting.settings.AlertingSettings.Companion.MAX_ACTION_THROTTLE_VALUE import org.opensearch.alerting.settings.AlertingSettings.Companion.REQUEST_TIMEOUT import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.IndexUtils import org.opensearch.alerting.util.isADMonitor import org.opensearch.alerting.util.isQueryLevelMonitor @@ -65,6 +64,8 @@ import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.ScheduledJob.Companion.SCHEDULED_JOBS_INDEX import org.opensearch.commons.alerting.model.SearchInput import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener @@ -78,6 +79,7 @@ import org.opensearch.rest.RestRequest import org.opensearch.search.builder.SearchSourceBuilder import org.opensearch.tasks.Task import org.opensearch.transport.TransportService +import java.util.Locale import java.util.UUID import java.util.stream.Collectors @@ -399,7 +401,9 @@ class TransportIndexWorkflowAction @Inject constructor( log.warn("Metadata doc id:${monitorMetadata.id} exists, but it shouldn't!") } - if (monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + if ( + Monitor.MonitorType.valueOf(monitor.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.DOC_LEVEL_MONITOR + ) { val oldMonitorMetadata = MonitorMetadataService.getMetadata(monitor) monitorMetadata = monitorMetadata.copy(sourceToQueryIndexMapping = oldMonitorMetadata!!.sourceToQueryIndexMapping) } @@ -552,8 +556,9 @@ class TransportIndexWorkflowAction @Inject constructor( createWithRunContext = true, workflowMetadataId = workflowMetadata.id ) - - if (created == false && monitor.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR) { + if (!created && + Monitor.MonitorType.valueOf(monitor.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.DOC_LEVEL_MONITOR + ) { var updatedMetadata = MonitorMetadataService.recreateRunContext(monitorMetadata, monitor) val oldMonitorMetadata = MonitorMetadataService.getMetadata(monitor) updatedMetadata = updatedMetadata.copy(sourceToQueryIndexMapping = oldMonitorMetadata!!.sourceToQueryIndexMapping) @@ -631,24 +636,28 @@ class TransportIndexWorkflowAction @Inject constructor( * Returns list of indices for the given monitor depending on it's type */ private fun getMonitorIndices(monitor: Monitor): List { - return when (monitor.monitorType) { - Monitor.MonitorType.DOC_LEVEL_MONITOR -> (monitor.inputs[0] as DocLevelMonitorInput).indices - Monitor.MonitorType.BUCKET_LEVEL_MONITOR -> monitor.inputs.flatMap { s -> (s as SearchInput).indices } - Monitor.MonitorType.QUERY_LEVEL_MONITOR -> { - if (isADMonitor(monitor)) monitor.inputs.flatMap { s -> (s as SearchInput).indices } - else { - val indices = mutableListOf() - for (input in monitor.inputs) { - when (input) { - is SearchInput -> indices.addAll(input.indices) - else -> indices + if (monitor.isMonitorOfStandardType()) { + return when (Monitor.MonitorType.valueOf(monitor.monitorType.uppercase(Locale.ROOT))) { + Monitor.MonitorType.DOC_LEVEL_MONITOR -> (monitor.inputs[0] as DocLevelMonitorInput).indices + Monitor.MonitorType.BUCKET_LEVEL_MONITOR -> monitor.inputs.flatMap { s -> (s as SearchInput).indices } + Monitor.MonitorType.QUERY_LEVEL_MONITOR -> { + if (isADMonitor(monitor)) monitor.inputs.flatMap { s -> (s as SearchInput).indices } + else { + val indices = mutableListOf() + for (input in monitor.inputs) { + when (input) { + is SearchInput -> indices.addAll(input.indices) + else -> indices + } } + indices } - indices } - } - else -> emptyList() + else -> emptyList() + } + } else { + return emptyList() } } diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchAlertingCommentAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchAlertingCommentAction.kt index 2e6199726..c5162e73b 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchAlertingCommentAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchAlertingCommentAction.kt @@ -18,7 +18,6 @@ import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.alerts.AlertIndices.Companion.ALL_ALERT_INDEX_PATTERN import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -30,6 +29,7 @@ import org.opensearch.commons.alerting.action.AlertingActions import org.opensearch.commons.alerting.action.SearchCommentRequest import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Comment +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailAccountAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailAccountAction.kt index 29ebbb90f..078ec90ba 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailAccountAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailAccountAction.kt @@ -12,12 +12,12 @@ import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.action.SearchEmailAccountAction import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DestinationType import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject import org.opensearch.common.settings.Settings +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.tasks.Task diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailGroupAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailGroupAction.kt index c6fc84640..f6a46a03f 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailGroupAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchEmailGroupAction.kt @@ -12,12 +12,12 @@ import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.action.SearchEmailGroupAction import org.opensearch.alerting.settings.DestinationSettings.Companion.ALLOW_LIST -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DestinationType import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject import org.opensearch.common.settings.Settings +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.tasks.Task diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchMonitorAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchMonitorAction.kt index 7359d60ea..d61056bb9 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchMonitorAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportSearchMonitorAction.kt @@ -13,7 +13,6 @@ import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction import org.opensearch.alerting.opensearchapi.addFilter import org.opensearch.alerting.settings.AlertingSettings -import org.opensearch.alerting.util.AlertingException import org.opensearch.client.Client import org.opensearch.cluster.service.ClusterService import org.opensearch.common.inject.Inject @@ -23,6 +22,7 @@ import org.opensearch.commons.alerting.action.SearchMonitorRequest import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.authuser.User import org.opensearch.commons.utils.recreateObject import org.opensearch.core.action.ActionListener diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/util/AggregationQueryRewriter.kt b/alerting/src/main/kotlin/org/opensearch/alerting/util/AggregationQueryRewriter.kt index 3989bd384..74cedd59a 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/util/AggregationQueryRewriter.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/util/AggregationQueryRewriter.kt @@ -6,11 +6,11 @@ package org.opensearch.alerting.util import org.opensearch.action.search.SearchResponse -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.TriggerAfterKey import org.opensearch.alerting.opensearchapi.convertToMap import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Trigger +import org.opensearch.commons.alerting.model.TriggerAfterKey import org.opensearch.search.aggregations.AggregationBuilder import org.opensearch.search.aggregations.AggregationBuilders import org.opensearch.search.aggregations.AggregatorFactories @@ -86,7 +86,7 @@ class AggregationQueryRewriter { } else { // if the afterKey from previous result is null, what does it signify? // A) result set exhausted OR B) first page ? - val afterKey = prevResult.aggTriggersAfterKey[trigger.id]!!.afterKey + val afterKey = prevResult.aggTriggersAfterKey!![trigger.id]!!.afterKey factory.aggregateAfter(afterKey) } } else { diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingException.kt b/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingException.kt deleted file mode 100644 index 3ed7a8674..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingException.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.util - -import org.apache.logging.log4j.LogManager -import org.opensearch.OpenSearchException -import org.opensearch.OpenSearchSecurityException -import org.opensearch.OpenSearchStatusException -import org.opensearch.core.common.Strings -import org.opensearch.core.rest.RestStatus -import org.opensearch.index.IndexNotFoundException -import org.opensearch.index.engine.VersionConflictEngineException -import org.opensearch.indices.InvalidIndexNameException - -private val log = LogManager.getLogger(AlertingException::class.java) - -/** - * Converts into a user friendly message. - */ -class AlertingException(message: String, val status: RestStatus, val ex: Exception) : OpenSearchException(message, ex) { - - override fun status(): RestStatus { - return status - } - - companion object { - @JvmStatic - fun wrap(ex: Exception): OpenSearchException { - log.error("Alerting error: $ex") - - var friendlyMsg = "Unknown error" - var status = RestStatus.INTERNAL_SERVER_ERROR - when (ex) { - is IndexNotFoundException -> { - status = ex.status() - friendlyMsg = "Configured indices are not found: ${ex.index}" - } - is OpenSearchSecurityException -> { - status = ex.status() - friendlyMsg = "User doesn't have permissions to execute this action. Contact administrator." - } - is OpenSearchStatusException -> { - status = ex.status() - friendlyMsg = ex.message as String - } - is IllegalArgumentException -> { - status = RestStatus.BAD_REQUEST - friendlyMsg = ex.message as String - } - is VersionConflictEngineException -> { - status = ex.status() - friendlyMsg = ex.message as String - } - is InvalidIndexNameException -> { - status = RestStatus.BAD_REQUEST - friendlyMsg = ex.message as String - } - else -> { - if (!Strings.isNullOrEmpty(ex.message)) { - friendlyMsg = ex.message as String - } - } - } - // Wrapping the origin exception as runtime to avoid it being formatted. - // Currently, alerting-kibana is using `error.root_cause.reason` as text in the toast message. - // Below logic is to set friendly message to error.root_cause.reason. - return AlertingException(friendlyMsg, status, Exception("${ex.javaClass.name}: ${ex.message}")) - } - - @JvmStatic - fun merge(vararg ex: AlertingException): AlertingException { - var friendlyMsg = "" - var unwrappedExceptionMsg = "" - ex.forEach { - if (friendlyMsg != "") { - friendlyMsg += ", ${it.message}" - unwrappedExceptionMsg += ", ${it.ex.message}" - } else { - friendlyMsg = it.message.orEmpty() - unwrappedExceptionMsg = "${it.ex.message}" - } - } - return AlertingException(friendlyMsg, ex.first().status, Exception(unwrappedExceptionMsg)) - } - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingUtils.kt b/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingUtils.kt index 5c9e8f280..241bb3d22 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingUtils.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/util/AlertingUtils.kt @@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager import org.opensearch.alerting.AlertService import org.opensearch.alerting.MonitorRunnerService import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.BucketLevelTriggerRunResult import org.opensearch.alerting.model.destination.Destination import org.opensearch.alerting.script.BucketLevelTriggerExecutionContext import org.opensearch.alerting.script.DocumentLevelTriggerExecutionContext @@ -18,6 +17,7 @@ import org.opensearch.cluster.service.ClusterService import org.opensearch.common.settings.Settings import org.opensearch.commons.alerting.model.AggregationResultBucket import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult import org.opensearch.commons.alerting.model.DocumentLevelTrigger import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.Trigger @@ -25,7 +25,9 @@ import org.opensearch.commons.alerting.model.action.Action import org.opensearch.commons.alerting.model.action.ActionExecutionPolicy import org.opensearch.commons.alerting.model.action.ActionExecutionScope import org.opensearch.commons.alerting.util.isBucketLevelMonitor +import org.opensearch.commons.alerting.util.isMonitorOfStandardType import org.opensearch.script.Script +import java.util.Locale import kotlin.math.max private val logger = LogManager.getLogger("AlertingUtils") @@ -77,9 +79,13 @@ fun Destination.isAllowed(allowList: List): Boolean = allowList.contains fun Destination.isTestAction(): Boolean = this.type == DestinationType.TEST_ACTION -fun Monitor.isDocLevelMonitor(): Boolean = this.monitorType == Monitor.MonitorType.DOC_LEVEL_MONITOR +fun Monitor.isDocLevelMonitor(): Boolean = + this.isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(this.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.DOC_LEVEL_MONITOR -fun Monitor.isQueryLevelMonitor(): Boolean = this.monitorType == Monitor.MonitorType.QUERY_LEVEL_MONITOR +fun Monitor.isQueryLevelMonitor(): Boolean = + this.isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(this.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.QUERY_LEVEL_MONITOR /** * Since buckets can have multi-value keys, this converts the bucket key values to a string that can be used diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.kt b/alerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.kt index 52ef1d26f..3a0a11e91 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.kt @@ -27,7 +27,6 @@ import org.opensearch.action.index.IndexRequest import org.opensearch.action.support.WriteRequest.RefreshPolicy import org.opensearch.action.support.master.AcknowledgedResponse import org.opensearch.alerting.MonitorRunnerService.monitorCtx -import org.opensearch.alerting.model.MonitorMetadata import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.client.Client import org.opensearch.cluster.ClusterState @@ -39,7 +38,9 @@ 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.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata import org.opensearch.commons.alerting.model.ScheduledJob +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener import org.opensearch.core.rest.RestStatus import org.opensearch.index.mapper.MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/util/ScheduledJobUtils.kt b/alerting/src/main/kotlin/org/opensearch/alerting/util/ScheduledJobUtils.kt index 70fe42a38..a00f21608 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/util/ScheduledJobUtils.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/util/ScheduledJobUtils.kt @@ -14,6 +14,7 @@ import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.NamedXContentRegistry diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/CompositeWorkflowRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/workflow/CompositeWorkflowRunner.kt index 30ac94ca6..14b951572 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/CompositeWorkflowRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/workflow/CompositeWorkflowRunner.kt @@ -13,12 +13,8 @@ import org.opensearch.alerting.DocumentLevelMonitorRunner import org.opensearch.alerting.MonitorRunnerExecutionContext import org.opensearch.alerting.QueryLevelMonitorRunner import org.opensearch.alerting.WorkflowMetadataService -import org.opensearch.alerting.model.ChainedAlertTriggerRunResult -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.model.WorkflowRunResult import org.opensearch.alerting.opensearchapi.suspendUntil import org.opensearch.alerting.script.ChainedAlertTriggerExecutionContext -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.isDocLevelMonitor import org.opensearch.alerting.util.isQueryLevelMonitor import org.opensearch.common.xcontent.LoggingDeprecationHandler @@ -26,11 +22,16 @@ import org.opensearch.common.xcontent.XContentHelper import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.ChainedAlertTrigger +import org.opensearch.commons.alerting.model.ChainedAlertTriggerRunResult import org.opensearch.commons.alerting.model.CompositeInput import org.opensearch.commons.alerting.model.DataSources import org.opensearch.commons.alerting.model.Delegate import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunContext +import org.opensearch.commons.alerting.model.WorkflowRunResult +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.commons.alerting.util.isBucketLevelMonitor import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunContext.kt b/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunContext.kt deleted file mode 100644 index 8ebb2be9c..000000000 --- a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunContext.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.workflow - -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.common.io.stream.StreamOutput -import org.opensearch.core.common.io.stream.Writeable -import org.opensearch.core.xcontent.ToXContent -import org.opensearch.core.xcontent.ToXContentObject -import org.opensearch.core.xcontent.XContentBuilder - -data class WorkflowRunContext( - // In case of dry run it's random generated id, while in other cases it's workflowId - val workflowId: String, - val workflowMetadataId: String, - val chainedMonitorId: String?, - val matchingDocIdsPerIndex: Map>, - val auditDelegateMonitorAlerts: Boolean, -) : Writeable, ToXContentObject { - companion object { - fun readFrom(sin: StreamInput): WorkflowRunContext { - return WorkflowRunContext(sin) - } - } - - constructor(sin: StreamInput) : this( - sin.readString(), - sin.readString(), - sin.readOptionalString(), - sin.readMap() as Map>, - sin.readBoolean() - ) - - override fun writeTo(out: StreamOutput) { - out.writeString(workflowId) - out.writeString(workflowMetadataId) - out.writeOptionalString(chainedMonitorId) - out.writeMap(matchingDocIdsPerIndex) - out.writeBoolean(auditDelegateMonitorAlerts) - } - - override fun toXContent(builder: XContentBuilder, params: ToXContent.Params?): XContentBuilder { - builder.startObject() - .field("workflow_id", workflowId) - .field("workflow_metadata_id", workflowMetadataId) - .field("chained_monitor_id", chainedMonitorId) - .field("matching_doc_ids_per_index", matchingDocIdsPerIndex) - .field("audit_delegate_monitor_alerts", auditDelegateMonitorAlerts) - .endObject() - return builder - } -} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunner.kt b/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunner.kt index 83fbf7212..743895836 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunner.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/workflow/WorkflowRunner.kt @@ -11,8 +11,6 @@ import org.opensearch.alerting.MonitorRunnerService import org.opensearch.alerting.action.GetDestinationsAction import org.opensearch.alerting.action.GetDestinationsRequest import org.opensearch.alerting.action.GetDestinationsResponse -import org.opensearch.alerting.model.ActionRunResult -import org.opensearch.alerting.model.WorkflowRunResult import org.opensearch.alerting.model.destination.Destination import org.opensearch.alerting.opensearchapi.InjectorContextElement import org.opensearch.alerting.opensearchapi.suspendUntil @@ -26,8 +24,10 @@ import org.opensearch.alerting.util.destinationmigration.sendNotification import org.opensearch.alerting.util.isAllowed import org.opensearch.alerting.util.isTestAction import org.opensearch.client.node.NodeClient +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.Table import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowRunResult import org.opensearch.commons.alerting.model.action.Action import org.opensearch.commons.notifications.model.NotificationConfigInfo import org.opensearch.core.common.Strings diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt b/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt index 6eda9ec30..49cb863e0 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt @@ -494,7 +494,7 @@ fun randomADMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/MonitorDataSourcesIT.kt b/alerting/src/test/kotlin/org/opensearch/alerting/MonitorDataSourcesIT.kt index c4f3b0b03..1bef411bd 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/MonitorDataSourcesIT.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/MonitorDataSourcesIT.kt @@ -26,10 +26,7 @@ import org.opensearch.action.search.SearchRequest import org.opensearch.action.support.WriteRequest import org.opensearch.alerting.alerts.AlertIndices import org.opensearch.alerting.core.ScheduledJobIndices -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.WorkflowMetadata import org.opensearch.alerting.transport.AlertingSingleNodeTestCase -import org.opensearch.alerting.util.AlertingException import org.opensearch.alerting.util.DocLevelMonitorQueries import org.opensearch.alerting.util.DocLevelMonitorQueries.Companion.INDEX_PATTERN_SUFFIX import org.opensearch.alerting.workflow.CompositeWorkflowRunner @@ -55,6 +52,7 @@ import org.opensearch.commons.alerting.model.DataSources import org.opensearch.commons.alerting.model.Delegate import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.DocLevelQuery +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult import org.opensearch.commons.alerting.model.IntervalSchedule import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.model.ScheduledJob @@ -63,6 +61,8 @@ import org.opensearch.commons.alerting.model.ScheduledJob.Companion.SCHEDULED_JO import org.opensearch.commons.alerting.model.SearchInput import org.opensearch.commons.alerting.model.Table import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowMetadata +import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.rest.RestStatus import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils @@ -1599,6 +1599,26 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() { """.trimIndent() val monitorId = "abc" indexDoc(SCHEDULED_JOBS_INDEX, monitorId, monitorStringWithoutName) + + val monitorMetadata = """ + { + "metadata": { + "monitor_id": "$monitorId", + "last_action_execution_times": [], + "last_run_context": { + "$index": { + "0": -1, + "index": "$index", + "shards_count": 1 + } + }, + "source_to_query_index_mapping": { + "$index$monitorId": ".opensearch-alerting-queries-000001" + } + } + } + """.trimIndent() + indexDoc(SCHEDULED_JOBS_INDEX, "$monitorId-metadata", monitorMetadata) val getMonitorResponse = getMonitorResponse(monitorId) Assert.assertNotNull(getMonitorResponse) Assert.assertNotNull(getMonitorResponse.monitor) @@ -1716,6 +1736,26 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() { """.trimIndent() val monitorId = "abc" indexDoc(SCHEDULED_JOBS_INDEX, monitorId, monitorStringWithoutName) + + val monitorMetadata = """ + { + "metadata": { + "monitor_id": "$monitorId", + "last_action_execution_times": [], + "last_run_context": { + "$index": { + "0": -1, + "index": "$index", + "shards_count": 1 + } + }, + "source_to_query_index_mapping": { + "$index$monitorId": ".opensearch-alerting-queries-000001" + } + } + } + """.trimIndent() + indexDoc(SCHEDULED_JOBS_INDEX, "$monitorId-metadata", monitorMetadata) val getMonitorResponse = getMonitorResponse(monitorId) Assert.assertNotNull(getMonitorResponse) Assert.assertNotNull(getMonitorResponse.monitor) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt b/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt index 209df41f0..0557ad482 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt @@ -8,13 +8,7 @@ package org.opensearch.alerting import junit.framework.TestCase.assertNull import org.apache.http.Header import org.apache.http.HttpEntity -import org.opensearch.alerting.model.ActionRunResult import org.opensearch.alerting.model.AlertContext -import org.opensearch.alerting.model.BucketLevelTriggerRunResult -import org.opensearch.alerting.model.DocumentLevelTriggerRunResult -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.MonitorRunResult -import org.opensearch.alerting.model.QueryLevelTriggerRunResult import org.opensearch.alerting.model.destination.email.EmailAccount import org.opensearch.alerting.model.destination.email.EmailEntry import org.opensearch.alerting.model.destination.email.EmailGroup @@ -32,9 +26,11 @@ import org.opensearch.common.xcontent.XContentType import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelectorExtAggregationBuilder import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelectorExtFilter import org.opensearch.commons.alerting.model.ActionExecutionResult +import org.opensearch.commons.alerting.model.ActionRunResult import org.opensearch.commons.alerting.model.AggregationResultBucket import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.BucketLevelTrigger +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult import org.opensearch.commons.alerting.model.ChainedAlertTrigger import org.opensearch.commons.alerting.model.ChainedMonitorFindings import org.opensearch.commons.alerting.model.ClusterMetricsInput @@ -44,11 +40,15 @@ import org.opensearch.commons.alerting.model.Delegate import org.opensearch.commons.alerting.model.DocLevelMonitorInput import org.opensearch.commons.alerting.model.DocLevelQuery import org.opensearch.commons.alerting.model.DocumentLevelTrigger +import org.opensearch.commons.alerting.model.DocumentLevelTriggerRunResult import org.opensearch.commons.alerting.model.Finding import org.opensearch.commons.alerting.model.Input +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.IntervalSchedule import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.commons.alerting.model.QueryLevelTrigger +import org.opensearch.commons.alerting.model.QueryLevelTriggerRunResult import org.opensearch.commons.alerting.model.Schedule import org.opensearch.commons.alerting.model.SearchInput import org.opensearch.commons.alerting.model.Sequence @@ -95,7 +95,7 @@ fun randomQueryLevelMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -113,7 +113,7 @@ fun randomQueryLevelMonitorWithoutUser( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = null, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -137,7 +137,7 @@ fun randomBucketLevelMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -162,7 +162,7 @@ fun randomBucketLevelMonitor( dataSources: DataSources ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf(), dataSources = dataSources @@ -181,7 +181,7 @@ fun randomClusterMetricsMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.CLUSTER_METRICS_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.CLUSTER_METRICS_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -200,7 +200,7 @@ fun randomDocumentLevelMonitor( owner: String? = null ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf(), owner = owner ) @@ -220,7 +220,7 @@ fun randomDocumentLevelMonitor( owner: String? = null ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf(), dataSources = dataSources, owner = owner ) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/TriggerServiceTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/TriggerServiceTests.kt index 6076ebac6..89e62756b 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/TriggerServiceTests.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/TriggerServiceTests.kt @@ -7,11 +7,11 @@ package org.opensearch.alerting import org.junit.Before import org.mockito.Mockito -import org.opensearch.alerting.model.BucketLevelTriggerRunResult -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.MonitorRunResult import org.opensearch.alerting.script.BucketLevelTriggerExecutionContext import org.opensearch.common.xcontent.XContentType +import org.opensearch.commons.alerting.model.BucketLevelTriggerRunResult +import org.opensearch.commons.alerting.model.InputRunResults +import org.opensearch.commons.alerting.model.MonitorRunResult import org.opensearch.core.xcontent.DeprecationHandler import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.script.ScriptService diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequestTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequestTests.kt deleted file mode 100644 index 05d59063c..000000000 --- a/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutRequestTests.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.action - -import org.opensearch.alerting.ALWAYS_RUN -import org.opensearch.alerting.model.ActionExecutionTime -import org.opensearch.alerting.model.IndexExecutionContext -import org.opensearch.alerting.model.MonitorMetadata -import org.opensearch.alerting.randomDocumentLevelMonitor -import org.opensearch.alerting.randomDocumentLevelTrigger -import org.opensearch.alerting.workflow.WorkflowRunContext -import org.opensearch.common.io.stream.BytesStreamOutput -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.Workflow -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.core.index.shard.ShardId -import org.opensearch.index.seqno.SequenceNumbers -import org.opensearch.test.OpenSearchTestCase -import java.time.Instant -import java.time.temporal.ChronoUnit -import java.util.UUID - -class DocLevelMonitorFanOutRequestTests : OpenSearchTestCase() { - - fun `test doc level monitor fan out request as stream`() { - val docQuery = DocLevelQuery(query = "test_field:\"us-west-2\"", fields = listOf(), name = "3") - val docLevelInput = DocLevelMonitorInput("description", listOf("test-index"), listOf(docQuery)) - - val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN) - val monitor = randomDocumentLevelMonitor( - inputs = listOf(docLevelInput), - triggers = listOf(trigger), - enabled = true, - schedule = IntervalSchedule(1, ChronoUnit.MINUTES) - ) - val monitorMetadata = MonitorMetadata( - "test", - SequenceNumbers.UNASSIGNED_SEQ_NO, - SequenceNumbers.UNASSIGNED_PRIMARY_TERM, - Monitor.NO_ID, - listOf(ActionExecutionTime("", Instant.now())), - mutableMapOf("index" to mutableMapOf("1" to "1")), - mutableMapOf("test-index" to ".opensearch-sap-test_windows-queries-000001") - ) - val indexExecutionContext = IndexExecutionContext( - listOf(docQuery), - mutableMapOf("index" to mutableMapOf("1" to "1")), - mutableMapOf("index" to mutableMapOf("1" to "1")), - "test-index", - "test-index", - listOf("test-index"), - listOf("test-index"), - listOf("test-field"), - listOf("1", "2") - ) - val workflowRunContext = WorkflowRunContext( - Workflow.NO_ID, - Workflow.NO_ID, - Monitor.NO_ID, - mutableMapOf("index" to listOf("1")), - true - ) - val docLevelMonitorFanOutRequest = DocLevelMonitorFanOutRequest( - monitor, - false, - monitorMetadata, - UUID.randomUUID().toString(), - indexExecutionContext, - listOf(ShardId("test-index", UUID.randomUUID().toString(), 0)), - listOf("test-index"), - workflowRunContext - ) - val out = BytesStreamOutput() - docLevelMonitorFanOutRequest.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newDocLevelMonitorFanOutRequest = DocLevelMonitorFanOutRequest(sin) - assertEquals(docLevelMonitorFanOutRequest.monitor, newDocLevelMonitorFanOutRequest.monitor) - assertEquals(docLevelMonitorFanOutRequest.executionId, newDocLevelMonitorFanOutRequest.executionId) - assertEquals(docLevelMonitorFanOutRequest.monitorMetadata, newDocLevelMonitorFanOutRequest.monitorMetadata) - assertEquals(docLevelMonitorFanOutRequest.indexExecutionContext, newDocLevelMonitorFanOutRequest.indexExecutionContext) - assertEquals(docLevelMonitorFanOutRequest.shardIds, newDocLevelMonitorFanOutRequest.shardIds) - assertEquals(docLevelMonitorFanOutRequest.workflowRunContext, newDocLevelMonitorFanOutRequest.workflowRunContext) - } -} diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponseTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponseTests.kt deleted file mode 100644 index c3b1d38a3..000000000 --- a/alerting/src/test/kotlin/org/opensearch/alerting/action/DocLevelMonitorFanOutResponseTests.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.alerting.action - -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.randomDocumentLevelTriggerRunResult -import org.opensearch.common.io.stream.BytesStreamOutput -import org.opensearch.core.common.io.stream.StreamInput -import org.opensearch.test.OpenSearchTestCase - -class DocLevelMonitorFanOutResponseTests : OpenSearchTestCase() { - fun `test doc level monitor fan out response with errors as stream`() { - val docLevelMonitorFanOutResponse = DocLevelMonitorFanOutResponse( - "nodeid", - "eid", - "monitorId", - mutableMapOf("index" to mutableMapOf("1" to "1")), - InputRunResults(error = null), - mapOf("1" to randomDocumentLevelTriggerRunResult(), "2" to randomDocumentLevelTriggerRunResult()) - ) - val out = BytesStreamOutput() - docLevelMonitorFanOutResponse.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newDocLevelMonitorFanOutResponse = DocLevelMonitorFanOutResponse(sin) - assertEquals(docLevelMonitorFanOutResponse.nodeId, newDocLevelMonitorFanOutResponse.nodeId) - assertEquals(docLevelMonitorFanOutResponse.executionId, newDocLevelMonitorFanOutResponse.executionId) - assertEquals(docLevelMonitorFanOutResponse.monitorId, newDocLevelMonitorFanOutResponse.monitorId) - assertEquals(docLevelMonitorFanOutResponse.lastRunContexts, newDocLevelMonitorFanOutResponse.lastRunContexts) - assertEquals(docLevelMonitorFanOutResponse.inputResults, newDocLevelMonitorFanOutResponse.inputResults) - assertEquals(docLevelMonitorFanOutResponse.triggerResults, newDocLevelMonitorFanOutResponse.triggerResults) - } - - fun `test doc level monitor fan out response as stream`() { - val workflow = DocLevelMonitorFanOutResponse( - "nodeid", - "eid", - "monitorId", - mapOf("index" to mapOf("1" to "1")) as MutableMap, - InputRunResults(), - mapOf("1" to randomDocumentLevelTriggerRunResult(), "2" to randomDocumentLevelTriggerRunResult()) - ) - val out = BytesStreamOutput() - workflow.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newWorkflow = DocLevelMonitorFanOutResponse(sin) - assertEquals(workflow.nodeId, newWorkflow.nodeId) - assertEquals(workflow.executionId, newWorkflow.executionId) - assertEquals(workflow.monitorId, newWorkflow.monitorId) - assertEquals(workflow.lastRunContexts, newWorkflow.lastRunContexts) - assertEquals(workflow.inputResults, newWorkflow.inputResults) - assertEquals(workflow.triggerResults, newWorkflow.triggerResults) - } -} diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/model/WriteableTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/model/WriteableTests.kt index c1d3ac304..d30864ec0 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/model/WriteableTests.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/model/WriteableTests.kt @@ -5,102 +5,18 @@ package org.opensearch.alerting.model -import org.junit.Assert import org.opensearch.alerting.model.destination.email.EmailAccount import org.opensearch.alerting.model.destination.email.EmailGroup -import org.opensearch.alerting.randomBucketLevelMonitorRunResult -import org.opensearch.alerting.randomBucketLevelTriggerRunResult -import org.opensearch.alerting.randomDocumentLevelMonitorRunResult import org.opensearch.alerting.randomEmailAccount import org.opensearch.alerting.randomEmailGroup -import org.opensearch.alerting.randomInputRunResults -import org.opensearch.alerting.randomQueryLevelMonitorRunResult -import org.opensearch.alerting.randomQueryLevelTriggerRunResult -import org.opensearch.common.UUIDs import org.opensearch.common.io.stream.BytesStreamOutput import org.opensearch.commons.alerting.model.SearchInput import org.opensearch.core.common.io.stream.StreamInput import org.opensearch.search.builder.SearchSourceBuilder import org.opensearch.test.OpenSearchTestCase -import java.time.Instant class WriteableTests : OpenSearchTestCase() { - fun `test actionrunresult as stream`() { - val actionRunResult = randomActionRunResult() - val out = BytesStreamOutput() - actionRunResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newActionRunResult = ActionRunResult(sin) - assertEquals("Round tripping ActionRunResult doesn't work", actionRunResult, newActionRunResult) - } - - fun `test query-level triggerrunresult as stream`() { - val runResult = randomQueryLevelTriggerRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = QueryLevelTriggerRunResult(sin) - assertEquals(runResult.triggerName, newRunResult.triggerName) - assertEquals(runResult.triggered, newRunResult.triggered) - assertEquals(runResult.error, newRunResult.error) - assertEquals(runResult.actionResults, newRunResult.actionResults) - } - - fun `test bucket-level triggerrunresult as stream`() { - val runResult = randomBucketLevelTriggerRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = BucketLevelTriggerRunResult(sin) - assertEquals("Round tripping ActionRunResult doesn't work", runResult, newRunResult) - } - - fun `test doc-level triggerrunresult as stream`() { - val runResult = randomDocumentLevelTriggerRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = DocumentLevelTriggerRunResult(sin) - assertEquals("Round tripping ActionRunResult doesn't work", runResult, newRunResult) - } - - fun `test inputrunresult as stream`() { - val runResult = randomInputRunResults() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = InputRunResults.readFrom(sin) - assertEquals("Round tripping InputRunResults doesn't work", runResult, newRunResult) - } - - fun `test query-level monitorrunresult as stream`() { - val runResult = randomQueryLevelMonitorRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = MonitorRunResult(sin) - assertEquals("Round tripping MonitorRunResult doesn't work", runResult, newRunResult) - } - - fun `test bucket-level monitorrunresult as stream`() { - val runResult = randomBucketLevelMonitorRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = MonitorRunResult(sin) - assertEquals("Round tripping MonitorRunResult doesn't work", runResult, newRunResult) - } - - fun `test doc-level monitorrunresult as stream`() { - val runResult = randomDocumentLevelMonitorRunResult() - val out = BytesStreamOutput() - runResult.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newRunResult = MonitorRunResult(sin) - assertEquals("Round tripping MonitorRunResult doesn't work", runResult, newRunResult) - } - fun `test searchinput as stream`() { val input = SearchInput(emptyList(), SearchSourceBuilder()) val out = BytesStreamOutput() @@ -127,35 +43,4 @@ class WriteableTests : OpenSearchTestCase() { val newEmailGroup = EmailGroup.readFrom(sin) assertEquals("Round tripping EmailGroup doesn't work", emailGroup, newEmailGroup) } - - fun `test DocumentLevelTriggerRunResult as stream`() { - val workflow = randomDocumentLevelTriggerRunResult() - val out = BytesStreamOutput() - workflow.writeTo(out) - val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) - val newWorkflow = DocumentLevelTriggerRunResult(sin) - Assert.assertEquals("Round tripping dltrr failed", newWorkflow, workflow) - } - - fun randomDocumentLevelTriggerRunResult(): DocumentLevelTriggerRunResult { - val map = mutableMapOf() - map.plus(Pair("key1", randomActionRunResult())) - map.plus(Pair("key2", randomActionRunResult())) - return DocumentLevelTriggerRunResult( - "trigger-name", - mutableListOf(UUIDs.randomBase64UUID().toString()), - null, - mutableMapOf(Pair("alertId", map)) - ) - } - - fun randomActionRunResult(): ActionRunResult { - val map = mutableMapOf() - map.plus(Pair("key1", "val1")) - map.plus(Pair("key2", "val2")) - return ActionRunResult( - "1234", "test-action", map, - false, Instant.now(), null - ) - } } diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/model/XContentTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/model/XContentTests.kt index 7d07af331..56fbd9866 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/model/XContentTests.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/model/XContentTests.kt @@ -14,7 +14,6 @@ import org.opensearch.alerting.randomAlert import org.opensearch.alerting.randomEmailAccount import org.opensearch.alerting.randomEmailGroup import org.opensearch.alerting.toJsonString -import org.opensearch.common.xcontent.XContentFactory import org.opensearch.commons.alerting.model.ActionExecutionResult import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.util.string @@ -78,17 +77,4 @@ class XContentTests : OpenSearchTestCase() { val parsedEmailGroup = EmailGroup.parse(parser(emailGroupString)) assertEquals("Round tripping EmailGroup doesn't work", emailGroup, parsedEmailGroup) } - - fun `test MonitorMetadata`() { - val monitorMetadata = MonitorMetadata( - id = "monitorId-metadata", - monitorId = "monitorId", - lastActionExecutionTimes = emptyList(), - lastRunContext = emptyMap(), - sourceToQueryIndexMapping = mutableMapOf() - ) - val monitorMetadataString = monitorMetadata.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS).string() - val parsedMonitorMetadata = MonitorMetadata.parse(parser(monitorMetadataString)) - assertEquals("Round tripping MonitorMetadata doesn't work", monitorMetadata, parsedMonitorMetadata) - } } diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/transport/AlertingSingleNodeTestCase.kt b/alerting/src/test/kotlin/org/opensearch/alerting/transport/AlertingSingleNodeTestCase.kt index f1f8882f7..91df57a13 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/transport/AlertingSingleNodeTestCase.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/transport/AlertingSingleNodeTestCase.kt @@ -23,8 +23,6 @@ import org.opensearch.alerting.action.ExecuteWorkflowAction import org.opensearch.alerting.action.ExecuteWorkflowRequest import org.opensearch.alerting.action.ExecuteWorkflowResponse import org.opensearch.alerting.alerts.AlertIndices -import org.opensearch.alerting.model.MonitorMetadata -import org.opensearch.alerting.model.WorkflowMetadata import org.opensearch.common.settings.Settings import org.opensearch.common.unit.TimeValue import org.opensearch.common.xcontent.XContentFactory @@ -47,9 +45,11 @@ import org.opensearch.commons.alerting.action.IndexWorkflowResponse import org.opensearch.commons.alerting.model.Alert import org.opensearch.commons.alerting.model.Finding import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata import org.opensearch.commons.alerting.model.ScheduledJob import org.opensearch.commons.alerting.model.Table import org.opensearch.commons.alerting.model.Workflow +import org.opensearch.commons.alerting.model.WorkflowMetadata import org.opensearch.core.xcontent.XContentBuilder import org.opensearch.core.xcontent.XContentParser import org.opensearch.index.IndexService diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/util/AggregationQueryRewriterTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/util/AggregationQueryRewriterTests.kt index f596f1b92..856dab160 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/util/AggregationQueryRewriterTests.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/util/AggregationQueryRewriterTests.kt @@ -7,15 +7,15 @@ package org.opensearch.alerting.util import org.junit.Assert import org.opensearch.action.search.SearchResponse -import org.opensearch.alerting.model.InputRunResults -import org.opensearch.alerting.model.TriggerAfterKey import org.opensearch.alerting.randomBucketLevelTrigger import org.opensearch.alerting.randomBucketSelectorExtAggregationBuilder import org.opensearch.alerting.randomQueryLevelTrigger import org.opensearch.cluster.ClusterModule import org.opensearch.common.CheckedFunction import org.opensearch.common.xcontent.json.JsonXContent +import org.opensearch.commons.alerting.model.InputRunResults import org.opensearch.commons.alerting.model.Trigger +import org.opensearch.commons.alerting.model.TriggerAfterKey import org.opensearch.core.ParseField import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.XContentParser diff --git a/build.gradle b/build.gradle index bdd8e4cdc..2025b6a0c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { apply from: 'build-tools/repositories.gradle' ext { - opensearch_version = System.getProperty("opensearch.version", "2.15.0-SNAPSHOT") + opensearch_version = System.getProperty("opensearch.version", "2.16.0-SNAPSHOT") buildVersionQualifier = System.getProperty("build.version_qualifier", "") isSnapshot = "true" == System.getProperty("build.snapshot", "true") // 2.7.0-SNAPSHOT -> 2.7.0.0-SNAPSHOT diff --git a/core/build.gradle b/core/build.gradle index 0eed85e3d..46b429c5a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -14,10 +14,6 @@ dependencies { api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin_version}" implementation "com.cronutils:cron-utils:9.1.7" api "org.opensearch.client:opensearch-rest-client:${opensearch_version}" - implementation('com.google.googlejavaformat:google-java-format:1.10.0') { - exclude group: 'com.google.guava' - } - implementation 'com.google.guava:guava:32.0.1-jre' api "org.opensearch:common-utils:${common_utils_version}@jar" implementation 'commons-validator:commons-validator:1.7' diff --git a/sample-remote-monitor-plugin/build.gradle b/sample-remote-monitor-plugin/build.gradle new file mode 100644 index 000000000..e1d38dbea --- /dev/null +++ b/sample-remote-monitor-plugin/build.gradle @@ -0,0 +1,160 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'opensearch.opensearchplugin' +apply plugin: 'opensearch.testclusters' +apply plugin: 'opensearch.java-rest-test' + +import org.opensearch.gradle.test.RestIntegTestTask +import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask +import org.apache.tools.ant.taskdefs.condition.Os + +import java.util.concurrent.Callable + + +opensearchplugin { + name 'sample-remote-monitor-plugin' + description 'Sample plugin that extends OpenSearch Alerting plugin' + classname 'org.opensearch.alerting.SampleRemoteMonitorPlugin' + extendedPlugins = ['opensearch-alerting'] +} + +ext { + projectSubstitutions = [:] + licenseFile = rootProject.file('LICENSE.txt') + noticeFile = rootProject.file('NOTICE.txt') +} + +repositories { + mavenLocal() + mavenCentral() + maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" } +} + +configurations { + zipArchive +} + +dependencies { + compileOnly project(path: ":alerting-spi", configuration: 'shadow') + compileOnly "org.opensearch:common-utils:${common_utils_version}@jar" + compileOnly "com.cronutils:cron-utils:9.1.7" + compileOnly "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" + compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1' + compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin_version}" +} + +def es_tmp_dir = rootProject.file('build/private/es_tmp').absoluteFile +es_tmp_dir.mkdirs() + +File repo = file("$buildDir/testclusters/repo") +def _numNodes = findProperty('numNodes') as Integer ?: 1 + +licenseHeaders.enabled = true +validateNebulaPom.enabled = false +testingConventions.enabled = false +loggerUsageCheck.enabled = false + +javaRestTest.dependsOn(rootProject.assemble) +javaRestTest { + systemProperty 'tests.security.manager', 'false' +} +testClusters.javaRestTest { + testDistribution = 'INTEG_TEST' +} + +task integTest(type: RestIntegTestTask) { + description = "Run tests against a cluster" + testClassesDirs = sourceSets.test.output.classesDirs + classpath = sourceSets.test.runtimeClasspath +} +tasks.named("check").configure { dependsOn(integTest) } + +integTest { + if (project.hasProperty('excludeTests')) { + project.properties['excludeTests']?.replaceAll('\\s', '')?.split('[,;]')?.each { + exclude "${it}" + } + } + systemProperty 'tests.security.manager', 'false' + systemProperty 'java.io.tmpdir', es_tmp_dir.absolutePath + + systemProperty "https", System.getProperty("https") + systemProperty "user", System.getProperty("user") + systemProperty "password", System.getProperty("password") + // Tell the test JVM if the cluster JVM is running under a debugger so that tests can use longer timeouts for + // requests. The 'doFirst' delays reading the debug setting on the cluster till execution time. + doFirst { + // Tell the test JVM if the cluster JVM is running under a debugger so that tests can + // use longer timeouts for requests. + def isDebuggingCluster = getDebug() || System.getProperty("test.debug") != null + systemProperty 'cluster.debug', isDebuggingCluster + // Set number of nodes system property to be used in tests + systemProperty 'cluster.number_of_nodes', "${_numNodes}" + // There seems to be an issue when running multi node run or integ tasks with unicast_hosts + // not being written, the waitForAllConditions ensures it's written + getClusters().forEach { cluster -> + cluster.waitForAllConditions() + } + } + + // The -Dcluster.debug option makes the cluster debuggable; this makes the tests debuggable + if (System.getProperty("test.debug") != null) { + jvmArgs '-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=8000' + } +} +project.getTasks().getByName('bundlePlugin').dependsOn(rootProject.project(":alerting").tasks.getByName('assemble')) +Zip bundle = (Zip) project.getTasks().getByName("bundlePlugin"); +Zip rootBundle = (Zip) rootProject.project(":alerting").getTasks().getByName("bundlePlugin"); +integTest.dependsOn(bundle) +integTest.getClusters().forEach{c -> { + c.plugin(rootProject.project(":alerting").getObjects().fileProperty().value(rootBundle.getArchiveFile())) + c.plugin(project.getObjects().fileProperty().value(bundle.getArchiveFile())) +}} + +testClusters.integTest { + testDistribution = 'ARCHIVE' + + // Cluster shrink exception thrown if we try to set numberOfNodes to 1, so only apply if > 1 + if (_numNodes > 1) numberOfNodes = _numNodes + // When running integration tests it doesn't forward the --debug-jvm to the cluster anymore + // i.e. we have to use a custom property to flag when we want to debug OpenSearch JVM + // since we also support multi node integration tests we increase debugPort per node + if (System.getProperty("cluster.debug") != null) { + def debugPort = 5005 + nodes.forEach { node -> + node.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=*:${debugPort}") + debugPort += 1 + } + } + setting 'path.repo', repo.absolutePath +} + +run { + doFirst { + // There seems to be an issue when running multi node run or integ tasks with unicast_hosts + // not being written, the waitForAllConditions ensures it's written + getClusters().forEach { cluster -> + cluster.waitForAllConditions() + } + } + useCluster testClusters.integTest +} + +// As of ES 7.7 the sample-extension-plugin is being added to the list of plugins for the testCluster during build before +// the job-scheduler plugin is causing build failures. +// The job-scheduler zip is added explicitly above but the sample-extension-plugin is added implicitly at some time during evaluation. +// Will need to do a deep dive to find out exactly what task adds the sample-extension-plugin and add job-scheduler there but a temporary hack is to +// reorder the plugins list after evaluation but prior to task execution when the plugins are installed. +afterEvaluate { + testClusters.javaRestTest.nodes.each { node -> + def nodePlugins = node.plugins + def firstPlugin = nodePlugins.get(0) + if (firstPlugin.provider == project.bundlePlugin.archiveFile) { + nodePlugins.remove(0) + nodePlugins.add(firstPlugin) + } + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorPlugin.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorPlugin.java new file mode 100644 index 000000000..b73d440f6 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorPlugin.java @@ -0,0 +1,104 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionRequest; +import org.opensearch.alerting.monitor.fanouts.TransportRemoteDocLevelMonitorFanOutAction; +import org.opensearch.alerting.monitor.runners.SampleRemoteDocLevelMonitorRunner; +import org.opensearch.alerting.monitor.runners.SampleRemoteMonitorRunner1; +import org.opensearch.alerting.monitor.runners.SampleRemoteMonitorRunner2; +import org.opensearch.alerting.spi.RemoteMonitorRunner; +import org.opensearch.alerting.spi.RemoteMonitorRunnerExtension; +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.IndexScopedSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.env.NodeEnvironment; +import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestHandler; +import org.opensearch.script.ScriptService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.watcher.ResourceWatcherService; +import reactor.util.annotation.NonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class SampleRemoteMonitorPlugin extends Plugin implements ActionPlugin, RemoteMonitorRunnerExtension { + + public static final String SAMPLE_REMOTE_MONITOR1 = "sample_remote_monitor1"; + + public static final String SAMPLE_REMOTE_MONITOR2 = "sample_remote_monitor2"; + + public static final String SAMPLE_REMOTE_DOC_LEVEL_MONITOR = "remote_doc_level_monitor"; + + private static final Logger log = LogManager.getLogger(SampleRemoteMonitorPlugin.class); + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + SampleRemoteMonitorRunner1 monitorRunner1 = SampleRemoteMonitorRunner1.getMonitorRunner(); + monitorRunner1.setClient(client); + SampleRemoteMonitorRunner2 monitorRunner2 = SampleRemoteMonitorRunner2.getMonitorRunner(); + monitorRunner2.setClient(client); + return Collections.emptyList(); + } + + @NonNull + @Override + public Map getMonitorTypesToMonitorRunners() { + return Map.of(SAMPLE_REMOTE_MONITOR1, SampleRemoteMonitorRunner1.getMonitorRunner(), + SAMPLE_REMOTE_MONITOR2, SampleRemoteMonitorRunner2.getMonitorRunner(), + SAMPLE_REMOTE_DOC_LEVEL_MONITOR, SampleRemoteDocLevelMonitorRunner.getMonitorRunner()); + } + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + return Collections.singletonList(new SampleRemoteMonitorRestHandler()); + } + + @Override + public List> getActions() { + return List.of( + new ActionPlugin.ActionHandler<>(SampleRemoteDocLevelMonitorRunner.REMOTE_DOC_LEVEL_MONITOR_ACTION_INSTANCE, TransportRemoteDocLevelMonitorFanOutAction.class) + ); + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorRestHandler.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorRestHandler.java new file mode 100644 index 000000000..0340baa6b --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/SampleRemoteMonitorRestHandler.java @@ -0,0 +1,278 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting; + +import org.opensearch.action.support.WriteRequest; +import org.opensearch.alerting.monitor.inputs.SampleRemoteDocLevelMonitorInput; +import org.opensearch.alerting.monitor.inputs.SampleRemoteMonitorInput1; +import org.opensearch.alerting.monitor.inputs.SampleRemoteMonitorInput2; +import org.opensearch.alerting.monitor.triggers.SampleRemoteMonitorTrigger1; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.commons.alerting.action.AlertingActions; +import org.opensearch.commons.alerting.action.IndexMonitorRequest; +import org.opensearch.commons.alerting.action.IndexMonitorResponse; +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.action.Action; +import org.opensearch.commons.alerting.model.action.Throttle; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorInput; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorTrigger; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.index.seqno.SequenceNumbers; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.RestResponse; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptType; + +import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyList; + +public class SampleRemoteMonitorRestHandler extends BaseRestHandler { + + @Override + public String getName() { + return "sample-remote-monitor-rest-handler"; + } + + @Override + public List routes() { + return Collections.unmodifiableList( + Arrays.asList(new Route(RestRequest.Method.POST, "_plugins/_sample_remote_monitor/monitor")) + ); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException { + String runMonitorParam = restRequest.param("run_monitor"); + + SampleRemoteMonitorInput1 input1 = new SampleRemoteMonitorInput1("hello", Map.of("test", 1.0f), 1); + BytesStreamOutput out = new BytesStreamOutput(); + input1.writeTo(out); + BytesReference input1Serialized = out.bytes(); + + SampleRemoteMonitorTrigger1 trigger1 = new SampleRemoteMonitorTrigger1("hello", Map.of("test", 1.0f), 1); + BytesStreamOutput outTrigger = new BytesStreamOutput(); + trigger1.writeTo(outTrigger); + BytesReference trigger1Serialized = outTrigger.bytes(); + + Monitor monitor1 = new Monitor( + Monitor.NO_ID, + Monitor.NO_VERSION, + "sample_remote_monitor", + true, + new IntervalSchedule(5, ChronoUnit.MINUTES, null), + Instant.now(), + Instant.now(), + SampleRemoteMonitorPlugin.SAMPLE_REMOTE_MONITOR1, + null, + 0, + List.of(new RemoteMonitorInput(input1Serialized)), + List.of(new RemoteMonitorTrigger("id", "name", "1", + List.of(new Action("name", "destinationId", new Script(ScriptType.INLINE, Script.DEFAULT_TEMPLATE_LANG, "Hello World", Map.of()), + new Script(ScriptType.INLINE, Script.DEFAULT_TEMPLATE_LANG, "Hello World", Map.of()), false, new Throttle(60, ChronoUnit.MINUTES), + "id", null)), trigger1Serialized)), + Map.of(), + new DataSources(), + "sample-remote-monitor-plugin" + ); + IndexMonitorRequest indexMonitorRequest1 = new IndexMonitorRequest( + Monitor.NO_ID, + SequenceNumbers.UNASSIGNED_SEQ_NO, + SequenceNumbers.UNASSIGNED_PRIMARY_TERM, + WriteRequest.RefreshPolicy.IMMEDIATE, + RestRequest.Method.POST, + monitor1, + null + ); + + if (runMonitorParam.equals("single")) { + return restChannel -> { + client.doExecute( + AlertingActions.INDEX_MONITOR_ACTION_TYPE, + indexMonitorRequest1, + new ActionListener<>() { + @Override + public void onResponse(IndexMonitorResponse indexMonitorResponse) { + try { + RestResponse restResponse = new BytesRestResponse( + RestStatus.OK, + indexMonitorResponse.toXContent(JsonXContent.contentBuilder(), ToXContent.EMPTY_PARAMS) + ); + restChannel.sendResponse(restResponse); + } catch (IOException e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + + @Override + public void onFailure(Exception e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + ); + }; + } else if (runMonitorParam.equals("multiple")) { + SampleRemoteMonitorInput2 input2 = new SampleRemoteMonitorInput2("hello", + new DocLevelMonitorInput("test", List.of("test"), List.of(new DocLevelQuery("query", "query", List.of(), "test:1", List.of())))); + BytesStreamOutput out1 = new BytesStreamOutput(); + input2.writeTo(out1); + BytesReference input1Serialized1 = out1.bytes(); + + Monitor monitor2 = new Monitor( + Monitor.NO_ID, + Monitor.NO_VERSION, + "sample_remote_monitor", + true, + new IntervalSchedule(5, ChronoUnit.MINUTES, null), + Instant.now(), + Instant.now(), + SampleRemoteMonitorPlugin.SAMPLE_REMOTE_MONITOR2, + null, + 0, + List.of(new RemoteMonitorInput(input1Serialized1)), + List.of(), + Map.of(), + new DataSources(), + "sample-remote-monitor-plugin" + ); + IndexMonitorRequest indexMonitorRequest2 = new IndexMonitorRequest( + Monitor.NO_ID, + SequenceNumbers.UNASSIGNED_SEQ_NO, + SequenceNumbers.UNASSIGNED_PRIMARY_TERM, + WriteRequest.RefreshPolicy.IMMEDIATE, + RestRequest.Method.POST, + monitor2, + null + ); + + return restChannel -> { + client.doExecute( + AlertingActions.INDEX_MONITOR_ACTION_TYPE, + indexMonitorRequest1, + new ActionListener<>() { + @Override + public void onResponse(IndexMonitorResponse indexMonitorResponse) { + String firstMonitorId = indexMonitorResponse.getId(); + client.doExecute( + AlertingActions.INDEX_MONITOR_ACTION_TYPE, + indexMonitorRequest2, + new ActionListener<>() { + @Override + public void onResponse(IndexMonitorResponse indexMonitorResponse) { + try { + indexMonitorResponse.setId(indexMonitorResponse.getId() + " " + firstMonitorId); + RestResponse restResponse = new BytesRestResponse( + RestStatus.OK, + indexMonitorResponse.toXContent(JsonXContent.contentBuilder(), ToXContent.EMPTY_PARAMS) + ); + restChannel.sendResponse(restResponse); + } catch (IOException e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + + @Override + public void onFailure(Exception e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + ); + } + + @Override + public void onFailure(Exception e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + ); + }; + } else { + String indices = restRequest.param("index", "index"); + List index = List.of(indices.split(",")); + SampleRemoteDocLevelMonitorInput sampleRemoteDocLevelMonitorInput = + new SampleRemoteDocLevelMonitorInput("hello", Map.of("world", 1), 2); + BytesStreamOutput out2 = new BytesStreamOutput(); + sampleRemoteDocLevelMonitorInput.writeTo(out2); + BytesReference sampleRemoteDocLevelMonitorInputSerialized = out2.bytes(); + + DocLevelMonitorInput docLevelMonitorInput = new DocLevelMonitorInput("description", index, emptyList()); + RemoteDocLevelMonitorInput remoteDocLevelMonitorInput = new RemoteDocLevelMonitorInput(sampleRemoteDocLevelMonitorInputSerialized, docLevelMonitorInput); + + Monitor remoteDocLevelMonitor = new Monitor( + Monitor.NO_ID, + Monitor.NO_VERSION, + SampleRemoteMonitorPlugin.SAMPLE_REMOTE_DOC_LEVEL_MONITOR, + true, + new IntervalSchedule(5, ChronoUnit.MINUTES, null), + Instant.now(), + Instant.now(), + SampleRemoteMonitorPlugin.SAMPLE_REMOTE_DOC_LEVEL_MONITOR, + null, + 0, + List.of(remoteDocLevelMonitorInput), + List.of(new RemoteMonitorTrigger("id", "name", "1", + List.of(new Action("name", "destinationId", new Script(ScriptType.INLINE, Script.DEFAULT_TEMPLATE_LANG, "Hello World", Map.of()), + new Script(ScriptType.INLINE, Script.DEFAULT_TEMPLATE_LANG, "Hello World", Map.of()), false, new Throttle(60, ChronoUnit.MINUTES), + "id", null)), trigger1Serialized)), + Map.of(), + new DataSources(), + "sample-remote-monitor-plugin" + ); + IndexMonitorRequest indexDocLevelMonitorRequest = new IndexMonitorRequest( + Monitor.NO_ID, + SequenceNumbers.UNASSIGNED_SEQ_NO, + SequenceNumbers.UNASSIGNED_PRIMARY_TERM, + WriteRequest.RefreshPolicy.IMMEDIATE, + RestRequest.Method.POST, + remoteDocLevelMonitor, + null + ); + return restChannel -> { + client.doExecute( + AlertingActions.INDEX_MONITOR_ACTION_TYPE, + indexDocLevelMonitorRequest, + new ActionListener<>() { + @Override + public void onResponse(IndexMonitorResponse indexMonitorResponse) { + try { + RestResponse restResponse = new BytesRestResponse( + RestStatus.OK, + indexMonitorResponse.toXContent(JsonXContent.contentBuilder(), ToXContent.EMPTY_PARAMS) + ); + restChannel.sendResponse(restResponse); + } catch (IOException e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + + @Override + public void onFailure(Exception e) { + restChannel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + ); + }; + } + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/fanouts/TransportRemoteDocLevelMonitorFanOutAction.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/fanouts/TransportRemoteDocLevelMonitorFanOutAction.java new file mode 100644 index 000000000..1be9255fe --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/fanouts/TransportRemoteDocLevelMonitorFanOutAction.java @@ -0,0 +1,118 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.fanouts; + +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.alerting.monitor.inputs.SampleRemoteDocLevelMonitorInput; +import org.opensearch.alerting.monitor.runners.SampleRemoteDocLevelMonitorRunner; +import org.opensearch.alerting.monitor.triggers.SampleRemoteMonitorTrigger1; +import org.opensearch.client.Client; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.Settings; +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutRequest; +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse; +import org.opensearch.commons.alerting.model.DocLevelMonitorInput; +import org.opensearch.commons.alerting.model.InputRunResults; +import org.opensearch.commons.alerting.model.Monitor; +import org.opensearch.commons.alerting.model.Trigger; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteDocLevelMonitorInput; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorTrigger; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import java.util.HashMap; +import java.util.Map; + +public class TransportRemoteDocLevelMonitorFanOutAction extends HandledTransportAction { + + private final ClusterService clusterService; + + private final Settings settings; + + private final Client client; + + private final NamedXContentRegistry xContentRegistry; + + @Inject + public TransportRemoteDocLevelMonitorFanOutAction( + TransportService transportService, + Client client, + NamedXContentRegistry xContentRegistry, + ClusterService clusterService, + Settings settings, + ActionFilters actionFilters + ) { + super(SampleRemoteDocLevelMonitorRunner.REMOTE_DOC_LEVEL_MONITOR_ACTION_NAME, transportService, actionFilters, DocLevelMonitorFanOutRequest::new); + this.clusterService = clusterService; + this.client = client; + this.xContentRegistry = xContentRegistry; + this.settings = settings; + } + + @Override + protected void doExecute(Task task, DocLevelMonitorFanOutRequest request, ActionListener actionListener) { + try { + Monitor monitor = request.getMonitor(); + Map lastRunContext = request.getMonitorMetadata().getLastRunContext(); + + RemoteDocLevelMonitorInput input = (RemoteDocLevelMonitorInput) monitor.getInputs().get(0); + BytesReference customInputSerialized = input.getInput(); + StreamInput sin = StreamInput.wrap(customInputSerialized.toBytesRef().bytes); + SampleRemoteDocLevelMonitorInput sampleRemoteDocLevelMonitorInput = new SampleRemoteDocLevelMonitorInput(sin); + DocLevelMonitorInput docLevelMonitorInput = input.getDocLevelMonitorInput(); + String index = docLevelMonitorInput.getIndices().get(0); + + BytesReference customTriggerSerialized = null; + Trigger trigger = monitor.getTriggers().get(0); + if (trigger instanceof RemoteMonitorTrigger) { + customTriggerSerialized = ((RemoteMonitorTrigger) trigger).getTrigger(); + } + StreamInput triggerSin = StreamInput.wrap(customTriggerSerialized.toBytesRef().bytes); + SampleRemoteMonitorTrigger1 remoteMonitorTrigger = new SampleRemoteMonitorTrigger1(triggerSin); + + + if (lastRunContext.containsKey(index)) { + ((Map) lastRunContext.get(index)).put("2", 0); + } + if (docLevelMonitorInput.getIndices().size() > 1 && lastRunContext.containsKey(docLevelMonitorInput.getIndices().get(1))) { + ((Map) lastRunContext.get(docLevelMonitorInput.getIndices().get(1))).put("4", 0); + } + IndexRequest indexRequest = new IndexRequest(SampleRemoteDocLevelMonitorRunner.SAMPLE_REMOTE_DOC_LEVEL_MONITOR_RUNNER_INDEX) + .source(Map.of(sampleRemoteDocLevelMonitorInput.getA(), remoteMonitorTrigger.getA())).setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); + this.client.index(indexRequest, new ActionListener<>() { + @Override + public void onResponse(IndexResponse indexResponse) { + DocLevelMonitorFanOutResponse response = new DocLevelMonitorFanOutResponse( + clusterService.localNode().getId(), + request.getExecutionId(), + monitor.getId(), + lastRunContext, + new InputRunResults(), + new HashMap<>(), + null + ); + actionListener.onResponse(response); + } + + @Override + public void onFailure(Exception e) { + actionListener.onFailure(e); + } + }); + } catch (Exception ex) { + actionListener.onFailure(ex); + } + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteDocLevelMonitorInput.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteDocLevelMonitorInput.java new file mode 100644 index 000000000..ecd3234ff --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteDocLevelMonitorInput.java @@ -0,0 +1,55 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.inputs; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Map; + +public class SampleRemoteDocLevelMonitorInput implements Writeable { + + private String a; + + private Map b; + + private int c; + + public SampleRemoteDocLevelMonitorInput(String a, Map b, int c) { + this.a = a; + this.b = b; + this.c = c; + } + + public SampleRemoteDocLevelMonitorInput(StreamInput sin) throws IOException { + this( + sin.readString(), + sin.readMap(), + sin.readInt() + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(a); + out.writeMap(b); + out.writeInt(c); + } + + public int getC() { + return c; + } + + public Map getB() { + return b; + } + + public String getA() { + return a; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput1.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput1.java new file mode 100644 index 000000000..fe27fdfe7 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput1.java @@ -0,0 +1,55 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.inputs; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Map; + +public class SampleRemoteMonitorInput1 implements Writeable { + + private String a; + + private Map b; + + private int c; + + public SampleRemoteMonitorInput1(String a, Map b, int c) { + this.a = a; + this.b = b; + this.c = c; + } + + public SampleRemoteMonitorInput1(StreamInput sin) throws IOException { + this( + sin.readString(), + sin.readMap(), + sin.readInt() + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(a); + out.writeMap(b); + out.writeInt(c); + } + + public int getC() { + return c; + } + + public Map getB() { + return b; + } + + public String getA() { + return a; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput2.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput2.java new file mode 100644 index 000000000..5f35f9a5b --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/inputs/SampleRemoteMonitorInput2.java @@ -0,0 +1,46 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.inputs; + +import org.opensearch.commons.alerting.model.DocLevelMonitorInput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; + +public class SampleRemoteMonitorInput2 implements Writeable { + + private String a; + + private DocLevelMonitorInput b; + + public SampleRemoteMonitorInput2(String a, DocLevelMonitorInput b) { + this.a = a; + this.b = b; + } + + public SampleRemoteMonitorInput2(StreamInput sin) throws IOException { + this( + sin.readString(), + DocLevelMonitorInput.readFrom(sin) + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(a); + b.writeTo(out); + } + + public String getA() { + return a; + } + + public DocLevelMonitorInput getB() { + return b; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteDocLevelMonitorRunner.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteDocLevelMonitorRunner.java new file mode 100644 index 000000000..66b5b0329 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteDocLevelMonitorRunner.java @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.runners; + +import org.opensearch.action.ActionType; +import org.opensearch.alerting.spi.RemoteMonitorRunner; +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse; + +public class SampleRemoteDocLevelMonitorRunner extends RemoteMonitorRunner { + + public static final String REMOTE_DOC_LEVEL_MONITOR_ACTION_NAME = "cluster:admin/opensearch/alerting/remote_monitor/fanout"; + + public static final String SAMPLE_REMOTE_DOC_LEVEL_MONITOR_RUNNER_INDEX = ".opensearch-alerting-sample-remote-doc-level-monitor"; + + public static final ActionType REMOTE_DOC_LEVEL_MONITOR_ACTION_INSTANCE = new ActionType<>(REMOTE_DOC_LEVEL_MONITOR_ACTION_NAME, + DocLevelMonitorFanOutResponse::new); + + private static SampleRemoteDocLevelMonitorRunner INSTANCE; + + public static SampleRemoteDocLevelMonitorRunner getMonitorRunner() { + if (INSTANCE != null) { + return INSTANCE; + } + synchronized (SampleRemoteDocLevelMonitorRunner.class) { + if (INSTANCE != null) { + return INSTANCE; + } + INSTANCE = new SampleRemoteDocLevelMonitorRunner(); + return INSTANCE; + } + } + + @Override + public String getFanOutAction() { + return REMOTE_DOC_LEVEL_MONITOR_ACTION_NAME; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner1.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner1.java new file mode 100644 index 000000000..f2e67f460 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner1.java @@ -0,0 +1,109 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.runners; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.alerting.monitor.inputs.SampleRemoteMonitorInput1; +import org.opensearch.alerting.monitor.trigger.results.SampleRemoteMonitorTriggerRunResult; +import org.opensearch.alerting.monitor.triggers.SampleRemoteMonitorTrigger1; +import org.opensearch.alerting.spi.RemoteMonitorRunner; +import org.opensearch.client.Client; +import org.opensearch.commons.alerting.model.Input; +import org.opensearch.commons.alerting.model.InputRunResults; +import org.opensearch.commons.alerting.model.Monitor; +import org.opensearch.commons.alerting.model.MonitorRunResult; +import org.opensearch.commons.alerting.model.Trigger; +import org.opensearch.commons.alerting.model.TriggerRunResult; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorInput; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorTrigger; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.transport.TransportService; + +import java.time.Instant; +import java.util.Map; + +public class SampleRemoteMonitorRunner1 extends RemoteMonitorRunner { + + public static final String SAMPLE_MONITOR_RUNNER1_INDEX = ".opensearch-alerting-sample-remote-monitor1"; + + private static final Logger log = LogManager.getLogger(SampleRemoteMonitorRunner1.class); + + private static SampleRemoteMonitorRunner1 INSTANCE; + + private Client client; + + public static SampleRemoteMonitorRunner1 getMonitorRunner() { + if (INSTANCE != null) { + return INSTANCE; + } + synchronized (SampleRemoteMonitorRunner1.class) { + if (INSTANCE != null) { + return INSTANCE; + } + INSTANCE = new SampleRemoteMonitorRunner1(); + return INSTANCE; + } + } + + public void setClient(Client client) { + this.client = client; + } + + @Override + public MonitorRunResult runMonitor( + Monitor monitor, + Instant periodStart, + Instant periodEnd, + boolean dryrun, + String executionId, + TransportService transportService + ) { + try { + BytesReference customInputSerialized = null; + Input input = monitor.getInputs().get(0); + if (input instanceof RemoteMonitorInput) { + customInputSerialized = ((RemoteMonitorInput) input).getInput(); + } + StreamInput sin = StreamInput.wrap(customInputSerialized.toBytesRef().bytes); + SampleRemoteMonitorInput1 remoteMonitorInput = new SampleRemoteMonitorInput1(sin); + + BytesReference customTriggerSerialized = null; + Trigger trigger = monitor.getTriggers().get(0); + if (trigger instanceof RemoteMonitorTrigger) { + customTriggerSerialized = ((RemoteMonitorTrigger) trigger).getTrigger(); + } + StreamInput triggerSin = StreamInput.wrap(customTriggerSerialized.toBytesRef().bytes); + SampleRemoteMonitorTrigger1 remoteMonitorTrigger = new SampleRemoteMonitorTrigger1(triggerSin); + + IndexRequest indexRequest = new IndexRequest(SAMPLE_MONITOR_RUNNER1_INDEX) + .source(Map.of(remoteMonitorInput.getA(), remoteMonitorTrigger.getC())).setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); + this.client.index(indexRequest); + + return new MonitorRunResult<>( + monitor.getName(), + periodStart, + periodEnd, + null, + new InputRunResults(), + Map.of("test-trigger", new SampleRemoteMonitorTriggerRunResult("test-trigger", null, Map.of())) + ); + } catch (Exception ex) { + return new MonitorRunResult<>( + monitor.getName(), + periodStart, + periodEnd, + ex, + new InputRunResults(), + Map.of("test-trigger", new SampleRemoteMonitorTriggerRunResult("test-trigger", ex, Map.of())) + ); + } + + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner2.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner2.java new file mode 100644 index 000000000..31f732b51 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/runners/SampleRemoteMonitorRunner2.java @@ -0,0 +1,98 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.runners; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.alerting.monitor.inputs.SampleRemoteMonitorInput2; +import org.opensearch.alerting.monitor.trigger.results.SampleRemoteMonitorTriggerRunResult; +import org.opensearch.alerting.spi.RemoteMonitorRunner; +import org.opensearch.client.Client; +import org.opensearch.commons.alerting.model.Input; +import org.opensearch.commons.alerting.model.InputRunResults; +import org.opensearch.commons.alerting.model.Monitor; +import org.opensearch.commons.alerting.model.MonitorRunResult; +import org.opensearch.commons.alerting.model.TriggerRunResult; +import org.opensearch.commons.alerting.model.remote.monitors.RemoteMonitorInput; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.transport.TransportService; + +import java.time.Instant; +import java.util.Map; + +public class SampleRemoteMonitorRunner2 extends RemoteMonitorRunner { + + public static final String SAMPLE_MONITOR_RUNNER2_INDEX = ".opensearch-alerting-sample-remote-monitor2"; + + private static final Logger log = LogManager.getLogger(SampleRemoteMonitorRunner2.class); + + private static SampleRemoteMonitorRunner2 INSTANCE; + + private Client client; + + public static SampleRemoteMonitorRunner2 getMonitorRunner() { + if (INSTANCE != null) { + return INSTANCE; + } + synchronized (SampleRemoteMonitorRunner2.class) { + if (INSTANCE != null) { + return INSTANCE; + } + INSTANCE = new SampleRemoteMonitorRunner2(); + return INSTANCE; + } + } + + public void setClient(Client client) { + this.client = client; + } + + @Override + public MonitorRunResult runMonitor( + Monitor monitor, + Instant periodStart, + Instant periodEnd, + boolean dryrun, + String executionId, + TransportService transportService + ) { + try { + BytesReference customInputSerialized = null; + Input input = monitor.getInputs().get(0); + if (input instanceof RemoteMonitorInput) { + customInputSerialized = ((RemoteMonitorInput) input).getInput(); + } + StreamInput sin = StreamInput.wrap(customInputSerialized.toBytesRef().bytes); + SampleRemoteMonitorInput2 remoteMonitorInput = new SampleRemoteMonitorInput2(sin); + + IndexRequest indexRequest = new IndexRequest(SAMPLE_MONITOR_RUNNER2_INDEX) + .source(Map.of(remoteMonitorInput.getB().name(), remoteMonitorInput.getB().getQueries().get(0).getQuery())) + .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); + this.client.index(indexRequest); + + return new MonitorRunResult<>( + monitor.getName(), + periodStart, + periodEnd, + null, + new InputRunResults(), + Map.of("test-trigger", new SampleRemoteMonitorTriggerRunResult("test-trigger", null, Map.of())) + ); + } catch (Exception ex) { + return new MonitorRunResult<>( + monitor.getName(), + periodStart, + periodEnd, + ex, + new InputRunResults(), + Map.of("test-trigger", new SampleRemoteMonitorTriggerRunResult("test-trigger", ex, Map.of())) + ); + } + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/trigger/results/SampleRemoteMonitorTriggerRunResult.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/trigger/results/SampleRemoteMonitorTriggerRunResult.java new file mode 100644 index 000000000..80b0914bf --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/trigger/results/SampleRemoteMonitorTriggerRunResult.java @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.trigger.results; + +import org.opensearch.commons.alerting.model.ActionRunResult; +import org.opensearch.commons.alerting.model.TriggerRunResult; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.script.ScriptException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class SampleRemoteMonitorTriggerRunResult extends TriggerRunResult { + + private Map> actionResultsMap; + + public SampleRemoteMonitorTriggerRunResult(String triggerName, + Exception error, + Map> actionResultsMap) { + super(triggerName, error); + this.actionResultsMap = actionResultsMap; + } + + public SampleRemoteMonitorTriggerRunResult readFrom(StreamInput sin) throws IOException { + return new SampleRemoteMonitorTriggerRunResult( + sin.readString(), + sin.readException(), + readActionResults(sin) + ); + } + + @Override + public XContentBuilder internalXContent(XContentBuilder xContentBuilder, Params params) { + try { + if (this.getError() instanceof ScriptException) { + this.setError(new Exception(((ScriptException) getError()).toJsonString(), getError())); + } + return xContentBuilder.field("action_results", actionResultsMap); + } catch (IOException ex) { + return xContentBuilder; + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeInt(actionResultsMap.size()); + for (var actionResultsEntry: actionResultsMap.entrySet()) { + String alert = actionResultsEntry.getKey(); + Map actionResults = actionResultsEntry.getValue(); + out.writeString(alert); + out.writeInt(actionResults.size()); + for (var actionResult: actionResults.entrySet()) { + String id = actionResult.getKey(); + ActionRunResult result = actionResult.getValue(); + out.writeString(id); + result.writeTo(out); + } + } + } + + private Map> readActionResults(StreamInput sin) throws IOException { + Map> actionResultsMapReconstruct = new HashMap<>(); + int size = sin.readInt(); + int idx = 0; + while (idx < size) { + String alert = sin.readString(); + int actionResultsSize = sin.readInt(); + Map actionRunResultElem = new HashMap<>(); + int i = 0; + while (i < actionResultsSize) { + String actionId = sin.readString(); + ActionRunResult actionResult = ActionRunResult.readFrom(sin); + actionRunResultElem.put(actionId, actionResult); + ++i; + } + actionResultsMapReconstruct.put(alert, actionRunResultElem); + ++idx; + } + return actionResultsMapReconstruct; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/triggers/SampleRemoteMonitorTrigger1.java b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/triggers/SampleRemoteMonitorTrigger1.java new file mode 100644 index 000000000..1f04e4a4e --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/java/org/opensearch/alerting/monitor/triggers/SampleRemoteMonitorTrigger1.java @@ -0,0 +1,55 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.monitor.triggers; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Map; + +public class SampleRemoteMonitorTrigger1 implements Writeable { + + private String a; + + private Map b; + + private int c; + + public SampleRemoteMonitorTrigger1(String a, Map b, int c) { + this.a = a; + this.b = b; + this.c = c; + } + + public SampleRemoteMonitorTrigger1(StreamInput sin) throws IOException { + this( + sin.readString(), + sin.readMap(), + sin.readInt() + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(a); + out.writeMap(b); + out.writeInt(c); + } + + public int getC() { + return c; + } + + public Map getB() { + return b; + } + + public String getA() { + return a; + } +} \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/main/resources/META-INF/services/org.opensearch.alerting.spi.RemoteMonitorRunnerExtension b/sample-remote-monitor-plugin/src/main/resources/META-INF/services/org.opensearch.alerting.spi.RemoteMonitorRunnerExtension new file mode 100644 index 000000000..16ebe5897 --- /dev/null +++ b/sample-remote-monitor-plugin/src/main/resources/META-INF/services/org.opensearch.alerting.spi.RemoteMonitorRunnerExtension @@ -0,0 +1,6 @@ +# +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 +# + +org.opensearch.alerting.SampleRemoteMonitorPlugin \ No newline at end of file diff --git a/sample-remote-monitor-plugin/src/test/java/org/opensearch/alerting/SampleRemoteMonitorIT.java b/sample-remote-monitor-plugin/src/test/java/org/opensearch/alerting/SampleRemoteMonitorIT.java new file mode 100644 index 000000000..e81d017cd --- /dev/null +++ b/sample-remote-monitor-plugin/src/test/java/org/opensearch/alerting/SampleRemoteMonitorIT.java @@ -0,0 +1,342 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.junit.AfterClass; +import org.junit.Assert; +import org.opensearch.alerting.monitor.runners.SampleRemoteDocLevelMonitorRunner; +import org.opensearch.alerting.monitor.runners.SampleRemoteMonitorRunner1; +import org.opensearch.alerting.monitor.runners.SampleRemoteMonitorRunner2; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.WarningsHandler; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.Strings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +import javax.management.MBeanServerInvocationHandler; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class SampleRemoteMonitorIT extends OpenSearchRestTestCase { + + @SuppressWarnings("unchecked") + public void testSingleSampleMonitor() throws IOException, InterruptedException { + Response response = makeRequest(client(), "POST", "_plugins/_sample_remote_monitor/monitor", Map.of("run_monitor", "single"), null); + Assert.assertEquals("Unable to create remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + Map responseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).map(); + String monitorId = responseJson.get("_id").toString(); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + AtomicBoolean found = new AtomicBoolean(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteMonitorRunner1.SAMPLE_MONITOR_RUNNER1_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) == 1 && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).containsKey("hello") && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).get("hello").toString().equals("1")); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + } + + @SuppressWarnings("unchecked") + public void testMultipleSampleMonitors() throws IOException, InterruptedException { + Response response = makeRequest(client(), "POST", "_plugins/_sample_remote_monitor/monitor", Map.of("run_monitor", "multiple"), null); + Assert.assertEquals("Unable to create remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + Map responseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).map(); + String monitorIds = responseJson.get("_id").toString(); + String firstMonitorId = monitorIds.split(" ")[0]; + String secondMonitorId = monitorIds.split(" ")[1]; + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + firstMonitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + secondMonitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + AtomicBoolean found = new AtomicBoolean(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteMonitorRunner1.SAMPLE_MONITOR_RUNNER1_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) == 1); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + + found.set(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteMonitorRunner2.SAMPLE_MONITOR_RUNNER2_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) == 1 && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).containsKey("doc_level_input") && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).get("doc_level_input").toString().equals("test:1")); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + } + + @SuppressWarnings("unchecked") + public void testSampleRemoteDocLevelMonitor() throws IOException, InterruptedException { + createIndex("index", Settings.builder().put("number_of_shards", "7").build()); + Response response = makeRequest(client(), "POST", "_plugins/_sample_remote_monitor/monitor", Map.of("run_monitor", "doc_level"), null); + Assert.assertEquals("Unable to create remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + Map responseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).map(); + String monitorId = responseJson.get("_id").toString(); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + AtomicBoolean found = new AtomicBoolean(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteDocLevelMonitorRunner.SAMPLE_REMOTE_DOC_LEVEL_MONITOR_RUNNER_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) > 0 && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).containsKey("hello") && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).get("hello").toString().equals("hello")); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + } + + @SuppressWarnings("unchecked") + public void testSampleRemoteDocLevelMonitorWithDynamicMetadataUpdate() throws IOException, InterruptedException { + createIndex("index1", Settings.builder().put("number_of_shards", "7").build()); + Response response = makeRequest(client(), "POST", "_plugins/_sample_remote_monitor/monitor", + Map.of("run_monitor", "doc_level", "index", "index1"), null); + Assert.assertEquals("Unable to create remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + Map responseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).map(); + String monitorId = responseJson.get("_id").toString(); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + createIndex("index2", Settings.builder().put("number_of_shards", "7").build()); + String updatedMonitor = String.format(Locale.ROOT, "{\"type\":\"monitor\",\"name\":\"remote_doc_level_monitor\",\"monitor_type\":\"remote_doc_level_monitor\"," + + "\"user\":{\"name\":\"\",\"backend_roles\":[],\"roles\":[],\"custom_attribute_names\":[],\"user_requested_tenant\":null},\"enabled\":true,\"schedule\":{\"period\":{\"interval\":5,\"unit\":\"MINUTES\"}}," + + "\"inputs\":[{\"remote_doc_level_monitor_input\":{\"size\":24,\"input\":\"BWhlbGxvCgEFd29ybGQBAAAAAQAAAAIA\",\"doc_level_input\":" + + "{\"doc_level_input\":{\"description\":\"description\",\"indices\":[%s],\"queries\":[]}}}}],\"" + + "triggers\":[{\"remote_monitor_trigger\":{\"id\":\"id\",\"name\":\"name\",\"severity\":\"1\"," + + "\"actions\":[{\"id\":\"id\",\"name\":\"name\",\"destination_id\":\"destinationId\",\"message_template\":{\"source\":\"Hello World\"," + + "\"lang\":\"mustache\"},\"throttle_enabled\":false,\"subject_template\":{\"source\":\"Hello World\",\"lang\":\"mustache\"}," + + "\"throttle\":{\"value\":60,\"unit\":\"MINUTES\"}}],\"size\":24,\"trigger\":\"BWhlbGxvCgEEdGVzdAM/gAAAAAAAAQAA\"}}]" + + "}", "\"index1\", \"index2\""); + makeRequest(client(), "PUT", "/_plugins/_alerting/monitors/" + monitorId, Map.of(), new StringEntity(updatedMonitor, ContentType.APPLICATION_JSON)); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + makeRequest(client(), "DELETE", "/index1", Map.of(), null); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + AtomicBoolean found = new AtomicBoolean(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteDocLevelMonitorRunner.SAMPLE_REMOTE_DOC_LEVEL_MONITOR_RUNNER_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) > 0 && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).containsKey("hello") && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).get("hello").toString().equals("hello")); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + } + + @SuppressWarnings("unchecked") + public void testSampleRemoteDocLevelMonitorWithAlias() throws IOException, InterruptedException { + String indexAlias = "test_alias"; + createIndex("index-000001", Settings.EMPTY); + makeRequest(client(), "POST", "_aliases", Map.of(), + new StringEntity(String.format(Locale.ROOT, "{\"actions\":[{\"add\":{\"index\":\"index-000001\",\"alias\":\"%s\",\"is_write_index\":true}}]}", indexAlias), ContentType.APPLICATION_JSON)); + Response response = makeRequest(client(), "POST", "_plugins/_sample_remote_monitor/monitor", Map.of("run_monitor", "doc_level", "index", indexAlias), null); + Assert.assertEquals("Unable to create remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + Map responseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + response.getEntity().getContent() + ).map(); + String monitorId = responseJson.get("_id").toString(); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + makeRequest(client(), "POST", String.format(Locale.ROOT, "%s/_rollover", indexAlias), Map.of(), + new StringEntity("", ContentType.APPLICATION_JSON)); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + + response = makeRequest(client(), "POST", "/_plugins/_alerting/monitors/" + monitorId + "/_execute", Map.of(), null); + Assert.assertEquals("Unable to execute remote monitor", RestStatus.OK, RestStatus.fromCode(response.getStatusLine().getStatusCode())); + AtomicBoolean found = new AtomicBoolean(false); + OpenSearchRestTestCase.waitUntil( + () -> { + try { + Response searchResponse = makeRequest(client(), "POST", SampleRemoteDocLevelMonitorRunner.SAMPLE_REMOTE_DOC_LEVEL_MONITOR_RUNNER_INDEX + "/_search", Map.of(), + new StringEntity("{\"query\":{\"match_all\":{}}}", ContentType.APPLICATION_JSON)); + Map searchResponseJson = JsonXContent.jsonXContent.createParser( + NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, + searchResponse.getEntity().getContent() + ).map(); + found.set(Integer.parseInt((((Map) ((Map) searchResponseJson.get("hits")).get("total")).get("value")).toString()) > 0 && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).containsKey("hello") && + ((Map) ((List>) ((Map) searchResponseJson.get("hits")).get("hits")).get(0).get("_source")).get("hello").toString().equals("hello")); + return found.get(); + } catch (IOException ex) { + return false; + } + }, 10, TimeUnit.SECONDS); + Assert.assertTrue(found.get()); + } + + protected Response makeRequest( + RestClient client, + String method, + String endpoint, + Map params, + HttpEntity entity, + Header... headers + ) throws IOException { + Request request = new Request(method, endpoint); + RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder(); + options.setWarningsHandler(WarningsHandler.PERMISSIVE); + + for (Header header : headers) { + options.addHeader(header.getName(), header.getValue()); + } + request.setOptions(options.build()); + request.addParameters(params); + if (entity != null) { + request.setEntity(entity); + } + return client.performRequest(request); + } + + @AfterClass + public static void dumpCoverage() throws IOException, MalformedObjectNameException { + // jacoco.dir is set in esplugin-coverage.gradle, if it doesn't exist we don't + // want to collect coverage so we can return early + String jacocoBuildPath = System.getProperty("jacoco.dir"); + if (Strings.isNullOrEmpty(jacocoBuildPath)) { + return; + } + + String serverUrl = "service:jmx:rmi:///jndi/rmi://127.0.0.1:7777/jmxrmi"; + try (JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(serverUrl))) { + IProxy proxy = MBeanServerInvocationHandler.newProxyInstance( + connector.getMBeanServerConnection(), new ObjectName("org.jacoco:type=Runtime"), IProxy.class, + false); + + Path path = org.opensearch.common.io.PathUtils.get(jacocoBuildPath + "/integTestRunner.exec"); + Files.write(path, proxy.getExecutionData(false)); + } catch (Exception ex) { + throw new RuntimeException("Failed to dump coverage: " + ex); + } + } + + public interface IProxy { + byte[] getExecutionData(boolean reset); + + void dump(boolean reset); + + void reset(); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bd847942f..40e295126 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,5 +6,11 @@ rootProject.name = 'opensearch-alerting' include 'alerting' include 'core' +include 'spi' project(":core").name = 'alerting-core' +project(":spi").name = 'alerting-spi' + +include 'sample-remote-monitor-plugin' +project(":sample-remote-monitor-plugin").name = "alerting-sample-remote-monitor-plugin" +startParameter.excludedTaskNames=["publishPluginZipPublicationToMavenLocal", "publishPluginZipPublicationToZipStagingRepository"] diff --git a/spi/build.gradle b/spi/build.gradle new file mode 100644 index 000000000..5a586bd48 --- /dev/null +++ b/spi/build.gradle @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +plugins { + id 'java-library' + id 'maven-publish' +} + +apply plugin: 'maven-publish' +apply plugin: 'java' +apply plugin: 'opensearch.java-rest-test' +apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'jacoco' +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'opensearch.repositories' + +dependencies { + compileOnly "org.opensearch:opensearch:${opensearch_version}" + compileOnly "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" + compileOnly "org.opensearch:common-utils:${common_utils_version}@jar" +} + +shadowJar { + archiveClassifier = null +} + +task sourcesJar(type: Jar) { + archiveClassifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + archiveClassifier = 'javadoc' + from javadoc.destinationDir +} + +publishing { + repositories { + maven { + name = 'staging' + url = "${rootProject.buildDir}/local-staging-repo" + } + maven { + name = "Snapshots" + url = "https://aws.oss.sonatype.org/content/repositories/snapshots" + credentials { + username "$System.env.SONATYPE_USERNAME" + password "$System.env.SONATYPE_PASSWORD" + } + } + } + publications { + shadow(MavenPublication) { + project.shadow.component(it) + groupId = 'org.opensearch.alerting' + artifactId = 'alerting-spi' + + artifact sourcesJar + artifact javadocJar + + pom { + name = "OpenSearch Alerting spi" + packaging = "jar" + url = "https://github.com/opensearch-project/alerting" + description = "OpenSearch Alerting spi" + scm { + connection = "scm:git@github.com:opensearch-project/alerting.git" + developerConnection = "scm:git@github.com:opensearch-project/alerting.git" + url = "git@github.com:opensearch-project/alerting.git" + } + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "OpenSearch" + url = "https://github.com/opensearch-project/alerting" + } + } + } + } + } + + gradle.startParameter.setShowStacktrace(ShowStacktrace.ALWAYS) + gradle.startParameter.setLogLevel(LogLevel.DEBUG) +} diff --git a/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunner.kt b/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunner.kt new file mode 100644 index 000000000..a2ceac7ba --- /dev/null +++ b/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunner.kt @@ -0,0 +1,188 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.spi + +import org.opensearch.action.ActionListenerResponseHandler +import org.opensearch.action.support.GroupedActionListener +import org.opensearch.cluster.node.DiscoveryNode +import org.opensearch.cluster.service.ClusterService +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutAction +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutRequest +import org.opensearch.commons.alerting.action.DocLevelMonitorFanOutResponse +import org.opensearch.commons.alerting.model.IndexExecutionContext +import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.model.MonitorMetadata +import org.opensearch.commons.alerting.model.MonitorRunResult +import org.opensearch.commons.alerting.model.TriggerRunResult +import org.opensearch.commons.alerting.model.WorkflowRunContext +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.core.action.ActionListener +import org.opensearch.core.common.breaker.CircuitBreakingException +import org.opensearch.core.common.io.stream.Writeable +import org.opensearch.core.index.shard.ShardId +import org.opensearch.node.NodeClosedException +import org.opensearch.transport.ActionNotFoundTransportException +import org.opensearch.transport.ConnectTransportException +import org.opensearch.transport.RemoteTransportException +import org.opensearch.transport.TransportException +import org.opensearch.transport.TransportRequestOptions +import org.opensearch.transport.TransportService +import java.time.Instant +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +open class RemoteMonitorRunner { + + open fun runMonitor( + monitor: Monitor, + periodStart: Instant, + periodEnd: Instant, + dryrun: Boolean, + executionId: String, + transportService: TransportService + ): MonitorRunResult { + return MonitorRunResult(monitor.name, periodStart, periodEnd) + } + + open fun getFanOutAction(): String { + return DocLevelMonitorFanOutAction.NAME + } + + open suspend fun doFanOut( + clusterService: ClusterService, + monitor: Monitor, + monitorMetadata: MonitorMetadata, + executionId: String, + concreteIndices: List, + workflowRunContext: WorkflowRunContext?, + dryrun: Boolean, + transportService: TransportService, + nodeMap: Map, + nodeShardAssignments: Map> + ): MutableList { + return suspendCoroutine { cont -> + val listener = GroupedActionListener( + object : ActionListener> { + override fun onResponse(response: Collection) { + cont.resume(response.toMutableList()) + } + + override fun onFailure(e: Exception) { + if (e.cause is Exception) + cont.resumeWithException(e.cause as Exception) + else + cont.resumeWithException(e) + } + }, + nodeShardAssignments.size + ) + val responseReader = Writeable.Reader { + DocLevelMonitorFanOutResponse(it) + } + for (node in nodeMap) { + if (nodeShardAssignments.containsKey(node.key)) { + val docLevelMonitorFanOutRequest = DocLevelMonitorFanOutRequest( + monitor, + dryrun, + monitorMetadata, + executionId, + indexExecutionContext = IndexExecutionContext( + listOf(), + mutableMapOf(), + mutableMapOf(), + "", + "", + listOf(), + listOf(), + listOf() + ), + nodeShardAssignments[node.key]!!.toList(), + concreteIndices, + workflowRunContext + ) + + transportService.sendRequest( + node.value, + getFanOutAction(), + docLevelMonitorFanOutRequest, + TransportRequestOptions.EMPTY, + object : ActionListenerResponseHandler( + listener, + responseReader + ) { + override fun handleException(e: TransportException) { + val cause = e.unwrapCause() + if (cause is ConnectTransportException || + ( + e is RemoteTransportException && + ( + cause is NodeClosedException || + cause is CircuitBreakingException || + cause is ActionNotFoundTransportException + ) + ) + ) { + val localNode = clusterService.localNode() + // retry in local node + transportService.sendRequest( + localNode, + DocLevelMonitorFanOutAction.NAME, + docLevelMonitorFanOutRequest, + TransportRequestOptions.EMPTY, + object : + ActionListenerResponseHandler( + listener, + responseReader + ) { + override fun handleException(e: TransportException) { + listener.onResponse( + DocLevelMonitorFanOutResponse( + "", + "", + "", + mutableMapOf(), + exception = if (e.cause is AlertingException) { + e.cause as AlertingException + } else { + AlertingException.wrap(e) as AlertingException + } + ) + ) + } + + override fun handleResponse(response: DocLevelMonitorFanOutResponse) { + listener.onResponse(response) + } + } + ) + } else { + listener.onResponse( + DocLevelMonitorFanOutResponse( + "", + "", + "", + mutableMapOf(), + exception = if (e.cause is AlertingException) { + e.cause as AlertingException + } else { + AlertingException.wrap(e) as AlertingException + } + ) + ) + } + } + + override fun handleResponse(response: DocLevelMonitorFanOutResponse) { + listener.onResponse(response) + } + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunnerExtension.kt b/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunnerExtension.kt new file mode 100644 index 000000000..eb07278da --- /dev/null +++ b/spi/src/main/kotlin/org/opensearch/alerting/spi/RemoteMonitorRunnerExtension.kt @@ -0,0 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.spi + +interface RemoteMonitorRunnerExtension { + + fun getMonitorTypesToMonitorRunners(): Map +} \ No newline at end of file