diff --git a/src/TaskManager/Plug-ins/Docker/DockerPlugin.cs b/src/TaskManager/Plug-ins/Docker/DockerPlugin.cs index a4382c4c5..eb2a2b88a 100644 --- a/src/TaskManager/Plug-ins/Docker/DockerPlugin.cs +++ b/src/TaskManager/Plug-ins/Docker/DockerPlugin.cs @@ -14,8 +14,8 @@ * limitations under the License. */ +using System.Diagnostics; using System.Globalization; -using Amazon.Runtime.Internal.Transform; using Ardalis.GuardClauses; using Docker.DotNet; using Docker.DotNet.Models; @@ -364,9 +364,14 @@ private CreateContainerParameters BuildContainerSpecification(IList> SetupInputs(CancellationToken can // eg: /monai/input var volumeMount = new ContainerVolumeMount(input, Event.TaskPluginArguments[input.Name], inputHostDirRoot, inputContainerDirRoot); volumeMounts.Add(volumeMount); + _logger.InputVolumeMountAdded(inputHostDirRoot, inputContainerDirRoot); // For each file, download from bucket and store in Task Manager Container. foreach (var obj in objects) @@ -424,6 +430,8 @@ private async Task> SetupInputs(CancellationToken can } } + SetPermission(containerPath); + return volumeMounts; } @@ -436,7 +444,10 @@ private ContainerVolumeMount SetupIntermediateVolume() Directory.CreateDirectory(containerPath); - return new ContainerVolumeMount(Event.IntermediateStorage, Event.TaskPluginArguments[Keys.WorkingDirectory], hostPath, containerPath); + var volumeMount = new ContainerVolumeMount(Event.IntermediateStorage, Event.TaskPluginArguments[Keys.WorkingDirectory], hostPath, containerPath); + _logger.IntermediateVolumeMountAdded(hostPath, containerPath); + SetPermission(containerPath); + return volumeMount; } return default!; @@ -465,11 +476,32 @@ private List SetupOutputs() Directory.CreateDirectory(containerPath); volumeMounts.Add(new ContainerVolumeMount(output, Event.TaskPluginArguments[output.Name], hostPath, containerPath)); + _logger.OutputVolumeMountAdded(hostPath, containerPath); } + SetPermission(containerRootPath); + return volumeMounts; } + private void SetPermission(string path) + { + if (Event.TaskPluginArguments.ContainsKey(Keys.User)) + { + if (!System.OperatingSystem.IsWindows()) + { + var process = Process.Start("chown", $"-R {Event.TaskPluginArguments[Keys.User]} {path}"); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + _logger.ErrorSettingDirectoryPermission(path, Event.TaskPluginArguments[Keys.User]); + throw new SetPermissionException($"chown command exited with code {process.ExitCode}"); + } + } + } + } + protected override void Dispose(bool disposing) { if (!DisposedValue && disposing) diff --git a/src/TaskManager/Plug-ins/Docker/Keys.cs b/src/TaskManager/Plug-ins/Docker/Keys.cs index 1e6b81e83..d71a6213d 100644 --- a/src/TaskManager/Plug-ins/Docker/Keys.cs +++ b/src/TaskManager/Plug-ins/Docker/Keys.cs @@ -33,6 +33,11 @@ internal static class Keys /// public static readonly string EntryPoint = "entrypoint"; + /// + /// Key for specifying the user to the container. Same as -u argument for docker run. + /// + public static readonly string User = "user"; + /// /// Key for the command to execute by the container. /// diff --git a/src/TaskManager/Plug-ins/Docker/Logging/Log.cs b/src/TaskManager/Plug-ins/Docker/Logging/Log.cs index 93fb6d934..c4e95a7ea 100644 --- a/src/TaskManager/Plug-ins/Docker/Logging/Log.cs +++ b/src/TaskManager/Plug-ins/Docker/Logging/Log.cs @@ -103,5 +103,17 @@ public static partial class Log [LoggerMessage(EventId = 1027, Level = LogLevel.Information, Message = "Image does not exist '{image}' locally, attempting to pull.")] public static partial void ImageDoesNotExist(this ILogger logger, string image); + + [LoggerMessage(EventId = 1028, Level = LogLevel.Information, Message = "Input volume added {hostPath} = {containerPath}.")] + public static partial void InputVolumeMountAdded(this ILogger logger, string hostPath, string containerPath); + + [LoggerMessage(EventId = 1029, Level = LogLevel.Information, Message = "Output volume added {hostPath} = {containerPath}.")] + public static partial void OutputVolumeMountAdded(this ILogger logger, string hostPath, string containerPath); + + [LoggerMessage(EventId = 1030, Level = LogLevel.Information, Message = "Intermediate volume added {hostPath} = {containerPath}.")] + public static partial void IntermediateVolumeMountAdded(this ILogger logger, string hostPath, string containerPath); + + [LoggerMessage(EventId = 1031, Level = LogLevel.Error, Message = "Error setting directory {path} with permission {user}.")] + public static partial void ErrorSettingDirectoryPermission(this ILogger logger, string path, string user); } } diff --git a/src/TaskManager/Plug-ins/Docker/SetPermissionException.cs b/src/TaskManager/Plug-ins/Docker/SetPermissionException.cs new file mode 100644 index 000000000..66c9695aa --- /dev/null +++ b/src/TaskManager/Plug-ins/Docker/SetPermissionException.cs @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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.Runtime.Serialization; + +namespace Monai.Deploy.WorkflowManager.TaskManager.Docker +{ + [Serializable] + internal class SetPermissionException : Exception + { + public SetPermissionException() + { + } + + public SetPermissionException(string? message) : base(message) + { + } + + public SetPermissionException(string? message, Exception? innerException) : base(message, innerException) + { + } + + protected SetPermissionException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/src/TaskManager/TaskManager/Services/Http/Startup.cs b/src/TaskManager/TaskManager/Services/Http/Startup.cs old mode 100644 new mode 100755 index 0dee0e0ca..09282d7a8 --- a/src/TaskManager/TaskManager/Services/Http/Startup.cs +++ b/src/TaskManager/TaskManager/Services/Http/Startup.cs @@ -62,6 +62,27 @@ public void ConfigureServices(IServiceCollection services) { c.SwaggerDoc("v1", new OpenApiInfo { Title = "MONAI Workflow Manager", Version = "v1" }); c.DescribeAllParametersInCamelCase(); + c.AddSecurityDefinition("basic", new OpenApiSecurityScheme + { + Scheme = "basic", + Name = "basic", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + }); + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "basic", + }, + }, + System.Array.Empty() + }, + }); }); var serviceProvider = services.BuildServiceProvider(); diff --git a/src/WorkflowManager/WorkflowExecuter/Services/WorkflowExecuterService.cs b/src/WorkflowManager/WorkflowExecuter/Services/WorkflowExecuterService.cs index 42d3cf3fe..276815405 100644 --- a/src/WorkflowManager/WorkflowExecuter/Services/WorkflowExecuterService.cs +++ b/src/WorkflowManager/WorkflowExecuter/Services/WorkflowExecuterService.cs @@ -271,7 +271,7 @@ private async Task ProcessArtifactReceivedOutputs(ArtifactsReceivedEvent message } } - var currentTask = workflowInstance.Tasks?.FirstOrDefault(t => t.TaskId == taskId); + var currentTask = workflowInstance.Tasks?.Find(t => t.TaskId == taskId); currentTask!.OutputArtifacts = validArtifacts; // added here are the parent function saves the object ! @@ -735,7 +735,7 @@ private async Task HandleOutputArtifacts(WorkflowInstance workflowInstance return false; } - var currentTask = workflowInstance.Tasks?.FirstOrDefault(t => t.TaskId == task.TaskId); + var currentTask = workflowInstance.Tasks?.Find(t => t.TaskId == task.TaskId); if (currentTask is not null) { diff --git a/src/WorkflowManager/WorkflowManager/Services/Http/Startup.cs b/src/WorkflowManager/WorkflowManager/Services/Http/Startup.cs old mode 100644 new mode 100755 diff --git a/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs b/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs old mode 100644 new mode 100755 index 67751c5db..8c92c5df5 --- a/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs +++ b/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs @@ -3184,6 +3184,7 @@ public async Task ArtifactReceveid_Valid_ReturnesTrue() Assert.True(result); } + [Fact] public async Task ProcessArtifactReceived_Calls_WorkflowInstanceRepository_UpdateTaskOutputArtifactsAsync() {