Skip to content

Commit

Permalink
gh-435 Fix CLI to read log dir path from NLog config file (#436)
Browse files Browse the repository at this point in the history
* gh-435 Fix CLI to read log dir path from NLog config file
* Update changelog
* Add/update unit tests


Signed-off-by: Victor Chang <vicchang@nvidia.com>
  • Loading branch information
mocsharp authored Aug 10, 2023
1 parent d54788e commit 0ff1963
Show file tree
Hide file tree
Showing 17 changed files with 427 additions and 52 deletions.
38 changes: 38 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/CLI/Logging/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

<ItemGroup>
<EmbeddedResource Include="..\InformaticsGateway\appsettings.json" Link="Resources\appsettings.json" />
<EmbeddedResource Include="..\InformaticsGateway\nlog.config" Link="Resources\nlog.config" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/CLI/Options/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
}
2 changes: 2 additions & 0 deletions src/CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ internal static Parser BuildParser()
services.AddScoped<IFileSystem, FileSystem>();
services.AddScoped<IConfirmationPrompt, ConfirmationPrompt>();
services.AddScoped<IConsoleRegion, ConsoleRegion>();
services.AddScoped<IConfigurationOptionAccessor, ConfigurationOptionAccessor>();
services.AddScoped<INLogConfigurationOptionAccessor, NLogConfigurationOptionAccessor>();
services.AddHttpClient<InformaticsGatewayClient>();
services.AddSingleton<IInformaticsGatewayClient>(p => p.GetRequiredService<InformaticsGatewayClient>());
services.AddSingleton<IConfigurationService, ConfigurationService>();
Expand Down
2 changes: 1 addition & 1 deletion src/CLI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"Monai.Deploy.InformaticsGateway.CLI": {
"commandName": "Project",
"commandLineArgs": "aet add -a Test"
"commandLineArgs": "start"
}
}
}
24 changes: 3 additions & 21 deletions src/CLI/Services/ConfigurationOptionAccessor.cs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -76,11 +76,6 @@ public interface IConfigurationOptionAccessor
/// </summary>
Uri InformaticsGatewayServerUri { get; }

/// <summary>
/// Gets the log storage path from appsettings.json.
/// </summary>
string LogStoragePath { get; }

/// <summary>
/// Gets or set the type of container runner from appsettings.json.
/// </summary>
Expand All @@ -99,7 +94,7 @@ public class ConfigurationOptionAccessor : IConfigurationOptionAccessor

public ConfigurationOptionAccessor(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
}

public int DicomListeningPort
Expand Down Expand Up @@ -223,19 +218,6 @@ public Uri InformaticsGatewayServerUri
}
}

public string LogStoragePath
{
get
{
var logPath = GetValueFromJsonPath<string>("Logging.File.BasePath");
if (logPath.StartsWith("/"))
{
return logPath;
}
return _fileSystem.Path.Combine(Common.ContainerApplicationRootPath, logPath);
}
}

public Runner Runner
{
get
Expand All @@ -255,7 +237,7 @@ public string TempStoragePath
{
get
{
return GetValueFromJsonPath<string>("InformaticsGateway.storage.temporary");
return GetValueFromJsonPath<string>("InformaticsGateway.storage.localTemporaryStoragePath");
}
}

Expand Down
34 changes: 26 additions & 8 deletions src/CLI/Services/ConfigurationService.cs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
Expand All @@ -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<ConfigurationService> logger, IFileSystem fileSystem, IEmbeddedResource embeddedResource)
public ConfigurationService(
ILogger<ConfigurationService> 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()
Expand All @@ -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);

}
}
}
7 changes: 4 additions & 3 deletions src/CLI/Services/DockerRunner.cs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -114,6 +114,7 @@ public async Task<bool> StartApplication(ImageVersion imageVersion, Cancellation
};

createContainerParams.HostConfig.PortBindings = new Dictionary<string, IList<PortBinding>>();
createContainerParams.HostConfig.NetworkMode = "monaideploy";

_logger.DockerPrtBinding(_configurationService.Configurations.DicomListeningPort);
createContainerParams.ExposedPorts.Add($"{_configurationService.Configurations.DicomListeningPort}/tcp", new EmptyStruct());
Expand All @@ -135,9 +136,9 @@ public async Task<bool> 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);
Expand Down
3 changes: 2 additions & 1 deletion src/CLI/Services/EmbeddedResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
5 changes: 5 additions & 0 deletions src/CLI/Services/IConfigurationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public interface IConfigurationService
/// </summary>
IConfigurationOptionAccessor Configurations { get; }

/// <summary>
/// Gets the configurations inside nlog.config
/// </summary>
INLogConfigurationOptionAccessor NLogConfigurations { get; }

/// <summary>
/// Gets whether the configuration file exists or not.
/// </summary>
Expand Down
60 changes: 60 additions & 0 deletions src/CLI/Services/NLogConfigurationOptionAccessor.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Gets the logs directory path.
/// </summary>
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;
}
}
}
}
Loading

0 comments on commit 0ff1963

Please sign in to comment.