Skip to content
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

feat: add support for registry mirrors #8244

Merged
merged 22 commits into from
Jan 22, 2025
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b9d46d7
feat(flag): add registry.mirrors flag
DmitriyLewen Jan 14, 2025
f798d68
feat(mage): handle map[string][]string flags + add example for regist…
DmitriyLewen Jan 14, 2025
04851e1
feat(options): add RegistryMirrors
DmitriyLewen Jan 15, 2025
8c4f6c1
feat(remote): check mirrors for `Get` and `Image` functions
DmitriyLewen Jan 15, 2025
696be1f
fix(remote): use replace to set mirror
DmitriyLewen Jan 15, 2025
392c920
test: add testcases
DmitriyLewen Jan 15, 2025
678ba5c
test: update testcase
DmitriyLewen Jan 15, 2025
c3682e0
test: add testcase for image without docker.io prefix
DmitriyLewen Jan 15, 2025
ccbf7c7
docs: add info about mirrors in docs
DmitriyLewen Jan 16, 2025
f9b6d01
refactor: use `index.docker.io` in example docs
DmitriyLewen Jan 16, 2025
3d6e223
docs: update mirror title
DmitriyLewen Jan 17, 2025
f097a43
refactor: don't use `ctx` variable
DmitriyLewen Jan 17, 2025
8d269d2
refactor: return error for wrong mirrors
DmitriyLewen Jan 17, 2025
ad56617
refactor: add tryImage and tryGet functions
DmitriyLewen Jan 17, 2025
d7a1b7d
test: add `bad credential for multiple mirrors`
DmitriyLewen Jan 17, 2025
d082591
docs: add order and example for mirrors
DmitriyLewen Jan 17, 2025
539e9f4
fix: linter error
DmitriyLewen Jan 17, 2025
f3247f1
docs: fix display of steps
DmitriyLewen Jan 17, 2025
04f1bc5
refactor(mage): remove example for mirrors
DmitriyLewen Jan 17, 2025
e3a9123
chore: add comment about WithMirror
DmitriyLewen Jan 17, 2025
7102e45
fix: logs about used mirrors
DmitriyLewen Jan 20, 2025
2981a6f
refactor: reduce duplicates
knqyf263 Jan 21, 2025
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
Prev Previous commit
Next Next commit
feat(remote): check mirrors for Get and Image functions
  • Loading branch information
DmitriyLewen committed Jan 15, 2025
commit 8c4f6c1895b7bc300d32924aa1e243129f0596f3
114 changes: 77 additions & 37 deletions pkg/remote/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"github.com/aquasecurity/trivy/pkg/version/app"
)

var logger = log.WithPrefix("remote")

type Descriptor = remote.Descriptor

// Get is a wrapper of google/go-containerregistry/pkg/v1/remote.Get
Expand All @@ -36,39 +38,46 @@ func Get(ctx context.Context, ref name.Reference, option types.RegistryOptions)
}

var errs error
// Try each authentication method until it succeeds
for _, authOpt := range authOptions(ctx, ref, option) {
remoteOpts := []remote.Option{
remote.WithTransport(tr),
authOpt,
}

if option.Platform.Platform != nil {
p, err := resolvePlatform(ref, option.Platform, remoteOpts)
if err != nil {
return nil, xerrors.Errorf("platform error: %w", err)
// Try each mirrors/host until it succeeds
for _, r := range append(registryMirrors(ref, option), ref) {
knqyf263 marked this conversation as resolved.
Show resolved Hide resolved
// Try each authentication method until it succeeds
knqyf263 marked this conversation as resolved.
Show resolved Hide resolved
for _, authOpt := range authOptions(ctx, r, option) {
remoteOpts := []remote.Option{
remote.WithTransport(tr),
authOpt,
}
// Don't pass platform when the specified image is single-arch.
if p.Platform != nil {
remoteOpts = append(remoteOpts, remote.WithPlatform(*p.Platform))

if option.Platform.Platform != nil {
p, err := resolvePlatform(r, option.Platform, remoteOpts)
if err != nil {
return nil, xerrors.Errorf("platform error: %w", err)
}
// Don't pass platform when the specified image is single-arch.
if p.Platform != nil {
remoteOpts = append(remoteOpts, remote.WithPlatform(*p.Platform))
}
}
}

desc, err := remote.Get(ref, remoteOpts...)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
var desc *remote.Descriptor
desc, err = remote.Get(r, remoteOpts...)
if err != nil {
errs = multierror.Append(errs, err)
continue
}

if option.Platform.Force {
if err = satisfyPlatform(desc, lo.FromPtr(option.Platform.Platform)); err != nil {
return nil, err
if option.Platform.Force {
if err = satisfyPlatform(desc, lo.FromPtr(option.Platform.Platform)); err != nil {
return nil, err
}
}
if ref.Context().RegistryStr() != r.Context().RegistryStr() {
log.WithPrefix("remote").Info("Mirror was used to get remote image", log.String("image", ref.String()), log.String("mirror", r.Context().RegistryStr()))
}
return desc, nil
}
return desc, nil
}

// No authentication succeeded
// No authentication for mirrors/host succeeded
return nil, errs
}

Expand All @@ -81,21 +90,28 @@ func Image(ctx context.Context, ref name.Reference, option types.RegistryOptions
}

var errs error
// Try each authentication method until it succeeds
for _, authOpt := range authOptions(ctx, ref, option) {
remoteOpts := []remote.Option{
remote.WithTransport(tr),
authOpt,
}
index, err := remote.Image(ref, remoteOpts...)
if err != nil {
errs = multierror.Append(errs, err)
continue
// Try each mirrors/host until it succeeds
for _, r := range append(registryMirrors(ref, option), ref) {
// Try each authentication method until it succeeds
for _, authOpt := range authOptions(ctx, r, option) {
remoteOpts := []remote.Option{
remote.WithTransport(tr),
authOpt,
}
index, err := remote.Image(r, remoteOpts...)
if err != nil {
errs = multierror.Append(errs, err)
continue
}

if ref.Context().RegistryStr() != r.Context().RegistryStr() {
log.WithPrefix("remote").Info("Mirror was used to get remote image", log.String("image", ref.String()), log.String("mirror", r.Context().RegistryStr()))
}
return index, nil
}
return index, nil
}

// No authentication succeeded
// No authentication for mirrors/host succeeded
return nil, errs
}

Expand Down Expand Up @@ -126,6 +142,30 @@ func Referrers(ctx context.Context, d name.Digest, option types.RegistryOptions)
return nil, errs
}

func registryMirrors(hostRef name.Reference, option types.RegistryOptions) []name.Reference {
var mirrors []name.Reference

ctx := hostRef.Context()
reg := ctx.RegistryStr()
DmitriyLewen marked this conversation as resolved.
Show resolved Hide resolved
repo := ctx.RepositoryStr()
if ms, ok := option.RegistryMirrors[reg]; ok {
for _, m := range ms {
var nameOpts []name.Option
if option.Insecure {
nameOpts = append(nameOpts, name.Insecure)
}
mirrorImageName := fmt.Sprintf("%s/%s", m, repo)
ref, err := name.ParseReference(mirrorImageName, nameOpts...)
if err != nil {
log.WithPrefix("remote").Warn("Unable to parse mirror of image", log.String("mirror", mirrorImageName))
knqyf263 marked this conversation as resolved.
Show resolved Hide resolved
continue
}
mirrors = append(mirrors, ref)
}
}
return mirrors
}

func httpTransport(option types.RegistryOptions) (http.RoundTripper, error) {
d := &net.Dialer{
Timeout: 10 * time.Minute,
Expand Down