Skip to content

Commit ee9f48b

Browse files
committed
merge from main
2 parents 2ad8afd + 36295f2 commit ee9f48b

26 files changed

+573
-398
lines changed

Directory.Build.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
<ItemGroup>
33
<PackageReference Include="Nerdbank.GitVersioning" PrivateAssets="all" IncludeAssets="build; buildMultitargeting" />
44
<PackageReference Include="Microsoft.VisualStudioEng.MicroBuild.Core" PrivateAssets="all" />
5-
<PackageReference Include="Microsoft.Net.Compilers.Toolset" PrivateAssets="all" />
65
</ItemGroup>
76
</Project>

Directory.Packages.props

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6+
<PackageVersion Include="coverlet.collector" Version="3.1.2" />
7+
<PackageVersion Include="GitHubActionsTestLogger" Version="2.0.1" />
8+
<PackageVersion Include="Microsoft.Build.Locator" Version="1.5.5" />
69
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.3.1" />
710
<PackageVersion Include="Microsoft.Build" Version="17.3.1" />
8-
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.4.0-4.final" />
911
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
1012
<PackageVersion Include="Microsoft.VisualStudioEng.MicroBuild.Core" Version="1.0.0" />
1113
<PackageVersion Include="MSTest.TestAdapter" Version="2.3.0-preview-20220810-02" />
1214
<PackageVersion Include="MSTest.TestFramework" Version="2.2.10" />
1315
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.5.109" />
14-
<PackageVersion Include="coverlet.collector" Version="3.1.2" />
15-
<PackageVersion Include="GitHubActionsTestLogger" Version="2.0.1" />
16-
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
17-
<PackageVersion Include="Microsoft.Build.Locator" Version="1.5.5" />
18-
<PackageVersion Include="Valleysoft.DockerCredsProvider" Version="2.1.0" />
1916
<PackageVersion Include="NuGet.Packaging" Version="6.3.1" />
17+
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
18+
<PackageVersion Include="Valleysoft.DockerCredsProvider" Version="2.1.0" />
2019
</ItemGroup>
2120
</Project>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.NET.Build.Containers;
8+
9+
/// <summary>
10+
/// A delegating handler that handles the special error handling needed for Amazon ECR.
11+
///
12+
/// When pushing images to ECR if the target container repository does not exist ECR ends
13+
/// the connection causing an IOException with a generic "The response ended prematurely."
14+
/// error message. The handler catches the generic error and provides a more informed error
15+
/// message to let the user know they need to create the repository.
16+
/// </summary>
17+
public class AmazonECRMessageHandler : DelegatingHandler
18+
{
19+
public AmazonECRMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
20+
21+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
22+
{
23+
try
24+
{
25+
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
26+
}
27+
catch (HttpRequestException e) when (e.InnerException is IOException ioe && ioe.Message.Equals("The response ended prematurely.", StringComparison.OrdinalIgnoreCase))
28+
{
29+
var message = "Request to Amazon Elastic Container Registry failed prematurely. This is often caused when the target repository does not exist in the registry.";
30+
throw new ContainerHttpException(message, request.RequestUri?.ToString(), null);
31+
}
32+
catch
33+
{
34+
throw;
35+
}
36+
}
37+
}

Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private record TokenResponse(string? token, string? access_token, int? expires_i
8585
/// <param name="scope"></param>
8686
/// <param name="cancellationToken"></param>
8787
/// <returns></returns>
88-
private async Task<AuthenticationHeaderValue?> GetAuthenticationAsync(string scheme, Uri uri, string service, string? scope, CancellationToken cancellationToken)
88+
private async Task<AuthenticationHeaderValue?> GetAuthenticationAsync(string registry, string scheme, Uri realm, string service, string? scope, CancellationToken cancellationToken)
8989
{
9090
// Allow overrides for auth via environment variables
9191
string? credU = Environment.GetEnvironmentVariable(ContainerHelpers.HostObjectUser);
@@ -102,26 +102,26 @@ private record TokenResponse(string? token, string? access_token, int? expires_i
102102
{
103103
try
104104
{
105-
privateRepoCreds = await CredsProvider.GetCredentialsAsync(uri.Host);
105+
privateRepoCreds = await CredsProvider.GetCredentialsAsync(registry);
106106
}
107107
catch (Exception e)
108108
{
109-
throw new CredentialRetrievalException(uri.Host, e);
109+
throw new CredentialRetrievalException(registry, e);
110110
}
111111
}
112112

113113
if (scheme is "Basic")
114114
{
115115
var basicAuth = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
116-
return AuthHeaderCache.AddOrUpdate(uri, basicAuth);
116+
return AuthHeaderCache.AddOrUpdate(realm, basicAuth);
117117
}
118118
else if (scheme is "Bearer")
119119
{
120120
// use those creds when calling the token provider
121121
var header = privateRepoCreds.Username == "<token>"
122122
? new AuthenticationHeaderValue("Bearer", privateRepoCreds.Password)
123123
: new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
124-
var builder = new UriBuilder(uri);
124+
var builder = new UriBuilder(realm);
125125
var queryDict = System.Web.HttpUtility.ParseQueryString("");
126126
queryDict["service"] = service;
127127
if (scope is string s)
@@ -143,7 +143,7 @@ private record TokenResponse(string? token, string? access_token, int? expires_i
143143

144144
// save the retrieved token in the cache
145145
var bearerAuth = new AuthenticationHeaderValue("Bearer", token.ResolvedToken);
146-
return AuthHeaderCache.AddOrUpdate(uri, bearerAuth);
146+
return AuthHeaderCache.AddOrUpdate(realm, bearerAuth);
147147
}
148148
else
149149
{
@@ -177,7 +177,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
177177
}
178178
else if (response is { StatusCode: HttpStatusCode.Unauthorized } && TryParseAuthenticationInfo(response, out string? scheme, out AuthInfo? authInfo))
179179
{
180-
if (await GetAuthenticationAsync(scheme, authInfo.Realm, authInfo.Service, authInfo.Scope, cancellationToken) is AuthenticationHeaderValue authentication)
180+
if (await GetAuthenticationAsync(request.RequestUri.Host, scheme, authInfo.Realm, authInfo.Service, authInfo.Scope, cancellationToken) is AuthenticationHeaderValue authentication)
181181
{
182182
request.Headers.Authorization = AuthHeaderCache.AddOrUpdate(request.RequestUri, authentication);
183183
return await base.SendAsync(request, cancellationToken);

Microsoft.NET.Build.Containers/ContainerHelpers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public enum PortType
1818

1919
public record Port(int number, PortType type);
2020

21+
2122
public static class ContainerHelpers
2223
{
2324
public const string HostObjectUser = "SDK_CONTAINER_REGISTRY_UNAME";

Microsoft.NET.Build.Containers/CreateNewImageToolTask.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,4 @@ private string Quote(string path)
9797

9898
return $"\"{path}\"";
9999
}
100-
}
100+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace Microsoft.NET.Build.Containers;
2+
3+
public static class KnownStrings
4+
{
5+
public static class Properties
6+
{
7+
public static string ContainerBaseImage = nameof(ContainerBaseImage);
8+
public static string ContainerRegistry = nameof(ContainerRegistry);
9+
public static string ContainerImageName = nameof(ContainerImageName);
10+
public static string ContainerImageTag = nameof(ContainerImageTag);
11+
public static string ContainerImageTags = nameof(ContainerImageTags);
12+
public static string ContainerWorkingDirectory = nameof(ContainerWorkingDirectory);
13+
public static string ContainerEntrypoint = nameof(ContainerEntrypoint);
14+
public static string UseAppHost = nameof(UseAppHost);
15+
public static string ContainerLabel = nameof(ContainerLabel);
16+
public static string SelfContained = nameof(SelfContained);
17+
public static string ContainerPort = nameof(ContainerPort);
18+
public static string ContainerEnvironmentVariable = nameof(ContainerEnvironmentVariable);
19+
20+
public static string ComputeContainerConfig = nameof(ComputeContainerConfig);
21+
public static string AssemblyName = nameof(AssemblyName);
22+
public static string ContainerBaseRegistry = nameof(ContainerBaseRegistry);
23+
public static string ContainerBaseName = nameof(ContainerBaseName);
24+
public static string ContainerBaseTag = nameof(ContainerBaseTag);
25+
}
26+
27+
public static class ErrorCodes
28+
{
29+
public static string CONTAINER001 = nameof(CONTAINER001);
30+
public static string CONTAINER004 = nameof(CONTAINER004);
31+
public static string CONTAINER005 = nameof(CONTAINER005);
32+
}
33+
}

Microsoft.NET.Build.Containers/LocalDocker.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,20 @@ public static async Task WriteImageToStream(Image x, string name, string tag, St
4747

4848
foreach (var d in x.LayerDescriptors)
4949
{
50-
if (x.originatingRegistry is null)
50+
if (x.originatingRegistry is {} registry)
5151
{
52-
throw new NotImplementedException("Need a good error for 'couldn't download a thing because no link to registry'");
53-
}
52+
string localPath = await registry.DownloadBlob(x.OriginatingName, d);
5453

55-
string localPath = await x.originatingRegistry.DownloadBlob(x.OriginatingName, d);
56-
57-
// Stuff that (uncompressed) tarball into the image tar stream
58-
// TODO uncompress!!
59-
string layerTarballPath = $"{d.Digest.Substring("sha256:".Length)}/layer.tar";
60-
await writer.WriteEntryAsync(localPath, layerTarballPath);
61-
layerTarballPaths.Add(layerTarballPath);
62-
}
54+
// Stuff that (uncompressed) tarball into the image tar stream
55+
// TODO uncompress!!
56+
string layerTarballPath = $"{d.Digest.Substring("sha256:".Length)}/layer.tar";
57+
await writer.WriteEntryAsync(localPath, layerTarballPath);
58+
layerTarballPaths.Add(layerTarballPath);
59+
}
60+
else
61+
{
62+
throw new NotImplementedException("Need a good error for 'couldn't download a thing because no link to registry'");
63+
} }
6364

6465
// add config
6566
string configTarballPath = $"{Image.GetSha(x.config)}.json";

Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
3131
<Compile Remove="*.*" />
3232
<Compile Include="ReferenceParser.cs" />
33+
<Compile Include="KnownStrings.cs" />
3334
<Compile Include="ParseContainerProperties.cs" />
3435
<Compile Include="CreateNewImage.Interface.cs" />
3536
<Compile Include="CreateNewImageToolTask.cs" />

Microsoft.NET.Build.Containers/ParseContainerProperties.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,15 @@ public override bool Execute()
115115
else
116116
{
117117
validTags = Array.Empty<string>();
118-
Log.LogError(null, "CONTAINER003", "Container.InvalidTag", null, 0, 0, 0, 0, $"Invalid {nameof(ContainerImageTag)} provided: {{0}}. Image tags must be alphanumeric, underscore, hyphen, or period.", ContainerImageTag);
118+
Log.LogError(null, KnownStrings.ErrorCodes.CONTAINER004, "Container.InvalidTag", null, 0, 0, 0, 0, "Invalid {0} provided: {1}. Image tags must be alphanumeric, underscore, hyphen, or period.", nameof(ContainerImageTag), ContainerImageTag);
119119
}
120120
}
121121
else if (ContainerImageTags.Length != 0 && TryValidateTags(ContainerImageTags, out var valids, out var invalids))
122122
{
123123
validTags = valids;
124124
if (invalids.Any())
125125
{
126-
(string message, string args) = invalids switch
127-
{
128-
{ Length: 1 } => ($"Invalid {nameof(ContainerImageTags)} provided: {{0}}. {nameof(ContainerImageTags)} must be a semicolon-delimited list of valid image tags. Image tags must be alphanumeric, underscore, hyphen, or period.", invalids[0]),
129-
_ => ($"Invalid {nameof(ContainerImageTags)} provided: {{0}}. {nameof(ContainerImageTags)} must be a semicolon-delimited list of valid image tags. Image tags must be alphanumeric, underscore, hyphen, or period.", String.Join(", ", invalids))
130-
};
131-
Log.LogError(null, "CONTAINER003", "Container.InvalidTag", null, 0, 0, 0, 0, message, args);
126+
Log.LogError(null, KnownStrings.ErrorCodes.CONTAINER004, "Container.InvalidTag", null, 0, 0, 0, 0, "Invalid {0} provided: {1}. {0} must be a semicolon-delimited list of valid image tags. Image tags must be alphanumeric, underscore, hyphen, or period.", nameof(ContainerImageTags), String.Join(",", invalids));
132127
return !Log.HasLoggedErrors;
133128
}
134129
}
@@ -163,10 +158,10 @@ public override bool Execute()
163158

164159
try
165160
{
166-
if (!ContainerHelpers.NormalizeImageName(ContainerImageName, out string? normalizedImageName))
161+
if (!ContainerHelpers.NormalizeImageName(ContainerImageName, out var normalizedImageName))
167162
{
168-
Log.LogMessage(MessageImportance.High, $"'{ContainerImageName}' was not a valid container image name, it was normalized to {normalizedImageName}");
169-
NewContainerImageName = normalizedImageName ?? "";
163+
Log.LogMessage(null, KnownStrings.ErrorCodes.CONTAINER001, "Container.InvalidImageName", null, 0, 0, 0, 0, MessageImportance.High, "'{0}' was not a valid container image name, it was normalized to '{1}'", nameof(ContainerImageName), normalizedImageName);
164+
NewContainerImageName = normalizedImageName!; // known to be not null due to output of NormalizeImageName
170165
}
171166
else
172167
{

0 commit comments

Comments
 (0)