Skip to content

Commit

Permalink
Display local ci status in client after simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
valentin-boehm committed Jan 10, 2024
1 parent eae4bcc commit 8f4b3a8
Show file tree
Hide file tree
Showing 21 changed files with 156 additions and 26 deletions.
8 changes: 5 additions & 3 deletions src/main/java/de/tum/cit/ase/domain/LocalCIStatus.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.tum.cit.ase.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;

@Entity
Expand All @@ -23,10 +24,11 @@ public class LocalCIStatus {
private int timeInMinutes;

@Column(name = "avg_jobs_per_minute")
private int avgJobsPerMinute;
private double avgJobsPerMinute;

@OneToOne
@JoinColumn(name = "simulation_run_id", nullable = false)
@JsonIgnore
private SimulationRun simulationRun;

public Long getId() {
Expand Down Expand Up @@ -69,11 +71,11 @@ public void setTimeInMinutes(int timeInMinutes) {
this.timeInMinutes = timeInMinutes;
}

public int getAvgJobsPerMinute() {
public double getAvgJobsPerMinute() {
return avgJobsPerMinute;
}

public void setAvgJobsPerMinute(int avgJobsPerMinute) {
public void setAvgJobsPerMinute(double avgJobsPerMinute) {
this.avgJobsPerMinute = avgJobsPerMinute;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package de.tum.cit.ase.repository;

import de.tum.cit.ase.domain.LocalCIStatus;
import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface LocalCIStatusRepository extends JpaRepository<LocalCIStatus, Long> {}
public interface LocalCIStatusRepository extends JpaRepository<LocalCIStatus, Long> {
@Modifying
@Transactional
@Query(value = "delete from LocalCIStatus status where status.isFinished = false")
void deleteAllNotFinished();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import de.tum.cit.ase.domain.SimulationRun;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
Expand All @@ -16,5 +17,5 @@ public interface SimulationRunRepository extends JpaRepository<SimulationRun, Lo
List<SimulationRun> findAllBySimulationId(@Param("simulationId") long simulationId);

@Query(value = "select run from SimulationRun run left join fetch run.stats s left join fetch run.logMessages l where run.id = :#{#id}")
SimulationRun findByIdWithStatsAndLogMessages(long id);
Optional<SimulationRun> findByIdWithStatsAndLogMessages(long id);
}
16 changes: 15 additions & 1 deletion src/main/java/de/tum/cit/ase/service/LocalCIStatusService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
import de.tum.cit.ase.repository.LocalCIStatusRepository;
import de.tum.cit.ase.service.artemis.interaction.SimulatedArtemisAdmin;
import de.tum.cit.ase.web.websocket.SimulationWebsocketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class LocalCIStatusService {

private final Logger log = LoggerFactory.getLogger(LocalCIStatusService.class);
private final LocalCIStatusRepository localCIStatusRepository;
private final SimulationWebsocketService websocketService;

public LocalCIStatusService(LocalCIStatusRepository localCIStatusRepository, SimulationWebsocketService websocketService) {
this.localCIStatusRepository = localCIStatusRepository;
this.websocketService = websocketService;
cleanup();
}

public LocalCIStatus createLocalCIStatus(SimulationRun simulationRun) {
Expand All @@ -32,27 +36,37 @@ public LocalCIStatus createLocalCIStatus(SimulationRun simulationRun) {

@Async
public void subscribeToLocalCIStatus(SimulationRun simulationRun, SimulatedArtemisAdmin admin, long courseId) {
log.info("Subscribing to local CI status for simulation run {}", simulationRun.getId());
LocalCIStatus status = createLocalCIStatus(simulationRun);

int numberOfQueuedJobs = admin.getBuildQueue(courseId).size();
status.setTotalJobs(numberOfQueuedJobs);
status.setQueuedJobs(numberOfQueuedJobs);
status = localCIStatusRepository.save(status);
websocketService.sendRunLocalCIUpdate(simulationRun.getId(), status);

do {
try {
Thread.sleep(1000 * 60);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.debug("Updating local CI status for simulation run {}", simulationRun.getId());
numberOfQueuedJobs = admin.getBuildQueue(courseId).size();
status.setQueuedJobs(numberOfQueuedJobs);
status.setTimeInMinutes(status.getTimeInMinutes() + 1);
status.setAvgJobsPerMinute((status.getTotalJobs() - status.getQueuedJobs()) / status.getTimeInMinutes());
status.setAvgJobsPerMinute((double) (status.getTotalJobs() - status.getQueuedJobs()) / status.getTimeInMinutes());
status = localCIStatusRepository.save(status);
websocketService.sendRunLocalCIUpdate(simulationRun.getId(), status);
} while (numberOfQueuedJobs > 0);
status.setFinished(true);
status = localCIStatusRepository.save(status);
websocketService.sendRunLocalCIUpdate(simulationRun.getId(), status);
log.info("Finished subscribing to local CI status for simulation run {}", simulationRun.getId());
}

private void cleanup() {
log.info("Cleaning up local CI status");
localCIStatusRepository.deleteAllNotFinished();
}
}
4 changes: 2 additions & 2 deletions src/main/java/de/tum/cit/ase/service/PrometheusService.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public List<MetricValue> getCpuUsageVcs(SimulationRun run) {
* @return A list of CPU usage values. An empty list if no Prometheus instance is configured for the CI system.
*/
public List<MetricValue> getCpuUsageCi(SimulationRun run) {
log.info("Getting CI CPU usage for {}", run);
log.debug("Getting CI CPU usage for {}", run);
var instance = artemisConfiguration.getPrometheusInstanceCi(run.getSimulation().getServer());
if (instance == null || instance.isBlank()) {
log.warn("No Prometheus instance configured for CI on {}", run.getSimulation().getServer());
Expand All @@ -94,7 +94,7 @@ public List<MetricValue> getCpuUsageCi(SimulationRun run) {
* @return The response from Prometheus.
*/
public QueryResponse executeQuery(String query, ZonedDateTime start, ZonedDateTime end) {
log.info("Querying Prometheus: {}", query);
log.debug("Querying Prometheus: {}", query);
if (webClient == null) {
setupWebclient();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ public void deleteExam(long courseId, long examId) {
public List<DomainObject> getBuildQueue(long courseId) {
return webClient
.get()
.uri(uriBuilder -> uriBuilder.pathSegment("api", "build-job-queue", "running", String.valueOf(courseId)).build())
.uri(uriBuilder -> uriBuilder.pathSegment("api", "build-job-queue", "queued", String.valueOf(courseId)).build())
.retrieve()
.bodyToFlux(DomainObject.class)
.collectList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public SimulationRun getSimulationRun(long id) {
}

public SimulationRun getSimulationRunWithStatsAndLogs(long id) {
return simulationRunRepository.findByIdWithStatsAndLogMessages(id);
return simulationRunRepository.findByIdWithStatsAndLogMessages(id).orElseThrow();
}

public void deleteSimulation(long id) {
Expand Down Expand Up @@ -166,7 +166,7 @@ public void cancelActiveRun(long runId) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
run = simulationRunRepository.findById(runId).orElseThrow();
run = simulationRunRepository.findByIdWithStatsAndLogMessages(runId).orElseThrow();

run.setStatus(SimulationRun.Status.CANCELLED);
run.setEndDateTime(now());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public synchronized void simulateExam(SimulationRun simulationRun) {
return;
}

if (!doNotSleep) {
if (!doNotSleep && !artemisConfiguration.getIsLocal(simulationRun.getSimulation().getServer())) {
// Wait for synchronization of user groups
try {
logAndSend(false, simulationRun, "Waiting for synchronization of user groups (1 min)...");
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/de/tum/cit/ase/web/rest/PrometheusResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import de.tum.cit.ase.prometheus.MetricValue;
import de.tum.cit.ase.security.AuthoritiesConstants;
import de.tum.cit.ase.service.PrometheusService;
import de.tum.cit.ase.service.artemis.ArtemisConfiguration;
import de.tum.cit.ase.service.simulation.SimulationDataService;
import java.util.List;
import org.springframework.http.ResponseEntity;
Expand All @@ -20,10 +21,16 @@ public class PrometheusResource {

private final PrometheusService prometheusService;
private final SimulationDataService simulationDataService;
private final ArtemisConfiguration artemisConfiguration;

public PrometheusResource(PrometheusService prometheusService, SimulationDataService simulationDataService) {
public PrometheusResource(
PrometheusService prometheusService,
SimulationDataService simulationDataService,
ArtemisConfiguration artemisConfiguration
) {
this.simulationDataService = simulationDataService;
this.prometheusService = prometheusService;
this.artemisConfiguration = artemisConfiguration;
}

/**
Expand Down Expand Up @@ -51,6 +58,9 @@ public ResponseEntity<List<MetricValue>> getCpuUsageVcs(@PathVariable("runId") l
if (run.getStatus() != SimulationRun.Status.FINISHED && run.getStatus() != SimulationRun.Status.RUNNING) {
return ResponseEntity.ok(List.of());
}
if (artemisConfiguration.getIsLocal(run.getSimulation().getServer())) {
return ResponseEntity.ok(List.of());
}
return ResponseEntity.ok(prometheusService.getCpuUsageVcs(run));
}

Expand All @@ -65,6 +75,9 @@ public ResponseEntity<List<MetricValue>> getCpuUsageCi(@PathVariable("runId") lo
if (run.getStatus() != SimulationRun.Status.FINISHED && run.getStatus() != SimulationRun.Status.RUNNING) {
return ResponseEntity.ok(List.of());
}
if (artemisConfiguration.getIsLocal(run.getSimulation().getServer())) {
return ResponseEntity.ok(List.of());
}
return ResponseEntity.ok(prometheusService.getCpuUsageCi(run));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<column name="time_in_minutes" type="int">
<constraints nullable="false"/>
</column>
<column name="avg_jobs_per_minute" type="int">
<column name="avg_jobs_per_minute" type="double">
<constraints nullable="false"/>
</column>
<column name="simulation_run_id" type="bigint">
Expand Down
2 changes: 2 additions & 0 deletions src/main/webapp/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { CreateSimulationBoxComponent } from './layouts/create-simulation-box/cr
import { ServerBadgeComponent } from './layouts/server-badge/server-badge.component';
import { ModeExplanationComponent } from './layouts/mode-explanation/mode-explanation.component';
import { PrometheusBoxComponent } from './layouts/prometheus-box/prometheus-box.component';
import { LocalCiStatusCardComponent } from './layouts/local-ci-status-card/local-ci-status-card.component';
// jhipster-needle-angular-add-module-import JHipster will add new module here

@NgModule({
Expand All @@ -44,6 +45,7 @@ import { PrometheusBoxComponent } from './layouts/prometheus-box/prometheus-box.
FontAwesomeModule,
NgbAccordionModule,
PrometheusBoxComponent,
LocalCiStatusCardComponent,
],
providers: [
Title,
Expand Down
2 changes: 1 addition & 1 deletion src/main/webapp/app/entities/simulation/localCIStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SimulationRun } from './simulationRun';
export class LocalCIStatus {
constructor(
public id: number,
public isFinished: boolean,
public finished: boolean,
public queuedJobs: number,
public totalJobs: number,
public timeInMinutes: number,
Expand Down
2 changes: 2 additions & 0 deletions src/main/webapp/app/entities/simulation/simulationRun.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SimulationStats } from './simulationStats';
import { Simulation } from './simulation';
import { LogMessage } from './logMessage';
import { LocalCIStatus } from './localCIStatus';

export class SimulationRun {
constructor(
Expand All @@ -11,6 +12,7 @@ export class SimulationRun {
public simulation: Simulation,
public logMessages: LogMessage[],
public endDateTime?: Date,
public localCIStatus?: LocalCIStatus,
) {}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="card h-100">
<div class="card-body">
@if (localCIStatus.finished) {
<strong>All jobs finished.</strong>
<div class="mt-1">
<p class="mb-0">Total jobs: {{ localCIStatus.totalJobs }}</p>
<p class="mb-0">Executing jobs took {{ localCIStatus.timeInMinutes }} minutes</p>
</div>
} @else {
<strong>Jobs running...</strong>
<div class="mt-1">
<p class="mb-0">Finished: {{ localCIStatus.totalJobs - localCIStatus.queuedJobs }} / {{ localCIStatus.totalJobs }}</p>
<p class="mb-0">Queued: {{ localCIStatus.queuedJobs }}</p>
<p class="mb-0">Running for {{ localCIStatus.timeInMinutes }} minutes</p>
</div>
}
<p class="mb-0">Average jobs per minute: {{ localCIStatus.avgJobsPerMinute | number: '1.0-1' }}</p>
</div>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Component, Input } from '@angular/core';
import { LocalCIStatus } from '../../entities/simulation/localCIStatus';
import { DecimalPipe } from '@angular/common';

@Component({
selector: 'jhi-local-ci-status-card',
standalone: true,
imports: [DecimalPipe],
templateUrl: './local-ci-status-card.component.html',
styleUrl: './local-ci-status-card.component.scss',
})
export class LocalCiStatusCardComponent {
@Input() localCIStatus!: LocalCIStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,53 @@ <h3 class="pe-3">
}
</h3>
</div>
<div ngbAccordion class="mb-3">
<div ngbAccordionItem [collapsed]="false">
<h2 ngbAccordionHeader>
<button ngbAccordionButton>Log Messages</button>
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody class="p-0">
<ng-template>
<jhi-log-box [logMessages]="selectedRun.logMessages"></jhi-log-box>
</ng-template>
@if (!selectedRun.localCIStatus) {
<div ngbAccordion class="mb-3">
<div ngbAccordionItem [collapsed]="false">
<h2 ngbAccordionHeader>
<button ngbAccordionButton>Log Messages</button>
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody class="p-0">
<ng-template>
<jhi-log-box [logMessages]="selectedRun.logMessages"></jhi-log-box>
</ng-template>
</div>
</div>
</div>
</div>
</div>
} @else {
<div class="accordion-wrapper">
<div ngbAccordion class="mb-3 log-accordion">
<div ngbAccordionItem [collapsed]="false">
<h2 ngbAccordionHeader>
<button ngbAccordionButton>Log Messages</button>
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody class="p-0">
<ng-template>
<jhi-log-box [logMessages]="selectedRun.logMessages"></jhi-log-box>
</ng-template>
</div>
</div>
</div>
</div>
<div ngbAccordion class="mb-3 status-accordion">
<div ngbAccordionItem [collapsed]="false">
<h2 ngbAccordionHeader>
<button ngbAccordionButton>Local CI Status</button>
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody class="p-0">
<ng-template>
<jhi-local-ci-status-card [localCIStatus]="selectedRun.localCIStatus"></jhi-local-ci-status-card>
</ng-template>
</div>
</div>
</div>
</div>
</div>
}
<div ngbAccordion class="mb-3">
<div ngbAccordionItem [collapsed]="true">
<h2 ngbAccordionHeader>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.accordion-wrapper {
@media (min-width: 1200px) {
display: flex;
.log-accordion {
width: 75%;
margin-right: 10px;
}
.status-accordion {
width: 25%;
}
}
}
Loading

0 comments on commit 8f4b3a8

Please sign in to comment.