Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static async Task PublishImageAsync(
BuiltImage singleArchImage,
SourceImageReference sourceImageReference,
DestinationImageReference destinationImageReference,
bool skipPushIfAlreadyPresent,
Microsoft.Build.Utilities.TaskLoggingHelper Log,
Telemetry telemetry,
CancellationToken cancellationToken)
Expand All @@ -37,7 +38,12 @@ await PushToRemoteRegistryAsync(
destinationImageReference,
Log,
cancellationToken,
destinationImageReference.RemoteRegistry!.PushAsync,
(image, source, destination, token) => destinationImageReference.RemoteRegistry!.PushAsync(
image,
source,
destination,
skipPushIfAlreadyPresent,
token),
Strings.ContainerBuilder_ImageUploadedToRegistry).ConfigureAwait(false);
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.get -> b
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateDigestLabel.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPublishing.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPushIfAlreadyPresent.get -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.SkipPushIfAlreadyPresent.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.get -> Microsoft.Build.Framework.ITaskItem![]!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GeneratedContainerNames.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ImageFormat.get -> string?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ internal DefaultManifestOperations(Uri baseUri, string registryName, HttpClient
_registryName = registryName;
}

public async Task<bool> ExistsAsync(string repositoryName, string reference, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, new Uri(_baseUri, $"/v2/{repositoryName}/manifests/{reference}")).AcceptManifestFormats();
using HttpResponseMessage response = await _client.SendAsync(request, cancellationToken).ConfigureAwait(false);
return response.StatusCode switch
{
HttpStatusCode.OK => true,
_ when (int)response.StatusCode >= 500 => await LogAndThrowContainerHttpException<bool>(response, cancellationToken).ConfigureAwait(false),
_ => false,
};
}

public async Task<HttpResponseMessage> GetAsync(string repositoryName, string reference, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace Microsoft.NET.Build.Containers;
/// </remarks>
internal interface IManifestOperations
{
public Task<bool> ExistsAsync(string repositoryName, string reference, CancellationToken cancellationToken);

public Task<HttpResponseMessage> GetAsync(string repositoryName, string reference, CancellationToken cancellationToken);

public Task PutAsync(string repositoryName, string reference, string manifestListJson, string mediaType, CancellationToken cancellationToken);
Expand Down
50 changes: 32 additions & 18 deletions src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,13 +595,24 @@ public async Task PushManifestListAsync(
}

public Task PushAsync(BuiltImage builtImage, SourceImageReference source, DestinationImageReference destination, CancellationToken cancellationToken)
=> PushAsync(builtImage, source, destination, pushTags: true, cancellationToken);
=> PushAsync(builtImage, source, destination, skipIfManifestExists: false, cancellationToken);
Comment thread
jetersen marked this conversation as resolved.
Outdated

private async Task PushAsync(BuiltImage builtImage, SourceImageReference source, DestinationImageReference destination, bool pushTags, CancellationToken cancellationToken)
public Task PushAsync(BuiltImage builtImage, SourceImageReference source, DestinationImageReference destination, bool skipIfManifestExists, CancellationToken cancellationToken)
=> PushAsync(builtImage, source, destination, pushTags: true, skipIfManifestExists, cancellationToken);
Comment thread
jetersen marked this conversation as resolved.
Outdated

private async Task PushAsync(BuiltImage builtImage, SourceImageReference source, DestinationImageReference destination, bool pushTags, bool skipIfManifestExists, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Registry destinationRegistry = destination.RemoteRegistry!;

bool manifestExists = skipIfManifestExists &&
await _registryAPI.Manifest.ExistsAsync(destination.Repository, builtImage.ManifestDigest, cancellationToken).ConfigureAwait(false);

if (manifestExists)
{
_logger.LogInformation(Strings.Registry_ManifestExists, builtImage.ManifestDigest, destination.Repository);
}

Func<Descriptor, Task> uploadLayerFunc = async (descriptor) =>
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down Expand Up @@ -634,25 +645,28 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source,
}
};

if (SupportsParallelUploads)
if (!manifestExists)
{
await Task.WhenAll(builtImage.LayerDescriptors.Select(descriptor => uploadLayerFunc(descriptor))).ConfigureAwait(false);
}
else
{
foreach (var descriptor in builtImage.LayerDescriptors)
if (SupportsParallelUploads)
{
await uploadLayerFunc(descriptor).ConfigureAwait(false);
await Task.WhenAll(builtImage.LayerDescriptors.Select(descriptor => uploadLayerFunc(descriptor))).ConfigureAwait(false);
}
else
{
foreach (var descriptor in builtImage.LayerDescriptors)
{
await uploadLayerFunc(descriptor).ConfigureAwait(false);
}
}
}

cancellationToken.ThrowIfCancellationRequested();
using (MemoryStream stringStream = new(Encoding.UTF8.GetBytes(builtImage.Config)))
{
var configDigest = builtImage.ImageDigest!;
_logger.LogInformation(Strings.Registry_ConfigUploadStarted, configDigest);
await UploadBlobAsync(destination.Repository, configDigest, stringStream, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_ConfigUploaded);
cancellationToken.ThrowIfCancellationRequested();
using (MemoryStream stringStream = new(Encoding.UTF8.GetBytes(builtImage.Config)))
{
var configDigest = builtImage.ImageDigest!;
_logger.LogInformation(Strings.Registry_ConfigUploadStarted, configDigest);
await UploadBlobAsync(destination.Repository, configDigest, stringStream, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(Strings.Registry_ConfigUploaded);
}
}

// Tags can refer to an image manifest or an image manifest list.
Expand All @@ -668,7 +682,7 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source,
_logger.LogInformation(Strings.Registry_TagUploaded, tag, RegistryName);
}
}
else
else if (!manifestExists)
{
_logger.LogInformation(Strings.Registry_ManifestUploadStarted, RegistryName, builtImage.ManifestDigest);
await _registryAPI.Manifest.PutAsync(destination.Repository, builtImage.ManifestDigest, builtImage.Manifest, builtImage.ManifestMediaType, cancellationToken).ConfigureAwait(false);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@
<value>Uploaded manifest to '{0}'.</value>
<comment>{0} is the registry name</comment>
</data>
<data name="Registry_ManifestExists" xml:space="preserve">
<value>Manifest '{0}' already exists in repository '{1}'. Skipping layer and configuration uploads.</value>
<comment>{0} is the manifest digest, {1} is the repository name</comment>
</data>
<data name="Registry_ManifestUploadStarted" xml:space="preserve">
<value>Uploading manifest to registry '{0}' as blob '{1}'.</value>
<comment>{0} is the registry name</comment>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading