Repository navigation
Conversation
The connect guard refused every loopback or private address, including the proxy connection, so pushes failed behind any local or corporate proxy, and ECR behind an interface VPC endpoint (which resolves to private addresses) was refused too. - Skip the address check for an Amazon ECR registry host and for the configured proxy: the target of the handler's CONNECT tunnel request, or the proxy that IWebProxy.GetProxy selects (not when it returns the destination itself). Every other host, including caller-supplied base registries, is still checked. - Match the ECR host pattern against the host alone, anchored with \z. - Report transport failures (DNS, connection, TLS, proxy, the guard) as RegistryException with the HttpRequestException as the inner exception. - RegistryClient.Create takes internal proxy and resolveHost parameters so tests can drive a local proxy and simulate private DNS.
…n errors BlobExistsAsync and ManifestExistsAsync returned false for any status other than 200. With an expired ECR token a push treated every base layer as missing and downloaded it before failing on the upload. - A HEAD success or redirect means present, 404 means absent, and anything else throws. - 401, 403 and ECR's 400 DENIED after the handshake become RegistryAuthenticationException, which now carries StatusCode. ECR answers a HEAD with a bare 400, so a GET of /v2/ is used to read the DENIED error. - The message for a client that sends no credentials (base-image pulls) points at a missing repository or tag rather than credentials.
A layer has no entries for the parent folders of mapped files, and some image consumers reject an image whose new folders have files but no entries of their own. AddDirectory adds a 0755 folder entry without a local source. Building a layer now also rejects a file or folder beneath a path that is mapped as a file.
.NET's PAX writer names each extended header PaxHeaders.<process id>, so the same files built in two processes gave different layer and image digests, and every build uploaded a new layer. Entries are now ustar, which has no atime or ctime fields, with fixed times and owners. An entry ustar cannot hold (a name too long for its fields, or a file of 8 GiB or more) falls back to PAX; whether it fits is decided by asking .NET's ustar writer. Directory names longer than 100 bytes drop their trailing slash so the name field is never left empty. GNU entries were not used: .NET 8 writes the current time into their atime and ctime, and the epoch written there makes Python's tarfile misread names.
System.Progress<T> raises callbacks on thread-pool threads when there is no synchronization context, which breaks PowerShell scriptblocks (no runspace) and any sink that is not thread-safe. LayerPushProgressQueue queues events for the caller to drain with TryDequeue or DequeueAll. The EcrPushOptions docs and README now describe Progress<T> accurately and show the queue.
OciImageBuilder.BaseImageDigest is the digest of the platform manifest the build is based on, and BaseImageIndexDigest the manifest list it was selected from (null for a single-platform reference). An internal FromBaseImageAsync overload takes the source client factory for tests.
| string registryHost, | ||
| IRegistryCredentialSource credentialSource, | ||
| TimeSpan? timeout = null, | ||
| IWebProxy? proxy = null, |
There was a problem hiding this comment.
I don't see how the user can take advantage of the proxy support. Do we need to add an option in EcrPushOptions for user to be able to specify the proxy and timeout?
| /// folders the base image already has, or their mode is reset to <c>0755</c>. | ||
| /// </remarks> | ||
| /// <param name="containerPath">The absolute folder path inside the container (for example <c>/opt/app/lib</c>).</param> | ||
| public void AddDirectory(string containerPath) |
There was a problem hiding this comment.
I would prefer AddEmptyDirectory to call out this is a special API to add something that doesn't map to something public.
| /// writer, against a throwaway stream, rather than re-implementing its rule for splitting a long name into the | ||
| /// prefix and name fields (it does not accept every split the format allows). | ||
| /// </summary> | ||
| internal static bool FitsUstar(TarEntryType type, string name, long size) |
There was a problem hiding this comment.
I was nervous about adding this complexity for the reproducible output and figured in our case most build images would change. Looking at your change for this it doesn't look too bad. I'm not fan of using the exception handling for control flow and worry if MSFT changes where the exception is thrown we would break. What do you think of this version:
// ustar header limits: a 100-byte name field, and an 11-digit octal size field, so the
// largest size is 8^11 - 1 (one byte under 8 GiB).
private const int UstarNameFieldLength = 100;
private const long UstarMaxSize = (1L << 33) - 1;
/// <summary>
/// Whether an entry can be written as ustar: its size fits the size field and its UTF-8 name fits
/// the name field. Longer names use PAX. ustar's 155-byte prefix field isn't used, because .NET
/// decides how to split a name between the two fields and doesn't document how.
/// </summary>
internal static bool FitsUstar(string name, long size)
=> size <= UstarMaxSize && Encoding.UTF8.GetByteCount(name) <= UstarNameFieldLength;
We also wouldn't need the HeaderProbeStream type as well with this approach.
These are fixes for the larger items in my review of #2602, one commit per finding, each with its tests. Merge, cherry-pick or close as you see fit; each commit builds and passes on its own. The smaller items are suggested changes on #2602 instead.
Tests and checks:
tarfileboth read them.HttpClient.DefaultProxy, both receiveCONNECT. The ECR VPC endpoint case is covered by a test that resolves the ECR host to a private address. It hasn't been run in a real VPC.Behavior changes worth a look:
RegistryAuthenticationException(now withStatusCode), notRegistryRequestException.RegistryException, notHttpRequestException.Internal test seams:
RegistryClient.CreatetakesproxyandresolveHost, there's aRegistryClientconstructor overload withanonymous,FromBaseImageAsynchas an overload that takes the source client factory, andLayer.CreateEntry/FitsUstarare internal.