Browse Source

SONAR-14128 Store and provide node name of CE Task in /api/ce/activity

tags/9.9.0.65466
Aurelien Poscia 1 year ago
parent
commit
6bf8a78f31
26 changed files with 418 additions and 23 deletions
  1. 1
    0
      server/sonar-ce-common/build.gradle
  2. 7
    2
      server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java
  3. 57
    7
      server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java
  4. 5
    2
      server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java
  5. 38
    5
      server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java
  6. 16
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java
  7. 3
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
  8. 2
    1
      server/sonar-db-dao/src/schema/schema-sq.ddl
  9. 3
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
  10. 17
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java
  11. 2
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java
  12. 51
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTable.java
  13. 31
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v99/DbVersion99.java
  14. 53
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTableTest.java
  15. 40
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v99/DbVersion99Test.java
  16. 25
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTableTest/schema.sql
  17. 4
    1
      server/sonar-server-common/src/main/java/org/sonar/server/platform/WebServer.java
  18. 10
    0
      server/sonar-server-common/src/main/java/org/sonar/server/platform/WebServerImpl.java
  19. 36
    0
      server/sonar-server-common/src/test/java/org/sonar/server/platform/WebServerImplTest.java
  20. 2
    1
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/serverid/ServerIdManager.java
  21. 1
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java
  22. 3
    1
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/projectdump/ExportSubmitterImplTest.java
  23. 4
    0
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
  24. 3
    1
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java
  25. 3
    0
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskFormatterTest.java
  26. 1
    0
      sonar-ws/src/main/protobuf/ws-ce.proto

+ 1
- 0
server/sonar-ce-common/build.gradle View File

@@ -50,6 +50,7 @@ dependencies {
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.assertj:assertj-guava'
testImplementation 'org.hamcrest:hamcrest-all'
testImplementation 'org.mockito:mockito-core'
testImplementation project(':sonar-plugin-api-impl')
testImplementation testFixtures(project(':server:sonar-server-common'))
}

+ 7
- 2
server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeQueueImpl.java View File

@@ -49,6 +49,7 @@ import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.DeleteIf;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.platform.WebServer;
import org.sonar.server.property.InternalProperties;

