diff --git a/src/main/java/org/opensearch/performanceanalyzer/rca/RcaController.java b/src/main/java/org/opensearch/performanceanalyzer/rca/RcaController.java index ab89f813e..9e32cddbb 100644 --- a/src/main/java/org/opensearch/performanceanalyzer/rca/RcaController.java +++ b/src/main/java/org/opensearch/performanceanalyzer/rca/RcaController.java @@ -366,7 +366,8 @@ private void readAndUpdateMutedComponentsDuringStart() { } } - private boolean updateMutedComponents() { + @VisibleForTesting + public boolean updateMutedComponents() { try { Set allNodes = ConnectedComponent.getNodesForAllComponents(this.connectedComponents); @@ -547,6 +548,16 @@ public void setDbProvider(Queryable dbProvider) throws InterruptedException { this.dbProvider = dbProvider; } + @VisibleForTesting + public void setRcaConf(RcaConf rcaConf) { + this.rcaConf = rcaConf; + } + + @VisibleForTesting + public void setConnectedComponents(List connectedComponents) { + this.connectedComponents = connectedComponents; + } + @VisibleForTesting public List getConnectedComponents() { return connectedComponents; diff --git a/src/test/java/org/opensearch/performanceanalyzer/rest/QueryRcaRequestHandlerTest.java b/src/test/java/org/opensearch/performanceanalyzer/rest/QueryRcaRequestHandlerTest.java new file mode 100644 index 000000000..8868ba43d --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/rest/QueryRcaRequestHandlerTest.java @@ -0,0 +1,171 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.rest; + + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Collections; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.opensearch.performanceanalyzer.AppContext; +import org.opensearch.performanceanalyzer.PerformanceAnalyzerApp; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics; +import org.opensearch.performanceanalyzer.rca.RcaController; +import org.opensearch.performanceanalyzer.rca.framework.core.ConnectedComponent; +import org.opensearch.performanceanalyzer.rca.framework.core.RcaConf; +import org.opensearch.performanceanalyzer.rca.framework.core.Stats; +import org.opensearch.performanceanalyzer.rca.store.rca.hotshard.HotShardClusterRca; +import org.opensearch.performanceanalyzer.rca.store.rca.temperature.NodeTemperatureRca; +import org.opensearch.performanceanalyzer.reader.ClusterDetailsEventProcessor; + +public class QueryRcaRequestHandlerTest { + private QueryRcaRequestHandler handler; + private AppContext appContext; + private RcaController rcaController; + private static final String queryPrefix = + "http://localhost:9600/_plugins/_performanceanalyzer/rca"; + private static final String mutedConfPath = + "./src/test/resources/rca/rca_query_muted_test.conf"; + private static final String nonMutedConfPath = "./src/test/resources/rca/rca_query_test.conf"; + + private void setClusterManagerContext(boolean isClusterManager) { + ClusterDetailsEventProcessor clusterDetailsEventProcessor = + new ClusterDetailsEventProcessor(); + clusterDetailsEventProcessor.setNodesDetails( + Collections.singletonList( + new ClusterDetailsEventProcessor.NodeDetails( + isClusterManager + ? AllMetrics.NodeRole.ELECTED_CLUSTER_MANAGER + : AllMetrics.NodeRole.DATA, + "test_node", + "127.0.0.1", + isClusterManager))); + appContext.setClusterDetailsEventProcessor(clusterDetailsEventProcessor); + } + + private void setConfPath(String rcaConfPath) { + rcaController.setRcaConf(new RcaConf(rcaConfPath)); + rcaController.updateMutedComponents(); + } + + private HttpExchange sendQuery(String query, String requestMethod, OutputStream os) + throws Exception { + HttpExchange exchange = Mockito.mock(HttpExchange.class); + Mockito.when(exchange.getResponseBody()).thenReturn(os != null ? os : System.out); + Mockito.when(exchange.getRequestMethod()).thenReturn(requestMethod); + Headers responseHeaders = Mockito.mock(Headers.class); + Mockito.when(exchange.getResponseHeaders()).thenReturn(responseHeaders); + Mockito.when(exchange.getRequestURI()).thenReturn(new URI(query)); + handler.handle(exchange); + return exchange; + } + + @Before + public void setUp() { + /* Prepares RcaController for with nodes and config files for testing */ + appContext = new AppContext(); + rcaController = new RcaController(); + ConnectedComponent connectedComponent = new ConnectedComponent(1); + connectedComponent.addLeafNode(new HotShardClusterRca(0, null)); + connectedComponent.addLeafNode(new NodeTemperatureRca(null, null, null)); + rcaController.setConnectedComponents(Collections.singletonList(connectedComponent)); + connectedComponent.getAllNodes(); // Initializes node names + Stats.getInstance().getConnectedComponents(); // Initializes muted graph nodes structure + setConfPath(nonMutedConfPath); + PerformanceAnalyzerApp.setRcaController(rcaController); + handler = new QueryRcaRequestHandler(appContext); + setClusterManagerContext(false); + } + + @After + public void reset() { + Stats.getInstance().getConnectedComponents(); + PerformanceAnalyzerApp.setRcaController(null); + } + + @Test + public void testInvalidNodeRole() throws Exception { + HttpExchange exchange = sendQuery(queryPrefix + "?name=HotShardClusterRca", "GET", null); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_BAD_REQUEST), + ArgumentMatchers.anyLong()); + } + + @Test + public void testValidNodeRole() throws Exception { + setClusterManagerContext(true); + HttpExchange exchange = sendQuery(queryPrefix + "?name=HotShardClusterRca", "GET", null); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_OK), ArgumentMatchers.anyLong()); + } + + @Test + public void testBadRequestMethod() throws Exception { + HttpExchange exchange = sendQuery(queryPrefix + "?name=HotShardClusterRca", "PUT", null); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_NOT_FOUND), + ArgumentMatchers.anyLong()); + } + + @Test + public void testMutedLocalTemperatureRCA() throws Exception { + setConfPath(mutedConfPath); + Assert.assertTrue(Stats.getInstance().getMutedGraphNodes().contains("NodeTemperatureRca")); + OutputStream exchangeOutputStream = new ByteArrayOutputStream(); + HttpExchange exchange = + sendQuery( + queryPrefix + "?name=NodeTemperatureRca&local=true", + "GET", + exchangeOutputStream); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_BAD_REQUEST), + ArgumentMatchers.anyLong()); + + Assert.assertTrue(exchangeOutputStream.toString().contains("muted")); + } + + @Test + public void testInvalidParams() throws Exception { + setClusterManagerContext(true); + OutputStream exchangeOutputStream = new ByteArrayOutputStream(); + HttpExchange exchange = + sendQuery(queryPrefix + "?name=NonExistingClusterRCA", "GET", exchangeOutputStream); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_BAD_REQUEST), + ArgumentMatchers.anyLong()); + + Assert.assertTrue(exchangeOutputStream.toString().contains("Invalid RCA")); + } + + @Test + public void testMutedClusterRCA() throws Exception { + setClusterManagerContext(true); + setConfPath(mutedConfPath); + Assert.assertTrue(Stats.getInstance().getMutedGraphNodes().contains("NodeTemperatureRca")); + OutputStream exchangeOutputStream = new ByteArrayOutputStream(); + HttpExchange exchange = + sendQuery(queryPrefix + "?name=HotShardClusterRca", "GET", exchangeOutputStream); + Mockito.verify(exchange) + .sendResponseHeaders( + ArgumentMatchers.eq(HttpURLConnection.HTTP_OK), ArgumentMatchers.anyLong()); + + Assert.assertEquals("{}", exchangeOutputStream.toString()); + } +} diff --git a/src/test/resources/rca/rca_query_muted_test.conf b/src/test/resources/rca/rca_query_muted_test.conf new file mode 100644 index 000000000..1ace7496c --- /dev/null +++ b/src/test/resources/rca/rca_query_muted_test.conf @@ -0,0 +1,27 @@ +{ + "analysis-graph-implementor" : "org.opensearch.performanceanalyzer.rca.store.AnalysisGraphTest", + "rca-store-location" : "s3://sifi-store/rcas/", + "threshold-store-location" : "s3://sifi-store/thresholds/", + "new-rca-check-minutes" : 60, + "new-threshold-check-minutes" : 30, + "network-queue-length" : 200, + "max-flow-units-per-vertex-buffer" : 200, + "tags" : { + "locus" : "data-node", + "disk" : "ssd", + "region" : "use1", + "instance-type" : "i3.8xl", + "domain" : "rca-test-cluster" + }, + "remote-peers" : [ "ip1", "ip2", "ip3" ], + "datastore" : { + "type" : "sqlite", + "location-dir" : "/tmp", + "filename" : "rca.sqlite", + "storage-file-retention-count" : 5, + "rotation-period-seconds" : 21600 + }, + "muted-rcas" : [ "HotShardClusterRca" , "NodeTemperatureRca"], + "muted-deciders" : [ ], + "muted-actions" : [ ] +} \ No newline at end of file diff --git a/src/test/resources/rca/rca_query_test.conf b/src/test/resources/rca/rca_query_test.conf new file mode 100644 index 000000000..c952b14b6 --- /dev/null +++ b/src/test/resources/rca/rca_query_test.conf @@ -0,0 +1,27 @@ +{ + "analysis-graph-implementor" : "org.opensearch.performanceanalyzer.rca.store.AnalysisGraphTest", + "rca-store-location" : "s3://sifi-store/rcas/", + "threshold-store-location" : "s3://sifi-store/thresholds/", + "new-rca-check-minutes" : 60, + "new-threshold-check-minutes" : 30, + "network-queue-length" : 200, + "max-flow-units-per-vertex-buffer" : 200, + "tags" : { + "locus" : "data-node", + "disk" : "ssd", + "region" : "use1", + "instance-type" : "i3.8xl", + "domain" : "rca-test-cluster" + }, + "remote-peers" : [ "ip1", "ip2", "ip3" ], + "datastore" : { + "type" : "sqlite", + "location-dir" : "/tmp", + "filename" : "rca.sqlite", + "storage-file-retention-count" : 5, + "rotation-period-seconds" : 21600 + }, + "muted-rcas" : [ ], + "muted-deciders" : [ ], + "muted-actions" : [ ] +} \ No newline at end of file