aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2017-08-31 17:12:00 +0200
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2017-09-13 15:50:51 +0200
commitfa0fe58b0561cac895939487f11c22c92f18d03f (patch)
tree54fb2eddd39747196f2871484b1afd379ae27901
parent84422d2ca5f4dcec1aa0ac17486a532574d676e1 (diff)
downloadsonarqube-fa0fe58b0561cac895939487f11c22c92f18d03f.tar.gz
sonarqube-fa0fe58b0561cac895939487f11c22c92f18d03f.zip
SONAR-9741 search nodes share startup health status
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java4
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java3
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterAppStateImpl.java (renamed from server/sonar-main/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java)8
-rw-r--r--server/sonar-main/src/main/java/org/sonar/application/health/SearchNodeHealthProvider.java35
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/AppStateFactoryTest.java6
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java57
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/TestAppState.java20
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/TestClusterAppState.java36
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterAppStateImplTest.java (renamed from server/sonar-main/src/test/java/org/sonar/application/cluster/AppStateClusterImplTest.java)22
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTest.java9
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTestHelper.java2
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/health/SearchNodeHealthProviderTest.java212
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java2
13 files changed, 338 insertions, 78 deletions
diff --git a/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java b/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
index 196aa2a9fa0..8f5cfbfe780 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java
@@ -19,7 +19,7 @@
*/
package org.sonar.application;
-import org.sonar.application.cluster.AppStateClusterImpl;
+import org.sonar.application.cluster.ClusterAppStateImpl;
import org.sonar.application.config.AppSettings;
import org.sonar.application.config.ClusterSettings;
@@ -32,6 +32,6 @@ public class AppStateFactory {
}
public AppState create() {
- return ClusterSettings.isClusterEnabled(settings) ? new AppStateClusterImpl(settings) : new AppStateImpl();
+ return ClusterSettings.isClusterEnabled(settings) ? new ClusterAppStateImpl(settings) : new AppStateImpl();
}
}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java b/server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java
index 621cce82b3b..3268bb249c6 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.NetworkUtils;
import org.sonar.api.utils.System2;
import org.sonar.application.cluster.ClusterAppState;
import org.sonar.application.config.AppSettings;
@@ -151,7 +152,7 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
ClusterAppState clusterAppState = (ClusterAppState) appState;
this.healthStateSharing = new HealthStateSharingImpl(
clusterAppState.getHazelcastClient(),
- new SearchNodeHealthProvider(settings.getProps(), System2.INSTANCE));
+ new SearchNodeHealthProvider(settings.getProps(), System2.INSTANCE, clusterAppState, NetworkUtils.INSTANCE));
this.healthStateSharing.start();
}
}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java b/server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterAppStateImpl.java
index 717f41b287b..9742941f132 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterAppStateImpl.java
@@ -36,13 +36,13 @@ import static org.sonar.cluster.ClusterProperties.CLUSTER_ENABLED;
import static org.sonar.cluster.ClusterProperties.CLUSTER_LOCALENDPOINT;
import static org.sonar.cluster.ClusterProperties.CLUSTER_MEMBERUUID;
-public class AppStateClusterImpl implements ClusterAppState {
- private static Logger LOGGER = LoggerFactory.getLogger(AppStateClusterImpl.class);
+public class ClusterAppStateImpl implements ClusterAppState {
+ private static Logger LOGGER = LoggerFactory.getLogger(ClusterAppStateImpl.class);
private final Map<ProcessId, Boolean> localProcesses = new EnumMap<>(ProcessId.class);
private final HazelcastCluster hazelcastCluster;
- public AppStateClusterImpl(AppSettings appSettings) {
+ public ClusterAppStateImpl(AppSettings appSettings) {
if (!appSettings.getProps().valueAsBoolean(CLUSTER_ENABLED)) {
throw new IllegalStateException("Cluster is not enabled on this instance");
}
@@ -123,7 +123,7 @@ public class AppStateClusterImpl implements ClusterAppState {
* @param logger
*/
static void setLogger(Logger logger) {
- AppStateClusterImpl.LOGGER = logger;
+ ClusterAppStateImpl.LOGGER = logger;
}
}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/health/SearchNodeHealthProvider.java b/server/sonar-main/src/main/java/org/sonar/application/health/SearchNodeHealthProvider.java
index 83f424217e1..d1e184ad436 100644
--- a/server/sonar-main/src/main/java/org/sonar/application/health/SearchNodeHealthProvider.java
+++ b/server/sonar-main/src/main/java/org/sonar/application/health/SearchNodeHealthProvider.java
@@ -19,37 +19,54 @@
*/
package org.sonar.application.health;
-import java.util.Random;
+import org.sonar.NetworkUtils;
import org.sonar.api.utils.System2;
-import org.sonar.cluster.ClusterProperties;
+import org.sonar.application.cluster.ClusterAppState;
import org.sonar.cluster.health.NodeDetails;
import org.sonar.cluster.health.NodeHealth;
import org.sonar.cluster.health.NodeHealthProvider;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_HOST;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_NAME;
import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_PORT;
public class SearchNodeHealthProvider implements NodeHealthProvider {
private final System2 system2;
+ private final ClusterAppState clusterAppState;
private final NodeDetails nodeDetails;
- public SearchNodeHealthProvider(Props props, System2 system2) {
+ public SearchNodeHealthProvider(Props props, System2 system2, ClusterAppState clusterAppState, NetworkUtils networkUtils) {
this.system2 = system2;
+ this.clusterAppState = clusterAppState;
this.nodeDetails = NodeDetails.newNodeDetailsBuilder()
.setType(NodeDetails.Type.SEARCH)
- .setName(props.nonNullValue(ClusterProperties.CLUSTER_NAME) + new Random().nextInt(999))
- // TODO read sonar.cluster.node.host
- .setHost("host hardcoded for now")
+ .setName(props.nonNullValue(CLUSTER_NODE_NAME))
+ .setHost(getHost(props, networkUtils))
.setPort(Integer.valueOf(props.nonNullValue(CLUSTER_NODE_PORT)))
- // TODO is now good enough?
.setStarted(system2.now())
.build();
}
+ private static String getHost(Props props, NetworkUtils networkUtils) {
+ String host = props.value(CLUSTER_NODE_HOST);
+ if (host != null && !host.isEmpty()) {
+ return host;
+ }
+ return networkUtils.getHostname();
+ }
+
@Override
public NodeHealth get() {
- return NodeHealth.newNodeHealthBuilder()
- .setStatus(NodeHealth.Status.GREEN)
+ NodeHealth.Builder builder = NodeHealth.newNodeHealthBuilder();
+ if (clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)) {
+ builder.setStatus(NodeHealth.Status.GREEN);
+ } else {
+ builder.setStatus(NodeHealth.Status.RED)
+ .addCause("Elasticsearch is not operational");
+ }
+ return builder
.setDetails(nodeDetails)
.setDate(system2.now())
.build();
diff --git a/server/sonar-main/src/test/java/org/sonar/application/AppStateFactoryTest.java b/server/sonar-main/src/test/java/org/sonar/application/AppStateFactoryTest.java
index 3998aabe983..10b9d0d6d3c 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/AppStateFactoryTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/AppStateFactoryTest.java
@@ -20,7 +20,7 @@
package org.sonar.application;
import org.junit.Test;
-import org.sonar.application.cluster.AppStateClusterImpl;
+import org.sonar.application.cluster.ClusterAppStateImpl;
import org.sonar.application.config.TestAppSettings;
import static org.assertj.core.api.Assertions.assertThat;
@@ -40,8 +40,8 @@ public class AppStateFactoryTest {
settings.set(CLUSTER_NAME, "foo");
AppState appState = underTest.create();
- assertThat(appState).isInstanceOf(AppStateClusterImpl.class);
- appState.close();
+ assertThat(appState).isInstanceOf(ClusterAppStateImpl.class);
+ ((ClusterAppStateImpl) appState).close();
}
@Test
diff --git a/server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java b/server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java
index 91bd75d1ba4..f419f4d0e17 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
+import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
@@ -40,6 +41,7 @@ import org.mockito.Mockito;
import org.sonar.application.config.TestAppSettings;
import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.ProcessMonitor;
+import org.sonar.cluster.localclient.HazelcastClient;
import org.sonar.process.ProcessId;
import org.sonar.process.command.AbstractCommand;
import org.sonar.process.command.CommandFactory;
@@ -47,12 +49,16 @@ import org.sonar.process.command.EsCommand;
import org.sonar.process.command.JavaCommand;
import static java.util.Collections.synchronizedList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.sonar.cluster.ClusterProperties.CLUSTER_ENABLED;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_HOST;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_NAME;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_PORT;
import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_TYPE;
import static org.sonar.process.ProcessId.COMPUTE_ENGINE;
import static org.sonar.process.ProcessId.ELASTICSEARCH;
@@ -77,6 +83,8 @@ public class SchedulerImplTest {
private TestCommandFactory javaCommandFactory = new TestCommandFactory();
private TestProcessLauncher processLauncher = new TestProcessLauncher();
private TestAppState appState = new TestAppState();
+ private HazelcastClient hazelcastClient = mock(HazelcastClient.class);
+ private TestClusterAppState clusterAppState = new TestClusterAppState(hazelcastClient);
private List<ProcessId> orderedStops = synchronizedList(new ArrayList<>());
@Before
@@ -95,7 +103,7 @@ public class SchedulerImplTest {
@Test
public void start_and_stop_sequence_of_ES_WEB_CE_in_order() throws Exception {
- SchedulerImpl underTest = newScheduler();
+ SchedulerImpl underTest = newScheduler(false);
underTest.schedule();
// elasticsearch does not have preconditions to start
@@ -105,7 +113,7 @@ public class SchedulerImplTest {
// elasticsearch becomes operational -> web leader is starting
es.operational = true;
- waitForAppStateOperational(ELASTICSEARCH);
+ waitForAppStateOperational(appState, ELASTICSEARCH);
TestProcess web = processLauncher.waitForProcess(WEB_SERVER);
assertThat(web.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(2);
@@ -113,7 +121,7 @@ public class SchedulerImplTest {
// web becomes operational -> CE is starting
web.operational = true;
- waitForAppStateOperational(WEB_SERVER);
+ waitForAppStateOperational(appState, WEB_SERVER);
TestProcess ce = processLauncher.waitForProcess(COMPUTE_ENGINE);
assertThat(ce.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(3);
@@ -148,7 +156,7 @@ public class SchedulerImplTest {
@Test
public void all_processes_are_stopped_if_one_process_fails_to_start() throws Exception {
- SchedulerImpl underTest = newScheduler();
+ SchedulerImpl underTest = newScheduler(false);
processLauncher.makeStartupFail = COMPUTE_ENGINE;
underTest.schedule();
@@ -237,7 +245,8 @@ public class SchedulerImplTest {
public void search_node_starts_only_elasticsearch() throws Exception {
settings.set(CLUSTER_ENABLED, "true");
settings.set(CLUSTER_NODE_TYPE, "search");
- SchedulerImpl underTest = newScheduler();
+ addRequiredNodeProperties();
+ SchedulerImpl underTest = newScheduler(true);
underTest.schedule();
processLauncher.waitForProcessAlive(ProcessId.ELASTICSEARCH);
@@ -248,10 +257,10 @@ public class SchedulerImplTest {
@Test
public void application_node_starts_only_web_and_ce() throws Exception {
- appState.setOperational(ProcessId.ELASTICSEARCH);
+ clusterAppState.setOperational(ProcessId.ELASTICSEARCH);
settings.set(CLUSTER_ENABLED, "true");
settings.set(CLUSTER_NODE_TYPE, "application");
- SchedulerImpl underTest = newScheduler();
+ SchedulerImpl underTest = newScheduler(true);
underTest.schedule();
TestProcess web = processLauncher.waitForProcessAlive(WEB_SERVER);
@@ -265,12 +274,13 @@ public class SchedulerImplTest {
@Test
public void search_node_starts_even_if_web_leader_is_not_yet_operational() throws Exception {
// leader takes the lock, so underTest won't get it
- assertThat(appState.tryToLockWebLeader()).isTrue();
+ assertThat(clusterAppState.tryToLockWebLeader()).isTrue();
- appState.setOperational(ProcessId.ELASTICSEARCH);
+ clusterAppState.setOperational(ProcessId.ELASTICSEARCH);
settings.set(CLUSTER_ENABLED, "true");
settings.set(CLUSTER_NODE_TYPE, "search");
- SchedulerImpl underTest = newScheduler();
+ addRequiredNodeProperties();
+ SchedulerImpl underTest = newScheduler(true);
underTest.schedule();
processLauncher.waitForProcessAlive(ProcessId.ELASTICSEARCH);
@@ -282,19 +292,18 @@ public class SchedulerImplTest {
@Test
public void web_follower_starts_only_when_web_leader_is_operational() throws Exception {
// leader takes the lock, so underTest won't get it
- assertThat(appState.tryToLockWebLeader()).isTrue();
- appState.setOperational(ProcessId.ELASTICSEARCH);
+ assertThat(clusterAppState.tryToLockWebLeader()).isTrue();
+ clusterAppState.setOperational(ProcessId.ELASTICSEARCH);
settings.set(CLUSTER_ENABLED, "true");
settings.set(CLUSTER_NODE_TYPE, "application");
- SchedulerImpl underTest = newScheduler();
+ SchedulerImpl underTest = newScheduler(true);
underTest.schedule();
assertThat(processLauncher.processes).hasSize(0);
// leader becomes operational -> follower can start
- appState.setOperational(WEB_SERVER);
-
+ clusterAppState.setOperational(WEB_SERVER);
processLauncher.waitForProcessAlive(WEB_SERVER);
processLauncher.waitForProcessAlive(COMPUTE_ENGINE);
assertThat(processLauncher.processes).hasSize(2);
@@ -306,27 +315,27 @@ public class SchedulerImplTest {
public void web_server_waits_for_remote_elasticsearch_to_be_started_if_local_es_is_disabled() throws Exception {
settings.set(CLUSTER_ENABLED, "true");
settings.set(CLUSTER_NODE_TYPE, "application");
- SchedulerImpl underTest = newScheduler();
+ SchedulerImpl underTest = newScheduler(true);
underTest.schedule();
// WEB and CE wait for ES to be up
assertThat(processLauncher.processes).isEmpty();
// ES becomes operational on another node -> web leader can start
- appState.setRemoteOperational(ProcessId.ELASTICSEARCH);
+ clusterAppState.setRemoteOperational(ProcessId.ELASTICSEARCH);
processLauncher.waitForProcessAlive(WEB_SERVER);
assertThat(processLauncher.processes).hasSize(1);
underTest.terminate();
}
- private SchedulerImpl newScheduler() {
- return new SchedulerImpl(settings, appReloader, javaCommandFactory, processLauncher, appState)
+ private SchedulerImpl newScheduler(boolean clustered) {
+ return new SchedulerImpl(settings, appReloader, javaCommandFactory, processLauncher, clustered ? clusterAppState : appState)
.setProcessWatcherDelayMs(1L);
}
private Scheduler startAll() throws InterruptedException {
- SchedulerImpl scheduler = newScheduler();
+ SchedulerImpl scheduler = newScheduler(false);
scheduler.schedule();
processLauncher.waitForProcess(ELASTICSEARCH).operational = true;
processLauncher.waitForProcess(WEB_SERVER).operational = true;
@@ -334,7 +343,7 @@ public class SchedulerImplTest {
return scheduler;
}
- private void waitForAppStateOperational(ProcessId id) throws InterruptedException {
+ private static void waitForAppStateOperational(AppState appState, ProcessId id) throws InterruptedException {
while (true) {
if (appState.isOperational(id, true)) {
return;
@@ -343,6 +352,12 @@ public class SchedulerImplTest {
}
}
+ private void addRequiredNodeProperties() {
+ settings.set(CLUSTER_NODE_NAME, randomAlphanumeric(4));
+ settings.set(CLUSTER_NODE_HOST, randomAlphanumeric(4));
+ settings.set(CLUSTER_NODE_PORT, String.valueOf(1 + new Random().nextInt(999)));
+ }
+
private class TestCommandFactory implements CommandFactory {
@Override
public EsCommand createEsCommand() {
diff --git a/server/sonar-main/src/test/java/org/sonar/application/TestAppState.java b/server/sonar-main/src/test/java/org/sonar/application/TestAppState.java
index 125ae867d40..27f6c4d5fb0 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/TestAppState.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/TestAppState.java
@@ -19,7 +19,6 @@
*/
package org.sonar.application;
-import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
@@ -28,25 +27,14 @@ import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import org.sonar.NetworkUtils;
-import org.sonar.application.cluster.ClusterAppState;
-import org.sonar.cluster.localclient.HazelcastClient;
import org.sonar.process.ProcessId;
-public class TestAppState implements ClusterAppState {
+public class TestAppState implements AppState {
private final Map<ProcessId, Boolean> localProcesses = new EnumMap<>(ProcessId.class);
private final Map<ProcessId, Boolean> remoteProcesses = new EnumMap<>(ProcessId.class);
private final List<AppStateListener> listeners = new ArrayList<>();
private final AtomicBoolean webLeaderLocked = new AtomicBoolean(false);
- private final HazelcastClient hazelcastClient;
-
- public TestAppState() {
- this(null);
- }
-
- public TestAppState(HazelcastClient hazelcastClient) {
- this.hazelcastClient = hazelcastClient;
- }
@Override
public void addListener(@Nonnull AppStateListener listener) {
@@ -100,12 +88,6 @@ public class TestAppState implements ClusterAppState {
}
@Override
- public HazelcastClient getHazelcastClient() {
- Preconditions.checkState(hazelcastClient != null, "An HazelcastClient should be provided when testing in cluster mode");
- return hazelcastClient;
- }
-
- @Override
public void close() {
// nothing to do
}
diff --git a/server/sonar-main/src/test/java/org/sonar/application/TestClusterAppState.java b/server/sonar-main/src/test/java/org/sonar/application/TestClusterAppState.java
new file mode 100644
index 00000000000..94d5e664f5c
--- /dev/null
+++ b/server/sonar-main/src/test/java/org/sonar/application/TestClusterAppState.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import org.sonar.application.cluster.ClusterAppState;
+import org.sonar.cluster.localclient.HazelcastClient;
+
+public class TestClusterAppState extends TestAppState implements ClusterAppState {
+ private final HazelcastClient hazelcastClient;
+
+ public TestClusterAppState(HazelcastClient hazelcastClient) {
+ this.hazelcastClient = hazelcastClient;
+ }
+
+ @Override
+ public HazelcastClient getHazelcastClient() {
+ return hazelcastClient;
+ }
+}
diff --git a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppStateClusterImplTest.java b/server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterAppStateImplTest.java
index 7e8610c5e80..240db1cbfe0 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppStateClusterImplTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterAppStateImplTest.java
@@ -46,7 +46,7 @@ import static org.sonar.cluster.ClusterObjectKeys.CLUSTER_NAME;
import static org.sonar.cluster.ClusterObjectKeys.SONARQUBE_VERSION;
import static org.sonar.cluster.ClusterProperties.CLUSTER_ENABLED;
-public class AppStateClusterImplTest {
+public class ClusterAppStateImplTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -62,14 +62,14 @@ public class AppStateClusterImplTest {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Cluster is not enabled on this instance");
- new AppStateClusterImpl(settings);
+ new ClusterAppStateImpl(settings);
}
@Test
public void tryToLockWebLeader_returns_true_only_for_the_first_call() throws Exception {
TestAppSettings settings = newApplicationSettings();
- try (AppStateClusterImpl underTest = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(settings)) {
assertThat(underTest.tryToLockWebLeader()).isEqualTo(true);
assertThat(underTest.tryToLockWebLeader()).isEqualTo(false);
}
@@ -81,9 +81,9 @@ public class AppStateClusterImplTest {
TestAppSettings settings = newApplicationSettings();
Logger logger = mock(Logger.class);
- AppStateClusterImpl.setLogger(logger);
+ ClusterAppStateImpl.setLogger(logger);
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
verify(logger).info(
eq("Joined a SonarQube cluster that contains the following hosts : [{}]"),
anyString());
@@ -93,7 +93,7 @@ public class AppStateClusterImplTest {
@Test
public void test_listeners() throws InterruptedException {
AppStateListener listener = mock(AppStateListener.class);
- try (AppStateClusterImpl underTest = new AppStateClusterImpl(newApplicationSettings())) {
+ try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(newApplicationSettings())) {
underTest.addListener(listener);
underTest.setOperational(ProcessId.ELASTICSEARCH);
@@ -110,7 +110,7 @@ public class AppStateClusterImplTest {
public void registerSonarQubeVersion_publishes_version_on_first_call() {
TestAppSettings settings = newApplicationSettings();
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
appStateCluster.registerSonarQubeVersion("6.4.1.5");
HazelcastInstance hzInstance = createHazelcastClient(appStateCluster);
@@ -126,7 +126,7 @@ public class AppStateClusterImplTest {
TestAppSettings settings = newApplicationSettings();
String clusterName = randomAlphanumeric(20);
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
appStateCluster.registerClusterName(clusterName);
HazelcastInstance hzInstance = createHazelcastClient(appStateCluster);
@@ -141,7 +141,7 @@ public class AppStateClusterImplTest {
public void reset_throws_always_ISE() {
TestAppSettings settings = newApplicationSettings();
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("state reset is not supported in cluster mode");
appStateCluster.reset();
@@ -153,7 +153,7 @@ public class AppStateClusterImplTest {
// Now launch an instance that try to be part of the hzInstance cluster
TestAppSettings settings = newApplicationSettings();
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
// Register first version
appStateCluster.registerSonarQubeVersion("1.0.0");
@@ -170,7 +170,7 @@ public class AppStateClusterImplTest {
// Now launch an instance that try to be part of the hzInstance cluster
TestAppSettings settings = newApplicationSettings();
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
// Register first version
appStateCluster.registerClusterName("goodClusterName");
diff --git a/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTest.java b/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTest.java
index 702359aa2ee..11a77839f35 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTest.java
@@ -244,7 +244,7 @@ public class HazelcastClusterTest {
settings.set(CLUSTER_NODE_HOST, InetAddress.getLoopbackAddress().getHostAddress());
AppStateListener listener = mock(AppStateListener.class);
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(settings)) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(settings)) {
appStateCluster.addListener(listener);
HazelcastInstance hzInstance = createHazelcastClient(appStateCluster.getHazelcastCluster());
@@ -277,18 +277,17 @@ public class HazelcastClusterTest {
memoryAppender.start();
lc.getLogger("com.hazelcast").addAppender(memoryAppender);
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(newApplicationSettings())) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(newApplicationSettings())) {
}
assertThat(memoryAppender.events).isNotEmpty();
memoryAppender.events.stream().forEach(
- e -> assertThat(e.getLoggerName()).startsWith("com.hazelcast")
- );
+ e -> assertThat(e.getLoggerName()).startsWith("com.hazelcast"));
}
@Test
public void removing_the_last_application_node_must_clear_web_leader() throws InterruptedException {
- try (AppStateClusterImpl appStateCluster = new AppStateClusterImpl(newSearchSettings())) {
+ try (ClusterAppStateImpl appStateCluster = new ClusterAppStateImpl(newSearchSettings())) {
TestAppSettings appSettings = newApplicationSettings();
appSettings.set(CLUSTER_HOSTS, appStateCluster.getHazelcastCluster().getLocalEndPoint());
appSettings.set(CLUSTER_NODE_PORT, "9004");
diff --git a/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTestHelper.java b/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTestHelper.java
index b1a03efba0b..0345c5bf672 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTestHelper.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/cluster/HazelcastClusterTestHelper.java
@@ -51,7 +51,7 @@ public class HazelcastClusterTestHelper {
return hazelcastInstance;
}
- static HazelcastInstance createHazelcastClient(AppStateClusterImpl appStateCluster) {
+ static HazelcastInstance createHazelcastClient(ClusterAppStateImpl appStateCluster) {
return createHazelcastClient(appStateCluster.getHazelcastCluster());
}
diff --git a/server/sonar-main/src/test/java/org/sonar/application/health/SearchNodeHealthProviderTest.java b/server/sonar-main/src/test/java/org/sonar/application/health/SearchNodeHealthProviderTest.java
index 0bc57d85f38..ae3084db84f 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/health/SearchNodeHealthProviderTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/health/SearchNodeHealthProviderTest.java
@@ -19,8 +19,218 @@
*/
package org.sonar.application.health;
-// TODO implement UT for SearchNodeHealthProviderTest when Daniel and Eric's branch in merged into master
+import java.util.Properties;
+import java.util.Random;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.NetworkUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.application.cluster.ClusterAppState;
+import org.sonar.cluster.ClusterProperties;
+import org.sonar.cluster.health.NodeHealth;
+import org.sonar.process.ProcessId;
+import org.sonar.process.Props;
+
+import static java.lang.String.valueOf;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_HOST;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_NAME;
+import static org.sonar.cluster.ClusterProperties.CLUSTER_NODE_PORT;
+
public class SearchNodeHealthProviderTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final Random random = new Random();
+ private System2 system2 = mock(System2.class);
+ private NetworkUtils networkUtils = mock(NetworkUtils.class);
+ private ClusterAppState clusterAppState = mock(ClusterAppState.class);
+
+ @Test
+ public void constructor_throws_IAE_if_property_node_name_is_not_set() {
+ Props props = new Props(new Properties());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Missing property: sonar.cluster.node.name");
+
+ new SearchNodeHealthProvider(props, system2, clusterAppState, networkUtils);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_NetworkUtils_getHostname_returns_null_and_property_is_not_set() {
+ Properties properties = new Properties();
+ properties.put(ClusterProperties.CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ Props props = new Props(properties);
+
+ expectedException.expect(NullPointerException.class);
+
+ new SearchNodeHealthProvider(props, system2, clusterAppState, networkUtils);
+ }
+
+ @Test
+ public void constructor_throws_IAE_if_property_node_port_is_not_set() {
+ Properties properties = new Properties();
+ properties.put(ClusterProperties.CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
+ Props props = new Props(properties);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Missing property: sonar.cluster.node.port");
+
+ new SearchNodeHealthProvider(props, system2, clusterAppState, networkUtils);
+ }
+
+ @Test
+ public void constructor_throws_FormatException_if_property_node_port_is_not_an_integer() {
+ String port = randomAlphanumeric(3);
+ Properties properties = new Properties();
+ properties.put(ClusterProperties.CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ properties.put(ClusterProperties.CLUSTER_NODE_PORT, port);
+ when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
+ Props props = new Props(properties);
+
+ expectedException.expect(NumberFormatException.class);
+ expectedException.expectMessage("For input string: \"" + port + "\"");
+
+ new SearchNodeHealthProvider(props, system2, clusterAppState, networkUtils);
+ }
+
+ @Test
+ public void get_returns_name_and_port_from_properties_at_constructor_time() {
+ String name = randomAlphanumeric(3);
+ int port = 1 + random.nextInt(4);
+ Properties properties = new Properties();
+ properties.setProperty(CLUSTER_NODE_NAME, name);
+ properties.setProperty(CLUSTER_NODE_PORT, valueOf(port));
+ when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
+ when(system2.now()).thenReturn(1L + random.nextInt(87));
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+
+ assertThat(nodeHealth.getDetails().getName()).isEqualTo(name);
+ assertThat(nodeHealth.getDetails().getPort()).isEqualTo(port);
+
+ // change values in properties
+ properties.setProperty(CLUSTER_NODE_NAME, randomAlphanumeric(6));
+ properties.setProperty(CLUSTER_NODE_PORT, valueOf(1 + random.nextInt(99)));
+
+ NodeHealth newNodeHealth = underTest.get();
+
+ assertThat(newNodeHealth.getDetails().getName()).isEqualTo(name);
+ assertThat(newNodeHealth.getDetails().getPort()).isEqualTo(port);
+ }
+
+ @Test
+ public void get_returns_host_from_property_if_set_at_constructor_time() {
+ String host = randomAlphanumeric(55);
+ Properties properties = new Properties();
+ properties.setProperty(CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ properties.setProperty(CLUSTER_NODE_PORT, valueOf(1 + random.nextInt(4)));
+ properties.setProperty(CLUSTER_NODE_HOST, host);
+ when(system2.now()).thenReturn(1L + random.nextInt(87));
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+
+ assertThat(nodeHealth.getDetails().getHost()).isEqualTo(host);
+
+ // change now
+ properties.setProperty(CLUSTER_NODE_HOST, randomAlphanumeric(96));
+
+ NodeHealth newNodeHealth = underTest.get();
+
+ assertThat(newNodeHealth.getDetails().getHost()).isEqualTo(host);
+ }
+
+ @Test
+ public void get_returns_host_from_NetworkUtils_getHostname_if_property_is_not_set_at_constructor_time() {
+ getReturnsHostFromNetworkUtils(null);
+ }
+
+ @Test
+ public void get_returns_host_from_NetworkUtils_getHostname_if_property_is_empty_at_constructor_time() {
+ getReturnsHostFromNetworkUtils(random.nextBoolean() ? "" : " ");
+ }
+
+ private void getReturnsHostFromNetworkUtils(@Nullable String hostPropertyValue) {
+ String host = randomAlphanumeric(34);
+ Properties properties = new Properties();
+ properties.setProperty(CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ properties.setProperty(CLUSTER_NODE_PORT, valueOf(1 + random.nextInt(4)));
+ if (hostPropertyValue != null) {
+ properties.setProperty(CLUSTER_NODE_HOST, hostPropertyValue);
+ }
+ when(system2.now()).thenReturn(1L + random.nextInt(87));
+ when(networkUtils.getHostname()).thenReturn(host);
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+
+ assertThat(nodeHealth.getDetails().getHost()).isEqualTo(host);
+
+ // change now
+ when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(96));
+
+ NodeHealth newNodeHealth = underTest.get();
+
+ assertThat(newNodeHealth.getDetails().getHost()).isEqualTo(host);
+ }
+
+ @Test
+ public void get_returns_started_from_System2_now_at_constructor_time() {
+ Properties properties = new Properties();
+ long now = setRequiredPropertiesAndMocks(properties);
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+
+ assertThat(nodeHealth.getDetails().getStarted()).isEqualTo(now);
+
+ // change now
+ when(system2.now()).thenReturn(now);
+
+ NodeHealth newNodeHealth = underTest.get();
+
+ assertThat(newNodeHealth.getDetails().getStarted()).isEqualTo(now);
+ }
+
+ @Test
+ public void get_returns_status_GREEN_if_elasticsearch_process_is_operational_in_ClusterAppState() {
+ Properties properties = new Properties();
+ setRequiredPropertiesAndMocks(properties);
+ when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(true);
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+
+ assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.GREEN);
+ }
+
+ @Test
+ public void get_returns_status_RED_with_cause_if_elasticsearch_process_is_not_operational_in_ClusterAppState() {
+ Properties properties = new Properties();
+ setRequiredPropertiesAndMocks(properties);
+ when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(false);
+ SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), system2, clusterAppState, networkUtils);
+
+ NodeHealth nodeHealth = underTest.get();
+ assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.RED);
+ assertThat(nodeHealth.getCauses()).containsOnly("Elasticsearch is not operational");
+ }
+ private long setRequiredPropertiesAndMocks(Properties properties) {
+ properties.setProperty(CLUSTER_NODE_NAME, randomAlphanumeric(3));
+ properties.setProperty(CLUSTER_NODE_PORT, valueOf(1 + random.nextInt(4)));
+ long now = 1L + random.nextInt(87);
+ when(system2.now()).thenReturn(now);
+ when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
+ return now;
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java
index 9f4ec3ebe7a..43e0db25c30 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java
@@ -132,7 +132,7 @@ public class HealthCheckerImplTest {
@Test
public void checkCluster_fails_with_ISE_in_clustering_and_HealthState_is_null() {
when(webServer.isStandalone()).thenReturn(false);
- HealthCheckerImpl underTest = new HealthCheckerImpl(webServer, new NodeHealthCheck[0], new ClusterHealthCheck[0], sharedHealthState);
+ HealthCheckerImpl underTest = new HealthCheckerImpl(webServer, new NodeHealthCheck[0], new ClusterHealthCheck[0], null);
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("HealthState instance can't be null when clustering is enabled");