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 }