import static com.google.common.base.Preconditions.checkState;
@@ -68,11 +69,13 @@ public class CeQueueImpl implements CeQueue {
private final System2 system2;
private final DbClient dbClient;
private final UuidFactory uuidFactory;
protected final WebServer webServer;

public CeQueueImpl(System2 system2, DbClient dbClient, UuidFactory uuidFactory) {
public CeQueueImpl(System2 system2, DbClient dbClient, UuidFactory uuidFactory, WebServer webServer) {
this.system2 = system2;
this.dbClient = dbClient;
this.uuidFactory = uuidFactory;
this.webServer = webServer;
}

@Override
@@ -243,6 +246,7 @@ public class CeQueueImpl implements CeQueue {

private void cancelImpl(DbSession dbSession, CeQueueDto q) {
CeActivityDto activityDto = new CeActivityDto(q);
activityDto.setNodeName(webServer.getNodeName().orElse(null));
activityDto.setStatus(CeActivityDto.Status.CANCELED);
remove(dbSession, q, activityDto);
}
@@ -251,13 +255,14 @@ public class CeQueueImpl implements CeQueue {
public void fail(DbSession dbSession, CeQueueDto task, @Nullable String errorType, @Nullable String errorMessage) {
checkState(IN_PROGRESS.equals(task.getStatus()), "Task is not in-progress and can't be marked as failed [uuid=%s]", task.getUuid());
CeActivityDto activityDto = new CeActivityDto(task);
activityDto.setNodeName(webServer.getNodeName().orElse(null));
activityDto.setStatus(CeActivityDto.Status.FAILED);
activityDto.setErrorType(errorType);
activityDto.setErrorMessage(errorMessage);
updateExecutionFields(activityDto);
remove(dbSession, task, activityDto);
}
protected long updateExecutionFields(CeActivityDto activityDto) {
Long startedAt = activityDto.getStartedAt();
if (startedAt == null) {

+ 57
- 7
server/sonar-ce-common/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java View File

@@ -43,6 +43,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTesting;
import org.sonar.server.platform.WebServer;

import static com.google.common.collect.ImmutableList.of;
import static java.util.Arrays.asList;
@@ -52,12 +53,15 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.ce.queue.CeQueue.SubmitOption.UNIQUE_QUEUE_PER_MAIN_COMPONENT;

public class CeQueueImplTest {

private static final String WORKER_UUID = "workerUuid";
private static final long NOW = 1_450_000_000_000L;
private static final String NODE_NAME = "nodeName1";

private System2 system2 = new TestSystem2().setNow(NOW);

@@ -68,7 +72,9 @@ public class CeQueueImplTest {

private UuidFactory uuidFactory = new SequenceUuidFactory();

private CeQueue underTest = new CeQueueImpl(system2, db.getDbClient(), uuidFactory);
private WebServer nodeInformationProvider = mock(WebServer.class);

private CeQueue underTest = new CeQueueImpl(system2, db.getDbClient(), uuidFactory, nodeInformationProvider);

@Test
public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() {
@@ -382,11 +388,37 @@ public class CeQueueImplTest {

underTest.cancel(db.getSession(), queueDto);

Optional<CeActivityDto> activity = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
Optional<CeActivityDto> activity = findCeActivityDtoInDb(task);
assertThat(activity).isPresent();
assertThat(activity.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
}

@Test
public void cancel_pending_whenNodeNameProvided_setItInCeActivity() {
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.of(NODE_NAME));
CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
CeQueueDto queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();

underTest.cancel(db.getSession(), queueDto);

Optional<CeActivityDto> activity = findCeActivityDtoInDb(task);
assertThat(activity).isPresent();
assertThat(activity.get().getNodeName()).isEqualTo(NODE_NAME);
}

@Test
public void cancel_pending_whenNodeNameNOtProvided_setNulInCeActivity() {
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.empty());
CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
CeQueueDto queueDto = db.getDbClient().ceQueueDao().selectByUuid(db.getSession(), task.getUuid()).get();

underTest.cancel(db.getSession(), queueDto);

Optional<CeActivityDto> activity = findCeActivityDtoInDb(task);
assertThat(activity).isPresent();
assertThat(activity.get().getNodeName()).isNull();
}

@Test
public void fail_to_cancel_if_in_progress() {
CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(11)));
@@ -408,11 +440,11 @@ public class CeQueueImplTest {
int canceledCount = underTest.cancelAll();
assertThat(canceledCount).isEqualTo(2);

Optional<CeActivityDto> ceActivityInProgress = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), pendingTask1.getUuid());
Optional<CeActivityDto> ceActivityInProgress = findCeActivityDtoInDb(pendingTask1);
assertThat(ceActivityInProgress.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
Optional<CeActivityDto> ceActivityPending1 = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), pendingTask2.getUuid());
Optional<CeActivityDto> ceActivityPending1 = findCeActivityDtoInDb(pendingTask2);
assertThat(ceActivityPending1.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
Optional<CeActivityDto> ceActivityPending2 = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), inProgressTask.getUuid());
Optional<CeActivityDto> ceActivityPending2 = findCeActivityDtoInDb(inProgressTask);
assertThat(ceActivityPending2).isNotPresent();
}

@@ -430,7 +462,7 @@ public class CeQueueImplTest {
@Test
public void pauseWorkers_marks_workers_as_pausing_if_some_tasks_in_progress() {
CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
db.getDbClient().ceQueueDao().tryToPeek(session, task.getUuid(), WORKER_UUID);
db.getDbClient().ceQueueDao().tryToPeek(session, task.getUuid(), WORKER_UUID);
// task is in-progress

assertThat(underTest.getWorkersPauseStatus()).isEqualTo(CeQueue.WorkersPauseStatus.RESUMED);
@@ -477,13 +509,31 @@ public class CeQueueImplTest {

underTest.fail(db.getSession(), queueDto, "TIMEOUT", "Failed on timeout");

Optional<CeActivityDto> activity = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
Optional<CeActivityDto> activity = findCeActivityDtoInDb(task);
assertThat(activity).isPresent();
assertThat(activity.get().getStatus()).isEqualTo(CeActivityDto.Status.FAILED);
assertThat(activity.get().getErrorType()).isEqualTo("TIMEOUT");
assertThat(activity.get().getErrorMessage()).isEqualTo("Failed on timeout");
assertThat(activity.get().getExecutedAt()).isEqualTo(NOW);
assertThat(activity.get().getWorkerUuid()).isEqualTo(WORKER_UUID);
assertThat(activity.get().getNodeName()).isNull();
}

@Test
public void fail_in_progress_task_whenNodeNameProvided_setsItInCeActivityDto() {
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.of(NODE_NAME));
CeTask task = submit(CeTaskTypes.REPORT, newComponent(randomAlphabetic(12)));
CeQueueDto queueDto = db.getDbClient().ceQueueDao().tryToPeek(db.getSession(), task.getUuid(), WORKER_UUID).get();

underTest.fail(db.getSession(), queueDto, "TIMEOUT", "Failed on timeout");

Optional<CeActivityDto> activity = findCeActivityDtoInDb(task);
assertThat(activity).isPresent();
assertThat(activity.get().getNodeName()).isEqualTo(NODE_NAME);
}

private Optional<CeActivityDto> findCeActivityDtoInDb(CeTask task) {
return db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
}

@Test

+ 5
- 2
server/sonar-ce/src/main/java/org/sonar/ce/queue/InternalCeQueueImpl.java View File

@@ -47,6 +47,7 @@ import org.sonar.db.ce.CeQueueDao;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.platform.WebServer;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
@@ -65,8 +66,8 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue
private final NextPendingTaskPicker nextPendingTaskPicker;

public InternalCeQueueImpl(System2 system2, DbClient dbClient, UuidFactory uuidFactory, CEQueueStatus queueStatus,
ComputeEngineStatus computeEngineStatus, NextPendingTaskPicker nextPendingTaskPicker) {
super(system2, dbClient, uuidFactory);
ComputeEngineStatus computeEngineStatus, NextPendingTaskPicker nextPendingTaskPicker, WebServer webServer) {
super(system2, dbClient, uuidFactory, webServer);
this.dbClient = dbClient;
this.queueStatus = queueStatus;
this.computeEngineStatus = computeEngineStatus;
@@ -113,6 +114,7 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue
CeQueueDto queueDto = dbClient.ceQueueDao().selectByUuid(dbSession, task.getUuid())
.orElseThrow(() -> new IllegalStateException("Task does not exist anymore: " + task));
CeActivityDto activityDto = new CeActivityDto(queueDto);
activityDto.setNodeName(webServer.getNodeName().orElse(null));
activityDto.setStatus(status);
executionTimeInMs = updateExecutionFields(activityDto);
updateTaskResult(activityDto, taskResult);
@@ -176,6 +178,7 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue
List<CeQueueDto> wornOutTasks = dbClient.ceQueueDao().selectWornout(dbSession);
wornOutTasks.forEach(queueDto -> {
CeActivityDto activityDto = new CeActivityDto(queueDto);
activityDto.setNodeName(webServer.getNodeName().orElse(null));
activityDto.setStatus(CeActivityDto.Status.CANCELED);
updateExecutionFields(activityDto);
remove(dbSession, queueDto, activityDto);

+ 38
- 5
server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java View File

@@ -48,6 +48,7 @@ import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.user.UserDto;
import org.sonar.server.platform.WebServer;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
@@ -68,6 +69,7 @@ public class InternalCeQueueImplTest {
private static final String AN_ANALYSIS_UUID = "U1";
private static final String WORKER_UUID_1 = "worker uuid 1";
private static final String WORKER_UUID_2 = "worker uuid 2";
private static final String NODE_NAME = "nodeName1";

private System2 system2 = new AlwaysIncreasingSystem2();

@@ -81,13 +83,15 @@ public class InternalCeQueueImplTest {
private ComputeEngineStatus computeEngineStatus = mock(ComputeEngineStatus.class);
private Configuration config = mock(Configuration.class);
private NextPendingTaskPicker nextPendingTaskPicker = new NextPendingTaskPicker(config, db.getDbClient());
private WebServer nodeInformationProvider = mock(WebServer.class);
private InternalCeQueue underTest = new InternalCeQueueImpl(system2, db.getDbClient(), uuidFactory, queueStatus,
computeEngineStatus, nextPendingTaskPicker);
computeEngineStatus, nextPendingTaskPicker, nodeInformationProvider);

@Before
public void setUp() {
when(config.getBoolean(any())).thenReturn(Optional.of(false));
when(computeEngineStatus.getStatus()).thenReturn(STARTED);
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.of(NODE_NAME));
}

@Test
@@ -210,6 +214,32 @@ public class InternalCeQueueImplTest {
assertThat(history.get().getAnalysisUuid()).isEqualTo("U1");
}

@Test
public void remove_sets_nodeName_in_CeActivity_when_nodeInformationProvider_defines_node_name() {
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.of(NODE_NAME));
CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));

Optional<CeTask> peek = underTest.peek(WORKER_UUID_2, true);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(AN_ANALYSIS_UUID), null);

Optional<CeActivityDto> history = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
assertThat(history).isPresent();
assertThat(history.get().getNodeName()).isEqualTo(NODE_NAME);
}

@Test
public void remove_do_not_set_nodeName_in_CeActivity_when_nodeInformationProvider_does_not_define_node_name() {
when(nodeInformationProvider.getNodeName()).thenReturn(Optional.empty());
CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));

Optional<CeTask> peek = underTest.peek(WORKER_UUID_2, true);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(AN_ANALYSIS_UUID), null);

Optional<CeActivityDto> history = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
assertThat(history).isPresent();
assertThat(history.get().getNodeName()).isNull();
}

