123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 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 ch.qos.logback.classic.spi.ILoggingEvent;
- import com.google.common.collect.ImmutableSet;
- import java.io.File;
- import java.io.IOException;
- import java.util.Properties;
- import org.apache.commons.io.FileUtils;
- import org.junit.After;
- import org.junit.Before;
- import org.junit.Rule;
- 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.application.logging.ListAppender;
- import org.sonar.core.extension.ServiceLoaderWrapper;
- import org.sonar.process.ProcessId;
- import org.sonar.process.ProcessProperties;
- import org.sonar.process.Props;
- import org.sonar.process.System2;
-
- import static org.assertj.core.api.Assertions.assertThat;
- import static org.assertj.core.api.Assertions.entry;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.when;
-
- public class CommandFactoryImplTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- private System2 system2 = Mockito.mock(System2.class);
- private JavaVersion javaVersion = Mockito.mock(JavaVersion.class);
- private File homeDir;
- private File tempDir;
- private File logsDir;
- private ListAppender listAppender;
-
- @Before
- public void setUp() throws Exception {
- homeDir = temp.newFolder();
- tempDir = temp.newFolder();
- logsDir = temp.newFolder();
- }
-
- @After
- public void tearDown() {
- if (listAppender != null) {
- ListAppender.detachMemoryAppenderToLoggerOf(CommandFactoryImpl.class, listAppender);
- }
- }
-
- @Test
- public void constructor_logs_no_warning_if_env_variable_JAVA_TOOL_OPTIONS_is_not_set() {
- attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);
-
- new CommandFactoryImpl(new Props(new Properties()), tempDir, system2, javaVersion);
-
- assertThat(listAppender.getLogs()).isEmpty();
- }
-
- @Test
- public void constructor_logs_warning_if_env_variable_JAVA_TOOL_OPTIONS_is_set() {
- when(system2.getenv("JAVA_TOOL_OPTIONS")).thenReturn("sds");
- attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);
-
- new CommandFactoryImpl(new Props(new Properties()), tempDir, system2, javaVersion);
-
- assertThat(listAppender.getLogs())
- .extracting(ILoggingEvent::getMessage)
- .containsOnly(
- "JAVA_TOOL_OPTIONS is defined but will be ignored. " +
- "Use properties sonar.*.javaOpts and/or sonar.*.javaAdditionalOpts in sonar.properties to change SQ JVM processes options");
- }
-
- @Test
- public void constructor_logs_warning_if_env_variable_ES_JAVA_OPTS_is_set() {
- when(system2.getenv("ES_JAVA_OPTS")).thenReturn("xyz");
- attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);
-
- new CommandFactoryImpl(new Props(new Properties()), tempDir, system2, javaVersion);
-
- assertThat(listAppender.getLogs())
- .extracting(ILoggingEvent::getMessage)
- .containsOnly(
- "ES_JAVA_OPTS is defined but will be ignored. " +
- "Use properties sonar.search.javaOpts and/or sonar.search.javaAdditionalOpts in sonar.properties to change SQ JVM processes options");
- }
-
- @Test
- public void createEsCommand_throws_ISE_if_es_binary_is_not_found() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Cannot find elasticsearch binary");
-
- newFactory(new Properties()).createEsCommand();
- }
-
- @Test
- public void createEsCommand_for_unix_returns_command_for_default_settings() throws Exception {
- when(system2.isOsWindows()).thenReturn(false);
- prepareEsFileSystem();
-
- Properties props = new Properties();
- props.setProperty("sonar.search.host", "localhost");
-
- AbstractCommand command = newFactory(props, system2).createEsCommand();
- assertThat(command).isInstanceOf(EsScriptCommand.class);
- EsScriptCommand esCommand = (EsScriptCommand) command;
- EsInstallation esConfig = esCommand.getEsInstallation();
-
- assertThat(esConfig.getHost()).isNotEmpty();
- assertThat(esConfig.getHttpPort()).isEqualTo(9001);
- assertThat(esConfig.getEsJvmOptions().getAll())
- // enforced values
- .contains("-XX:+UseConcMarkSweepGC", "-Dfile.encoding=UTF-8")
- // default settings
- .contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
- assertThat(esConfig.getEsYmlSettings()).isNotNull();
- assertThat(esConfig.getLog4j2Properties())
- .contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));
-
- File esConfDir = new File(tempDir, "conf/es");
- assertThat(esCommand.getEnvVariables())
- .contains(entry("ES_PATH_CONF", esConfDir.getAbsolutePath()))
- .contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
- .containsKey("JAVA_HOME");
- assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS", "ES_JAVA_OPTS");
-
- assertThat(esConfig.getEsJvmOptions().getAll())
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath());
- }
-
- @Test
- public void createEsCommand_for_windows_returns_command_for_default_settings() throws Exception {
- when(system2.isOsWindows()).thenReturn(true);
- prepareEsFileSystem();
-
- Properties props = new Properties();
- props.setProperty("sonar.search.host", "localhost");
-
- AbstractCommand command = newFactory(props, system2).createEsCommand();
- assertThat(command).isInstanceOf(JavaCommand.class);
- JavaCommand<?> esCommand = (JavaCommand<?>) command;
- EsInstallation esConfig = esCommand.getEsInstallation();
-
- assertThat(esConfig.getHost()).isNotEmpty();
- assertThat(esConfig.getHttpPort()).isEqualTo(9001);
- assertThat(esConfig.getEsJvmOptions().getAll())
- // enforced values
- .contains("-XX:+UseConcMarkSweepGC", "-Dfile.encoding=UTF-8")
- // default settings
- .contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
- assertThat(esConfig.getEsYmlSettings()).isNotNull();
-
- assertThat(esConfig.getLog4j2Properties())
- .contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));
-
- File esConfDir = new File(tempDir, "conf/es");
- assertThat(esCommand.getArguments()).isEmpty();
- assertThat(esCommand.getEnvVariables())
- .contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
- .containsKey("JAVA_HOME");
- assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS", "ES_JAVA_OPTS");
-
- assertThat(esConfig.getEsJvmOptions().getAll())
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath());
- assertThat(esCommand.getJvmOptions().getAll())
- .containsAll(esConfig.getEsJvmOptions().getAll())
- .contains("-Delasticsearch")
- .contains("-Des.path.home=" + new File(homeDir, "elasticsearch"))
- .contains("-Des.path.conf=" + esConfDir.getAbsolutePath());
- }
-
- @Test
- public void createEsCommand_returns_command_for_overridden_settings() throws Exception {
- prepareEsFileSystem();
-
- Properties props = new Properties();
- props.setProperty("sonar.search.host", "localhost");
- props.setProperty("sonar.cluster.name", "foo");
- props.setProperty("sonar.search.port", "1234");
- props.setProperty("sonar.search.javaOpts", "-Xms10G -Xmx10G");
-
- AbstractCommand esCommand = newFactory(props).createEsCommand();
- EsInstallation esConfig = esCommand.getEsInstallation();
-
- assertThat(esConfig.getHttpPort()).isEqualTo(1234);
- assertThat(esConfig.getEsJvmOptions().getAll())
- // enforced values
- .contains("-XX:+UseConcMarkSweepGC", "-Dfile.encoding=UTF-8")
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath())
- // user settings
- .contains("-Xms10G", "-Xmx10G")
- // default values disabled
- .doesNotContain("-XX:+HeapDumpOnOutOfMemoryError");
- }
-
- @Test
- public void createWebCommand_returns_command_for_default_settings() {
- JavaCommand command = newFactory(new Properties()).createWebCommand(true);
-
- assertThat(command.getClassName()).isEqualTo("org.sonar.server.app.WebServer");
- assertThat(command.getWorkDir().getAbsolutePath()).isEqualTo(homeDir.getAbsolutePath());
- assertThat(command.getClasspath()).hasSize(1).allMatch(p -> p.toString().startsWith("./lib/sonar-application-"));
- assertThat(command.getJvmOptions().getAll())
- // enforced values
- .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
- // default settings
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
- .contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
- assertThat(command.getProcessId()).isEqualTo(ProcessId.WEB_SERVER);
- assertThat(command.getEnvVariables())
- .isNotEmpty();
- assertThat(command.getArguments())
- // default settings
- .contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
- .contains(entry("sonar.cluster.enabled", "false"));
-
- assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
- }
-
- @Test
- public void createCeCommand_returns_command_for_default_settings() {
- JavaCommand command = newFactory(new Properties()).createCeCommand();
-
- assertThat(command.getClassName()).isEqualTo("org.sonar.ce.app.CeServer");
- assertThat(command.getWorkDir().getAbsolutePath()).isEqualTo(homeDir.getAbsolutePath());
- assertThat(command.getClasspath()).hasSize(1).allMatch(p -> p.toString().startsWith("./lib/sonar-application-"));
- assertThat(command.getJvmOptions().getAll())
- // enforced values
- .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
- // default settings
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
- .contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
- assertThat(command.getProcessId()).isEqualTo(ProcessId.COMPUTE_ENGINE);
- assertThat(command.getEnvVariables())
- .isNotEmpty();
- assertThat(command.getArguments())
- // default settings
- .contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
- .contains(entry("sonar.cluster.enabled", "false"));
-
- assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
- }
-
- @Test
- public void createWebCommand_configures_command_with_overridden_settings() {
- Properties props = new Properties();
- props.setProperty("sonar.web.port", "1234");
- props.setProperty("sonar.web.javaOpts", "-Xmx10G");
- JavaCommand command = newFactory(props).createWebCommand(true);
-
- assertThat(command.getJvmOptions().getAll())
- // enforced values
- .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
- // default settings
- .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
- // overridden values
- .contains("-Xmx10G")
- .doesNotContain("-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
- assertThat(command.getArguments())
- // default settings
- .contains(entry("sonar.web.javaOpts", "-Xmx10G"))
- .contains(entry("sonar.cluster.enabled", "false"));
-
- assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
- }
-
- @Test
- public void createWebCommand_adds_configured_jdbc_driver_to_classpath() throws Exception {
- Properties props = new Properties();
- File driverFile = temp.newFile();
- props.setProperty("sonar.jdbc.driverPath", driverFile.getAbsolutePath());
-
- JavaCommand command = newFactory(props).createWebCommand(true);
- assertThat(command.getClasspath()).hasSize(2);
- assertThat(command.getClasspath().get(0).toString()).startsWith("./lib/sonar-application-");
- assertThat(command.getClasspath().get(1)).isEqualTo(driverFile.getAbsolutePath());
- }
-
- private void prepareEsFileSystem() throws IOException {
- FileUtils.touch(new File(homeDir, "elasticsearch/bin/elasticsearch"));
- FileUtils.touch(new File(homeDir, "elasticsearch/bin/elasticsearch.bat"));
- }
-
- private CommandFactoryImpl newFactory(Properties userProps) {
- return newFactory(userProps, System2.INSTANCE);
- }
-
- private CommandFactoryImpl newFactory(Properties userProps, System2 system2) {
- Properties p = new Properties();
- p.setProperty("sonar.path.home", homeDir.getAbsolutePath());
- p.setProperty("sonar.path.temp", tempDir.getAbsolutePath());
- p.setProperty("sonar.path.logs", logsDir.getAbsolutePath());
- p.putAll(userProps);
-
- Props props = new Props(p);
- ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
- when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
- new ProcessProperties(serviceLoaderWrapper).completeDefaults(props);
- return new CommandFactoryImpl(props, tempDir, system2, javaVersion);
- }
-
- private <T> void attachMemoryAppenderToLoggerOf(Class<T> loggerClass) {
- this.listAppender = ListAppender.attachMemoryAppenderToLoggerOf(loggerClass);
- }
-
- }
|