From 8782863ad2fe6815d77b2ff5f81d7f370e542246 Mon Sep 17 00:00:00 2001 From: Pierre Guillot <50145663+pierre-guillot-sonarsource@users.noreply.github.com> Date: Mon, 30 Nov 2020 11:50:10 +0100 Subject: [PATCH] SONAR-14091 handle indexation tasks bound to an orphan branch --- .../taskprocessor/IgnoreOrphanBranchStep.java | 61 ++++++++++ .../taskprocessor/IndexIssuesStep.java | 1 - .../taskprocessor/IssueSyncTaskModule.java | 1 + .../taskprocessor/IssueSyncTaskProcessor.java | 5 +- .../IgnoreOrphanBranchStepTest.java | 111 ++++++++++++++++++ .../IssueSyncTaskProcessorTest.java | 4 +- .../org/sonar/db/ce/CeQueueMapper.xml | 2 +- .../java/org/sonar/db/ce/CeQueueDaoTest.java | 14 +++ 8 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java create mode 100644 server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepTest.java diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java new file mode 100644 index 00000000000..1e809a00506 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.task.projectanalysis.taskprocessor; + +import java.util.Optional; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.ce.task.CeTask; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; + +public final class IgnoreOrphanBranchStep implements ComputationStep { + private static final Logger LOG = Loggers.get(IgnoreOrphanBranchStep.class); + private final CeTask ceTask; + private final DbClient dbClient; + + public IgnoreOrphanBranchStep(CeTask ceTask, DbClient dbClient) { + this.ceTask = ceTask; + this.dbClient = dbClient; + } + + @Override + public void execute(Context context) { + String mainComponentUuid = ceTask.getMainComponent().orElseThrow(() -> new UnsupportedOperationException("main component not found in task")).getUuid(); + String componentUuid = ceTask.getComponent().orElseThrow(() -> new UnsupportedOperationException("component not found in task")).getUuid(); + + try (DbSession dbSession = dbClient.openSession(false)) { + Optional componentDto = dbClient.componentDao().selectByUuid(dbSession, mainComponentUuid); + if(!componentDto.isPresent()){ + LOG.info("reindexation task has been trigger on an orphan branch. removing any exclude_from_purge flag, and skip the indexation"); + dbClient.branchDao().updateExcludeFromPurge(dbSession, componentUuid, false); + dbClient.branchDao().updateNeedIssueSync(dbSession, componentUuid, false); + dbSession.commit(); + } + } + } + + @Override + public String getDescription() { + return "Ignore orphan component"; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IndexIssuesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IndexIssuesStep.java index a291b7006ea..38fcc30536f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IndexIssuesStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IndexIssuesStep.java @@ -57,7 +57,6 @@ public final class IndexIssuesStep implements ComputationStep { LOG.debug("issues of branch {} are already in sync", branchUuid); } }); - } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskModule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskModule.java index 23eb54fdf1e..b40e333009c 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskModule.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskModule.java @@ -26,6 +26,7 @@ public class IssueSyncTaskModule extends Module { @Override protected void configureModule() { add( + IgnoreOrphanBranchStep.class, IssueSyncTaskProcessor.class); } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessor.java index 53f1826975b..ba2d179871b 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessor.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessor.java @@ -20,7 +20,7 @@ package org.sonar.ce.task.projectanalysis.taskprocessor; import com.google.common.collect.ImmutableSet; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.Set; import javax.annotation.CheckForNull; @@ -64,6 +64,7 @@ public class IssueSyncTaskProcessor implements CeTaskProcessor { static ContainerPopulator newContainerPopulator(CeTask task) { return taskContainer -> { taskContainer.add(task); + taskContainer.add(IgnoreOrphanBranchStep.class); taskContainer.add(IndexIssuesStep.class); taskContainer.add(new SyncComputationSteps(taskContainer)); taskContainer.add(ComputationStepExecutor.class); @@ -78,7 +79,7 @@ public class IssueSyncTaskProcessor implements CeTaskProcessor { @Override public List> orderedStepClasses() { - return Collections.singletonList(IndexIssuesStep.class); + return Arrays.asList(IgnoreOrphanBranchStep.class, IndexIssuesStep.class); } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepTest.java new file mode 100644 index 00000000000..91ed08e795a --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepTest.java @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.task.projectanalysis.taskprocessor; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.ce.task.CeTask; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.BranchDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonar.db.component.BranchType.BRANCH; + +public class IgnoreOrphanBranchStepTest { + + private String BRANCH_UUID = "branch_uuid"; + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + private CeTask.Component component = new CeTask.Component(BRANCH_UUID, "component key", "component name"); + private CeTask ceTask = new CeTask.Builder() + .setOrganizationUuid("organizationUuid") + .setType("type") + .setUuid("uuid") + .setComponent(component) + .setMainComponent(component) + .build(); + + private DbClient dbClient = dbTester.getDbClient(); + private IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient); + + @Test + public void execute() { + BranchDto branchDto = new BranchDto() + .setBranchType(BRANCH) + .setKey("branchName") + .setUuid(BRANCH_UUID) + .setProjectUuid("project_uuid") + .setNeedIssueSync(true); + dbClient.branchDao().insert(dbTester.getSession(), branchDto); + dbTester.commit(); + + underTest.execute(() -> null); + + Optional branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID); + assertThat(branch.get().isNeedIssueSync()).isFalse(); + assertThat(branch.get().isExcludeFromPurge()).isFalse(); + } + + @Test + public void execute_on_already_indexed_branch() { + BranchDto branchDto = new BranchDto() + .setBranchType(BRANCH) + .setKey("branchName") + .setUuid(BRANCH_UUID) + .setProjectUuid("project_uuid") + .setNeedIssueSync(false); + dbClient.branchDao().insert(dbTester.getSession(), branchDto); + dbTester.commit(); + + underTest.execute(() -> null); + + Optional branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID); + assertThat(branch.get().isNeedIssueSync()).isFalse(); + assertThat(branch.get().isExcludeFromPurge()).isFalse(); + } + + @Test + public void fail_if_missing_main_component_in_task() { + CeTask ceTask = new CeTask.Builder() + .setOrganizationUuid("organizationUuid") + .setType("type") + .setUuid("uuid") + .setComponent(null) + .setMainComponent(null) + .build(); + IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient); + + assertThatThrownBy(() -> underTest.execute(() -> null)) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("main component not found in task"); + } + + @Test + public void verify_step_description() { + assertThat(underTest.getDescription()).isEqualTo("Ignore orphan component"); + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessorTest.java index cbc4bfb530e..3dd49083c26 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessorTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/IssueSyncTaskProcessorTest.java @@ -53,7 +53,7 @@ public class IssueSyncTaskProcessorTest { .build(); IssueSyncTaskProcessor.newContainerPopulator(task).populateContainer(container); - Mockito.verify(container, Mockito.times(4)).add(any()); + Mockito.verify(container, Mockito.times(5)).add(any()); } @Test @@ -62,7 +62,7 @@ public class IssueSyncTaskProcessorTest { List> steps = syncComputationSteps.orderedStepClasses(); - Assertions.assertThat(steps).containsExactly(IndexIssuesStep.class); + Assertions.assertThat(steps).containsExactly(IgnoreOrphanBranchStep.class, IndexIssuesStep.class); } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml index 92003dda714..8f39246a624 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml @@ -180,7 +180,7 @@ from ce_queue cq - inner join components c on c.uuid = cq.main_component_uuid and c.qualifier <> 'VW' + left join components c on c.uuid = cq.main_component_uuid and c.qualifier <> 'VW' where cq.status='PENDING' diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java index 21b8b38be47..3af142c4223 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java @@ -659,6 +659,20 @@ public class CeQueueDaoTest { assertThat(peek2.get().getUuid()).isEqualTo(TASK_UUID_2); } + @Test + public void excluding_view_pick_up_orphan_branches() { + insertPending(newCeQueueDto(TASK_UUID_1) + .setComponentUuid(MAIN_COMPONENT_UUID_1) + .setMainComponentUuid("non-existing-uuid") + .setStatus(PENDING) + .setTaskType(CeTaskTypes.BRANCH_ISSUE_SYNC) + .setCreatedAt(100_000L)); + + Optional peek = underTest.peek(db.getSession(), WORKER_UUID_1, false, true); + assertThat(peek).isPresent(); + assertThat(peek.get().getUuid()).isEqualTo(TASK_UUID_1); + } + @Test public void hasAnyIssueSyncTaskPendingOrInProgress_PENDING() { assertThat(underTest.hasAnyIssueSyncTaskPendingOrInProgress(db.getSession())).isFalse(); -- 2.39.5