]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8383 add param "process" to api/system/logs
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 16 Nov 2016 14:51:13 +0000 (15:51 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 17 Nov 2016 06:36:51 +0000 (07:36 +0100)
server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java
server/sonar-process/src/main/java/org/sonar/process/ProcessId.java
server/sonar-process/src/test/java/org/sonar/process/ProcessIdTest.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/LogsAction.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/LogsActionTest.java

index cd7ae2dae59a2b49cdc4786448d821f23d2fde71..183aa1463a686715262b7616c0b3794821d846a4 100644 (file)
@@ -45,6 +45,7 @@ import javax.annotation.CheckForNull;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.LoggerFactory;
 
+import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
 import static org.slf4j.Logger.ROOT_LOGGER_NAME;
 
@@ -273,7 +274,7 @@ public class LogbackHelper {
 
     Level level = Level.toLevel(value, Level.INFO);
     if (!isAllowed(level)) {
-      throw new IllegalArgumentException(String.format("log level %s in property %s is not a supported value (allowed levels are %s)",
+      throw new IllegalArgumentException(format("log level %s in property %s is not a supported value (allowed levels are %s)",
         level, propertyKey, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
     }
     return level;
@@ -302,7 +303,7 @@ public class LogbackHelper {
 
   private static void ensureSupportedLevel(Level newLevel) {
     if (!isAllowed(newLevel)) {
-      throw new IllegalArgumentException(String.format("%s log level is not supported (allowed levels are %s)", newLevel, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
+      throw new IllegalArgumentException(format("%s log level is not supported (allowed levels are %s)", newLevel, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
     }
   }
 
@@ -362,7 +363,7 @@ public class LogbackHelper {
       return new NoRollingPolicy(ctx, filenamePrefix, logsDir, maxFiles);
 
     } else {
-      throw new MessageException(String.format("Unsupported value for property %s: %s", ROLLING_POLICY_PROPERTY, rollingPolicy));
+      throw new MessageException(format("Unsupported value for property %s: %s", ROLLING_POLICY_PROPERTY, rollingPolicy));
     }
   }
 
index 37db2a08f32748688a15d68588cb6a27c05809d2..4f7fa8beb14ef5b6679f4a060292726969728902 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.process;
 
+import static java.lang.String.format;
+
 public enum ProcessId {
 
   APP("app", 0, "sonar"),
@@ -64,4 +66,14 @@ public enum ProcessId {
     sb.append(']');
     return sb.toString();
   }
+
+  public static ProcessId fromKey(String key) {
+    for (ProcessId processId : values()) {
+      if (processId.getKey().equals(key)) {
+        return processId;
+      }
+    }
+    throw new IllegalArgumentException(format("Process [%s] does not exist", key));
+  }
+
 }
index 4eebf2cd124fd0c182463ae6357f397a0b4d97dd..5a652bcb110637c4d1e8807415870460215a56a3 100644 (file)
@@ -21,12 +21,17 @@ package org.sonar.process;
 
 import java.util.HashSet;
 import java.util.Set;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class ProcessIdTest {
 
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Test
   public void test_constants() {
     assertThat(ProcessId.COMPUTE_ENGINE.getKey()).isEqualTo("ce");
@@ -44,4 +49,28 @@ public class ProcessIdTest {
     assertThat(ipcIndices).hasSize(ProcessId.values().length);
     assertThat(keys).hasSize(ProcessId.values().length);
   }
+
+  @Test
+  public void fromKey_searches_process_by_its_key() {
+    assertThat(ProcessId.fromKey("app")).isEqualTo(ProcessId.APP);
+    assertThat(ProcessId.fromKey("ce")).isEqualTo(ProcessId.COMPUTE_ENGINE);
+    assertThat(ProcessId.fromKey("es")).isEqualTo(ProcessId.ELASTICSEARCH);
+    assertThat(ProcessId.fromKey("web")).isEqualTo(ProcessId.WEB_SERVER);
+  }
+
+  @Test
+  public void fromKey_throws_IAE_if_key_is_null() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Process [null] does not exist");
+
+    ProcessId.fromKey(null);
+  }
+
+  @Test
+  public void fromKey_throws_IAE_if_key_does_not_exist() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Process [foo] does not exist");
+
+    ProcessId.fromKey("foo");
+  }
 }
index 545144d697c0e0e673d3d23f593641f8df4cb05c..141f0d8493882981ddce2bfdc60bc2d7a44ba332 100644 (file)
 package org.sonar.server.platform.ws;
 
 import java.io.File;
+import java.net.HttpURLConnection;
 import org.apache.commons.io.FileUtils;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.stream.Collectors;
+import org.sonar.process.ProcessId;
 import org.sonar.server.platform.ServerLogging;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.MediaTypes;
 
+import static java.util.Arrays.stream;
+
 public class LogsAction implements SystemWsAction {
 
+  private static final String PROCESS_PROPERTY = "process";
+
   private final UserSession userSession;
   private final ServerLogging serverLogging;
 
@@ -40,21 +47,39 @@ public class LogsAction implements SystemWsAction {
 
   @Override
   public void define(WebService.NewController controller) {
-    controller.createAction("logs")
+    WebService.NewAction action = controller.createAction("logs")
       .setDescription("Get system logs in plain-text format. Requires system administration permission.")
       .setResponseExample(getClass().getResource("logs-example.log"))
       .setSince("5.2")
       .setHandler(this);
+
+    action
+      .createParam(PROCESS_PROPERTY)
+      .setPossibleValues(stream(ProcessId.values())
+        .map(ProcessId::getKey)
+        .sorted()
+        .collect(Collectors.toList(ProcessId.values().length)))
+      .setDefaultValue(ProcessId.APP.getKey())
+      .setSince("6.2")
+      .setDescription("Process to get logs from");
   }
 
   @Override
   public void handle(Request wsRequest, Response wsResponse) throws Exception {
     userSession.checkIsRoot();
 
-    wsResponse.stream().setMediaType(MediaTypes.TXT);
-    File file = serverLogging.getCurrentLogFile();
-    if (file.exists()) {
+    String processKey = wsRequest.mandatoryParam(PROCESS_PROPERTY);
+    ProcessId processId = ProcessId.fromKey(processKey);
+
+    File logsDir = serverLogging.getLogsDir();
+    File file = new File(logsDir, processId.getLogFilenamePrefix() + ".log");
+    // filenames are defined in the enum LogProcess. Still to prevent any vulnerability,
+    // path is double-checked to prevent returning any file present on the file system.
+    if (file.exists() && file.getParentFile().equals(logsDir)) {
+      wsResponse.stream().setMediaType(MediaTypes.TXT);
       FileUtils.copyFile(file, wsResponse.stream().output());
+    } else {
+      wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
     }
   }
 }
index feac4ff14fdc32f7fe1d57dae83cddf24bca5dbd..a894e880bfa87120f3c63a31f35e34ac2a2f7697 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.ws;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Set;
 import org.apache.commons.io.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
@@ -50,6 +51,13 @@ public class LogsActionTest {
   private LogsAction underTest = new LogsAction(userSession, serverLogging);
   private WsActionTester actionTester = new WsActionTester(underTest);
 
+  @Test
+  public void values_of_process_parameter_are_names_of_processes() {
+    Set<String> values = actionTester.getDef().param("process").possibleValues();
+    // values are lower-case and alphabetically ordered
+    assertThat(values).containsExactly("app", "ce", "es", "web");
+  }
+
   @Test
   public void request_fails_with_ForbiddenException_when_user_is_not_logged_in() {
     expectedException.expect(ForbiddenException.class);
@@ -67,29 +75,93 @@ public class LogsActionTest {
   }
 
   @Test
-  public void get_logs() throws IOException {
+  public void get_app_logs_by_default() throws IOException {
     makeAuthenticatedUserRoot();
 
-    File file = temp.newFile();
-    FileUtils.write(file, "{logs}");
-    when(serverLogging.getCurrentLogFile()).thenReturn(file);
+    createAllLogsFiles();
 
     TestResponse response = actionTester.newRequest().execute();
     assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
-    assertThat(response.getInput()).isEqualTo("{logs}");
+    assertThat(response.getInput()).isEqualTo("{app}");
   }
 
   @Test
-  public void get_empty_logs_if_file_does_not_exist() throws IOException {
+  public void return_404_not_found_if_file_does_not_exist() throws IOException {
     makeAuthenticatedUserRoot();
 
-    File file = temp.newFile();
-    file.delete();
-    when(serverLogging.getCurrentLogFile()).thenReturn(file);
+    createLogsDir();
 
     TestResponse response = actionTester.newRequest().execute();
+    assertThat(response.getStatus()).isEqualTo(404);
+  }
+
+  @Test
+  public void get_ce_logs() throws IOException {
+    makeAuthenticatedUserRoot();
+
+    createAllLogsFiles();
+
+    TestResponse response = actionTester.newRequest()
+      .setParam("process", "ce")
+      .execute();
+    assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+    assertThat(response.getInput()).isEqualTo("{ce}");
+  }
+
+  @Test
+  public void get_es_logs() throws IOException {
+    makeAuthenticatedUserRoot();
+
+    createAllLogsFiles();
+
+    TestResponse response = actionTester.newRequest()
+      .setParam("process", "es")
+      .execute();
+    assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+    assertThat(response.getInput()).isEqualTo("{es}");
+  }
+
+  @Test
+  public void get_web_logs() throws IOException {
+    makeAuthenticatedUserRoot();
+
+    createAllLogsFiles();
+
+    TestResponse response = actionTester.newRequest()
+      .setParam("process", "web")
+      .execute();
     assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
-    assertThat(response.getInput()).isEqualTo("");
+    assertThat(response.getInput()).isEqualTo("{web}");
+  }
+
+  @Test
+  public void do_not_return_rotated_files() throws IOException {
+    makeAuthenticatedUserRoot();
+
+    File dir = createLogsDir();
+    FileUtils.write(new File(dir, "sonar.1.log"), "{old}");
+    FileUtils.write(new File(dir, "sonar.log"), "{recent}");
+
+    TestResponse response = actionTester.newRequest()
+      .setParam("process", "app")
+      .execute();
+    assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+    assertThat(response.getInput()).isEqualTo("{recent}");
+  }
+
+  private File createAllLogsFiles() throws IOException {
+    File dir = createLogsDir();
+    FileUtils.write(new File(dir, "sonar.log"), "{app}");
+    FileUtils.write(new File(dir, "ce.log"), "{ce}");
+    FileUtils.write(new File(dir, "es.log"), "{es}");
+    FileUtils.write(new File(dir, "web.log"), "{web}");
+    return dir;
+  }
+
+  private File createLogsDir() throws IOException {
+    File dir = temp.newFolder();
+    when(serverLogging.getLogsDir()).thenReturn(dir);
+    return dir;
   }
 
   private void makeAuthenticatedUserRoot() {