Skip to content

Commit

Permalink
Merge pull request dotnet#348 from vlada-shubina/cancellable-task
Browse files Browse the repository at this point in the history
Cancellation for `CreateNewImage` task
  • Loading branch information
vlada-shubina authored Feb 20, 2023
2 parents 72c7dd9 + 77966d3 commit fd78a5b
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 127 deletions.
23 changes: 15 additions & 8 deletions Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ public async Task ApiEndToEndWithRegistryPushAndPull()

Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));

ImageBuilder imageBuilder = await registry.GetImageManifest(
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
ToolsetUtils.GetRuntimeGraphFilePath(),
cancellationToken: default).ConfigureAwait(false);

Assert.NotNull(imageBuilder);

Expand All @@ -59,7 +60,7 @@ public async Task ApiEndToEndWithRegistryPushAndPull()
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), "latest");

await registry.Push(builtImage, sourceReference, destinationReference, Console.WriteLine).ConfigureAwait(false);
await registry.PushAsync(builtImage, sourceReference, destinationReference, Console.WriteLine, cancellationToken: default).ConfigureAwait(false);

// pull it back locally
new BasicCommand(_testOutput, "docker", "pull", $"{DockerRegistryManager.LocalRegistry}/{NewImageName()}:latest")
Expand All @@ -81,11 +82,12 @@ public async Task ApiEndToEndWithLocalLoad()

Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));

ImageBuilder imageBuilder = await registry.GetImageManifest(
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
ToolsetUtils.GetRuntimeGraphFilePath(),
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);

Layer l = Layer.FromDirectory(publishDirectory, "/app");
Expand All @@ -100,7 +102,7 @@ public async Task ApiEndToEndWithLocalLoad()
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), "latest");

await new LocalDocker(Console.WriteLine).Load(builtImage, sourceReference, destinationReference).ConfigureAwait(false);
await new LocalDocker(Console.WriteLine).LoadAsync(builtImage, sourceReference, destinationReference, default).ConfigureAwait(false);

// Run the image
new BasicCommand(_testOutput, "docker", "run", "--rm", "--tty", $"{NewImageName()}:latest")
Expand Down Expand Up @@ -293,7 +295,12 @@ public async Task CanPackageForAllSupportedContainerRIDs(string rid, bool isRIDS
// Build the image
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.BaseImageSource));

ImageBuilder? imageBuilder = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag, rid, ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
ImageBuilder? imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net7ImageTag,
rid,
ToolsetUtils.GetRuntimeGraphFilePath(),
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);

Layer l = Layer.FromDirectory(publishDirectory, "/app");
Expand All @@ -309,7 +316,7 @@ public async Task CanPackageForAllSupportedContainerRIDs(string rid, bool isRIDS
// Load the image into the local Docker daemon
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), rid);
await new LocalDocker(Console.WriteLine).Load(builtImage, sourceReference, destinationReference).ConfigureAwait(false);
await new LocalDocker(Console.WriteLine).LoadAsync(builtImage, sourceReference, destinationReference, default).ConfigureAwait(false);

// Run the image
new BasicCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ public async Task GetFromRegistry()

// Don't need rid graph for local registry image pulls - since we're only pushing single image manifests (not manifest lists)
// as part of our setup, we could put literally anything in here. The file at the passed-in path would only get read when parsing manifests lists.
ImageBuilder? downloadedImage = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", ridgraphfile).ConfigureAwait(false);
ImageBuilder? downloadedImage = await registry.GetImageManifestAsync(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
ridgraphfile,
cancellationToken: default).ConfigureAwait(false);

