Skip to content

Commit df25fa2

Browse files
committed
merge in
Signed-off-by: Neil South <neil.south@answerdigital.com>
2 parents 5388837 + 6f94803 commit df25fa2

File tree

4 files changed

+122
-6
lines changed

4 files changed

+122
-6
lines changed

src/TaskManager/Plug-ins/Docker/DockerPlugin.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
using System.Globalization;
18+
using Amazon.Runtime.Internal.Transform;
1819
using Ardalis.GuardClauses;
1920
using Docker.DotNet;
2021
using Docker.DotNet.Models;
@@ -132,13 +133,17 @@ public override async Task<ExecutionStatus> ExecuteTask(CancellationToken cancel
132133

133134
try
134135
{
135-
var imageCreateParameters = new ImagesCreateParameters()
136+
var alwaysPull = Event.TaskPluginArguments.ContainsKey(Keys.AlwaysPull) && Event.TaskPluginArguments[Keys.AlwaysPull].Equals("true", StringComparison.OrdinalIgnoreCase);
137+
if (alwaysPull || !await ImageExistsAsync(cancellationToken).ConfigureAwait(false))
136138
{
137-
FromImage = Event.TaskPluginArguments[Keys.ContainerImage],
138-
};
139-
140-
// Pull image.
141-
await _dockerClient.Images.CreateImageAsync(imageCreateParameters, new AuthConfig(), new Progress<JSONMessage>(), cancellationToken).ConfigureAwait(false);
139+
// Pull image.
140+
_logger.ImageDoesNotExist(Event.TaskPluginArguments[Keys.ContainerImage]);
141+
var imageCreateParameters = new ImagesCreateParameters()
142+
{
143+
FromImage = Event.TaskPluginArguments[Keys.ContainerImage],
144+
};
145+
await _dockerClient.Images.CreateImageAsync(imageCreateParameters, new AuthConfig(), new Progress<JSONMessage>(), cancellationToken).ConfigureAwait(false);
146+
}
142147
}
143148
catch (Exception exception)
144149
{
@@ -199,6 +204,20 @@ public override async Task<ExecutionStatus> ExecuteTask(CancellationToken cancel
199204
};
200205
}
201206

207+
private async Task<bool> ImageExistsAsync(CancellationToken cancellationToken)
208+
{
209+
var imageListParameters = new ImagesListParameters
210+
{
211+
Filters = new Dictionary<string, IDictionary<string, bool>>
212+
{
213+
{ "reference", new Dictionary<string, bool> { { Event.TaskPluginArguments[Keys.ContainerImage], true } } }
214+
}
215+
};
216+
217+
var results = await _dockerClient.Images.ListImagesAsync(imageListParameters, cancellationToken);
218+
return results?.Any() ?? false;
219+
}
220+
202221
public override async Task<ExecutionStatus> GetStatus(string identity, TaskCallbackEvent callbackEvent, CancellationToken cancellationToken = default)
203222
{
204223
Guard.Against.NullOrWhiteSpace(identity, nameof(identity));

src/TaskManager/Plug-ins/Docker/Keys.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ internal static class Keys
3838
/// </summary>
3939
public static readonly string Command = "command";
4040

41+
/// <summary>
42+
/// Key to indicate whether to always pull the image.
43+
/// </summary>
44+
public static readonly string AlwaysPull = "always_pull";
45+
4146
/// <summary>
4247
/// Key for task timeout value.
4348
/// </summary>

src/TaskManager/Plug-ins/Docker/Logging/Log.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,8 @@ public static partial class Log
100100

101101
[LoggerMessage(EventId = 1026, Level = LogLevel.Error, Message = "Error terminating container '{container}'.")]
102102
public static partial void ErrorTerminatingContainer(this ILogger logger, string container, Exception ex);
103+
104+
[LoggerMessage(EventId = 1027, Level = LogLevel.Information, Message = "Image does not exist '{image}' locally, attempting to pull.")]
105+
public static partial void ImageDoesNotExist(this ILogger logger, string image);
103106
}
104107
}

tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,95 @@ public async Task ExecuteTask_WhenFailedToMonitorContainer_ExpectTaskToBeAccepte
228228
runner.Dispose();
229229
}
230230

