From 58de5a9bc9f0c8d5a0a5f14b78671651a8165f39 Mon Sep 17 00:00:00 2001 From: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:38:05 -0800 Subject: [PATCH 1/3] feat: add an api endpoint to toggle the state of a monitor Signed-off-by: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> --- .../org/opensearch/alerting/AlertingPlugin.kt | 6 +- .../RestUpdateMonitorStateAction.kt | 104 ++++++++++++++ .../TransportUpdateMonitorStateAction.kt | 131 ++++++++++++++++++ 3 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt create mode 100644 alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt index 1f8a4d514..a759b40e3 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt @@ -51,6 +51,7 @@ import org.opensearch.alerting.resthandler.RestSearchAlertingCommentAction import org.opensearch.alerting.resthandler.RestSearchEmailAccountAction import org.opensearch.alerting.resthandler.RestSearchEmailGroupAction import org.opensearch.alerting.resthandler.RestSearchMonitorAction +import org.opensearch.alerting.resthandler.RestUpdateMonitorStateAction import org.opensearch.alerting.script.TriggerScript import org.opensearch.alerting.service.DeleteMonitorService import org.opensearch.alerting.settings.AlertingSettings @@ -83,6 +84,7 @@ import org.opensearch.alerting.transport.TransportSearchAlertingCommentAction import org.opensearch.alerting.transport.TransportSearchEmailAccountAction import org.opensearch.alerting.transport.TransportSearchEmailGroupAction import org.opensearch.alerting.transport.TransportSearchMonitorAction +import org.opensearch.alerting.transport.TransportUpdateMonitorStateAction import org.opensearch.alerting.util.DocLevelMonitorQueries import org.opensearch.alerting.util.destinationmigration.DestinationMigrationCoordinator import org.opensearch.client.Client @@ -217,6 +219,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R RestIndexAlertingCommentAction(), RestSearchAlertingCommentAction(), RestDeleteAlertingCommentAction(), + RestUpdateMonitorStateAction(), ) } @@ -248,7 +251,8 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R ActionPlugin.ActionHandler(AlertingActions.DELETE_COMMENT_ACTION_TYPE, TransportDeleteAlertingCommentAction::class.java), ActionPlugin.ActionHandler(ExecuteWorkflowAction.INSTANCE, TransportExecuteWorkflowAction::class.java), ActionPlugin.ActionHandler(GetRemoteIndexesAction.INSTANCE, TransportGetRemoteIndexesAction::class.java), - ActionPlugin.ActionHandler(DocLevelMonitorFanOutAction.INSTANCE, TransportDocLevelMonitorFanOutAction::class.java) + ActionPlugin.ActionHandler(DocLevelMonitorFanOutAction.INSTANCE, TransportDocLevelMonitorFanOutAction::class.java), + ActionPlugin.ActionHandler(AlertingActions.UPDATE_MONITOR_STATE_ACTION_TYPE, TransportUpdateMonitorStateAction::class.java) ) } diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt new file mode 100644 index 000000000..4e8f188fc --- /dev/null +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt @@ -0,0 +1,104 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.resthandler + +import org.apache.logging.log4j.LogManager +import org.opensearch.alerting.AlertingPlugin +import org.opensearch.client.node.NodeClient +import org.opensearch.common.xcontent.XContentType +import org.opensearch.commons.alerting.action.AlertingActions.UPDATE_MONITOR_STATE_ACTION_TYPE +import org.opensearch.commons.alerting.action.UpdateMonitorStateRequest +import org.opensearch.commons.alerting.action.UpdateMonitorStateResponse +import org.opensearch.commons.alerting.model.Monitor +import org.opensearch.commons.alerting.util.AlertingException +import org.opensearch.core.action.ActionListener +import org.opensearch.core.rest.RestStatus +import org.opensearch.core.xcontent.ToXContent +import org.opensearch.core.xcontent.XContentBuilder +import org.opensearch.rest.BaseRestHandler +import org.opensearch.rest.BytesRestResponse +import org.opensearch.rest.RestHandler +import org.opensearch.rest.RestHandler.ReplacedRoute +import org.opensearch.rest.RestHandler.Route +import org.opensearch.rest.RestRequest +import org.opensearch.rest.RestRequest.Method.PUT +import java.io.IOException + +private val log = LogManager.getLogger(RestUpdateMonitorStateAction::class.java) + +// Rest handler to enable and disable monitors. +class RestUpdateMonitorStateAction : BaseRestHandler() { + override fun getName(): String { + return "update_monitor_state_action" + } + + override fun routes(): List { + return listOf() + } + + override fun replacedRoutes(): MutableList { + return mutableListOf( + ReplacedRoute( + PUT, + "${AlertingPlugin.MONITOR_BASE_URI}/{monitorID}/enable", + PUT, + "${AlertingPlugin.LEGACY_OPENDISTRO_MONITOR_BASE_URI}/{monitorID}/enable" + ), + ReplacedRoute( + PUT, + "${AlertingPlugin.MONITOR_BASE_URI}/{monitorID}/disable", + PUT, + "${AlertingPlugin.LEGACY_OPENDISTRO_MONITOR_BASE_URI}/{monitorID}/disable" + ) + ) + } + + @Throws(IOException::class) + override fun prepareRequest(request: RestRequest, client: NodeClient): RestChannelConsumer { + val monitorId = request.param("monitorID", Monitor.NO_ID) + if (request.method() == PUT && Monitor.NO_ID == monitorId) { + throw AlertingException.wrap(IllegalArgumentException("Missing monitor ID")) + } + + // Check if the request is being made to enable the monitor + val enabled = request.path().endsWith("/enable") + + log.debug("{} {}/{}/{}", request.method(), AlertingPlugin.MONITOR_BASE_URI, monitorId, if (enabled) "enable" else "disable") + + return RestChannelConsumer { channel -> + val updateMonitorStateRequest = UpdateMonitorStateRequest( + monitorId = monitorId, + enabled = enabled, + seqNo = -1, // This will be handled in transport layer + primaryTerm = -1, // This will be handled in transport layer + method = request.method() + ) + + client.execute( + UPDATE_MONITOR_STATE_ACTION_TYPE, + updateMonitorStateRequest, + object : ActionListener { + override fun onResponse(response: UpdateMonitorStateResponse) { + channel.sendResponse( + BytesRestResponse( + RestStatus.OK, + response.toXContent( + XContentBuilder.builder( + XContentType.JSON.xContent() + ), + ToXContent.EMPTY_PARAMS + ) + ) + ) + } + override fun onFailure(e: Exception) { + channel.sendResponse(BytesRestResponse(channel, e)) + } + } + ) + } + } +} diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt new file mode 100644 index 000000000..ea7da5117 --- /dev/null +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt @@ -0,0 +1,131 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.alerting.transport + +import org.apache.logging.log4j.LogManager +import org.opensearch.OpenSearchStatusException +import org.opensearch.action.support.ActionFilters +import org.opensearch.action.support.HandledTransportAction +import org.opensearch.action.support.WriteRequest +import org.opensearch.client.node.NodeClient +import org.opensearch.common.inject.Inject +import org.opensearch.commons.alerting.action.AlertingActions +import org.opensearch.commons.alerting.action.AlertingActions.GET_MONITOR_ACTION_TYPE +import org.opensearch.commons.alerting.action.AlertingActions.INDEX_MONITOR_ACTION_TYPE +import org.opensearch.commons.alerting.action.GetMonitorRequest +import org.opensearch.commons.alerting.action.GetMonitorResponse +import org.opensearch.commons.alerting.action.IndexMonitorRequest +import org.opensearch.commons.alerting.action.IndexMonitorResponse +import org.opensearch.commons.alerting.action.UpdateMonitorStateRequest +import org.opensearch.commons.alerting.action.UpdateMonitorStateResponse +import org.opensearch.core.action.ActionListener +import org.opensearch.core.common.io.stream.NamedWriteableRegistry +import org.opensearch.core.rest.RestStatus +import org.opensearch.tasks.Task +import org.opensearch.transport.TransportService +import java.time.Instant + +class TransportUpdateMonitorStateAction @Inject constructor( + transportService: TransportService, + val client: NodeClient, + val namedWriteableRegistry: NamedWriteableRegistry, + actionFilters: ActionFilters +) : HandledTransportAction( + AlertingActions.UPDATE_MONITOR_STATE_ACTION_NAME, + transportService, + actionFilters, + ::UpdateMonitorStateRequest +) { + private val log = LogManager.getLogger(javaClass) + + override fun doExecute( + task: Task, + request: UpdateMonitorStateRequest, + actionListener: ActionListener + ) { + val monitorId = request.monitorId + val enabled = request.enabled + + val getMonitorRequest = GetMonitorRequest( + monitorId = monitorId, + version = -3L, + method = request.method, + srcContext = null + ) + + client.execute( + GET_MONITOR_ACTION_TYPE, + getMonitorRequest, + object : ActionListener { + override fun onResponse(getMonitorResponse: GetMonitorResponse) { + try { + if (getMonitorResponse.monitor == null) { + actionListener.onFailure( + OpenSearchStatusException("Monitor $monitorId not found", RestStatus.NOT_FOUND) + ) + return + } + + if (getMonitorResponse.monitor!!.enabled == enabled) { + actionListener.onFailure( + OpenSearchStatusException( + "Monitor $monitorId is already ${if (enabled) "enabled" else "disabled"}", + RestStatus.BAD_REQUEST + ) + ) + return + } + + // Create updated monitor with new enabled state + val updatedMonitor = getMonitorResponse.monitor!!.copy( + enabled = enabled, + enabledTime = if (enabled) Instant.now() else null + ) + + // Create index monitor request + val indexMonitorRequest = IndexMonitorRequest( + monitorId = monitorId, + seqNo = getMonitorResponse.seqNo, + primaryTerm = getMonitorResponse.primaryTerm, + refreshPolicy = WriteRequest.RefreshPolicy.IMMEDIATE, + method = request.method, + monitor = updatedMonitor, + ) + + // Execute index monitor request + client.execute( + INDEX_MONITOR_ACTION_TYPE, + indexMonitorRequest, + object : ActionListener { + override fun onResponse(indexMonitorResponse: IndexMonitorResponse) { + actionListener.onResponse( + UpdateMonitorStateResponse( + id = monitorId, + version = indexMonitorResponse.version, + seqNo = indexMonitorResponse.seqNo, + primaryTerm = indexMonitorResponse.primaryTerm, + monitor = updatedMonitor + ) + ) + } + + override fun onFailure(e: Exception) { + actionListener.onFailure(e) + } + } + ) + } catch (e: Exception) { + actionListener.onFailure(e) + } + } + + override fun onFailure(e: Exception) { + actionListener.onFailure(e) + } + } + ) + } +} From 314dc06383ef40299c95a0058ce7eba200576f76 Mon Sep 17 00:00:00 2001 From: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:51:34 -0800 Subject: [PATCH 2/3] feat: add an api to toggle monitor's enabled state Signed-off-by: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> --- .../org/opensearch/alerting/AlertingPlugin.kt | 8 +++--- ...teAction.kt => RestToggleMonitorAction.kt} | 27 +++++++++---------- ...ion.kt => TransportToggleMonitorAction.kt} | 25 ++++++++--------- 3 files changed, 28 insertions(+), 32 deletions(-) rename alerting/src/main/kotlin/org/opensearch/alerting/resthandler/{RestUpdateMonitorStateAction.kt => RestToggleMonitorAction.kt} (77%) rename alerting/src/main/kotlin/org/opensearch/alerting/transport/{TransportUpdateMonitorStateAction.kt => TransportToggleMonitorAction.kt} (85%) diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt index a759b40e3..b90c8b1e3 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/AlertingPlugin.kt @@ -51,7 +51,7 @@ import org.opensearch.alerting.resthandler.RestSearchAlertingCommentAction import org.opensearch.alerting.resthandler.RestSearchEmailAccountAction import org.opensearch.alerting.resthandler.RestSearchEmailGroupAction import org.opensearch.alerting.resthandler.RestSearchMonitorAction -import org.opensearch.alerting.resthandler.RestUpdateMonitorStateAction +import org.opensearch.alerting.resthandler.RestToggleMonitorAction import org.opensearch.alerting.script.TriggerScript import org.opensearch.alerting.service.DeleteMonitorService import org.opensearch.alerting.settings.AlertingSettings @@ -84,7 +84,7 @@ import org.opensearch.alerting.transport.TransportSearchAlertingCommentAction import org.opensearch.alerting.transport.TransportSearchEmailAccountAction import org.opensearch.alerting.transport.TransportSearchEmailGroupAction import org.opensearch.alerting.transport.TransportSearchMonitorAction -import org.opensearch.alerting.transport.TransportUpdateMonitorStateAction +import org.opensearch.alerting.transport.TransportToggleMonitorAction import org.opensearch.alerting.util.DocLevelMonitorQueries import org.opensearch.alerting.util.destinationmigration.DestinationMigrationCoordinator import org.opensearch.client.Client @@ -219,7 +219,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R RestIndexAlertingCommentAction(), RestSearchAlertingCommentAction(), RestDeleteAlertingCommentAction(), - RestUpdateMonitorStateAction(), + RestToggleMonitorAction(), ) } @@ -252,7 +252,7 @@ internal class AlertingPlugin : PainlessExtension, ActionPlugin, ScriptPlugin, R ActionPlugin.ActionHandler(ExecuteWorkflowAction.INSTANCE, TransportExecuteWorkflowAction::class.java), ActionPlugin.ActionHandler(GetRemoteIndexesAction.INSTANCE, TransportGetRemoteIndexesAction::class.java), ActionPlugin.ActionHandler(DocLevelMonitorFanOutAction.INSTANCE, TransportDocLevelMonitorFanOutAction::class.java), - ActionPlugin.ActionHandler(AlertingActions.UPDATE_MONITOR_STATE_ACTION_TYPE, TransportUpdateMonitorStateAction::class.java) + ActionPlugin.ActionHandler(AlertingActions.TOGGLE_MONITOR_ACTION_TYPE, TransportToggleMonitorAction::class.java) ) } diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestToggleMonitorAction.kt similarity index 77% rename from alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt rename to alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestToggleMonitorAction.kt index 4e8f188fc..318f1ae3d 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestUpdateMonitorStateAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/resthandler/RestToggleMonitorAction.kt @@ -9,9 +9,9 @@ import org.apache.logging.log4j.LogManager import org.opensearch.alerting.AlertingPlugin import org.opensearch.client.node.NodeClient import org.opensearch.common.xcontent.XContentType -import org.opensearch.commons.alerting.action.AlertingActions.UPDATE_MONITOR_STATE_ACTION_TYPE -import org.opensearch.commons.alerting.action.UpdateMonitorStateRequest -import org.opensearch.commons.alerting.action.UpdateMonitorStateResponse +import org.opensearch.commons.alerting.action.AlertingActions.TOGGLE_MONITOR_ACTION_TYPE +import org.opensearch.commons.alerting.action.ToggleMonitorRequest +import org.opensearch.commons.alerting.action.ToggleMonitorResponse import org.opensearch.commons.alerting.model.Monitor import org.opensearch.commons.alerting.util.AlertingException import org.opensearch.core.action.ActionListener @@ -27,12 +27,11 @@ import org.opensearch.rest.RestRequest import org.opensearch.rest.RestRequest.Method.PUT import java.io.IOException -private val log = LogManager.getLogger(RestUpdateMonitorStateAction::class.java) +private val log = LogManager.getLogger(RestToggleMonitorAction::class.java) -// Rest handler to enable and disable monitors. -class RestUpdateMonitorStateAction : BaseRestHandler() { +class RestToggleMonitorAction : BaseRestHandler() { override fun getName(): String { - return "update_monitor_state_action" + return "toggle_monitor_action" } override fun routes(): List { @@ -69,19 +68,19 @@ class RestUpdateMonitorStateAction : BaseRestHandler() { log.debug("{} {}/{}/{}", request.method(), AlertingPlugin.MONITOR_BASE_URI, monitorId, if (enabled) "enable" else "disable") return RestChannelConsumer { channel -> - val updateMonitorStateRequest = UpdateMonitorStateRequest( + val toggleMonitorRequest = ToggleMonitorRequest( monitorId = monitorId, enabled = enabled, - seqNo = -1, // This will be handled in transport layer - primaryTerm = -1, // This will be handled in transport layer + seqNo = -1, // Updated in the transport layer + primaryTerm = -1, // Updated in the transport layer method = request.method() ) client.execute( - UPDATE_MONITOR_STATE_ACTION_TYPE, - updateMonitorStateRequest, - object : ActionListener { - override fun onResponse(response: UpdateMonitorStateResponse) { + TOGGLE_MONITOR_ACTION_TYPE, + toggleMonitorRequest, + object : ActionListener { + override fun onResponse(response: ToggleMonitorResponse) { channel.sendResponse( BytesRestResponse( RestStatus.OK, diff --git a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportToggleMonitorAction.kt similarity index 85% rename from alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt rename to alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportToggleMonitorAction.kt index ea7da5117..652943df5 100644 --- a/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportUpdateMonitorStateAction.kt +++ b/alerting/src/main/kotlin/org/opensearch/alerting/transport/TransportToggleMonitorAction.kt @@ -5,7 +5,6 @@ package org.opensearch.alerting.transport -import org.apache.logging.log4j.LogManager import org.opensearch.OpenSearchStatusException import org.opensearch.action.support.ActionFilters import org.opensearch.action.support.HandledTransportAction @@ -19,8 +18,8 @@ import org.opensearch.commons.alerting.action.GetMonitorRequest import org.opensearch.commons.alerting.action.GetMonitorResponse import org.opensearch.commons.alerting.action.IndexMonitorRequest import org.opensearch.commons.alerting.action.IndexMonitorResponse -import org.opensearch.commons.alerting.action.UpdateMonitorStateRequest -import org.opensearch.commons.alerting.action.UpdateMonitorStateResponse +import org.opensearch.commons.alerting.action.ToggleMonitorRequest +import org.opensearch.commons.alerting.action.ToggleMonitorResponse import org.opensearch.core.action.ActionListener import org.opensearch.core.common.io.stream.NamedWriteableRegistry import org.opensearch.core.rest.RestStatus @@ -28,23 +27,22 @@ import org.opensearch.tasks.Task import org.opensearch.transport.TransportService import java.time.Instant -class TransportUpdateMonitorStateAction @Inject constructor( +class TransportToggleMonitorAction @Inject constructor( transportService: TransportService, val client: NodeClient, val namedWriteableRegistry: NamedWriteableRegistry, actionFilters: ActionFilters -) : HandledTransportAction( - AlertingActions.UPDATE_MONITOR_STATE_ACTION_NAME, +) : HandledTransportAction( + AlertingActions.TOGGLE_MONITOR_ACTION_NAME, transportService, actionFilters, - ::UpdateMonitorStateRequest + ::ToggleMonitorRequest ) { - private val log = LogManager.getLogger(javaClass) override fun doExecute( task: Task, - request: UpdateMonitorStateRequest, - actionListener: ActionListener + request: ToggleMonitorRequest, + actionListener: ActionListener ) { val monitorId = request.monitorId val enabled = request.enabled @@ -79,13 +77,13 @@ class TransportUpdateMonitorStateAction @Inject constructor( return } - // Create updated monitor with new enabled state + // Create a copy of the monitor with the updated state val updatedMonitor = getMonitorResponse.monitor!!.copy( enabled = enabled, enabledTime = if (enabled) Instant.now() else null ) - // Create index monitor request + // Call indexMonitor API to update the monitor val indexMonitorRequest = IndexMonitorRequest( monitorId = monitorId, seqNo = getMonitorResponse.seqNo, @@ -95,14 +93,13 @@ class TransportUpdateMonitorStateAction @Inject constructor( monitor = updatedMonitor, ) - // Execute index monitor request client.execute( INDEX_MONITOR_ACTION_TYPE, indexMonitorRequest, object : ActionListener { override fun onResponse(indexMonitorResponse: IndexMonitorResponse) { actionListener.onResponse( - UpdateMonitorStateResponse( + ToggleMonitorResponse( id = monitorId, version = indexMonitorResponse.version, seqNo = indexMonitorResponse.seqNo, From a40061d1d632cc904ee0d204dbebb60e3e795918 Mon Sep 17 00:00:00 2001 From: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:15:00 -0800 Subject: [PATCH 3/3] feat: add tests for the toggle monitor api Signed-off-by: vikhy-aws <191836418+vikhy-aws@users.noreply.github.com> --- .../alerting/resthandler/MonitorRestApiIT.kt | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/MonitorRestApiIT.kt b/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/MonitorRestApiIT.kt index 79c871a97..890efb2de 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/MonitorRestApiIT.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/MonitorRestApiIT.kt @@ -11,6 +11,7 @@ import org.apache.hc.core5.http.message.BasicHeader import org.opensearch.alerting.ALERTING_BASE_URI import org.opensearch.alerting.ALWAYS_RUN import org.opensearch.alerting.ANOMALY_DETECTOR_INDEX +import org.opensearch.alerting.AlertingPlugin import org.opensearch.alerting.AlertingRestTestCase import org.opensearch.alerting.LEGACY_OPENDISTRO_ALERTING_BASE_URI import org.opensearch.alerting.alerts.AlertIndices @@ -1332,6 +1333,94 @@ class MonitorRestApiIT : AlertingRestTestCase() { } } + fun `test enable monitor`() { + val monitorId = createMonitor(randomQueryLevelMonitor(enabled = false, enabledTime = null), refresh = true).id + val response = client().makeRequest( + "PUT", + "${AlertingPlugin.MONITOR_BASE_URI}/$monitorId/enable", + emptyMap(), + null + ) + val jsonResponse = createParser(XContentType.JSON.xContent(), response.entity.content).map() + val monitor = jsonResponse["Monitor"] as Map<*, *> + val enabled = monitor["enabled"] + // Monitor should be enabled + assertEquals(true, enabled) + } + + fun `test disable monitor`() { + val monitorId = createMonitor(randomQueryLevelMonitor(enabled = true, enabledTime = Instant.now()), refresh = true).id + val response = client().makeRequest( + "PUT", + "${AlertingPlugin.MONITOR_BASE_URI}/$monitorId/disable", + emptyMap(), + null + ) + val jsonResponse = createParser(XContentType.JSON.xContent(), response.entity.content).map() + val monitor = jsonResponse["Monitor"] as Map<*, *> + val enabled = monitor["enabled"] + val enabledTime = monitor["enabledTime"] + // Monitor should be disabled and the enabledTime should be null + assertEquals(false, enabled) + assertNull(enabledTime) + } + + fun `test enable monitor when already enabled`() { + val monitorId = createMonitor(randomQueryLevelMonitor(enabled = true, enabledTime = Instant.now()), refresh = true).id + val exception = assertThrows(ResponseException::class.java) { + client().makeRequest( + "PUT", + "${AlertingPlugin.MONITOR_BASE_URI}/$monitorId/enable", + emptyMap(), + null + ) + } + val errorResponse = createParser(XContentType.JSON.xContent(), exception.response.entity.content).map() + // Expected error + val expectedError = mapOf( + "error" to mapOf( + "root_cause" to listOf( + mapOf( + "type" to "status_exception", + "reason" to "Monitor $monitorId is already enabled" + ) + ), + "type" to "status_exception", + "reason" to "Monitor $monitorId is already enabled" + ), + "status" to 400 + ) + assertEquals(expectedError, errorResponse) + } + + fun `test disable monitor when already disabled`() { + val monitorId = createMonitor(randomQueryLevelMonitor(enabled = false, enabledTime = null), refresh = true).id + val exception = assertThrows(ResponseException::class.java) { + client().makeRequest( + "PUT", + "${AlertingPlugin.MONITOR_BASE_URI}/$monitorId/disable", + emptyMap(), + null + ) + } + val errorResponse = createParser(XContentType.JSON.xContent(), exception.response.entity.content).map() + // Expected error + val expectedError = mapOf( + "error" to mapOf( + "root_cause" to listOf( + mapOf( + "type" to "status_exception", + "reason" to "Monitor $monitorId is already disabled" + ) + ), + "type" to "status_exception", + "reason" to "Monitor $monitorId is already disabled" + ), + "status" to 400 + ) + assertEquals(expectedError, errorResponse) + } + /** * This use case is needed by the frontend plugin for displaying alert counts on the Monitors list page. * https://github.com/opensearch-project/alerting-dashboards-plugin/blob/main/server/services/MonitorService.js#L235