using System.Diagnostics; using System.Text; using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; /// /// Neustart über die offiziellen Sonic-Bin-Scripts (laut Doku): /// SonicHome\bin\stopcontainer.bat Domain.Container /// SonicHome\bin\startcontainer.bat Domain.Container /// Die Scripts setzen selbst JAVA_HOME – kein separates System-Java nötig. /// public sealed class SonicBinRestartExecutor { private readonly SonicConnection _connection; public SonicBinRestartExecutor(SonicConnection connection) { _connection = connection; } public (bool Ok, string? Error, string? Detail) Probe() { string sonicHome = _connection.SonicHome?.Trim() ?? string.Empty; if (string.IsNullOrWhiteSpace(sonicHome)) { return (false, "SonicHome ist leer – in appsettings setzen.", null); } if (!Directory.Exists(sonicHome)) { return (false, $"SonicHome existiert nicht: '{sonicHome}'", null); } string stop = Path.Combine(sonicHome, "bin", "stopcontainer.bat"); string start = Path.Combine(sonicHome, "bin", "startcontainer.bat"); if (!File.Exists(stop) || !File.Exists(start)) { return (false, $"stopcontainer.bat/startcontainer.bat fehlen unter '{Path.Combine(sonicHome, "bin")}'.", $"stop={File.Exists(stop)}; start={File.Exists(start)}"); } return (true, null, $"SonicHome={sonicHome}"); } public async Task<(bool Success, string Status, string? Detail)> RestartAsync( string containerName, CancellationToken cancellationToken = default) { (bool probeOk, string? probeError, string? probeDetail) = Probe(); if (!probeOk) { return (false, "Neustart fehlgeschlagen (SonicBin)", $"{probeError}\n{probeDetail}"); } string sonicHome = _connection.SonicHome.Trim(); string bin = Path.Combine(sonicHome, "bin"); string stopBat = Path.Combine(bin, "stopcontainer.bat"); string startBat = Path.Combine(bin, "startcontainer.bat"); string shortName = containerName.Contains('.') ? containerName[(containerName.IndexOf('.') + 1)..] : containerName; string fullName = containerName.Contains('.') ? containerName : $"{_connection.DomainName}.{containerName}"; StringBuilder log = new(); log.AppendLine($"SonicHome={sonicHome}"); log.AppendLine($"Container={fullName} (kurz={shortName})"); (bool stopOk, string stopOut, string stopErr, int stopCode) = await RunBatAsync(stopBat, fullName, bin, cancellationToken); log.AppendLine($"STOP ExitCode={stopCode}"); if (stopOut.Length > 0) log.AppendLine("STOP out: " + Truncate(stopOut)); if (stopErr.Length > 0) log.AppendLine("STOP err: " + Truncate(stopErr)); // stopcontainer kann ExitCode!=0 liefern wenn Container schon down – trotzdem starten await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); (bool startOk, string startOut, string startErr, int startCode) = await RunBatAsync(startBat, fullName, bin, cancellationToken); log.AppendLine($"START ExitCode={startCode}"); if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut)); if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr)); if (!startOk || startCode != 0) { // Fallback: Kurzname ohne Domain if (!string.Equals(shortName, fullName, StringComparison.OrdinalIgnoreCase)) { log.AppendLine($"Retry START mit Kurzname '{shortName}'…"); (startOk, startOut, startErr, startCode) = await RunBatAsync(startBat, shortName, bin, cancellationToken); log.AppendLine($"START(short) ExitCode={startCode}"); if (startOut.Length > 0) log.AppendLine("START out: " + Truncate(startOut)); if (startErr.Length > 0) log.AppendLine("START err: " + Truncate(startErr)); } } await Task.Delay( TimeSpan.FromSeconds(Math.Clamp(_connection.PostRestartDelaySeconds, 0, 120)), cancellationToken); if (startCode != 0) { return (false, "Neustart fehlgeschlagen (SonicBin)", $"startcontainer ExitCode={startCode} (stop ExitCode={stopCode}).\n{log}"); } return (true, $"Container '{fullName}' neugestartet (SonicBin)", log.ToString()); } private static async Task<(bool Started, string StdOut, string StdErr, int ExitCode)> RunBatAsync( string batPath, string argument, string workingDirectory, CancellationToken cancellationToken) { ProcessStartInfo psi = new() { FileName = "cmd.exe", Arguments = $"/c \"\"{batPath}\" \"{argument}\"\"", WorkingDirectory = workingDirectory, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; using Process process = new() { StartInfo = psi }; if (!process.Start()) { return (false, string.Empty, "cmd.exe konnte nicht gestartet werden.", -1); } Task stdoutTask = process.StandardOutput.ReadToEndAsync(cancellationToken); Task stderrTask = process.StandardError.ReadToEndAsync(cancellationToken); await process.WaitForExitAsync(cancellationToken); return (true, (await stdoutTask).Trim(), (await stderrTask).Trim(), process.ExitCode); } private static string Truncate(string value, int max = 500) => value.Length <= max ? value : value[..max] + "…"; }