From c8be71ca5c703d740c02e4de0d31d6c9e42b1c38 Mon Sep 17 00:00:00 2001 From: Fernando Perez Osan Date: Tue, 8 Jul 2025 17:32:52 -0300 Subject: [PATCH 1/2] Add support for public ecr Signed-off-by: Fernando Perez Osan --- go.mod | 1 + go.sum | 2 + pkg/client/client.go | 4 ++ pkg/client/ecrpublic/ecr.go | 89 +++++++++++++++++++++++++ pkg/client/ecrpublic/path.go | 25 +++++++ pkg/client/ecrpublic/path_test.go | 106 ++++++++++++++++++++++++++++++ 6 files changed, 227 insertions(+) create mode 100644 pkg/client/ecrpublic/ecr.go create mode 100644 pkg/client/ecrpublic/path.go create mode 100644 pkg/client/ecrpublic/path_test.go diff --git a/go.mod b/go.mod index 2263841b..328ba34f 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.29.17 github.com/aws/aws-sdk-go-v2/credentials v1.17.70 github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 github.com/bombsimon/logrusr/v4 v4.1.0 github.com/go-chi/transport v0.5.0 github.com/gofri/go-github-ratelimit v1.1.1 diff --git a/go.sum b/go.sum index a43c58ed..af667baa 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 h1:Bwzh202Aq7/MYnAjXA9VawCf6u+hjwMdoYmZ4HYsdf8= github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1/go.mod h1:xZzWl9AXYa6zsLLH41HBFW8KRKJRIzlGmvSM0mVMIX4= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 h1:XJ/AEFYj9VFPJdF+VFi4SUPEDfz1akHwxxm07JfZJcs= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2/go.mod h1:JUBHdhvKbbKmhaHjLsKJAWnQL80T6nURmhB/LEprV+4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI= diff --git a/pkg/client/client.go b/pkg/client/client.go index fd54cd08..949be435 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -12,6 +12,7 @@ import ( "github.com/jetstack/version-checker/pkg/client/acr" "github.com/jetstack/version-checker/pkg/client/docker" "github.com/jetstack/version-checker/pkg/client/ecr" + "github.com/jetstack/version-checker/pkg/client/ecrpublic" "github.com/jetstack/version-checker/pkg/client/fallback" "github.com/jetstack/version-checker/pkg/client/gcr" "github.com/jetstack/version-checker/pkg/client/ghcr" @@ -38,6 +39,7 @@ type Client struct { type Options struct { ACR acr.Options ECR ecr.Options + ECRPublic ecrpublic.Options // Add ECRPublic options GCR gcr.Options GHCR ghcr.Options Docker docker.Options @@ -54,6 +56,7 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) if opts.Transport != nil { opts.Quay.Transporter = opts.Transport opts.ECR.Transporter = opts.Transport + opts.ECRPublic.Transporter = opts.Transport // Add ECR public transporter opts.GHCR.Transporter = opts.Transport opts.GCR.Transporter = opts.Transport } @@ -106,6 +109,7 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) selfhostedClients, acrClient, ecr.New(opts.ECR), + ecrpublic.New(opts.ECRPublic), dockerClient, gcr.New(opts.GCR), ghcr.New(opts.GHCR), diff --git a/pkg/client/ecrpublic/ecr.go b/pkg/client/ecrpublic/ecr.go new file mode 100644 index 00000000..64772fe4 --- /dev/null +++ b/pkg/client/ecrpublic/ecr.go @@ -0,0 +1,89 @@ +package ecrpublic + +import ( + "context" + "fmt" + "net/http" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + + "github.com/aws/aws-sdk-go-v2/service/ecrpublic" + "github.com/jetstack/version-checker/pkg/api" + "github.com/jetstack/version-checker/pkg/client/util" +) + +type Client struct { + Config aws.Config + + Options +} + +type Options struct { + IamRoleArn string + AccessKeyID string + SecretAccessKey string + SessionToken string + Transporter http.RoundTripper +} + +func New(opts Options) *Client { + return &Client{ + Options: opts, + } +} + +func (c *Client) Name() string { + return "ecrpublic" +} + +func (c *Client) Tags(ctx context.Context, host, repo, image string) ([]api.ImageTag, error) { + client, err := c.createClient(ctx, "us-east-1") + if err != nil { + return nil, err + } + repoName := util.JoinRepoImage(repo, image) + images, err := client.DescribeImages(ctx, &ecrpublic.DescribeImagesInput{ + RepositoryName: &repoName, + }) + + if err != nil { + return nil, fmt.Errorf("failed to describe images: %s", err) + } + + var tags []api.ImageTag + for _, img := range images.ImageDetails { + // Continue early if no tags available + if len(img.ImageTags) == 0 { + tags = append(tags, api.ImageTag{ + SHA: *img.ImageDigest, + Timestamp: *img.ImagePushedAt, + }) + + continue + } + + for _, tag := range img.ImageTags { + tags = append(tags, api.ImageTag{ + SHA: *img.ImageDigest, + Timestamp: *img.ImagePushedAt, + Tag: tag, + }) + } + } + // For public ECR, RegistryId is not required, so id can be left empty + + return tags, nil +} + +func (c *Client) createClient(ctx context.Context, region string) (*ecrpublic.Client, error) { + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(region), + ) + if err != nil { + return nil, err + } + + client := ecrpublic.NewFromConfig(cfg) + return client, nil +} diff --git a/pkg/client/ecrpublic/path.go b/pkg/client/ecrpublic/path.go new file mode 100644 index 00000000..674e5f12 --- /dev/null +++ b/pkg/client/ecrpublic/path.go @@ -0,0 +1,25 @@ +package ecrpublic + +import ( + "regexp" + "strings" +) + +var ( + // For public ECR, we only need to match the exact hostname "public.ecr.aws" + ecrPublicPattern = regexp.MustCompile(`^public\.ecr\.aws$`) +) + +func (c *Client) IsHost(host string) bool { + return ecrPublicPattern.MatchString(host) +} + +func (c *Client) RepoImageFromPath(path string) (string, string) { + lastIndex := strings.LastIndex(path, "/") + + if lastIndex == -1 { + return "", path + } + + return path[:lastIndex], path[lastIndex+1:] +} diff --git a/pkg/client/ecrpublic/path_test.go b/pkg/client/ecrpublic/path_test.go new file mode 100644 index 00000000..6bece7a6 --- /dev/null +++ b/pkg/client/ecrpublic/path_test.go @@ -0,0 +1,106 @@ +package ecrpublic + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsHost(t *testing.T) { + tests := map[string]struct { + host string + expIs bool + }{ + "an empty host should be false": { + host: "", + expIs: false, + }, + "random string should be false": { + host: "foobar", + expIs: false, + }, + "random string with dots should be false": { + host: "foobar.foo", + expIs: false, + }, + "just amazonawsaws.com should be false": { + host: "amazonaws.com", + expIs: false, + }, + "ecr.foo.amazonaws.com with random sub domains should be false": { + host: "bar.ecr.foo.amazonaws.com", + expIs: false, + }, + "dkr.ecr.foo.amazonaws.com with random sub domains should be false": { + host: "dkr.ecr.foo.amazonaws.com", + expIs: false, + }, + "hello123.dkr.ecr.foo.amazonaws.com false": { + host: "hello123.dkr.ecr.foo.amazonaws.com", + expIs: false, + }, + "123hello.hello.dkr.ecr.foo.amazonaws.com false": { + host: "123hello.hello.dkr.ecr.foo.amazonaws.com", + expIs: false, + }, + "123hello.dkr.ecr.foo.amazonaws.comfoo false": { + host: "123hello.dkr.ecr.foo.amazonaws.comfoo", + expIs: false, + }, + "public.ecr.aws should be true": { + host: "public.ecr.aws", + expIs: true, + }, + "public.ecr.aws with random subdomains should be false": { + host: "foo.public.ecr.aws", + expIs: false, + }, + } + + handler := new(Client) + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if isHost := handler.IsHost(test.host); isHost != test.expIs { + t.Errorf("%s: unexpected IsHost, exp=%t got=%t", + test.host, test.expIs, isHost) + } + }) + } +} + +func TestRepoImage(t *testing.T) { + tests := map[string]struct { + path string + expRepo, expImage string + }{ + "single image should return as image": { + path: "kube-scheduler", + expRepo: "", + expImage: "kube-scheduler", + }, + "two segments to path should return both": { + path: "jetstack-cre/version-checker", + expRepo: "jetstack-cre", + expImage: "version-checker", + }, + "multiple segments to path should return all in repo, last segment image": { + path: "k8s-artifacts-prod/ingress-nginx/nginx", + expRepo: "k8s-artifacts-prod/ingress-nginx", + expImage: "nginx", + }, + "region": { + path: "000000000000.dkr.ecr.eu-west-2.amazonaws.com/version-checker", + expRepo: "000000000000.dkr.ecr.eu-west-2.amazonaws.com", + expImage: "version-checker", + }, + } + + handler := new(Client) + for name, test := range tests { + t.Run(name, func(t *testing.T) { + repo, image := handler.RepoImageFromPath(test.path) + assert.Equal(t, repo, test.expRepo) + assert.Equal(t, image, test.expImage) + }) + } +} From 3bd6b182c65dc9c6579b99256f75d8df99b02dea Mon Sep 17 00:00:00 2001 From: Fernando Perez Osan Date: Fri, 8 Aug 2025 16:55:04 -0300 Subject: [PATCH 2/2] Don't use ecrpublic library, adopt approach similar to docker --- go.mod | 1 - go.sum | 2 - pkg/client/client.go | 6 +- pkg/client/ecrpublic/ecr.go | 89 ----------------- pkg/client/ecrpublic/ecrpublic.go | 152 ++++++++++++++++++++++++++++++ pkg/client/ecrpublic/path.go | 9 +- pkg/client/ecrpublic/path_test.go | 79 +++++++--------- pkg/client/ecrpublic/types.go | 11 +++ 8 files changed, 204 insertions(+), 145 deletions(-) delete mode 100644 pkg/client/ecrpublic/ecr.go create mode 100644 pkg/client/ecrpublic/ecrpublic.go create mode 100644 pkg/client/ecrpublic/types.go diff --git a/go.mod b/go.mod index 328ba34f..2263841b 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,6 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.29.17 github.com/aws/aws-sdk-go-v2/credentials v1.17.70 github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 github.com/bombsimon/logrusr/v4 v4.1.0 github.com/go-chi/transport v0.5.0 github.com/gofri/go-github-ratelimit v1.1.1 diff --git a/go.sum b/go.sum index af667baa..a43c58ed 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,6 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 h1:Bwzh202Aq7/MYnAjXA9VawCf6u+hjwMdoYmZ4HYsdf8= github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1/go.mod h1:xZzWl9AXYa6zsLLH41HBFW8KRKJRIzlGmvSM0mVMIX4= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 h1:XJ/AEFYj9VFPJdF+VFi4SUPEDfz1akHwxxm07JfZJcs= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2/go.mod h1:JUBHdhvKbbKmhaHjLsKJAWnQL80T6nURmhB/LEprV+4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI= diff --git a/pkg/client/client.go b/pkg/client/client.go index 949be435..0ad3b18e 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -69,6 +69,10 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) if err != nil { return nil, fmt.Errorf("failed to create docker client: %w", err) } + ecrPublicClient, err := ecrpublic.New(opts.ECRPublic, log) + if err != nil { + return nil, fmt.Errorf("failed to create ecr public client: %w", err) + } var selfhostedClients []api.ImageClient for _, sOpts := range opts.Selfhosted { @@ -109,7 +113,7 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) selfhostedClients, acrClient, ecr.New(opts.ECR), - ecrpublic.New(opts.ECRPublic), + ecrPublicClient, dockerClient, gcr.New(opts.GCR), ghcr.New(opts.GHCR), diff --git a/pkg/client/ecrpublic/ecr.go b/pkg/client/ecrpublic/ecr.go deleted file mode 100644 index 64772fe4..00000000 --- a/pkg/client/ecrpublic/ecr.go +++ /dev/null @@ -1,89 +0,0 @@ -package ecrpublic - -import ( - "context" - "fmt" - "net/http" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - - "github.com/aws/aws-sdk-go-v2/service/ecrpublic" - "github.com/jetstack/version-checker/pkg/api" - "github.com/jetstack/version-checker/pkg/client/util" -) - -type Client struct { - Config aws.Config - - Options -} - -type Options struct { - IamRoleArn string - AccessKeyID string - SecretAccessKey string - SessionToken string - Transporter http.RoundTripper -} - -func New(opts Options) *Client { - return &Client{ - Options: opts, - } -} - -func (c *Client) Name() string { - return "ecrpublic" -} - -func (c *Client) Tags(ctx context.Context, host, repo, image string) ([]api.ImageTag, error) { - client, err := c.createClient(ctx, "us-east-1") - if err != nil { - return nil, err - } - repoName := util.JoinRepoImage(repo, image) - images, err := client.DescribeImages(ctx, &ecrpublic.DescribeImagesInput{ - RepositoryName: &repoName, - }) - - if err != nil { - return nil, fmt.Errorf("failed to describe images: %s", err) - } - - var tags []api.ImageTag - for _, img := range images.ImageDetails { - // Continue early if no tags available - if len(img.ImageTags) == 0 { - tags = append(tags, api.ImageTag{ - SHA: *img.ImageDigest, - Timestamp: *img.ImagePushedAt, - }) - - continue - } - - for _, tag := range img.ImageTags { - tags = append(tags, api.ImageTag{ - SHA: *img.ImageDigest, - Timestamp: *img.ImagePushedAt, - Tag: tag, - }) - } - } - // For public ECR, RegistryId is not required, so id can be left empty - - return tags, nil -} - -func (c *Client) createClient(ctx context.Context, region string) (*ecrpublic.Client, error) { - cfg, err := config.LoadDefaultConfig(ctx, - config.WithRegion(region), - ) - if err != nil { - return nil, err - } - - client := ecrpublic.NewFromConfig(cfg) - return client, nil -} diff --git a/pkg/client/ecrpublic/ecrpublic.go b/pkg/client/ecrpublic/ecrpublic.go new file mode 100644 index 00000000..5bd0f44f --- /dev/null +++ b/pkg/client/ecrpublic/ecrpublic.go @@ -0,0 +1,152 @@ +package ecrpublic + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "time" + + "github.com/sirupsen/logrus" + + retryablehttp "github.com/hashicorp/go-retryablehttp" + "github.com/jetstack/version-checker/pkg/api" + "github.com/jetstack/version-checker/pkg/client/util" +) + +const ( + ecrPublicLookupURL = "https://public.ecr.aws/v2/%s/%s/tags/list" + loginURL = "https://public.ecr.aws/token/?service=ecr-public" +) + +type Options struct { + Username string + Password string + Transporter http.RoundTripper +} + +type Client struct { + *http.Client + Options +} + +func New(opts Options, log *logrus.Entry) (*Client, error) { + retryclient := retryablehttp.NewClient() + if opts.Transporter != nil { + retryclient.HTTPClient.Transport = opts.Transporter + } + retryclient.HTTPClient.Timeout = 10 * time.Second + retryclient.RetryMax = 10 + retryclient.RetryWaitMax = 10 * time.Minute + retryclient.RetryWaitMin = 1 * time.Second + // This custom backoff will fail requests that have a max wait of the RetryWaitMax + retryclient.Backoff = util.HTTPBackOff + retryclient.Logger = log.WithField("client", "ecrpublic") + client := retryclient.StandardClient() + + return &Client{ + Options: opts, + Client: client, + }, nil +} + +func (c *Client) Name() string { + return "ecrpublic" +} + +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 +} diff --git a/pkg/client/ecrpublic/path.go b/pkg/client/ecrpublic/path.go index 674e5f12..d43db192 100644 --- a/pkg/client/ecrpublic/path.go +++ b/pkg/client/ecrpublic/path.go @@ -6,7 +6,6 @@ import ( ) var ( - // For public ECR, we only need to match the exact hostname "public.ecr.aws" ecrPublicPattern = regexp.MustCompile(`^public\.ecr\.aws$`) ) @@ -15,11 +14,7 @@ func (c *Client) IsHost(host string) bool { } func (c *Client) RepoImageFromPath(path string) (string, string) { - lastIndex := strings.LastIndex(path, "/") + parts := strings.Split(path, "/") - if lastIndex == -1 { - return "", path - } - - return path[:lastIndex], path[lastIndex+1:] + return parts[0], strings.Join(parts[1:], "/") } diff --git a/pkg/client/ecrpublic/path_test.go b/pkg/client/ecrpublic/path_test.go index 6bece7a6..3cdd6872 100644 --- a/pkg/client/ecrpublic/path_test.go +++ b/pkg/client/ecrpublic/path_test.go @@ -1,10 +1,6 @@ package ecrpublic -import ( - "testing" - - "github.com/stretchr/testify/assert" -) +import "testing" func TestIsHost(t *testing.T) { tests := map[string]struct { @@ -19,39 +15,35 @@ func TestIsHost(t *testing.T) { host: "foobar", expIs: false, }, - "random string with dots should be false": { - host: "foobar.foo", - expIs: false, - }, - "just amazonawsaws.com should be false": { - host: "amazonaws.com", - expIs: false, - }, - "ecr.foo.amazonaws.com with random sub domains should be false": { - host: "bar.ecr.foo.amazonaws.com", + "path with two segments should be false": { + host: "joshvanl/version-checker", expIs: false, }, - "dkr.ecr.foo.amazonaws.com with random sub domains should be false": { - host: "dkr.ecr.foo.amazonaws.com", + "path with three segments should be false": { + host: "jetstack/joshvanl/version-checker", expIs: false, }, - "hello123.dkr.ecr.foo.amazonaws.com false": { - host: "hello123.dkr.ecr.foo.amazonaws.com", + "random string with dots should be false": { + host: "foobar.foo", expIs: false, }, - "123hello.hello.dkr.ecr.foo.amazonaws.com false": { - host: "123hello.hello.dkr.ecr.foo.amazonaws.com", + "docker.io should be false": { + host: "docker.io", expIs: false, }, - "123hello.dkr.ecr.foo.amazonaws.comfoo false": { - host: "123hello.dkr.ecr.foo.amazonaws.comfoo", + "docker.com should be false": { + host: "docker.com", expIs: false, }, - "public.ecr.aws should be true": { + "just public.ecr.aws should be true": { host: "public.ecr.aws", expIs: true, }, - "public.ecr.aws with random subdomains should be false": { + "public.ecr.aws.foo should be false": { + host: "public.ecr.aws.foo", + expIs: false, + }, + "foo.public.ecr.aws should be false": { host: "foo.public.ecr.aws", expIs: false, }, @@ -68,30 +60,25 @@ func TestIsHost(t *testing.T) { } } -func TestRepoImage(t *testing.T) { +func TestRepoImageFromPath(t *testing.T) { tests := map[string]struct { path string expRepo, expImage string }{ - "single image should return as image": { - path: "kube-scheduler", - expRepo: "", - expImage: "kube-scheduler", - }, - "two segments to path should return both": { - path: "jetstack-cre/version-checker", - expRepo: "jetstack-cre", - expImage: "version-checker", + "single image should return registry and image": { + path: "nginx", + expRepo: "nginx", + expImage: "", }, - "multiple segments to path should return all in repo, last segment image": { - path: "k8s-artifacts-prod/ingress-nginx/nginx", - expRepo: "k8s-artifacts-prod/ingress-nginx", - expImage: "nginx", + "two segments to path should return registry and repo": { + path: "eks-distro/kubernetes", + expRepo: "eks-distro", + expImage: "kubernetes", }, - "region": { - path: "000000000000.dkr.ecr.eu-west-2.amazonaws.com/version-checker", - expRepo: "000000000000.dkr.ecr.eu-west-2.amazonaws.com", - expImage: "version-checker", + "three segments to path should return registry and combined repo": { + path: "eks-distro/kubernetes/kube-proxy", + expRepo: "eks-distro", + expImage: "kubernetes/kube-proxy", }, } @@ -99,8 +86,10 @@ func TestRepoImage(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { repo, image := handler.RepoImageFromPath(test.path) - assert.Equal(t, repo, test.expRepo) - assert.Equal(t, image, test.expImage) + if repo != test.expRepo || image != test.expImage { + t.Errorf("%s: unexpected repo/image, exp=%s/%s got=%s/%s", + test.path, test.expRepo, test.expImage, repo, image) + } }) } } diff --git a/pkg/client/ecrpublic/types.go b/pkg/client/ecrpublic/types.go new file mode 100644 index 00000000..a44a2997 --- /dev/null +++ b/pkg/client/ecrpublic/types.go @@ -0,0 +1,11 @@ +package ecrpublic + +type AuthResponse struct { + Token string `json:"token"` +} + +type TagResponse struct { + Next string `json:"next"` + Name string `json:"name"` + Tags []string `json:"tags"` +}