Skip to content

Instantly share code, notes, and snippets.

@mburumaxwell
Created March 27, 2020 16:23
Show Gist options
  • Save mburumaxwell/1eb7f26d4edd32346f5532fe70175566 to your computer and use it in GitHub Desktop.
Save mburumaxwell/1eb7f26d4edd32346f5532fe70175566 to your computer and use it in GitHub Desktop.
NGINX for Service Fabric
internal sealed class NginxWrapper : StatelessService
{
public NginxWrapper(StatelessServiceContext context) : base(context) { }
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// listen to changes in configuration
Context.CodePackageActivationContext.ConfigurationPackageModifiedEvent += OnConfigurationPackageModified;
ServiceEventSource.Current.ServiceMessage(Context, "Service started. Configuration changes listener registered");
while (true)
{
// if cancellation has been requested, attempt shutdown gracefully but force if it fails
if (cancellationToken.IsCancellationRequested)
{
ServiceEventSource.Current.ServiceMessage(Context, "Cancellation requested. Graceful shutdown started.");
GracefulShutdown(); // attempt graceful shutdown
// gracefully shutdown the process within 5 seconds
for (var t = 0; t < 5; t++)
{
if (!IsRunning) break; // stop looping if it is no longer running
await Task.Delay(TimeSpan.FromSeconds(1));
}
// if the process is stil running forcefully close
if (IsRunning) KillProcesses();
return; // nothing more todo
}
// ensure the process is still running incase it had been mistakenly dropped
if (!IsRunning)
{
ServiceEventSource.Current.ServiceMessage(
Context,
"Either the process was manually stopped or had not be started. Starting the process ...");
SetupAndValidatePaths();
StartProcess();
}
// wait a little while before working to prevent over tasking the cpu
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
private string ConfigurationFilePath { get; set; }
private string ExecutableFilePath { get; set; }
private string RunningProcessName { get; set; }
private bool IsRunning => !string.IsNullOrWhiteSpace(RunningProcessName) && Process.GetProcessesByName(RunningProcessName).Any();
private void OnConfigurationPackageModified(object sender, PackageModifiedEventArgs<ConfigurationPackage> e)
{
ServiceEventSource.Current.ServiceMessage(Context, "Configuration package was changed. Process shall be restarted");
GracefulShutdown();
KillProcesses(); // kill any pending proceses
SetupAndValidatePaths();
StartProcess();
}
private void SetupAndValidatePaths()
{
var configPackage = Context.CodePackageActivationContext.GetConfigurationPackageObject("Config");
var parameters = configPackage.Settings.Sections["Nginx"].Parameters;
var configurationFileName = parameters["ConfigurationFileName"].Value;
var executableFileName = parameters["ExecutableFileName"].Value;
var codePackageName = Context.CodePackageActivationContext.CodePackageName;
ExecutableFilePath = Path.Combine(
Context.CodePackageActivationContext.GetCodePackageObject(codePackageName).Path,
executableFileName);
ConfigurationFilePath = Path.Combine(
configPackage.Path,
configurationFileName);
ServiceEventSource.Current.ServiceMessage(
Context,
"Setting up service with. {0}={1} and {2}={3}",
nameof(ConfigurationFilePath),
ConfigurationFilePath,
nameof(ExecutableFilePath),
ExecutableFilePath);
if (!File.Exists(ExecutableFilePath))
throw new FileNotFoundException("The specified executable file must exist", ExecutableFilePath);
if (!File.Exists(ConfigurationFilePath))
throw new FileNotFoundException("The specified configuration file must exist", ConfigurationFilePath);
}
private void KillProcesses()
{
if (!string.IsNullOrWhiteSpace(RunningProcessName))
{
var processes = Process.GetProcessesByName(RunningProcessName);
ServiceEventSource.Current.ServiceMessage(
Context,
"Found {0} processes with name {1} to forcefully close.",
processes.Length,
RunningProcessName);
foreach (var p in processes)
{
ServiceEventSource.Current.ServiceMessage(Context, "Killing process with Id: {0}", p.Id);
p.Kill();
}
}
}
private void StartProcess() => LaunchProcess("-c " + ConfigurationFilePath);
private void GracefulShutdown() => LaunchProcess("-c " + ConfigurationFilePath + " -s quit");
private void LaunchProcess(string arguments)
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = ExecutableFilePath,
WorkingDirectory = Path.GetDirectoryName(ExecutableFilePath),
UseShellExecute = false,
Arguments = arguments,
},
};
ServiceEventSource.Current.ServiceMessage(
Context,
"Starting process {0} at {1} with arguments {2}",
Path.GetFileName(process.StartInfo.FileName),
process.StartInfo.FileName,
process.StartInfo.Arguments);
process.Start();
RunningProcessName = process.ProcessName;
ServiceEventSource.Current.ServiceMessage(Context, "Service started with name: {0}", RunningProcessName);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment