0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/extensions/sync/remote.go
peusebiu 377aff1853
fix(sync): fixed skipping docker images when they already synced (#1521)
before syncing an image we first check if it's already present in our storage
to do that we get the manifest from remote and compare it with the local one
but in the case of syncing docker images, because the conversion to OCI format is done while
syncing, we get a docker manifest before conversion, so sync detects that local manifest and
remote one are different, so it starts syncing again.

to overcome this, convert remote docker manifests to OCI manifests and then compare.

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
2023-06-21 11:05:52 -07:00

143 lines
3.9 KiB
Go

//go:build sync
// +build sync
package sync
import (
"context"
"fmt"
"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.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/common"
client "zotregistry.io/zot/pkg/extensions/sync/httpclient"
"zotregistry.io/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) GetContext() *types.SystemContext {
return registry.context
}
func (registry *RemoteRegistry) GetRepositories(ctx context.Context) ([]string, error) {
var catalog catalog
_, _, _, err := registry.client.MakeGetRequest(&catalog, "application/json", //nolint: dogsled
constants.RoutePrefix, constants.ExtCatalogPrefix)
if err != nil {
return []string{}, err
}
return catalog.Repositories, nil
}
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
}