mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
377aff1853
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>
143 lines
3.9 KiB
Go
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
|
|
}
|