From 9566924fc30af834c1f7ccc98e1d9df4b68e3919 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 22 Jan 2024 22:44:31 +0000 Subject: [PATCH] Add alerting tools IT; fix missing system index bug of SearchMonitorsTool (#135) Signed-off-by: Tyler Ohlsen (cherry picked from commit 3e4d45106d10588c866899082b2493e17d26335e) Signed-off-by: github-actions[bot] --- build.gradle | 3 +- .../agent/tools/SearchMonitorsTool.java | 83 ++++++++++++------- .../agent/tools/utils/ToolConstants.java | 6 +- .../integTest/SearchAlertsToolIT.java | 57 +++++++++++++ .../integTest/SearchMonitorsToolIT.java | 58 +++++++++++++ ...nt_of_search_alerts_tool_request_body.json | 10 +++ ..._of_search_monitors_tool_request_body.json | 10 +++ 7 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 src/test/java/org/opensearch/integTest/SearchAlertsToolIT.java create mode 100644 src/test/java/org/opensearch/integTest/SearchMonitorsToolIT.java create mode 100644 src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_alerts_tool_request_body.json create mode 100644 src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_monitors_tool_request_body.json diff --git a/build.gradle b/build.gradle index 45b3f314..9d857f9c 100644 --- a/build.gradle +++ b/build.gradle @@ -129,6 +129,7 @@ dependencies { zipArchive group: 'org.opensearch.plugin', name:'opensearch-sql-plugin', version: "${version}" zipArchive group: 'org.opensearch.plugin', name:'opensearch-knn', version: "${version}" zipArchive group: 'org.opensearch.plugin', name:'neural-search', version: "${version}" + zipArchive group: 'org.opensearch.plugin', name:'alerting', version: "${version}" // Test dependencies testImplementation "org.opensearch.test:framework:${opensearch_version}" @@ -388,4 +389,4 @@ task updateVersion { // Include the required files that needs to be updated with new Version ant.replaceregexp(file:'build.gradle', match: '"opensearch.version", "\\d.*"', replace: '"opensearch.version", "' + newVersion.tokenize('-')[0] + '-SNAPSHOT"', flags:'g', byline:true) } -} +} \ No newline at end of file diff --git a/src/main/java/org/opensearch/agent/tools/SearchMonitorsTool.java b/src/main/java/org/opensearch/agent/tools/SearchMonitorsTool.java index 91f25182..8edcf22c 100644 --- a/src/main/java/org/opensearch/agent/tools/SearchMonitorsTool.java +++ b/src/main/java/org/opensearch/agent/tools/SearchMonitorsTool.java @@ -6,8 +6,11 @@ package org.opensearch.agent.tools; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.lucene.search.join.ScoreMode; @@ -20,6 +23,7 @@ import org.opensearch.commons.alerting.action.GetMonitorResponse; import org.opensearch.commons.alerting.action.SearchMonitorRequest; import org.opensearch.commons.alerting.model.Monitor; +import org.opensearch.commons.alerting.model.ScheduledJob; import org.opensearch.core.action.ActionListener; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.ExistsQueryBuilder; @@ -104,22 +108,17 @@ public void run(Map parameters, ActionListener listener) if (monitorId != null) { GetMonitorRequest getMonitorRequest = new GetMonitorRequest(monitorId, 1L, RestRequest.Method.GET, null); ActionListener getMonitorListener = ActionListener.wrap(response -> { - StringBuilder sb = new StringBuilder(); Monitor monitor = response.getMonitor(); - if (monitor != null) { - sb.append("Monitors=["); - sb.append("{"); - sb.append("id=").append(monitor.getId()).append(","); - sb.append("name=").append(monitor.getName()); - sb.append("}]"); - sb.append("TotalMonitors=1"); + processGetMonitorHit(monitor, listener); + }, e -> { + // System index isn't initialized by default, so ignore such errors. Alerting plugin does not return the + // standard IndexNotFoundException so we parse the message instead + if (e.getMessage().contains("Configured indices are not found")) { + processGetMonitorHit(null, listener); } else { - sb.append("Monitors=[]TotalMonitors=0"); + log.error("Failed to get monitor.", e); + listener.onFailure(e); } - listener.onResponse((T) sb.toString()); - }, e -> { - log.error("Failed to search monitors.", e); - listener.onFailure(e); }); AlertingPluginInterface.INSTANCE.getMonitor((NodeClient) client, getMonitorRequest, getMonitorListener); } else { @@ -167,24 +166,23 @@ public void run(Map parameters, ActionListener listener) .from(startIndex) .sort(sortString, sortOrder); - SearchMonitorRequest searchMonitorRequest = new SearchMonitorRequest(new SearchRequest().source(searchSourceBuilder)); + SearchRequest searchRequest = new SearchRequest().source(searchSourceBuilder).indices(ScheduledJob.SCHEDULED_JOBS_INDEX); + SearchMonitorRequest searchMonitorRequest = new SearchMonitorRequest(searchRequest); ActionListener searchMonitorListener = ActionListener.wrap(response -> { - StringBuilder sb = new StringBuilder(); - SearchHit[] hits = response.getHits().getHits(); - sb.append("Monitors=["); - for (SearchHit hit : hits) { - sb.append("{"); - sb.append("id=").append(hit.getId()).append(","); - sb.append("name=").append(hit.getSourceAsMap().get("name")); - sb.append("}"); - } - sb.append("]"); - sb.append("TotalMonitors=").append(response.getHits().getTotalHits().value); - listener.onResponse((T) sb.toString()); + List hits = Arrays.asList(response.getHits().getHits()); + Map hitsAsMap = hits.stream().collect(Collectors.toMap(SearchHit::getId, hit -> hit)); + processHits(hitsAsMap, listener); + }, e -> { - log.error("Failed to search monitors.", e); - listener.onFailure(e); + // System index isn't initialized by default, so ignore such errors. Alerting plugin does not return the + // standard IndexNotFoundException so we parse the message instead + if (e.getMessage().contains("Configured indices are not found")) { + processHits(Collections.emptyMap(), listener); + } else { + log.error("Failed to search monitors.", e); + listener.onFailure(e); + } }); AlertingPluginInterface.INSTANCE.searchMonitors((NodeClient) client, searchMonitorRequest, searchMonitorListener); } @@ -200,6 +198,35 @@ public String getType() { return TYPE; } + private void processHits(Map hitsAsMap, ActionListener listener) { + StringBuilder sb = new StringBuilder(); + sb.append("Monitors=["); + for (SearchHit hit : hitsAsMap.values()) { + sb.append("{"); + sb.append("id=").append(hit.getId()).append(","); + sb.append("name=").append(hit.getSourceAsMap().get("name")); + sb.append("}"); + } + sb.append("]"); + sb.append("TotalMonitors=").append(hitsAsMap.size()); + listener.onResponse((T) sb.toString()); + } + + private void processGetMonitorHit(Monitor monitor, ActionListener listener) { + StringBuilder sb = new StringBuilder(); + if (monitor != null) { + sb.append("Monitors=["); + sb.append("{"); + sb.append("id=").append(monitor.getId()).append(","); + sb.append("name=").append(monitor.getName()); + sb.append("}]"); + sb.append("TotalMonitors=1"); + } else { + sb.append("Monitors=[]TotalMonitors=0"); + } + listener.onResponse((T) sb.toString()); + } + /** * Factory for the {@link SearchMonitorsTool} */ diff --git a/src/main/java/org/opensearch/agent/tools/utils/ToolConstants.java b/src/main/java/org/opensearch/agent/tools/utils/ToolConstants.java index 047075c2..e6d95afe 100644 --- a/src/main/java/org/opensearch/agent/tools/utils/ToolConstants.java +++ b/src/main/java/org/opensearch/agent/tools/utils/ToolConstants.java @@ -17,8 +17,10 @@ public static enum DetectorStateString { Initializing } - // System indices constants are not cleanly exposed from the AD plugin, so we persist our - // own constant here. + // System indices constants are not cleanly exposed from the AD & Alerting plugins, so we persist our + // own constants here. public static final String AD_RESULTS_INDEX_PATTERN = ".opendistro-anomaly-results*"; public static final String AD_DETECTORS_INDEX = ".opendistro-anomaly-detectors"; + + public static final String ALERTING_CONFIG_INDEX = ".opendistro-alerting-config"; } diff --git a/src/test/java/org/opensearch/integTest/SearchAlertsToolIT.java b/src/test/java/org/opensearch/integTest/SearchAlertsToolIT.java new file mode 100644 index 00000000..66e8f233 --- /dev/null +++ b/src/test/java/org/opensearch/integTest/SearchAlertsToolIT.java @@ -0,0 +1,57 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.integTest; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.After; +import org.junit.Before; + +import lombok.SneakyThrows; + +public class SearchAlertsToolIT extends BaseAgentToolsIT { + private String registerAgentRequestBody; + private static final String monitorId = "foo-id"; + private static final String monitorName = "foo-name"; + + @Before + @SneakyThrows + public void setUp() { + super.setUp(); + registerAgentRequestBody = Files + .readString( + Path + .of( + this + .getClass() + .getClassLoader() + .getResource("org/opensearch/agent/tools/register_flow_agent_of_search_alerts_tool_request_body.json") + .toURI() + ) + ); + } + + @After + @SneakyThrows + public void tearDown() { + super.tearDown(); + deleteExternalIndices(); + deleteSystemIndices(); + } + + @SneakyThrows + public void testSearchAlertsToolInFlowAgent_withNoSystemIndex() { + deleteSystemIndices(); + String agentId = createAgent(registerAgentRequestBody); + String agentInput = "{\"parameters\":{\"monitorId\": \"" + monitorId + "\"}}"; + String result = executeAgent(agentId, agentInput); + assertEquals("Alerts=[]TotalAlerts=0", result); + } + + // TODO: Add IT to test against sample alerts data + // https://github.com/opensearch-project/skills/issues/136 +} diff --git a/src/test/java/org/opensearch/integTest/SearchMonitorsToolIT.java b/src/test/java/org/opensearch/integTest/SearchMonitorsToolIT.java new file mode 100644 index 00000000..c94bf8e8 --- /dev/null +++ b/src/test/java/org/opensearch/integTest/SearchMonitorsToolIT.java @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.integTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.After; +import org.junit.Before; + +import lombok.SneakyThrows; + +public class SearchMonitorsToolIT extends BaseAgentToolsIT { + private String registerAgentRequestBody; + private static final String monitorId = "foo-id"; + private static final String monitorName = "foo-name"; + + @Before + @SneakyThrows + public void setUp() { + super.setUp(); + registerAgentRequestBody = Files + .readString( + Path + .of( + this + .getClass() + .getClassLoader() + .getResource("org/opensearch/agent/tools/register_flow_agent_of_search_monitors_tool_request_body.json") + .toURI() + ) + ); + } + + @After + @SneakyThrows + public void tearDown() { + super.tearDown(); + deleteExternalIndices(); + deleteSystemIndices(); + } + + @SneakyThrows + public void testSearchMonitorsToolInFlowAgent_withNoSystemIndex() { + deleteSystemIndices(); + String agentId = createAgent(registerAgentRequestBody); + String agentInput = "{\"parameters\":{\"monitorName\": \"" + monitorName + "\"}}"; + String result = executeAgent(agentId, agentInput); + assertEquals("Monitors=[]TotalMonitors=0", result); + } + + // TODO: Add IT to test against sample monitor data +} diff --git a/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_alerts_tool_request_body.json b/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_alerts_tool_request_body.json new file mode 100644 index 00000000..80d1146b --- /dev/null +++ b/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_alerts_tool_request_body.json @@ -0,0 +1,10 @@ +{ + "name": "Test_Search_Alerts_Agent", + "type": "flow", + "tools": [ + { + "type": "SearchAlertsTool", + "description": "Use this tool to search alerts." + } + ] +} \ No newline at end of file diff --git a/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_monitors_tool_request_body.json b/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_monitors_tool_request_body.json new file mode 100644 index 00000000..d38d955f --- /dev/null +++ b/src/test/resources/org/opensearch/agent/tools/register_flow_agent_of_search_monitors_tool_request_body.json @@ -0,0 +1,10 @@ +{ + "name": "Test_Search_Monitors_Agent", + "type": "flow", + "tools": [ + { + "type": "SearchMonitorsTool", + "description": "Use this tool to search alerting monitors." + } + ] +} \ No newline at end of file