]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9525 add CE WS to refresh enabled worker count
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 10 Jul 2017 08:49:17 +0000 (10:49 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 18 Jul 2017 06:51:46 +0000 (08:51 +0200)
15 files changed:
server/sonar-ce/src/main/java/org/sonar/ce/CeHttpModule.java
server/sonar-ce/src/main/java/org/sonar/ce/configuration/CeConfiguration.java
server/sonar-ce/src/main/java/org/sonar/ce/configuration/CeConfigurationImpl.java
server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/EnabledCeWorkerController.java
server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/EnabledCeWorkerControllerImpl.java
server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/RefreshWorkerCountAction.java [new file with mode: 0644]
server/sonar-ce/src/test/java/org/sonar/ce/configuration/CeConfigurationImplTest.java
server/sonar-ce/src/test/java/org/sonar/ce/configuration/CeConfigurationRule.java
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-ce/src/test/java/org/sonar/ce/logging/ChangeLogLevelHttpActionTest.java
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CeTasksMBeanImplTest.java
server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/EnabledCeWorkerControllerImplTest.java
server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/RefreshWorkerCountActionTest.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClient.java
server/sonar-server/src/test/java/org/sonar/ce/http/CeHttpClientTest.java

index f6889c3fbfa7ba498bf2f57f9202e033385a4c8f..10aa8bcd896aa74ba8bff172aed6a0562dc4531b 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.ce;
 import org.sonar.ce.httpd.CeHttpServer;
 import org.sonar.ce.logging.ChangeLogLevelHttpAction;
 import org.sonar.ce.systeminfo.SystemInfoHttpAction;
+import org.sonar.ce.taskprocessor.RefreshWorkerCountAction;
 import org.sonar.core.platform.Module;
 
 public class CeHttpModule extends Module {
@@ -30,6 +31,7 @@ public class CeHttpModule extends Module {
     add(
       CeHttpServer.class,
       SystemInfoHttpAction.class,
-      ChangeLogLevelHttpAction.class);
+      ChangeLogLevelHttpAction.class,
+      RefreshWorkerCountAction.class);
   }
 }
