diff --git a/src/ServiceControl.Audit.UnitTests/PopulateAppSettingsTests.cs b/src/ServiceControl.Audit.UnitTests/PopulateAppSettingsTests.cs new file mode 100644 index 0000000000..294e40cd78 --- /dev/null +++ b/src/ServiceControl.Audit.UnitTests/PopulateAppSettingsTests.cs @@ -0,0 +1,74 @@ +namespace ServiceControl.UnitTests; + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using NUnit.Framework; + +[TestFixture] +public class PopulateAppSettingsTests +{ + [Test] + public async Task Should_populate_appSettings_from_exe_config_file() + { + const string MagicValue = "7303A0AA-1003-4DC4-823B-4E8B2A35CF57"; + + var config = $""" + + + + + + + """; + + await File.WriteAllTextAsync("ServiceControl.Audit.exe.config", config); + + var fileName = "ServiceControl.Audit"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName = "ServiceControl.Audit.exe"; + } + + var startInfo = new ProcessStartInfo(fileName) { RedirectStandardOutput = true, UseShellExecute = false }; + + var p = Process.Start(startInfo); + + if (p == null) + { + throw new Exception($"Failed to start {fileName}"); + } + + var pathIsSet = false; + + var outputTask = Task.Run(async () => + { + while (!p.StandardOutput.EndOfStream) + { + var line = await p.StandardOutput.ReadLineAsync(); + + Console.WriteLine(line); + + if (line.Contains($"Logging to {MagicValue}")) + { + pathIsSet = true; + p.Kill(true); + } + } + }); + + if (!p.WaitForExit(5000)) + { + p.Kill(true); + } + + await outputTask; + + Assert.That(pathIsSet, Is.True); + } + + [TearDown] + public void TearDown() => File.Delete("ServiceControl.exe.config"); +} \ No newline at end of file diff --git a/src/ServiceControl.Audit/Program.cs b/src/ServiceControl.Audit/Program.cs index dbe5268e93..763329c2f6 100644 --- a/src/ServiceControl.Audit/Program.cs +++ b/src/ServiceControl.Audit/Program.cs @@ -11,6 +11,8 @@ try { + ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); + var loggingSettings = new LoggingSettings(Settings.SettingsRootNamespace); LoggingConfigurator.ConfigureLogging(loggingSettings); logger = LoggerUtil.CreateStaticLogger(typeof(Program)); @@ -25,7 +27,6 @@ return exitCode; } - ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); var arguments = new HostArguments(args); diff --git a/src/ServiceControl.Configuration/ExeConfiguration.cs b/src/ServiceControl.Configuration/ExeConfiguration.cs index 2f0b0f3c5b..671d490ab3 100644 --- a/src/ServiceControl.Configuration/ExeConfiguration.cs +++ b/src/ServiceControl.Configuration/ExeConfiguration.cs @@ -1,5 +1,6 @@ namespace ServiceControl.Configuration { + using System; using System.Configuration; using System.IO; using System.Linq; @@ -12,27 +13,37 @@ public static class ExeConfiguration // This code reads in the exe.config files and adds all the values into the ConfigurationManager's collections. public static void PopulateAppSettings(Assembly assembly) { - var location = Path.GetDirectoryName(assembly.Location); - var assemblyName = Path.GetFileNameWithoutExtension(assembly.Location); - var exeConfigPath = Path.Combine(location, $"{assemblyName}.exe.config"); - var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = exeConfigPath }; - var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); - - foreach (var key in configuration.AppSettings.Settings.AllKeys) + try { - ConfigurationManager.AppSettings.Set(key, configuration.AppSettings.Settings[key].Value); + var location = Path.GetDirectoryName(assembly.Location); + var assemblyName = Path.GetFileNameWithoutExtension(assembly.Location); + var exeConfigPath = Path.Combine(location, $"{assemblyName}.exe.config"); + var fileMap = new ExeConfigurationFileMap + { + ExeConfigFilename = exeConfigPath + }; + var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); + + foreach (var key in configuration.AppSettings.Settings.AllKeys) + { + ConfigurationManager.AppSettings.Set(key, configuration.AppSettings.Settings[key].Value); + } + + // The connection strings collection has had its read only flag set, so we need to clear it before we can add items to it + UnsetCollectionReadonly(ConfigurationManager.ConnectionStrings); + + foreach (var connectionStringSetting in configuration.ConnectionStrings.ConnectionStrings.Cast()) + { + ConfigurationManager.ConnectionStrings.Add(connectionStringSetting); + } + + // Put the collection back into its previous state after we're done adding items to it + SetCollectionReadOnly(ConfigurationManager.ConnectionStrings); } - - // The connection strings collection has had its read only flag set, so we need to clear it before we can add items to it - UnsetCollectionReadonly(ConfigurationManager.ConnectionStrings); - - foreach (var connectionStringSetting in configuration.ConnectionStrings.ConnectionStrings.Cast()) + catch (Exception e) { - ConfigurationManager.ConnectionStrings.Add(connectionStringSetting); + throw new Exception("Failed to populate app settings.", e); } - - // Put the collection back into its previous state after we're done adding items to it - SetCollectionReadOnly(ConfigurationManager.ConnectionStrings); } static void UnsetCollectionReadonly(ConfigurationElementCollection collection) @@ -47,4 +58,4 @@ static void UnsetCollectionReadonly(ConfigurationElementCollection collection) [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetReadOnly")] static extern void SetCollectionReadOnly(ConfigurationElementCollection collection); } -} +} \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.UnitTests/PopulateAppSettingsTests.cs b/src/ServiceControl.Monitoring.UnitTests/PopulateAppSettingsTests.cs new file mode 100644 index 0000000000..b7abd42264 --- /dev/null +++ b/src/ServiceControl.Monitoring.UnitTests/PopulateAppSettingsTests.cs @@ -0,0 +1,78 @@ +namespace ServiceControl.UnitTests; + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using NUnit.Framework; + +[TestFixture] +public class PopulateAppSettingsTests +{ + [Test] + public async Task Should_populate_appSettings_from_exe_config_file() + { + const string MagicValue = "7303A0AA-1003-4DC4-823B-4E8B2A35CF57"; + + var config = $""" + + + + + + + """; + + await File.WriteAllTextAsync("ServiceControl.Monitoring.exe.config", config); + + var fileName = "ServiceControl.Monitoring"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName = "ServiceControl.Monitoring.exe"; + } + + var startInfo = new ProcessStartInfo(fileName) + { + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var p = Process.Start(startInfo); + + if (p == null) + { + throw new Exception($"Failed to start {fileName}"); + } + + var pathIsSet = false; + + var outputTask = Task.Run(async () => + { + while (!p.StandardOutput.EndOfStream) + { + var line = await p.StandardOutput.ReadLineAsync(); + + Console.WriteLine(line); + + if (line.Contains($"Logging to {MagicValue}")) + { + pathIsSet = true; + p.Kill(true); + } + } + }); + + if (!p.WaitForExit(5000)) + { + p.Kill(true); + } + + await outputTask; + + Assert.That(pathIsSet, Is.True); + } + + [TearDown] + public void TearDown() => File.Delete("ServiceControl.exe.config"); +} \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/Program.cs b/src/ServiceControl.Monitoring/Program.cs index e8dce81512..81393c0c61 100644 --- a/src/ServiceControl.Monitoring/Program.cs +++ b/src/ServiceControl.Monitoring/Program.cs @@ -9,6 +9,8 @@ try { + ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); + var loggingSettings = new LoggingSettings(Settings.SettingsRootNamespace); LoggingConfigurator.ConfigureLogging(loggingSettings); logger = LoggerUtil.CreateStaticLogger(typeof(Program)); @@ -23,8 +25,6 @@ return exitCode; } - ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); - var arguments = new HostArguments(args); var settings = new Settings(loggingSettings: loggingSettings); diff --git a/src/ServiceControl.UnitTests/PopulateAppSettingsTests.cs b/src/ServiceControl.UnitTests/PopulateAppSettingsTests.cs new file mode 100644 index 0000000000..28b5e3e088 --- /dev/null +++ b/src/ServiceControl.UnitTests/PopulateAppSettingsTests.cs @@ -0,0 +1,76 @@ +namespace ServiceControl.UnitTests; + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using NUnit.Framework; + +[TestFixture] +public class PopulateAppSettingsTests +{ + [Test] + public async Task Should_populate_appSettings_from_exe_config_file() + { + const string MagicValue = "7303A0AA-1003-4DC4-823B-4E8B2A35CF57"; + + var config = $""" + + + + + + + """; + + await File.WriteAllTextAsync("ServiceControl.exe.config", config); + + var fileName = "ServiceControl"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName = "ServiceControl.exe"; + } + + var startInfo = new ProcessStartInfo(fileName) + { + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var p = Process.Start(startInfo); + + if (p == null) + { + throw new Exception("Failed to start ServiceControl"); + } + + var pathIsSet = false; + + var outputTask = Task.Run(async () => + { + while (!p.StandardOutput.EndOfStream) + { + var line = await p.StandardOutput.ReadLineAsync(); + + if (line.Contains($"Logging to {MagicValue}")) + { + pathIsSet = true; + p.Kill(true); + } + } + }); + + if (!p.WaitForExit(5000)) + { + p.Kill(true); + } + + await outputTask; + + Assert.That(pathIsSet, Is.True); + } + + [TearDown] + public void TearDown() => File.Delete("ServiceControl.exe.config"); +} \ No newline at end of file diff --git a/src/ServiceControl/Program.cs b/src/ServiceControl/Program.cs index ec4a0bc70d..857ed0a782 100644 --- a/src/ServiceControl/Program.cs +++ b/src/ServiceControl/Program.cs @@ -11,6 +11,8 @@ try { + ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); + var loggingSettings = new LoggingSettings(Settings.SettingsRootNamespace); LoggingConfigurator.ConfigureLogging(loggingSettings); logger = LoggerUtil.CreateStaticLogger(typeof(Program)); @@ -25,8 +27,6 @@ return exitCode; } - ExeConfiguration.PopulateAppSettings(Assembly.GetExecutingAssembly()); - var arguments = new HostArguments(args); if (arguments.Help)