From: Julien HENRY Date: Tue, 15 Sep 2015 13:37:38 +0000 (+0200) Subject: SONAR-6835 Include batch analysis context in the report sent by the batch X-Git-Tag: 5.2-RC1~374 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fpull%2F509%2Fhead;p=sonarqube.git SONAR-6835 Include batch analysis context in the report sent by the batch --- diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java index 6d0c9f3fa67..967a6e39fe3 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java @@ -62,6 +62,10 @@ public class FileStructure { return new File(dir, "metadata.pb"); } + public File analysisLog() { + return new File(dir, "analysis.log"); + } + public File activeRules() { return new File(dir, "activerules.pb"); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/AnalysisContextReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/AnalysisContextReportPublisher.java new file mode 100644 index 00000000000..c4e396fd381 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/AnalysisContextReportPublisher.java @@ -0,0 +1,92 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.report; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.Map; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.BatchSide; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.config.Settings; +import org.sonar.batch.bootstrap.BatchPluginRepository; +import org.sonar.batch.protocol.output.BatchReportWriter; +import org.sonar.core.platform.PluginInfo; + +@BatchSide +public class AnalysisContextReportPublisher { + + private final BatchPluginRepository pluginRepo; + private final AnalysisMode mode; + + private BatchReportWriter writer; + + public AnalysisContextReportPublisher(AnalysisMode mode, BatchPluginRepository pluginRepo) { + this.mode = mode; + this.pluginRepo = pluginRepo; + } + + public void init(BatchReportWriter writer) { + if (mode.isIssues()) { + return; + } + this.writer = writer; + File analysisLog = writer.getFileStructure().analysisLog(); + try (BufferedWriter fileWriter = Files.newBufferedWriter(analysisLog.toPath(), StandardCharsets.UTF_8)) { + fileWriter.append("Environement variables:\n"); + for (Map.Entry env : System.getenv().entrySet()) { + fileWriter.append(String.format(" - %s=%s", env.getKey(), env.getValue())).append('\n'); + } + fileWriter.write("System properties:\n"); + for (Map.Entry env : System.getProperties().entrySet()) { + fileWriter.append(String.format(" - %s=%s", env.getKey(), env.getValue())).append('\n'); + } + fileWriter.write("SonarQube plugins:\n"); + for (PluginInfo p : pluginRepo.getPluginInfos()) { + fileWriter.append(String.format(" - %s %s (%s)", p.getName(), p.getVersion(), p.getKey())).append('\n'); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to write analysis log", e); + } + } + + public void dumpSettings(ProjectDefinition moduleDefinition, Settings settings) { + if (mode.isIssues()) { + return; + } + File analysisLog = writer.getFileStructure().analysisLog(); + try (BufferedWriter fileWriter = Files.newBufferedWriter(analysisLog.toPath(), StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { + fileWriter.append(String.format("Settings for module: %s", moduleDefinition.getKey())).append('\n'); + for (Map.Entry prop : settings.getProperties().entrySet()) { + fileWriter.append(String.format(" - %s=%s", prop.getKey(), sensitive(prop.getKey()) ? "******" : prop.getValue())).append('\n'); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to write analysis log", e); + } + } + + private static boolean sensitive(String key) { + return key.contains(".password") || key.contains(".secured"); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java index 6638fbd4721..418703ae7fa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java @@ -19,12 +19,8 @@ */ package org.sonar.batch.report; -import org.sonar.batch.analysis.DefaultAnalysisMode; - -import org.sonar.batch.util.BatchUtils; import com.github.kevinsawicki.http.HttpRequest; import com.google.common.annotations.VisibleForTesting; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -32,7 +28,6 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.Date; - import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; import org.slf4j.Logger; @@ -44,9 +39,12 @@ import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.ZipUtils; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.bootstrap.ServerClient; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.batch.scan.ImmutableProjectReactor; +import org.sonar.batch.util.BatchUtils; + import static java.lang.String.format; @BatchSide @@ -62,16 +60,18 @@ public class ReportPublisher implements Startable { private final ImmutableProjectReactor projectReactor; private final DefaultAnalysisMode analysisMode; private final TempFolder temp; + private final AnalysisContextReportPublisher contextPublisher; private ReportPublisherStep[] publishers; private File reportDir; private BatchReportWriter writer; - public ReportPublisher(Settings settings, ServerClient serverClient, Server server, + public ReportPublisher(Settings settings, ServerClient serverClient, Server server, AnalysisContextReportPublisher contextPublisher, ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { this.serverClient = serverClient; this.server = server; + this.contextPublisher = contextPublisher; this.projectReactor = projectReactor; this.settings = settings; this.analysisMode = analysisMode; @@ -83,6 +83,7 @@ public class ReportPublisher implements Startable { public void start() { reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report"); writer = new BatchReportWriter(reportDir); + contextPublisher.init(writer); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ImmutableProjectReactor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ImmutableProjectReactor.java index b28bea91d20..f7652b22c72 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ImmutableProjectReactor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ImmutableProjectReactor.java @@ -20,7 +20,7 @@ package org.sonar.batch.scan; import java.util.Collection; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.CheckForNull; import org.sonar.api.batch.BatchSide; @@ -33,7 +33,7 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; public class ImmutableProjectReactor { private ProjectDefinition root; - private Map byKey = new HashMap<>(); + private Map byKey = new LinkedHashMap<>(); public ImmutableProjectReactor(ProjectDefinition root) { if (root.getParent() != null) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java index 00692d53cfa..43ca0dd6ac1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java @@ -19,18 +19,16 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.repository.ProjectSettingsRepo; - -import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.collect.Lists; - import java.util.List; - import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.bootstrap.GlobalSettings; +import org.sonar.batch.report.AnalysisContextReportPublisher; +import org.sonar.batch.repository.ProjectSettingsRepo; /** * @since 2.12 @@ -38,16 +36,17 @@ import org.sonar.batch.bootstrap.GlobalSettings; public class ModuleSettings extends Settings { private final ProjectSettingsRepo projectSettingsRepo; - private DefaultAnalysisMode analysisMode; + private final DefaultAnalysisMode analysisMode; public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectSettingsRepo projectSettingsRepo, - DefaultAnalysisMode analysisMode) { + DefaultAnalysisMode analysisMode, AnalysisContextReportPublisher contextReportPublisher) { super(batchSettings.getDefinitions()); this.projectSettingsRepo = projectSettingsRepo; this.analysisMode = analysisMode; getEncryption().setPathToSecretKey(batchSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); init(moduleDefinition, batchSettings); + contextReportPublisher.dumpSettings(moduleDefinition, this); } private ModuleSettings init(ProjectDefinition moduleDefinition, GlobalSettings batchSettings) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 7d65aae8ab5..431b916ebff 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -72,6 +72,7 @@ import org.sonar.batch.mediumtest.ScanTaskObservers; import org.sonar.batch.phases.PhasesTimeProfiler; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; import org.sonar.batch.report.ActiveRulesPublisher; +import org.sonar.batch.report.AnalysisContextReportPublisher; import org.sonar.batch.report.ComponentsPublisher; import org.sonar.batch.report.CoveragePublisher; import org.sonar.batch.report.DuplicationsPublisher; @@ -192,6 +193,7 @@ public class ProjectScanContainer extends ComponentContainer { // Report ReportPublisher.class, + AnalysisContextReportPublisher.class, MetadataPublisher.class, ActiveRulesPublisher.class, ComponentsPublisher.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java new file mode 100644 index 00000000000..e7bdc1b66e4 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java @@ -0,0 +1,91 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.report; + +import java.util.Arrays; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.config.Settings; +import org.sonar.batch.bootstrap.BatchPluginRepository; +import org.sonar.batch.protocol.output.BatchReportWriter; +import org.sonar.core.platform.PluginInfo; +import org.sonar.updatecenter.common.Version; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AnalysisContextReportPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private BatchPluginRepository pluginRepo = mock(BatchPluginRepository.class); + private AnalysisContextReportPublisher publisher; + private AnalysisMode analysisMode = mock(AnalysisMode.class); + + @Before + public void prepare() throws Exception { + publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo); + } + + @Test + public void shouldDumpPlugins() throws Exception { + when(pluginRepo.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("xoo").setName("Xoo").setVersion(Version.create("1.0")))); + + BatchReportWriter writer = new BatchReportWriter(temp.newFolder()); + publisher.init(writer); + + assertThat(writer.getFileStructure().analysisLog()).exists(); + assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).contains("Xoo 1.0 (xoo)"); + } + + @Test + public void shouldNotDumpInIssuesMode() throws Exception { + when(analysisMode.isIssues()).thenReturn(true); + + BatchReportWriter writer = new BatchReportWriter(temp.newFolder()); + publisher.init(writer); + publisher.dumpSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo"), new Settings()); + + assertThat(writer.getFileStructure().analysisLog()).doesNotExist(); + } + + @Test + public void shouldNotDumpSensitiveProperties() throws Exception { + BatchReportWriter writer = new BatchReportWriter(temp.newFolder()); + publisher.init(writer); + + assertThat(writer.getFileStructure().analysisLog()).exists(); + + Settings settings = new Settings(); + settings.setProperty("sonar.projectKey", "foo"); + settings.setProperty("sonar.password", "azerty"); + settings.setProperty("sonar.cpp.license.secured", "AZERTY"); + publisher.dumpSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo"), settings); + + assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).contains("sonar.projectKey=foo", "sonar.password=******", "sonar.cpp.license.secured=******"); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java index de11e64af45..7d1c47d058d 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java @@ -19,8 +19,6 @@ */ package org.sonar.batch.report; -import org.sonar.batch.analysis.DefaultAnalysisMode; - import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; @@ -29,8 +27,10 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; import org.sonar.api.utils.TempFolder; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.bootstrap.ServerClient; import org.sonar.batch.scan.ImmutableProjectReactor; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -52,7 +52,8 @@ public class ReportPublisherTest { public void should_log_successful_analysis() { Settings settings = new Settings(); settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); - ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, + mock(TempFolder.class), new ReportPublisherStep[0]); Logger logger = mock(Logger.class); job.logSuccess(logger); @@ -65,7 +66,8 @@ public class ReportPublisherTest { public void should_log_successful_issues_analysis() { Settings settings = new Settings(); when(mode.isIssues()).thenReturn(true); - ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, + mock(TempFolder.class), new ReportPublisherStep[0]); Logger logger = mock(Logger.class); job.logSuccess(logger); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java index b68eee07f32..336cf7a2a85 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java @@ -20,10 +20,11 @@ package org.sonar.batch.scan; import com.google.common.collect.HashBasedTable; - import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Table; +import java.util.List; +import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -34,11 +35,9 @@ import org.sonar.api.utils.MessageException; import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.bootstrap.GlobalSettings; import org.sonar.batch.protocol.input.FileData; +import org.sonar.batch.report.AnalysisContextReportPublisher; import org.sonar.batch.repository.ProjectSettingsRepo; -import java.util.List; -import java.util.Map; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -85,14 +84,13 @@ public class ModuleSettingsTest { when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( "overridding", "batch", - "on-batch", "true" - )); + "on-batch", "true")); ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module")); ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); - ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class)); assertThat(moduleSettings.getString("overridding")).isEqualTo("module"); assertThat(moduleSettings.getString("on-batch")).isEqualTo("true"); @@ -105,14 +103,13 @@ public class ModuleSettingsTest { GlobalSettings batchSettings = mock(GlobalSettings.class); when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( - "sonar.foo.secured", "bar" - )); + "sonar.foo.secured", "bar")); ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); - ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class)); assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); assertThat(moduleSettings.getString("sonar.foo.secured")).isEqualTo("bar"); @@ -123,8 +120,7 @@ public class ModuleSettingsTest { GlobalSettings batchSettings = mock(GlobalSettings.class); when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions()); when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( - "sonar.foo.secured", "bar" - )); + "sonar.foo.secured", "bar")); ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); @@ -132,13 +128,14 @@ public class ModuleSettingsTest { ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); - ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class)); assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); thrown.expect(MessageException.class); thrown - .expectMessage("Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode."); + .expectMessage( + "Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode."); moduleSettings.getString("sonar.foo.secured"); } }