]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9908 on Windows, launch Elasticsearch directly, without batch files (#2642)
authorDaniel Schwarz <bartfastiel@users.noreply.github.com>
Tue, 10 Oct 2017 14:10:19 +0000 (16:10 +0200)
committerGitHub <noreply@github.com>
Tue, 10 Oct 2017 14:10:19 +0000 (16:10 +0200)
SONAR-9908 on Windows, launch Elasticsearch directly, without batch files

When starting the batch files on windows, we were not able to shutdown Elasticsearch gracefully.

21 files changed:
server/sonar-main/src/main/java/org/sonar/application/SchedulerImpl.java
server/sonar-main/src/main/java/org/sonar/application/command/AbstractCommand.java
server/sonar-main/src/main/java/org/sonar/application/command/CommandFactory.java
server/sonar-main/src/main/java/org/sonar/application/command/CommandFactoryImpl.java
server/sonar-main/src/main/java/org/sonar/application/command/EsCommand.java [deleted file]
server/sonar-main/src/main/java/org/sonar/application/command/EsScriptCommand.java [new file with mode: 0644]
server/sonar-main/src/main/java/org/sonar/application/command/JavaCommand.java
server/sonar-main/src/main/java/org/sonar/application/es/EsFileSystem.java [deleted file]
server/sonar-main/src/main/java/org/sonar/application/es/EsInstallation.java [new file with mode: 0644]
server/sonar-main/src/main/java/org/sonar/application/es/EsSettings.java
server/sonar-main/src/main/java/org/sonar/application/process/EsProcessMonitor.java
server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncher.java
server/sonar-main/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java
server/sonar-main/src/test/java/org/sonar/application/SchedulerImplTest.java
server/sonar-main/src/test/java/org/sonar/application/command/AbstractCommandTest.java
server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java
server/sonar-main/src/test/java/org/sonar/application/es/EsFileSystemTest.java [deleted file]
server/sonar-main/src/test/java/org/sonar/application/es/EsInstallationTest.java [new file with mode: 0644]
server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java
server/sonar-main/src/test/java/org/sonar/application/process/EsProcessMonitorTest.java
server/sonar-main/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java

index 943f5de6efaeba8c8122f9e0c6e66849d0916866..c1f04ef837d11cc6d967f9d4fe47a3366b014117 100644 (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);
     });
   }
index 2da97bb8f8ff8da2ebcced21b118904b4162111c..d9520742937dcc601bc8270eca9154636e7d407e 100644 (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;
+  }
 }
index e55f364be1177f55bdf101217ba8fc2c78ba87a7..4342d3bf0e44a5d326d48c1da335f80fa081054f 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.application.command;
 
 public interface CommandFactory {
 
-  EsCommand createEsCommand();
+  AbstractCommand createEsCommand();
 
   JavaCommand createWebCommand(boolean leader);
 
index 0df586aa7f03b10aeb3c98d8ad25328beeb2ae85..ef09996c8cc937bd9cb0c6a1902ef0fad5f410f5 100644 (file)
@@ -23,15 +23,16 @@ 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.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
 import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
 import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
 import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
@@ -67,27 +68,69 @@ public class CommandFactoryImpl implements CommandFactory {
   }
 
   @Override
-  public EsCommand createEsCommand() {
-    EsFileSystem esFileSystem = new EsFileSystem(props);
-    if (!esFileSystem.getExecutable().exists()) {
+  public AbstractCommand<?> createEsCommand() {
+    if (IS_OS_WINDOWS) {
+      return createEsCommandForWindows();
+    }
+    return createEsCommandForUnix();
+  }
+
+  private EsScriptCommand createEsCommandForUnix() {
+    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())
+    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))
       .setClusterName(settingsMap.get("cluster.name"))
       .setHost(settingsMap.get("network.host"))
-      .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
-      .addEsOption("-Epath.conf=" + esFileSystem.getConfDirectory().getAbsolutePath())
+      .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")));
+
+    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 = new EsInstallation(props);
+    if (!esInstallation.getExecutable().exists()) {
+      throw new IllegalStateException("Cannot find elasticsearch binary");
+    }
+    Map<String, String> settingsMap = new EsSettings(props, esInstallation, System2.INSTANCE).build();
+
+    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())
+      .setClusterName(settingsMap.get("cluster.name"))
+      .setHost(settingsMap.get("network.host"))
+      .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")));
+
+    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);
   }
 