@Test
public void remove_saves_error_message_and_stacktrace_when_exception_is_provided() {
Throwable error = new NullPointerException("Fake NPE to test persistence to DB");
@@ -248,7 +278,7 @@ public class InternalCeQueueImplTest {
db.getDbClient().ceQueueDao().deleteByUuid(db.getSession(), task.getUuid());
db.commit();

InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatus, null, null);
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatus, null, null, nodeInformationProvider);

try {
underTest.remove(task, CeActivityDto.Status.SUCCESS, null, null);
@@ -265,7 +295,7 @@ public class InternalCeQueueImplTest {
CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
db.getDbClient().ceQueueDao().deleteByUuid(db.getSession(), task.getUuid());
db.commit();
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null);
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null, nodeInformationProvider);

try {
underTest.remove(task, CeActivityDto.Status.FAILED, null, null);
@@ -277,16 +307,19 @@ public class InternalCeQueueImplTest {

@Test
public void cancelWornOuts_does_not_update_queueStatus() {

CEQueueStatus queueStatusMock = mock(CEQueueStatus.class);

CeTask task = submit(CeTaskTypes.REPORT, newProjectDto("PROJECT_1"));
db.executeUpdateSql("update ce_queue set status = 'PENDING', started_at = 123 where uuid = '" + task.getUuid() + "'");
db.commit();
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null);
InternalCeQueueImpl underTest = new InternalCeQueueImpl(system2, db.getDbClient(), null, queueStatusMock, null, null, nodeInformationProvider);

underTest.cancelWornOuts();

assertThat(db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid())).isPresent();
Optional<CeActivityDto> ceActivityDto = db.getDbClient().ceActivityDao().selectByUuid(db.getSession(), task.getUuid());
assertThat(ceActivityDto).isPresent();
assertThat(ceActivityDto.get().getNodeName()).isEqualTo(NODE_NAME);
verifyNoInteractions(queueStatusMock);
}


