Skip to content

Commit 2e8919b

Browse files
authored
Merge branch 'develop' into chore/development/update-core-files-to-new-angular-api
2 parents 2925c0d + 5f2e030 commit 2e8919b

File tree

72 files changed

+1900
-526
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1900
-526
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ Prerequisites:
111111
- [ ] Test 2
112112

113113
### Test Coverage
114-
<!-- Please add the test coverages for all changed files here. You can see this when executing the tests locally (see build.gradle and package.json) or when looking into the corresponding Bamboo build plan. -->
115-
<!-- The line coverage must be above 90% for changes files and you must use extensive and useful assertions for server tests and expect statements for client tests. -->
116-
<!-- Note: Use the table below and confirm in the last column that you have implemented extensive assertions for server tests and expect statements for client tests. -->
117-
<!-- You can use `supporting_script/generate_code_cov_table/generate_code_cov_table.py` to automatically generate one from the corresponding Bamboo build plan artefacts. -->
114+
<!-- Please add the test coverages for all changed files modified in this PR here. You can use `supporting_script/generate_code_cov_table/generate_code_cov_table.py` to automatically generate the coverage table from the corresponding artefacts of your branch (follow the ReadMe for setup details). -->
115+
<!-- Alternatively you can execute the tests locally (see build.gradle and package.json) or look into the corresponding artefacts. -->
116+
<!-- The line coverage must be above 90% for changes files, and you must use extensive and useful assertions for server tests and expect statements for client tests. -->
117+
<!-- Note: Confirm in the last column that you have implemented extensive assertions for server tests and expect statements for client tests. -->
118118
<!-- Remove rows with only trivial changes from the table. -->
119119
<!--
120120
| Class/File | Line Coverage | Confirmation (assert/expect) |

src/main/java/de/tum/cit/aet/artemis/buildagent/dto/BuildJobQueueItem.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,9 @@ public BuildJobQueueItem(BuildJobQueueItem queueItem, ResultDTO submissionResult
4848
this(queueItem.id(), queueItem.name(), queueItem.buildAgent(), queueItem.participationId(), queueItem.courseId(), queueItem.exerciseId(), queueItem.retryCount(),
4949
queueItem.priority(), queueItem.status(), queueItem.repositoryInfo(), queueItem.jobTimingInfo(), queueItem.buildConfig(), submissionResult);
5050
}
51+
52+
public BuildJobQueueItem(BuildJobQueueItem queueItem, BuildAgentDTO buildAgent, int newRetryCount) {
53+
this(queueItem.id(), queueItem.name(), buildAgent, queueItem.participationId(), queueItem.courseId(), queueItem.exerciseId(), newRetryCount, queueItem.priority(), null,
54+
queueItem.repositoryInfo(), new JobTimingInfo(queueItem.jobTimingInfo.submissionDate(), ZonedDateTime.now(), null), queueItem.buildConfig(), null);
55+
}
5156
}

src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobExecutionService.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -378,18 +378,18 @@ private BuildResult parseTestResults(TarArchiveInputStream testResultsTarInputSt
378378
}
379379

380380
// Read the contents of the tar entry as a string.
381-
String xmlString = readTarEntryContent(testResultsTarInputStream);
381+
String fileString = readTarEntryContent(testResultsTarInputStream);
382382
// Get the file name of the tar entry.
383383
String fileName = getFileName(tarEntry);
384384

