Skip to content

Add support for public ecr #391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
4 changes: 4 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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),
Expand Down
89 changes: 89 additions & 0 deletions pkg/client/ecrpublic/ecr.go
Original file line number Diff line number Diff line change
@@ -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
}
25 changes: 25 additions & 0 deletions pkg/client/ecrpublic/path.go
Original file line number Diff line number Diff line change
@@ -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:]
}
106 changes: 106 additions & 0 deletions pkg/client/ecrpublic/path_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
Loading