Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrated code lifecycle: Improve build details view #10462

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a1caf48
server and client
BBesrour Mar 9, 2025
3fefebc
server and client
BBesrour Mar 9, 2025
562506a
remove recent builds server
BBesrour Mar 10, 2025
13aa324
refactor filter (no tests)
BBesrour Mar 10, 2025
eca10cf
refactor filter (tests)
BBesrour Mar 10, 2025
9001584
use filter and paging in build agent details
BBesrour Mar 10, 2025
632fca4
fix issue where agent address not sent in request
BBesrour Mar 10, 2025
1d88e6b
fix tests
BBesrour Mar 10, 2025
e3e1390
fix tests
BBesrour Mar 11, 2025
71720bd
fix tests
BBesrour Mar 11, 2025
3356dd0
fix tests
BBesrour Mar 11, 2025
32dee38
fix tests
BBesrour Mar 11, 2025
5582397
fix
BBesrour Mar 11, 2025
8178de7
fix
BBesrour Mar 11, 2025
9af70ba
fix running build jobs
BBesrour Mar 11, 2025
fb61fc3
fix tests
BBesrour Mar 11, 2025
e163f1f
fix translation
BBesrour Mar 11, 2025
e70a91c
Merge branch 'develop' into feature/integrated-code-lifecycle/improve…
BBesrour Mar 12, 2025
e8299cc
merge conflict and feedback
BBesrour Mar 12, 2025
8032834
merge conflict and feedback
BBesrour Mar 12, 2025
63f48d9
Merge branch 'develop' into feature/integrated-code-lifecycle/improve…
BBesrour Mar 13, 2025
cf208ba
Merge branch 'develop' into feature/integrated-code-lifecycle/improve…
BBesrour Mar 13, 2025
e62e02c
fix stats not auto update
BBesrour Mar 13, 2025
4cfa856
fix missing info
BBesrour Mar 14, 2025
05bce0a
fix missing info
BBesrour Mar 14, 2025
0e5656b
Merge branch 'develop' into feature/integrated-code-lifecycle/improve…
BBesrour Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.tum.cit.aet.artemis.buildagent.dto;

import java.io.Serial;
import java.io.Serializable;
import java.time.ZonedDateTime;

