Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions src/Particular.PlatformSample/AppLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ sealed class AppLauncher(bool showPlatformToolConsoleOutput) : IAsyncDisposable
readonly string platformPath = Path.Combine(AppContext.BaseDirectory, "platform");
readonly bool hideConsoleOutput = !showPlatformToolConsoleOutput;
readonly Stack<Func<ValueTask>> cleanupActions = new();
readonly List<PlatformTool> platformTools = [];

public sealed record PlatformTool(string Name, Process Process, StringBuilder ErrorOutput);

public void StopProcesses()
{
foreach (var tool in platformTools)
{
try
{
tool.Process.Kill(entireProcessTree: true);
}
catch (InvalidOperationException)
{
// The process has already exited
}
}
}

public List<PlatformTool> FindExitedTools() => platformTools.FindAll(tool => tool.Process.HasExited);

public async Task<Uri> RavenDB(string logsPath, string dataDirectory, CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -101,7 +121,7 @@ public void ServiceControl(int port, int maintenancePort, string logPath, string
return ValueTask.CompletedTask;
});

StartProcess(Path.Combine(platformPath, "servicecontrol", "servicecontrol-instance", "ServiceControl.dll"));
StartProcess("ServiceControl", Path.Combine(platformPath, "servicecontrol", "servicecontrol-instance", "ServiceControl.dll"));
}

public void ServiceControlAudit(int port, int maintenancePort, string logPath, string transportPath, Uri connectionString)
Expand All @@ -123,7 +143,7 @@ public void ServiceControlAudit(int port, int maintenancePort, string logPath, s
return ValueTask.CompletedTask;
});

StartProcess(Path.Combine(platformPath, "servicecontrol", "servicecontrol-audit-instance", "ServiceControl.Audit.dll"));
StartProcess("ServiceControl Audit", Path.Combine(platformPath, "servicecontrol", "servicecontrol-audit-instance", "ServiceControl.Audit.dll"));
}

public void Monitoring(int port, string logPath, string transportPath)
Expand All @@ -143,7 +163,7 @@ public void Monitoring(int port, string logPath, string transportPath)
return ValueTask.CompletedTask;
});

StartProcess(Path.Combine(platformPath, "servicecontrol", "monitoring-instance", "ServiceControl.Monitoring.dll"));
StartProcess("ServiceControl Monitoring", Path.Combine(platformPath, "servicecontrol", "monitoring-instance", "ServiceControl.Monitoring.dll"));
}

public void ServicePulse(int port, int serviceControlPort, int monitoringPort, string defaultRoute)
Expand All @@ -160,10 +180,10 @@ public void ServicePulse(int port, int serviceControlPort, int monitoringPort, s
environmentVariables.Add("DEFAULT_ROUTE", defaultRoute);
}

StartProcess(Path.Combine(platformPath, "servicepulse", "ServicePulse.dll"), environmentVariables);
StartProcess("ServicePulse", Path.Combine(platformPath, "servicepulse", "ServicePulse.dll"), environmentVariables);
}

void StartProcess(string assemblyPath, Dictionary<string, string> environmentVariables = null)
void StartProcess(string name, string assemblyPath, Dictionary<string, string> environmentVariables = null)
{
var workingDirectory = Path.GetDirectoryName(assemblyPath)!;
var startInfo = new ProcessStartInfo("dotnet")
Expand All @@ -190,13 +210,25 @@ void StartProcess(string assemblyPath, Dictionary<string, string> environmentVar
}

var process = Process.Start(startInfo);
var tool = new PlatformTool(name, process, new StringBuilder());

// without this in certain occasions the start takes waaaaay longer!
if (hideConsoleOutput)
{
// Keep the hidden error output so it can be shown if the tool fails to start
process.ErrorDataReceived += (_, args) =>
{
if (args.Data is not null)
{
tool.ErrorOutput.AppendLine(args.Data);
}
};

process.BeginErrorReadLine();
process.BeginOutputReadLine();
}

platformTools.Add(tool);
}

static string GetResource(string resourceName)
Expand Down
4 changes: 2 additions & 2 deletions src/Particular.PlatformSample/Network.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ static bool TestPort(int port)
}
}

public static async Task WaitForHttpOk(string url, int timeoutMilliseconds = 1000, CancellationToken cancellationToken = default)
public static async Task WaitForHttpOk(string url, int timeoutMilliseconds = 1000, Func<bool> stopWaiting = null, CancellationToken cancellationToken = default)
{
var status = HttpStatusCode.Ambiguous;
var c = new HttpClient();

while (!cancellationToken.IsCancellationRequested && status != HttpStatusCode.OK)
while (!cancellationToken.IsCancellationRequested && status != HttpStatusCode.OK && stopWaiting?.Invoke() != true)
{
try
{
Expand Down
18 changes: 17 additions & 1 deletion src/Particular.PlatformSample/PlatformLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,25 @@ public static async Task Launch(bool showPlatformToolConsoleOutput = false, stri
launcher.ServicePulse(pulsePort, controlPort, monitoringPort, servicePulseDefaultRoute);

Console.Write("Waiting for ServiceControl to be available");
await Network.WaitForHttpOk($"http://localhost:{controlPort}/api", cancellationToken: tokenSource.Token)
await Network.WaitForHttpOk($"http://localhost:{controlPort}/api", stopWaiting: () => launcher.FindExitedTools().Count > 0, cancellationToken: tokenSource.Token)
.ConfigureAwait(false);

// A platform tool exited on its own (not because of Ctrl+C), for example because the .NET runtime version it requires is not installed
var failedTools = launcher.FindExitedTools();
if (failedTools.Count > 0 && !tokenSource.IsCancellationRequested)
{
foreach (var failedTool in failedTools)
{
// HasExited can be true before all the error output has been read; this waits for the rest
await failedTool.Process.WaitForExitAsync(CancellationToken.None).ConfigureAwait(false);
Console.WriteLine($"{failedTool.Name} failed to start:");
Console.WriteLine(failedTool.ErrorOutput.ToString());
}

launcher.StopProcesses();
return;
}

if (!tokenSource.IsCancellationRequested)
{
var serviceControlApiUrl = $"http://localhost:{controlPort}/api";
Expand Down
Loading