385385
try {
386386
// Check if the file is a static code analysis report file
387387
if (StaticCodeAnalysisTool.getToolByFilePattern(fileName).isPresent()) {
388-
processStaticCodeAnalysisReportFile(fileName, xmlString, staticCodeAnalysisReports, buildJobId);
388+
processStaticCodeAnalysisReportFile(fileName, fileString, staticCodeAnalysisReports, buildJobId);
389389
}
390390
else {
391391
// ugly workaround because in swift result files \n\t breaks the parsing
392-
var testResultFileString = xmlString.replace("\n\t", "");
392+
var testResultFileString = fileString.replace("\n\t", "");
393393
if (!testResultFileString.isBlank()) {
394394
processTestResultFile(testResultFileString, failedTests, successfulTests);
395395
}
@@ -418,7 +418,7 @@ private boolean isValidTestResultFile(TarArchiveEntry tarArchiveEntry) {
418418
String result = (lastIndexOfSlash != -1 && lastIndexOfSlash + 1 < name.length()) ? name.substring(lastIndexOfSlash + 1) : name;
419419

420420
// Java test result files are named "TEST-*.xml", Python test result files are named "*results.xml".
421-
return !tarArchiveEntry.isDirectory() && result.endsWith(".xml") && !result.equals("pom.xml");
421+
return !tarArchiveEntry.isDirectory() && (result.endsWith(".xml") && !result.equals("pom.xml") || result.endsWith(".sarif"));
422422
}
423423

424424
/**
@@ -444,12 +444,12 @@ private String getFileName(TarArchiveEntry tarEntry) {
444444
* Processes a static code analysis report file and adds the report to the corresponding list.
445445
*
446446
* @param fileName the file name of the static code analysis report file
447-
* @param xmlString the content of the static code analysis report file
447+
* @param reportContent the content of the static code analysis report file
448448
* @param staticCodeAnalysisReports the list of static code analysis reports
449449
*/
450-
private void processStaticCodeAnalysisReportFile(String fileName, String xmlString, List<StaticCodeAnalysisReportDTO> staticCodeAnalysisReports, String buildJobId) {
450+
private void processStaticCodeAnalysisReportFile(String fileName, String reportContent, List<StaticCodeAnalysisReportDTO> staticCodeAnalysisReports, String buildJobId) {
451451
try {
452-
staticCodeAnalysisReports.add(ReportParser.getReport(xmlString, fileName));
452+
staticCodeAnalysisReports.add(ReportParser.getReport(reportContent, fileName));
453453
}
454454
catch (UnsupportedToolException e) {
455455
String msg = "Failed to parse static code analysis report for " + fileName;

src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,26 @@ private void checkAvailabilityAndProcessNextBuild() {
266266
processBuild(buildJob);
267267
}
268268
catch (RejectedExecutionException e) {
269-
log.error("Couldn't add build job to threadpool: {}\n Concurrent Build Jobs Count: {} Active tasks in pool: {}, Concurrent Build Jobs Size: {}", buildJob,
269+
// TODO: we should log this centrally and not on the local node
270+
log.error("Couldn't add build job to thread pool: {}\n Concurrent Build Jobs Count: {} Active tasks in pool: {}, Concurrent Build Jobs Size: {}", buildJob,
270271
localProcessingJobs.get(), localCIBuildExecutorService.getActiveCount(), localCIBuildExecutorService.getMaximumPoolSize(), e);
271272

272273
// Add the build job back to the queue
273274
if (buildJob != null) {
274275
processingJobs.remove(buildJob.id());
275276

276-
buildJob = new BuildJobQueueItem(buildJob, new BuildAgentDTO("", "", ""));
277-
log.info("Adding build job back to the queue: {}", buildJob);
278-
queue.add(buildJob);
277+
// At most try out the build job 5 times when they get rejected
278+
if (buildJob.retryCount() >= 5) {
279+
// TODO: we should log this centrally and not on the local node
280+
log.error("Build job was rejected 5 times. Not adding build job back to the queue: {}", buildJob);
281+
}
282+
else {
283+
// NOTE: we increase the retry count here, because the build job was not processed successfully
284+
// TODO: we should try to run this job on a different build agent to avoid getting the same error again
285+
buildJob = new BuildJobQueueItem(buildJob, new BuildAgentDTO("", "", ""), buildJob.retryCount() + 1);
286+
log.info("Adding build job {} back to the queue with retry count {}", buildJob, buildJob.retryCount());
287+
queue.add(buildJob);
288+
}
279289
localProcessingJobs.decrementAndGet();
280290
}
281291

@@ -360,7 +370,9 @@ private BuildAgentInformation getUpdatedLocalBuildAgentInformation(BuildJobQueue
360370
}
361371

362372
private List<BuildJobQueueItem> getProcessingJobsOfNode(String memberAddress) {
363-
return processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgent().memberAddress(), memberAddress)).toList();
373+
// NOTE: we should not use streams with IMap, because it can be unstable, when many items are added at the same time and there is a slow network condition
374+
List<BuildJobQueueItem> processingJobsList = new ArrayList<>(processingJobs.values());
375+
return processingJobsList.stream().filter(job -> Objects.equals(job.buildAgent().memberAddress(), memberAddress)).toList();
364376
}
365377

366378
private void removeOfflineNodes() {
@@ -549,7 +561,8 @@ private void resumeBuildAgent() {
549561
private boolean nodeIsAvailable() {
550562
log.debug("Currently processing jobs on this node: {}, active threads in Pool: {}, maximum pool size of thread executor : {}", localProcessingJobs.get(),
551563
localCIBuildExecutorService.getActiveCount(), localCIBuildExecutorService.getMaximumPoolSize());
552-
return localProcessingJobs.get() < localCIBuildExecutorService.getMaximumPoolSize();
564+
return localProcessingJobs.get() < localCIBuildExecutorService.getMaximumPoolSize()
565+
&& localCIBuildExecutorService.getActiveCount() < localCIBuildExecutorService.getMaximumPoolSize() && localCIBuildExecutorService.getQueue().isEmpty();
553566
}
554567

555568
public class QueuedBuildJobItemListener implements ItemListener<BuildJobQueueItem> {

src/main/java/de/tum/cit/aet/artemis/core/config/MetricsBean.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,21 +290,21 @@ private void registerLocalCIMetrics() {
290290
}
291291

292292
private static int extractRunningBuilds(Optional<SharedQueueManagementService> sharedQueueManagementService) {
293-
return sharedQueueManagementService.map(queueManagementService -> queueManagementService.getBuildAgentInformation().stream()
294-
.map(buildAgentInformation -> buildAgentInformation.runningBuildJobs().size()).reduce(0, Integer::sum)).orElse(0);
293+
return sharedQueueManagementService.map(SharedQueueManagementService::getProcessingJobsSize).orElse(0);
295294
}
296295

297296
private static int extractQueuedBuilds(Optional<SharedQueueManagementService> sharedQueueManagementService) {
298-
return sharedQueueManagementService.map(queueManagementService -> queueManagementService.getQueuedJobs().size()).orElse(0);
297+
return sharedQueueManagementService.map(SharedQueueManagementService::getQueuedJobsSize).orElse(0);
299298
}
300299

301300
private static int extractBuildAgents(Optional<SharedQueueManagementService> sharedQueueManagementService) {
302-
return sharedQueueManagementService.map(queueManagementService -> queueManagementService.getBuildAgentInformation().size()).orElse(0);
301+
return sharedQueueManagementService.map(SharedQueueManagementService::getBuildAgentInformationSize).orElse(0);
303302
}
304303

305304
private static int extractMaxConcurrentBuilds(Optional<SharedQueueManagementService> sharedQueueManagementService) {
306305
return sharedQueueManagementService.map(queueManagementService -> queueManagementService.getBuildAgentInformation().stream()
307-
.map(BuildAgentInformation::maxNumberOfConcurrentBuildJobs).reduce(0, Integer::sum)).orElse(0);
306+
.filter(agent -> agent.status() != BuildAgentInformation.BuildAgentStatus.PAUSED).map(BuildAgentInformation::maxNumberOfConcurrentBuildJobs)
307+
.reduce(0, Integer::sum)).orElse(0);
308308
}
309309

310310
private void registerWebsocketMetrics() {

src/main/java/de/tum/cit/aet/artemis/core/web/admin/AdminBuildJobQueueResource.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public ResponseEntity<BuildJobsStatisticsDTO> getBuildJobStatistics(@RequestPara
195195
}
196196

197197
/**
198-
* {@code PUT /api/admin/agent/{agentName}/pause} : Pause the specified build agent.
198+
* {@code PUT /api/admin/agents/{agentName}/pause} : Pause the specified build agent.
199199
* This endpoint allows administrators to pause a specific build agent by its name.
200200
* Pausing a build agent will prevent it from picking up any new build jobs until it is resumed.
201201
*
@@ -207,15 +207,34 @@ public ResponseEntity<BuildJobsStatisticsDTO> getBuildJobStatistics(@RequestPara
207207
* @return {@link ResponseEntity} with status code 204 (No Content) if the agent was successfully paused
208208
* or an appropriate error response if something went wrong
209209
*/
210-
@PutMapping("agent/{agentName}/pause")
210+
@PutMapping("agents/{agentName}/pause")
211211
public ResponseEntity<Void> pauseBuildAgent(@PathVariable String agentName) {
212212
log.debug("REST request to pause agent {}", agentName);
213213
localCIBuildJobQueueService.pauseBuildAgent(agentName);
214214
return ResponseEntity.noContent().build();
215215
}
216216

217217
/**
218-
* {@code PUT /api/admin/agent/{agentName}/resume} : Resume the specified build agent.
218+
* {@code PUT /api/admin/agents/pause-all} : Pause all build agents.
219+
* This endpoint allows administrators to pause all build agents.
220+
* Pausing all build agents will prevent them from picking up any new build jobs until they are resumed.
221+
*
222+
* <p>
223+
* <strong>Authorization:</strong> This operation requires admin privileges, enforced by {@code @EnforceAdmin}.
224+
* </p>
225+
*
226+
* @return {@link ResponseEntity} with status code 204 (No Content) if all agents were successfully paused
227+
* or an appropriate error response if something went wrong
228+
*/
229+
@PutMapping("agents/pause-all")
230+
public ResponseEntity<Void> pauseAllBuildAgents() {
231+
log.debug("REST request to pause all agents");
232+
localCIBuildJobQueueService.pauseAllBuildAgents();
233+
return ResponseEntity.noContent().build();
234+
}
235+
236+
/**
237+
* {@code PUT /api/admin/agents/{agentName}/resume} : Resume the specified build agent.
219238
* This endpoint allows administrators to resume a specific build agent by its name.
220239
* Resuming a build agent will allow it to pick up new build jobs again.
221240
*
@@ -227,10 +246,29 @@ public ResponseEntity<Void> pauseBuildAgent(@PathVariable String agentName) {
227246
* @return {@link ResponseEntity} with status code 204 (No Content) if the agent was successfully resumed
228247
* or an appropriate error response if something went wrong
229248
*/
230-
@PutMapping("agent/{agentName}/resume")
249+
@PutMapping("agents/{agentName}/resume")
231250
public ResponseEntity<Void> resumeBuildAgent(@PathVariable String agentName) {
232251
log.debug("REST request to resume agent {}", agentName);
233252
localCIBuildJobQueueService.resumeBuildAgent(agentName);
234253
return ResponseEntity.noContent().build();
235254
}
255+
256+
/**
257+
* {@code PUT /api/admin/agents/resume-all} : Resume all build agents.
258+
* This endpoint allows administrators to resume all build agents.
259+
* Resuming all build agents will allow them to pick up new build jobs again.
260+
*
261+
* <p>
262+
* <strong>Authorization:</strong> This operation requires admin privileges, enforced by {@code @EnforceAdmin}.
263+
* </p>
264+
*
265+
* @return {@link ResponseEntity} with status code 204 (No Content) if all agents were successfully resumed
266+
* or an appropriate error response if something went wrong
267+
*/
268+
@PutMapping("agents/resume-all")
269+
public ResponseEntity<Void> resumeAllBuildAgents() {
270+
log.debug("REST request to resume all agents");
271+
localCIBuildJobQueueService.resumeAllBuildAgents();
272+
return ResponseEntity.noContent().build();
273+
}
236274
}

src/main/java/de/tum/cit/aet/artemis/programming/domain/StaticCodeAnalysisTool.java

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package de.tum.cit.aet.artemis.programming.domain;
22

3-
import java.util.ArrayList;
3+
import java.util.EnumMap;
44
import java.util.List;
5+
import java.util.Map;
56
import java.util.Objects;
67
import java.util.Optional;
78

@@ -10,28 +11,29 @@
1011
*/
1112
public enum StaticCodeAnalysisTool {
1213

13-
SPOTBUGS(ProgrammingLanguage.JAVA, "spotbugs:spotbugs", "spotbugsXml.xml"), CHECKSTYLE(ProgrammingLanguage.JAVA, "checkstyle:checkstyle", "checkstyle-result.xml"),
14-
PMD(ProgrammingLanguage.JAVA, "pmd:pmd", "pmd.xml"), PMD_CPD(ProgrammingLanguage.JAVA, "pmd:cpd", "cpd.xml"), SWIFTLINT(ProgrammingLanguage.SWIFT, "", "swiftlint-result.xml"),
15-
GCC(ProgrammingLanguage.C, "", "gcc.xml");
14+
// @formatter:off
15+
SPOTBUGS("spotbugsXml.xml"),
16+
CHECKSTYLE("checkstyle-result.xml"),
17+
PMD("pmd.xml"),
18+
PMD_CPD("cpd.xml"),
19+
SWIFTLINT("swiftlint-result.xml"),
20+
GCC("gcc.xml"),
21+
OTHER(null),
22+
;
23+
// @formatter:on
1624

17-
private final ProgrammingLanguage language;
25+
// @formatter:off
26+
private static final Map<ProgrammingLanguage, List<StaticCodeAnalysisTool>> TOOLS_OF_PROGRAMMING_LANGUAGE = new EnumMap<>(Map.of(
27+
ProgrammingLanguage.JAVA, List.of(SPOTBUGS, CHECKSTYLE, PMD, PMD_CPD),
28+
ProgrammingLanguage.SWIFT, List.of(SWIFTLINT),
29+
ProgrammingLanguage.C, List.of(GCC)
30+
));
31+
// @formatter:on
1832

19-
private final String command;
33+
private final String fileName;
2034

21-
private final String filePattern;
22-
23-
StaticCodeAnalysisTool(ProgrammingLanguage language, String command, String filePattern) {
24-
this.language = language;
25-
this.command = command;
26-
this.filePattern = filePattern;
27-
}
28-
29-
public String getTask() {
30-
return this.command;
31-
}
32-
33-
public String getFilePattern() {
34-
return this.filePattern;
35+
StaticCodeAnalysisTool(String fileName) {
36+
this.fileName = fileName;
3537
}
3638

3739
/**
@@ -41,13 +43,7 @@ public String getFilePattern() {
4143
* @return List of static code analysis
4244
*/
4345
public static List<StaticCodeAnalysisTool> getToolsForProgrammingLanguage(ProgrammingLanguage language) {
44-
List<StaticCodeAnalysisTool> tools = new ArrayList<>();
45-
for (var tool : StaticCodeAnalysisTool.values()) {
46-
if (tool.language == language) {
47-
tools.add(tool);
48-
}
49-
}
50-
return tools;
46+
return TOOLS_OF_PROGRAMMING_LANGUAGE.getOrDefault(language, List.of());
5147
}
5248

5349
/**
@@ -58,7 +54,7 @@ public static List<StaticCodeAnalysisTool> getToolsForProgrammingLanguage(Progra
5854
*/
5955
public static Optional<StaticCodeAnalysisTool> getToolByFilePattern(String fileName) {
6056
for (StaticCodeAnalysisTool tool : StaticCodeAnalysisTool.values()) {
61-
if (Objects.equals(fileName, tool.filePattern)) {
57+
if (Objects.equals(fileName, tool.fileName)) {
6258
return Optional.of(tool);
6359
}
6460
}

0 commit comments

Comments
 (0)