From c17b2c4f505be118f4b2680f158af03dda302ec4 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour <58034472+BBesrour@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:38:35 +0200 Subject: [PATCH] Integrated code lifecycle: Improve clean up of temp folders in build agents (#9542) --- .../service/BuildJobExecutionService.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java index 9c968c453e47..75bbaf826b00 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java @@ -9,8 +9,11 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -27,7 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import com.github.dockerjava.api.command.CreateContainerResponse; @@ -71,6 +77,8 @@ public class BuildJobExecutionService { @Value("${artemis.version-control.default-branch:main}") private String defaultBranch; + private static final Duration TEMP_DIR_RETENTION_PERIOD = Duration.ofMinutes(5); + public BuildJobExecutionService(BuildJobContainerService buildJobContainerService, BuildJobGitService buildJobGitService, BuildAgentDockerService buildAgentDockerService, BuildLogsMap buildLogsMap) { this.buildJobContainerService = buildJobContainerService; @@ -79,6 +87,38 @@ public BuildJobExecutionService(BuildJobContainerService buildJobContainerServic this.buildLogsMap = buildLogsMap; } + /** + * This method is responsible for cleaning up temporary directories that were used for checking out repositories. + * It is triggered when the application is ready and runs asynchronously. + */ + @EventListener(ApplicationReadyEvent.class) + @Async + public void initAsync() { + final ZonedDateTime currentTime = ZonedDateTime.now(); + cleanUpTempDirectoriesAsync(currentTime); + } + + private void cleanUpTempDirectoriesAsync(ZonedDateTime currentTime) { + log.info("Cleaning up temporary directories in {}", CHECKED_OUT_REPOS_TEMP_DIR); + try (DirectoryStream directoryStream = Files.newDirectoryStream(Path.of(CHECKED_OUT_REPOS_TEMP_DIR))) { + for (Path path : directoryStream) { + try { + ZonedDateTime lastModifiedTime = ZonedDateTime.ofInstant(Files.getLastModifiedTime(path).toInstant(), currentTime.getZone()); + if (Files.isDirectory(path) && lastModifiedTime.isBefore(currentTime.minus(TEMP_DIR_RETENTION_PERIOD))) { + FileUtils.deleteDirectory(path.toFile()); + } + } + catch (IOException e) { + log.error("Could not delete temporary directory {}", path, e); + } + } + } + catch (IOException e) { + log.error("Could not delete temporary directories", e); + } + log.info("Clean up of temporary directories in {} completed.", CHECKED_OUT_REPOS_TEMP_DIR); + } + /** * Orchestrates the execution of a build job in a Docker container. This method handles the preparation and configuration of the container, * including cloning the necessary repositories, checking out the appropriate branches, and preparing the environment for the build. @@ -512,15 +552,16 @@ private void deleteCloneRepo(VcsRepositoryUri repositoryUri, @Nullable String co } buildJobGitService.deleteLocalRepository(repository); } + // Do not throw an exception if deletion fails. If an exception occurs, clean up will happen in the next server start. catch (EntityNotFoundException e) { msg = "Error while checking out repository"; buildLogsMap.appendBuildLogEntry(buildJobId, msg); - throw new LocalCIException(msg, e); + log.error("Error while deleting repository with URI {} and Path {}", repositoryUri, repositoryPath, e); } catch (IOException e) { msg = "Error while deleting repository"; buildLogsMap.appendBuildLogEntry(buildJobId, msg); - throw new LocalCIException(msg, e); + log.error("Error while deleting repository with URI {} and Path {}", repositoryUri, repositoryPath, e); } }