97 lines
3.2 KiB
C#
97 lines
3.2 KiB
C#
|
|
using Microsoft.AspNetCore.SignalR;
|
||
|
|
using OTSSignsOrchestrator.Server.Data;
|
||
|
|
using OTSSignsOrchestrator.Server.Data.Entities;
|
||
|
|
using OTSSignsOrchestrator.Server.Hubs;
|
||
|
|
|
||
|
|
namespace OTSSignsOrchestrator.Server.Workers;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Helper that wraps pipeline step execution with <see cref="JobStep"/> lifecycle management:
|
||
|
|
/// creates the row, sets Running, captures output, marks Completed/Failed, and broadcasts
|
||
|
|
/// progress via SignalR.
|
||
|
|
/// </summary>
|
||
|
|
public sealed class StepRunner
|
||
|
|
{
|
||
|
|
private readonly OrchestratorDbContext _db;
|
||
|
|
private readonly IHubContext<FleetHub, IFleetClient> _hub;
|
||
|
|
private readonly ILogger _logger;
|
||
|
|
private readonly Guid _jobId;
|
||
|
|
private readonly int _totalSteps;
|
||
|
|
private int _currentStep;
|
||
|
|
|
||
|
|
public StepRunner(
|
||
|
|
OrchestratorDbContext db,
|
||
|
|
IHubContext<FleetHub, IFleetClient> hub,
|
||
|
|
ILogger logger,
|
||
|
|
Guid jobId,
|
||
|
|
int totalSteps)
|
||
|
|
{
|
||
|
|
_db = db;
|
||
|
|
_hub = hub;
|
||
|
|
_logger = logger;
|
||
|
|
_jobId = jobId;
|
||
|
|
_totalSteps = totalSteps;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Execute a named step, persisting a <see cref="JobStep"/> record and broadcasting progress.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="stepName">Short identifier for the step (e.g. "mysql-setup").</param>
|
||
|
|
/// <param name="action">
|
||
|
|
/// Async delegate that performs the work. Return a log string summarising what happened.
|
||
|
|
/// </param>
|
||
|
|
/// <param name="ct">Cancellation token.</param>
|
||
|
|
public async Task RunAsync(string stepName, Func<Task<string>> action, CancellationToken ct)
|
||
|
|
{
|
||
|
|
_currentStep++;
|
||
|
|
var pct = (int)((double)_currentStep / _totalSteps * 100);
|
||
|
|
|
||
|
|
var step = new JobStep
|
||
|
|
{
|
||
|
|
Id = Guid.NewGuid(),
|
||
|
|
JobId = _jobId,
|
||
|
|
StepName = stepName,
|
||
|
|
Status = JobStepStatus.Running,
|
||
|
|
StartedAt = DateTime.UtcNow,
|
||
|
|
};
|
||
|
|
|
||
|
|
_db.JobSteps.Add(step);
|
||
|
|
await _db.SaveChangesAsync(ct);
|
||
|
|
|
||
|
|
_logger.LogInformation("Job {JobId}: step [{Step}/{Total}] {StepName} started",
|
||
|
|
_jobId, _currentStep, _totalSteps, stepName);
|
||
|
|
|
||
|
|
await _hub.Clients.All.SendJobProgressUpdate(
|
||
|
|
_jobId.ToString(), stepName, pct, $"Starting {stepName}…");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var logOutput = await action();
|
||
|
|
|
||
|
|
step.Status = JobStepStatus.Completed;
|
||
|
|
step.LogOutput = logOutput;
|
||
|
|
step.CompletedAt = DateTime.UtcNow;
|
||
|
|
await _db.SaveChangesAsync(ct);
|
||
|
|
|
||
|
|
_logger.LogInformation("Job {JobId}: step {StepName} completed", _jobId, stepName);
|
||
|
|
|
||
|
|
await _hub.Clients.All.SendJobProgressUpdate(
|
||
|
|
_jobId.ToString(), stepName, pct, logOutput);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
step.Status = JobStepStatus.Failed;
|
||
|
|
step.LogOutput = ex.Message;
|
||
|
|
step.CompletedAt = DateTime.UtcNow;
|
||
|
|
await _db.SaveChangesAsync(CancellationToken.None);
|
||
|
|
|
||
|
|
_logger.LogError(ex, "Job {JobId}: step {StepName} failed", _jobId, stepName);
|
||
|
|
|
||
|
|
await _hub.Clients.All.SendJobProgressUpdate(
|
||
|
|
_jobId.ToString(), stepName, pct, $"FAILED: {ex.Message}");
|
||
|
|
|
||
|
|
throw; // re-throw to fail the job
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|