290 lines
9.7 KiB
C#
290 lines
9.7 KiB
C#
using ZA.CoreService.ESBCertificateManager.Models;
|
|
|
|
namespace ZA.CoreService.ESBCertificateManager.Services;
|
|
|
|
/// <summary>
|
|
/// Orchestriert nur noch Container-Neustart (kein Deploy/TLS/SQL).
|
|
/// </summary>
|
|
public sealed class DeploymentOrchestrator
|
|
{
|
|
private readonly RestartExecutor _restartExecutor;
|
|
private readonly LocalCertificateDeployer _localCertificateDeployer;
|
|
private readonly PreflightValidator _preflightValidator = new();
|
|
private readonly AppSettings _settings;
|
|
|
|
public DeploymentOrchestrator(
|
|
AppSettings settings,
|
|
IReadOnlyList<SonicConnection> sonicConnections)
|
|
{
|
|
_settings = settings;
|
|
|
|
_restartExecutor =
|
|
new RestartExecutor(sonicConnections);
|
|
|
|
_localCertificateDeployer =
|
|
new LocalCertificateDeployer();
|
|
}
|
|
|
|
|
|
public PreflightValidationResult ValidateRestartOnly(IReadOnlyList<DeploymentTarget> selectedTargets)
|
|
=> _preflightValidator.ValidateRestartOnly(selectedTargets);
|
|
|
|
public async Task<DeploymentRunResult> RestartOnlyAsync(
|
|
IReadOnlyList<DeploymentTarget> selectedTargets,
|
|
IProgress<TargetProgressUpdate>? progress,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
DateTimeOffset startedAt = DateTimeOffset.Now;
|
|
List<TargetStepResult> results = [];
|
|
|
|
using RunLogger logger = new(_settings.LogDirectory);
|
|
logger.Write($"Neustart gestartet für {selectedTargets.Count} Ziel(e).");
|
|
|
|
foreach (DeploymentTarget target in selectedTargets)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
results.Add(await RestartTargetOnlyAsync(target, logger, progress, cancellationToken));
|
|
}
|
|
|
|
DateTimeOffset finishedAt = DateTimeOffset.Now;
|
|
DeploymentRunResult runResult = new()
|
|
{
|
|
TargetResults = results,
|
|
StartedAt = startedAt,
|
|
FinishedAt = finishedAt,
|
|
CertificateFilePath = string.Empty,
|
|
CertificateFingerprint = string.Empty
|
|
};
|
|
|
|
logger.Write(
|
|
$"Neustart beendet. Erfolg={runResult.OverallSuccess}; Dauer={(finishedAt - startedAt).TotalSeconds:F1}s; Log={logger.LogFilePath}");
|
|
|
|
return runResult;
|
|
}
|
|
public async Task<DeploymentRunResult> DeployAsync(
|
|
string certificateFilePath,
|
|
string certificateFingerprint,
|
|
IReadOnlyList<DeploymentTarget> selectedTargets,
|
|
IProgress<TargetProgressUpdate>? progress,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(certificateFilePath))
|
|
{
|
|
throw new ArgumentException(
|
|
"Es wurde kein Zertifikatsdateipfad angegeben.",
|
|
nameof(certificateFilePath));
|
|
}
|
|
|
|
if (!File.Exists(certificateFilePath))
|
|
{
|
|
throw new FileNotFoundException(
|
|
"Die ausgewählte Zertifikatsdatei wurde nicht gefunden.",
|
|
certificateFilePath);
|
|
}
|
|
|
|
if (selectedTargets.Count == 0)
|
|
{
|
|
throw new ArgumentException(
|
|
"Es wurde kein Bereitstellungsziel ausgewählt.",
|
|
nameof(selectedTargets));
|
|
}
|
|
|
|
DateTimeOffset startedAt = DateTimeOffset.Now;
|
|
List<TargetStepResult> results = [];
|
|
|
|
using RunLogger logger = new(_settings.LogDirectory);
|
|
|
|
logger.Write(
|
|
$"Deployment gestartet für {selectedTargets.Count} Ziel(e). " +
|
|
$"Zertifikat={Path.GetFileName(certificateFilePath)}");
|
|
|
|
foreach (DeploymentTarget target in selectedTargets)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
TargetStepResult result = await DeployTargetAsync(
|
|
certificateFilePath,
|
|
target,
|
|
logger,
|
|
progress,
|
|
cancellationToken);
|
|
|
|
results.Add(result);
|
|
}
|
|
|
|
DateTimeOffset finishedAt = DateTimeOffset.Now;
|
|
|
|
DeploymentRunResult runResult = new()
|
|
{
|
|
TargetResults = results,
|
|
StartedAt = startedAt,
|
|
FinishedAt = finishedAt,
|
|
CertificateFilePath = certificateFilePath,
|
|
CertificateFingerprint = certificateFingerprint
|
|
};
|
|
|
|
logger.Write(
|
|
$"Deployment beendet. Erfolg={runResult.OverallSuccess}; " +
|
|
$"Dauer={(finishedAt - startedAt).TotalSeconds:F1}s; " +
|
|
$"Log={logger.LogFilePath}");
|
|
|
|
return runResult;
|
|
}
|
|
private async Task<TargetStepResult> RestartTargetOnlyAsync(
|
|
DeploymentTarget target,
|
|
RunLogger logger,
|
|
IProgress<TargetProgressUpdate>? progress,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
List<string> steps = [];
|
|
DateTimeOffset targetStart = DateTimeOffset.Now;
|
|
progress?.Report(new TargetProgressUpdate(target.Id, "Neustart…", false));
|
|
|
|
(bool restartOk, string restartStatus, string? restartDetail) =
|
|
await _restartExecutor.ExecuteAsync(target, cancellationToken);
|
|
|
|
steps.Add($"{restartStatus}: {restartDetail}");
|
|
logger.Write($"[{target.Name}] Restart: {restartStatus} | {restartDetail}");
|
|
|
|
TargetStepResult targetResult = new()
|
|
{
|
|
TargetId = target.Id,
|
|
TargetName = target.Name,
|
|
Success = restartOk,
|
|
StatusText = restartStatus,
|
|
Detail = restartDetail,
|
|
Steps = steps,
|
|
StartedAt = targetStart,
|
|
FinishedAt = DateTimeOffset.Now,
|
|
CopySucceeded = true,
|
|
RestartSucceeded = restartOk,
|
|
TlsSucceeded = true,
|
|
ObservedFingerprint = null
|
|
};
|
|
|
|
progress?.Report(new TargetProgressUpdate(target.Id, restartStatus, restartOk));
|
|
return targetResult;
|
|
}
|
|
private async Task<TargetStepResult> DeployTargetAsync(
|
|
string certificateFilePath,
|
|
DeploymentTarget target,
|
|
RunLogger logger,
|
|
IProgress<TargetProgressUpdate>? progress,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
List<string> steps = [];
|
|
DateTimeOffset targetStart = DateTimeOffset.Now;
|
|
|
|
try
|
|
{
|
|
progress?.Report(new TargetProgressUpdate(
|
|
target.Id,
|
|
$"Zertifikat wird nach '{target.TargetDirectory}' kopiert ...",
|
|
false));
|
|
|
|
string deployedFilePath =
|
|
await _localCertificateDeployer.DeployAsync(
|
|
certificateFilePath,
|
|
target.TargetDirectory,
|
|
target.CertificateFileName,
|
|
target.BackupEnabled,
|
|
target.BackupDirectoryName,
|
|
cancellationToken);
|
|
|
|
string copyStatus = $"Zertifikat kopiert: {Path.GetFileName(deployedFilePath)}";
|
|
|
|
steps.Add(copyStatus);
|
|
logger.Write($"[{target.Name}] {copyStatus} | Ziel={deployedFilePath}");
|
|
|
|
progress?.Report(new TargetProgressUpdate(
|
|
target.Id,
|
|
copyStatus,
|
|
true));
|
|
|
|
return new TargetStepResult
|
|
{
|
|
TargetId = target.Id,
|
|
TargetName = target.Name,
|
|
Success = true,
|
|
StatusText = "Kopieren erfolgreich",
|
|
Detail = deployedFilePath,
|
|
Steps = steps,
|
|
StartedAt = targetStart,
|
|
FinishedAt = DateTimeOffset.Now,
|
|
|
|
CopySucceeded = true,
|
|
RestartSucceeded = true,
|
|
TlsSucceeded = true,
|
|
ObservedFingerprint = null
|
|
};
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string errorStatus = $"Kopieren fehlgeschlagen: {ex.Message}";
|
|
|
|
steps.Add(errorStatus);
|
|
logger.Write($"[{target.Name}] {errorStatus}");
|
|
|
|
progress?.Report(new TargetProgressUpdate(
|
|
target.Id,
|
|
errorStatus,
|
|
false));
|
|
|
|
return new TargetStepResult
|
|
{
|
|
TargetId = target.Id,
|
|
TargetName = target.Name,
|
|
Success = false,
|
|
StatusText = "Kopieren fehlgeschlagen",
|
|
Detail = ex.Message,
|
|
Steps = steps,
|
|
StartedAt = targetStart,
|
|
FinishedAt = DateTimeOffset.Now,
|
|
|
|
CopySucceeded = false,
|
|
RestartSucceeded = false,
|
|
TlsSucceeded = false,
|
|
ObservedFingerprint = null
|
|
};
|
|
}
|
|
}
|
|
private static string? CreateCertificateBackup(string currentCertificatePath)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(currentCertificatePath) ||
|
|
!File.Exists(currentCertificatePath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string certificateDirectory =
|
|
Path.GetDirectoryName(currentCertificatePath)
|
|
?? throw new InvalidOperationException(
|
|
$"Kein Zielordner für Zertifikat gefunden: {currentCertificatePath}");
|
|
|
|
string backupDirectory = Path.Combine(certificateDirectory, "Backup");
|
|
Directory.CreateDirectory(backupDirectory);
|
|
|
|
string fileNameWithoutExtension =
|
|
Path.GetFileNameWithoutExtension(currentCertificatePath);
|
|
|
|
string extension = Path.GetExtension(currentCertificatePath);
|
|
|
|
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
|
|
string backupFileName =
|
|
$"{fileNameWithoutExtension}_{timestamp}{extension}";
|
|
|
|
string backupPath = Path.Combine(backupDirectory, backupFileName);
|
|
|
|
File.Copy(currentCertificatePath, backupPath, overwrite: false);
|
|
|
|
return backupPath;
|
|
}
|
|
}
|
|
|
|
public readonly record struct TargetProgressUpdate(int TargetId, string StatusText, bool? SuccessHint);
|