]> source.dussan.org Git - sonarqube.git/commitdiff
Add test for EsProcessMonitor
authorDaniel Schwarz <daniel.schwarz@sonarsource.com>
Thu, 7 Sep 2017 12:02:53 +0000 (14:02 +0200)
committerDaniel Schwarz <bartfastiel@users.noreply.github.com>
Mon, 11 Sep 2017 11:40:02 +0000 (13:40 +0200)
server/sonar-main/src/main/java/org/sonar/application/process/EsConnector.java [new file with mode: 0644]
server/sonar-main/src/main/java/org/sonar/application/process/EsConnectorImpl.java [new file with mode: 0644]
server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java
server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java
server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java [new file with mode: 0644]

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 (file)
index 0000000..8104430
--- /dev/null
@@ -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 (file)
index 0000000..a55f059
--- /dev/null
@@ -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();
+  }
+}
index 52c0a9d917a618a18a731a443b2465d9794021de..ff049e69e67875fe5490012df938913063c703dc 100644 (file)
@@ -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) {
index af17e5ff80b2d71352d940ba5bb72f5ea4ae8c90..17547f420bf7f7d83931026b1199cb43230fb0b3 100644 (file)
@@ -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 (file)
index 0000000..f1993b4
--- /dev/null
@@ -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));
+  }
+}