index b719e969a937acad7077f6c039702d8bb0218f48..f1fd9a3f3fc1ffa4210bc0adb0d1b253ddff7915 100644 (file)
@@ -21,6 +21,11 @@ package org.sonar.ce.configuration;
 
 public interface CeConfiguration {
 
+  /**
+   * Requests {@link CeConfiguration} to refresh its state, if it has any.
+   */
+  void refresh();
+
   /**
    * The maximum number of workers to process CeTasks concurrently, integer strictly greater than 0.
    */
index 75cd049f9a5c6738db6fae2cec086b66c6b45920..ca43fd3abe5bd99423ed2e41246fed00ddb5174d 100644 (file)
  */
 package org.sonar.ce.configuration;
 
+import javax.annotation.CheckForNull;
 import org.picocontainer.Startable;
 import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
 
 import static java.lang.String.format;
 
@@ -33,8 +32,6 @@ import static java.lang.String.format;
  * {@link CeConfiguration#getQueuePollingDelay()} is called.
  */
 public class CeConfigurationImpl implements CeConfiguration, Startable {
-  private static final Logger LOG = Loggers.get(CeConfigurationImpl.class);
-
   private static final int DEFAULT_WORKER_THREAD_COUNT = 1;
   private static final int MAX_WORKER_THREAD_COUNT = 10;
   private static final int DEFAULT_WORKER_COUNT = 1;
@@ -45,21 +42,29 @@ public class CeConfigurationImpl implements CeConfiguration, Startable {
   // 10 minutes
   private static final long CANCEL_WORN_OUTS_DELAY = 10;
 
+  @CheckForNull
+  private final WorkerCountProvider workerCountProvider;
   private final int workerThreadCount;
-  private final int workerCount;
+  private int workerCount;
 
   public CeConfigurationImpl() {
+    this.workerCountProvider = null;
     this.workerThreadCount = DEFAULT_WORKER_THREAD_COUNT;
     this.workerCount = DEFAULT_WORKER_COUNT;
   }
 
   public CeConfigurationImpl(WorkerCountProvider workerCountProvider) {
+    this.workerCountProvider = workerCountProvider;
+    this.workerThreadCount = MAX_WORKER_THREAD_COUNT;
+    this.workerCount = readWorkerCount(workerCountProvider);
+  }
+
+  private static int readWorkerCount(WorkerCountProvider workerCountProvider) {
     int value = workerCountProvider.get();
     if (value < DEFAULT_WORKER_COUNT || value > MAX_WORKER_THREAD_COUNT) {
       throw parsingError(value);
     }
-    this.workerThreadCount = MAX_WORKER_THREAD_COUNT;
-    this.workerCount = value;
+    return value;
   }
 
   private static MessageException parsingError(int value) {
@@ -70,9 +75,7 @@ public class CeConfigurationImpl implements CeConfiguration, Startable {
 
   @Override
   public void start() {
-    if (this.workerCount > 1) {
-      LOG.info("Compute Engine will use {} concurrent workers to process tasks", this.workerCount);
-    }
+    //
   }
 
   @Override
@@ -80,6 +83,13 @@ public class CeConfigurationImpl implements CeConfiguration, Startable {
     // nothing to do
   }
 
+  @Override
+  public void refresh() {
+    if (workerCountProvider != null) {
+      this.workerCount = readWorkerCount(workerCountProvider);
+    }
+  }
+
   @Override
   public int getWorkerMaxCount() {
     return workerThreadCount;
index 03e2a37f3bfa7cf286fcbd029e2aaac87e1a3912..37c6c59c0ef21b19dfd9dfacd51a743e41cccd42 100644 (file)
@@ -24,6 +24,11 @@ package org.sonar.ce.taskprocessor;
  * task to process.
  */
 public interface EnabledCeWorkerController {
+  /**
+   * Requests the {@link EnabledCeWorkerController} to refresh its state, if it has any.
+   */
+  void refresh();
+
   /**
    * Returns {@code true} if the specified {@link CeWorker} is enabled
    */
index 19ae6998732408008b5cac83340cb37e58bccc56..7bb905a3db53af4c1615c8864e08b46869a4a591 100644 (file)
 package org.sonar.ce.taskprocessor;
 
 import java.util.concurrent.atomic.AtomicInteger;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.ce.configuration.CeConfiguration;
 
 public class EnabledCeWorkerControllerImpl implements EnabledCeWorkerController {
+  private final CeConfiguration ceConfiguration;
   private final AtomicInteger workerCount;
 
   public EnabledCeWorkerControllerImpl(CeConfiguration ceConfiguration) {
+    this.ceConfiguration = ceConfiguration;
     this.workerCount = new AtomicInteger(ceConfiguration.getWorkerCount());
+    logEnabledWorkerCount();
+  }
+
+  private void logEnabledWorkerCount() {
+    if (workerCount.get() > 1) {
+      Loggers.get(EnabledCeWorkerController.class).info("Compute Engine will use {} concurrent workers to process tasks", this.workerCount);
+    }
+  }
+
+  @Override
+  public void refresh() {
+    ceConfiguration.refresh();
+    this.workerCount.set(ceConfiguration.getWorkerCount());
+    logEnabledWorkerCount();
   }
 
   /**
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/RefreshWorkerCountAction.java b/server/sonar-ce/src/main/java/org/sonar/ce/taskprocessor/RefreshWorkerCountAction.java
new file mode 100644 (file)
index 0000000..3597c66
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce.taskprocessor;
+
+import fi.iki.elonen.NanoHTTPD;
+import org.sonar.ce.httpd.HttpAction;
+
+import static fi.iki.elonen.NanoHTTPD.MIME_PLAINTEXT;
+import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.METHOD_NOT_ALLOWED;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.OK;
+
+public class RefreshWorkerCountAction implements HttpAction {
+  private static final String PATH = "refreshWorkerCount";
+
+  private final EnabledCeWorkerController enabledCeWorkerController;
+
+  public RefreshWorkerCountAction(EnabledCeWorkerController enabledCeWorkerController) {
+    this.enabledCeWorkerController = enabledCeWorkerController;
+  }
+
+  @Override
+  public void register(ActionRegistry registry) {
+    registry.register(PATH, this);
+  }
+
+  @Override
+  public NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session) {
+    if (session.getMethod() != NanoHTTPD.Method.POST) {
+      return newFixedLengthResponse(METHOD_NOT_ALLOWED, MIME_PLAINTEXT, null);
+    }
+
+    enabledCeWorkerController.refresh();
+
+    return newFixedLengthResponse(OK, MIME_PLAINTEXT, null);
+  }
+}
index 2a8ac541d5c69aa16095b09468ef9b89b9f8958e..65f2b5a3c00cfa7f381868081b2cccef1bf6fda6 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.ce.configuration;
 
 import java.util.Random;
+import java.util.stream.IntStream;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -40,21 +41,21 @@ public class CeConfigurationImplTest {
   }
 
   @Test
-  public void getWorkerThreadCount_returns_1_when_there_is_no_WorkerCountProvider() {
+  public void getWorkerMaxCount_returns_1_when_there_is_no_WorkerCountProvider() {
     assertThat(new CeConfigurationImpl().getWorkerMaxCount()).isEqualTo(1);
   }
 
   @Test
   public void getWorkerCount_returns_value_returned_by_WorkerCountProvider_when_available() {
-    int value = 1 + Math.abs(new Random().nextInt(10));
+    int value = randomValidWorkerCount();
     workerCountProvider.set(value);
 
     assertThat(new CeConfigurationImpl(workerCountProvider).getWorkerCount()).isEqualTo(value);
   }
 
   @Test
-  public void getWorkerThreadCount_returns_10_whichever_the_value_returned_by_WorkerCountProvider() {
-    int value = 1 + Math.abs(new Random().nextInt(10));
+  public void getWorkerMaxCount_returns_10_whichever_the_value_returned_by_WorkerCountProvider() {
+    int value = randomValidWorkerCount();
     workerCountProvider.set(value);
 
     assertThat(new CeConfigurationImpl(workerCountProvider).getWorkerMaxCount()).isEqualTo(10);
@@ -92,25 +93,69 @@ public class CeConfigurationImplTest {
   private void expectMessageException(int value) {
     expectedException.expect(MessageException.class);
     expectedException.expectMessage("Worker count '" + value + "' is invalid. " +
-        "It must an integer strictly greater than 0 and less or equal to 10");
+      "It must an integer strictly greater than 0 and less or equal to 10");
   }
 
   @Test
   public void getCleanCeTasksInitialDelay_returns_1() {
     assertThat(new CeConfigurationImpl().getCleanCeTasksInitialDelay())
-        .isEqualTo(1L);
+      .isEqualTo(1L);
     workerCountProvider.set(1);
     assertThat(new CeConfigurationImpl(workerCountProvider).getCleanCeTasksInitialDelay())
-        .isEqualTo(1L);
+      .isEqualTo(1L);
   }
 
   @Test
   public void getCleanCeTasksDelay_returns_10() {
     assertThat(new CeConfigurationImpl().getCleanCeTasksDelay())
-        .isEqualTo(10L);
+      .isEqualTo(10L);
     workerCountProvider.set(1);
     assertThat(new CeConfigurationImpl(workerCountProvider).getCleanCeTasksDelay())
-        .isEqualTo(10L);
+      .isEqualTo(10L);
+  }
+
+  @Test
+  public void refresh_does_not_change_any_value_when_there_is_no_WorkerCountProvider() {
+    CeConfigurationImpl underTest = new CeConfigurationImpl();
+    long cleanCeTasksInitialDelay = underTest.getCleanCeTasksInitialDelay();
+    long cleanCeTasksDelay = underTest.getCleanCeTasksDelay();
+    long queuePollingDelay = underTest.getQueuePollingDelay();
+    int workerThreadCount = underTest.getWorkerMaxCount();
+    int workerCount = underTest.getWorkerCount();
+
+    IntStream.range(0, 1 + abs(new Random().nextInt(10)))
+      .forEach(ignored -> {
+        underTest.refresh();
+        assertThat(underTest.getCleanCeTasksInitialDelay()).isEqualTo(cleanCeTasksInitialDelay);
+        assertThat(underTest.getCleanCeTasksDelay()).isEqualTo(cleanCeTasksDelay);
+        assertThat(underTest.getQueuePollingDelay()).isEqualTo(queuePollingDelay);
+        assertThat(underTest.getWorkerMaxCount()).isEqualTo(workerThreadCount);
+        assertThat(underTest.getWorkerCount()).isEqualTo(workerCount);
+      });
+  }
+
+  @Test
+  public void refresh_updates_only_workerCount_from_WorkerCountProvider_when_there_WorkerCountProvider_is_present() {
+    workerCountProvider.set(randomValidWorkerCount());
+    CeConfigurationImpl underTest = new CeConfigurationImpl(workerCountProvider);
+    long cleanCeTasksInitialDelay = underTest.getCleanCeTasksInitialDelay();
+    long cleanCeTasksDelay = underTest.getCleanCeTasksDelay();
+    long queuePollingDelay = underTest.getQueuePollingDelay();
+    int workerThreadCount = underTest.getWorkerMaxCount();
+
+    IntStream.range(0, 1 + abs(new Random().nextInt(10)))
+      .forEach(ignored -> {
+        int newWorkerCount = randomValidWorkerCount();
+        workerCountProvider.set(newWorkerCount);
+
+        underTest.refresh();
+
+        assertThat(underTest.getCleanCeTasksInitialDelay()).isEqualTo(cleanCeTasksInitialDelay);
+        assertThat(underTest.getCleanCeTasksDelay()).isEqualTo(cleanCeTasksDelay);
+        assertThat(underTest.getQueuePollingDelay()).isEqualTo(queuePollingDelay);
+        assertThat(underTest.getWorkerMaxCount()).isEqualTo(workerThreadCount);
+        assertThat(underTest.getWorkerCount()).isEqualTo(newWorkerCount);
+      });
   }
 
   private static final class SimpleWorkerCountProvider implements WorkerCountProvider {
@@ -125,4 +170,8 @@ public class CeConfigurationImplTest {
       return value;
     }
   }
+
+  private static int randomValidWorkerCount() {
+    return 1 + Math.abs(new Random().nextInt(10));
+  }
 }
index b936ea73137fc56114ae3e7ba6b1f2eda2c80a53..95fa52a359de231b7f84e56fc590e61f604f7c4c 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.ce.configuration;
 
+import java.util.function.Consumer;
 import org.junit.rules.ExternalResource;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -32,6 +33,18 @@ public class CeConfigurationRule extends ExternalResource implements CeConfigura
   private long queuePollingDelay = 2 * 1000L;
   private long cancelWornOutsInitialDelay = 1L;
   private long cancelWornOutsDelay = 10L;
+  private Consumer<CeConfigurationRule> refreshCallHook;
+
+  @Override
+  public void refresh() {
+    if (this.refreshCallHook != null) {
+      this.refreshCallHook.accept(this);
+    }
+  }
+
+  public void setRefreshCallHook(Consumer<CeConfigurationRule> refreshCallHook) {
+    this.refreshCallHook = refreshCallHook;
+  }
 
   @Override
   public int getWorkerMaxCount() {
index 3f03a0076444ff8acb73d3c5d5261a789d0af0f1..ad62ace71c2f988a6355cbce482aa227f7787894 100644 (file)
@@ -116,7 +116,7 @@ public class ComputeEngineContainerImplTest {
           + 73 // level 4
           + 4 // content of CeConfigurationModule
           + 4 // content of CeQueueModule
-          + 3 // content of CeHttpModule
+          + 4 // content of CeHttpModule
           + 3 // content of CeTaskCommonsModule
           + 4 // content of ProjectAnalysisTaskModule
           + 5 // content of CeTaskProcessorModule
index f5e31054df8adc16aa8404e938f837f0b6d2060d..88435a2a8ba76ae3939d713538a3369a1888d21e 100644 (file)
@@ -47,7 +47,7 @@ public class ChangeLogLevelHttpActionTest {
   private ChangeLogLevelHttpAction underTest = new ChangeLogLevelHttpAction(serverLogging, database, ceProcessLogging);
 
   @Test
-  public void register_to_path_systemInfo() {
+  public void register_to_path_changeLogLevel() {
     HttpAction.ActionRegistry actionRegistry = mock(HttpAction.ActionRegistry.class);
 
     underTest.register(actionRegistry);
index a87598bfeedec14451e54fe57c6f3af31ee3f545..277d2d3ced830d1332a9731087d830a1cd87e869 100644 (file)
@@ -130,6 +130,11 @@ public class CeTasksMBeanImplTest {
   }
 
   private static class DumbCeConfiguration implements CeConfiguration {
+    @Override
+    public void refresh() {
+      throw new UnsupportedOperationException("Refresh is not implemented");
+    }
+
     @Override
     public int getWorkerMaxCount() {
       return WORKER_MAX_COUNT;
index d050ed0b87024514841d0a4f4854e9960fa754a0..a0ed2f6a36901035bf4c50f5adae6db1d8ec45d1 100644 (file)
@@ -22,6 +22,8 @@ package org.sonar.ce.taskprocessor;
 import java.util.Random;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.ce.configuration.CeConfigurationRule;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -31,28 +33,30 @@ import static org.mockito.Mockito.when;
 public class EnabledCeWorkerControllerImplTest {
   private Random random = new Random();
   /** 1 <= workerCount <= 5 */
-  private int workerCount = 1 + random.nextInt(5);
+  private int randomWorkerCount = 1 + random.nextInt(5);
 
   @Rule
   public CeConfigurationRule ceConfigurationRule = new CeConfigurationRule()
-    .setWorkerCount(workerCount);
+    .setWorkerCount(randomWorkerCount);
+  @Rule
+  public LogTester logTester = new LogTester();
 
   private CeWorker ceWorker = mock(CeWorker.class);
   private EnabledCeWorkerControllerImpl underTest = new EnabledCeWorkerControllerImpl(ceConfigurationRule);
 
   @Test
   public void isEnabled_returns_true_if_worker_ordinal_is_less_than_CeConfiguration_workerCount() {
-    int ordinal = workerCount + Math.min(-1, -random.nextInt(workerCount));
+    int ordinal = randomWorkerCount + Math.min(-1, -random.nextInt(randomWorkerCount));
     when(ceWorker.getOrdinal()).thenReturn(ordinal);
 
     assertThat(underTest.isEnabled(ceWorker))
-      .as("For ordinal " + ordinal + " and workerCount " + workerCount)
+      .as("For ordinal " + ordinal + " and workerCount " + randomWorkerCount)
       .isTrue();
   }
 
   @Test
   public void isEnabled_returns_false_if_worker_ordinal_is_equal_to_CeConfiguration_workerCount() {
-    when(ceWorker.getOrdinal()).thenReturn(workerCount);
+    when(ceWorker.getOrdinal()).thenReturn(randomWorkerCount);
 
     assertThat(underTest.isEnabled(ceWorker)).isFalse();
   }
@@ -63,16 +67,75 @@ public class EnabledCeWorkerControllerImplTest {
     when(ceWorker.getOrdinal()).thenReturn(ordinal);
 
     assertThat(underTest.isEnabled(ceWorker))
-      .as("For invalid ordinal " + ordinal + " and workerCount " + workerCount)
+      .as("For invalid ordinal " + ordinal + " and workerCount " + randomWorkerCount)
       .isTrue();
   }
 
+  @Test
+  public void constructor_writes_no_info_log_if_workerCount_is_1() {
+    ceConfigurationRule.setWorkerCount(1);
+    logTester.clear();
+
+    new EnabledCeWorkerControllerImpl(ceConfigurationRule);
+
+    assertThat(logTester.logs()).isEmpty();
+  }
+
+  @Test
+  public void constructor_writes_info_log_if_workerCount_is_greater_than_1() {
+    int newWorkerCount = randomWorkerCount + 1;
+    ceConfigurationRule.setWorkerCount(newWorkerCount);
+    logTester.clear();
+
+    new EnabledCeWorkerControllerImpl(ceConfigurationRule);
+
+    verifyInfoLog(newWorkerCount);
+  }
+
   @Test
   public void workerCount_is_loaded_in_constructor() {
-    when(ceWorker.getOrdinal()).thenReturn(workerCount);
+    when(ceWorker.getOrdinal()).thenReturn(randomWorkerCount);
     assertThat(underTest.isEnabled(ceWorker)).isFalse();
 
-    ceConfigurationRule.setWorkerCount(workerCount + 1);
+    ceConfigurationRule.setWorkerCount(randomWorkerCount + 1);
+    assertThat(underTest.isEnabled(ceWorker)).isFalse();
+  }
+
+  @Test
+  public void refresh_reloads_workerCount() {
+    when(ceWorker.getOrdinal()).thenReturn(randomWorkerCount);
     assertThat(underTest.isEnabled(ceWorker)).isFalse();
+    ceConfigurationRule.setRefreshCallHook((rule) -> rule.setWorkerCount(randomWorkerCount + 1));
+
+    underTest.refresh();
+
+    assertThat(underTest.isEnabled(ceWorker)).isTrue();
+  }
+
+  @Test
+  public void refresh_writes_info_log_if_workerCount_is_greater_than_1() {
+    logTester.clear();
+    int newWorkerCount = randomWorkerCount + 1;
+    ceConfigurationRule.setRefreshCallHook((rule) -> rule.setWorkerCount(newWorkerCount));
+
+    underTest.refresh();
+
+    verifyInfoLog(newWorkerCount);
+  }
+
+  @Test
+  public void refresh_writes_no_info_log_if_workerCount_is_1() {
+    logTester.clear();
+    ceConfigurationRule.setRefreshCallHook((rule) -> rule.setWorkerCount(1));
+
+    underTest.refresh();
+
+    assertThat(logTester.logs()).isEmpty();
+  }
+
+  private void verifyInfoLog(int workerCount) {
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.INFO))
+      .containsOnly("Compute Engine will use " + workerCount + " concurrent workers to process tasks");
   }
 }
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/RefreshWorkerCountActionTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/taskprocessor/RefreshWorkerCountActionTest.java
new file mode 100644 (file)
index 0000000..a7d4cac
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce.taskprocessor;
+
+import fi.iki.elonen.NanoHTTPD;
+import org.junit.Test;
+import org.sonar.ce.httpd.HttpAction;
+
+import static fi.iki.elonen.NanoHTTPD.Method.GET;
+import static fi.iki.elonen.NanoHTTPD.Method.POST;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.METHOD_NOT_ALLOWED;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.OK;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.ce.httpd.CeHttpUtils.createHttpSession;
+
+public class RefreshWorkerCountActionTest {
+  private EnabledCeWorkerController enabledCeWorkerController = mock(EnabledCeWorkerController.class);
+  private RefreshWorkerCountAction underTest = new RefreshWorkerCountAction(enabledCeWorkerController);
+
+  @Test
+  public void register_to_path_changeLogLevel() {
+    HttpAction.ActionRegistry actionRegistry = mock(HttpAction.ActionRegistry.class);
+
+    underTest.register(actionRegistry);
+
+    verify(actionRegistry).register("refreshWorkerCount", underTest);
+  }
+
+  @Test
+  public void serves_METHOD_NOT_ALLOWED_error_when_method_is_not_POST() {
+    NanoHTTPD.Response response = underTest.serve(createHttpSession(GET));
+
+    assertThat(response.getStatus()).isEqualTo(METHOD_NOT_ALLOWED);
+    verifyZeroInteractions(enabledCeWorkerController);
+  }
+
+  @Test
+  public void call_EnabledCeWorkerController_refresh_on_POST() {
+    NanoHTTPD.Response response = underTest.serve(createHttpSession(POST));
+
+    assertThat(response.getStatus()).isEqualTo(OK);
+    verify(enabledCeWorkerController).refresh();
+    verifyNoMoreInteractions(enabledCeWorkerController);
+  }
+}
index 23b1ebb10db25100e17930cefd5f1e41030ff9ce..4d6271a79219dc990b5db7bf0ab7c9804bc7ae68 100644 (file)
@@ -119,6 +119,42 @@ public class CeHttpClient {
     }
   }
 
+  public void refreshCeWorkerCount() {
+    call(RefreshCeWorkerCountActionClient.INSTANCE);
+  }
+
+  private enum RefreshCeWorkerCountActionClient implements ActionClient<Void> {
+    INSTANCE;
+
+    @Override
+    public String getPath() {
+      return "refreshWorkerCount";
+    }
+
+    @Override
+    public Void getDefault() {
+      return null;
+    }
+
+    @Override
+    public Void call(String url) throws Exception {
+      okhttp3.Request request = new okhttp3.Request.Builder()
+        .post(RequestBody.create(null, new byte[0]))
+        .url(url)
+        .build();
+      okhttp3.Response response = new OkHttpClient().newCall(request).execute();
+      if (response.code() != 200) {
+        throw new IOException(
+          String.format(
+            "Failed to trigger refresh of CE Worker count. Code was '%s' and response was '%s' for url '%s'",
+            response.code(),
+            response.body().string(),
+            url));
+      }
+      return null;
+    }
+  }
+
   private <T> T call(ActionClient<T> actionClient) {
     try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(ipcSharedDir, COMPUTE_ENGINE.getIpcIndex())) {
       if (commands.isUp()) {
index 86d319d9e0ff5a68e58e236e0f2af0d355d951cc..f44a234f28c32477ba619e783f0b751138391bd6 100644 (file)
@@ -133,6 +133,31 @@ public class CeHttpClientTest {
     underTest.changeLogLevel(LoggerLevel.INFO);
   }
 
+  @Test
+  public void refreshCeWorkerCount_throws_ISE_if_http_error() {
+    String message = "blah";
+    server.enqueue(new MockResponse().setResponseCode(500).setBody(message));
+    // initialize registration of process
+    setUpWithHttpUrl(ProcessId.COMPUTE_ENGINE);
+
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Failed to call HTTP server of process " + ProcessId.COMPUTE_ENGINE);
+    expectedException.expectCause(hasType(IOException.class)
+        .andMessage(format("Failed to trigger refresh of CE Worker count. Code was '500' and response was 'blah' for url " +
+            "'http://%s:%s/refreshWorkerCount'", server.getHostName(), server.getPort())));
+
+    underTest.refreshCeWorkerCount();
+  }
+
+  @Test
+  public void refreshCeWorkerCount_does_not_fail_when_http_code_is_200() {
+    server.enqueue(new MockResponse().setResponseCode(200));
+
+    setUpWithHttpUrl(ProcessId.COMPUTE_ENGINE);
+
+    underTest.refreshCeWorkerCount();
+  }
+
   private void setUpWithHttpUrl(ProcessId processId) {
     try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(ipcSharedDir, processId.getIpcIndex())) {
       processCommands.setUp();