From 6271399c381a33364927e8147b6d6e3bc3f583cc Mon Sep 17 00:00:00 2001 From: Surya Sashank Nistala Date: Wed, 12 Apr 2023 13:09:13 -0700 Subject: [PATCH] Handle monitor or monitor index not exists during detector deletion (#384) Signed-off-by: Surya Sashank Nistala --- .../TransportDeleteDetectorAction.java | 35 ++++++++++++++-- .../SecurityAnalyticsRestTestCase.java | 12 ++++++ .../resthandler/DetectorRestApiIT.java | 42 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteDetectorAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteDetectorAction.java index 454dd2208..135b6adaa 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteDetectorAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportDeleteDetectorAction.java @@ -156,7 +156,6 @@ public void onFailure(Exception t) { private void onGetResponse(Detector detector) { List monitorIds = detector.getMonitorIds(); - String ruleIndex = detector.getRuleIndex(); ActionListener deletesListener = new GroupedActionListener<>(new ActionListener<>() { @Override public void onResponse(Collection responses) { @@ -176,8 +175,13 @@ public void onResponse(Collection responses) { @Override public void onFailure(Exception e) { - if (counter.compareAndSet(false, true)) { - finishHim(null, e); + if(isOnlyMonitorOrIndexMissingExceptionThrownByGroupedActionListener(e, detector.getId())) { + deleteDetectorFromConfig(detector.getId(), request.getRefreshPolicy()); + } else { + log.error(String.format(Locale.ROOT, "Failed to delete detector %s", detector.getId()), e); + if (counter.compareAndSet(false, true)) { + finishHim(null, e); + } } } }, monitorIds.size()); @@ -231,6 +235,7 @@ private void onFailures(Exception t) { private void finishHim(String detectorId, Exception t) { threadPool.executor(ThreadPool.Names.GENERIC).execute(ActionRunnable.supply(listener, () -> { if (t != null) { + log.error(String.format(Locale.ROOT, "Failed to delete detector %s",detectorId), t); if (t instanceof OpenSearchStatusException) { throw t; } @@ -240,5 +245,29 @@ private void finishHim(String detectorId, Exception t) { } })); } + + private boolean isOnlyMonitorOrIndexMissingExceptionThrownByGroupedActionListener( + Exception ex, + String detectorId + ) { + // grouped action listener listens on mutliple listeners but throws only one exception. If multiple + // listeners fail the other exceptions are added as suppressed exceptions to the first failure. + int len = ex.getSuppressed().length; + for (int i = 0; i <= len; i++) { + Throwable e = i == len ? ex : ex.getSuppressed()[i]; + if (e.getMessage().matches("(.*)Monitor(.*) is not found(.*)") + || e.getMessage().contains( + "Configured indices are not found: [.opendistro-alerting-config]") + ) { + log.error( + String.format(Locale.ROOT, "Monitor or jobs index already deleted." + + " Proceeding with detector %s deletion", detectorId), + e); + } else { + return false; + } + } + return true; + } } } \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java b/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java index c97db6ca6..dd8ec164f 100644 --- a/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java +++ b/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java @@ -239,6 +239,18 @@ protected Response executeAlertingMonitor(RestClient client, String monitorId, M return makeRequest(client, "POST", String.format(Locale.getDefault(), "/_plugins/_alerting/monitors/%s/_execute", monitorId), params, null); } + protected Response deleteAlertingMonitorIndex() throws IOException { + return makeRequest(client(), "DELETE", String.format(Locale.getDefault(), "/.opendistro-alerting-config"), new HashMap<>(), null); + } + + protected Response deleteAlertingMonitor(String monitorId) throws IOException { + return deleteAlertingMonitor(client(), monitorId); + } + + protected Response deleteAlertingMonitor(RestClient client, String monitorId) throws IOException { + return makeRequest(client, "DELETE", String.format(Locale.getDefault(), "/_plugins/_alerting/monitors/%s", monitorId), new HashMap<>(), null); + } + protected List executeSearch(String index, String request) throws IOException { return executeSearch(index, request, true); } diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/DetectorRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/DetectorRestApiIT.java index 5c16c5d8f..61d025242 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/DetectorRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/DetectorRestApiIT.java @@ -65,6 +65,48 @@ public void testNewLogTypes() throws IOException { Assert.assertEquals("Create detector failed", RestStatus.CREATED, restStatus(createResponse)); } + @SuppressWarnings("unchecked") + public void testDeletingADetector_MonitorNotExists() throws IOException { + String index = createTestIndex(randomIndex(), windowsIndexMapping()); + + // Execute CreateMappingsAction to add alias mapping for index + Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI); + // both req params and req body are supported + createMappingRequest.setJsonEntity( + "{ \"index_name\":\"" + index + "\"," + + " \"rule_topic\":\"" + randomDetectorType() + "\", " + + " \"partial\":true" + + "}" + ); + + Response response = client().performRequest(createMappingRequest); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + // Create detector #1 of type test_windows + Detector detector1 = randomDetectorWithTriggers(getRandomPrePackagedRules(), List.of(new DetectorTrigger(null, "test-trigger", "1", List.of(randomDetectorType()), List.of(), List.of(), List.of(), List.of()))); + String detectorId1 = createDetector(detector1); + + String request = "{\n" + + " \"query\" : {\n" + + " \"match\":{\n" + + " \"_id\": \"" + detectorId1 + "\"\n" + + " }\n" + + " }\n" + + "}"; + List hits = executeSearch(Detector.DETECTORS_INDEX, request); + SearchHit hit = hits.get(0); + + String monitorId = ((List) ((Map) hit.getSourceAsMap().get("detector")).get("monitor_id")).get(0); + + Response deleteMonitorResponse = deleteAlertingMonitor(monitorId); + assertEquals(200, deleteMonitorResponse.getStatusLine().getStatusCode()); + entityAsMap(deleteMonitorResponse); + + Response deleteResponse = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.DETECTOR_BASE_URI + "/" + detectorId1, Collections.emptyMap(), null); + Assert.assertEquals("Delete detector failed", RestStatus.OK, restStatus(deleteResponse)); + hits = executeSearch(Detector.DETECTORS_INDEX, request); + Assert.assertEquals(0, hits.size()); + } + @SuppressWarnings("unchecked") public void testCreatingADetector() throws IOException { String index = createTestIndex(randomIndex(), windowsIndexMapping());