286 lines
11 KiB
C#
286 lines
11 KiB
C#
using Microsoft.VisualBasic.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace eSuite.WorkBench.Services
|
|
{
|
|
public class CommandsService : ICommandsService
|
|
{
|
|
private static readonly List<string> containerNames = new() { "esuite.Database.Migrator", "esuite.API", "esuite.WebUI", "esuite.messageprocessor", "esuite.scheduler" };
|
|
|
|
public CommandsService()
|
|
{
|
|
var p = new Process
|
|
{
|
|
StartInfo =
|
|
{
|
|
FileName = "docker",
|
|
Arguments = BuildLoginCommand(),
|
|
RedirectStandardError = true,
|
|
RedirectStandardOutput = true,
|
|
CreateNoWindow = true,
|
|
}
|
|
};
|
|
|
|
p.Start();
|
|
p.WaitForExit();
|
|
}
|
|
|
|
public event EventHandler<FeedbackEventArgs> FeedbackMessage;
|
|
protected void DoFeedbackMessage(string message)
|
|
{
|
|
if (FeedbackMessage != null)
|
|
{
|
|
var feedbackEventArgs = new FeedbackEventArgs
|
|
{
|
|
Message = message
|
|
};
|
|
|
|
|
|
FeedbackMessage(this, feedbackEventArgs);
|
|
}
|
|
}
|
|
|
|
private static async Task PullImageAsync(string imageName, string tag)
|
|
{
|
|
await RunCommandAsync("docker", $"pull {imageName}:{tag}", createWindow:true, useShellExecute:true);
|
|
}
|
|
|
|
public async Task StartProxyContainerAsync(string tag)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting Proxy container");
|
|
await PullImageAsync("esuite.azurecr.io/e-suite.api", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.Proxy -d -p3001:80 --env SERVER_NAME=localhost --env API_URL=http://host.docker.internal:7066 --env WEBUI_URL=http://host.docker.internal:3000 esuite.azurecr.io/e-suite.proxy:{tag}");
|
|
DoFeedbackMessage("Started API container");
|
|
}
|
|
|
|
public async Task StartApiContainerAsync(string tag, string databaseName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting API container");
|
|
await PullImageAsync("esuite.azurecr.io/e-suite.api", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.API -d -p7066:80 --env BASE_URL=http://localhost:3001/ --env SQL_SERVER=host.docker.internal --env SQL_DATABASE=esuite_{databaseName} --env SQL_USER=esuite_{databaseName}ApplicationUser --env MAIL_SERVER=host.docker.internal --env RABBITMQ_HOSTNAME=host.docker.internal esuite.azurecr.io/e-suite.api:{tag}");
|
|
DoFeedbackMessage("Started API container");
|
|
}
|
|
|
|
public async Task StartWebUiContainerAsync(string tag)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting WebUI container");
|
|
await PullImageAsync("esuite.azurecr.io/e-suite.webui", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.WebUI -d -p3000:80 --env NODE_ENV=production --env API_URL=http://localhost:3001/api esuite.azurecr.io/e-suite.webui:{tag}");
|
|
DoFeedbackMessage("Started WebUI container");
|
|
}
|
|
|
|
public async Task StartDatabaseMigratorContainerAsync(string tag, string databaseName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting database migrator container");
|
|
await PullImageAsync("esuite.azurecr.io/esuite.database.migrator", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.Database.Migrator --rm --env SQL_SERVER=host.docker.internal --env SQL_DATABASE=esuite_{databaseName} --env SQL_USER={Properties.Settings.Default.SqlServerAdminLogin} --env SQL_PASSWORD={Properties.Settings.Default.SqlServerAdminPassword} --env SQL_APPLICATION_USER=esuite_{databaseName}ApplicationUser esuite.azurecr.io/e-suite.database.migrator:{tag}");
|
|
DoFeedbackMessage("Started database migrator container");
|
|
}
|
|
|
|
public async Task StartRabbitMQContainerAsync()
|
|
{
|
|
DoFeedbackMessage("Starting RabbitMQ");
|
|
await PullImageAsync("rabbitmq", "management");
|
|
await RunCommandAsync("docker", $"run --name RabbitMQ -d -p8080:15672 -p5672:5672 --restart always rabbitmq:management");
|
|
DoFeedbackMessage("Started RabbitMQ");
|
|
}
|
|
|
|
public async Task StartSchedulerContainerAsync(string tag, string databaseName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting e-suite.scheduler");
|
|
await PullImageAsync("esuite.azurecr.io/e-suite.scheduler", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.scheduler -d --env SQL_SERVER=host.docker.internal --env SQL_DATABASE=esuite_{databaseName} --env SQL_USER=esuite_{databaseName}ApplicationUser --env RABBITMQ_HOSTNAME=host.docker.internal esuite.azurecr.io/e-suite.scheduler:{tag}");
|
|
DoFeedbackMessage("Started e-suite.scheduler");
|
|
}
|
|
|
|
public async Task StartMessageProcessorContainerAsync(string tag, string databaseName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag))
|
|
return;
|
|
|
|
DoFeedbackMessage("Starting esuite.messageprocessor");
|
|
await PullImageAsync("esuite.azurecr.io/e-suite.messageprocessor", tag);
|
|
await RunCommandAsync("docker", $"run --name esuite.messageprocessor -d --env SQL_SERVER=host.docker.internal --env SQL_DATABASE=esuite_{databaseName} --env SQL_USER=esuite_{databaseName}ApplicationUser --env RABBITMQ_HOSTNAME=host.docker.internal esuite.azurecr.io/e-suite.messageprocessor:{tag}");
|
|
DoFeedbackMessage("Started esuite.messageprocessor");
|
|
}
|
|
|
|
public async Task<bool> IsAnyContainerRunningAsync()
|
|
{
|
|
var runningContainers = (await RunCommandAsync("docker", "container ls -a --format \"{{.Names}}\"")).StdOut;
|
|
return containerNames.Any(x => runningContainers.Contains(x));
|
|
}
|
|
|
|
public bool IsAnyContainerRunning()
|
|
{
|
|
var startInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "docker",
|
|
Arguments = "container ls -a --format \"{{.Names}}\"",
|
|
CreateNoWindow = true,
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
};
|
|
|
|
var process = Process.Start(startInfo);
|
|
var outputBuilder = new StringBuilder();
|
|
|
|
process.OutputDataReceived += (sender, e) =>
|
|
{
|
|
if (e.Data == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
outputBuilder.AppendLine(e.Data);
|
|
};
|
|
|
|
process.BeginOutputReadLine();
|
|
process.WaitForExit();
|
|
|
|
var runningContainers = outputBuilder.ToString();
|
|
return containerNames.Any(x => runningContainers.Contains(x));
|
|
}
|
|
|
|
public async Task ConnectToContainerRegistry()
|
|
{
|
|
var loginCommand =
|
|
BuildLoginCommand();
|
|
await RunCommandAsync("docker", loginCommand);
|
|
}
|
|
|
|
private static string BuildLoginCommand()
|
|
{
|
|
return $"login -u {ConfigurationManager.AppSettings["AzureContainerRegistryUserName"]} -p {ConfigurationManager.AppSettings["AzureContainerRegistryPassword"]} esuite.azurecr.io";
|
|
}
|
|
|
|
public async Task LaunchSwagger()
|
|
{
|
|
await RunCommandAsync("http://localhost:3001/swagger", useShellExecute: true, launchAndForget: true);
|
|
}
|
|
|
|
public async Task LaunchWebUi()
|
|
{
|
|
await RunCommandAsync("http://localhost:3001/", useShellExecute: true, launchAndForget: true);
|
|
}
|
|
|
|
public async Task LaunchHealthZ()
|
|
{
|
|
await RunCommandAsync("http://localhost:3001/healthz", useShellExecute: true, launchAndForget: true);
|
|
}
|
|
|
|
public async Task StopContainersAsync()
|
|
{
|
|
//Note RabbitMQ is left running, this is to make it easy for developers not to have to run the command.
|
|
await RunCommandAsync("docker", "stop -t 2 esuite.API esuite.WebUI esuite.scheduler esuite.messageprocessor esuite.Proxy RabbitMQ");
|
|
await RunCommandAsync("docker", "rm esuite.API esuite.WebUI esuite.scheduler esuite.messageprocessor esuite.Proxy RabbitMQ");
|
|
}
|
|
|
|
private static async Task<RunCommandResult> RunCommandAsync(string command, string arguments = null, TimeSpan? timeout = null, bool createWindow = false, bool useShellExecute = false, bool launchAndForget = false)
|
|
{
|
|
if (timeout == null)
|
|
{
|
|
timeout = TimeSpan.FromSeconds(300);
|
|
}
|
|
|
|
var startInfo = new ProcessStartInfo
|
|
{
|
|
Arguments = arguments ?? string.Empty,
|
|
CreateNoWindow = !createWindow,
|
|
FileName = command,
|
|
RedirectStandardError = !useShellExecute,
|
|
RedirectStandardOutput = !useShellExecute,
|
|
UseShellExecute = useShellExecute
|
|
};
|
|
|
|
var process = Process.Start(startInfo);
|
|
var errorBuilder = new StringBuilder();
|
|
var outputBuilder = new StringBuilder();
|
|
|
|
process.ErrorDataReceived += (sender, e) =>
|
|
{
|
|
if (e.Data == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (errorBuilder)
|
|
{
|
|
errorBuilder.AppendLine(e.Data);
|
|
}
|
|
};
|
|
|
|
process.OutputDataReceived += (sender, e) =>
|
|
{
|
|
if (e.Data == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (outputBuilder)
|
|
{
|
|
outputBuilder.AppendLine(e.Data);
|
|
}
|
|
};
|
|
|
|
if (!useShellExecute)
|
|
{
|
|
process.BeginOutputReadLine();
|
|
process.BeginErrorReadLine();
|
|
}
|
|
|
|
if (!launchAndForget)
|
|
{
|
|
await Task.Run(() => process.WaitForExit((int)timeout.Value.TotalMilliseconds));
|
|
|
|
return new RunCommandResult
|
|
{
|
|
ExitCode = process.ExitCode,
|
|
StdErr = errorBuilder.ToString(),
|
|
StdOut = outputBuilder.ToString()
|
|
};
|
|
}
|
|
|
|
return new RunCommandResult
|
|
{
|
|
ExitCode = 0,
|
|
StdErr = string.Empty,
|
|
StdOut = string.Empty
|
|
};
|
|
|
|
}
|
|
|
|
private class RunCommandResult
|
|
{
|
|
public int ExitCode { get; set; }
|
|
|
|
public string StdErr { get; set; }
|
|
|
|
public string StdOut { get; set; }
|
|
|
|
public bool Success => ExitCode == 0;
|
|
}
|
|
}
|
|
}
|