Browse Source

SONAR-13971 Add properties to configure ES network in DCE

tags/8.6.0.39681
Michal Duda 3 years ago
parent
commit
15bfd33b82

+ 0
- 11
server/sonar-main/src/main/java/org/sonar/application/AppStateFactory.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.application;

import com.google.common.collect.ImmutableSet;
import com.google.common.net.HostAndPort;
import java.util.Arrays;
import java.util.Set;
@@ -32,7 +31,6 @@ import org.sonar.application.es.EsConnector;
import org.sonar.application.es.EsConnectorImpl;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;
import org.sonar.process.cluster.NodeType;
import org.sonar.process.cluster.hz.HazelcastMember;
import org.sonar.process.cluster.hz.HazelcastMemberBuilder;

@@ -42,8 +40,6 @@ import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HZ_PORT;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.SEARCH_PORT;

public class AppStateFactory {
private final AppSettings settings;
@@ -73,13 +69,6 @@ public class AppStateFactory {
}

private static EsConnector createEsConnector(Props props) {
NodeType nodeType = ClusterSettings.toNodeType(props);
if (nodeType == NodeType.SEARCH) {
String host = props.nonNullValue(SEARCH_HOST.getKey());
String port = props.nonNullValue(SEARCH_PORT.getKey());
return new EsConnectorImpl(ImmutableSet.of(HostAndPort.fromParts(host, Integer.valueOf(port))));
}

String searchHosts = props.nonNullValue(CLUSTER_SEARCH_HOSTS.getKey());
Set<HostAndPort> hostAndPorts = Arrays.stream(searchHosts.split(","))
.map(HostAndPort::fromString)

+ 29
- 11
server/sonar-main/src/main/java/org/sonar/application/config/ClusterSettings.java View File

@@ -45,14 +45,15 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.sonar.process.ProcessProperties.Property.AUTH_JWT_SECRET;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ES_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_HZ_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_TYPE;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_WEB_STARTUP_LEADER;
import static org.sonar.process.ProcessProperties.Property.JDBC_URL;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.SEARCH_PORT;

public class ClusterSettings implements Consumer<Props> {

@@ -75,11 +76,6 @@ public class ClusterSettings implements Consumer<Props> {
throw new MessageException(format("Property [%s] is forbidden", CLUSTER_WEB_STARTUP_LEADER.getKey()));
}

checkNodeSpecificProperties(props);
checkCommonProperties(props);
}

private void checkNodeSpecificProperties(Props props) {
NodeType nodeType = toNodeType(props);
switch (nodeType) {
case APPLICATION:
@@ -87,24 +83,37 @@ public class ClusterSettings implements Consumer<Props> {
requireValue(props, AUTH_JWT_SECRET);
Set<AddressAndPort> hzNodes = parseHosts(CLUSTER_HZ_HOSTS, requireValue(props, CLUSTER_HZ_HOSTS));
ensureNotLoopbackAddresses(CLUSTER_HZ_HOSTS, hzNodes);
checkClusterNodeHost(props);
checkClusterSearchHosts(props);
break;
case SEARCH:
AddressAndPort searchHost = parseAndCheckHost(SEARCH_HOST, requireValue(props, SEARCH_HOST));
ensureLocalButNotLoopbackAddress(SEARCH_HOST, searchHost);
requireValue(props, SEARCH_PORT);
AddressAndPort searchHost = parseAndCheckHost(CLUSTER_NODE_SEARCH_HOST, requireValue(props, CLUSTER_NODE_SEARCH_HOST));
ensureLocalButNotLoopbackAddress(CLUSTER_NODE_SEARCH_HOST, searchHost);
AddressAndPort esHost = parseAndCheckHost(CLUSTER_NODE_ES_HOST, requireValue(props, CLUSTER_NODE_ES_HOST));
ensureLocalButNotLoopbackAddress(CLUSTER_NODE_ES_HOST, esHost);
checkClusterEsHosts(props);
break;
default:
throw new UnsupportedOperationException("Unknown value: " + nodeType);
}
}

private void checkCommonProperties(Props props) {
private void checkClusterNodeHost(Props props) {
AddressAndPort clusterNodeHost = parseAndCheckHost(CLUSTER_NODE_HOST, requireValue(props, CLUSTER_NODE_HOST));
ensureLocalButNotLoopbackAddress(CLUSTER_NODE_HOST, clusterNodeHost);
}

private void checkClusterSearchHosts(Props props) {
Set<AddressAndPort> searchHosts = parseHosts(CLUSTER_SEARCH_HOSTS, requireValue(props, CLUSTER_SEARCH_HOSTS));
ensureNotLoopbackAddresses(CLUSTER_SEARCH_HOSTS, searchHosts);
}

private void checkClusterEsHosts(Props props) {
Set<AddressAndPort> esHosts = parseHosts(CLUSTER_ES_HOSTS, requireValue(props, CLUSTER_ES_HOSTS));
ensureNotLoopbackAddresses(CLUSTER_ES_HOSTS, esHosts);
ensureEitherPortsAreProvidedOrOnlyHosts(CLUSTER_ES_HOSTS, esHosts);
}

private Set<AddressAndPort> parseHosts(Property property, String value) {
Set<AddressAndPort> res = stream(value.split(","))
.filter(Objects::nonNull)
@@ -182,6 +191,15 @@ public class ClusterSettings implements Consumer<Props> {
}
}

private static void ensureEitherPortsAreProvidedOrOnlyHosts(Property property, Set<AddressAndPort> addressAndPorts) {
Set<AddressAndPort> hostsWithoutPort = addressAndPorts.stream()
.filter(t -> !t.hasPort())
.collect(toSet());
if (!hostsWithoutPort.isEmpty() && hostsWithoutPort.size() != addressAndPorts.size()) {
throw new MessageException(format("Entries in property %s must not mix 'host:port' and 'host'. Provide hosts without port only or hosts with port only.", property.getKey()));
}
}

private static class AddressAndPort {
private static final int NO_PORT = -1;


+ 53
- 23
server/sonar-main/src/main/java/org/sonar/application/es/EsSettings.java View File

@@ -24,23 +24,33 @@ import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.MessageException;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;
import org.sonar.process.System2;

import static java.lang.String.valueOf;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ES_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_PORT;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_PORT;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.SEARCH_INITIAL_STATE_TIMEOUT;
import static org.sonar.process.ProcessProperties.Property.SEARCH_PORT;
import static org.sonar.process.ProcessProperties.Property.SEARCH_TRANSPORT_PORT;

public class EsSettings {
private static final String ES_HTTP_HOST_KEY = "http.host";
private static final String ES_HTTP_PORT_KEY = "http.port";
private static final String ES_TRANSPORT_HOST_KEY = "transport.host";
private static final String ES_TRANSPORT_PORT_KEY = "transport.port";

private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class);
private static final String STANDALONE_NODE_NAME = "sonarqube";
@@ -53,6 +63,7 @@ public class EsSettings {
private final boolean clusterEnabled;
private final String clusterName;
private final String nodeName;
private final InetAddress loopbackAddress;

public EsSettings(Props props, EsInstallation fileSystem, System2 system2) {
this.props = props;
@@ -65,6 +76,7 @@ public class EsSettings {
} else {
this.nodeName = STANDALONE_NODE_NAME;
}
this.loopbackAddress = InetAddress.getLoopbackAddress();
String esJvmOptions = system2.getenv("ES_JVM_OPTIONS");
if (esJvmOptions != null && !esJvmOptions.trim().isEmpty()) {
LOGGER.warn("ES_JVM_OPTIONS is defined but will be ignored. " +
@@ -75,9 +87,10 @@ public class EsSettings {
public Map<String, String> build() {
Map<String, String> builder = new HashMap<>();
configureFileSystem(builder);
InetAddress host = configureNetwork(builder);
configureCluster(builder, host);
configureNetwork(builder);
configureCluster(builder);
configureOthers(builder);
LOGGER.info("Elasticsearch listening on {}:{}", builder.get(ES_HTTP_HOST_KEY), builder.get(ES_HTTP_PORT_KEY));
return builder;
}

@@ -86,39 +99,49 @@ public class EsSettings {
builder.put("path.logs", fileSystem.getLogDirectory().getAbsolutePath());
}

private InetAddress configureNetwork(Map<String, String> builder) {
InetAddress host = readHost();
int httpPort = Integer.parseInt(props.nonNullValue(SEARCH_PORT.getKey()));
LOGGER.info("Elasticsearch listening on {}:{}", host, httpPort);
private void configureNetwork(Map<String, String> builder) {
InetAddress searchHost = resolveAddress(SEARCH_HOST);
int searchPort = Integer.parseInt(props.nonNullValue(SEARCH_PORT.getKey()));

// see https://github.com/lmenezes/elasticsearch-kopf/issues/195
builder.put("http.cors.enabled", valueOf(true));
builder.put("http.cors.allow-origin", "*");
builder.put("http.host", host.getHostAddress());
builder.put("http.port", valueOf(httpPort));
builder.put(ES_HTTP_HOST_KEY, searchHost.getHostAddress());
builder.put(ES_HTTP_PORT_KEY, valueOf(searchPort));

builder.put("network.host", valueOf(host.getHostAddress()));
builder.put("network.host", valueOf(searchHost.getHostAddress()));
// Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ?
builder.put("network.tcp.reuse_address", valueOf(true));

// FIXME remove definition of transport properties when Web and CE have moved to ES Rest client
int tcpPort = props.valueAsInt(SEARCH_TRANSPORT_PORT.getKey(), 9002);
builder.put("transport.port", valueOf(tcpPort));
builder.put("transport.host", valueOf(host.getHostAddress()));
int transportPort = props.valueAsInt(SEARCH_TRANSPORT_PORT.getKey(), 9002);
builder.put(ES_TRANSPORT_HOST_KEY, searchHost.getHostAddress());
builder.put(ES_TRANSPORT_PORT_KEY, valueOf(transportPort));
}

return host;
private InetAddress resolveAddress(ProcessProperties.Property prop) {
return resolveAddress(prop, null);
}

private InetAddress readHost() {
String hostProperty = props.nonNullValue(SEARCH_HOST.getKey());
private InetAddress resolveAddress(ProcessProperties.Property prop, @Nullable InetAddress defaultAddress) {
String address;
if (defaultAddress == null) {
address = props.nonNullValue(prop.getKey());
} else {
address = props.value(prop.getKey());
if (address == null) {
return defaultAddress;
}
}

try {
return InetAddress.getByName(hostProperty);
return InetAddress.getByName(address);
} catch (UnknownHostException e) {
throw new IllegalStateException("Can not resolve host [" + hostProperty + "]. Please check network settings and property " + SEARCH_HOST.getKey(), e);
throw new IllegalStateException("Can not resolve host [" + address + "]. Please check network settings and property " + prop.getKey(), e);
}
}

private void configureCluster(Map<String, String> builder, InetAddress host) {
private void configureCluster(Map<String, String> builder) {
// Default value in a standalone mode, not overridable

String initialStateTimeOut = "30s";
@@ -126,11 +149,18 @@ public class EsSettings {
if (clusterEnabled) {
initialStateTimeOut = props.value(SEARCH_INITIAL_STATE_TIMEOUT.getKey(), "120s");

int tcpPort = props.valueAsInt(SEARCH_TRANSPORT_PORT.getKey(), 9002);
builder.put("transport.port", valueOf(tcpPort));
builder.put("transport.host", valueOf(host.getHostAddress()));
String nodeSearchHost = resolveAddress(CLUSTER_NODE_SEARCH_HOST, loopbackAddress).getHostAddress();
int nodeSearchPort = props.valueAsInt(CLUSTER_NODE_SEARCH_PORT.getKey(), 9001);
builder.put(ES_HTTP_HOST_KEY, nodeSearchHost);
builder.put(ES_HTTP_PORT_KEY, valueOf(nodeSearchPort));

String nodeTransportHost = resolveAddress(CLUSTER_NODE_ES_HOST, loopbackAddress).getHostAddress();
int nodeTransportPort = props.valueAsInt(CLUSTER_NODE_ES_PORT.getKey(), 9002);
builder.put(ES_TRANSPORT_HOST_KEY, nodeTransportHost);
builder.put(ES_TRANSPORT_PORT_KEY, valueOf(nodeTransportPort));
builder.put("network.host", nodeTransportHost);

String hosts = props.value(CLUSTER_SEARCH_HOSTS.getKey(), "");
String hosts = props.value(CLUSTER_ES_HOSTS.getKey(), loopbackAddress.getHostAddress());
LOGGER.info("Elasticsearch cluster enabled. Connect to hosts [{}]", hosts);
builder.put("discovery.seed_hosts", hosts);
builder.put("cluster.initial_master_nodes", hosts);

+ 35
- 34
server/sonar-main/src/test/java/org/sonar/application/config/ClusterSettingsLoopbackTest.java View File

@@ -23,31 +23,31 @@ import java.net.InetAddress;
import java.util.Optional;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.process.MessageException;
import org.sonar.process.NetworkUtils;
import org.sonar.process.NetworkUtilsImpl;
import org.sonar.process.Props;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assume.assumeThat;
import static org.mockito.Mockito.spy;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ES_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_HZ_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_TYPE;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.JDBC_URL;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;

public class ClusterSettingsLoopbackTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();
private final InetAddress loopback = InetAddress.getLoopbackAddress();
private final NetworkUtils network = spy(NetworkUtilsImpl.INSTANCE);

private InetAddress loopback = InetAddress.getLoopbackAddress();
private InetAddress nonLoopbackLocal;
private NetworkUtils network = spy(NetworkUtilsImpl.INSTANCE);

@Before
public void setUp() {
@@ -58,37 +58,39 @@ public class ClusterSettingsLoopbackTest {
}

@Test
public void ClusterSettings_throws_MessageException_if_host_of_search_node_is_loopback() {
verifySearchFailureIfLoopback(CLUSTER_NODE_HOST.getKey());
verifySearchFailureIfLoopback(CLUSTER_SEARCH_HOSTS.getKey());
verifySearchFailureIfLoopback(CLUSTER_HZ_HOSTS.getKey());
verifySearchFailureIfLoopback(SEARCH_HOST.getKey());
}
public void ClusterSettings_throws_MessageException_if_es_http_host_of_search_node_is_loopback() {
TestAppSettings settings = newSettingsForSearchNode();
settings.set(CLUSTER_NODE_SEARCH_HOST.getKey(), loopback.getHostAddress());
Props props = settings.getProps();
ClusterSettings clusterSettings = new ClusterSettings(network);

@Test
public void ClusterSettings_throws_MessageException_if_host_of_app_node_is_loopback() {
verifyAppFailureIfLoopback(CLUSTER_NODE_HOST.getKey());
verifyAppFailureIfLoopback(CLUSTER_SEARCH_HOSTS.getKey());
verifyAppFailureIfLoopback(CLUSTER_HZ_HOSTS.getKey());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Property " + CLUSTER_NODE_SEARCH_HOST.getKey() + " must be a local non-loopback address: " + loopback.getHostAddress());
}

private void verifySearchFailureIfLoopback(String propertyKey) {
@Test
public void ClusterSettings_throws_MessageException_if_es_transport_host_of_search_node_is_loopback() {
TestAppSettings settings = newSettingsForSearchNode();
verifyFailure(propertyKey, settings);
}
settings.set(CLUSTER_NODE_ES_HOST.getKey(), loopback.getHostAddress());
Props props = settings.getProps();
ClusterSettings clusterSettings = new ClusterSettings(network);

private void verifyAppFailureIfLoopback(String propertyKey) {
TestAppSettings settings = newSettingsForAppNode();
verifyFailure(propertyKey, settings);
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Property " + CLUSTER_NODE_ES_HOST.getKey() + " must be a local non-loopback address: " + loopback.getHostAddress());
}

private void verifyFailure(String propertyKey, TestAppSettings settings) {
settings.set(propertyKey, loopback.getHostAddress());

expectedException.expect(MessageException.class);
expectedException.expectMessage("Property " + propertyKey + " must be a local non-loopback address: " + loopback.getHostAddress());
@Test
public void ClusterSettings_throws_MessageException_if_host_of_app_node_is_loopback() {
TestAppSettings settings = newSettingsForAppNode();
settings.set(CLUSTER_NODE_HOST.getKey(), loopback.getHostAddress());
Props props = settings.getProps();
ClusterSettings clusterSettings = new ClusterSettings(network);

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Property " + CLUSTER_NODE_HOST.getKey() + " must be a local non-loopback address: " + loopback.getHostAddress());
}

private TestAppSettings newSettingsForAppNode() {
@@ -106,9 +108,8 @@ public class ClusterSettingsLoopbackTest {
return new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_HOST.getKey(), nonLoopbackLocal.getHostAddress())
.set(CLUSTER_HZ_HOSTS.getKey(), nonLoopbackLocal.getHostAddress())
.set(CLUSTER_SEARCH_HOSTS.getKey(), nonLoopbackLocal.getHostAddress())
.set(SEARCH_HOST.getKey(), nonLoopbackLocal.getHostAddress());
.set(CLUSTER_ES_HOSTS.getKey(), nonLoopbackLocal.getHostAddress())
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), nonLoopbackLocal.getHostAddress())
.set(CLUSTER_NODE_ES_HOST.getKey(), nonLoopbackLocal.getHostAddress());
}
}

+ 190
- 70
server/sonar-main/src/test/java/org/sonar/application/config/ClusterSettingsTest.java View File

@@ -27,18 +27,18 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.sonar.process.MessageException;
import org.sonar.process.NetworkUtils;
import org.sonar.process.Props;

import static com.google.common.collect.ImmutableList.of;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.reset;
@@ -47,20 +47,19 @@ import static org.sonar.process.ProcessId.COMPUTE_ENGINE;
import static org.sonar.process.ProcessId.ELASTICSEARCH;
import static org.sonar.process.ProcessId.WEB_SERVER;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ES_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_HZ_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_TYPE;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.JDBC_URL;
import static org.sonar.process.ProcessProperties.Property.SEARCH_HOST;

@RunWith(DataProviderRunner.class)
public class ClusterSettingsTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

private NetworkUtils network = Mockito.mock(NetworkUtils.class);
private final NetworkUtils network = Mockito.mock(NetworkUtils.class);

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
@@ -97,11 +96,12 @@ public class ClusterSettingsTest {
public void accept_throws_MessageException_if_no_node_type_is_configured() {
TestAppSettings settings = new TestAppSettings();
settings.set(CLUSTER_ENABLED.getKey(), "true");
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

expectedException.expect(MessageException.class);
expectedException.expectMessage("Property sonar.cluster.node.type is mandatory");

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Property sonar.cluster.node.type is mandatory");
}

@Test
@@ -109,11 +109,12 @@ public class ClusterSettingsTest {
TestAppSettings settings = new TestAppSettings();
settings.set(CLUSTER_ENABLED.getKey(), "true");
settings.set(CLUSTER_NODE_TYPE.getKey(), "bla");
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

expectedException.expect(MessageException.class);
expectedException.expectMessage("Invalid value for property sonar.cluster.node.type: [bla], only [application, search] are allowed");

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Invalid value for property sonar.cluster.node.type: [bla], only [application, search] are allowed");
}

@Test
@@ -121,11 +122,12 @@ public class ClusterSettingsTest {
public void accept_throws_MessageException_if_internal_property_for_startup_leader_is_configured(String host) {
TestAppSettings settings = newSettingsForAppNode(host);
settings.set("sonar.cluster.web.startupLeader", "true");
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

expectedException.expect(MessageException.class);
expectedException.expectMessage("Property [sonar.cluster.web.startupLeader] is forbidden");

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Property [sonar.cluster.web.startupLeader] is forbidden");
}

@Test
@@ -143,11 +145,12 @@ public class ClusterSettingsTest {
public void accept_throws_MessageException_if_h2_on_application_node(String host) {
TestAppSettings settings = newSettingsForAppNode(host);
settings.set("sonar.jdbc.url", "jdbc:h2:mem");
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

expectedException.expect(MessageException.class);
expectedException.expectMessage("Embedded database is not supported in cluster mode");

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Embedded database is not supported in cluster mode");
}

@Test
@@ -167,11 +170,12 @@ public class ClusterSettingsTest {
public void accept_throws_MessageException_on_application_node_if_default_jdbc_url(String host) {
TestAppSettings settings = newSettingsForAppNode(host);
settings.clearProperty(JDBC_URL.getKey());
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

expectedException.expect(MessageException.class);
expectedException.expectMessage("Embedded database is not supported in cluster mode");

new ClusterSettings(network).accept(settings.getProps());
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Embedded database is not supported in cluster mode");
}

@Test
@@ -226,18 +230,54 @@ public class ClusterSettingsTest {

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_searchHost_is_missing(String host) {
public void accept_throws_MessageException_if_clusterNodeEsHost_is_missing(String host) {
TestAppSettings settings = newSettingsForSearchNode(host);
settings.clearProperty(SEARCH_HOST.getKey());
assertThatPropertyIsMandatory(settings, SEARCH_HOST.getKey());
String searchHost = "search_host";
mockValidHost(searchHost);
mockLocalNonLoopback(searchHost);
settings.set(CLUSTER_NODE_SEARCH_HOST.getKey(), searchHost);

settings.clearProperty(CLUSTER_NODE_ES_HOST.getKey());
assertThatPropertyIsMandatory(settings, CLUSTER_NODE_ES_HOST.getKey());
}

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_searchHost_is_empty(String host) {
public void accept_throws_MessageException_if_clusterNodeSearchHost_is_missing(String host) {
TestAppSettings settings = newSettingsForSearchNode(host);
settings.set(SEARCH_HOST.getKey(), "");
assertThatPropertyIsMandatory(settings, SEARCH_HOST.getKey());
String esHost = "es_host";
mockValidHost(esHost);
mockLocalNonLoopback(esHost);
settings.set(CLUSTER_NODE_ES_HOST.getKey(), esHost);

settings.clearProperty(CLUSTER_NODE_SEARCH_HOST.getKey());
assertThatPropertyIsMandatory(settings, CLUSTER_NODE_SEARCH_HOST.getKey());
}

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_sonarClusterNodeSearchHost_is_empty(String host) {
TestAppSettings settings = newSettingsForSearchNode(host);
settings.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "");
String esHost = "es_host";
mockValidHost(esHost);
mockLocalNonLoopback(esHost);
settings.set(CLUSTER_NODE_ES_HOST.getKey(), esHost);

assertThatPropertyIsMandatory(settings, CLUSTER_NODE_SEARCH_HOST.getKey());
}

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_sonarClusterNodeEsHost_is_empty(String host) {
TestAppSettings settings = newSettingsForSearchNode(host);
settings.set(CLUSTER_NODE_ES_HOST.getKey(), "");
String searchHost = "search_host";
mockValidHost(searchHost);
mockLocalNonLoopback(searchHost);
settings.set(CLUSTER_NODE_SEARCH_HOST.getKey(), searchHost);

assertThatPropertyIsMandatory(settings, CLUSTER_NODE_ES_HOST.getKey());
}

@Test
@@ -250,22 +290,22 @@ public class ClusterSettingsTest {

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_clusterSearchHosts_is_missing(String host) {
public void accept_throws_MessageException_if_clusterEsHosts_is_missing(String host) {
mockValidHost(host);
mockLocalNonLoopback(host);
TestAppSettings settings = newSettingsForSearchNode(host);
settings.clearProperty(CLUSTER_SEARCH_HOSTS.getKey());
assertThatPropertyIsMandatory(settings, CLUSTER_SEARCH_HOSTS.getKey());
settings.clearProperty(CLUSTER_ES_HOSTS.getKey());
assertThatPropertyIsMandatory(settings, CLUSTER_ES_HOSTS.getKey());
}

@Test
@UseDataProvider("validIPv4andIPv6Addresses")
public void accept_throws_MessageException_if_clusterSearchHosts_is_empty(String host) {
public void accept_throws_MessageException_if_clusterEsHosts_is_empty(String host) {
mockValidHost(host);
mockLocalNonLoopback(host);
TestAppSettings settings = newSettingsForSearchNode(host);
settings.set(CLUSTER_SEARCH_HOSTS.getKey(), "");
assertThatPropertyIsMandatory(settings, CLUSTER_SEARCH_HOSTS.getKey());
settings.set(CLUSTER_ES_HOSTS.getKey(), "");
assertThatPropertyIsMandatory(settings, CLUSTER_ES_HOSTS.getKey());
}

@Test
@@ -323,21 +363,22 @@ public class ClusterSettingsTest {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_HOST.getKey(), "hz_host")
.set(CLUSTER_SEARCH_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(SEARCH_HOST.getKey(), "search_host");
.set(CLUSTER_ES_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "search_host");

verifyHostIsChecked(settings, of("hz_host"), "Address in property sonar.cluster.node.host is not a valid address: hz_host");
verifyHostIsChecked(settings, of("remote_search_host_1"), "Address in property sonar.cluster.search.hosts is not a valid address: remote_search_host_1");
verifyHostIsChecked(settings, of("remote_search_host_2"), "Address in property sonar.cluster.search.hosts is not a valid address: remote_search_host_2");
verifyHostIsChecked(settings, of("search_host"), "Address in property sonar.search.host is not a valid address: search_host");
verifyHostIsChecked(settings, of("remote_search_host_1"), "Address in property sonar.cluster.es.hosts is not a valid address: remote_search_host_1");
verifyHostIsChecked(settings, of("remote_search_host_2"), "Address in property sonar.cluster.es.hosts is not a valid address: remote_search_host_2");
verifyHostIsChecked(settings, of("search_host"), "Address in property sonar.cluster.node.search.host is not a valid address: search_host");
}

private void verifyHostIsChecked(TestAppSettings settings, Collection<String> invalidHosts, String expectedMessage) {
reset(network);
mockAllHostsValidBut(invalidHosts);
mockLocalNonLoopback("hz_host", "search_host");
assertThatThrownBy(() -> new ClusterSettings(network).accept(settings.getProps()))
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage(expectedMessage);
}
@@ -371,26 +412,28 @@ public class ClusterSettingsTest {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_HOST.getKey(), "hz_host")
.set(CLUSTER_SEARCH_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(SEARCH_HOST.getKey(), "search_host");
verifyLoopbackChecked(settings, of("hz_host"), "Property sonar.cluster.node.host must be a local non-loopback address: hz_host");
verifyLoopbackChecked(settings, of("search_host"), "Property sonar.search.host must be a local non-loopback address: search_host");
verifyLoopbackChecked(settings, of("remote_search_host_1"), "Property sonar.cluster.search.hosts must not contain a loopback address: remote_search_host_1");
verifyLoopbackChecked(settings, of("remote_search_host_2"), "Property sonar.cluster.search.hosts must not contain a loopback address: remote_search_host_2");
.set(CLUSTER_ES_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "transport_host");
verifyLoopbackChecked(settings, of("search_host"), "Property sonar.cluster.node.search.host must be a local non-loopback address: search_host");
verifyLoopbackChecked(settings, of("transport_host"), "Property sonar.cluster.node.es.host must be a local non-loopback address: transport_host");
verifyLoopbackChecked(settings, of("remote_search_host_1"), "Property sonar.cluster.es.hosts must not contain a loopback address: remote_search_host_1");
verifyLoopbackChecked(settings, of("remote_search_host_2"), "Property sonar.cluster.es.hosts must not contain a loopback address: remote_search_host_2");
verifyLoopbackChecked(settings,
of("remote_search_host_1", "remote_search_host_2"),
"Property sonar.cluster.search.hosts must not contain a loopback address: remote_search_host_1, remote_search_host_2");
"Property sonar.cluster.es.hosts must not contain a loopback address: remote_search_host_1, remote_search_host_2");
}

private void verifyLoopbackChecked(TestAppSettings settings, Collection<String> hosts, String expectedMessage) {
reset(network);
mockAllHostsValid();
mockLocalNonLoopback("hz_host", "search_host");
mockLocalNonLoopback("hz_host", "search_host", "transport_host");
// will overwrite above move if necessary
hosts.forEach(this::mockLoopback);
assertThatThrownBy(() -> new ClusterSettings(network).accept(settings.getProps()))
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage(expectedMessage);
}
@@ -414,12 +457,11 @@ public class ClusterSettingsTest {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_HOST.getKey(), "hz_host")
.set(CLUSTER_SEARCH_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(SEARCH_HOST.getKey(), "search_host");
.set(CLUSTER_ES_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "search_host");

verifyLocalChecked(settings, "hz_host", "Property sonar.cluster.node.host must be a local non-loopback address: hz_host");
verifyLocalChecked(settings, "search_host", "Property sonar.search.host must be a local non-loopback address: search_host");
verifyLocalChecked(settings, "search_host", "Property sonar.cluster.node.search.host must be a local non-loopback address: search_host");
}

private void verifyLocalChecked(TestAppSettings settings, String host, String expectedMessage) {
@@ -429,11 +471,87 @@ public class ClusterSettingsTest {
// will overwrite above move if necessary
mockAllNonLoopback();
mockNonLocal(host);
assertThatThrownBy(() -> new ClusterSettings(network).accept(settings.getProps()))
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage(expectedMessage);
}

@Test
public void accept_hosts_only_or_hosts_and_ports_only_in_property_SONAR_CLUSTER_ES_HOSTS_of_search_node() {
mockAllHostsValid();
mockLocalNonLoopback("search_host", "transport_host");

verifyAllHostsWithPortsOrAllHostsWithoutPortsIsValid(CLUSTER_ES_HOSTS.getKey(), "remote_search_host_1, remote_search_host_2");
verifyAllHostsWithPortsOrAllHostsWithoutPortsIsValid(CLUSTER_ES_HOSTS.getKey(), "remote_search_host_1:9001, remote_search_host_2:9001");
}

private void verifyAllHostsWithPortsOrAllHostsWithoutPortsIsValid(String propertyKey, String searchPropertyValue) {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "transport_host")
.set(propertyKey, searchPropertyValue);
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

assertThatCode(() -> clusterSettings.accept(props))
.doesNotThrowAnyException();
}

@Test
public void accept_any_properties_configuration_in_SONAR_CLUSTER_SEARCH_HOSTS_of_search_node() {
mockAllHostsValid();
mockLocalNonLoopback("search_host", "transport_host");

verifyAnyHostsConfigurationIsValid("remote_search_host_1, remote_search_host_2");
verifyAnyHostsConfigurationIsValid("remote_search_host_1:9001, remote_search_host_2:9001");
verifyAnyHostsConfigurationIsValid("remote_search_host_1, remote_search_host_2:9001");
verifyAnyHostsConfigurationIsValid("remote_search_host_1, remote_search_host_2");
}

private void verifyAnyHostsConfigurationIsValid(String searchPropertyValue) {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_ES_HOSTS.getKey(), "transport_host,transport_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "transport_host")
.set(CLUSTER_SEARCH_HOSTS.getKey(), searchPropertyValue);
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

assertThatCode(() -> clusterSettings.accept(props))
.doesNotThrowAnyException();
}

@Test
public void ensure_no_mixed_settings_in_ES_HOSTS_in_properties_of_SEARCH_node() {
mockAllHostsValid();
mockLocalNonLoopback("hz_host", "search_host", "transport_host");

verifyPortsAreCheckedOnEsNode("remote_search_host_1,remote_search_host_2:9001");
verifyPortsAreCheckedOnEsNode("remote_search_host_1:9002, remote_search_host_2");
}

private void verifyPortsAreCheckedOnEsNode(String searchPropertyValue) {
TestAppSettings settings = new TestAppSettings()
.set(CLUSTER_ENABLED.getKey(), "true")
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "search_host")
.set(CLUSTER_NODE_ES_HOST.getKey(), "transport_host")
.set(CLUSTER_ES_HOSTS.getKey(), searchPropertyValue);
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();

assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage("Entries in property sonar.cluster.es.hosts must not mix 'host:port' and 'host'. Provide hosts without port only or hosts with port only.");
}

private void mockAllNonLoopback() {
when(network.isLoopback(anyString())).thenReturn(false);
}
@@ -485,10 +603,11 @@ public class ClusterSettingsTest {
}

private void assertThatPropertyIsMandatory(TestAppSettings settings, String key) {
expectedException.expect(MessageException.class);
expectedException.expectMessage(format("Property %s is mandatory", key));

new ClusterSettings(network).accept(settings.getProps());
ClusterSettings clusterSettings = new ClusterSettings(network);
Props props = settings.getProps();
assertThatThrownBy(() -> clusterSettings.accept(props))
.isInstanceOf(MessageException.class)
.hasMessage(format("Property %s is mandatory", key));
}

private TestAppSettings newSettingsForAppNode(String host) {
@@ -508,8 +627,9 @@ public class ClusterSettingsTest {
.set(CLUSTER_NODE_TYPE.getKey(), "search")
.set(CLUSTER_NODE_HOST.getKey(), host)
.set(CLUSTER_HZ_HOSTS.getKey(), host)
.set(CLUSTER_SEARCH_HOSTS.getKey(), host + ":9001")
.set(SEARCH_HOST.getKey(), host);
.set(CLUSTER_ES_HOSTS.getKey(), host + ":9001")
.set(CLUSTER_NODE_SEARCH_HOST.getKey(), host)
.set(CLUSTER_NODE_ES_HOST.getKey(), host);
}

}

+ 57
- 29
server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java View File

@@ -29,10 +29,10 @@ import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.sonar.application.logging.ListAppender;
@@ -43,14 +43,19 @@ import org.sonar.process.ProcessProperties.Property;
import org.sonar.process.Props;
import org.sonar.process.System2;

import static java.util.Optional.ofNullable;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_ES_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_ES_PORT;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_SEARCH_HOSTS;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_HOST;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_SEARCH_PORT;
import static org.sonar.process.ProcessProperties.Property.PATH_DATA;
import static org.sonar.process.ProcessProperties.Property.PATH_HOME;
import static org.sonar.process.ProcessProperties.Property.PATH_LOGS;
@@ -67,11 +72,10 @@ public class EsSettingsTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
public ExpectedException expectedException = ExpectedException.none();

private ListAppender listAppender;

private System2 system = mock(System2.class);
private final System2 system = mock(System2.class);

@After
public void tearDown() {
@@ -166,6 +170,10 @@ public class EsSettingsTest {
Props props = new Props(new Properties());
props.set(SEARCH_PORT.getKey(), "1234");
props.set(SEARCH_HOST.getKey(), "127.0.0.1");
props.set(CLUSTER_NODE_SEARCH_HOST.getKey(), "127.0.0.1");
props.set(CLUSTER_NODE_SEARCH_PORT.getKey(), "1234");
props.set(CLUSTER_NODE_ES_HOST.getKey(), "127.0.0.1");
props.set(CLUSTER_NODE_ES_PORT.getKey(), "1234");
props.set(PATH_HOME.getKey(), homeDir.getAbsolutePath());
props.set(PATH_DATA.getKey(), temp.newFolder().getAbsolutePath());
props.set(PATH_TEMP.getKey(), temp.newFolder().getAbsolutePath());
@@ -177,8 +185,9 @@ public class EsSettingsTest {
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), system);

Map<String, String> generated = esSettings.build();
assertThat(generated.get("cluster.name")).isEqualTo("sonarqube-1");
assertThat(generated.get("node.name")).isEqualTo("node-1");
assertThat(generated)
.containsEntry("cluster.name", "sonarqube-1")
.containsEntry("node.name", "node-1");
}

@Test
@@ -212,7 +221,7 @@ public class EsSettingsTest {
props.set(PATH_LOGS.getKey(), temp.newFolder().getAbsolutePath());
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), system);
Map<String, String> generated = esSettings.build();
assertThat(generated.get("node.name")).isEqualTo("sonarqube");
assertThat(generated).containsEntry("node.name", "sonarqube");
}

@Test
@@ -231,29 +240,32 @@ public class EsSettingsTest {
EsSettings underTest = new EsSettings(minProps(new Random().nextBoolean()), mockedEsInstallation, system);

Map<String, String> generated = underTest.build();
assertThat(generated.get("path.data")).isEqualTo(data.getPath());
assertThat(generated.get("path.logs")).isEqualTo(log.getPath());
assertThat(generated)
.containsEntry("path.data", data.getPath())
.containsEntry("path.logs", log.getPath());
assertThat(generated.get("path.conf")).isNull();
}

@Test
public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
Props props = minProps(CLUSTER_ENABLED);
props.set(CLUSTER_SEARCH_HOSTS.getKey(), "1.2.3.4:9000,1.2.3.5:8080");
props.set(CLUSTER_ES_HOSTS.getKey(), "1.2.3.4:9000,1.2.3.5:8080");
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("discovery.seed_hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
assertThat(settings)
.containsEntry("discovery.seed_hosts", "1.2.3.4:9000,1.2.3.5:8080")
.containsEntry("discovery.initial_state_timeout", "120s");
}

@Test
public void set_initial_master_nodes_settings_if_cluster_is_enabled() throws Exception {
Props props = minProps(CLUSTER_ENABLED);
props.set(CLUSTER_SEARCH_HOSTS.getKey(), "1.2.3.4:9000,1.2.3.5:8080");
props.set(CLUSTER_ES_HOSTS.getKey(), "1.2.3.4:9000,1.2.3.5:8080");
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("cluster.initial_master_nodes")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
assertThat(settings)
.containsEntry("cluster.initial_master_nodes", "1.2.3.4:9000,1.2.3.5:8080")
.containsEntry("discovery.initial_state_timeout", "120s");
}

@Test
@@ -262,7 +274,7 @@ public class EsSettingsTest {
props.set(SEARCH_INITIAL_STATE_TIMEOUT.getKey(), "10s");
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
assertThat(settings).containsEntry("discovery.initial_state_timeout", "10s");
}

@Test
@@ -271,7 +283,7 @@ public class EsSettingsTest {
props.set(SEARCH_INITIAL_STATE_TIMEOUT.getKey(), "10s");
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
assertThat(settings).containsEntry("discovery.initial_state_timeout", "30s");
}

@Test
@@ -280,31 +292,32 @@ public class EsSettingsTest {
Props props = minProps(clusterEnabled);
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("http.port")).isEqualTo("9001");
assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
assertThat(settings)
.containsEntry("http.port", "9001")
.containsEntry("http.host", "127.0.0.1");
}

@Test
@UseDataProvider("clusterEnabledOrNot")
public void enable_http_connector_on_specified_port(boolean clusterEnabled) throws Exception {
String port = "" + new Random().nextInt(49151);
Props props = minProps(clusterEnabled);
props.set(SEARCH_PORT.getKey(), port);
Props props = minProps(clusterEnabled, null, port);
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("http.port")).isEqualTo(port);
assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
assertThat(settings)
.containsEntry("http.port", port)
.containsEntry("http.host", "127.0.0.1");
}

@Test
@UseDataProvider("clusterEnabledOrNot")
public void enable_http_connector_different_host(boolean clusterEnabled) throws Exception {
Props props = minProps(clusterEnabled);
props.set(SEARCH_HOST.getKey(), "127.0.0.2");
Props props = minProps(clusterEnabled, "127.0.0.2", null);
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("http.port")).isEqualTo("9001");
assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");
assertThat(settings)
.containsEntry("http.port", "9001")
.containsEntry("http.host", "127.0.0.2");
}

@Test
@@ -323,7 +336,7 @@ public class EsSettingsTest {
props.set("sonar.search.javaAdditionalOpts", "-Xmx1G -Dbootstrap.system_call_filter=false -Dfoo=bar");
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), system).build();

assertThat(settings.get("bootstrap.system_call_filter")).isEqualTo("false");
assertThat(settings).containsEntry("bootstrap.system_call_filter", "false");
}

@Test
@@ -357,6 +370,10 @@ public class EsSettingsTest {
}

private Props minProps(boolean cluster) throws IOException {
return minProps(cluster, null, null);
}

private Props minProps(boolean cluster, @Nullable String host, @Nullable String port) throws IOException {
File homeDir = temp.newFolder();
Props props = new Props(new Properties());
ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
@@ -364,6 +381,17 @@ public class EsSettingsTest {
new ProcessProperties(serviceLoaderWrapper).completeDefaults(props);
props.set(PATH_HOME.getKey(), homeDir.getAbsolutePath());
props.set(Property.CLUSTER_ENABLED.getKey(), Boolean.toString(cluster));
if (cluster) {
ofNullable(host).ifPresent(h -> props.set(CLUSTER_NODE_ES_HOST.getKey(), h));
ofNullable(port).ifPresent(p -> props.set(CLUSTER_NODE_ES_PORT.getKey(), p));
ofNullable(host).ifPresent(h -> props.set(CLUSTER_NODE_SEARCH_HOST.getKey(), h));
ofNullable(port).ifPresent(p -> props.set(CLUSTER_NODE_SEARCH_PORT.getKey(), p));
ofNullable(port).ifPresent(h -> props.set(CLUSTER_ES_HOSTS.getKey(), h));
} else {
ofNullable(host).ifPresent(h -> props.set(SEARCH_HOST.getKey(), h));
ofNullable(port).ifPresent(p -> props.set(SEARCH_PORT.getKey(), p));
}

return props;
}
}

+ 7
- 0
server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java View File

@@ -120,6 +120,13 @@ public class ProcessProperties {
CLUSTER_NAME("sonar.cluster.name", "sonarqube"),
CLUSTER_WEB_STARTUP_LEADER("sonar.cluster.web.startupLeader"),

// search node only settings
CLUSTER_ES_HOSTS("sonar.cluster.es.hosts"),
CLUSTER_NODE_SEARCH_HOST("sonar.cluster.node.search.host"),
CLUSTER_NODE_SEARCH_PORT("sonar.cluster.node.search.port"),
CLUSTER_NODE_ES_HOST("sonar.cluster.node.es.host"),
CLUSTER_NODE_ES_PORT("sonar.cluster.node.es.port"),

AUTH_JWT_SECRET("sonar.auth.jwtBase64Hs256Secret"),
SONAR_WEB_SSO_ENABLE("sonar.web.sso.enable", "false"),
SONAR_WEB_SSO_LOGIN_HEADER("sonar.web.sso.loginHeader", "X-Forwarded-Login"),

Loading…
Cancel
Save