--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
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;
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;
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
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) {
@Override
public ProcessMonitor launch(EsCommand esCommand) {
Process process = null;
- ProcessId processId = esCommand.getProcessId();
try {
writeConfFiles(esCommand);
ProcessBuilder processBuilder = create(esCommand);
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);
}
}
--- /dev/null
+/*
+ * 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));
+ }
+}