231+
[Fact(DisplayName = "ExecuteTask - do not pull the image when the specified image exists")]
232+
public async Task ExecuteTask_WhenImageExists_ExpectNotToPull()
233+
{
234+
var payloadFiles = new List<VirtualFileInfo>()
235+
{
236+
new VirtualFileInfo( "file.dcm", "path/to/file.dcm", "etag", 1000)
237+
};
238+
var contianerId = Guid.NewGuid().ToString();
239+
240+
_dockerClient.Setup(p => p.Images.CreateImageAsync(
241+
It.IsAny<ImagesCreateParameters>(),
242+
It.IsAny<AuthConfig>(),
243+
It.IsAny<IProgress<JSONMessage>>(),
244+
It.IsAny<CancellationToken>()));
245+
_dockerClient.Setup(p => p.Images.ListImagesAsync(
246+
It.IsAny<ImagesListParameters>(),
247+
It.IsAny<CancellationToken>()))
248+
.ReturnsAsync(new List<ImagesListResponse>() { new ImagesListResponse() });
249+
_dockerClient.Setup(p => p.Containers.CreateContainerAsync(
250+
It.IsAny<CreateContainerParameters>(),
251+
It.IsAny<CancellationToken>()))
252+
.ReturnsAsync(new CreateContainerResponse { ID = contianerId, Warnings = new List<string>() { "warning" } });
253+
254+
_storageService.Setup(p => p.ListObjectsAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
255+
.ReturnsAsync(payloadFiles);
256+
_storageService.Setup(p => p.GetObjectAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
257+
.ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("hello")));
258+
259+
var message = GenerateTaskDispatchEventWithValidArguments();
260+
261+
var runner = new DockerPlugin(_serviceScopeFactory.Object, _logger.Object, message);
262+
var result = await runner.ExecuteTask(CancellationToken.None).ConfigureAwait(false);
263+
264+
_dockerClient.Verify(p => p.Images.CreateImageAsync(
265+
It.IsAny<ImagesCreateParameters>(),
266+
It.IsAny<AuthConfig>(),
267+
It.IsAny<IProgress<JSONMessage>>(),
268+
It.IsAny<CancellationToken>()), Times.Never());
269+
_dockerClient.Verify(p => p.Images.ListImagesAsync(
270+
It.IsAny<ImagesListParameters>(),
271+
It.IsAny<CancellationToken>()), Times.Once());
272+
runner.Dispose();
273+
}
274+
275+
[Fact(DisplayName = "ExecuteTask - pull the image when force by the user even the specified image exists")]
276+
public async Task ExecuteTask_WhenAlwaysPullIsSet_ExpectToPullEvenWhenImageExists()
277+
{
278+
var payloadFiles = new List<VirtualFileInfo>()
279+
{
280+
new VirtualFileInfo( "file.dcm", "path/to/file.dcm", "etag", 1000)
281+
};
282+
var contianerId = Guid.NewGuid().ToString();
283+
284+
_dockerClient.Setup(p => p.Images.CreateImageAsync(
285+
It.IsAny<ImagesCreateParameters>(),
286+
It.IsAny<AuthConfig>(),
287+
It.IsAny<IProgress<JSONMessage>>(),
288+
It.IsAny<CancellationToken>()));
289+
_dockerClient.Setup(p => p.Images.ListImagesAsync(
290+
It.IsAny<ImagesListParameters>(),
291+
It.IsAny<CancellationToken>()))
292+
.ReturnsAsync(new List<ImagesListResponse>() { new ImagesListResponse() });
293+
_dockerClient.Setup(p => p.Containers.CreateContainerAsync(
294+
It.IsAny<CreateContainerParameters>(),
295+
It.IsAny<CancellationToken>()))
296+
.ReturnsAsync(new CreateContainerResponse { ID = contianerId, Warnings = new List<string>() { "warning" } });
297+
298+
_storageService.Setup(p => p.ListObjectsAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()))
299+
.ReturnsAsync(payloadFiles);
300+
_storageService.Setup(p => p.GetObjectAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
301+
.ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("hello")));
302+
303+
var message = GenerateTaskDispatchEventWithValidArguments();
304+
message.TaskPluginArguments.Add(Keys.AlwaysPull, bool.TrueString);
305+
306+
var runner = new DockerPlugin(_serviceScopeFactory.Object, _logger.Object, message);
307+
var result = await runner.ExecuteTask(CancellationToken.None).ConfigureAwait(false);
308+
309+
_dockerClient.Verify(p => p.Images.CreateImageAsync(
310+
It.IsAny<ImagesCreateParameters>(),
311+
It.IsAny<AuthConfig>(),
312+
It.IsAny<IProgress<JSONMessage>>(),
313+
It.IsAny<CancellationToken>()), Times.Once());
314+
_dockerClient.Verify(p => p.Images.ListImagesAsync(
315+
It.IsAny<ImagesListParameters>(),
316+
It.IsAny<CancellationToken>()), Times.Never());
317+
runner.Dispose();
318+
}
319+
231320
[Fact(DisplayName = "ExecuteTask - when called with a valid event expect task to be accepted and monitored in the background")]
232321
public async Task ExecuteTask_WhenCalledWithValidEvent_ExpectTaskToBeAcceptedAndMonitored()
233322
{

0 commit comments

Comments
 (0)