You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProcessLauncherImplTest.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.application;
  21. import java.io.File;
  22. import java.io.FileReader;
  23. import java.io.IOException;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Properties;
  28. import java.util.Random;
  29. import org.junit.Rule;
  30. import org.junit.Test;
  31. import org.junit.rules.ExpectedException;
  32. import org.junit.rules.TemporaryFolder;
  33. import org.sonar.application.command.EsJvmOptions;
  34. import org.sonar.application.command.EsScriptCommand;
  35. import org.sonar.application.command.JavaCommand;
  36. import org.sonar.application.command.JvmOptions;
  37. import org.sonar.application.es.EsInstallation;
  38. import org.sonar.application.es.EsYmlSettings;
  39. import org.sonar.application.process.ManagedProcess;
  40. import org.sonar.process.ProcessId;
  41. import org.sonar.process.Props;
  42. import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
  43. import static org.assertj.core.api.Assertions.assertThat;
  44. import static org.assertj.core.data.MapEntry.entry;
  45. import static org.mockito.Mockito.RETURNS_MOCKS;
  46. import static org.mockito.Mockito.mock;
  47. import static org.mockito.Mockito.when;
  48. public class ProcessLauncherImplTest {
  49. @Rule
  50. public TemporaryFolder temp = new TemporaryFolder();
  51. @Rule
  52. public ExpectedException expectedException = ExpectedException.none();
  53. private AllProcessesCommands commands = mock(AllProcessesCommands.class, RETURNS_MOCKS);
  54. @Test
  55. public void launch_forks_a_new_process() throws Exception {
  56. File tempDir = temp.newFolder();
  57. TestProcessBuilder processBuilder = new TestProcessBuilder();
  58. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
  59. JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.ELASTICSEARCH, temp.newFolder());
  60. command.addClasspath("lib/*.class");
  61. command.addClasspath("lib/*.jar");
  62. command.setArgument("foo", "bar");
  63. command.setClassName("org.sonarqube.Main");
  64. command.setEnvVariable("VAR1", "valueOfVar1");
  65. command.setJvmOptions(new JvmOptions<>()
  66. .add("-Dfoo=bar")
  67. .add("-Dfoo2=bar2"));
  68. command.setEsInstallation(createEsInstallation());
  69. ManagedProcess monitor = underTest.launch(command);
  70. assertThat(monitor).isNotNull();
  71. assertThat(processBuilder.started).isTrue();
  72. assertThat(processBuilder.commands.get(0)).endsWith("java");
  73. assertThat(processBuilder.commands).containsSubsequence(
  74. "-Dfoo=bar",
  75. "-Dfoo2=bar2",
  76. "-cp",
  77. "lib/*.class" + System.getProperty("path.separator") + "lib/*.jar",
  78. "org.sonarqube.Main");
  79. assertThat(processBuilder.dir).isEqualTo(command.getWorkDir());
  80. assertThat(processBuilder.redirectErrorStream).isTrue();
  81. assertThat(processBuilder.environment)
  82. .contains(entry("VAR1", "valueOfVar1"))
  83. .containsAllEntriesOf(command.getEnvVariables());
  84. }
  85. @Test
  86. public void properties_are_passed_to_command_via_a_temporary_properties_file() throws Exception {
  87. File tempDir = temp.newFolder();
  88. TestProcessBuilder processBuilder = new TestProcessBuilder();
  89. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
  90. long gracefulStopTimeoutMs = 1 + new Random().nextInt(10_000);
  91. JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.WEB_SERVER, temp.newFolder())
  92. .setReadsArgumentsFromFile(true)
  93. .setArgument("foo", "bar")
  94. .setArgument("baz", "woo")
  95. .setJvmOptions(new JvmOptions<>())
  96. .setGracefulStopTimeoutMs(gracefulStopTimeoutMs);
  97. underTest.launch(command);
  98. String propsFilePath = processBuilder.commands.get(processBuilder.commands.size() - 1);
  99. File file = new File(propsFilePath);
  100. assertThat(file).exists().isFile();
  101. try (FileReader reader = new FileReader(file)) {
  102. Properties props = new Properties();
  103. props.load(reader);
  104. assertThat(props).containsOnly(
  105. entry("foo", "bar"),
  106. entry("baz", "woo"),
  107. entry("process.gracefulStopTimeout", gracefulStopTimeoutMs + ""),
  108. entry("process.key", ProcessId.WEB_SERVER.getKey()),
  109. entry("process.index", String.valueOf(ProcessId.WEB_SERVER.getIpcIndex())),
  110. entry("process.sharedDir", tempDir.getAbsolutePath()));
  111. }
  112. }
  113. @Test
  114. public void temporary_properties_file_can_be_avoided() throws Exception {
  115. File tempDir = temp.newFolder();
  116. TestProcessBuilder processBuilder = new TestProcessBuilder();
  117. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
  118. JavaCommand<JvmOptions> command = new JavaCommand<>(ProcessId.WEB_SERVER, temp.newFolder());
  119. command.setReadsArgumentsFromFile(false);
  120. command.setArgument("foo", "bar");
  121. command.setArgument("baz", "woo");
  122. command.setJvmOptions(new JvmOptions<>());
  123. underTest.launch(command);
  124. String propsFilePath = processBuilder.commands.get(processBuilder.commands.size() - 1);
  125. File file = new File(propsFilePath);
  126. assertThat(file).doesNotExist();
  127. }
  128. @Test
  129. public void clean_up_old_es_data() throws Exception {
  130. File tempDir = temp.newFolder();
  131. File homeDir = temp.newFolder();
  132. File dataDir = temp.newFolder();
  133. File logDir = temp.newFolder();
  134. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> new TestProcessBuilder());
  135. EsScriptCommand command = createEsScriptCommand(tempDir, homeDir, dataDir, logDir);
  136. File outdatedEsDir = new File(dataDir, "es");
  137. assertThat(outdatedEsDir.mkdir()).isTrue();
  138. assertThat(outdatedEsDir.exists()).isTrue();
  139. underTest.launch(command);
  140. assertThat(outdatedEsDir.exists()).isFalse();
  141. }
  142. @Test
  143. public void do_not_fail_if_outdated_es_directory_does_not_exist() throws Exception {
  144. File tempDir = temp.newFolder();
  145. File homeDir = temp.newFolder();
  146. File dataDir = temp.newFolder();
  147. File logDir = temp.newFolder();
  148. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, TestProcessBuilder::new);
  149. EsScriptCommand command = createEsScriptCommand(tempDir, homeDir, dataDir, logDir);
  150. File outdatedEsDir = new File(dataDir, "es");
  151. assertThat(outdatedEsDir.exists()).isFalse();
  152. underTest.launch(command);
  153. assertThat(outdatedEsDir.exists()).isFalse();
  154. }
  155. @Test
  156. public void throw_ISE_if_command_fails() throws IOException {
  157. File tempDir = temp.newFolder();
  158. ProcessLauncherImpl.ProcessBuilder processBuilder = mock(ProcessLauncherImpl.ProcessBuilder.class, RETURNS_MOCKS);
  159. when(processBuilder.start()).thenThrow(new IOException("error"));
  160. ProcessLauncher underTest = new ProcessLauncherImpl(tempDir, commands, () -> processBuilder);
  161. expectedException.expect(IllegalStateException.class);
  162. expectedException.expectMessage("Fail to launch process [es]");
  163. underTest.launch(new JavaCommand(ProcessId.ELASTICSEARCH, temp.newFolder()));
  164. }
  165. private EsScriptCommand createEsScriptCommand(File tempDir, File homeDir, File dataDir, File logDir) throws IOException {
  166. EsScriptCommand command = new EsScriptCommand(ProcessId.ELASTICSEARCH, temp.newFolder());
  167. Props props = new Props(new Properties());
  168. props.set("sonar.path.temp", tempDir.getAbsolutePath());
  169. props.set("sonar.path.home", homeDir.getAbsolutePath());
  170. props.set("sonar.path.data", dataDir.getAbsolutePath());
  171. props.set("sonar.path.logs", logDir.getAbsolutePath());
  172. command.setEsInstallation(new EsInstallation(props)
  173. .setEsYmlSettings(mock(EsYmlSettings.class))
  174. .setEsJvmOptions(mock(EsJvmOptions.class))
  175. .setLog4j2Properties(new Properties())
  176. .setHost("localhost")
  177. .setPort(9001)
  178. .setClusterName("sonarqube"));
  179. return command;
  180. }
  181. private EsInstallation createEsInstallation() throws IOException {
  182. File tempFolder = this.temp.newFolder("temp");
  183. return new EsInstallation(new Props(new Properties())
  184. .set("sonar.path.home", this.temp.newFolder("home").getAbsolutePath())
  185. .set("sonar.path.data", this.temp.newFolder("data").getAbsolutePath())
  186. .set("sonar.path.temp", tempFolder.getAbsolutePath())
  187. .set("sonar.path.logs", this.temp.newFolder("logs").getAbsolutePath()))
  188. .setClusterName("cluster")
  189. .setPort(9001)
  190. .setHost("localhost")
  191. .setEsYmlSettings(new EsYmlSettings(new HashMap<>()))
  192. .setEsJvmOptions(new EsJvmOptions(new Props(new Properties()), tempFolder))
  193. .setLog4j2Properties(new Properties());
  194. }
  195. private static class TestProcessBuilder implements ProcessLauncherImpl.ProcessBuilder {
  196. private List<String> commands = null;
  197. private File dir = null;
  198. private Boolean redirectErrorStream = null;
  199. private final Map<String, String> environment = new HashMap<>();
  200. private boolean started = false;
  201. @Override
  202. public List<String> command() {
  203. return commands;
  204. }
  205. @Override
  206. public TestProcessBuilder command(List<String> commands) {
  207. this.commands = commands;
  208. return this;
  209. }
  210. @Override
  211. public TestProcessBuilder directory(File dir) {
  212. this.dir = dir;
  213. return this;
  214. }
  215. @Override
  216. public Map<String, String> environment() {
  217. return environment;
  218. }
  219. @Override
  220. public TestProcessBuilder redirectErrorStream(boolean b) {
  221. this.redirectErrorStream = b;
  222. return this;
  223. }
  224. @Override
  225. public Process start() {
  226. this.started = true;
  227. return mock(Process.class);
  228. }
  229. }
  230. }