Assert.NotNull(downloadedImage);
}
Expand Down
4 changes: 2 additions & 2 deletions Microsoft.NET.Build.Containers.UnitTests/DockerDaemonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public async Task Can_detect_when_no_daemon_is_running() {
// mimic no daemon running by setting the DOCKER_HOST to a nonexistent socket
try {
System.Environment.SetEnvironmentVariable("DOCKER_HOST", "tcp://123.123.123.123:12345");
var available = await new LocalDocker(Console.WriteLine).IsAvailable().ConfigureAwait(false);
var available = await new LocalDocker(Console.WriteLine).IsAvailableAsync(default).ConfigureAwait(false);
Assert.False(available, "No daemon should be listening at that port");
} finally {
System.Environment.SetEnvironmentVariable("DOCKER_HOST", null);
Expand All @@ -21,7 +21,7 @@ public async Task Can_detect_when_no_daemon_is_running() {

[Fact]
public async Task Can_detect_when_daemon_is_running() {
var available = await new LocalDocker(Console.WriteLine).IsAvailable().ConfigureAwait(false);
var available = await new LocalDocker(Console.WriteLine).IsAvailableAsync(default).ConfigureAwait(false);
Assert.True(available, "Should have found a working daemon");
}
}
37 changes: 32 additions & 5 deletions Microsoft.NET.Build.Containers/ContainerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,26 @@ namespace Microsoft.NET.Build.Containers;

public static class ContainerBuilder
{
public static async Task Containerize(DirectoryInfo folder, string workingDir, string registryName, string baseName, string baseTag, string[] entrypoint, string[] entrypointArgs, string imageName, string[] imageTags, string? outputRegistry, string[] labels, Port[] exposedPorts, string[] envVars, string containerRuntimeIdentifier, string ridGraphPath, string localContainerDaemon)
public static async Task ContainerizeAsync(
DirectoryInfo folder,
string workingDir,
string registryName,
string baseName,
string baseTag,
string[] entrypoint,
string[] entrypointArgs,
string imageName,
string[] imageTags,
string? outputRegistry,
string[] labels,
Port[] exposedPorts,
string[] envVars,
string containerRuntimeIdentifier,
string ridGraphPath,
string localContainerDaemon,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var isDaemonPull = String.IsNullOrEmpty(registryName);
if (isDaemonPull)
{
Expand All @@ -22,7 +40,9 @@ public static async Task Containerize(DirectoryInfo folder, string workingDir, s
var isDockerPush = String.IsNullOrEmpty(outputRegistry);
var destinationImageReferences = imageTags.Select(t => new ImageReference(isDockerPush ? null : new Registry(ContainerHelpers.TryExpandRegistryToUri(outputRegistry!)), imageName, t));

ImageBuilder imageBuilder = await baseRegistry.GetImageManifest(baseName, baseTag, containerRuntimeIdentifier, ridGraphPath).ConfigureAwait(false);
ImageBuilder imageBuilder = await baseRegistry.GetImageManifestAsync(baseName, baseTag, containerRuntimeIdentifier, ridGraphPath, cancellationToken).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();

imageBuilder.SetWorkingDirectory(workingDir);

Expand Down Expand Up @@ -60,13 +80,20 @@ public static async Task Containerize(DirectoryInfo folder, string workingDir, s

BuiltImage builtImage = imageBuilder.Build();

cancellationToken.ThrowIfCancellationRequested();

foreach (var destinationImageReference in destinationImageReferences)
{
if (destinationImageReference.Registry is { } outReg)
{
try
{
outReg.Push(builtImage, sourceImageReference, destinationImageReference, (message) => Console.WriteLine($"Containerize: {message}")).Wait();
await outReg.PushAsync(
builtImage,
sourceImageReference,
destinationImageReference,
(message) => Console.WriteLine($"Containerize: {message}"),
cancellationToken).ConfigureAwait(false);
Console.WriteLine($"Containerize: Pushed container '{destinationImageReference.RepositoryAndTag}' to registry '{outputRegistry}'");
}
catch (Exception e)
Expand All @@ -79,15 +106,15 @@ public static async Task Containerize(DirectoryInfo folder, string workingDir, s
{

var localDaemon = GetLocalDaemon(localContainerDaemon, Console.WriteLine);
if (!(await localDaemon.IsAvailable().ConfigureAwait(false)))
if (!(await localDaemon.IsAvailableAsync(cancellationToken).ConfigureAwait(false)))
{
Console.WriteLine("Containerize: error CONTAINER007: The Docker daemon is not available, but pushing to a local daemon was requested. Please start Docker and try again.");
Environment.ExitCode = 7;
return;
}
try
{
localDaemon.Load(builtImage, sourceImageReference, destinationImageReference).Wait();
await localDaemon.LoadAsync(builtImage, sourceImageReference, destinationImageReference, cancellationToken).ConfigureAwait(false);
Console.WriteLine("Containerize: Pushed container '{0}' to Docker daemon", destinationImageReference.RepositoryAndTag);
}
catch (Exception e)
Expand Down
10 changes: 8 additions & 2 deletions Microsoft.NET.Build.Containers/LocalDaemons/ILocalDaemon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ internal interface ILocalDaemon {
/// <summary>
/// Loads an image (presumably from a tarball) into the local container runtime.
/// </summary>
public Task Load(BuiltImage image, ImageReference sourceReference, ImageReference destinationReference);
public Task LoadAsync(BuiltImage image, ImageReference sourceReference, ImageReference destinationReference, CancellationToken cancellationToken);

/// <summary>
/// Checks to see if the local container runtime is available. This is used to give nice errors to the user.
/// </summary>
public Task<bool> IsAvailable();
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken);

/// <summary>
/// Checks to see if the local container runtime is available. This is used to give nice errors to the user.
/// See <see cref="IsAvailableAsync(CancellationToken)"/> for async version.
/// </summary>
public bool IsAvailable();
}
Loading

0 comments on commit fd78a5b

Please sign in to comment.