zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/extensions/sync/remote.go (about)

     1  //go:build sync
     2  // +build sync
     3  
     4  package sync
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/containers/image/v5/docker"
    11  	dockerReference "github.com/containers/image/v5/docker/reference"
    12  	"github.com/containers/image/v5/manifest"
    13  	"github.com/containers/image/v5/types"
    14  	"github.com/opencontainers/go-digest"
    15  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    16  
    17  	"zotregistry.io/zot/pkg/api/constants"
    18  	"zotregistry.io/zot/pkg/common"
    19  	client "zotregistry.io/zot/pkg/extensions/sync/httpclient"
    20  	"zotregistry.io/zot/pkg/log"
    21  )
    22  
    23  type catalog struct {
    24  	Repositories []string `json:"repositories"`
    25  }
    26  
    27  type RemoteRegistry struct {
    28  	client  *client.Client
    29  	context *types.SystemContext
    30  	log     log.Logger
    31  }
    32  
    33  func NewRemoteRegistry(client *client.Client, logger log.Logger) Remote {
    34  	registry := &RemoteRegistry{}
    35  
    36  	registry.log = logger
    37  	registry.client = client
    38  	clientConfig := client.GetConfig()
    39  	registry.context = getUpstreamContext(clientConfig.CertDir, clientConfig.Username,
    40  		clientConfig.Password, clientConfig.TLSVerify)
    41  
    42  	return registry
    43  }
    44  
    45  func (registry *RemoteRegistry) GetContext() *types.SystemContext {
    46  	return registry.context
    47  }
    48  
    49  func (registry *RemoteRegistry) GetRepositories(ctx context.Context) ([]string, error) {
    50  	var catalog catalog
    51  
    52  	_, _, _, err := registry.client.MakeGetRequest(ctx, &catalog, "application/json", //nolint: dogsled
    53  		constants.RoutePrefix, constants.ExtCatalogPrefix)
    54  	if err != nil {
    55  		return []string{}, err
    56  	}
    57  
    58  	return catalog.Repositories, nil
    59  }
    60  
    61  func (registry *RemoteRegistry) GetImageReference(repo, reference string) (types.ImageReference, error) {
    62  	remoteHost := registry.client.GetHostname()
    63  
    64  	repoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", remoteHost, repo))
    65  	if err != nil {
    66  		registry.log.Error().Str("errorType", common.TypeOf(err)).Str("repo", repo).
    67  			Str("reference", reference).Str("remote", remoteHost).
    68  			Err(err).Msg("couldn't parse repository reference")
    69  
    70  		return nil, err
    71  	}
    72  
    73  	var namedRepoRef dockerReference.Named
    74  
    75  	digest, ok := parseReference(reference)
    76  	if ok {
    77  		namedRepoRef, err = dockerReference.WithDigest(repoRef, digest)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  	} else {
    82  		namedRepoRef, err = dockerReference.WithTag(repoRef, reference)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  	}
    87  
    88  	imageRef, err := docker.NewReference(namedRepoRef)
    89  	if err != nil {
    90  		registry.log.Err(err).Str("transport", docker.Transport.Name()).Str("reference", namedRepoRef.String()).
    91  			Msg("cannot obtain a valid image reference for given transport and reference")
    92  
    93  		return nil, err
    94  	}
    95  
    96  	return imageRef, nil
    97  }
    98  
    99  func (registry *RemoteRegistry) GetManifestContent(imageReference types.ImageReference) (
   100  	[]byte, string, digest.Digest, error,
   101  ) {
   102  	imageSource, err := imageReference.NewImageSource(context.Background(), registry.GetContext())
   103  	if err != nil {
   104  		return []byte{}, "", "", err
   105  	}
   106  
   107  	defer imageSource.Close()
   108  
   109  	manifestBuf, mediaType, err := imageSource.GetManifest(context.Background(), nil)
   110  	if err != nil {
   111  		return []byte{}, "", "", err
   112  	}
   113  
   114  	// if mediatype is docker then convert to OCI
   115  	switch mediaType {
   116  	case manifest.DockerV2Schema2MediaType:
   117  		manifestBuf, err = convertDockerManifestToOCI(imageSource, manifestBuf)
   118  		if err != nil {
   119  			return []byte{}, "", "", err
   120  		}
   121  	case manifest.DockerV2ListMediaType:
   122  		manifestBuf, err = convertDockerIndexToOCI(imageSource, manifestBuf)
   123  		if err != nil {
   124  			return []byte{}, "", "", err
   125  		}
   126  	}
   127  
   128  	return manifestBuf, ispec.MediaTypeImageManifest, digest.FromBytes(manifestBuf), nil
   129  }
   130  
   131  func (registry *RemoteRegistry) GetRepoTags(repo string) ([]string, error) {
   132  	remoteHost := registry.client.GetHostname()
   133  
   134  	tags, err := getRepoTags(context.Background(), registry.GetContext(), remoteHost, repo)
   135  	if err != nil {
   136  		registry.log.Error().Str("errorType", common.TypeOf(err)).Str("repo", repo).
   137  			Str("remote", remoteHost).Err(err).Msg("couldn't fetch tags for repo")
   138  
   139  		return []string{}, err
   140  	}
   141  
   142  	return tags, nil
   143  }