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
+16 -1
View File
@@ -28,7 +28,22 @@ nach dem nächsten Agentenlauf das 2.2.1-Fehlerbild `Item not found in
monitoring data`. Nur bei bewusst auf `true` gesetztem Opt-in ist anschließend monitoring data`. Nur bei bewusst auf `true` gesetztem Opt-in ist anschließend
eine Checkmk Service Discovery erforderlich. eine Checkmk Service Discovery erforderlich.
Vor dem Stoppen des vorhandenen Tasks wird die neue Version separat getestet. Ab Version 2.2.3 vergleicht der Installer bei jedem Update die exakten
Servicenamen der installierten und der vorbereiteten Version. Neue, entfernte
oder umbenannte Services führen **vor dem Stoppen des Tasks** zu einem
Sicherheitsstopp. Die Installation darf nur über die Checkbox
**Service-Rename ist beabsichtigt; Checkmk Service Discovery ist eingeplant**
fortgesetzt werden. Der Installer zeigt dabei die entfernten und neuen Namen
an. Eine bloß geänderte Ausgabereihenfolge gilt nicht als Rename.
Beim korrigierenden Wechsel von 2.2.1 mit `BizTalk ACC ...` auf stabile
`BizTalk ...`-Namen ist die Änderung beabsichtigt: Checkbox aktivieren und den
Servicebestand danach per Discovery abgleichen. Sind in Checkmk bereits die
unpräfixierten Services vorhanden, werden damit insbesondere eventuell
vorhandene falsche `BizTalk ACC ...`-Services bereinigt.
Vor dem Stoppen des vorhandenen Tasks wird die neue Version separat getestet
und ihr Checkmk-Servicevertrag mit der installierten Version verglichen.
Bei normalen Servicekonten prüft der Installer außerdem Kennwort und Bei normalen Servicekonten prüft der Installer außerdem Kennwort und
Batch-Anmelderecht vor der Umschaltung. Batch-Anmelderecht vor der Umschaltung.
Der Task wird vor dem Dateitausch deaktiviert und sein Prozessende maximal zehn Der Task wird vor dem Dateitausch deaktiviert und sein Prozessende maximal zehn
@@ -71,6 +71,10 @@ nicht netzwerkprüfbare Kandidaten. Version 2.2.2 weist sie explizit als
`BizTalk ...` und passen wieder zu den bereits entdeckten ACC-Services. `BizTalk ...` und passen wieder zu den bereits entdeckten ACC-Services.
- Nur ein bewusstes Opt-in mit `true` erzeugt Namen wie `BizTalk ACC ...` und - Nur ein bewusstes Opt-in mit `true` erzeugt Namen wie `BizTalk ACC ...` und
verlangt anschließend eine Checkmk Service Discovery. verlangt anschließend eine Checkmk Service Discovery.
- Version 2.2.3 ergänzt einen versionsübergreifenden Installer-Schutz: Der
tatsächliche Servicevertrag der vorhandenen und der neuen EXE wird vor dem
Task-Stopp verglichen. Unbestätigte Renames, neue oder entfernte Services
blockieren das Update; die Differenz wird vollständig angezeigt.
### Korrekte Abdeckungssemantik ### Korrekte Abdeckungssemantik
@@ -131,7 +135,9 @@ Endpoint-/Namensfix bewusst nicht grüngefärbt.
## ACC-Abnahme nach Installation ## ACC-Abnahme nach Installation
1. Setup 2.2.2 als Update ausführen; Umgebung `ACC` kann bestehen bleiben. 1. Setup 2.2.3 als Update ausführen; Umgebung `ACC` kann bestehen bleiben.
Beim Wechsel von der fehlerhaften 2.2.1-Ausgabe `BizTalk ACC ...` auf die
stabilen Namen die Rename-/Discovery-Checkbox bewusst aktivieren.
2. In der installierten Config bestätigen: 2. In der installierten Config bestätigen:
```text ```text
+8 -7
View File
@@ -306,7 +306,7 @@ def configure_document(document):
p = footer.paragraphs[0] p = footer.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(0) p.paragraph_format.space_after = Pt(0)
format_run(p.add_run("Version 2.2.2 | Stand 10.08.2026 | Seite "), size=7.5, color=MUTED) format_run(p.add_run("Version 2.2.3 | Stand 10.08.2026 | Seite "), size=7.5, color=MUTED)
add_page_field(p) add_page_field(p)
@@ -320,7 +320,7 @@ def build_document():
core.title = "BizTalk Checkmk Pulse Architektur- und Lösungsüberblick" core.title = "BizTalk Checkmk Pulse Architektur- und Lösungsüberblick"
core.subject = "Gesamtarchitektur, Funktionsumfang und Datenaustausch" core.subject = "Gesamtarchitektur, Funktionsumfang und Datenaustausch"
core.author = "BEW" core.author = "BEW"
core.comments = "Externe Lösungsübersicht zum implementierten Stand 2.2.2" core.comments = "Externe Lösungsübersicht zum implementierten Stand 2.2.3"
# Cover # Cover
banner = document.add_table(rows=1, cols=1) banner = document.add_table(rows=1, cols=1)
@@ -377,7 +377,7 @@ def build_document():
document, document,
("Dokumentmerkmal", "Angabe"), ("Dokumentmerkmal", "Angabe"),
( (
("Lösungsstand", "BizTalk Checkmk Pulse 2.2.2"), ("Lösungsstand", "BizTalk Checkmk Pulse 2.2.3"),
("Zielplattform", "Microsoft BizTalk Server 2020 · Windows Server 2019 · Checkmk 2.4"), ("Zielplattform", "Microsoft BizTalk Server 2020 · Windows Server 2019 · Checkmk 2.4"),
("Dokumentstand", "10. August 2026"), ("Dokumentstand", "10. August 2026"),
("Dokumentzweck", "Architektur- und Leistungsüberblick für technische Stakeholder"), ("Dokumentzweck", "Architektur- und Leistungsüberblick für technische Stakeholder"),
@@ -406,7 +406,7 @@ def build_document():
add_heading(document, "2. Was ist Checkmk und was ist ein Local Check?") add_heading(document, "2. Was ist Checkmk und was ist ein Local Check?")
add_body(document, "Checkmk ist eine zentrale Monitoring-Plattform für IT-Infrastrukturen und Anwendungen. Auf überwachten Servern liefert ein Agent technische Zustände und Messwerte an die Checkmk-Instanz. Dort werden die Daten als Hosts und Services dargestellt, historisiert und für Dashboards, Schwellwerte und Benachrichtigungen verwendet.") add_body(document, "Checkmk ist eine zentrale Monitoring-Plattform für IT-Infrastrukturen und Anwendungen. Auf überwachten Servern liefert ein Agent technische Zustände und Messwerte an die Checkmk-Instanz. Dort werden die Daten als Hosts und Services dargestellt, historisiert und für Dashboards, Schwellwerte und Benachrichtigungen verwendet.")
add_body(document, "Ein Local Check ist eine bewusst einfache Erweiterungsmöglichkeit des Checkmk-Agenten. Die Prüfung läuft auf dem Zielsystem und gibt pro Service eine Textzeile mit vier Bestandteilen aus:") add_body(document, "Ein Local Check ist eine bewusst einfache Erweiterungsmöglichkeit des Checkmk-Agenten. Die Prüfung läuft auf dem Zielsystem und gibt pro Service eine Textzeile mit vier Bestandteilen aus:")
add_code_block(document, ('0 "BizTalk PRD Send Ports" biztalk_send_ports_total=31;;;0|biztalk_send_ports_started=31;;;0', 'Alle Send Ports sind aktiv.')) add_code_block(document, ('0 "BizTalk Send Ports" biztalk_send_ports_total=31;;;0|biztalk_send_ports_started=31;;;0', 'Alle Send Ports sind aktiv.'))
add_table( add_table(
document, document,
("Bestandteil", "Bedeutung"), ("Bestandteil", "Bedeutung"),
@@ -512,8 +512,8 @@ def build_document():
"payloadLines=9", "payloadLines=9",
"payloadSha256=<64 hexadezimale Zeichen>", "payloadSha256=<64 hexadezimale Zeichen>",
"", "",
'0 "BizTalk PRD Suspended Instances" biztalk_suspended_total=0;;;0|... Suspended total=0.', '0 "BizTalk Suspended Instances" biztalk_suspended_total=0;;;0|... Suspended total=0.',
'0 "BizTalk PRD Endpoint Reachability" biztalk_endpoints_active=61;;;0|... Alle 61 aktiven Endpunkte sind erreichbar.', '0 "BizTalk Endpoint Reachability" biztalk_endpoints_active=61;;;0|... Alle 61 aktiven Endpunkte sind erreichbar.',
), ),
) )
add_body(document, "Die Base64-Felder verhindern problematische Trennzeichen im Header; sie sind keine Verschlüsselung. SHA-256 erkennt unvollständige oder veränderte Snapshots. Der Schutz vor unberechtigtem lokalem Zugriff erfolgt über Windows-Dateirechte.") add_body(document, "Die Base64-Felder verhindern problematische Trennzeichen im Header; sie sind keine Verschlüsselung. SHA-256 erkennt unvollständige oder veränderte Snapshots. Der Schutz vor unberechtigtem lokalem Zugriff erfolgt über Windows-Dateirechte.")
@@ -543,6 +543,7 @@ def build_document():
add_bullet(document, "Getrennte Tageslogs für Provider und Consumer; Standardaufbewahrung 30 Tage.") add_bullet(document, "Getrennte Tageslogs für Provider und Consumer; Standardaufbewahrung 30 Tage.")
add_bullet(document, "Erwartet inaktive Receive Locations und Send Ports können exakt allowlisted werden und bleiben als Messwert sichtbar.") add_bullet(document, "Erwartet inaktive Receive Locations und Send Ports können exakt allowlisted werden und bleiben als Messwert sichtbar.")
add_bullet(document, "Endpoint-Prüfung standardmäßig mit maximal 100 Zielen, 16 parallelen Probes und 3 Sekunden Timeout je Ziel.") add_bullet(document, "Endpoint-Prüfung standardmäßig mit maximal 100 Zielen, 16 parallelen Probes und 3 Sekunden Timeout je Ziel.")
add_bullet(document, "Der Installer vergleicht bei Updates die exakten Checkmk-Servicenamen und stoppt unbestätigte Änderungen, bevor Task oder Programmdateien angefasst werden.")
add_heading(document, "Sicherheitsmodell", level=2) add_heading(document, "Sicherheitsmodell", level=2)
add_table( add_table(
@@ -571,7 +572,7 @@ def build_document():
add_callout(document, "Kernaussage: ", "Ein privilegierter Read-only-Collector sammelt, ein unprivilegierter Consumer validiert und übergibt Checkmk erhält neun stabile Services statt direkten Zugriff auf BizTalk oder SQL.", fill=LIGHT_GREEN, border=GREEN) add_callout(document, "Kernaussage: ", "Ein privilegierter Read-only-Collector sammelt, ein unprivilegierter Consumer validiert und übergibt Checkmk erhält neun stabile Services statt direkten Zugriff auf BizTalk oder SQL.", fill=LIGHT_GREEN, border=GREEN)
add_rich_paragraph( add_rich_paragraph(
document, document,
(("Dokumentbasis: Implementierung und Projektdokumentation von BizTalk Checkmk Pulse 2.2.2 sowie Checkmk User Guide „Local checks“, abgerufen am 10. August 2026.", {"size": 7.8, "color": MUTED}),), (("Dokumentbasis: Implementierung und Projektdokumentation von BizTalk Checkmk Pulse 2.2.3 sowie Checkmk User Guide „Local checks“, abgerufen am 10. August 2026.", {"size": 7.8, "color": MUTED}),),
before=8, before=8,
after=0, after=0,
) )
@@ -27,7 +27,13 @@ namespace BizTalkCheckmkPulse.Setup
checkmkLocalDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "checkmk", "agent", "local"); 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); Validate(account, password, isGmsa, environmentName);
report = report ?? delegate { }; report = report ?? delegate { };
@@ -72,9 +78,21 @@ namespace BizTalkCheckmkPulse.Setup
stagedConfig, stagedConfig,
File.Exists(targetConfig) ? targetConfig : null, File.Exists(targetConfig) ? targetConfig : null,
environmentName); environmentName);
RunSelfTest(stagedExe); var stagedServiceNames = RunSelfTest(stagedExe);
report("Update-Staging validiert. Umgebung=" + (effectiveEnvironment.Length == 0 ? "(keine)" : effectiveEnvironment) + "."); 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. // Erst nach vollstaendiger Staging-Pruefung wird der laufende Provider angehalten.
scheduler.DeleteIfExists(TaskName); scheduler.DeleteIfExists(TaskName);
taskRemoved = true; taskRemoved = true;
@@ -397,7 +415,7 @@ namespace BizTalkCheckmkPulse.Setup
new DirectoryInfo(path).SetAccessControl(security); 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") var start = new ProcessStartInfo(executable, "--self-test")
{ {
@@ -419,7 +437,45 @@ namespace BizTalkCheckmkPulse.Setup
var lines = output.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); 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))) 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); 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;
}
} }
} }
+15 -7
View File
@@ -12,6 +12,7 @@ namespace BizTalkCheckmkPulse.Setup
private readonly TextBox password = new TextBox(); private readonly TextBox password = new TextBox();
private readonly ComboBox environment = new ComboBox(); private readonly ComboBox environment = new ComboBox();
private readonly CheckBox gmsa = new CheckBox(); private readonly CheckBox gmsa = new CheckBox();
private readonly CheckBox allowServiceNameChange = new CheckBox();
private readonly Button install = new Button(); private readonly Button install = new Button();
private readonly Button uninstall = new Button(); private readonly Button uninstall = new Button();
private readonly TextBox status = new TextBox(); private readonly TextBox status = new TextBox();
@@ -20,8 +21,8 @@ namespace BizTalkCheckmkPulse.Setup
{ {
this.engine = engine; this.engine = engine;
Text = "BizTalk Checkmk Pulse Setup"; Text = "BizTalk Checkmk Pulse Setup";
ClientSize = new Size(650, 485); ClientSize = new Size(650, 535);
MinimumSize = new Size(666, 524); MinimumSize = new Size(666, 574);
StartPosition = FormStartPosition.CenterScreen; StartPosition = FormStartPosition.CenterScreen;
Font = new Font("Segoe UI", 9F); Font = new Font("Segoe UI", 9F);
@@ -68,18 +69,23 @@ namespace BizTalkCheckmkPulse.Setup
if (gmsa.Checked) password.Clear(); if (gmsa.Checked) password.Clear();
}; };
install.Location = new Point(190, 267); allowServiceNameChange.Location = new Point(190, 262);
allowServiceNameChange.Size = new Size(420, 34);
allowServiceNameChange.Text = "Service-Rename ist beabsichtigt; Checkmk Service Discovery ist eingeplant";
allowServiceNameChange.Enabled = engine.IsInstalled;
install.Location = new Point(190, 306);
install.Size = new Size(200, 34); install.Size = new Size(200, 34);
install.Text = "Installieren / aktualisieren"; install.Text = "Installieren / aktualisieren";
install.Click += async delegate { await InstallAsync(); }; install.Click += async delegate { await InstallAsync(); };
uninstall.Location = new Point(410, 267); uninstall.Location = new Point(410, 306);
uninstall.Size = new Size(200, 34); uninstall.Size = new Size(200, 34);
uninstall.Text = "Deinstallieren"; uninstall.Text = "Deinstallieren";
uninstall.Click += async delegate { await UninstallAsync(); }; uninstall.Click += async delegate { await UninstallAsync(); };
status.Location = new Point(22, 320); status.Location = new Point(22, 359);
status.Size = new Size(588, 135); status.Size = new Size(588, 146);
status.Multiline = true; status.Multiline = true;
status.ReadOnly = true; status.ReadOnly = true;
status.ScrollBars = ScrollBars.Vertical; status.ScrollBars = ScrollBars.Vertical;
@@ -89,7 +95,7 @@ namespace BizTalkCheckmkPulse.Setup
Controls.AddRange(new Control[] Controls.AddRange(new Control[]
{ {
title, description, account, password, environment, gmsa, install, uninstall, status title, description, account, password, environment, gmsa, allowServiceNameChange, install, uninstall, status
}); });
AcceptButton = install; AcceptButton = install;
} }
@@ -114,6 +120,7 @@ namespace BizTalkCheckmkPulse.Setup
collectorPassword, collectorPassword,
collectorIsGmsa, collectorIsGmsa,
environmentName, environmentName,
allowServiceNameChange.Checked,
Report)); Report));
password.Clear(); password.Clear();
Report("Installation abgeschlossen. Der Provider-Task wurde einmalig gestartet."); Report("Installation abgeschlossen. Der Provider-Task wurde einmalig gestartet.");
@@ -166,6 +173,7 @@ namespace BizTalkCheckmkPulse.Setup
account.Enabled = !busy; account.Enabled = !busy;
environment.Enabled = !busy; environment.Enabled = !busy;
gmsa.Enabled = !busy; gmsa.Enabled = !busy;
allowServiceNameChange.Enabled = !busy && engine.IsInstalled;
password.Enabled = !busy && !gmsa.Checked; password.Enabled = !busy && !gmsa.Checked;
UseWaitCursor = busy; UseWaitCursor = busy;
if (message != null) Report(message); if (message != null) Report(message);
@@ -6,5 +6,5 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyDescription("PowerShell-free installer and updater for BizTalk Checkmk Pulse")] [assembly: AssemblyDescription("PowerShell-free installer and updater for BizTalk Checkmk Pulse")]
[assembly: AssemblyCompany("BEW")] [assembly: AssemblyCompany("BEW")]
[assembly: AssemblyProduct("BizTalk Checkmk Pulse")] [assembly: AssemblyProduct("BizTalk Checkmk Pulse")]
[assembly: AssemblyVersion("2.2.2.0")] [assembly: AssemblyVersion("2.2.3.0")]
[assembly: AssemblyFileVersion("2.2.2.0")] [assembly: AssemblyFileVersion("2.2.3.0")]
@@ -6,5 +6,5 @@ using System.Reflection;
[assembly: AssemblyDescription("Privileged BizTalk data provider and validated Checkmk snapshot consumer")] [assembly: AssemblyDescription("Privileged BizTalk data provider and validated Checkmk snapshot consumer")]
[assembly: AssemblyCompany("BEW")] [assembly: AssemblyCompany("BEW")]
[assembly: AssemblyProduct("BizTalk Checkmk Pulse")] [assembly: AssemblyProduct("BizTalk Checkmk Pulse")]
[assembly: AssemblyVersion("2.2.2.0")] [assembly: AssemblyVersion("2.2.3.0")]
[assembly: AssemblyFileVersion("2.2.2.0")] [assembly: AssemblyFileVersion("2.2.3.0")]
@@ -37,6 +37,13 @@ namespace BizTalkCheckmkPulse.Tests
Run("EndpointOutputListsOnlyUnavailableTargets", EndpointOutputListsOnlyUnavailableTargets); Run("EndpointOutputListsOnlyUnavailableTargets", EndpointOutputListsOnlyUnavailableTargets);
Run("EndpointOutputExplainsOnlyRealResolutionGaps", EndpointOutputExplainsOnlyRealResolutionGaps); Run("EndpointOutputExplainsOnlyRealResolutionGaps", EndpointOutputExplainsOnlyRealResolutionGaps);
Run("EndpointProbeBudgetFitsMinuteInterval", EndpointProbeBudgetFitsMinuteInterval); Run("EndpointProbeBudgetFitsMinuteInterval", EndpointProbeBudgetFitsMinuteInterval);
<<<<<<< HEAD
=======
Run("EnvironmentLabelDoesNotRenameServicesByDefault", EnvironmentLabelDoesNotRenameServicesByDefault);
Run("DefaultServiceContractIsExact", DefaultServiceContractIsExact);
Run("InstallerRejectsUnconfirmedServiceRename", InstallerRejectsUnconfirmedServiceRename);
Run("InstallerAllowsConfirmedServiceRename", InstallerAllowsConfirmedServiceRename);
>>>>>>> e694e4c (Guard Checkmk service renames during updates)
Run("InstallerUpdatePreservesExistingSettings", InstallerUpdatePreservesExistingSettings); Run("InstallerUpdatePreservesExistingSettings", InstallerUpdatePreservesExistingSettings);
Run("SnapshotRoundTripPreservesLines", SnapshotRoundTripPreservesLines); Run("SnapshotRoundTripPreservesLines", SnapshotRoundTripPreservesLines);
Run("SnapshotRejectsTampering", SnapshotRejectsTampering); Run("SnapshotRejectsTampering", SnapshotRejectsTampering);
@@ -683,6 +690,82 @@ namespace BizTalkCheckmkPulse.Tests
Assert(configuredMaximum <= 30000, "configured endpoint worst-case must leave headroom in the minute interval"); Assert(configuredMaximum <= 30000, "configured endpoint worst-case must leave headroom in the minute interval");
} }
<<<<<<< HEAD
=======
private static void EnvironmentLabelDoesNotRenameServicesByDefault()
{
var options = new MonitoringOptions { EnvironmentName = "ACC" };
var lines = new CheckmkLocalFormatter(options).FormatSelfTest().ToArray();
Assert(lines.Any(x => x.Contains("\"BizTalk Platform\"")), "stable service name without environment missing");
Assert(lines.All(x => x.IndexOf("\"BizTalk ACC ", StringComparison.Ordinal) < 0), "environment label must not rename services by default");
options.IncludeEnvironmentInServiceName = true;
lines = new CheckmkLocalFormatter(options).FormatSelfTest().ToArray();
Assert(lines.Any(x => x.Contains("\"BizTalk ACC Platform\"")), "explicit environment service-name opt-in missing");
}
private static void DefaultServiceContractIsExact()
{
var actual = new CheckmkLocalFormatter(new MonitoringOptions())
.FormatSelfTest()
.Select(ExtractQuotedServiceName)
.ToArray();
var expected = new[]
{
"BizTalk Platform",
"BizTalk SQL Access",
"BizTalk Suspended Instances",
"BizTalk Host Instances",
"BizTalk Receive Locations",
"BizTalk Send Ports",
"BizTalk Endpoint Reachability",
"BizTalk Orchestrations",
"BizTalk Event Log"
};
AssertEqual(string.Join("|", expected), string.Join("|", actual), "exact default service contract");
}
private static void InstallerRejectsUnconfirmedServiceRename()
{
try
{
BizTalkCheckmkPulse.Setup.InstallerEngine.EnsureServiceNameCompatibility(
new[] { "BizTalk Platform", "BizTalk Event Log" },
new[] { "BizTalk ACC Platform", "BizTalk ACC Event Log" },
false);
throw new InvalidOperationException("unconfirmed rename was accepted");
}
catch (InvalidOperationException ex)
{
Assert(ex.Message.Contains("Sicherheitsstopp"), "rename rejection must explain the safety stop");
Assert(ex.Message.Contains("Service Discovery"), "rename rejection must require discovery");
}
}
private static void InstallerAllowsConfirmedServiceRename()
{
var unchanged = BizTalkCheckmkPulse.Setup.InstallerEngine.EnsureServiceNameCompatibility(
new[] { "BizTalk Platform", "BizTalk Event Log" },
new[] { "BizTalk Event Log", "BizTalk Platform" },
false);
AssertEqual(string.Empty, unchanged, "service ordering must not be treated as rename");
var change = BizTalkCheckmkPulse.Setup.InstallerEngine.EnsureServiceNameCompatibility(
new[] { "BizTalk ACC Platform" },
new[] { "BizTalk Platform" },
true);
Assert(change.Contains("BizTalk ACC Platform"), "confirmed rename must report removed service");
Assert(change.Contains("BizTalk Platform"), "confirmed rename must report new service");
}
private static string ExtractQuotedServiceName(string line)
{
var start = line.IndexOf('"') + 1;
var end = line.IndexOf('"', start);
return line.Substring(start, end - start);
}
>>>>>>> e694e4c (Guard Checkmk service renames during updates)
private static void InstallerUpdatePreservesExistingSettings() private static void InstallerUpdatePreservesExistingSettings()
{ {
var directory = Path.Combine(Path.GetTempPath(), "BizTalkCheckmkPulse.InstallerTests." + Guid.NewGuid().ToString("N")); var directory = Path.Combine(Path.GetTempPath(), "BizTalkCheckmkPulse.InstallerTests." + Guid.NewGuid().ToString("N"));