diff --git a/docs/changelog.md b/docs/changelog.md index 7aea4128f..68ef2ff73 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -17,6 +17,44 @@ # Changelog +## 0.4.0 + +[GitHub Milestone 0.4.0](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/5) + +- gh-435 Fix CLI to read log dir path from NLog config file. + + +## 0.3.21 + +[GitHub Milestone 0.3.21](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/26) + +- Remove the need to double copy files to storage service. + +## 0.3.20 + +[GitHub Milestone 0.3.20](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/25) + +- gh-396 Spawn new thread for processing echo requests. + +## 0.3.19 + +[GitHub Milestone 0.3.19](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/24) + +- gh-392 Fix GET /config/aetitle URL. + +## 0.3.18 + +[GitHub Milestone 0.3.18](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/23) + +- gh-390 New API to retrieve registered source AETs. + +## 0.3.11 + +[GitHub Milestone 0.3.17](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/22) + +- gh-385 Resets ActionBlock if faulted or cancelled in Payload assembler pipeline. + + ## 0.3.16 [GitHub Milestone 0.3.16](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/21) diff --git a/src/CLI/Logging/Log.cs b/src/CLI/Logging/Log.cs index 9c1a3d43f..818b6bd5f 100644 --- a/src/CLI/Logging/Log.cs +++ b/src/CLI/Logging/Log.cs @@ -133,8 +133,8 @@ public static partial class Log [LoggerMessage(EventId = 30043, Level = LogLevel.Debug, Message = "Available manifest names {names}.")] public static partial void AvailableManifest(this ILogger logger, string names); - [LoggerMessage(EventId = 30044, Level = LogLevel.Information, Message = "Saving appsettings.json to {path}.")] - public static partial void SaveAppSettings(this ILogger logger, string path); + [LoggerMessage(EventId = 30044, Level = LogLevel.Information, Message = "Saving {resource} to {path}.")] + public static partial void SaveAppSettings(this ILogger logger, string resource, string path); [LoggerMessage(EventId = 30045, Level = LogLevel.Information, Message = "{path} updated successfully.")] public static partial void AppSettingUpdated(this ILogger logger, string path); diff --git a/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj b/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj index c8ac72782..fc2a72c16 100644 --- a/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj +++ b/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj @@ -51,6 +51,7 @@ + diff --git a/src/CLI/Options/Common.cs b/src/CLI/Options/Common.cs index 41ab89436..7fc50b35d 100644 --- a/src/CLI/Options/Common.cs +++ b/src/CLI/Options/Common.cs @@ -25,9 +25,12 @@ public static class Common public static readonly string MigDirectory = Path.Combine(HomeDir, ".mig"); public static readonly string ContainerApplicationRootPath = "/opt/monai/ig"; public static readonly string MountedConfigFilePath = Path.Combine(ContainerApplicationRootPath, "appsettings.json"); + public static readonly string MountedNLogConfigFilePath = Path.Combine(ContainerApplicationRootPath, "nlog.config"); public static readonly string MountedDatabasePath = "/database"; public static readonly string MountedPlugInsPath = Path.Combine(ContainerApplicationRootPath, "plug-ins"); public static readonly string ConfigFilePath = Path.Combine(MigDirectory, "appsettings.json"); + public static readonly string NLogConfigFilePath = Path.Combine(MigDirectory, "nlog.config"); public static readonly string AppSettingsResourceName = $"{typeof(Program).Namespace}.Resources.appsettings.json"; + public static readonly string NLogConfigResourceName = $"{typeof(Program).Namespace}.Resources.nlog.config"; } } diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index f21f666b0..7e5e7e4cd 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -63,6 +63,8 @@ internal static Parser BuildParser() services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddHttpClient(); services.AddSingleton(p => p.GetRequiredService()); services.AddSingleton(); diff --git a/src/CLI/Properties/launchSettings.json b/src/CLI/Properties/launchSettings.json index 284f8dba9..aff7ae893 100644 --- a/src/CLI/Properties/launchSettings.json +++ b/src/CLI/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Monai.Deploy.InformaticsGateway.CLI": { "commandName": "Project", - "commandLineArgs": "aet add -a Test" + "commandLineArgs": "start" } } } \ No newline at end of file diff --git a/src/CLI/Services/ConfigurationOptionAccessor.cs b/src/CLI/Services/ConfigurationOptionAccessor.cs index fd134c93e..c61ae761c 100644 --- a/src/CLI/Services/ConfigurationOptionAccessor.cs +++ b/src/CLI/Services/ConfigurationOptionAccessor.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,11 +76,6 @@ public interface IConfigurationOptionAccessor /// Uri InformaticsGatewayServerUri { get; } - /// - /// Gets the log storage path from appsettings.json. - /// - string LogStoragePath { get; } - /// /// Gets or set the type of container runner from appsettings.json. /// @@ -99,7 +94,7 @@ public class ConfigurationOptionAccessor : IConfigurationOptionAccessor public ConfigurationOptionAccessor(IFileSystem fileSystem) { - _fileSystem = fileSystem; + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); } public int DicomListeningPort @@ -223,19 +218,6 @@ public Uri InformaticsGatewayServerUri } } - public string LogStoragePath - { - get - { - var logPath = GetValueFromJsonPath("Logging.File.BasePath"); - if (logPath.StartsWith("/")) - { - return logPath; - } - return _fileSystem.Path.Combine(Common.ContainerApplicationRootPath, logPath); - } - } - public Runner Runner { get @@ -255,7 +237,7 @@ public string TempStoragePath { get { - return GetValueFromJsonPath("InformaticsGateway.storage.temporary"); + return GetValueFromJsonPath("InformaticsGateway.storage.localTemporaryStoragePath"); } } diff --git a/src/CLI/Services/ConfigurationService.cs b/src/CLI/Services/ConfigurationService.cs index c46c16960..7e71c6afd 100644 --- a/src/CLI/Services/ConfigurationService.cs +++ b/src/CLI/Services/ConfigurationService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; namespace Monai.Deploy.InformaticsGateway.CLI.Services @@ -35,13 +36,20 @@ public class ConfigurationService : IConfigurationService public bool IsConfigExists => _fileSystem.File.Exists(Common.ConfigFilePath); public IConfigurationOptionAccessor Configurations { get; } + public INLogConfigurationOptionAccessor NLogConfigurations { get; } - public ConfigurationService(ILogger logger, IFileSystem fileSystem, IEmbeddedResource embeddedResource) + public ConfigurationService( + ILogger logger, + IFileSystem fileSystem, + IEmbeddedResource embeddedResource, + IConfigurationOptionAccessor configurationOptionAccessor, + INLogConfigurationOptionAccessor nLogConfigurationOptionAccessor) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _embeddedResource = embeddedResource ?? throw new ArgumentNullException(nameof(embeddedResource)); - Configurations = new ConfigurationOptionAccessor(fileSystem); + Configurations = configurationOptionAccessor ?? throw new ArgumentNullException(nameof(configurationOptionAccessor)); + NLogConfigurations = nLogConfigurationOptionAccessor ?? throw new ArgumentNullException(nameof(nLogConfigurationOptionAccessor)); } public void CreateConfigDirectoryIfNotExist() @@ -55,22 +63,32 @@ public void CreateConfigDirectoryIfNotExist() public async Task Initialize(CancellationToken cancellationToken) { _logger.DebugMessage("Reading default application configurations..."); - using var stream = _embeddedResource.GetManifestResourceStream(Common.AppSettingsResourceName); + await WriteConfigFile(Common.AppSettingsResourceName, Common.ConfigFilePath, cancellationToken).ConfigureAwait(false); + await WriteConfigFile(Common.NLogConfigResourceName, Common.NLogConfigFilePath, cancellationToken).ConfigureAwait(false); + } + + public async Task WriteConfigFile(string resourceName, string outputPath, CancellationToken cancellationToken) + { + Guard.Against.NullOrWhiteSpace(resourceName, nameof(resourceName)); + Guard.Against.NullOrWhiteSpace(outputPath, nameof(outputPath)); + + using var stream = _embeddedResource.GetManifestResourceStream(resourceName); if (stream is null) { _logger.AvailableManifest(string.Join(",", Assembly.GetExecutingAssembly().GetManifestResourceNames())); - throw new ConfigurationException($"Default configuration file could not be loaded, please reinstall the CLI."); + throw new ConfigurationException($"Default configuration file: {resourceName} could not be loaded, please reinstall the CLI."); } CreateConfigDirectoryIfNotExist(); - _logger.SaveAppSettings(Common.ConfigFilePath); - using (var fileStream = _fileSystem.FileStream.Create(Common.ConfigFilePath, FileMode.Create)) + _logger.SaveAppSettings(resourceName, outputPath); + using (var fileStream = _fileSystem.FileStream.Create(outputPath, FileMode.Create)) { await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); } - _logger.AppSettingUpdated(Common.ConfigFilePath); + _logger.AppSettingUpdated(outputPath); + } } } diff --git a/src/CLI/Services/DockerRunner.cs b/src/CLI/Services/DockerRunner.cs index 20d1c15a0..f3eb2abfb 100644 --- a/src/CLI/Services/DockerRunner.cs +++ b/src/CLI/Services/DockerRunner.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,6 +114,7 @@ public async Task StartApplication(ImageVersion imageVersion, Cancellation }; createContainerParams.HostConfig.PortBindings = new Dictionary>(); + createContainerParams.HostConfig.NetworkMode = "monaideploy"; _logger.DockerPrtBinding(_configurationService.Configurations.DicomListeningPort); createContainerParams.ExposedPorts.Add($"{_configurationService.Configurations.DicomListeningPort}/tcp", new EmptyStruct()); @@ -135,9 +136,9 @@ public async Task StartApplication(ImageVersion imageVersion, Cancellation _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostDatabaseStorageMount); createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostDatabaseStorageMount, Target = Common.MountedDatabasePath }); - _logger.DockerMountAppLogs(_configurationService.Configurations.HostLogsStorageMount, _configurationService.Configurations.LogStoragePath); + _logger.DockerMountAppLogs(_configurationService.Configurations.HostLogsStorageMount, _configurationService.NLogConfigurations.LogStoragePath); _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostLogsStorageMount); - createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostLogsStorageMount, Target = _configurationService.Configurations.LogStoragePath }); + createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostLogsStorageMount, Target = _configurationService.NLogConfigurations.LogStoragePath }); _logger.DockerMountPlugins(_configurationService.Configurations.HostPlugInsStorageMount, Common.MountedPlugInsPath); _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostPlugInsStorageMount); diff --git a/src/CLI/Services/EmbeddedResource.cs b/src/CLI/Services/EmbeddedResource.cs index 9b21c127e..d93151475 100644 --- a/src/CLI/Services/EmbeddedResource.cs +++ b/src/CLI/Services/EmbeddedResource.cs @@ -29,7 +29,8 @@ public class EmbeddedResource : IEmbeddedResource public Stream GetManifestResourceStream(string name) { Guard.Against.NullOrWhiteSpace(name, nameof(name)); - return GetType().Assembly.GetManifestResourceStream(Common.AppSettingsResourceName); + + return GetType().Assembly.GetManifestResourceStream(name); } } } diff --git a/src/CLI/Services/IConfigurationService.cs b/src/CLI/Services/IConfigurationService.cs index a4a0d8fa0..77cfe108c 100644 --- a/src/CLI/Services/IConfigurationService.cs +++ b/src/CLI/Services/IConfigurationService.cs @@ -26,6 +26,11 @@ public interface IConfigurationService /// IConfigurationOptionAccessor Configurations { get; } + /// + /// Gets the configurations inside nlog.config + /// + INLogConfigurationOptionAccessor NLogConfigurations { get; } + /// /// Gets whether the configuration file exists or not. /// diff --git a/src/CLI/Services/NLogConfigurationOptionAccessor.cs b/src/CLI/Services/NLogConfigurationOptionAccessor.cs new file mode 100644 index 000000000..404446704 --- /dev/null +++ b/src/CLI/Services/NLogConfigurationOptionAccessor.cs @@ -0,0 +1,60 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO.Abstractions; +using System.Xml; + +namespace Monai.Deploy.InformaticsGateway.CLI.Services +{ + public interface INLogConfigurationOptionAccessor + { + /// + /// Gets the logs directory path. + /// + string LogStoragePath { get; } + } + + public class NLogConfigurationOptionAccessor : INLogConfigurationOptionAccessor + { + private readonly XmlDocument _xmlDocument; + private readonly XmlNamespaceManager _namespaceManager; + + public NLogConfigurationOptionAccessor(IFileSystem fileSystem) + { + if (fileSystem is null) + { + throw new ArgumentNullException(nameof(fileSystem)); + } + + var xml = fileSystem.File.ReadAllText(Common.NLogConfigFilePath); + _xmlDocument = new XmlDocument(); + _xmlDocument.LoadXml(xml); + _namespaceManager = new XmlNamespaceManager(_xmlDocument.NameTable); + _namespaceManager.AddNamespace("ns", "http://www.nlog-project.org/schemas/NLog.xsd"); + } + + public string LogStoragePath + { + get + { + var value = _xmlDocument.SelectSingleNode("//ns:variable[@name='logDir']/@value", _namespaceManager).InnerText; + value = value.Replace("${basedir}", Common.ContainerApplicationRootPath); + return value; + } + } + } +} diff --git a/src/CLI/Test/ConfigurationOptionAccessorTest.cs b/src/CLI/Test/ConfigurationOptionAccessorTest.cs new file mode 100644 index 000000000..5098ce214 --- /dev/null +++ b/src/CLI/Test/ConfigurationOptionAccessorTest.cs @@ -0,0 +1,194 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using Ardalis.GuardClauses; +using Monai.Deploy.InformaticsGateway.CLI.Services; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.CLI.Test +{ + public class ConfigurationOptionAccessorTest + { + private readonly IFileSystem _fileSystem; + + public ConfigurationOptionAccessorTest() + { + } + + [Fact(DisplayName = "ConfigurationOptionAccessor Constructor")] + public void DockerRunner_Constructor() + { + Assert.Throws(() => new ConfigurationOptionAccessor(null)); + } + + [Fact] + public void DicomListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"dicom\": {\"scp\": {\"port\": 104}}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(104, configurationOptionAccessor.DicomListeningPort); + } + + [Fact] + public void DicomListeningPort_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"dicom\": {\"scp\": {\"port\": 104}}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.DicomListeningPort = 1000; + Assert.Equal(1000, configurationOptionAccessor.DicomListeningPort); + } + + [Fact] + public void Hl7ListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"hl7\": {\"port\": 2575}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(2575, configurationOptionAccessor.Hl7ListeningPort); + } + + [Fact] + public void Hl7ListeningPort_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"hl7\": {\"port\": 2575}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.Hl7ListeningPort = 1000; + Assert.Equal(1000, configurationOptionAccessor.Hl7ListeningPort); + } + + [Fact] + public void DockerImagePrefix_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"DockerImagePrefix\": \"ghcr.io/project-monai/monai-deploy-informatics-gateway\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal("ghcr.io/project-monai/monai-deploy-informatics-gateway", configurationOptionAccessor.DockerImagePrefix); + } + + [Fact] + public void HostDatabaseStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostDatabaseStorageMount\": \"~/.mig/database\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/database", configurationOptionAccessor.HostDatabaseStorageMount); + } + + [Fact] + public void HostDataStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostDataStorageMount\": \"~/.mig/data\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/data", configurationOptionAccessor.HostDataStorageMount); + } + + [Fact] + public void HostPlugInsStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostPlugInsStorageMount\": \"~/.mig/plug-ins\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/plug-ins", configurationOptionAccessor.HostPlugInsStorageMount); + } + + [Fact] + public void HostLogsStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostLogsStorageMount\": \"~/.mig/logs\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/logs", configurationOptionAccessor.HostLogsStorageMount); + } + + [Fact] + public void InformaticsGatewayServerEndpoint_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal("http://localhost:5000", configurationOptionAccessor.InformaticsGatewayServerEndpoint); + } + + [Fact] + public void InformaticsGatewayServerEndpoint_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.InformaticsGatewayServerEndpoint = "http://hello-world"; + Assert.Equal("http://hello-world", configurationOptionAccessor.InformaticsGatewayServerEndpoint); + } + + [Fact] + public void InformaticsGatewayServerPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(5000, configurationOptionAccessor.InformaticsGatewayServerPort); + } + + [Fact] + public void InformaticsGatewayServerUri_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(new Uri("http://localhost:5000"), configurationOptionAccessor.InformaticsGatewayServerUri); + } + + [Fact] + public void Runner_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"Runner\": \"Docker\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(Runner.Docker, configurationOptionAccessor.Runner); + } + + [Fact] + public void Runner_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"Runner\": \"Docker\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.Runner = Runner.Kubernetes; + Assert.Equal(Runner.Kubernetes, configurationOptionAccessor.Runner); + } + + [Fact] + public void TempStoragePath_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"storage\": {\"localTemporaryStoragePath\": \"/payloads\"}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + Assert.Equal("/payloads", configurationOptionAccessor.TempStoragePath); + } + + private IFileSystem SetupFileSystem(string config) + { + Guard.Against.NullOrWhiteSpace(config, nameof(config)); + + return new MockFileSystem(new Dictionary + { + {Common.ConfigFilePath, new MockFileData(config) } + }); + } + } +} diff --git a/src/CLI/Test/ConfigurationServiceTest.cs b/src/CLI/Test/ConfigurationServiceTest.cs index 5fa218e31..0c295d44e 100644 --- a/src/CLI/Test/ConfigurationServiceTest.cs +++ b/src/CLI/Test/ConfigurationServiceTest.cs @@ -33,29 +33,35 @@ public class ConfigurationServiceTest private readonly Mock> _logger; private readonly Mock _fileSystem; private readonly Mock _embeddedResource; + private readonly Mock _configurationOptionAccessor; + private readonly Mock _nLogConfigurationOptionAccessor; public ConfigurationServiceTest() { _logger = new Mock>(); _fileSystem = new Mock(); _embeddedResource = new Mock(); + _configurationOptionAccessor = new Mock(); + _nLogConfigurationOptionAccessor = new Mock(); } [Fact(DisplayName = "ConfigurationServiceTest constructor")] public void ConfigurationServiceTest_Constructor() { - Assert.Throws(() => new ConfigurationService(null, null, null)); - Assert.Throws(() => new ConfigurationService(_logger.Object, null, null)); - Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, null)); + Assert.Throws(() => new ConfigurationService(null, null, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, null, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, null)); - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); Assert.NotNull(svc.Configurations); } [Fact(DisplayName = "CreateConfigDirectoryIfNotExist creates directory")] public void CreateConfigDirectoryIfNotExist_CreateDirectory() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(false); svc.CreateConfigDirectoryIfNotExist(); @@ -66,7 +72,7 @@ public void CreateConfigDirectoryIfNotExist_CreateDirectory() [Fact(DisplayName = "CreateConfigDirectoryIfNotExist skips creating directory")] public void CreateConfigDirectoryIfNotExist_SkipsCreation() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); svc.CreateConfigDirectoryIfNotExist(); @@ -77,7 +83,7 @@ public void CreateConfigDirectoryIfNotExist_SkipsCreation() [Fact(DisplayName = "IsInitialized")] public void IsInitialized() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); _fileSystem.Setup(p => p.File.Exists(It.IsAny())).Returns(true); @@ -89,7 +95,7 @@ public void IsInitialized() [Fact(DisplayName = "IsConfigExists")] public void ConfigurationExists() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.File.Exists(It.IsAny())).Returns(true); Assert.True(svc.IsConfigExists); @@ -102,27 +108,33 @@ public async Task Initialize_ShallThrowWhenConfigReousrceIsMissing() { _embeddedResource.Setup(p => p.GetManifestResourceStream(It.IsAny())).Returns(default(Stream)); - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); await Assert.ThrowsAsync(async () => await svc.Initialize(CancellationToken.None)); } - [Fact(DisplayName = "Initialize creates the config file")] - public async Task Initialize_CreatesTheConfigFile() + [Fact(DisplayName = "Initialize creates the config files")] + public async Task Initialize_CreatesTheConfigFiles() { var fileSystem = new MockFileSystem(); var testString = "hello world"; - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + var appConfigMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + var nLogConfigMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); var mockSteam = new Mock(); - _embeddedResource.Setup(p => p.GetManifestResourceStream(It.IsAny())).Returns(memoryStream); + _embeddedResource.Setup(p => p.GetManifestResourceStream(It.Is(p => p == Common.AppSettingsResourceName))).Returns(appConfigMemoryStream); + _embeddedResource.Setup(p => p.GetManifestResourceStream(It.Is(p => p == Common.NLogConfigResourceName))).Returns(nLogConfigMemoryStream); - var svc = new ConfigurationService(_logger.Object, fileSystem, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, fileSystem, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); await svc.Initialize(CancellationToken.None); _embeddedResource.Verify(p => p.GetManifestResourceStream(Common.AppSettingsResourceName), Times.Once()); + _embeddedResource.Verify(p => p.GetManifestResourceStream(Common.NLogConfigResourceName), Times.Once()); var bytesWritten = await fileSystem.File.ReadAllBytesAsync(Common.ConfigFilePath).ConfigureAwait(false); Assert.Equal(testString, Encoding.UTF8.GetString(bytesWritten)); + + bytesWritten = await fileSystem.File.ReadAllBytesAsync(Common.NLogConfigFilePath).ConfigureAwait(false); + Assert.Equal(testString, Encoding.UTF8.GetString(bytesWritten)); } } } diff --git a/src/CLI/Test/DockerRunnerTest.cs b/src/CLI/Test/DockerRunnerTest.cs index 2ecb446d5..b992bb6d2 100644 --- a/src/CLI/Test/DockerRunnerTest.cs +++ b/src/CLI/Test/DockerRunnerTest.cs @@ -163,7 +163,7 @@ public async Task StartApplication() _configurationService.SetupGet(p => p.Configurations.HostLogsStorageMount).Returns("/logs"); _configurationService.SetupGet(p => p.Configurations.HostPlugInsStorageMount).Returns("/plug-ins"); _configurationService.SetupGet(p => p.Configurations.TempStoragePath).Returns("/tempdata"); - _configurationService.SetupGet(p => p.Configurations.LogStoragePath).Returns("/templogs"); + _configurationService.SetupGet(p => p.NLogConfigurations.LogStoragePath).Returns("/templogs"); Assert.True(await runner.StartApplication(image, CancellationToken.None)); Assert.False(await runner.StartApplication(image, CancellationToken.None)); diff --git a/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs b/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs new file mode 100644 index 000000000..469fb9f6f --- /dev/null +++ b/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs @@ -0,0 +1,58 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using Monai.Deploy.InformaticsGateway.CLI.Services; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.CLI.Test +{ + public class NLogConfigurationOptionAccessorTest + { + private readonly IFileSystem _fileSystem; + + public NLogConfigurationOptionAccessorTest() + { + } + + [Fact(DisplayName = "NLogConfigurationOptionAccessor Constructor")] + public void DockerRunner_Constructor() + { + Assert.Throws(() => new NLogConfigurationOptionAccessor(null)); + } + + + [Fact] + public void DicomListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem(); + var configurationOptionAccessor = new NLogConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.ContainerApplicationRootPath}/logs/", configurationOptionAccessor.LogStoragePath); + } + + private IFileSystem SetupFileSystem() + { + return new MockFileSystem(new Dictionary + { + {Common.NLogConfigFilePath, new MockFileData("\r\n \r\n") } + }); + } + } +} diff --git a/src/CLI/Test/ProgramTest.cs b/src/CLI/Test/ProgramTest.cs index 82f8761f8..8ef0bc856 100644 --- a/src/CLI/Test/ProgramTest.cs +++ b/src/CLI/Test/ProgramTest.cs @@ -25,7 +25,7 @@ public class ProgramTest public void Startup_RunsProperly() { var host = Program.BuildParser(); - + Assert.NotNull(host); } }