]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6835 Include batch analysis context in the report sent by the batch 509/head
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 15 Sep 2015 13:37:38 +0000 (15:37 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 16 Sep 2015 13:14:49 +0000 (15:14 +0200)
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java
sonar-batch/src/main/java/org/sonar/batch/report/AnalysisContextReportPublisher.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
sonar-batch/src/main/java/org/sonar/batch/scan/ImmutableProjectReactor.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java

index 6d0c9f3fa6704b0f15a9f938c7b3414adeea223f..967a6e39fe3c0c7d3e589cc16819959aef955337 100644 (file)
@@ -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 (file)
index 0000000..c4e396f
--- /dev/null
@@ -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<String, String> env : System.getenv().entrySet()) {
+        fileWriter.append(String.format("  - %s=%s", env.getKey(), env.getValue())).append('\n');
+      }
+      fileWriter.write("System properties:\n");
+      for (Map.Entry<Object, Object> 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<String, String> 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");
+  }
+}
index 6638fbd47218f053bd0a744b61fba2009629929b..418703ae7fa86aed49beba4bd06fe2d36979c86a 100644 (file)
  */
 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
index b28bea91d20abb23f7eb610ffac4c12cc347a009..f7652b22c721f223142d14e725855ae1a2db2d7a 100644 (file)
@@ -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<String, ProjectDefinition> byKey = new HashMap<>();
+  private Map<String, ProjectDefinition> byKey = new LinkedHashMap<>();
 
   public ImmutableProjectReactor(ProjectDefinition root) {
     if (root.getParent() != null) {
index 00692d53cfae184ec49b07bbaee841ef0d61dc2c..43ca0dd6ac1dc70a2c7ac19fed27fe30836bf7eb 100644 (file)
  */
 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) {
index 7d65aae8ab52b31fe0a2c683f03abf404289ddf3..431b916ebff3b595937a501e5f069b2cfc5fe4bb 100644 (file)
@@ -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 (file)
index 0000000..e7bdc1b
--- /dev/null
@@ -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=******");
+  }
+}
index de11e64af4582b8f93bd9d9445136e79aa18ca2a..7d1c47d058d8f1dbcef79ba5e756cfb680f6a7ad 100644 (file)
@@ -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);
index b68eee07f32b5c898a6d9a54bfd3edb6943d2f7a..336cf7a2a853533d9f9ba205fb5de179efd5a195 100644 (file)
 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");
   }
 }