Browse Source

Automatic merge from branch-6.6

* origin/branch-6.6:
  Prepare for next development iteration
  SONAR-9721 Fix potential OkHttpClient connection leak
  Publish 6.6 Release Candidate 1
  fix quality flaws
  Propagate parameters of SQ analysis required by burgr
  fix unwanted text wrapping in the header in ie11
  SONAR-9908 on Windows, launch Elasticsearch directly, without batch files (#2642)
  SONAR-9694 Ignore when secondary locations are out of current file for backdating
  Fix sidebar position helper in IE
tags/6.7-RC1
ssjenka 6 years ago
parent
commit
ba47e7fd39
29 changed files with 575 additions and 380 deletions
  1. 7
    15
      server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java
  2. 14
    25
      server/sonar-main/src/main/java/org/sonar/application/command/AbstractCommand.java
  3. 1
    1
      server/sonar-main/src/main/java/org/sonar/application/command/CommandFactory.java
  4. 54
    19
      server/sonar-main/src/main/java/org/sonar/application/command/CommandFactoryImpl.java
  5. 0
    118
      server/sonar-main/src/main/java/org/sonar/application/command/EsCommand.java
  6. 45
    0
      server/sonar-main/src/main/java/org/sonar/application/command/EsScriptCommand.java
  7. 36
    0
      server/sonar-main/src/main/java/org/sonar/application/command/JavaCommand.java
  8. 70
    4
      server/sonar-main/src/main/java/org/sonar/application/es/EsInstallation.java
  9. 2
    2
      server/sonar-main/src/main/java/org/sonar/application/es/EsSettings.java
  10. 8
    7
      server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java
  11. 3
    11
      server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncher.java
  12. 57
    38
      server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java
  13. 11
    14
      server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java
  14. 0
    22
      server/sonar-main/src/test/java/org/sonar/application/command/AbstractCommandTest.java
  15. 83
    16
      server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java
  16. 13
    13
      server/sonar-main/src/test/java/org/sonar/application/es/EsInstallationTest.java
  17. 21
    21
      server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java
  18. 17
    10
      server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java
  19. 32
    13
      server/sonar-main/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java
  20. 11
    0
      server/sonar-process/src/main/java/org/sonar/process/System2.java
  21. 21
    18
      server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java
  22. 8
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java
  23. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java
  24. 10
    2
      server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java
  25. 34
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java
  26. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java
  27. 1
    0
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css
  28. 4
    1
      server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx
  29. 10
    2
      travis.sh

+ 7
- 15
server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java View File

@@ -28,9 +28,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.application.command.AbstractCommand;
import org.sonar.application.command.CommandFactory;
import org.sonar.application.command.EsCommand;
import org.sonar.application.command.JavaCommand;
import org.sonar.application.config.AppSettings;
import org.sonar.application.config.ClusterSettings;
import org.sonar.application.process.Lifecycle;
@@ -108,7 +107,7 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
private void tryToStartEs() {
SQProcess process = processesById.get(ProcessId.ELASTICSEARCH);
if (process != null) {
tryToStartEsProcess(process, commandFactory::createEsCommand);
tryToStartProcess(process, commandFactory::createEsCommand);
}
}

@@ -124,9 +123,9 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
return;
}
if (appState.isOperational(ProcessId.WEB_SERVER, false)) {
tryToStartJavaProcess(process, () -> commandFactory.createWebCommand(false));
tryToStartProcess(process, () -> commandFactory.createWebCommand(false));
} else if (appState.tryToLockWebLeader()) {
tryToStartJavaProcess(process, () -> commandFactory.createWebCommand(true));
tryToStartProcess(process, () -> commandFactory.createWebCommand(true));
} else {
Optional<String> leader = appState.getLeaderHostName();
if (leader.isPresent()) {
@@ -140,7 +139,7 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
private void tryToStartCe() {
SQProcess process = processesById.get(ProcessId.COMPUTE_ENGINE);
if (process != null && appState.isOperational(ProcessId.WEB_SERVER, false) && isEsClientStartable()) {
tryToStartJavaProcess(process, commandFactory::createCeCommand);
tryToStartProcess(process, commandFactory::createCeCommand);
}
}

@@ -149,16 +148,9 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
return appState.isOperational(ProcessId.ELASTICSEARCH, requireLocalEs);
}

private void tryToStartJavaProcess(SQProcess process, Supplier<JavaCommand> commandSupplier) {
private void tryToStartProcess(SQProcess process, Supplier<AbstractCommand> commandSupplier) {
tryToStart(process, () -> {
JavaCommand command = commandSupplier.get();
return processLauncher.launch(command);
});
}

private void tryToStartEsProcess(SQProcess process, Supplier<EsCommand> commandSupplier) {
tryToStart(process, () -> {
EsCommand command = commandSupplier.get();
AbstractCommand command = commandSupplier.get();
return processLauncher.launch(command);
});
}

+ 14
- 25
server/sonar-main/src/main/java/org/sonar/application/command/AbstractCommand.java View File

@@ -22,11 +22,10 @@ package org.sonar.application.command;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.CheckForNull;
import org.sonar.application.es.EsInstallation;
import org.sonar.process.ProcessId;
import org.sonar.process.System2;

@@ -35,11 +34,10 @@ import static java.util.Objects.requireNonNull;
public abstract class AbstractCommand<T extends AbstractCommand> {
// unique key among the group of commands to launch
private final ProcessId id;
// program arguments
private final Map<String, String> arguments = new LinkedHashMap<>();
private final Map<String, String> envVariables;
private final Set<String> suppressedEnvVariables = new HashSet<>();
private final File workDir;
private EsInstallation esInstallation;

protected AbstractCommand(ProcessId id, File workDir, System2 system2) {
this.id = requireNonNull(id, "ProcessId can't be null");
@@ -60,26 +58,6 @@ public abstract class AbstractCommand<T extends AbstractCommand> {
return (T) this;
}

public Map<String, String> getArguments() {
return arguments;
}

public T setArgument(String key, @Nullable String value) {
if (value == null) {
arguments.remove(key);
} else {
arguments.put(key, value);
}
return castThis();
}

public T setArguments(Properties args) {
for (Map.Entry<Object, Object> entry : args.entrySet()) {
setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
}
return castThis();
}

public Map<String, String> getEnvVariables() {
return envVariables;
}
@@ -101,4 +79,15 @@ public abstract class AbstractCommand<T extends AbstractCommand> {
requireNonNull(value, "value can't be null"));
return castThis();
}


public T setEsInstallation(EsInstallation esInstallation) {
this.esInstallation = esInstallation;
return castThis();
}

@CheckForNull
public EsInstallation getEsInstallation() {
return esInstallation;
}
}

+ 1
- 1
server/sonar-main/src/main/java/org/sonar/application/command/CommandFactory.java View File

@@ -21,7 +21,7 @@ package org.sonar.application.command;

public interface CommandFactory {

EsCommand createEsCommand();
AbstractCommand createEsCommand();

JavaCommand createWebCommand(boolean leader);


+ 54
- 19
server/sonar-main/src/main/java/org/sonar/application/command/CommandFactoryImpl.java View File

@@ -23,14 +23,14 @@ import java.io.File;
import java.util.Map;
import java.util.Optional;
import org.slf4j.LoggerFactory;
import org.sonar.application.es.EsInstallation;
import org.sonar.application.es.EsLogging;
import org.sonar.application.es.EsSettings;
import org.sonar.application.es.EsYmlSettings;
import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;
import org.sonar.process.System2;
import org.sonar.application.es.EsFileSystem;
import org.sonar.application.es.EsLogging;
import org.sonar.application.es.EsSettings;
import org.sonar.application.es.EsYmlSettings;

import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
@@ -54,10 +54,12 @@ public class CommandFactoryImpl implements CommandFactory {

private final Props props;
private final File tempDir;
private final System2 system2;

public CommandFactoryImpl(Props props, File tempDir, System2 system2) {
this.props = props;
this.tempDir = tempDir;
this.system2 = system2;
String javaToolOptions = system2.getenv(ENV_VAR_JAVA_TOOL_OPTIONS);
if (javaToolOptions != null && !javaToolOptions.trim().isEmpty()) {
LoggerFactory.getLogger(CommandFactoryImpl.class)
@@ -67,28 +69,59 @@ public class CommandFactoryImpl implements CommandFactory {
}

@Override
public EsCommand createEsCommand() {
EsFileSystem esFileSystem = new EsFileSystem(props);
if (!esFileSystem.getExecutable().exists()) {
public AbstractCommand<?> createEsCommand() {
if (system2.isOsWindows()) {
return createEsCommandForWindows();
}
return createEsCommandForUnix();
}

private EsScriptCommand createEsCommandForUnix() {
EsInstallation esInstallation = createEsInstallation();
return new EsScriptCommand(ProcessId.ELASTICSEARCH, esInstallation.getHomeDirectory())
.setEsInstallation(esInstallation)
.addOption("-Epath.conf=" + esInstallation.getConfDirectory().getAbsolutePath())
.setEnvVariable("ES_JVM_OPTIONS", esInstallation.getJvmOptions().getAbsolutePath())
.setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"))
.suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS);
}

private JavaCommand createEsCommandForWindows() {
EsInstallation esInstallation = createEsInstallation();
return new JavaCommand<EsJvmOptions>(ProcessId.ELASTICSEARCH, esInstallation.getHomeDirectory())
.setEsInstallation(esInstallation)
.setReadsArgumentsFromFile(false)
.setArgument("path.conf", esInstallation.getConfDirectory().getAbsolutePath())
.setJvmOptions(new EsJvmOptions()
.addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_OPTS)
.addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS)
.add("-Delasticsearch")
.add("-Des.path.home=" + esInstallation.getHomeDirectory())
)
.setEnvVariable("ES_JVM_OPTIONS", esInstallation.getJvmOptions().getAbsolutePath())
.setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"))
.setClassName("org.elasticsearch.bootstrap.Elasticsearch")
.addClasspath("lib/*")
.suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS);
}

private EsInstallation createEsInstallation() {
EsInstallation esInstallation = new EsInstallation(props);
if (!esInstallation.getExecutable().exists()) {
throw new IllegalStateException("Cannot find elasticsearch binary");
}
Map<String, String> settingsMap = new EsSettings(props, esFileSystem, System2.INSTANCE).build();
Map<String, String> settingsMap = new EsSettings(props, esInstallation, System2.INSTANCE).build();

return new EsCommand(ProcessId.ELASTICSEARCH, esFileSystem.getHomeDirectory())
.setFileSystem(esFileSystem)
.setLog4j2Properties(new EsLogging().createProperties(props, esFileSystem.getLogDirectory()))
.setArguments(props.rawProperties())
.setClusterName(settingsMap.get("cluster.name"))
.setHost(settingsMap.get("network.host"))
.setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
.addEsOption("-Epath.conf=" + esFileSystem.getConfDirectory().getAbsolutePath())
esInstallation
.setLog4j2Properties(new EsLogging().createProperties(props, esInstallation.getLogDirectory()))
.setEsJvmOptions(new EsJvmOptions()
.addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_OPTS)
.addFromMandatoryProperty(props, ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
.setEsYmlSettings(new EsYmlSettings(settingsMap))
.setEnvVariable("ES_JVM_OPTIONS", esFileSystem.getJvmOptions().getAbsolutePath())
.setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"))
.suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS);
.setClusterName(settingsMap.get("cluster.name"))
.setHost(settingsMap.get("network.host"))
.setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")));
return esInstallation;
}

@Override
@@ -101,6 +134,7 @@ public class CommandFactoryImpl implements CommandFactory {
addProxyJvmOptions(jvmOptions);

JavaCommand<WebJvmOptions> command = new JavaCommand<WebJvmOptions>(ProcessId.WEB_SERVER, homeDir)
.setReadsArgumentsFromFile(true)
.setArguments(props.rawProperties())
.setJvmOptions(jvmOptions)
// required for logback tomcat valve
@@ -127,6 +161,7 @@ public class CommandFactoryImpl implements CommandFactory {
addProxyJvmOptions(jvmOptions);

JavaCommand<CeJvmOptions> command = new JavaCommand<CeJvmOptions>(ProcessId.COMPUTE_ENGINE, homeDir)
.setReadsArgumentsFromFile(true)
.setArguments(props.rawProperties())
.setJvmOptions(jvmOptions)
.setClassName("org.sonar.ce.app.CeServer")

+ 0
- 118
server/sonar-main/src/main/java/org/sonar/application/command/EsCommand.java View File

@@ -1,118 +0,0 @@
/*
* 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.command;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.sonar.application.es.EsFileSystem;
import org.sonar.application.es.EsYmlSettings;
import org.sonar.process.ProcessId;
import org.sonar.process.System2;

public class EsCommand extends AbstractCommand<EsCommand> {
private EsFileSystem fileSystem;
private String clusterName;
private String host;
private int port;
private Properties log4j2Properties;
private List<String> esOptions = new ArrayList<>();
private EsJvmOptions esJvmOptions;
private EsYmlSettings esYmlSettings;

public EsCommand(ProcessId id, File workDir) {
super(id, workDir, System2.INSTANCE);
}

public EsFileSystem getFileSystem() {
return fileSystem;
}

public EsCommand setFileSystem(EsFileSystem fileSystem) {
this.fileSystem = fileSystem;
return this;
}

public String getClusterName() {
return clusterName;
}

public EsCommand setClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}

public String getHost() {
return host;
}

public EsCommand setHost(String host) {
this.host = host;
return this;
}

public int getPort() {
return port;
}

public EsCommand setPort(int port) {
this.port = port;
return this;
}

public Properties getLog4j2Properties() {
return log4j2Properties;
}

public EsCommand setLog4j2Properties(Properties log4j2Properties) {
this.log4j2Properties = log4j2Properties;
return this;
}

public List<String> getEsOptions() {
return esOptions;
}

public EsCommand addEsOption(String s) {
if (!s.isEmpty()) {
esOptions.add(s);
}
return this;
}

public EsCommand setEsJvmOptions(EsJvmOptions esJvmOptions) {
this.esJvmOptions = esJvmOptions;
return this;
}

public EsJvmOptions getEsJvmOptions() {
return esJvmOptions;
}

public EsCommand setEsYmlSettings(EsYmlSettings esYmlSettings) {
this.esYmlSettings = esYmlSettings;
return this;
}

public EsYmlSettings getEsYmlSettings() {
return esYmlSettings;
}
}

+ 45
- 0
server/sonar-main/src/main/java/org/sonar/application/command/EsScriptCommand.java View File

@@ -0,0 +1,45 @@
/*
* 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.command;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.sonar.process.ProcessId;
import org.sonar.process.System2;

public class EsScriptCommand extends AbstractCommand<EsScriptCommand> {
private List<String> options = new ArrayList<>();

public EsScriptCommand(ProcessId id, File workDir) {
super(id, workDir, System2.INSTANCE);
}

public List<String> getOptions() {
return options;
}

public EsScriptCommand addOption(String s) {
if (!s.isEmpty()) {
options.add(s);
}
return this;
}
}

+ 36
- 0
server/sonar-main/src/main/java/org/sonar/application/command/JavaCommand.java View File

@@ -21,16 +21,23 @@ package org.sonar.application.command;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.annotation.Nullable;
import org.sonar.process.ProcessId;
import org.sonar.process.System2;

public class JavaCommand<T extends JvmOptions> extends AbstractCommand<JavaCommand<T>> {
// program arguments
private final Map<String, String> arguments = new LinkedHashMap<>();
// entry point
private String className;
private JvmOptions<T> jvmOptions;
// relative path to JAR files
private final List<String> classpath = new ArrayList<>();
private boolean readsArgumentsFromFile;

public JavaCommand(ProcessId id, File workDir) {
super(id, workDir, System2.INSTANCE);
@@ -64,6 +71,35 @@ public class JavaCommand<T extends JvmOptions> extends AbstractCommand<JavaComma
return this;
}

public boolean getReadsArgumentsFromFile() {
return readsArgumentsFromFile;
}

public JavaCommand<T> setReadsArgumentsFromFile(boolean readsArgumentsFromFile) {
this.readsArgumentsFromFile = readsArgumentsFromFile;
return this;
}

public Map<String, String> getArguments() {
return arguments;
}

public JavaCommand<T> setArgument(String key, @Nullable String value) {
if (value == null) {
arguments.remove(key);
} else {
arguments.put(key, value);
}
return this;
}

public JavaCommand<T> setArguments(Properties args) {
for (Map.Entry<Object, Object> entry : args.entrySet()) {
setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
}
return this;
}

@Override
public String toString() {
return "JavaCommand{" + "workDir=" + getWorkDir() +

server/sonar-main/src/main/java/org/sonar/application/es/EsFileSystem.java → server/sonar-main/src/main/java/org/sonar/application/es/EsInstallation.java View File

@@ -22,6 +22,8 @@ package org.sonar.application.es;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.sonar.application.command.EsJvmOptions;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;

@@ -33,14 +35,20 @@ import org.sonar.process.Props;
* This class does not ensure files nor directories actually exist.
* </p>
*/
public class EsFileSystem {
public class EsInstallation {
private final File homeDirectory;
private final List<File> outdatedSearchDirectories;
private final File dataDirectory;
private final File confDirectory;
private final File logDirectory;

public EsFileSystem(Props props) {
private EsJvmOptions esJvmOptions;
private EsYmlSettings esYmlSettings;
private Properties log4j2Properties;
private String clusterName;
private String host;
private int port;

public EsInstallation(Props props) {
File sqHomeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);

this.homeDirectory = new File(sqHomeDir, "elasticsearch");
@@ -100,7 +108,7 @@ public class EsFileSystem {
return "elasticsearch";
}

public File getLog4j2Properties() {
public File getLog4j2PropertiesLocation() {
return new File(confDirectory, "log4j2.properties");
}

@@ -111,4 +119,62 @@ public class EsFileSystem {
public File getJvmOptions() {
return new File(confDirectory, "jvm.options");
}

public File getLibDirectory() {
return new File(homeDirectory, "lib");
}

public EsJvmOptions getEsJvmOptions() {
return esJvmOptions;
}

public EsInstallation setEsJvmOptions(EsJvmOptions esJvmOptions) {
this.esJvmOptions = esJvmOptions;
return this;
}

public EsYmlSettings getEsYmlSettings() {
return esYmlSettings;
}

public EsInstallation setEsYmlSettings(EsYmlSettings esYmlSettings) {
this.esYmlSettings = esYmlSettings;
return this;
}

public Properties getLog4j2Properties() {
return log4j2Properties;
}

public EsInstallation setLog4j2Properties(Properties log4j2Properties) {
this.log4j2Properties = log4j2Properties;
return this;
}

public String getClusterName() {
return clusterName;
}

public EsInstallation setClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}

public String getHost() {
return host;
}

public EsInstallation setHost(String host) {
this.host = host;
return this;
}

public int getPort() {
return port;
}

public EsInstallation setPort(int port) {
this.port = port;
return this;
}
}

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

@@ -42,13 +42,13 @@ public class EsSettings {
private static final String STANDALONE_NODE_NAME = "sonarqube";

private final Props props;
private final EsFileSystem fileSystem;
private final EsInstallation fileSystem;

private final boolean clusterEnabled;
private final String clusterName;
private final String nodeName;

public EsSettings(Props props, EsFileSystem fileSystem, System2 system2) {
public EsSettings(Props props, EsInstallation fileSystem, System2 system2) {
this.props = props;
this.fileSystem = fileSystem;


+ 8
- 7
server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java View File

@@ -38,7 +38,8 @@ import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.elasticsearch.transport.Netty4Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.application.command.EsCommand;
import org.sonar.application.es.EsInstallation;
import org.sonar.process.ProcessId;

import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
@@ -56,13 +57,13 @@ public class EsProcessMonitor extends AbstractProcessMonitor {
private final AtomicBoolean nodeUp = new AtomicBoolean(false);
private final AtomicBoolean nodeOperational = new AtomicBoolean(false);
private final AtomicBoolean firstMasterNotDiscoveredLog = new AtomicBoolean(true);
private final EsCommand esCommand;
private final EsInstallation esConfig;
private final EsConnector esConnector;
private AtomicReference<TransportClient> transportClient = new AtomicReference<>(null);

public EsProcessMonitor(Process process, EsCommand esCommand, EsConnector esConnector) {
super(process, esCommand.getProcessId());
this.esCommand = esCommand;
public EsProcessMonitor(Process process, ProcessId processId, EsInstallation esConfig, EsConnector esConnector) {
super(process, processId);
this.esConfig = esConfig;
this.esConnector = esConnector;
}

@@ -169,10 +170,10 @@ public class EsProcessMonitor extends AbstractProcessMonitor {
Settings.Builder esSettings = Settings.builder();

// mandatory property defined by bootstrap process
esSettings.put("cluster.name", esCommand.getClusterName());
esSettings.put("cluster.name", esConfig.getClusterName());

TransportClient nativeClient = new MinimalTransportClient(esSettings.build());
HostAndPort host = HostAndPort.fromParts(esCommand.getHost(), esCommand.getPort());
HostAndPort host = HostAndPort.fromParts(esConfig.getHost(), esConfig.getPort());
addHostToClient(host, nativeClient);
if (LOG.isDebugEnabled()) {
LOG.debug("Connected to Elasticsearch node: [{}]", displayedAddresses(nativeClient));

+ 3
- 11
server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncher.java View File

@@ -20,8 +20,7 @@
package org.sonar.application.process;

import java.io.Closeable;
import org.sonar.application.command.EsCommand;
import org.sonar.application.command.JavaCommand;
import org.sonar.application.command.AbstractCommand;

public interface ProcessLauncher extends Closeable {

@@ -29,16 +28,9 @@ public interface ProcessLauncher extends Closeable {
void close();

/**
* Launch an ES command.
* Launch a command.
*
* @throws IllegalStateException if an error occurs
*/
ProcessMonitor launch(EsCommand esCommand);

/**
* Launch a Java command.
*
* @throws IllegalStateException if an error occurs
*/
ProcessMonitor launch(JavaCommand javaCommand);
ProcessMonitor launch(AbstractCommand command);
}

+ 57
- 38
server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java View File

@@ -33,10 +33,10 @@ import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.application.command.AbstractCommand;
import org.sonar.application.command.EsCommand;
import org.sonar.application.command.EsScriptCommand;
import org.sonar.application.command.JavaCommand;
import org.sonar.application.command.JvmOptions;
import org.sonar.application.es.EsFileSystem;
import org.sonar.application.es.EsInstallation;
import org.sonar.process.ProcessId;
import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
import org.sonar.process.sharedmemoryfile.ProcessCommands;
@@ -69,30 +69,51 @@ public class ProcessLauncherImpl implements ProcessLauncher {
allProcessesCommands.close();
}

@Override
public ProcessMonitor launch(EsCommand esCommand) {
Process process = null;
try {
cleanupOutdatedEsData(esCommand);
writeConfFiles(esCommand);
ProcessBuilder processBuilder = create(esCommand);
logLaunchedCommand(esCommand, processBuilder);
public ProcessMonitor launch(AbstractCommand command) {
EsInstallation fileSystem = command.getEsInstallation();
if (fileSystem != null) {
cleanupOutdatedEsData(fileSystem);
writeConfFiles(fileSystem);
}

process = processBuilder.start();
Process process;
if (command instanceof EsScriptCommand) {
process = launchExternal((EsScriptCommand) command);
} else if (command instanceof JavaCommand) {
process = launchJava((JavaCommand) command);
} else {
throw new IllegalStateException("Unexpected type of command: " + command.getClass());
}

return new EsProcessMonitor(process, esCommand, new EsConnectorImpl());
ProcessId processId = command.getProcessId();
try {
if (processId == ProcessId.ELASTICSEARCH) {
return new EsProcessMonitor(process, processId, command.getEsInstallation(), new EsConnectorImpl());
} else {
ProcessCommands commands = allProcessesCommands.createAfterClean(processId.getIpcIndex());
return new ProcessCommandsProcessMonitor(process, processId, commands);
}
} catch (Exception e) {
// just in case
if (process != null) {
process.destroyForcibly();
}
throw new IllegalStateException(format("Fail to launch process [%s]", esCommand.getProcessId().getKey()), e);
throw new IllegalStateException(format("Fail to launch monitor of process [%s]", processId.getKey()), e);
}
}

private static void cleanupOutdatedEsData(EsCommand esCommand) {
EsFileSystem esFileSystem = esCommand.getFileSystem();
esFileSystem.getOutdatedSearchDirectories().forEach(outdatedDir -> {
private Process launchExternal(EsScriptCommand esScriptCommand) {
try {
ProcessBuilder processBuilder = create(esScriptCommand);
logLaunchedCommand(esScriptCommand, processBuilder);
return processBuilder.start();
} catch (Exception e) {
throw new IllegalStateException(format("Fail to launch process [%s]", esScriptCommand.getProcessId().getKey()), e);
}
}

private static void cleanupOutdatedEsData(EsInstallation esInstallation) {
esInstallation.getOutdatedSearchDirectories().forEach(outdatedDir -> {
if (outdatedDir.exists()) {
LOG.info("Deleting outdated search index data directory {}", outdatedDir.getAbsolutePath());
try {
@@ -104,9 +125,8 @@ public class ProcessLauncherImpl implements ProcessLauncher {
});
}

private static void writeConfFiles(EsCommand esCommand) {
EsFileSystem esFileSystem = esCommand.getFileSystem();
File confDir = esFileSystem.getConfDirectory();
private static void writeConfFiles(EsInstallation esInstallation) {
File confDir = esInstallation.getConfDirectory();
if (!confDir.exists() && !confDir.mkdirs()) {
String error = format("Failed to create temporary configuration directory [%s]", confDir.getAbsolutePath());
LOG.error(error);
@@ -114,30 +134,21 @@ public class ProcessLauncherImpl implements ProcessLauncher {
}

try {
esCommand.getEsYmlSettings().writeToYmlSettingsFile(esFileSystem.getElasticsearchYml());
esCommand.getEsJvmOptions().writeToJvmOptionFile(esFileSystem.getJvmOptions());
esCommand.getLog4j2Properties().store(new FileOutputStream(esFileSystem.getLog4j2Properties()), "log4j2 properties file for ES bundled in SonarQube");
esInstallation.getEsYmlSettings().writeToYmlSettingsFile(esInstallation.getElasticsearchYml());
esInstallation.getEsJvmOptions().writeToJvmOptionFile(esInstallation.getJvmOptions());
esInstallation.getLog4j2Properties().store(new FileOutputStream(esInstallation.getLog4j2PropertiesLocation()), "log4j2 properties file for ES bundled in SonarQube");
} catch (IOException e) {
throw new IllegalStateException("Failed to write ES configuration files", e);
}
}

@Override
public ProcessMonitor launch(JavaCommand javaCommand) {
Process process = null;
private Process launchJava(JavaCommand javaCommand) {
ProcessId processId = javaCommand.getProcessId();
try {
ProcessCommands commands = allProcessesCommands.createAfterClean(processId.getIpcIndex());

ProcessBuilder processBuilder = create(javaCommand);
logLaunchedCommand(javaCommand, processBuilder);
process = processBuilder.start();
return new ProcessCommandsProcessMonitor(process, processId, commands);
return processBuilder.start();
} catch (Exception e) {
// just in case
if (process != null) {
process.destroyForcibly();
}
throw new IllegalStateException(format("Fail to launch process [%s]", processId.getKey()), e);
}
}
@@ -151,12 +162,12 @@ public class ProcessLauncherImpl implements ProcessLauncher {
}
}

private ProcessBuilder create(EsCommand esCommand) {
private ProcessBuilder create(EsScriptCommand esScriptCommand) {
List<String> commands = new ArrayList<>();
commands.add(esCommand.getFileSystem().getExecutable().getAbsolutePath());
commands.addAll(esCommand.getEsOptions());
commands.add(esScriptCommand.getEsInstallation().getExecutable().getAbsolutePath());
commands.addAll(esScriptCommand.getOptions());

return create(esCommand, commands);
return create(esScriptCommand, commands);
}

private <T extends JvmOptions> ProcessBuilder create(JavaCommand<T> javaCommand) {
@@ -165,7 +176,15 @@ public class ProcessLauncherImpl implements ProcessLauncher {
commands.addAll(javaCommand.getJvmOptions().getAll());
commands.addAll(buildClasspath(javaCommand));
commands.add(javaCommand.getClassName());
commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
if (javaCommand.getReadsArgumentsFromFile()) {
commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
} else {
javaCommand.getArguments().forEach((key, value) -> {
if (value != null && !value.isEmpty()) {
commands.add("-E" + key + "=" + value);
}
});
}

return create(javaCommand, commands);
}

+ 11
- 14
server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java View File

@@ -40,7 +40,7 @@ import org.junit.rules.Timeout;
import org.mockito.Mockito;
import org.sonar.application.command.AbstractCommand;
import org.sonar.application.command.CommandFactory;
import org.sonar.application.command.EsCommand;
import org.sonar.application.command.EsScriptCommand;
import org.sonar.application.command.JavaCommand;
import org.sonar.application.config.TestAppSettings;
import org.sonar.application.process.ProcessLauncher;
@@ -73,7 +73,8 @@ public class SchedulerImplTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

private EsCommand esCommand;
private EsScriptCommand esScriptCommand;
private JavaCommand esJavaCommand;
private JavaCommand webLeaderCommand;
private JavaCommand webFollowerCommand;
private JavaCommand ceCommand;
@@ -90,7 +91,8 @@ public class SchedulerImplTest {
@Before
public void setUp() throws Exception {
File tempDir = temporaryFolder.newFolder();
esCommand = new EsCommand(ELASTICSEARCH, tempDir);
esScriptCommand = new EsScriptCommand(ELASTICSEARCH, tempDir);
esJavaCommand = new JavaCommand(ELASTICSEARCH, tempDir);
webLeaderCommand = new JavaCommand(WEB_SERVER, tempDir);
webFollowerCommand = new JavaCommand(WEB_SERVER, tempDir);
ceCommand = new JavaCommand(COMPUTE_ENGINE, tempDir);
@@ -117,7 +119,7 @@ public class SchedulerImplTest {
TestProcess web = processLauncher.waitForProcess(WEB_SERVER);
assertThat(web.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(2);
assertThat(processLauncher.commands).containsExactly(esCommand, webLeaderCommand);
assertThat(processLauncher.commands).containsExactly(esScriptCommand, webLeaderCommand);

// web becomes operational -> CE is starting
web.operational = true;
@@ -125,7 +127,7 @@ public class SchedulerImplTest {
TestProcess ce = processLauncher.waitForProcess(COMPUTE_ENGINE);
assertThat(ce.isAlive()).isTrue();
assertThat(processLauncher.processes).hasSize(3);
assertThat(processLauncher.commands).containsExactly(esCommand, webLeaderCommand, ceCommand);
assertThat(processLauncher.commands).containsExactly(esScriptCommand, webLeaderCommand, ceCommand);

// all processes are up
processLauncher.processes.values().forEach(p -> assertThat(p.isAlive()).isTrue());
@@ -360,8 +362,8 @@ public class SchedulerImplTest {

private class TestCommandFactory implements CommandFactory {
@Override
public EsCommand createEsCommand() {
return esCommand;
public EsScriptCommand createEsCommand() {
return esScriptCommand;
}

@Override
@@ -381,13 +383,8 @@ public class SchedulerImplTest {
private ProcessId makeStartupFail = null;

@Override
public ProcessMonitor launch(EsCommand esCommand) {
return launchImpl(esCommand);
}

@Override
public ProcessMonitor launch(JavaCommand javaCommand) {
return launchImpl(javaCommand);
public ProcessMonitor launch(AbstractCommand command) {
return launchImpl(command);
}

private ProcessMonitor launchImpl(AbstractCommand<?> javaCommand) {

+ 0
- 22
server/sonar-main/src/test/java/org/sonar/application/command/AbstractCommandTest.java View File

@@ -66,28 +66,6 @@ public class AbstractCommandTest {
};
}

@Test
public void test_command_with_complete_information() throws Exception {
File workDir = temp.newFolder();
AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, System2.INSTANCE) {

};

command.setArgument("first_arg", "val1");
Properties args = new Properties();
args.setProperty("second_arg", "val2");
command.setArguments(args);

command.setEnvVariable("JAVA_COMMAND_TEST", "1000");

assertThat(command.toString()).isNotNull();
assertThat(command.getWorkDir()).isSameAs(workDir);

// copy current env variables
assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
}

@Test
public void setEnvVariable_fails_with_NPE_if_key_is_null() throws IOException {
File workDir = temp.newFolder();

+ 83
- 16
server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java View File

@@ -31,6 +31,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.sonar.application.es.EsInstallation;
import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;
@@ -103,30 +104,66 @@ public class CommandFactoryImplTest {
}

@Test
public void createEsCommand_returns_command_for_default_settings() throws Exception {
public void createEsCommand_for_unix_returns_command_for_default_settings() throws Exception {
System2 system2 = Mockito.mock(System2.class);
when(system2.isOsWindows()).thenReturn(false);
prepareEsFileSystem();

Properties props = new Properties();
props.setProperty("sonar.search.host", "localhost");

AbstractCommand esCommand = newFactory(props, system2).createEsCommand();
EsInstallation esConfig = esCommand.getEsInstallation();

assertThat(esCommand).isInstanceOf(EsScriptCommand.class);
assertThat(esConfig.getClusterName()).isEqualTo("sonarqube");
assertThat(esConfig.getHost()).isNotEmpty();
assertThat(esConfig.getPort()).isEqualTo(9001);
assertThat(esConfig.getEsJvmOptions().getAll())
// enforced values
.contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
// default settings
.contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
File esConfDir = new File(tempDir, "conf/es");
assertThat(esCommand.getEnvVariables())
.contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
.containsKey("JAVA_HOME");
assertThat(esConfig.getEsYmlSettings()).isNotNull();

assertThat(esConfig.getLog4j2Properties())
.contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));

assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
}

@Test
public void createEsCommand_for_windows_returns_command_for_default_settings() throws Exception {
System2 system2 = Mockito.mock(System2.class);
when(system2.isOsWindows()).thenReturn(true);
prepareEsFileSystem();

Properties props = new Properties();
props.setProperty("sonar.search.host", "localhost");

EsCommand esCommand = newFactory(props).createEsCommand();
AbstractCommand esCommand = newFactory(props, system2).createEsCommand();
EsInstallation esConfig = esCommand.getEsInstallation();

assertThat(esCommand.getClusterName()).isEqualTo("sonarqube");
assertThat(esCommand.getHost()).isNotEmpty();
assertThat(esCommand.getPort()).isEqualTo(9001);
assertThat(esCommand.getEsJvmOptions().getAll())
assertThat(esCommand).isInstanceOf(JavaCommand.class);
assertThat(esConfig.getClusterName()).isEqualTo("sonarqube");
assertThat(esConfig.getHost()).isNotEmpty();
assertThat(esConfig.getPort()).isEqualTo(9001);
assertThat(esConfig.getEsJvmOptions().getAll())
// enforced values
.contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
// default settings
.contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
File esConfDir = new File(tempDir, "conf/es");
assertThat(esCommand.getEsOptions()).containsOnly("-Epath.conf=" + esConfDir.getAbsolutePath());
assertThat(esCommand.getEnvVariables())
.contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
.containsKey("JAVA_HOME");
assertThat(esCommand.getEsYmlSettings()).isNotNull();
assertThat(esConfig.getEsYmlSettings()).isNotNull();

assertThat(esCommand.getLog4j2Properties())
assertThat(esConfig.getLog4j2Properties())
.contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));

assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
@@ -142,11 +179,12 @@ public class CommandFactoryImplTest {
props.setProperty("sonar.search.port", "1234");
props.setProperty("sonar.search.javaOpts", "-Xms10G -Xmx10G");

EsCommand command = newFactory(props).createEsCommand();
AbstractCommand esCommand = newFactory(props).createEsCommand();
EsInstallation esConfig = esCommand.getEsInstallation();

assertThat(command.getClusterName()).isEqualTo("foo");
assertThat(command.getPort()).isEqualTo(1234);
assertThat(command.getEsJvmOptions().getAll())
assertThat(esConfig.getClusterName()).isEqualTo("foo");
assertThat(esConfig.getPort()).isEqualTo(1234);
assertThat(esConfig.getEsJvmOptions().getAll())
// enforced values
.contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
// user settings
@@ -171,7 +209,32 @@ public class CommandFactoryImplTest {
.contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
assertThat(command.getProcessId()).isEqualTo(ProcessId.WEB_SERVER);
assertThat(command.getEnvVariables())
.containsKey("JAVA_HOME");
.isNotEmpty();
assertThat(command.getArguments())
// default settings
.contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
.contains(entry("sonar.cluster.enabled", "false"));

assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
}

@Test
public void createCeCommand_returns_command_for_default_settings() throws Exception {
JavaCommand command = newFactory(new Properties()).createCeCommand();

assertThat(command.getClassName()).isEqualTo("org.sonar.ce.app.CeServer");
assertThat(command.getWorkDir().getAbsolutePath()).isEqualTo(homeDir.getAbsolutePath());
assertThat(command.getClasspath())
.containsExactlyInAnyOrder("./lib/common/*", "./lib/server/*", "./lib/ce/*");
assertThat(command.getJvmOptions().getAll())
// enforced values
.contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
// default settings
.contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
.contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
assertThat(command.getProcessId()).isEqualTo(ProcessId.COMPUTE_ENGINE);
assertThat(command.getEnvVariables())
.isNotEmpty();
assertThat(command.getArguments())
// default settings
.contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
@@ -220,7 +283,11 @@ public class CommandFactoryImplTest {
FileUtils.touch(new File(homeDir, "elasticsearch/bin/elasticsearch.bat"));
}

private CommandFactory newFactory(Properties userProps) throws IOException {
private CommandFactoryImpl newFactory(Properties userProps) throws IOException {
return newFactory(userProps, System2.INSTANCE);
}

private CommandFactoryImpl newFactory(Properties userProps, System2 system2) throws IOException {
Properties p = new Properties();
p.setProperty("sonar.path.home", homeDir.getAbsolutePath());
p.setProperty("sonar.path.temp", tempDir.getAbsolutePath());
@@ -229,7 +296,7 @@ public class CommandFactoryImplTest {

Props props = new Props(p);
ProcessProperties.completeDefaults(props);
return new CommandFactoryImpl(props, tempDir, System2.INSTANCE);
return new CommandFactoryImpl(props, tempDir, system2);
}

private <T> void attachMemoryAppenderToLoggerOf(Class<T> loggerClass) {

server/sonar-main/src/test/java/org/sonar/application/es/EsFileSystemTest.java → server/sonar-main/src/test/java/org/sonar/application/es/EsInstallationTest.java View File

@@ -31,7 +31,7 @@ import org.sonar.process.Props;

import static org.assertj.core.api.Assertions.assertThat;

public class EsFileSystemTest {
public class EsInstallationTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();
@@ -45,7 +45,7 @@ public class EsFileSystemTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Property sonar.path.home is not set");

new EsFileSystem(props);
new EsInstallation(props);
}

@Test
@@ -57,7 +57,7 @@ public class EsFileSystemTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Property sonar.path.temp is not set");

new EsFileSystem(props);
new EsInstallation(props);
}

@Test
@@ -68,7 +68,7 @@ public class EsFileSystemTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Missing property: sonar.path.data");

new EsFileSystem(props);
new EsInstallation(props);
}

@Test
@@ -80,7 +80,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getHomeDirectory()).isEqualTo(new File(sqHomeDir, "elasticsearch"));
}
@@ -97,7 +97,7 @@ public class EsFileSystemTest {

props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getDataDirectory()).isEqualTo(new File(dataDir, "es5"));
}
@@ -112,7 +112,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getLogDirectory()).isEqualTo(logDir);
}
@@ -126,7 +126,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getConfDirectory()).isEqualTo(new File(tempDir, "conf/es"));
}
@@ -140,7 +140,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

if (System.getProperty("os.name").startsWith("Windows")) {
assertThat(underTest.getExecutable()).isEqualTo(new File(sqHomeDir, "elasticsearch/bin/elasticsearch.bat"));
@@ -158,9 +158,9 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getLog4j2Properties()).isEqualTo(new File(tempDir, "conf/es/log4j2.properties"));
assertThat(underTest.getLog4j2PropertiesLocation()).isEqualTo(new File(tempDir, "conf/es/log4j2.properties"));
}

@Test
@@ -172,7 +172,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getElasticsearchYml()).isEqualTo(new File(tempDir, "conf/es/elasticsearch.yml"));
}
@@ -186,7 +186,7 @@ public class EsFileSystemTest {
props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());

EsFileSystem underTest = new EsFileSystem(props);
EsInstallation underTest = new EsInstallation(props);

assertThat(underTest.getJvmOptions()).isEqualTo(new File(tempDir, "conf/es/jvm.options"));
}

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

@@ -65,7 +65,7 @@ public class EsSettingsTest {
this.listAppender = ListAppender.attachMemoryAppenderToLoggerOf(EsSettings.class);
Props props = minimalProps();
System2 system2 = mock(System2.class);
new EsSettings(props, new EsFileSystem(props), system2);
new EsSettings(props, new EsInstallation(props), system2);

assertThat(listAppender.getLogs()).isEmpty();
}
@@ -76,7 +76,7 @@ public class EsSettingsTest {
Props props = minimalProps();
System2 system2 = mock(System2.class);
when(system2.getenv("ES_JVM_OPTIONS")).thenReturn(" ");
new EsSettings(props, new EsFileSystem(props), system2);
new EsSettings(props, new EsInstallation(props), system2);

assertThat(listAppender.getLogs()).isEmpty();
}
@@ -87,7 +87,7 @@ public class EsSettingsTest {
Props props = minimalProps();
System2 system2 = mock(System2.class);
when(system2.getenv("ES_JVM_OPTIONS")).thenReturn(randomAlphanumeric(2));
new EsSettings(props, new EsFileSystem(props), system2);
new EsSettings(props, new EsInstallation(props), system2);

assertThat(listAppender.getLogs())
.extracting(ILoggingEvent::getMessage)
@@ -117,7 +117,7 @@ public class EsSettingsTest {
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
props.set(CLUSTER_NAME, "sonarqube");

EsSettings esSettings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE);
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE);

Map<String, String> generated = esSettings.build();
assertThat(generated.get("transport.tcp.port")).isEqualTo("1234");
@@ -156,7 +156,7 @@ public class EsSettingsTest {
props.set(ProcessProperties.CLUSTER_ENABLED, "true");
props.set(ProcessProperties.CLUSTER_NODE_NAME, "node-1");

EsSettings esSettings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE);
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE);

Map<String, String> generated = esSettings.build();
assertThat(generated.get("cluster.name")).isEqualTo("sonarqube-1");
@@ -175,7 +175,7 @@ public class EsSettingsTest {
props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
EsSettings esSettings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE);
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE);
Map<String, String> generated = esSettings.build();
assertThat(generated.get("node.name")).startsWith("sonarqube-");
}
@@ -192,20 +192,20 @@ public class EsSettingsTest {
props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
EsSettings esSettings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE);
EsSettings esSettings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE);
Map<String, String> generated = esSettings.build();
assertThat(generated.get("node.name")).isEqualTo("sonarqube");
}

@Test
public void path_properties_are_values_from_EsFileSystem_argument() throws IOException {
EsFileSystem mockedEsFileSystem = mock(EsFileSystem.class);
when(mockedEsFileSystem.getHomeDirectory()).thenReturn(new File("/foo/home"));
when(mockedEsFileSystem.getConfDirectory()).thenReturn(new File("/foo/conf"));
when(mockedEsFileSystem.getLogDirectory()).thenReturn(new File("/foo/log"));
when(mockedEsFileSystem.getDataDirectory()).thenReturn(new File("/foo/data"));
EsInstallation mockedEsInstallation = mock(EsInstallation.class);
when(mockedEsInstallation.getHomeDirectory()).thenReturn(new File("/foo/home"));
when(mockedEsInstallation.getConfDirectory()).thenReturn(new File("/foo/conf"));
when(mockedEsInstallation.getLogDirectory()).thenReturn(new File("/foo/log"));
when(mockedEsInstallation.getDataDirectory()).thenReturn(new File("/foo/data"));

EsSettings underTest = new EsSettings(minProps(new Random().nextBoolean()), mockedEsFileSystem, System2.INSTANCE);
EsSettings underTest = new EsSettings(minProps(new Random().nextBoolean()), mockedEsInstallation, System2.INSTANCE);

Map<String, String> generated = underTest.build();
assertThat(generated.get("path.data")).isEqualTo("/foo/data");
@@ -217,7 +217,7 @@ public class EsSettingsTest {
public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
Props props = minProps(CLUSTER_ENABLED);
props.set(CLUSTER_SEARCH_HOSTS, "1.2.3.4:9000,1.2.3.5:8080");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("2");
@@ -229,7 +229,7 @@ public class EsSettingsTest {
Props props = minProps(CLUSTER_ENABLED);
props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "ꝱꝲꝳପ");

EsSettings underTest = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE);
EsSettings underTest = new EsSettings(props, new EsInstallation(props), System2.INSTANCE);

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Value of property sonar.search.minimumMasterNodes is not an integer:");
@@ -240,7 +240,7 @@ public class EsSettingsTest {
public void cluster_is_enabled_with_defined_minimum_master_nodes() throws Exception {
Props props = minProps(CLUSTER_ENABLED);
props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("5");
}
@@ -249,7 +249,7 @@ public class EsSettingsTest {
public void cluster_is_enabled_with_defined_initialTimeout() throws Exception {
Props props = minProps(CLUSTER_ENABLED);
props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
}
@@ -258,7 +258,7 @@ public class EsSettingsTest {
public void in_standalone_initialTimeout_is_not_overridable() throws Exception {
Props props = minProps(CLUSTER_DISABLED);
props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
}
@@ -267,7 +267,7 @@ public class EsSettingsTest {
public void in_standalone_minimumMasterNodes_is_not_overridable() throws Exception {
Props props = minProps(CLUSTER_DISABLED);
props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
}
@@ -276,7 +276,7 @@ public class EsSettingsTest {
public void enable_http_connector() throws Exception {
Props props = minProps(CLUSTER_DISABLED);
props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("http.port")).isEqualTo("9010");
assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
@@ -288,7 +288,7 @@ public class EsSettingsTest {
Props props = minProps(CLUSTER_DISABLED);
props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2");
Map<String, String> settings = new EsSettings(props, new EsFileSystem(props), System2.INSTANCE).build();
Map<String, String> settings = new EsSettings(props, new EsInstallation(props), System2.INSTANCE).build();

assertThat(settings.get("http.port")).isEqualTo("9010");
assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");

+ 17
- 10
server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java View File

@@ -28,14 +28,16 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import org.sonar.application.es.EsInstallation;
import org.sonar.process.ProcessId;
import org.sonar.application.command.EsCommand;
import org.sonar.process.Props;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
@@ -49,7 +51,7 @@ public class EsProcessMonitorTest {
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);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isFalse();
}

@@ -57,7 +59,7 @@ public class EsProcessMonitorTest {
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);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isTrue();
}

@@ -65,7 +67,7 @@ public class EsProcessMonitorTest {
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);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isTrue();
}

@@ -73,7 +75,7 @@ public class EsProcessMonitorTest {
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);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isTrue();

when(esConnector.getClusterHealthStatus(any())).thenReturn(ClusterHealthStatus.RED);
@@ -86,7 +88,7 @@ public class EsProcessMonitorTest {
when(esConnector.getClusterHealthStatus(any()))
.thenThrow(new NoNodeAvailableException("test"))
.thenReturn(ClusterHealthStatus.GREEN);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isTrue();
}

@@ -95,7 +97,7 @@ public class EsProcessMonitorTest {
EsConnector esConnector = mock(EsConnector.class);
when(esConnector.getClusterHealthStatus(any()))
.thenThrow(new RuntimeException("test"));
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isFalse();
}

@@ -112,7 +114,7 @@ public class EsProcessMonitorTest {
when(esConnector.getClusterHealthStatus(any()))
.thenThrow(new MasterNotDiscoveredException("Master not elected -test-"));

EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), getEsCommand(), esConnector);
EsProcessMonitor underTest = new EsProcessMonitor(mock(Process.class), ProcessId.ELASTICSEARCH, getEsConfig(), esConnector);
assertThat(underTest.isOperational()).isFalse();
assertThat(memoryAppender.events).isNotEmpty();
assertThat(memoryAppender.events)
@@ -130,9 +132,14 @@ public class EsProcessMonitorTest {
);
}

private EsCommand getEsCommand() throws IOException {
private EsInstallation getEsConfig() throws IOException {
Path tempDirectory = Files.createTempDirectory(getClass().getSimpleName());
return new EsCommand(ProcessId.ELASTICSEARCH, tempDirectory.toFile())
Properties properties = new Properties();
properties.setProperty("sonar.path.home", "/imaginary/path");
properties.setProperty("sonar.path.data", "/imaginary/path");
properties.setProperty("sonar.path.temp", "/imaginary/path");
properties.setProperty("sonar.path.logs", "/imaginary/path");
return new EsInstallation(new Props(properties))
.setHost("localhost")
.setPort(new Random().nextInt(40000));
}

+ 32
- 13
server/sonar-main/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java View File

@@ -30,11 +30,11 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.application.command.EsCommand;
import org.sonar.application.command.EsJvmOptions;
import org.sonar.application.command.EsScriptCommand;
import org.sonar.application.command.JavaCommand;
import org.sonar.application.command.JvmOptions;
import org.sonar.application.es.EsFileSystem;
import org.sonar.application.es.EsInstallation;
import org.sonar.application.es.EsYmlSettings;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;
@@ -93,7 +93,8 @@ public class ProcessLauncherImplTest {
File tempDir = temp.newFolder();
TestProcessBuilder processBuilder = new TestProcessBuilder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.ELASTICSEARCH, temp.newFolder());
JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.WEB_SERVER, temp.newFolder());
command.setReadsArgumentsFromFile(true);
command.setArgument("foo", "bar");
command.setArgument("baz", "woo");
command.setJvmOptions(new JvmOptions<>());
@@ -110,12 +111,30 @@ public class ProcessLauncherImplTest {
entry("foo", "bar"),
entry("baz", "woo"),
entry("process.terminationTimeout", "60000"),
entry("process.key", ProcessId.ELASTICSEARCH.getKey()),
entry("process.index", String.valueOf(ProcessId.ELASTICSEARCH.getIpcIndex())),
entry("process.key", ProcessId.WEB_SERVER.getKey()),
entry("process.index", String.valueOf(ProcessId.WEB_SERVER.getIpcIndex())),
entry("process.sharedDir", tempDir.getAbsolutePath()));
}
}

@Test
public void temporary_properties_file_can_be_avoided() throws Exception {
File tempDir = temp.newFolder();
TestProcessBuilder processBuilder = new TestProcessBuilder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.WEB_SERVER, temp.newFolder());
command.setReadsArgumentsFromFile(false);
command.setArgument("foo", "bar");
command.setArgument("baz", "woo");
command.setJvmOptions(new JvmOptions<>());

underTest.launch(command);

String propsFilePath = processBuilder.commands.get(processBuilder.commands.size() - 1);
File file = new File(propsFilePath);
assertThat(file).doesNotExist();
}

@Test
public void clean_up_old_es_data() throws Exception {
File tempDir = temp.newFolder();
@@ -123,7 +142,7 @@ public class ProcessLauncherImplTest {
File dataDir = temp.newFolder();
File logDir = temp.newFolder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> new TestProcessBuilder());
EsCommand command = createEsCommand(tempDir, homeDir, dataDir, logDir);
EsScriptCommand command = createEsScriptCommand(tempDir, homeDir, dataDir, logDir);

File outdatedEsDir = new File(dataDir, "es");
assertThat(outdatedEsDir.mkdir()).isTrue();
@@ -141,7 +160,7 @@ public class ProcessLauncherImplTest {
File dataDir = temp.newFolder();
File logDir = temp.newFolder();
ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> new TestProcessBuilder());
EsCommand command = createEsCommand(tempDir, homeDir, dataDir, logDir);
EsScriptCommand command = createEsScriptCommand(tempDir, homeDir, dataDir, logDir);

File outdatedEsDir = new File(dataDir, "es");
assertThat(outdatedEsDir.exists()).isFalse();
@@ -164,17 +183,17 @@ public class ProcessLauncherImplTest {
underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH, temp.newFolder()));
}

private EsCommand createEsCommand(File tempDir, File homeDir, File dataDir, File logDir) throws IOException {
EsCommand command = new EsCommand(ProcessId.ELASTICSEARCH, temp.newFolder());
private EsScriptCommand createEsScriptCommand(File tempDir, File homeDir, File dataDir, File logDir) throws IOException {
EsScriptCommand command = new EsScriptCommand(ProcessId.ELASTICSEARCH, temp.newFolder());
Props props = new Props(new Properties());
props.set("sonar.path.temp", tempDir.getAbsolutePath());
props.set("sonar.path.home", homeDir.getAbsolutePath());
props.set("sonar.path.data", dataDir.getAbsolutePath());
props.set("sonar.path.logs", logDir.getAbsolutePath());
command.setFileSystem(new EsFileSystem(props));
command.setEsYmlSettings(mock(EsYmlSettings.class));
command.setEsJvmOptions(mock(EsJvmOptions.class));
command.setLog4j2Properties(new Properties());
command.setEsInstallation(new EsInstallation(props)
.setEsYmlSettings(mock(EsYmlSettings.class))
.setEsJvmOptions(mock(EsJvmOptions.class))
.setLog4j2Properties(new Properties()));
return command;
}


+ 11
- 0
server/sonar-process/src/main/java/org/sonar/process/System2.java View File

@@ -20,6 +20,7 @@
package org.sonar.process;

import java.util.Map;
import org.apache.commons.lang.SystemUtils;

/**
* An interface allowing to wrap around static call to {@link System} class.
@@ -35,6 +36,11 @@ public interface System2 {
public String getenv(String name) {
return System.getenv(name);
}

@Override
public boolean isOsWindows() {
return SystemUtils.IS_OS_WINDOWS;
}
};

/**
@@ -46,4 +52,9 @@ public interface System2 {
* Proxy to {@link System#getenv(String)}.
*/
String getenv(String name);

/**
* True if this is MS Windows.
*/
boolean isOsWindows();
}

+ 21
- 18
server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java View File

@@ -51,6 +51,7 @@ public class CeHttpClientImpl implements CeHttpClient {

/**
* Connects to the specified JVM process and requests system information.
*
* @return the system info, or absent if the process is not up or if its HTTP URL
* is not registered into IPC.
*/
@@ -108,16 +109,17 @@ public class CeHttpClientImpl implements CeHttpClient {
.post(RequestBody.create(null, new byte[0]))
.url(url + "?level=" + newLogLevel.name())
.build();
okhttp3.Response response = new OkHttpClient().newCall(request).execute();
if (response.code() != 200) {
throw new IOException(
String.format(
"Failed to change log level in Compute Engine. Code was '%s' and response was '%s' for url '%s'",
response.code(),
response.body().string(),
url));
try (okhttp3.Response response = new OkHttpClient().newCall(request).execute()) {
if (response.code() != 200) {
throw new IOException(
String.format(
"Failed to change log level in Compute Engine. Code was '%s' and response was '%s' for url '%s'",
response.code(),
response.body().string(),
url));
}
return null;
}
return null;
}
}

@@ -145,16 +147,17 @@ public class CeHttpClientImpl implements CeHttpClient {
.post(RequestBody.create(null, new byte[0]))
.url(url)
.build();
okhttp3.Response response = new OkHttpClient().newCall(request).execute();
if (response.code() != 200) {
throw new IOException(
String.format(
"Failed to trigger refresh of CE Worker count. Code was '%s' and response was '%s' for url '%s'",
response.code(),
response.body().string(),
url));
try (okhttp3.Response response = new OkHttpClient().newCall(request).execute()) {
if (response.code() != 200) {
throw new IOException(
String.format(
"Failed to trigger refresh of CE Worker count. Code was '%s' and response was '%s' for url '%s'",
response.code(),
response.body().string(),
url));
}
return null;
}
return null;
}
}


+ 8
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java View File

@@ -23,6 +23,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
@@ -94,7 +95,7 @@ public class IssueCreationDateCalculator extends IssueVisitor {
if (pluginKey == null) {
return false;
}
ScannerPlugin scannerPlugin = Optional.ofNullable(analysisMetadataHolder.getScannerPluginsByKey().get(pluginKey))
.orElseThrow(illegalStateException("The rule %s is declared to come from plugin %s, but this plugin was not used by scanner.", activeRule.getRuleKey(), pluginKey));
return pluginIsNew(scannerPlugin, lastAnalysisDate)
@@ -121,7 +122,7 @@ public class IssueCreationDateCalculator extends IssueVisitor {

private Optional<Date> getScmChangeDate(Component component, DefaultIssue issue) {
return getScmInfo(component)
.flatMap(scmInfo -> getChangeset(scmInfo, issue))
.flatMap(scmInfo -> getChangeset(component, scmInfo, issue))
.map(IssueCreationDateCalculator::getChangeDate);
}

@@ -133,7 +134,7 @@ public class IssueCreationDateCalculator extends IssueVisitor {
return toJavaUtilOptional(scmInfoRepository.getScmInfo(component));
}

private static Optional<Changeset> getChangeset(ScmInfo scmInfo, DefaultIssue issue) {
private static Optional<Changeset> getChangeset(Component component, ScmInfo scmInfo, DefaultIssue issue) {
Set<Integer> involvedLines = new HashSet<>();
DbIssues.Locations locations = issue.getLocations();
if (locations != null) {
@@ -142,7 +143,10 @@ public class IssueCreationDateCalculator extends IssueVisitor {
}
for (Flow f : locations.getFlowList()) {
for (Location l : f.getLocationList()) {
addLines(involvedLines, l.getTextRange());
if (Objects.equals(l.getComponentId(), component.getUuid())) {
// Ignore locations in other files, since it is currently not very common, and this is hard to load SCM by component UUID.
addLines(involvedLines, l.getTextRange());
}
}
}
if (!involvedLines.isEmpty()) {

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java View File

@@ -45,7 +45,7 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository {

@Override
public Optional<ScmInfo> getScmInfo(Component component) {
requireNonNull(component, "Component cannot be bull");
requireNonNull(component, "Component cannot be null");
return initializeScmInfoForComponent(component);
}


+ 10
- 2
server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java View File

@@ -21,10 +21,12 @@
package org.sonar.server.telemetry;

import java.io.IOException;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.sonar.api.config.Configuration;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.log.Logger;
@@ -47,7 +49,7 @@ public class TelemetryClient {

void upload(String json) throws IOException {
Request request = buildHttpRequest(json);
okHttpClient.newCall(request).execute();
execute(okHttpClient.newCall(request));
}

void optOut(String json) {
@@ -57,7 +59,7 @@ public class TelemetryClient {
request.delete(body);

try {
okHttpClient.newCall(request.build()).execute();
execute(okHttpClient.newCall(request.build()));
} catch (IOException e) {
LOG.debug("Error when sending opt-out usage statistics: {}", e.getMessage());
}
@@ -75,4 +77,10 @@ public class TelemetryClient {
return config.get(PROP_URL).orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", PROP_URL)));
}

private static void execute(Call call) throws IOException {
try (Response ignored = call.execute()) {
// auto close connection to avoid leaked connection
}
}

}

+ 34
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java View File

@@ -54,6 +54,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class IssueCreationDateCalculatorTest {
private static final String COMPONENT_UUID = "ab12";

@Rule
public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();

@@ -78,6 +80,7 @@ public class IssueCreationDateCalculatorTest {
issueUpdater = mock(IssueFieldsSetter.class);
activeRulesHolder = mock(ActiveRulesHolder.class);
component = mock(Component.class);
when(component.getUuid()).thenReturn(COMPONENT_UUID);
ruleKey = RuleKey.of("reop", "rule");
issue = mock(DefaultIssue.class);
activeRule = mock(ActiveRule.class);
@@ -253,8 +256,8 @@ public class IssueCreationDateCalculatorTest {
Flow.Builder secondary = Flow.newBuilder().addLocation(Location.newBuilder().setTextRange(range(4, 5)));
builder.addFlow(secondary).build();
Flow.Builder flow = Flow.newBuilder()
.addLocation(Location.newBuilder().setTextRange(range(6, 7)))
.addLocation(Location.newBuilder().setTextRange(range(8, 9)));
.addLocation(Location.newBuilder().setTextRange(range(6, 7)).setComponentId(COMPONENT_UUID))
.addLocation(Location.newBuilder().setTextRange(range(8, 9)).setComponentId(COMPONENT_UUID));
builder.addFlow(flow).build();
when(issue.getLocations()).thenReturn(builder.build());
withScmAt(2, 1200L);
@@ -271,6 +274,35 @@ public class IssueCreationDateCalculatorTest {
assertChangeOfCreationDateTo(1900L);
}

@Test
public void should_ignore_flows_location_outside_current_file_when_backdating() {
analysisMetadataHolder.setBaseAnalysis(null);
currentAnalysisIs(3000L);

newIssue();
Builder builder = DbIssues.Locations.newBuilder()
.setTextRange(range(2, 3));
Flow.Builder secondary = Flow.newBuilder().addLocation(Location.newBuilder().setTextRange(range(4, 5)));
builder.addFlow(secondary).build();
Flow.Builder flow = Flow.newBuilder()
.addLocation(Location.newBuilder().setTextRange(range(6, 7)).setComponentId(COMPONENT_UUID))
.addLocation(Location.newBuilder().setTextRange(range(8, 9)).setComponentId("another"));
builder.addFlow(flow).build();
when(issue.getLocations()).thenReturn(builder.build());
withScmAt(2, 1200L);
withScmAt(3, 1300L);
withScmAt(4, 1400L);
withScmAt(5, 1500L);
withScmAt(6, 1600L);
withScmAt(7, 1700L);
withScmAt(8, 1800L);
withScmAt(9, 1900L);

run();

assertChangeOfCreationDateTo(1700L);
}

private org.sonar.db.protobuf.DbCommons.TextRange.Builder range(int startLine, int endLine) {
return TextRange.newBuilder().setStartLine(startLine).setEndLine(endLine);
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java View File

@@ -120,7 +120,7 @@ public class ScmInfoRepositoryImplTest {
@Test
public void fail_with_NPE_when_component_is_null() throws Exception {
thrown.expect(NullPointerException.class);
thrown.expectMessage("Component cannot be bull");
thrown.expectMessage("Component cannot be null");

underTest.getScmInfo(null);
}

+ 1
- 0
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css View File

@@ -19,6 +19,7 @@

.navbar-login {
margin-right: -10px;
white-space: nowrap;
}

.navbar-avatar {

+ 4
- 1
server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx View File

@@ -48,7 +48,10 @@ export default class ScreenPositionHelper extends React.PureComponent<Props> {
if (!containerPos) {
return { top: 0, left: 0 };
}
return { top: window.scrollY + containerPos.top, left: window.scrollX + containerPos.left };
return {
top: window.pageYOffset + containerPos.top,
left: window.pageXOffset + containerPos.left
};
};

render() {

+ 10
- 2
travis.sh View File

@@ -163,7 +163,11 @@ BUILD)
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.login=$SONAR_TOKEN \
-Dsonar.branch.name=$TRAVIS_BRANCH \
-Dsonar.projectVersion=$INITIAL_VERSION
-Dsonar.projectVersion=$INITIAL_VERSION \
-Dsonar.analysis.buildNumber=$TRAVIS_BUILD_NUMBER \
-Dsonar.analysis.pipeline=$TRAVIS_BUILD_NUMBER \
-Dsonar.analysis.sha1=$TRAVIS_COMMIT \
-Dsonar.analysis.repository=$TRAVIS_REPO_SLUG

elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ -n "${GITHUB_TOKEN:-}" ]; then
echo 'Build and analyze internal pull request'
@@ -190,7 +194,11 @@ BUILD)
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.login=$SONAR_TOKEN \
-Dsonar.branch.name=$TRAVIS_PULL_REQUEST_BRANCH \
-Dsonar.branch.target=$TRAVIS_BRANCH
-Dsonar.branch.target=$TRAVIS_BRANCH \
-Dsonar.analysis.buildNumber=$TRAVIS_BUILD_NUMBER \
-Dsonar.analysis.pipeline=$TRAVIS_BUILD_NUMBER \
-Dsonar.analysis.sha1=$TRAVIS_COMMIT \
-Dsonar.analysis.repository=$TRAVIS_REPO_SLUG
fi
else
echo 'Build feature branch or external pull request'

Loading…
Cancel
Save