@@ -101,6 +144,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 +171,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")
diff --git a/server/sonar-main/src/main/java/org/sonar/application/command/EsCommand.java b/server/sonar-main/src/main/java/org/sonar/application/command/EsCommand.java
deleted file mode 100644 (file)
index d44e272..0000000
+++ /dev/null
@@ -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;
-  }
-}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/command/EsScriptCommand.java b/server/sonar-main/src/main/java/org/sonar/application/command/EsScriptCommand.java
new file mode 100644 (file)
index 0000000..72e6f09
--- /dev/null
@@ -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;
+  }
+}
index 5cee5a95e334bc503ccef435eae71f94c5f104f3..864a9033b19cf224c4ea98c8319af9f71c2ca8dd 100644 (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() +
diff --git a/server/sonar-main/src/main/java/org/sonar/application/es/EsFileSystem.java b/server/sonar-main/src/main/java/org/sonar/application/es/EsFileSystem.java
deleted file mode 100644 (file)
index 1fb91dc..0000000
+++ /dev/null
@@ -1,114 +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.es;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-/**
- * Holds {@link File} to the various directories of ElasticSearch distribution embedded in SonarQube and provides
- * {@link File} objects to the various files of it SonarQube cares about.
- *
- * <p>
- * This class does not ensure files nor directories actually exist.
- * </p>
- */
-public class EsFileSystem {
-  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) {
-    File sqHomeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
-
-    this.homeDirectory = new File(sqHomeDir, "elasticsearch");
-    this.outdatedSearchDirectories = buildOutdatedSearchDirs(props);
-    this.dataDirectory = buildDataDir(props);
-    this.confDirectory = buildConfDir(props);
-    this.logDirectory = buildLogPath(props);
-  }
-
-  private static List<File> buildOutdatedSearchDirs(Props props) {
-    String dataPath = props.nonNullValue(ProcessProperties.PATH_DATA);
-    return Collections.singletonList(new File(dataPath, "es"));
-  }
-
-  private static File buildDataDir(Props props) {
-    String dataPath = props.nonNullValue(ProcessProperties.PATH_DATA);
-    return new File(dataPath, "es5");
-  }
-
-  private static File buildLogPath(Props props) {
-    return props.nonNullValueAsFile(ProcessProperties.PATH_LOGS);
-  }
-
-  private static File buildConfDir(Props props) {
-    File tempPath = props.nonNullValueAsFile(ProcessProperties.PATH_TEMP);
-    return new File(new File(tempPath, "conf"), "es");
-  }
-
-  public File getHomeDirectory() {
-    return homeDirectory;
-  }
-
-  public List<File> getOutdatedSearchDirectories() {
-    return Collections.unmodifiableList(outdatedSearchDirectories);
-  }
-
-  public File getDataDirectory() {
-    return dataDirectory;
-  }
-
-  public File getConfDirectory() {
-    return confDirectory;
-  }
-
-  public File getLogDirectory() {
-    return logDirectory;
-  }
-
-  public File getExecutable() {
-    return new File(homeDirectory, "bin/" + getExecutableName());
-  }
-
-  private static String getExecutableName() {
-    if (System.getProperty("os.name").startsWith("Windows")) {
-      return "elasticsearch.bat";
-    }
-    return "elasticsearch";
-  }
-
-  public File getLog4j2Properties() {
-    return new File(confDirectory, "log4j2.properties");
-  }
-
-  public File getElasticsearchYml() {
-    return new File(confDirectory, "elasticsearch.yml");
-  }
-
-  public File getJvmOptions() {
-    return new File(confDirectory, "jvm.options");
-  }
-}
diff --git a/server/sonar-main/src/main/java/org/sonar/application/es/EsInstallation.java b/server/sonar-main/src/main/java/org/sonar/application/es/EsInstallation.java
new file mode 100644 (file)
index 0000000..b7eb3b4
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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.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;
+
+/**
+ * Holds {@link File} to the various directories of ElasticSearch distribution embedded in SonarQube and provides
+ * {@link File} objects to the various files of it SonarQube cares about.
+ *
+ * <p>
+ * This class does not ensure files nor directories actually exist.
+ * </p>
+ */
+public class EsInstallation {
+  private final File homeDirectory;
+  private final List<File> outdatedSearchDirectories;
+  private final File dataDirectory;
+  private final File confDirectory;
+  private final File logDirectory;
+  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");
+    this.outdatedSearchDirectories = buildOutdatedSearchDirs(props);
+    this.dataDirectory = buildDataDir(props);
+    this.confDirectory = buildConfDir(props);
+    this.logDirectory = buildLogPath(props);
+  }
+
+  private static List<File> buildOutdatedSearchDirs(Props props) {
+    String dataPath = props.nonNullValue(ProcessProperties.PATH_DATA);
+    return Collections.singletonList(new File(dataPath, "es"));
+  }
+
+  private static File buildDataDir(Props props) {
+    String dataPath = props.nonNullValue(ProcessProperties.PATH_DATA);
+    return new File(dataPath, "es5");
+  }
+
+  private static File buildLogPath(Props props) {
+    return props.nonNullValueAsFile(ProcessProperties.PATH_LOGS);
+  }
+
+  private static File buildConfDir(Props props) {
+    File tempPath = props.nonNullValueAsFile(ProcessProperties.PATH_TEMP);
+    return new File(new File(tempPath, "conf"), "es");
+  }
+
+  public File getHomeDirectory() {
+    return homeDirectory;
+  }
+
+  public List<File> getOutdatedSearchDirectories() {
+    return Collections.unmodifiableList(outdatedSearchDirectories);
+  }
+
+  public File getDataDirectory() {
+    return dataDirectory;
+  }
+
+  public File getConfDirectory() {
+    return confDirectory;
+  }
+
+  public File getLogDirectory() {
+    return logDirectory;
+  }
+
+  public File getExecutable() {
+    return new File(homeDirectory, "bin/" + getExecutableName());
+  }
+
+  private static String getExecutableName() {
+    if (System.getProperty("os.name").startsWith("Windows")) {
+      return "elasticsearch.bat";
+    }
+    return "elasticsearch";
+  }
+
+  public File getLog4j2PropertiesLocation() {
+    return new File(confDirectory, "log4j2.properties");
+  }
+
+  public File getElasticsearchYml() {
+    return new File(confDirectory, "elasticsearch.yml");
+  }
+
+  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;
+  }
+}
index e8dc7e3b6eb7308bb85cbbc3d5cc09334021ccc8..79e949163cdd890706b05cb53fbf3c0760330732 100644 (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;
 
index f4b3ad9240de44b210378193aaf1f9da21c6425b..3e3c13582edb60ca8d8aa2d634762351449c5ddc 100644 (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));
index c0ca8ec7825976a69ac3d529173929a52b283b7c..d420f368c92c7dd3555ec37b22318fadd30a7ea4 100644 (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);
 }
