Make installer runtime acceptance transactional
This commit is contained in:
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace BizTalkCheckmkPulse.Setup
|
||||
@@ -14,6 +15,7 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
internal sealed class InstallerEngine
|
||||
{
|
||||
internal const string TaskName = "BizTalk Checkmk Pulse Provider";
|
||||
private static readonly TimeSpan PostInstallValidationTimeout = TimeSpan.FromMinutes(4);
|
||||
private readonly string packageDirectory;
|
||||
private readonly string installDirectory;
|
||||
private readonly string runtimeDirectory;
|
||||
@@ -54,7 +56,8 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
var targetWrapper = Path.Combine(checkmkLocalDirectory, "biztalk_checkmk_pulse.cmd");
|
||||
var previousWrapper = File.Exists(targetWrapper) ? File.ReadAllBytes(targetWrapper) : null;
|
||||
var hadExistingInstallation = Directory.Exists(installDirectory);
|
||||
var taskRemoved = false;
|
||||
var hadExistingExecutable = File.Exists(targetExe);
|
||||
var mutationStarted = false;
|
||||
var backupCreated = false;
|
||||
var filesActivated = false;
|
||||
var scheduler = new TaskSchedulerService();
|
||||
@@ -94,8 +97,8 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
}
|
||||
|
||||
// Erst nach vollstaendiger Staging-Pruefung wird der laufende Provider angehalten.
|
||||
mutationStarted = true;
|
||||
scheduler.DeleteIfExists(TaskName);
|
||||
taskRemoved = true;
|
||||
report("Vorhandener Scheduled Task angehalten und fuer das Update entfernt.");
|
||||
|
||||
if (hadExistingInstallation)
|
||||
@@ -129,28 +132,53 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
RunSelfTest(targetExe);
|
||||
report("Installierter Self-Test erfolgreich: neun Checkmk-Services.");
|
||||
|
||||
scheduler.RegisterAndStart(
|
||||
var runtimeValidationStartedUtc = DateTime.UtcNow;
|
||||
scheduler.RegisterValidationAndStart(
|
||||
TaskName,
|
||||
targetExe,
|
||||
installDirectory,
|
||||
account,
|
||||
isGmsa ? null : password,
|
||||
isGmsa);
|
||||
taskRemoved = false;
|
||||
report("Scheduled Task registriert und einmalig gestartet: " + TaskName);
|
||||
report("Einmaliger Provider-Abnahmelauf mit erzwungenem Endpoint-Katalogabgleich gestartet.");
|
||||
|
||||
var completedRun = scheduler.WaitForSuccessfulRun(
|
||||
TaskName,
|
||||
runtimeValidationStartedUtc,
|
||||
PostInstallValidationTimeout,
|
||||
report);
|
||||
report("Provider-Abnahmelauf erfolgreich: LastTaskResult=" + completedRun.ExitCode + ".");
|
||||
|
||||
var runtimeValidation = RunRuntimeValidation(
|
||||
targetExe,
|
||||
runtimeValidationStartedUtc,
|
||||
account);
|
||||
report("Post-Install-Runtime-Abnahme erfolgreich: " + runtimeValidation);
|
||||
|
||||
scheduler.RegisterRecurring(
|
||||
TaskName,
|
||||
targetExe,
|
||||
installDirectory,
|
||||
account,
|
||||
isGmsa ? null : password,
|
||||
isGmsa);
|
||||
report("Validierter minuetlicher Scheduled Task registriert: " + TaskName);
|
||||
|
||||
TryDeleteDirectory(backupDirectory, report);
|
||||
}
|
||||
catch (Exception installException)
|
||||
{
|
||||
var rollbackFailures = new List<string>();
|
||||
try
|
||||
if (mutationStarted)
|
||||
{
|
||||
scheduler.DeleteIfExists(TaskName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rollbackFailures.Add("Task stoppen: " + ex.Message);
|
||||
try
|
||||
{
|
||||
scheduler.DeleteIfExists(TaskName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rollbackFailures.Add("Task stoppen: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (filesActivated || backupCreated)
|
||||
@@ -167,27 +195,36 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RestoreWrapper(targetWrapper, previousWrapper);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rollbackFailures.Add("Checkmk-Wrapper wiederherstellen: " + ex.Message);
|
||||
}
|
||||
|
||||
if (taskRemoved && hadExistingInstallation && File.Exists(targetExe))
|
||||
if (mutationStarted)
|
||||
{
|
||||
try
|
||||
{
|
||||
scheduler.RegisterAndStart(
|
||||
RestoreWrapper(targetWrapper, previousWrapper);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rollbackFailures.Add("Checkmk-Wrapper wiederherstellen: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (mutationStarted && hadExistingExecutable && File.Exists(targetExe))
|
||||
{
|
||||
try
|
||||
{
|
||||
var rollbackRunStartedUtc = DateTime.UtcNow;
|
||||
scheduler.RegisterRecurringAndStart(
|
||||
TaskName,
|
||||
targetExe,
|
||||
installDirectory,
|
||||
account,
|
||||
isGmsa ? null : password,
|
||||
isGmsa);
|
||||
report("Scheduled Task fuer die vorherige Version wiederhergestellt.");
|
||||
scheduler.WaitForSuccessfulRun(
|
||||
TaskName,
|
||||
rollbackRunStartedUtc,
|
||||
PostInstallValidationTimeout,
|
||||
report);
|
||||
report("Scheduled Task und frischer Snapshot der vorherigen Version wiederhergestellt.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -445,6 +482,45 @@ namespace BizTalkCheckmkPulse.Setup
|
||||
}
|
||||
}
|
||||
|
||||
private static string RunRuntimeValidation(
|
||||
string executable,
|
||||
DateTime notBeforeUtc,
|
||||
string expectedIdentity)
|
||||
{
|
||||
var identityBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(expectedIdentity));
|
||||
var arguments = "--validate-runtime --validation-not-before-utc "
|
||||
+ notBeforeUtc.ToUniversalTime().ToString("o")
|
||||
+ " --expected-identity-base64 " + identityBase64;
|
||||
var start = new ProcessStartInfo(executable, arguments)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
using (var process = Process.Start(start))
|
||||
{
|
||||
if (process == null) throw new InvalidOperationException("Runtime-Abnahme konnte nicht gestartet werden.");
|
||||
var output = process.StandardOutput.ReadToEnd().Trim();
|
||||
var error = process.StandardError.ReadToEnd().Trim();
|
||||
if (!process.WaitForExit(30000))
|
||||
{
|
||||
process.Kill();
|
||||
throw new TimeoutException("Runtime-Abnahme hat das Zeitlimit ueberschritten.");
|
||||
}
|
||||
|
||||
if (process.ExitCode != 0
|
||||
|| !output.StartsWith("RUNTIME_VALIDATION_V1 ", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Runtime-Abnahme fehlgeschlagen. Exitcode=" + process.ExitCode
|
||||
+ ". " + (error.Length == 0 ? output : error));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractServiceName(string line)
|
||||
{
|
||||
var firstQuote = line.IndexOf('"');
|
||||
|
||||
Reference in New Issue
Block a user