Files
zot/pkg/extensions/sync/remote.go
T
tamil vanan d0de12d2d3 [Feat]: Support for AWS ECR Authentication with Temporary Tokens (#2907)
feat: add support for aws ecr authentication

Signed-off-by: K Tamil Vanan <vanan@arcesium.com>
2025-01-26 11:38:22 +02:00

197 lines
5.2 KiB
Go

//go:build sync
// +build sync
package sync
import (
"context"
"fmt"
"net/url"
"strings"
"github.com/containers/image/v5/docker"
dockerReference "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"zotregistry.dev/zot/pkg/api/constants"
"zotregistry.dev/zot/pkg/common"
client "zotregistry.dev/zot/pkg/extensions/sync/httpclient"
"zotregistry.dev/zot/pkg/log"
)
type catalog struct {
Repositories []string `json:"repositories"`
}
type RemoteRegistry struct {
client *client.Client
context *types.SystemContext
log log.Logger
}
func NewRemoteRegistry(client *client.Client, logger log.Logger) Remote {
registry := &RemoteRegistry{}
registry.log = logger
registry.client = client
clientConfig := client.GetConfig()
registry.context = getUpstreamContext(clientConfig.CertDir, clientConfig.Username,
clientConfig.Password, clientConfig.TLSVerify)
return registry
}
func (registry *RemoteRegistry) SetUpstreamAuthConfig(username, password string) {
registry.context.DockerAuthConfig = &types.DockerAuthConfig{
Username: username,
Password: password,
}
}
func (registry *RemoteRegistry) GetContext() *types.SystemContext {
return registry.context
}
func (registry *RemoteRegistry) GetRepositories(ctx context.Context) ([]string, error) {
var catalog catalog
_, header, _, err := registry.client.MakeGetRequest(ctx, &catalog, "application/json", "", //nolint: dogsled
constants.RoutePrefix, constants.ExtCatalogPrefix)
if err != nil {
return []string{}, err
}
var repos []string
repos = append(repos, catalog.Repositories...)
link := header.Get("Link")
for link != "" {
linkURLPart, _, _ := strings.Cut(link, ";")
linkURL, err := url.Parse(strings.Trim(linkURLPart, "<>"))
if err != nil {
return catalog.Repositories, err
}
_, header, _, err := registry.client.MakeGetRequest(ctx, &catalog, "application/json",
linkURL.RawQuery, constants.RoutePrefix, constants.ExtCatalogPrefix) //nolint: dogsled
if err != nil {
return repos, err
}
repos = append(repos, catalog.Repositories...)
link = header.Get("Link")
}
return repos, nil
}
func (registry *RemoteRegistry) GetDockerRemoteRepo(repo string) string {
dockerNamespace := "library"
dockerRegistry := "docker.io"
remoteHost := registry.client.GetHostname()
repoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", remoteHost, repo))
if err != nil {
return repo
}
if !strings.Contains(repo, dockerNamespace) &&
strings.Contains(repoRef.String(), dockerNamespace) &&
strings.Contains(repoRef.String(), dockerRegistry) {
return fmt.Sprintf("%s/%s", dockerNamespace, repo)
}
return repo
}
func (registry *RemoteRegistry) GetImageReference(repo, reference string) (types.ImageReference, error) {
remoteHost := registry.client.GetHostname()
repoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", remoteHost, repo))
if err != nil {
registry.log.Error().Str("errorType", common.TypeOf(err)).Str("repo", repo).
Str("reference", reference).Str("remote", remoteHost).
Err(err).Msg("couldn't parse repository reference")
return nil, err
}
var namedRepoRef dockerReference.Named
digest, ok := parseReference(reference)
if ok {
namedRepoRef, err = dockerReference.WithDigest(repoRef, digest)
if err != nil {
return nil, err
}
} else {
namedRepoRef, err = dockerReference.WithTag(repoRef, reference)
if err != nil {
return nil, err
}
}
imageRef, err := docker.NewReference(namedRepoRef)
if err != nil {
registry.log.Err(err).Str("transport", docker.Transport.Name()).Str("reference", namedRepoRef.String()).
Msg("cannot obtain a valid image reference for given transport and reference")
return nil, err
}
return imageRef, nil
}
func (registry *RemoteRegistry) GetManifestContent(imageReference types.ImageReference) (
[]byte, string, digest.Digest, error,
) {
imageSource, err := imageReference.NewImageSource(context.Background(), registry.GetContext())
if err != nil {
return []byte{}, "", "", err
}
defer imageSource.Close()
manifestBuf, mediaType, err := imageSource.GetManifest(context.Background(), nil)
if err != nil {
return []byte{}, "", "", err
}
// if mediatype is docker then convert to OCI
switch mediaType {
case manifest.DockerV2Schema2MediaType:
manifestBuf, err = convertDockerManifestToOCI(imageSource, manifestBuf)
if err != nil {
return []byte{}, "", "", err
}
case manifest.DockerV2ListMediaType:
manifestBuf, err = convertDockerIndexToOCI(imageSource, manifestBuf)
if err != nil {
return []byte{}, "", "", err
}
}
return manifestBuf, ispec.MediaTypeImageManifest, digest.FromBytes(manifestBuf), nil
}
func (registry *RemoteRegistry) GetRepoTags(repo string) ([]string, error) {
remoteHost := registry.client.GetHostname()
tags, err := getRepoTags(context.Background(), registry.GetContext(), remoteHost, repo)
if err != nil {
registry.log.Error().Str("errorType", common.TypeOf(err)).Str("repo", repo).
Str("remote", remoteHost).Err(err).Msg("couldn't fetch tags for repo")
return []string{}, err
}
return tags, nil
}