index 802d53bdf162d3f47fb2f4fff9e54e7680576e23..2a50850d373b5b1324ee06ed82c17cfeb802e366 100644 (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);
   }
index a3125ad6823ba1c6173e203cb78e0c628f29d335..76fc63ea42af93d85d60ab531489d4c4494cb1a6 100644 (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) {
index c5ff76bc4dc3dd44ee5856c5856e09876d8adc21..efecd933874c6a73e07b66eb8a3b9a7f1788e50d 100644 (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();
index ceb13a08dac81df8988319b8070558dd9ef0f3cf..6058590a20d42d289dff5c493c13b49590f9752c 100644 (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;
@@ -109,24 +110,24 @@ public class CommandFactoryImplTest {
     Properties props = new Properties();
     props.setProperty("sonar.search.host", "localhost");
 
-    EsCommand esCommand = newFactory(props).createEsCommand();
+    AbstractCommand esCommand = newFactory(props).createEsCommand();
+    EsInstallation esConfig = esCommand.getEsInstallation();
 
-    assertThat(esCommand.getClusterName()).isEqualTo("sonarqube");
-    assertThat(esCommand.getHost()).isNotEmpty();
-    assertThat(esCommand.getPort()).isEqualTo(9001);
-    assertThat(esCommand.getEsJvmOptions().getAll())
+    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 +143,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 +173,7 @@ 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"))
diff --git a/server/sonar-main/src/test/java/org/sonar/application/es/EsFileSystemTest.java b/server/sonar-main/src/test/java/org/sonar/application/es/EsFileSystemTest.java
deleted file mode 100644 (file)
index b2ae4c9..0000000
+++ /dev/null
@@ -1,193 +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.es;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EsFileSystemTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Test
-  public void constructor_fails_with_IAE_if_sq_home_property_is_not_defined() {
-    Props props = new Props(new Properties());
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Property sonar.path.home is not set");
-
-    new EsFileSystem(props);
-  }
-
-  @Test
-  public void constructor_fails_with_IAE_if_temp_dir_property_is_not_defined() throws IOException {
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Property sonar.path.temp is not set");
-
-    new EsFileSystem(props);
-  }
-
-  @Test
-  public void constructor_fails_with_IAE_if_data_dir_property_is_not_defined() throws IOException {
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Missing property: sonar.path.data");
-
-    new EsFileSystem(props);
-  }
-
-  @Test
-  public void getHomeDirectory_is_elasticsearch_subdirectory_of_sq_home_directory() throws IOException {
-    File sqHomeDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getHomeDirectory()).isEqualTo(new File(sqHomeDir, "elasticsearch"));
-  }
-
-  @Test
-  public void override_data_dir() throws Exception {
-    File sqHomeDir = temp.newFolder();
-    File tempDir = temp.newFolder();
-    File dataDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getDataDirectory()).isEqualTo(new File(dataDir, "es5"));
-  }
-
-  @Test
-  public void getLogDirectory_is_configured_with_non_nullable_PATH_LOG_variable() throws IOException {
-    File sqHomeDir = temp.newFolder();
-    File logDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getLogDirectory()).isEqualTo(logDir);
-  }
-
-  @Test
-  public void conf_directory_is_conf_es_subdirectory_of_sq_temp_directory() throws IOException {
-    File tempDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getConfDirectory()).isEqualTo(new File(tempDir, "conf/es"));
-  }
-
-  @Test
-  public void getExecutable_resolve_executable_for_platform() throws IOException {
-    File sqHomeDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    if (System.getProperty("os.name").startsWith("Windows")) {
-      assertThat(underTest.getExecutable()).isEqualTo(new File(sqHomeDir, "elasticsearch/bin/elasticsearch.bat"));
-    } else {
-      assertThat(underTest.getExecutable()).isEqualTo(new File(sqHomeDir, "elasticsearch/bin/elasticsearch"));
-    }
-  }
-
-  @Test
-  public void getLog4j2Properties_is_in_es_conf_directory() throws IOException {
-    File tempDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getLog4j2Properties()).isEqualTo(new File(tempDir, "conf/es/log4j2.properties"));
-  }
-
-  @Test
-  public void getElasticsearchYml_is_in_es_conf_directory() throws IOException {
-    File tempDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getElasticsearchYml()).isEqualTo(new File(tempDir, "conf/es/elasticsearch.yml"));
-  }
-
-  @Test
-  public void getJvmOptions_is_in_es_conf_directory() throws IOException {
-    File tempDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
-
-    EsFileSystem underTest = new EsFileSystem(props);
-
-    assertThat(underTest.getJvmOptions()).isEqualTo(new File(tempDir, "conf/es/jvm.options"));
-  }
-}
diff --git a/server/sonar-main/src/test/java/org/sonar/application/es/EsInstallationTest.java b/server/sonar-main/src/test/java/org/sonar/application/es/EsInstallationTest.java
new file mode 100644 (file)
index 0000000..3821d40
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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.es;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EsInstallationTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void constructor_fails_with_IAE_if_sq_home_property_is_not_defined() {
+    Props props = new Props(new Properties());
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Property sonar.path.home is not set");
+
+    new EsInstallation(props);
+  }
+
+  @Test
+  public void constructor_fails_with_IAE_if_temp_dir_property_is_not_defined() throws IOException {
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Property sonar.path.temp is not set");
+
+    new EsInstallation(props);
+  }
+
+  @Test
+  public void constructor_fails_with_IAE_if_data_dir_property_is_not_defined() throws IOException {
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Missing property: sonar.path.data");
+
+    new EsInstallation(props);
+  }
+
+  @Test
+  public void getHomeDirectory_is_elasticsearch_subdirectory_of_sq_home_directory() throws IOException {
+    File sqHomeDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getHomeDirectory()).isEqualTo(new File(sqHomeDir, "elasticsearch"));
+  }
+
+  @Test
+  public void override_data_dir() throws Exception {
+    File sqHomeDir = temp.newFolder();
+    File tempDir = temp.newFolder();
+    File dataDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getDataDirectory()).isEqualTo(new File(dataDir, "es5"));
+  }
+
+  @Test
+  public void getLogDirectory_is_configured_with_non_nullable_PATH_LOG_variable() throws IOException {
+    File sqHomeDir = temp.newFolder();
+    File logDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getLogDirectory()).isEqualTo(logDir);
+  }
+
+  @Test
+  public void conf_directory_is_conf_es_subdirectory_of_sq_temp_directory() throws IOException {
+    File tempDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getConfDirectory()).isEqualTo(new File(tempDir, "conf/es"));
+  }
+
+  @Test
+  public void getExecutable_resolve_executable_for_platform() throws IOException {
+    File sqHomeDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, sqHomeDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    if (System.getProperty("os.name").startsWith("Windows")) {
+      assertThat(underTest.getExecutable()).isEqualTo(new File(sqHomeDir, "elasticsearch/bin/elasticsearch.bat"));
+    } else {
+      assertThat(underTest.getExecutable()).isEqualTo(new File(sqHomeDir, "elasticsearch/bin/elasticsearch"));
+    }
+  }
+
+  @Test
+  public void getLog4j2Properties_is_in_es_conf_directory() throws IOException {
+    File tempDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getLog4j2PropertiesLocation()).isEqualTo(new File(tempDir, "conf/es/log4j2.properties"));
+  }
+
+  @Test
+  public void getElasticsearchYml_is_in_es_conf_directory() throws IOException {
+    File tempDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getElasticsearchYml()).isEqualTo(new File(tempDir, "conf/es/elasticsearch.yml"));
+  }
+
+  @Test
+  public void getJvmOptions_is_in_es_conf_directory() throws IOException {
+    File tempDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.PATH_DATA, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, temp.newFolder().getAbsolutePath());
+
+    EsInstallation underTest = new EsInstallation(props);
+
+    assertThat(underTest.getJvmOptions()).isEqualTo(new File(tempDir, "conf/es/jvm.options"));
+  }
+}
index ff6db104e8b3b7bd4620981483873c407954653a..3cc7580e2be0083e53391bae882cf6701c62d72b 100644 (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");
index 7af20a08cc1e405b3303a9dfbf324481f9d54fd6..3c80c1c24299b4c604606116e5cf8a84bb1bd950 100644 (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));
   }
index d98723eb892772e1d146c96d3649bbee8fce310c..2f6a39edfc168c9d7b7b79d111dc9764741cefac 100644 (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;
   }