+ 16
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDto.java View File

@@ -30,7 +30,8 @@ import static java.lang.String.format;

public class CeActivityDto {

private static final int MAX_SIZE_ERROR_MESSAGE = 1000;
private static final int ERROR_MESSAGE_MAX_SIZE = 1000;
private static final int NODE_NAME_MAX_SIZE = 100;

public enum Status {
SUCCESS, FAILED, CANCELED
@@ -105,6 +106,8 @@ public class CeActivityDto {
*/
private int warningCount = 0;

private String nodeName;

CeActivityDto() {
// required for MyBatis
}
@@ -287,7 +290,7 @@ public class CeActivityDto {
}

public CeActivityDto setErrorMessage(@Nullable String errorMessage) {
this.errorMessage = ensureNotTooBig(removeCharZeros(errorMessage), MAX_SIZE_ERROR_MESSAGE);
this.errorMessage = ensureNotTooBig(removeCharZeros(errorMessage), ERROR_MESSAGE_MAX_SIZE);
return this;
}

@@ -331,10 +334,21 @@ public class CeActivityDto {
return this;
}

@CheckForNull
public String getNodeName() {
return nodeName;
}

public CeActivityDto setNodeName(@Nullable String nodeName) {
this.nodeName = ensureNotTooBig(nodeName, NODE_NAME_MAX_SIZE);
return this;
}

@Override
public String toString() {
return "CeActivityDto{" +
"uuid='" + uuid + '\'' +
", nodeName='" + nodeName + '\'' +
", componentUuid='" + componentUuid + '\'' +
", mainComponentUuid='" + mainComponentUuid + '\'' +
", analysisUuid='" + analysisUuid + '\'' +

+ 3
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml View File

@@ -21,6 +21,7 @@

<sql id="ceActivityColumns">
ca.uuid,
ca.node_name as nodeName,
ca.task_type as taskType,
ca.component_uuid as componentUuid,
ca.main_component_uuid as mainComponentUuid,
@@ -175,6 +176,7 @@
<insert id="insert" parameterType="org.sonar.db.ce.CeActivityDto" useGeneratedKeys="false">
insert into ce_activity (
uuid,
node_name,
component_uuid,
main_component_uuid,
analysis_uuid,
@@ -199,6 +201,7 @@
)
values (
#{uuid,jdbcType=VARCHAR},
#{nodeName,jdbcType=VARCHAR},
#{componentUuid,jdbcType=VARCHAR},
#{mainComponentUuid,jdbcType=VARCHAR},
#{analysisUuid,jdbcType=VARCHAR},

+ 2
- 1
server/sonar-db-dao/src/schema/schema-sq.ddl View File

@@ -136,7 +136,8 @@ CREATE TABLE "CE_ACTIVITY"(
"ERROR_TYPE" CHARACTER VARYING(20),
"WORKER_UUID" CHARACTER VARYING(40),
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
"UPDATED_AT" BIGINT NOT NULL,
"NODE_NAME" CHARACTER VARYING(100)
);
ALTER TABLE "CE_ACTIVITY" ADD CONSTRAINT "PK_CE_ACTIVITY" PRIMARY KEY("UUID");
CREATE INDEX "CE_ACTIVITY_COMPONENT" ON "CE_ACTIVITY"("COMPONENT_UUID" NULLS FIRST);

+ 3
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java View File

@@ -71,6 +71,7 @@ public class CeActivityDaoTest {
private static final String COMPONENT_1 = randomAlphabetic(14);

private static final long INITIAL_TIME = 1_450_000_000_000L;
private static final String NODE_NAME = "node1";

private final TestSystem2 system2 = new TestSystem2().setNow(INITIAL_TIME);

@@ -93,6 +94,7 @@ public class CeActivityDaoTest {
assertThat(saved).isPresent();
CeActivityDto dto = saved.get();
assertThat(dto.getUuid()).isEqualTo("TASK_1");
assertThat(dto.getNodeName()).isEqualTo(NODE_NAME);
assertThat(dto.getMainComponentUuid()).isEqualTo(MAINCOMPONENT_1);
assertThat(dto.getComponentUuid()).isEqualTo(COMPONENT_1);
assertThat(dto.getStatus()).isEqualTo(SUCCESS);
@@ -858,6 +860,7 @@ public class CeActivityDaoTest {
CeQueueDto ceQueueDto = db.getDbClient().ceQueueDao().selectByUuid(dbSession, uuid).get();

CeActivityDto dto = new CeActivityDto(ceQueueDto);
dto.setNodeName(NODE_NAME);
dto.setStatus(status);
dto.setStartedAt(1_500_000_000_000L);
dto.setExecutedAt(1_500_000_000_500L);

+ 17
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDtoTest.java View File

@@ -26,6 +26,7 @@ import java.util.Random;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -33,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
@RunWith(DataProviderRunner.class)
public class CeActivityDtoTest {
private static final String STR_40_CHARS = "0123456789012345678901234567890123456789";
private static final String STR_100_CHARS = randomAlphabetic(100);
private CeActivityDto underTest = new CeActivityDto();

@Test
@@ -99,6 +101,21 @@ public class CeActivityDtoTest {
.hasMessage("Value is too long for column CE_ACTIVITY.MAIN_COMPONENT_UUID: " + str_41_chars);
}

@Test
public void setNodeName_accepts_null_empty_and_string_100_chars_or_less() {
underTest.setNodeName(null);
underTest.setNodeName("");
underTest.setNodeName("bar");
underTest.setNodeName(STR_100_CHARS);
assertThat(underTest.getNodeName()).isEqualTo(STR_100_CHARS);
}

@Test
public void setNodeName_ifMoreThan100chars_truncates() {
underTest.setNodeName(STR_100_CHARS + "This should be truncated");
assertThat(underTest.getNodeName()).isEqualTo(STR_100_CHARS);
}

@Test
@UseDataProvider("stringsWithChar0")
public void setStacktrace_filters_out_char_zero(String withChar0, String expected) {

+ 2
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java View File

@@ -37,6 +37,7 @@ import org.sonar.server.platform.db.migration.version.v95.DbVersion95;
import org.sonar.server.platform.db.migration.version.v96.DbVersion96;
import org.sonar.server.platform.db.migration.version.v97.DbVersion97;
import org.sonar.server.platform.db.migration.version.v98.DbVersion98;
import org.sonar.server.platform.db.migration.version.v99.DbVersion99;

public class MigrationConfigurationModule extends Module {
@Override
@@ -54,6 +55,7 @@ public class MigrationConfigurationModule extends Module {
DbVersion96.class,
DbVersion97.class,
DbVersion98.class,
DbVersion99.class,

// migration steps
MigrationStepRegistryImpl.class,

+ 51
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTable.java View File

@@ -0,0 +1,51 @@
/*
* SonarQube
* Copyright (C) 2009-2022 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.server.platform.db.migration.version.v99;

import com.google.common.annotations.VisibleForTesting;
import java.sql.Connection;
import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.db.DatabaseUtils;
import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

class AddNodeNameColumnToCeActivityTable extends DdlChange {
@VisibleForTesting
static final String TABLE_NAME = "ce_activity";
@VisibleForTesting
static final String COLUMN_NAME = "node_name";

public AddNodeNameColumnToCeActivityTable(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
try (Connection c = getDatabase().getDataSource().getConnection()) {
if (!DatabaseUtils.tableColumnExists(c, TABLE_NAME, COLUMN_NAME)) {
context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
.addColumn(VarcharColumnDef.newVarcharColumnDefBuilder(COLUMN_NAME).setLimit(100).setIsNullable(true).build())
.build());
}
}
}
}

+ 31
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v99/DbVersion99.java View File

@@ -0,0 +1,31 @@
/*
* SonarQube
* Copyright (C) 2009-2022 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.server.platform.db.migration.version.v99;

import org.sonar.server.platform.db.migration.step.MigrationStepRegistry;
import org.sonar.server.platform.db.migration.version.DbVersion;

public class DbVersion99 implements DbVersion {
@Override
public void addSteps(MigrationStepRegistry registry) {
registry
.add(6800, "Add node_name column to ce_activity table", AddNodeNameColumnToCeActivityTable.class);
}
}

+ 53
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTableTest.java View File

@@ -0,0 +1,53 @@
/*
* SonarQube
* Copyright (C) 2009-2022 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.server.platform.db.migration.version.v99;

import java.sql.SQLException;
import java.sql.Types;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;

import static org.sonar.server.platform.db.migration.version.v99.AddNodeNameColumnToCeActivityTable.COLUMN_NAME;
import static org.sonar.server.platform.db.migration.version.v99.AddNodeNameColumnToCeActivityTable.TABLE_NAME;

public class AddNodeNameColumnToCeActivityTableTest {

@Rule
public final CoreDbTester db = CoreDbTester.createForSchema(AddNodeNameColumnToCeActivityTableTest.class, "schema.sql");

private final AddNodeNameColumnToCeActivityTable underTest = new AddNodeNameColumnToCeActivityTable(db.database());

@Test
public void migration_should_add_column() throws SQLException {
db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
underTest.execute();
db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.VARCHAR, null, true);
}

@Test
public void migration_should_be_reentrant() throws SQLException {
db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
underTest.execute();
// re-entrant
underTest.execute();
db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.VARCHAR, null, true);
}
}

+ 40
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v99/DbVersion99Test.java View File

@@ -0,0 +1,40 @@
/*
* SonarQube
* Copyright (C) 2009-2022 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.server.platform.db.migration.version.v99;

import org.junit.Test;

import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationNotEmpty;
import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;

public class DbVersion99Test {
private final DbVersion99 underTest = new DbVersion99();

@Test
public void migrationNumber_starts_at_6800() {
verifyMinimumMigrationNumber(underTest, 6800);
}

@Test
public void verify_migration_is_not_empty() {
verifyMigrationNotEmpty(underTest);
}

}

+ 25
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v99/AddNodeNameColumnToCeActivityTableTest/schema.sql View File

@@ -0,0 +1,25 @@

CREATE TABLE "CE_ACTIVITY"(
"UUID" CHARACTER VARYING(40) NOT NULL,
"TASK_TYPE" CHARACTER VARYING(15) NOT NULL,
"MAIN_COMPONENT_UUID" CHARACTER VARYING(40),
"COMPONENT_UUID" CHARACTER VARYING(40),
"STATUS" CHARACTER VARYING(15) NOT NULL,
"MAIN_IS_LAST" BOOLEAN NOT NULL,
"MAIN_IS_LAST_KEY" CHARACTER VARYING(55) NOT NULL,
"IS_LAST" BOOLEAN NOT NULL,
"IS_LAST_KEY" CHARACTER VARYING(55) NOT NULL,
"SUBMITTER_UUID" CHARACTER VARYING(255),
"SUBMITTED_AT" BIGINT NOT NULL,
"STARTED_AT" BIGINT,
"EXECUTED_AT" BIGINT,
"EXECUTION_COUNT" INTEGER NOT NULL,
"EXECUTION_TIME_MS" BIGINT,
"ANALYSIS_UUID" CHARACTER VARYING(50),
"ERROR_MESSAGE" CHARACTER VARYING(1000),
"ERROR_STACKTRACE" CHARACTER LARGE OBJECT,
"ERROR_TYPE" CHARACTER VARYING(20),
"WORKER_UUID" CHARACTER VARYING(40),
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
);

+ 4
- 1
server/sonar-server-common/src/main/java/org/sonar/server/platform/WebServer.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.server.platform;

import java.util.Optional;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;

@@ -27,7 +28,7 @@ import org.sonar.api.server.ServerSide;
public interface WebServer {

/**
* WebServer is standalone when property {@link org.sonar.process.ProcessProperties.Property#CLUSTER_ENABLED} is {@code false} or
* Node is standalone when property {@link org.sonar.process.ProcessProperties.Property#CLUSTER_ENABLED} is {@code false} or
* undefined.
*/
boolean isStandalone();
@@ -40,4 +41,6 @@ public interface WebServer {
*/
boolean isStartupLeader();

Optional<String> getNodeName();

}

+ 10
- 0
server/sonar-server-common/src/main/java/org/sonar/server/platform/WebServerImpl.java View File

@@ -19,24 +19,29 @@
*/
package org.sonar.server.platform;

import java.util.Optional;
import org.sonar.api.config.Configuration;
import org.sonar.api.utils.log.Loggers;

import static org.sonar.process.ProcessProperties.Property.CLUSTER_ENABLED;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
import static org.sonar.process.ProcessProperties.Property.CLUSTER_WEB_STARTUP_LEADER;

public class WebServerImpl implements WebServer {

private final boolean clusterEnabled;
private final boolean startupLeader;
private final String nodeName;

public WebServerImpl(Configuration config) {
this.clusterEnabled = config.getBoolean(CLUSTER_ENABLED.getKey()).orElse(false);
if (this.clusterEnabled) {
this.startupLeader = config.getBoolean(CLUSTER_WEB_STARTUP_LEADER.getKey()).orElse(false);
this.nodeName = config.get(CLUSTER_NODE_NAME.getKey()).orElse(CLUSTER_NODE_NAME.getDefaultValue());
Loggers.get(WebServerImpl.class).info("Cluster enabled (startup {})", startupLeader ? "leader" : "follower");
} else {
this.startupLeader = true;
this.nodeName = null;
}
}

@@ -49,4 +54,9 @@ public class WebServerImpl implements WebServer {
public boolean isStartupLeader() {
return startupLeader;
}

@Override
public Optional<String> getNodeName() {
return Optional.ofNullable(nodeName);
}
}

+ 36
- 0
server/sonar-server-common/src/test/java/org/sonar/server/platform/WebServerImplTest.java View File

@@ -69,4 +69,40 @@ public class WebServerImplTest {
assertThat(underTest.isStartupLeader()).isFalse();
}

@Test
public void getNodeName_whenNotACluster_isEmpty() {
settings.setProperty("sonar.cluster.enabled", "false");
settings.setProperty("sonar.cluster.node.name", "nameIgnored");

WebServerImpl underTest = new WebServerImpl(settings.asConfig());

assertThat(underTest.getNodeName()).isEmpty();
}

@Test
public void getNodeName_whenClusterAndNameNotDefined_fallbacksToDefaultName() {
settings.setProperty("sonar.cluster.enabled", "true");
settings.removeProperty("sonar.cluster.node.name");

WebServerImpl underTest = new WebServerImpl(settings.asConfig());

assertThat(underTest.getNodeName()).isNotEmpty();
String nodeNameFirstCallToGetNodeName = underTest.getNodeName().get();
assertThat(nodeNameFirstCallToGetNodeName).startsWith("sonarqube-");
String nodeNameSecondCallToGetNodeName = underTest.getNodeName().get();
assertThat(nodeNameFirstCallToGetNodeName).isEqualTo(nodeNameSecondCallToGetNodeName);
}

@Test
public void getNodeName_whenClusterAndNameDefined_returnName() {
String nodeName = "nodeName1";
settings.setProperty("sonar.cluster.enabled", "true");
settings.setProperty("sonar.cluster.node.name", nodeName);

WebServerImpl underTest = new WebServerImpl(settings.asConfig());

assertThat(underTest.getNodeName()).isNotEmpty();
assertThat(underTest.getNodeName().get()).startsWith(nodeName);
}

}

+ 2
- 1
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/serverid/ServerIdManager.java View File

@@ -47,7 +47,8 @@ public class ServerIdManager implements Startable {
private final SonarRuntime runtime;
private final WebServer webServer;

public ServerIdManager(ServerIdChecksum serverIdChecksum, ServerIdFactory serverIdFactory, DbClient dbClient, SonarRuntime runtime, WebServer webServer) {
public ServerIdManager(ServerIdChecksum serverIdChecksum, ServerIdFactory serverIdFactory,
DbClient dbClient, SonarRuntime runtime, WebServer webServer) {
this.serverIdChecksum = serverIdChecksum;
this.serverIdFactory = serverIdFactory;
this.dbClient = dbClient;

+ 1
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java View File

@@ -107,6 +107,7 @@ public class TaskFormatter {
builder.setId(dto.getUuid());
builder.setStatus(Ce.TaskStatus.valueOf(dto.getStatus().name()));
builder.setType(dto.getTaskType());
ofNullable(dto.getNodeName()).ifPresent(builder::setNodeName);
ofNullable(dto.getComponentUuid()).ifPresent(uuid -> setComponent(builder, uuid, cache).setComponentId(uuid));
String analysisUuid = dto.getAnalysisUuid();
ofNullable(analysisUuid).ifPresent(builder::setAnalysisId);

+ 3
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/projectdump/ExportSubmitterImplTest.java View File

@@ -29,10 +29,12 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.platform.WebServer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock;

public class ExportSubmitterImplTest {

@@ -43,7 +45,7 @@ public class ExportSubmitterImplTest {
public DbTester db = DbTester.create(system2);

private final DbClient dbClient = db.getDbClient();
private final CeQueue ceQueue = new CeQueueImpl(system2, db.getDbClient(), UuidFactoryFast.getInstance());
private final CeQueue ceQueue = new CeQueueImpl(system2, db.getDbClient(), UuidFactoryFast.getInstance(), mock(WebServer.class));

private final ExportSubmitterImpl underTest = new ExportSubmitterImpl(ceQueue, dbClient);


+ 4
- 0
server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java View File

@@ -84,6 +84,7 @@ import static org.sonar.server.ce.ws.CeWsParameters.PARAM_TYPE;
public class ActivityActionTest {

private static final long EXECUTED_AT = System2.INSTANCE.now();
private static final String NODE_NAME = "nodeName1";

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@@ -111,6 +112,7 @@ public class ActivityActionTest {
Task task = activityResponse.getTasks(0);
assertThat(task.getId()).isEqualTo("T2");
assertThat(task.getStatus()).isEqualTo(Ce.TaskStatus.FAILED);
assertThat(task.getNodeName()).isEqualTo(NODE_NAME);
assertThat(task.getComponentId()).isEqualTo(project2.uuid());
assertThat(task.hasAnalysisId()).isFalse();
assertThat(task.getExecutionTimeMs()).isEqualTo(500L);
@@ -118,6 +120,7 @@ public class ActivityActionTest {

task = activityResponse.getTasks(1);
assertThat(task.getId()).isEqualTo("T1");
assertThat(task.getNodeName()).isEqualTo(NODE_NAME);
assertThat(task.getStatus()).isEqualTo(Ce.TaskStatus.SUCCESS);
assertThat(task.getComponentId()).isEqualTo(project1.uuid());
assertThat(task.getWarningCount()).isZero();
@@ -678,6 +681,7 @@ public class ActivityActionTest {
activityDto.setStatus(status);
activityDto.setExecutionTimeMs(500L);
activityDto.setExecutedAt(EXECUTED_AT);
activityDto.setNodeName(NODE_NAME);
activityDto.setAnalysisUuid(analysis == null ? null : analysis.getUuid());
db.getDbClient().ceActivityDao().insert(db.getSession(), activityDto);
db.commit();

+ 3
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/CancelActionTest.java View File

@@ -36,12 +36,14 @@ import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.platform.WebServer;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester;

import static java.util.Collections.emptyMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;

public class CancelActionTest {

@@ -51,7 +53,7 @@ public class CancelActionTest {
public DbTester db = DbTester.create();

private System2 system2 = new TestSystem2();
private CeQueue queue = new CeQueueImpl(system2, db.getDbClient(), UuidFactoryFast.getInstance());
private CeQueue queue = new CeQueueImpl(system2, db.getDbClient(), UuidFactoryFast.getInstance(), mock(WebServer.class));

private CancelAction underTest = new CancelAction(userSession, db.getDbClient(), queue);
private WsActionTester tester = new WsActionTester(underTest);

+ 3
- 0
server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/ws/TaskFormatterTest.java View File

@@ -49,6 +49,7 @@ import static org.sonar.db.ce.CeQueueTesting.makeInProgress;

public class TaskFormatterTest {

private static final String NODE_NAME = "nodeName1";
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

@@ -187,6 +188,7 @@ public class TaskFormatterTest {

assertThat(wsTask.getType()).isEqualTo(CeTaskTypes.REPORT);
assertThat(wsTask.getId()).isEqualTo("UUID");
assertThat(wsTask.getNodeName()).isEqualTo(NODE_NAME);
assertThat(wsTask.getStatus()).isEqualTo(Ce.TaskStatus.FAILED);
assertThat(wsTask.getSubmittedAt()).isEqualTo(DateUtils.formatDateTime(new Date(1_450_000_000_000L)));
assertThat(wsTask.getSubmitterLogin()).isEqualTo(user.getLogin());
@@ -282,6 +284,7 @@ public class TaskFormatterTest {
testActivityDto.setWarningCount(warningCount);
return testActivityDto
.setStatus(status)
.setNodeName(NODE_NAME)
.setExecutionTimeMs(500L)
.setAnalysisUuid("U1");
}

+ 1
- 0
sonar-ws/src/main/protobuf/ws-ce.proto View File

@@ -134,6 +134,7 @@ message Task {
optional string pullRequestTitle = 25;
optional int32 warningCount = 26;
repeated string warnings = 27;
optional string nodeName = 28;
}

enum TaskStatus {

Loading…
Cancel
Save