Keep original UI; drop unused deploy/XApi/WinRM/TLS/SQL paths.
Form1 design unchanged; only MfApi restart and certificate recognition remain active. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,167 +0,0 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using ZA.CoreService.ESBCertificateManager.Models;
|
||||
|
||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt Deployment-Lauf-Ergebnisse in die SQL-Datenbank.
|
||||
/// Tabellen: dbo.DeploymentRuns (GUID-PK) + dbo.DeploymentTargetResults.
|
||||
/// Ist kein ConnectionString konfiguriert, werden alle Operationen still übersprungen.
|
||||
/// </summary>
|
||||
public sealed class SqlRunLogger
|
||||
{
|
||||
private readonly string? _connectionString;
|
||||
|
||||
public SqlRunLogger(string? connectionString)
|
||||
{
|
||||
_connectionString = string.IsNullOrWhiteSpace(connectionString)
|
||||
? null
|
||||
: connectionString;
|
||||
}
|
||||
|
||||
public bool IsEnabled => _connectionString is not null;
|
||||
|
||||
/// <summary>
|
||||
/// Legt einen neuen Lauf-Datensatz an.
|
||||
/// </summary>
|
||||
public async Task BeginRunAsync(
|
||||
Guid runId,
|
||||
DateTimeOffset startedAt,
|
||||
string certificateFilePath,
|
||||
string certificateFingerprint,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_connectionString is null) return;
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO dbo.DeploymentRuns
|
||||
(Id, StartedAtUtc, SourceFile, SourceFingerprint, StartedBy, MachineName, OverallStatus)
|
||||
VALUES
|
||||
(@Id, @StartedAtUtc, @SourceFile, @SourceFingerprint, @StartedBy, @MachineName, N'Running');
|
||||
""";
|
||||
|
||||
await using SqlConnection conn = new(_connectionString);
|
||||
await conn.OpenAsync(cancellationToken);
|
||||
await using SqlCommand cmd = new(sql, conn);
|
||||
|
||||
cmd.Parameters.AddWithValue("@Id", runId);
|
||||
cmd.Parameters.AddWithValue("@StartedAtUtc", startedAt.UtcDateTime);
|
||||
cmd.Parameters.AddWithValue("@SourceFile", Path.GetFileName(certificateFilePath));
|
||||
cmd.Parameters.AddWithValue("@SourceFingerprint", certificateFingerprint);
|
||||
cmd.Parameters.AddWithValue("@StartedBy", Environment.UserName);
|
||||
cmd.Parameters.AddWithValue("@MachineName", Environment.MachineName);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aktualisiert den Lauf-Datensatz mit Endzeitpunkt und Gesamtstatus.
|
||||
/// </summary>
|
||||
public async Task CompleteRunAsync(
|
||||
Guid runId,
|
||||
DateTimeOffset finishedAt,
|
||||
bool overallSuccess,
|
||||
int successCount,
|
||||
int totalCount,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_connectionString is null) return;
|
||||
|
||||
string status = overallSuccess
|
||||
? "Success"
|
||||
: (successCount > 0 ? "PartialFailure" : "Failure");
|
||||
|
||||
const string sql = """
|
||||
UPDATE dbo.DeploymentRuns
|
||||
SET FinishedAtUtc = @FinishedAtUtc,
|
||||
OverallStatus = @OverallStatus
|
||||
WHERE Id = @Id;
|
||||
""";
|
||||
|
||||
await using SqlConnection conn = new(_connectionString);
|
||||
await conn.OpenAsync(cancellationToken);
|
||||
await using SqlCommand cmd = new(sql, conn);
|
||||
|
||||
cmd.Parameters.AddWithValue("@Id", runId);
|
||||
cmd.Parameters.AddWithValue("@FinishedAtUtc", finishedAt.UtcDateTime);
|
||||
cmd.Parameters.AddWithValue("@OverallStatus", status);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt das Ergebnis eines einzelnen Deployment-Ziels.
|
||||
/// </summary>
|
||||
public async Task WriteTargetResultAsync(
|
||||
Guid runId,
|
||||
TargetStepResult step,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_connectionString is null) return;
|
||||
|
||||
string status = step.Success ? "Success" : "Failure";
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO dbo.DeploymentTargetResults
|
||||
(DeploymentRunId, TargetId, TargetName,
|
||||
StartedAtUtc, FinishedAtUtc,
|
||||
CopySucceeded, RestartSucceeded, TlsSucceeded,
|
||||
ObservedFingerprint, Status, ErrorMessage)
|
||||
VALUES
|
||||
(@RunId, @TargetId, @TargetName,
|
||||
@StartedAtUtc, @FinishedAtUtc,
|
||||
@CopySucceeded, @RestartSucceeded, @TlsSucceeded,
|
||||
@ObservedFingerprint, @Status, @ErrorMessage);
|
||||
""";
|
||||
|
||||
await using SqlConnection conn = new(_connectionString);
|
||||
await conn.OpenAsync(cancellationToken);
|
||||
await using SqlCommand cmd = new(sql, conn);
|
||||
|
||||
cmd.Parameters.AddWithValue("@RunId", runId);
|
||||
cmd.Parameters.AddWithValue("@TargetId", step.TargetId);
|
||||
cmd.Parameters.AddWithValue("@TargetName", step.TargetName);
|
||||
cmd.Parameters.AddWithValue("@StartedAtUtc", step.StartedAt.UtcDateTime);
|
||||
cmd.Parameters.AddWithValue("@FinishedAtUtc", step.FinishedAt.UtcDateTime);
|
||||
cmd.Parameters.AddWithValue("@CopySucceeded", step.CopySucceeded);
|
||||
cmd.Parameters.AddWithValue("@RestartSucceeded", step.RestartSucceeded);
|
||||
cmd.Parameters.AddWithValue("@TlsSucceeded", step.TlsSucceeded);
|
||||
cmd.Parameters.AddWithValue("@ObservedFingerprint", (object?)step.ObservedFingerprint ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Status", status);
|
||||
cmd.Parameters.AddWithValue("@ErrorMessage", step.Success ? DBNull.Value : (object?)(step.Detail ?? step.StatusText));
|
||||
|
||||
await cmd.ExecuteNonQueryAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistiert ein vollständiges <see cref="DeploymentRunResult"/> in einem einzigen Aufruf.
|
||||
/// </summary>
|
||||
public async Task PersistRunResultAsync(
|
||||
DeploymentRunResult runResult,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_connectionString is null) return;
|
||||
|
||||
await BeginRunAsync(
|
||||
runResult.RunId,
|
||||
runResult.StartedAt,
|
||||
runResult.CertificateFilePath,
|
||||
runResult.CertificateFingerprint,
|
||||
cancellationToken);
|
||||
|
||||
foreach (TargetStepResult step in runResult.TargetResults)
|
||||
{
|
||||
await WriteTargetResultAsync(runResult.RunId, step, cancellationToken);
|
||||
}
|
||||
|
||||
int successCount = runResult.TargetResults.Count(r => r.Success);
|
||||
|
||||
await CompleteRunAsync(
|
||||
runResult.RunId,
|
||||
runResult.FinishedAt,
|
||||
runResult.OverallSuccess,
|
||||
successCount,
|
||||
runResult.TargetResults.Count,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using ZA.CoreService.ESBCertificateManager.Models;
|
||||
|
||||
namespace ZA.CoreService.ESBCertificateManager.Data;
|
||||
|
||||
public sealed class SqlTargetRepository : ITargetRepository
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
|
||||
public SqlTargetRepository(string connectionString)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
}
|
||||
|
||||
public string SourceDescription => "SQL Server (dbo.DeploymentTargets)";
|
||||
|
||||
public async Task<IReadOnlyList<DeploymentTarget>> GetActiveTargetsAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("Es ist keine SQL-Verbindungszeichenfolge konfiguriert.");
|
||||
}
|
||||
|
||||
const string sql = """
|
||||
SELECT
|
||||
Id,
|
||||
Name,
|
||||
Environment,
|
||||
IsActive,
|
||||
CertificateTargetPath,
|
||||
CertificateFileName,
|
||||
ContainerName,
|
||||
RestartType,
|
||||
RestartCommand,
|
||||
RestartArguments,
|
||||
RestartTimeoutSeconds,
|
||||
SonicConnectionName,
|
||||
XapiSourcePath,
|
||||
TlsHost,
|
||||
TlsPort,
|
||||
TlsServerName,
|
||||
ExpectedFingerprint,
|
||||
SortOrder
|
||||
FROM dbo.DeploymentTargets
|
||||
WHERE IsActive = 1
|
||||
ORDER BY SortOrder, Name;
|
||||
""";
|
||||
|
||||
List<DeploymentTarget> targets = [];
|
||||
|
||||
await using SqlConnection connection = new(_connectionString);
|
||||
await connection.OpenAsync(cancellationToken);
|
||||
|
||||
await using SqlCommand command = new(sql, connection);
|
||||
await using SqlDataReader reader = await command.ExecuteReaderAsync(cancellationToken);
|
||||
|
||||
int id = reader.GetOrdinal("Id");
|
||||
int name = reader.GetOrdinal("Name");
|
||||
int environment = reader.GetOrdinal("Environment");
|
||||
int isActive = reader.GetOrdinal("IsActive");
|
||||
int certificateTargetPath = reader.GetOrdinal("CertificateTargetPath");
|
||||
int certificateFileName = reader.GetOrdinal("CertificateFileName");
|
||||
int containerName = reader.GetOrdinal("ContainerName");
|
||||
int restartType = reader.GetOrdinal("RestartType");
|
||||
int restartCommand = reader.GetOrdinal("RestartCommand");
|
||||
int restartArguments = reader.GetOrdinal("RestartArguments");
|
||||
int restartTimeoutSeconds = reader.GetOrdinal("RestartTimeoutSeconds");
|
||||
int sonicConnectionName = reader.GetOrdinal("SonicConnectionName");
|
||||
int xapiSourcePath = reader.GetOrdinal("XapiSourcePath");
|
||||
int tlsHost = reader.GetOrdinal("TlsHost");
|
||||
int tlsPort = reader.GetOrdinal("TlsPort");
|
||||
int tlsServerName = reader.GetOrdinal("TlsServerName");
|
||||
int expectedFingerprint = reader.GetOrdinal("ExpectedFingerprint");
|
||||
int sortOrder = reader.GetOrdinal("SortOrder");
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken))
|
||||
{
|
||||
RestartType parsedRestartType = Enum.TryParse(
|
||||
reader.GetString(restartType),
|
||||
ignoreCase: true,
|
||||
out RestartType parsed)
|
||||
? parsed
|
||||
: RestartType.None;
|
||||
|
||||
targets.Add(new DeploymentTarget
|
||||
{
|
||||
Id = reader.GetInt32(id),
|
||||
Name = reader.GetString(name),
|
||||
Environment = reader.GetString(environment),
|
||||
IsActive = reader.GetBoolean(isActive),
|
||||
TargetDirectory = reader.GetString(certificateTargetPath),
|
||||
CertificateFileName = reader.GetString(certificateFileName),
|
||||
ContainerName = reader.GetString(containerName),
|
||||
RestartType = parsedRestartType,
|
||||
RestartCommand = reader.IsDBNull(restartCommand) ? string.Empty : reader.GetString(restartCommand),
|
||||
RestartArguments = reader.GetString(restartArguments),
|
||||
RestartTimeoutSeconds = reader.GetInt32(restartTimeoutSeconds),
|
||||
SonicConnectionName = reader.GetString(sonicConnectionName),
|
||||
XapiSourcePath = reader.GetString(xapiSourcePath),
|
||||
TlsHost = reader.GetString(tlsHost),
|
||||
TlsPort = reader.IsDBNull(tlsPort) ? null : reader.GetInt32(tlsPort),
|
||||
TlsServerName = reader.GetString(tlsServerName),
|
||||
ExpectedFingerprint = reader.IsDBNull(expectedFingerprint) ? null : reader.GetString(expectedFingerprint),
|
||||
SortOrder = reader.GetInt32(sortOrder)
|
||||
});
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
@@ -11,34 +11,8 @@ public static class TargetRepositoryFactory
|
||||
string samplePath = Path.GetFullPath(
|
||||
Path.Combine(AppContext.BaseDirectory, settings.SampleTargetsPath));
|
||||
|
||||
if (settings.UseOfflineSampleData)
|
||||
{
|
||||
ITargetRepository jsonRepo = new JsonTargetRepository(samplePath);
|
||||
_ = await jsonRepo.GetActiveTargetsAsync(cancellationToken);
|
||||
return (jsonRepo, $"Ziele geladen aus {jsonRepo.SourceDescription}.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.ConnectionString))
|
||||
{
|
||||
return OfflineSample(samplePath, "Keine SQL-Verbindung konfiguriert – Offline-Sample wird verwendet.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SqlTargetRepository sqlRepo = new(settings.ConnectionString);
|
||||
_ = await sqlRepo.GetActiveTargetsAsync(cancellationToken);
|
||||
return (sqlRepo, $"Ziele geladen aus {sqlRepo.SourceDescription}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return OfflineSample(
|
||||
samplePath,
|
||||
$"SQL nicht erreichbar ({ex.Message}). Offline-Sample wird verwendet.");
|
||||
}
|
||||
ITargetRepository jsonRepo = new JsonTargetRepository(samplePath);
|
||||
_ = await jsonRepo.GetActiveTargetsAsync(cancellationToken);
|
||||
return (jsonRepo, $"Ziele geladen aus {jsonRepo.SourceDescription}.");
|
||||
}
|
||||
|
||||
private static (ITargetRepository Repository, string LoadMessage) OfflineSample(
|
||||
string samplePath,
|
||||
string message)
|
||||
=> (new JsonTargetRepository(samplePath), message);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"Name": "DE-Test / ct-ZADBService",
|
||||
"Environment": "TEST",
|
||||
"IsActive": true,
|
||||
"TargetDirectory": "DeploySandbox/target-esb",
|
||||
"CertificateFileName": "esb-cert.cer",
|
||||
"TargetDirectory": "",
|
||||
"CertificateFileName": "",
|
||||
"ContainerName": "ct-ZADBService",
|
||||
"RestartType": "SonicContainer",
|
||||
"RestartCommand": "",
|
||||
@@ -18,25 +18,5 @@
|
||||
"TlsServerName": "",
|
||||
"ExpectedFingerprint": null,
|
||||
"SortOrder": 10
|
||||
},
|
||||
{
|
||||
"Id": 2,
|
||||
"Name": "Lokaler CMD-Test (Echo)",
|
||||
"Environment": "DEV",
|
||||
"IsActive": false,
|
||||
"TargetDirectory": "DeploySandbox/target-cmd",
|
||||
"CertificateFileName": "esb-cert.cer",
|
||||
"ContainerName": "",
|
||||
"RestartType": "Command",
|
||||
"RestartCommand": "cmd.exe",
|
||||
"RestartArguments": "/c echo Neustart-Simulation OK",
|
||||
"RestartTimeoutSeconds": 15,
|
||||
"SonicConnectionName": "",
|
||||
"XapiSourcePath": "",
|
||||
"TlsHost": "",
|
||||
"TlsPort": null,
|
||||
"TlsServerName": "",
|
||||
"ExpectedFingerprint": null,
|
||||
"SortOrder": 20
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user