diff options
author | Alain Kermis <alain.kermis@sonarsource.com> | 2023-06-08 16:37:41 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-06-13 20:03:39 +0000 |
commit | e1676e22d0f752fcc358aa630e3ce6651a404faa (patch) | |
tree | 17d19dbd33b5fec6c90fc44ecef926fbf0223549 /server/sonar-webserver-core/src/main/java | |
parent | 555abeb7d7c38477b11f7b3f18f35d7c668b5cff (diff) | |
download | sonarqube-e1676e22d0f752fcc358aa630e3ce6651a404faa.tar.gz sonarqube-e1676e22d0f752fcc358aa630e3ce6651a404faa.zip |
SONAR-19425 Update logic for SQ container context
Diffstat (limited to 'server/sonar-webserver-core/src/main/java')
3 files changed, 96 insertions, 13 deletions
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupport.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupport.java index 22a59f5207f..30c18a19b2b 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupport.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupport.java @@ -19,10 +19,15 @@ */ package org.sonar.server.platform; +import javax.annotation.CheckForNull; + public interface ContainerSupport { /** * @return {@code true} if we can detect that SQ is running inside a docker container */ boolean isRunningInContainer(); + @CheckForNull + String getContainerContext(); + } diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupportImpl.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupportImpl.java index 38997c558f5..e6d81ac1a9b 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupportImpl.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/ContainerSupportImpl.java @@ -19,27 +19,100 @@ */ package org.sonar.server.platform; -import java.io.IOException; -import java.nio.file.Files; -import java.util.stream.Stream; +import com.google.common.annotations.VisibleForTesting; +import java.util.Objects; +import java.util.Scanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.utils.System2; import org.sonar.server.util.Paths2; +import static java.nio.charset.StandardCharsets.UTF_8; + public class ContainerSupportImpl implements ContainerSupport { + + private static final Logger LOG = LoggerFactory.getLogger(ContainerSupportImpl.class); + private static final String CONTAINER_FILE_PATH = "/run/.containerenv"; + private static final String DOCKER = "docker"; + private static final String PODMAN = "podman"; + private static final String BUILDAH = "buildah"; + private static final String CONTAINER_D = "containerd"; + private static final String GENERAL_CONTAINER = "general_container"; + + private static final String[] MOUNT_GREP_COMMAND = {"bash", "-c", "mount | grep 'overlay on /'"}; + private static final String[] CAT_COMMAND = {"bash", "-c", "cat /run/.containerenv"}; + + private final System2 system2; private final Paths2 paths2; + private String containerContextCache; - public ContainerSupportImpl(Paths2 paths2) { + public ContainerSupportImpl(Paths2 paths2, System2 system2) { this.paths2 = paths2; + this.system2 = system2; + + populateCache(); + } + + @VisibleForTesting + void populateCache() { + if (isDocker()) { + containerContextCache = DOCKER; + } else if (isPodman()) { + containerContextCache = PODMAN; + } else if (isBuildah()) { + containerContextCache = BUILDAH; + } else if (isContainerd()) { + containerContextCache = CONTAINER_D; + } else if (isGeneralContainer()) { + containerContextCache = GENERAL_CONTAINER; + } else { + containerContextCache = null; + } } @Override public boolean isRunningInContainer() { - if (paths2.exists("/run/.containerenv")) { - return true; - } - try (Stream<String> stream = Files.lines(paths2.get("/proc/1/cgroup"))) { - return stream.anyMatch(line -> line.contains("/docker") || line.contains("/kubepods") || line.contains("containerd.service") ); - } catch (IOException e) { - return false; + return containerContextCache != null; + } + + @Override + public String getContainerContext() { + return containerContextCache; + } + + private boolean isDocker() { + return executeCommand(MOUNT_GREP_COMMAND).contains("/docker") && paths2.exists("/.dockerenv"); + } + + private boolean isPodman() { + return Objects.equals(system2.envVariable("container"), PODMAN) && paths2.exists(CONTAINER_FILE_PATH); + } + + private boolean isBuildah() { + return paths2.exists(CONTAINER_FILE_PATH) && executeCommand(CAT_COMMAND).contains("engine=\"buildah-"); + } + + private boolean isContainerd() { + return executeCommand(MOUNT_GREP_COMMAND).contains("/containerd"); + } + + private boolean isGeneralContainer() { + return paths2.exists(CONTAINER_FILE_PATH); + } + + @VisibleForTesting + String executeCommand(String[] command) { + try { + Process process = new ProcessBuilder().command(command).start(); + try (Scanner scanner = new Scanner(process.getInputStream(), UTF_8)) { + scanner.useDelimiter("\n"); + return scanner.next(); + } finally { + process.destroy(); + } + } catch (Exception e) { + LOG.debug("Failed to execute command", e); + return ""; } } } diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/CloudUsageDataProvider.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/CloudUsageDataProvider.java index f35f1f026f2..dd10f981ab5 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/CloudUsageDataProvider.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/CloudUsageDataProvider.java @@ -48,6 +48,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; +import org.sonar.server.platform.ContainerSupport; import org.sonar.server.util.Paths2; import static java.nio.charset.StandardCharsets.UTF_8; @@ -64,13 +65,15 @@ public class CloudUsageDataProvider { static final String SONAR_HELM_CHART_VERSION = "SONAR_HELM_CHART_VERSION"; static final String DOCKER_RUNNING = "DOCKER_RUNNING"; private static final String[] KUBERNETES_PROVIDER_COMMAND = {"bash", "-c", "uname -r"}; + private final ContainerSupport containerSupport; private final System2 system2; private final Paths2 paths2; private OkHttpClient httpClient; private TelemetryData.CloudUsage cloudUsageData; @Inject - public CloudUsageDataProvider(System2 system2, Paths2 paths2) { + public CloudUsageDataProvider(ContainerSupport containerSupport, System2 system2, Paths2 paths2) { + this.containerSupport = containerSupport; this.system2 = system2; this.paths2 = paths2; if (isOnKubernetes()) { @@ -79,7 +82,8 @@ public class CloudUsageDataProvider { } @VisibleForTesting - CloudUsageDataProvider(System2 system2, Paths2 paths2, OkHttpClient httpClient) { + CloudUsageDataProvider(ContainerSupport containerSupport, System2 system2, Paths2 paths2, OkHttpClient httpClient) { + this.containerSupport = containerSupport; this.system2 = system2; this.paths2 = paths2; this.httpClient = httpClient; @@ -107,6 +111,7 @@ public class CloudUsageDataProvider { kubernetesPlatform, getKubernetesProvider(), getOfficialHelmChartVersion(), + containerSupport.getContainerContext(), isOfficialImageUsed()); return cloudUsageData; |