import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.util.OkHttpClientProvider;
+import org.sonar.server.util.Paths2Impl;
import org.sonar.server.view.index.ViewIndex;
import org.sonar.server.view.index.ViewIndexer;
import org.sonar.server.webhook.WebhookModule;
ServerFileSystemImpl.class,
new TempFolderProvider(),
System2.INSTANCE,
+ Paths2Impl.getInstance(),
Clock.systemDefaultZone(),
// DB
);
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
- + 26 // level 1
+ + 27 // level 1
+ 63 // content of DaoModule
+ 3 // content of EsModule
+ 51 // content of CorePropertyDefinitions
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.util;
+
+import java.net.URI;
+import java.nio.file.Path;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * An interface exposing with the same interface as {@link java.nio.file.Paths} which classes can get injected
+ * with instead of statically binding to {@link java.nio.file.Paths} class.
+ * <p>
+ * This interface can be used to improve testability of classes.
+ */
+@ServerSide
+@ComputeEngineSide
+public interface Paths2 {
+ /**
+ * @see java.nio.file.Paths#get(String, String...)
+ */
+ Path get(String first, String... more);
+
+ /**
+ * @see java.nio.file.Paths#get(URI)
+ */
+ Path get(URI uri);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.util;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public final class Paths2Impl implements Paths2 {
+ private static final Paths2 INSTANCE = new Paths2Impl();
+
+ public static Paths2 getInstance() {
+ return INSTANCE;
+ }
+
+ private Paths2Impl() {
+ // prevents instantiation and subclassing, use getInstance() instead
+ }
+
+ @Override
+ public Path get(String first, String... more) {
+ return Paths.get(first, more);
+ }
+
+ @Override
+ public Path get(URI uri) {
+ return Paths.get(uri);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.util;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.net.URI;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class Paths2ImplTest {
+ @Test
+ public void getInstance_returns_the_same_object_for_every_call() {
+ assertThat(Paths2Impl.getInstance())
+ .isSameAs(Paths2Impl.getInstance())
+ .isSameAs(Paths2Impl.getInstance());
+ }
+
+ @Test
+ @UseDataProvider("getStringParameters")
+ public void get_String_returns_result_of_Paths_get(String first, String... others) {
+ assertThat(Paths2Impl.getInstance().get(first, others))
+ .isEqualTo(Paths.get(first, others));
+ }
+
+ @DataProvider
+ public static Object[][] getStringParameters() {
+ return new Object[][] {
+ {"a", new String[] {}},
+ {"a", new String[] {"b"}},
+ {"a", new String[] {"b", "c"}}
+ };
+ }
+
+ @Test
+ @UseDataProvider("getURIParameter")
+ public void get_URI_returns_result_of_Paths_get(URI uri) {
+ assertThat(Paths2Impl.getInstance().get(uri))
+ .isEqualTo(Paths.get(uri));
+ }
+
+ @DataProvider
+ public static Object[][] getURIParameter() {
+ return new Object[][] {
+ {URI.create("file:///")},
+ {URI.create("file:///a")},
+ {URI.create("file:///b/c")}
+ };
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.platform;
+
+public interface DockerSupport {
+ /**
+ * @return {@code true} if we can detect that SQ is running inside a docker container
+ */
+ boolean isRunningInDocker();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.platform;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.stream.Stream;
+import org.sonar.server.util.Paths2;
+
+public class DockerSupportImpl implements DockerSupport {
+ private final Paths2 paths2;
+
+ public DockerSupportImpl(Paths2 paths2) {
+ this.paths2 = paths2;
+ }
+
+ @Override
+ public boolean isRunningInDocker() {
+ try (Stream<String> stream = Files.lines(paths2.get("/proc/1/cgroup"))) {
+ return stream.anyMatch(line -> line.contains("/docker"));
+ } catch (IOException e) {
+ return false;
+ }
+ }
+}
EsIndexesSection.class,
LoggingSection.class,
PluginsSection.class,
- SettingsSection.class
+ SettingsSection.class,
+ DockerSupportImpl.class
);
if (standalone) {
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.authentication.IdentityProviderRepository;
import org.sonar.server.log.ServerLogging;
+import org.sonar.server.platform.DockerSupport;
import org.sonar.server.platform.OfficialDistribution;
import org.sonar.server.user.SecurityRealmFactory;
private final Server server;
private final ServerLogging serverLogging;
private final OfficialDistribution officialDistribution;
+ private final DockerSupport dockerSupport;
public StandaloneSystemSection(Configuration config, SecurityRealmFactory securityRealmFactory,
IdentityProviderRepository identityProviderRepository, Server server, ServerLogging serverLogging,
- OfficialDistribution officialDistribution) {
+ OfficialDistribution officialDistribution, DockerSupport dockerSupport) {
this.config = config;
this.securityRealmFactory = securityRealmFactory;
this.identityProviderRepository = identityProviderRepository;
this.server = server;
this.serverLogging = serverLogging;
this.officialDistribution = officialDistribution;
+ this.dockerSupport = dockerSupport;
}
@Override
setAttribute(protobuf, "Server ID", server.getId());
setAttribute(protobuf, "Version", getVersion());
+ setAttribute(protobuf, "Docker", dockerSupport.isRunningInDocker());
setAttribute(protobuf, "External User Authentication", getExternalUserAuthentication());
addIfNotEmpty(protobuf, "Accepted external identity providers", getEnabledIdentityProviders());
addIfNotEmpty(protobuf, "External identity providers whose users are allowed to sign themselves up", getAllowsToSignUpEnabledIdentityProviders());
import org.sonar.process.systeminfo.SystemInfoSection;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.authentication.IdentityProviderRepository;
+import org.sonar.server.platform.DockerSupport;
import org.sonar.server.user.SecurityRealmFactory;
import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
private final Server server;
private final SecurityRealmFactory securityRealmFactory;
private final IdentityProviderRepository identityProviderRepository;
+ private final DockerSupport dockerSupport;
public GlobalSystemSection(Configuration config, Server server, SecurityRealmFactory securityRealmFactory,
- IdentityProviderRepository identityProviderRepository) {
+ IdentityProviderRepository identityProviderRepository, DockerSupport dockerSupport) {
this.config = config;
this.server = server;
this.securityRealmFactory = securityRealmFactory;
this.identityProviderRepository = identityProviderRepository;
+ this.dockerSupport = dockerSupport;
}
@Override
protobuf.setName("System");
setAttribute(protobuf, "Server ID", server.getId());
+ setAttribute(protobuf, "Docker", dockerSupport.isRunningInDocker());
setAttribute(protobuf, "High Availability", true);
setAttribute(protobuf, "External User Authentication", getExternalUserAuthentication());
addIfNotEmpty(protobuf, "Accepted external identity providers", getEnabledIdentityProviders());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.server.platform;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.server.util.Paths2;
+
+import static java.lang.System.lineSeparator;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DockerSupportImplTest {
+ private static final String CGROUP_DIR = "/proc/1/cgroup";
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private Paths2 paths2 = mock(Paths2.class);
+ private DockerSupportImpl underTest = new DockerSupportImpl(paths2);
+
+ @Test
+ public void isInDocker_returns_false_if_cgroup_file_does_not_exist() throws IOException {
+ Path emptyFile = temporaryFolder.newFile().toPath();
+ Files.delete(emptyFile);
+ when(paths2.get(CGROUP_DIR)).thenReturn(emptyFile);
+
+ assertThat(underTest.isRunningInDocker()).isFalse();
+ }
+
+ @Test
+ public void isInDocker_returns_false_if_cgroup_file_is_empty() throws IOException {
+ Path emptyFile = temporaryFolder.newFile().toPath();
+ when(paths2.get(CGROUP_DIR)).thenReturn(emptyFile);
+
+ assertThat(underTest.isRunningInDocker()).isFalse();
+ }
+
+ @Test
+ public void isInDocker_returns_false_if_cgroup_dir_contains_no_file_with_slash_docker_string() throws IOException {
+ Path cgroupFile = temporaryFolder.newFile().toPath();
+ String content = "11:name=systemd:/" + lineSeparator() +
+ "10:hugetlb:/" + lineSeparator() +
+ "9:perf_event:/" + lineSeparator() +
+ "8:blkio:/" + lineSeparator() +
+ "7:freezer:/" + lineSeparator() +
+ "6:devices:/" + lineSeparator() +
+ "5:memory:/" + lineSeparator() +
+ "4:cpuacct:/" + lineSeparator() +
+ "3:cpu:/" + lineSeparator() +
+ "2:cpuset:/";
+ FileUtils.write(cgroupFile.toFile(), content, StandardCharsets.UTF_8);
+ when(paths2.get(CGROUP_DIR)).thenReturn(cgroupFile);
+
+ assertThat(underTest.isRunningInDocker()).isFalse();
+ }
+
+ @Test
+ public void isInDocker_returns_true_if_cgroup_dir_contains_file_with_slash_docker_string() throws IOException {
+ Path cgroupFile = temporaryFolder.newFile().toPath();
+ String content = "11:name=systemd:/" + lineSeparator() +
+ "10:hugetlb:/" + lineSeparator() +
+ "9:perf_event:/" + lineSeparator() +
+ "8:blkio:/" + lineSeparator() +
+ "7:freezer:/" + lineSeparator() +
+ "6:devices:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2" + lineSeparator() +
+ "5:memory:/" + lineSeparator() +
+ "4:cpuacct:/" + lineSeparator() +
+ "3:cpu:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2" + lineSeparator() +
+ "2:cpuset:/";
+ FileUtils.write(cgroupFile.toFile(), content, StandardCharsets.UTF_8);
+ when(paths2.get(CGROUP_DIR)).thenReturn(cgroupFile);
+
+ assertThat(underTest.isRunningInDocker()).isTrue();
+ }
+
+}
Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
assertThat(adapters)
- .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 17);
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 18);
}
@Test
underTest.configure(container);
- verifyConfigurationStandaloneSQ(container);
- }
-
- public void verifyConfigurationStandaloneSQ(ComponentContainer container) {
Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
assertThat(adapters)
- .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 11);
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 12);
}
}
*/
package org.sonar.server.platform.monitoring;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.Server;
import org.sonar.api.security.SecurityRealm;
import org.sonar.server.authentication.IdentityProviderRepositoryRule;
import org.sonar.server.authentication.TestIdentityProvider;
import org.sonar.server.log.ServerLogging;
+import org.sonar.server.platform.DockerSupport;
import org.sonar.server.platform.OfficialDistribution;
import org.sonar.server.user.SecurityRealmFactory;
import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
+@RunWith(DataProviderRunner.class)
public class StandaloneSystemSectionTest {
- private static final String SERVER_ID_PROPERTY = "Server ID";
- private static final String SERVER_ID_VALIDATED_PROPERTY = "Server ID validated";
-
@Rule
public IdentityProviderRepositoryRule identityProviderRepository = new IdentityProviderRepositoryRule();
private ServerLogging serverLogging = mock(ServerLogging.class);
private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
private OfficialDistribution officialDistribution = mock(OfficialDistribution.class);
+ private DockerSupport dockerSupport = mock(DockerSupport.class);
private StandaloneSystemSection underTest = new StandaloneSystemSection(settings.asConfig(), securityRealmFactory, identityProviderRepository, server,
- serverLogging, officialDistribution);
+ serverLogging, officialDistribution, dockerSupport);
@Before
- public void setUp() throws Exception {
+ public void setUp() {
when(serverLogging.getRootLoggerLevel()).thenReturn(LoggerLevel.DEBUG);
}
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThat(attribute(protobuf, "Processors").getLongValue()).isGreaterThan(0);
}
+
+ @Test
+ @UseDataProvider("trueOrFalse")
+ public void return_docker_flag_from_DockerSupport(boolean flag) {
+ when(dockerSupport.isRunningInDocker()).thenReturn(flag);
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThat(attribute(protobuf, "Docker").getBooleanValue()).isEqualTo(flag);
+ }
+
+ @DataProvider
+ public static Object[][] trueOrFalse() {
+ return new Object[][] {
+ {true},
+ {false}
+ };
+ }
}
*/
package org.sonar.server.platform.monitoring.cluster;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.Server;
import org.sonar.api.security.SecurityRealm;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.authentication.IdentityProviderRepositoryRule;
import org.sonar.server.authentication.TestIdentityProvider;
+import org.sonar.server.platform.DockerSupport;
import org.sonar.server.user.SecurityRealmFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
+@RunWith(DataProviderRunner.class)
public class GlobalSystemSectionTest {
@Rule
private Server server = mock(Server.class);
private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
+ private DockerSupport dockerSupport = mock(DockerSupport.class);
private GlobalSystemSection underTest = new GlobalSystemSection(settings.asConfig(),
- server, securityRealmFactory, identityProviderRepository);
+ server, securityRealmFactory, identityProviderRepository, dockerSupport);
@Test
public void name_is_not_empty() {
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThatAttributeIs(protobuf, "External identity providers whose users are allowed to sign themselves up", "GitHub");
}
+
+ @Test
+ @UseDataProvider("trueOrFalse")
+ public void get_docker_flag(boolean flag) {
+ when(dockerSupport.isRunningInDocker()).thenReturn(flag);
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Docker", flag);
+ }
+
+ @DataProvider
+ public static Object[][] trueOrFalse() {
+ return new Object[][] {
+ {true},
+ {false},
+ };
+ }
}
import org.sonar.api.utils.System2;
import org.sonar.api.utils.Version;
import org.sonar.server.util.GlobalLockManagerImpl;
+import org.sonar.server.util.Paths2Impl;
import org.sonar.server.util.TempFolderCleaner;
import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.core.extension.CoreExtensionRepositoryImpl;
TempFolderCleaner.class,
new TempFolderProvider(),
System2.INSTANCE,
+ Paths2Impl.getInstance(),
Clock.systemDefaultZone(),
// user session