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