Guard Checkmk service renames during updates

This commit is contained in:
2026-08-10 18:06:00 +02:00
parent d408419c5f
commit 8fe1b57173
9 changed files with 192 additions and 23 deletions
@@ -27,7 +27,13 @@ namespace BizTalkCheckmkPulse.Setup
checkmkLocalDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "checkmk", "agent", "local");
}
public void Install(string account, string password, bool isGmsa, string environmentName, Action<string> report)
public void Install(
string account,
string password,
bool isGmsa,
string environmentName,
bool allowServiceNameChange,
Action<string> report)
{
Validate(account, password, isGmsa, environmentName);
report = report ?? delegate { };
@@ -72,9 +78,21 @@ namespace BizTalkCheckmkPulse.Setup
stagedConfig,
File.Exists(targetConfig) ? targetConfig : null,
environmentName);
RunSelfTest(stagedExe);
var stagedServiceNames = RunSelfTest(stagedExe);
report("Update-Staging validiert. Umgebung=" + (effectiveEnvironment.Length == 0 ? "(keine)" : effectiveEnvironment) + ".");
if (File.Exists(targetExe))
{
var installedServiceNames = RunSelfTest(targetExe);
var serviceNameChange = EnsureServiceNameCompatibility(
installedServiceNames,
stagedServiceNames,
allowServiceNameChange);
report(serviceNameChange.Length == 0
? "Checkmk-Servicevertrag unveraendert: keine Service Discovery erforderlich."
: "Checkmk-Service-Rename ausdruecklich bestaetigt; Service Discovery erforderlich. " + serviceNameChange);
}
// Erst nach vollstaendiger Staging-Pruefung wird der laufende Provider angehalten.
scheduler.DeleteIfExists(TaskName);
taskRemoved = true;
@@ -397,7 +415,7 @@ namespace BizTalkCheckmkPulse.Setup
new DirectoryInfo(path).SetAccessControl(security);
}
private static void RunSelfTest(string executable)
private static IReadOnlyList<string> RunSelfTest(string executable)
{
var start = new ProcessStartInfo(executable, "--self-test")
{
@@ -419,7 +437,45 @@ namespace BizTalkCheckmkPulse.Setup
var lines = output.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
if (process.ExitCode != 0 || lines.Length != 9 || lines.Any(x => !x.StartsWith("0 ", StringComparison.Ordinal)))
throw new InvalidOperationException("Self-Test fehlgeschlagen. Exitcode=" + process.ExitCode + ", Zeilen=" + lines.Length + ". " + error);
var serviceNames = lines.Select(ExtractServiceName).ToArray();
if (serviceNames.Distinct(StringComparer.Ordinal).Count() != serviceNames.Length)
throw new InvalidOperationException("Self-Test fehlgeschlagen: Checkmk-Servicenamen sind nicht eindeutig.");
return serviceNames;
}
}
private static string ExtractServiceName(string line)
{
var firstQuote = line.IndexOf('"');
var secondQuote = firstQuote < 0 ? -1 : line.IndexOf('"', firstQuote + 1);
if (firstQuote < 0 || secondQuote <= firstQuote + 1)
throw new InvalidOperationException("Self-Test fehlgeschlagen: Checkmk-Servicename kann nicht gelesen werden: " + line);
return line.Substring(firstQuote + 1, secondQuote - firstQuote - 1);
}
internal static string EnsureServiceNameCompatibility(
IEnumerable<string> installedServiceNames,
IEnumerable<string> stagedServiceNames,
bool allowServiceNameChange)
{
var installed = new HashSet<string>(installedServiceNames ?? Enumerable.Empty<string>(), StringComparer.Ordinal);
var staged = new HashSet<string>(stagedServiceNames ?? Enumerable.Empty<string>(), StringComparer.Ordinal);
if (installed.SetEquals(staged)) return string.Empty;
var removed = installed.Except(staged, StringComparer.Ordinal).OrderBy(x => x, StringComparer.Ordinal).ToArray();
var added = staged.Except(installed, StringComparer.Ordinal).OrderBy(x => x, StringComparer.Ordinal).ToArray();
var description = "Entfernt=[" + string.Join(", ", removed) + "]; Neu=[" + string.Join(", ", added) + "].";
if (!allowServiceNameChange)
{
throw new InvalidOperationException(
"Sicherheitsstopp: Das Update wuerde Checkmk-Servicenamen aendern. "
+ description
+ " Ohne Service Discovery entstehen verwaiste bzw. fehlende Services. "
+ "Nur wenn die Aenderung beabsichtigt ist, im Setup 'Service Discovery ist eingeplant' bestaetigen und anschliessend die Discovery ausfuehren.");
}
return description;
}
}
}