public record BuildAgentDetailsDTO(long averageBuildDuration, long successfulBuilds, long failedBuilds, long cancelledBuilds, long timedOutBuild, long totalBuilds,
ZonedDateTime lastBuildDate, ZonedDateTime startDate, String gitRevision) implements Serializable {

@Serial
private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record BuildAgentInformation(BuildAgentDTO buildAgent, int maxNumberOfConcurrentBuildJobs, int numberOfCurrentBuildJobs, List<BuildJobQueueItem> runningBuildJobs,
BuildAgentStatus status, List<BuildJobQueueItem> recentBuildJobs, String publicSshKey) implements Serializable {
BuildAgentStatus status, String publicSshKey, BuildAgentDetailsDTO buildAgentDetails) implements Serializable {

@Serial
private static final long serialVersionUID = 1L;
Expand All @@ -21,11 +21,10 @@ public record BuildAgentInformation(BuildAgentDTO buildAgent, int maxNumberOfCon
* Constructor used to update the list of recently finished build jobs
*
* @param agentInformation The agent information
* @param recentBuildJobs The list of recent build jobs
*/
public BuildAgentInformation(BuildAgentInformation agentInformation, List<BuildJobQueueItem> recentBuildJobs) {
public BuildAgentInformation(BuildAgentInformation agentInformation) {
this(agentInformation.buildAgent(), agentInformation.maxNumberOfConcurrentBuildJobs(), agentInformation.numberOfCurrentBuildJobs(), agentInformation.runningBuildJobs,
agentInformation.status(), recentBuildJobs, agentInformation.publicSshKey());
agentInformation.status(), agentInformation.publicSshKey(), agentInformation.buildAgentDetails());
}

public enum BuildAgentStatus {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package de.tum.cit.aet.artemis.buildagent.service;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_BUILDAGENT;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.GitProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import com.hazelcast.core.HazelcastInstance;

import de.tum.cit.aet.artemis.buildagent.BuildAgentConfiguration;
import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentDTO;
import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentDetailsDTO;
import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentInformation;
import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem;
import de.tum.cit.aet.artemis.programming.domain.build.BuildStatus;
import de.tum.cit.aet.artemis.programming.service.localci.DistributedDataAccessService;

@Profile(PROFILE_BUILDAGENT)
@Service
public class BuildAgentInformationService {

private static final Logger log = org.slf4j.LoggerFactory.getLogger(BuildAgentInformationService.class);

private final HazelcastInstance hazelcastInstance;

private final BuildAgentConfiguration buildAgentConfiguration;

private final BuildAgentSshKeyService buildAgentSSHKeyService;

private final GitProperties gitProperties;

private final DistributedDataAccessService distributedDataAccessService;

@Value("${artemis.continuous-integration.build-agent.short-name}")
private String buildAgentShortName;

@Value("${artemis.continuous-integration.build-agent.display-name:}")
private String buildAgentDisplayName;

public BuildAgentInformationService(HazelcastInstance hazelcastInstance, BuildAgentConfiguration buildAgentConfiguration, BuildAgentSshKeyService buildAgentSSHKeyService,
DistributedDataAccessService distributedDataAccessService, GitProperties gitProperties) {
this.hazelcastInstance = hazelcastInstance;
this.buildAgentConfiguration = buildAgentConfiguration;
this.buildAgentSSHKeyService = buildAgentSSHKeyService;
this.gitProperties = gitProperties;
this.distributedDataAccessService = distributedDataAccessService;
}

public void updateLocalBuildAgentInformation(boolean isPaused) {
updateLocalBuildAgentInformationWithRecentJob(null, isPaused);
}

/**
* Updates the local build agent information with the most recent build job.
*
* @param recentBuildJob the most recent build job
* @param isPaused whether the build agent is paused
*/
public void updateLocalBuildAgentInformationWithRecentJob(BuildJobQueueItem recentBuildJob, boolean isPaused) {
String memberAddress = hazelcastInstance.getCluster().getLocalMember().getAddress().toString();
try {
distributedDataAccessService.getDistributedBuildAgentInformation().lock(memberAddress);
// Add/update
BuildAgentInformation info = getUpdatedLocalBuildAgentInformation(recentBuildJob, isPaused);
try {
distributedDataAccessService.getDistributedBuildAgentInformation().put(info.buildAgent().memberAddress(), info);
}
catch (Exception e) {
log.error("Error while updating build agent information for agent {} with address {}", info.buildAgent().name(), info.buildAgent().memberAddress(), e);
}
}
catch (Exception e) {
log.error("Error while updating build agent information for agent with address {}", memberAddress, e);
}
finally {
distributedDataAccessService.getDistributedBuildAgentInformation().unlock(memberAddress);
}
}

private BuildAgentInformation getUpdatedLocalBuildAgentInformation(BuildJobQueueItem recentBuildJob, boolean isPaused) {
String memberAddress = hazelcastInstance.getCluster().getLocalMember().getAddress().toString();
List<BuildJobQueueItem> processingJobsOfMember = getProcessingJobsOfNode(memberAddress);
int numberOfCurrentBuildJobs = processingJobsOfMember.size();
int maxNumberOfConcurrentBuilds = buildAgentConfiguration.getBuildExecutor() != null ? buildAgentConfiguration.getBuildExecutor().getMaximumPoolSize()
: buildAgentConfiguration.getThreadPoolSize();
boolean hasJobs = numberOfCurrentBuildJobs > 0;
BuildAgentInformation.BuildAgentStatus status = isPaused ? BuildAgentInformation.BuildAgentStatus.PAUSED
: hasJobs ? BuildAgentInformation.BuildAgentStatus.ACTIVE : BuildAgentInformation.BuildAgentStatus.IDLE;
BuildAgentInformation agent = distributedDataAccessService.getDistributedBuildAgentInformation().get(memberAddress);

String publicSshKey = buildAgentSSHKeyService.getPublicKeyAsString();

BuildAgentDTO agentInfo = new BuildAgentDTO(buildAgentShortName, memberAddress, buildAgentDisplayName);

BuildAgentDetailsDTO agentDetails = getBuildAgentDetails(agent, recentBuildJob);

return new BuildAgentInformation(agentInfo, maxNumberOfConcurrentBuilds, numberOfCurrentBuildJobs, processingJobsOfMember, status, publicSshKey, agentDetails);
}

private BuildAgentDetailsDTO getBuildAgentDetails(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
var gitRevision = gitProperties.getShortCommitId();
var lastBuildDate = getLastBuildDate(agent, recentBuildJob);
var startDate = getStartDate(agent);
var currentBuildDuration = getCurrentBuildDuration(recentBuildJob);
var averageBuildDuration = getAverageBuildDuration(agent, currentBuildDuration);
var totalsBuilds = getTotalBuilds(agent, recentBuildJob);
var successfulBuilds = getSuccessfulBuilds(agent, recentBuildJob);
var failedBuilds = getFailedBuilds(agent, recentBuildJob);
var cancelledBuilds = getCancelledBuilds(agent, recentBuildJob);
var timedOutBuilds = getTimedOutBuilds(agent, recentBuildJob);

return new BuildAgentDetailsDTO(averageBuildDuration, successfulBuilds, failedBuilds, cancelledBuilds, timedOutBuilds, totalsBuilds, lastBuildDate, startDate, gitRevision);
}

private ZonedDateTime getLastBuildDate(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return recentBuildJob != null ? recentBuildJob.jobTimingInfo().buildStartDate()
: (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().lastBuildDate() : null);
}

private ZonedDateTime getStartDate(BuildAgentInformation agent) {
return agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().startDate() : ZonedDateTime.now();
}

private long getCurrentBuildDuration(BuildJobQueueItem recentBuildJob) {
return recentBuildJob != null ? Duration.between(recentBuildJob.jobTimingInfo().buildStartDate(), recentBuildJob.jobTimingInfo().buildCompletionDate()).toSeconds() : 0;
}

private long getAverageBuildDuration(BuildAgentInformation agent, long currentBuildDuration) {
if (agent == null || agent.buildAgentDetails() == null) {
return currentBuildDuration;
}
else if (currentBuildDuration == 0) {
return agent.buildAgentDetails().averageBuildDuration();
}
return (agent.buildAgentDetails().averageBuildDuration() * agent.buildAgentDetails().totalBuilds() + currentBuildDuration) / (agent.buildAgentDetails().totalBuilds() + 1);
}

private long getTotalBuilds(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().totalBuilds() : 0) + (recentBuildJob != null ? 1 : 0);
}

private long getSuccessfulBuilds(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().successfulBuilds() : 0)
+ (recentBuildJob != null && recentBuildJob.status() == BuildStatus.SUCCESSFUL ? 1 : 0);
}

private long getFailedBuilds(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().failedBuilds() : 0)
+ (recentBuildJob != null && recentBuildJob.status() == BuildStatus.FAILED ? 1 : 0);
}

private long getCancelledBuilds(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().cancelledBuilds() : 0)
+ (recentBuildJob != null && recentBuildJob.status() == BuildStatus.CANCELLED ? 1 : 0);
}

private long getTimedOutBuilds(BuildAgentInformation agent, BuildJobQueueItem recentBuildJob) {
return (agent != null && agent.buildAgentDetails() != null ? agent.buildAgentDetails().timedOutBuild() : 0)
+ (recentBuildJob != null && recentBuildJob.status() == BuildStatus.TIMEOUT ? 1 : 0);
}

private List<BuildJobQueueItem> getProcessingJobsOfNode(String memberAddress) {
return distributedDataAccessService.getProcessingJobs().stream().filter(job -> Objects.equals(job.buildAgent().memberAddress(), memberAddress)).toList();
}
}
Loading
Loading