2023-05-31 12:26:23 -05:00
|
|
|
//go:build sync
|
|
|
|
// +build sync
|
|
|
|
|
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/containers/image/v5/docker"
|
|
|
|
dockerReference "github.com/containers/image/v5/docker/reference"
|
2023-06-21 13:05:52 -05:00
|
|
|
"github.com/containers/image/v5/manifest"
|
2023-05-31 12:26:23 -05:00
|
|
|
"github.com/containers/image/v5/types"
|
|
|
|
"github.com/opencontainers/go-digest"
|
2023-06-21 13:05:52 -05:00
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
2023-05-31 12:26:23 -05:00
|
|
|
|
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
2023-06-21 13:05:52 -05:00
|
|
|
// 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
|
2023-05-31 12:26:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|