diff options
author | Daniel Schwarz <daniel.schwarz@sonarsource.com> | 2017-09-07 14:02:53 +0200 |
---|---|---|
committer | Daniel Schwarz <bartfastiel@users.noreply.github.com> | 2017-09-11 13:40:02 +0200 |
commit | d77cf9f6541ed04f39b0e0201618343e80946602 (patch) | |
tree | b8bab566db9cb880f6e673181abf7b6d9e41b686 | |
parent | 8b2756441322b038aab642023846e91334b70f76 (diff) | |
download | sonarqube-d77cf9f6541ed04f39b0e0201618343e80946602.tar.gz sonarqube-d77cf9f6541ed04f39b0e0201618343e80946602.zip |
Add test for EsProcessMonitor
5 files changed, 176 insertions, 22 deletions
diff --git a/server/sonar-main/src/main/java/org/sonar/application/process/EsConnector.java b/server/sonar-main/src/main/java/org/sonar/application/process/EsConnector.java new file mode 100644 index 00000000000..8104430d5a3 --- /dev/null +++ b/server/sonar-main/src/main/java/org/sonar/application/process/EsConnector.java @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.application.process; + +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.cluster.health.ClusterHealthStatus; + +public interface EsConnector { + ClusterHealthStatus getClusterHealthStatus(TransportClient transportClient); +} diff --git a/server/sonar-main/src/main/java/org/sonar/application/process/EsConnectorImpl.java b/server/sonar-main/src/main/java/org/sonar/application/process/EsConnectorImpl.java new file mode 100644 index 00000000000..a55f0590bea --- /dev/null +++ b/server/sonar-main/src/main/java/org/sonar/application/process/EsConnectorImpl.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.application.process; + +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.cluster.health.ClusterHealthStatus; + +import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; + +public class EsConnectorImpl implements EsConnector { + @Override + public ClusterHealthStatus getClusterHealthStatus(TransportClient transportClient) { + return transportClient.admin().cluster() + .health(new ClusterHealthRequest().waitForStatus(ClusterHealthStatus.YELLOW).timeout(timeValueSeconds(30))) + .actionGet().getStatus(); + } +} diff --git a/server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java b/server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java index 52c0a9d917a..ff049e69e67 100644 --- a/server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java +++ b/server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java @@ -28,11 +28,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.client.transport.NoNodeAvailableException; import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; @@ -40,12 +37,10 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.transport.Netty4Plugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.process.ProcessId; import org.sonar.process.command.EsCommand; import static java.util.Collections.singletonList; import static java.util.Collections.unmodifiableList; -import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; import static org.sonar.application.process.EsProcessMonitor.Status.CONNECTION_REFUSED; import static org.sonar.application.process.EsProcessMonitor.Status.GREEN; import static org.sonar.application.process.EsProcessMonitor.Status.KO; @@ -60,11 +55,13 @@ public class EsProcessMonitor extends AbstractProcessMonitor { private final AtomicBoolean nodeUp = new AtomicBoolean(false); private final AtomicBoolean nodeOperational = new AtomicBoolean(false); private final EsCommand esCommand; + private final EsConnector esConnector; private AtomicReference<TransportClient> transportClient = new AtomicReference<>(null); - public EsProcessMonitor(Process process, ProcessId processId, EsCommand esCommand) { - super(process, processId); + public EsProcessMonitor(Process process, EsCommand esCommand, EsConnector esConnector) { + super(process, esCommand.getProcessId()); this.esCommand = esCommand; + this.esConnector = esConnector; } @Override @@ -131,19 +128,16 @@ public class EsProcessMonitor extends AbstractProcessMonitor { private Status checkStatus() { try { - ClusterHealthResponse response = getTransportClient().admin().cluster() - .health(new ClusterHealthRequest().waitForStatus(ClusterHealthStatus.YELLOW).timeout(timeValueSeconds(30))) - .actionGet(); - if (response.getStatus() == ClusterHealthStatus.GREEN) { - return GREEN; + switch (esConnector.getClusterHealthStatus(getTransportClient())) { + case GREEN: + return GREEN; + case YELLOW: + return YELLOW; + case RED: + return RED; + default: + return KO; } - if (response.getStatus() == ClusterHealthStatus.YELLOW) { - return YELLOW; - } - if (response.getStatus() == ClusterHealthStatus.RED) { - return RED; - } - return KO; } catch (NoNodeAvailableException e) { return CONNECTION_REFUSED; } catch (Exception e) { diff --git a/server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java b/server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java index af17e5ff80b..17547f420bf 100644 --- a/server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java +++ b/server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java @@ -71,7 +71,6 @@ public class ProcessLauncherImpl implements ProcessLauncher { @Override public ProcessMonitor launch(EsCommand esCommand) { Process process = null; - ProcessId processId = esCommand.getProcessId(); try { writeConfFiles(esCommand); ProcessBuilder processBuilder = create(esCommand); @@ -79,13 +78,13 @@ public class ProcessLauncherImpl implements ProcessLauncher { process = processBuilder.start(); - return new EsProcessMonitor(process, processId, esCommand); + return new EsProcessMonitor(process, esCommand, new EsConnectorImpl()); } catch (Exception e) { // just in case if (process != null) { process.destroyForcibly(); } - throw new IllegalStateException(format("Fail to launch process [%s]", processId.getKey()), e); + throw new IllegalStateException(format("Fail to launch process [%s]", esCommand.getProcessId().getKey()), e); } } diff --git a/server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java b/server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java new file mode 100644 index 00000000000..f1993b462f6 --- /dev/null +++ b/server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.application.process; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Random; +import org.elasticsearch.client.transport.NoNodeAvailableException; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.junit.Test; +import org.sonar.process.ProcessId; +import org.sonar.process.command.EsCommand; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class EsProcessMonitorTest { + + @Test + public void isOperational_should_return_false_if_Elasticsearch_is_RED() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.RED); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isFalse(); + } + + @Test + public void isOperational_should_return_true_if_Elasticsearch_is_YELLOW() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.YELLOW); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isTrue(); + } + + @Test + public void isOperational_should_return_true_if_Elasticsearch_is_GREEN() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.GREEN); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isTrue(); + } + + @Test + public void isOperational_should_return_true_if_Elasticsearch_was_GREEN_once() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.GREEN); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isTrue(); + + when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.RED); + assertThat(underTest.isOperational()).isTrue(); + } + + @Test + public void isOperational_should_retry_if_Elasticsearch_is_unreachable() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())) + .thenThrow(new NoNodeAvailableException("test")) + .thenReturn(ClusterHealthStatus.GREEN); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isTrue(); + } + + @Test + public void isOperational_should_return_false_if_Elasticsearch_status_cannot_be_evaluated() throws Exception { + EsConnector esConnector = mock(EsConnector.class); + when(esConnector.getClusterHealthStatus(any())) + .thenThrow(new RuntimeException("test")); + EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector); + assertThat(underTest.isOperational()).isFalse(); + } + + private EsCommand getEsCommand() throws IOException { + Path tempDirectory = Files.createTempDirectory(getClass().getSimpleName()); + return new EsCommand(ProcessId.ELASTICSEARCH, tempDirectory.toFile()) + .setHost("localhost") + .setPort(new Random().nextInt(40000)); + } +} |