diff options
author | belen-pruvost-sonarsource <belen.pruvost@sonarsource.com> | 2021-07-22 21:01:19 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-07-27 20:03:03 +0000 |
commit | b7dec8c5218ba79517376612e47ab0c63aed466f (patch) | |
tree | e1992e4358c5a4bf285d8a65681d3b106a5a8c82 /server | |
parent | 6af6a9cdebbef03fe6ac5b8205eae9665a241626 (diff) | |
download | sonarqube-b7dec8c5218ba79517376612e47ab0c63aed466f.tar.gz sonarqube-b7dec8c5218ba79517376612e47ab0c63aed466f.zip |
SONAR-15144 - Audit Log Clean Up Job
Diffstat (limited to 'server')
15 files changed, 597 insertions, 11 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelper.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelper.java new file mode 100644 index 00000000000..b35b84a29a8 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelper.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Optional; +import org.sonar.api.utils.System2; +import org.sonar.core.config.Frequency; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.property.PropertyDto; + +import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY; +import static org.sonar.core.config.PurgeProperties.DEFAULT_FREQUENCY; + +public class AuditHousekeepingFrequencyHelper { + private final System2 system2; + + public AuditHousekeepingFrequencyHelper(System2 system2) { + this.system2 = system2; + } + + public PropertyDto getHouseKeepingFrequency(DbClient dbClient, DbSession dbSession) { + return Optional.ofNullable(dbClient.propertiesDao() + .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY)) + .orElse(defaultAuditHouseKeepingProperty()); + } + + public long getThresholdDate(String frequency) { + Optional<Frequency> housekeepingFrequency = Arrays.stream(Frequency.values()) + .filter(f -> f.name().equalsIgnoreCase(frequency)).findFirst(); + if (housekeepingFrequency.isEmpty()) { + throw new IllegalArgumentException("Unsupported frequency: " + frequency); + } + + return Instant.ofEpochMilli(system2.now()) + .minus(housekeepingFrequency.get().getDays(), ChronoUnit.DAYS) + .toEpochMilli(); + } + + private static PropertyDto defaultAuditHouseKeepingProperty() { + PropertyDto property = new PropertyDto(); + property.setKey(AUDIT_HOUSEKEEPING_FREQUENCY); + property.setValue(DEFAULT_FREQUENCY); + return property; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStep.java new file mode 100644 index 00000000000..af44ef8937f --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStep.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.Set; +import java.util.stream.Collectors; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.audit.AuditDto; +import org.sonar.db.property.PropertyDto; + +public final class AuditPurgeStep implements ComputationStep { + private static final Logger LOG = Loggers.get(AuditPurgeStep.class); + + private final AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper; + private final DbClient dbClient; + + public AuditPurgeStep(AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper, DbClient dbClient) { + this.auditHousekeepingFrequencyHelper = auditHousekeepingFrequencyHelper; + this.dbClient = dbClient; + } + + @Override + public void execute(Context context) { + try (DbSession dbSession = dbClient.openSession(false)) { + PropertyDto property = auditHousekeepingFrequencyHelper.getHouseKeepingFrequency(dbClient, dbSession); + long deleteBefore = auditHousekeepingFrequencyHelper.getThresholdDate(property.getValue()); + Set<String> auditUuids = dbClient.auditDao() + .selectOlderThan(dbSession, deleteBefore) + .stream() + .map(AuditDto::getUuid) + .collect(Collectors.toSet()); + LOG.info(String.format("%s audit logs to be deleted...", auditUuids.size())); + dbClient.auditDao().deleteByUuids(dbSession, auditUuids); + dbSession.commit(); + } + } + + @Override + public String getDescription() { + return "Purge Audit Logs"; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModule.java new file mode 100644 index 00000000000..936f9ed9f2d --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModule.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 org.sonar.core.platform.Module; + +public class AuditPurgeTaskModule extends Module { + + @Override + protected void configureModule() { + add(AuditPurgeTaskProcessor.class); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessor.java new file mode 100644 index 00000000000..14c1b034b94 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessor.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.Arrays; +import java.util.List; +import java.util.Set; +import javax.annotation.CheckForNull; +import org.sonar.ce.task.CeTask; +import org.sonar.ce.task.CeTaskResult; +import org.sonar.ce.task.container.TaskContainer; +import org.sonar.ce.task.container.TaskContainerImpl; +import org.sonar.ce.task.projectanalysis.step.AbstractComputationSteps; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.ce.task.step.ComputationStepExecutor; +import org.sonar.ce.task.taskprocessor.CeTaskProcessor; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.platform.ContainerPopulator; + +import static org.sonar.db.ce.CeTaskTypes.AUDIT_PURGE; + +public class AuditPurgeTaskProcessor implements CeTaskProcessor { + private static final Set<String> HANDLED_TYPES = Set.of(AUDIT_PURGE); + + private final ComponentContainer ceEngineContainer; + + public AuditPurgeTaskProcessor(ComponentContainer ceEngineContainer) { + this.ceEngineContainer = ceEngineContainer; + } + + @Override + public Set<String> getHandledCeTaskTypes() { + return HANDLED_TYPES; + } + + @CheckForNull + @Override + public CeTaskResult process(CeTask task) { + try (TaskContainer container = new TaskContainerImpl(ceEngineContainer, newContainerPopulator(task))) { + container.bootup(); + container.getComponentByType(ComputationStepExecutor.class).execute(); + } + return null; + } + + static ContainerPopulator<TaskContainer> newContainerPopulator(CeTask task) { + return taskContainer -> { + taskContainer.add(task); + taskContainer.add(AuditHousekeepingFrequencyHelper.class); + taskContainer.add(AuditPurgeStep.class); + taskContainer.add(new AuditPurgeComputationSteps(taskContainer)); + taskContainer.add(ComputationStepExecutor.class); + }; + } + + public static final class AuditPurgeComputationSteps extends AbstractComputationSteps { + + public AuditPurgeComputationSteps(ContainerPopulator.Container container) { + super(container); + } + + @Override + public List<Class<? extends ComputationStep>> orderedStepClasses() { + return Arrays.asList(AuditPurgeStep.class); + } + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelperTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelperTest.java new file mode 100644 index 00000000000..ce5a3df70dc --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditHousekeepingFrequencyHelperTest.java @@ -0,0 +1,104 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.api.utils.System2; +import org.sonar.core.config.Frequency; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.property.PropertiesDao; +import org.sonar.db.property.PropertyDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY; +import static org.sonar.core.config.PurgeProperties.DEFAULT_FREQUENCY; + +@RunWith(DataProviderRunner.class) +public class AuditHousekeepingFrequencyHelperTest { + private static final long NOW = 10_000_000_000L; + + private final DbClient dbClient = mock(DbClient.class); + private final DbSession dbSession = mock(DbSession.class); + private final PropertiesDao propertiesDao = mock(PropertiesDao.class); + private final System2 system2 = new TestSystem2().setNow(NOW); + private final AuditHousekeepingFrequencyHelper underTest = new AuditHousekeepingFrequencyHelper(system2); + + @Test + @UseDataProvider("frequencyOptions") + public void getThresholdDate(Frequency frequency) { + long result = underTest.getThresholdDate(frequency.getDescription()); + + + long expected = Instant.ofEpochMilli(system2.now()) + .minus(frequency.getDays(), ChronoUnit.DAYS) + .toEpochMilli(); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void getThresholdDateForUnknownFrequencyFails() { + assertThatThrownBy(() -> underTest.getThresholdDate("Lalala")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported frequency: Lalala"); + } + + @Test + public void getHouseKeepingFrequency() { + String value = "Weekly"; + PropertyDto propertyDto = new PropertyDto().setKey(AUDIT_HOUSEKEEPING_FREQUENCY).setValue(value); + when(dbClient.propertiesDao()).thenReturn(propertiesDao); + when(propertiesDao + .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY)) + .thenReturn(propertyDto); + assertThat(underTest.getHouseKeepingFrequency(dbClient, dbSession).getValue()).isEqualTo(value); + } + + @Test + public void getDefaultHouseKeepingFrequencyWhenNotSet() { + when(dbClient.propertiesDao()).thenReturn(propertiesDao); + when(propertiesDao + .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY)) + .thenReturn(null); + assertThat(underTest.getHouseKeepingFrequency(dbClient, dbSession).getValue()) + .isEqualTo(DEFAULT_FREQUENCY); + } + + @DataProvider + public static Object[][] frequencyOptions() { + return new Object[][] { + {Frequency.WEEKLY}, + {Frequency.MONTHLY}, + {Frequency.TRIMESTRIAL}, + {Frequency.YEARLY} + }; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStepTest.java new file mode 100644 index 00000000000..96d6e03d52f --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeStepTest.java @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.audit.AuditDto; +import org.sonar.db.audit.AuditTesting; +import org.sonar.db.property.PropertyDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.core.config.Frequency.MONTHLY; +import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY; + +public class AuditPurgeStepTest { + private final static long NOW = 1_400_000_000_000L; + private final static long BEFORE = 1_300_000_000_000L; + private final static long LATER = 1_500_000_000_000L; + private final static ZonedDateTime thresholdDate = Instant.ofEpochMilli(NOW) + .atZone(ZoneId.systemDefault()); + private final static PropertyDto FREQUENCY_PROPERTY = new PropertyDto() + .setKey(AUDIT_HOUSEKEEPING_FREQUENCY) + .setValue(MONTHLY.name()); + + @Rule + public final DbTester dbTester = DbTester.create(System2.INSTANCE); + + private final DbClient dbClient = dbTester.getDbClient(); + + private final System2 system2 = new System2(); + + @Rule + public final DbTester db = DbTester.create(system2); + + private final AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper = mock(AuditHousekeepingFrequencyHelper.class); + + private final AuditPurgeStep underTest = new AuditPurgeStep(auditHousekeepingFrequencyHelper, dbClient); + + @Before + public void setUp() { + when(auditHousekeepingFrequencyHelper.getHouseKeepingFrequency(any(), any())).thenReturn(FREQUENCY_PROPERTY); + when(auditHousekeepingFrequencyHelper.getThresholdDate(anyString())).thenReturn(NOW); + } + + @Test + public void executeDeletesOlderAudits() { + prepareRowsWithDeterministicCreatedAt(); + assertThat(dbClient.auditDao().selectOlderThan(db.getSession(), LATER + 1)).hasSize(3); + + underTest.execute(() -> null); + + assertThat(dbClient.auditDao().selectOlderThan(db.getSession(), LATER + 1)).hasSize(2); + } + + @Test + public void getDescription() { + assertThat(underTest.getDescription()).isEqualTo("Purge Audit Logs"); + } + + private void prepareRowsWithDeterministicCreatedAt() { + insertAudit(BEFORE); + insertAudit(NOW); + insertAudit(LATER); + db.getSession().commit(); + } + + private void insertAudit(long timestamp) { + AuditDto auditDto = AuditTesting.newAuditDto(timestamp); + dbClient.auditDao().insert(db.getSession(), auditDto); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModuleTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModuleTest.java new file mode 100644 index 00000000000..53857b49fa9 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskModuleTest.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER; + +public class AuditPurgeTaskModuleTest { + + @Test + public void verifyCountOfAddedComponents() { + ComponentContainer container = new ComponentContainer(); + new AuditPurgeTaskModule().configure(container); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 1); + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessorTest.java new file mode 100644 index 00000000000..9e68167b02b --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/taskprocessor/AuditPurgeTaskProcessorTest.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.List; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.ce.task.CeTask; +import org.sonar.ce.task.container.TaskContainer; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.core.platform.ComponentContainer; + +import static org.mockito.ArgumentMatchers.any; +import static org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskProcessor.AuditPurgeComputationSteps; +import static org.sonar.db.ce.CeTaskTypes.AUDIT_PURGE; + +public class AuditPurgeTaskProcessorTest { + + private ComponentContainer ceEngineContainer = Mockito.mock(ComponentContainer.class); + + private AuditPurgeTaskProcessor underTest = new AuditPurgeTaskProcessor(ceEngineContainer); + private TaskContainer container = Mockito.spy(TaskContainer.class); + + @Test + public void getHandledCeTaskTypes() { + Assertions.assertThat(underTest.getHandledCeTaskTypes()).containsExactly(AUDIT_PURGE); + } + + @Test(expected = NullPointerException.class) + public void processThrowsNPEIfCeTaskIsNull() { + underTest.process(null); + } + + @Test + public void newContainerPopulator() { + CeTask task = new CeTask.Builder() + .setUuid("TASK_UUID") + .setType("Type") + .build(); + + AuditPurgeTaskProcessor.newContainerPopulator(task).populateContainer(container); + Mockito.verify(container, Mockito.times(5)).add(any()); + } + + @Test + public void orderedStepClasses(){ + AuditPurgeComputationSteps auditPurgeComputationSteps = new AuditPurgeComputationSteps(null); + + List<Class<? extends ComputationStep>> steps = auditPurgeComputationSteps.orderedStepClasses(); + + Assertions.assertThat(steps).containsExactly(AuditPurgeStep.class); + } + +} diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index ddb8a16dd86..e8204854e61 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -63,6 +63,7 @@ import org.sonar.ce.task.projectanalysis.ProjectAnalysisTaskModule; import org.sonar.ce.task.projectanalysis.analysis.ProjectConfigurationFactory; import org.sonar.ce.task.projectanalysis.issue.AdHocRuleCreator; import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationModule; +import org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskModule; import org.sonar.ce.task.projectanalysis.taskprocessor.IssueSyncTaskModule; import org.sonar.ce.taskprocessor.CeProcessingScheduler; import org.sonar.ce.taskprocessor.CeTaskProcessorModule; @@ -433,6 +434,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { CeTaskCommonsModule.class, ProjectAnalysisTaskModule.class, IssueSyncTaskModule.class, + AuditPurgeTaskModule.class, CeTaskProcessorModule.class, OfficialDistribution.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java index 7c2b2d67979..75a8ed49b7b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java @@ -20,12 +20,14 @@ package org.sonar.db.audit; import java.util.List; +import java.util.Set; import org.sonar.api.utils.System2; import org.sonar.core.util.UuidFactory; import org.sonar.db.Dao; import org.sonar.db.DbSession; import org.sonar.db.Pagination; +import static org.sonar.db.DatabaseUtils.executeLargeUpdates; import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE; public class AuditDao implements Dao { @@ -63,7 +65,11 @@ public class AuditDao implements Dao { getMapper(dbSession).insert(auditDto); } - public void deleteIfBeforeSelectedDate(DbSession dbSession, long timestamp) { - getMapper(dbSession).deleteIfBeforeSelectedDate(timestamp); + public List<AuditDto> selectOlderThan(DbSession dbSession, long beforeTimestamp) { + return getMapper(dbSession).selectOlderThan(beforeTimestamp); + } + + public void deleteByUuids(DbSession dbSession, Set<String> uuids) { + executeLargeUpdates(uuids, getMapper(dbSession)::deleteByUuids); } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java index f5a45d34ba6..f252f36bf19 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java @@ -19,19 +19,20 @@ */ package org.sonar.db.audit; +import java.util.List; import org.apache.ibatis.annotations.Param; import org.sonar.db.Pagination; -import java.util.List; - public interface AuditMapper { void insert(@Param("dto") AuditDto auditDto); void delete(@Param("uuids") List<String> uuids); - void deleteIfBeforeSelectedDate(@Param("timestamp") long timestamp); - List<AuditDto> selectByPeriodPaginated(@Param("start")long start, @Param("end") long end, @Param("pagination") Pagination pagination); + List<AuditDto> selectOlderThan(@Param("beforeTimestamp") long beforeTimestamp); + + void deleteByUuids(@Param("uuids") List<String> uuids); + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java index fc1aadcc181..aae74db3d32 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java @@ -21,8 +21,9 @@ package org.sonar.db.ce; public final class CeTaskTypes { - public static final String REPORT = "REPORT"; + public static final String AUDIT_PURGE = "AUDIT_PURGE"; public static final String BRANCH_ISSUE_SYNC = "ISSUE_SYNC"; + public static final String REPORT = "REPORT"; private CeTaskTypes() { // only statics diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml index a2a6347c333..469560c01a5 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml @@ -47,10 +47,22 @@ ) </insert> - <delete id="deleteIfBeforeSelectedDate"> - delete from audits + <select id="selectOlderThan" parameterType="long" resultType="org.sonar.db.audit.AuditDto"> + select + <include refid="sqlColumns"/> + from audits a where - created_at <= #{timestamp,jdbcType=BIGINT} + a.created_at < #{beforeTimestamp,jdbcType=BIGINT} + </select> + + <delete id="deleteByUuids" parameterType="string"> + delete + from audits + where + uuid in + <foreach collection="uuids" open="(" close=")" item="uuid" separator=","> + #{uuid,jdbcType=VARCHAR} + </foreach> </delete> </mapper> diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/audit/AuditDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/audit/AuditDaoTest.java index b338b23132d..3634503a394 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/audit/AuditDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/audit/AuditDaoTest.java @@ -20,6 +20,8 @@ package org.sonar.db.audit; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import org.junit.Rule; import org.junit.Test; import org.sonar.api.impl.utils.TestSystem2; @@ -63,7 +65,12 @@ public class AuditDaoTest { public void deleteIfBeforeSelectedDate_deleteTwoRows() { prepareRowsWithDeterministicCreatedAt(3); - testAuditDao.deleteIfBeforeSelectedDate(dbSession, 2); + Set<String> auditUuids = testAuditDao.selectOlderThan(dbSession, 3) + .stream() + .map(AuditDto::getUuid) + .collect(Collectors.toSet()); + + testAuditDao.deleteByUuids(dbSession, auditUuids); List<AuditDto> auditDtos = testAuditDao.selectByPeriodPaginated(dbSession, 1, 4, 1); assertThat(auditDtos.size()).isEqualTo(1); diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 69c70726abc..318df07af2d 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -39,6 +39,7 @@ import org.sonar.auth.gitlab.GitLabModule; import org.sonar.auth.ldap.LdapModule; import org.sonar.auth.saml.SamlModule; import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationModule; +import org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskProcessor; import org.sonar.ce.task.projectanalysis.taskprocessor.IssueSyncTaskProcessor; import org.sonar.ce.task.projectanalysis.taskprocessor.ReportTaskProcessor; import org.sonar.core.component.DefaultResourceTypes; @@ -523,6 +524,7 @@ public class PlatformLevel4 extends PlatformLevel { CeWsModule.class, ReportTaskProcessor.class, IssueSyncTaskProcessor.class, + AuditPurgeTaskProcessor.class, // SonarSource editions PlatformEditionProvider.class, |