mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
612a12e5a8
Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
172 lines
4.4 KiB
Go
172 lines
4.4 KiB
Go
//go:build sync
|
|
// +build sync
|
|
|
|
package sync
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/containers/image/v5/copy"
|
|
"github.com/containers/image/v5/docker"
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/image/v5/signature"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
"zotregistry.io/zot/pkg/common"
|
|
syncconf "zotregistry.io/zot/pkg/extensions/config/sync"
|
|
"zotregistry.io/zot/pkg/log"
|
|
"zotregistry.io/zot/pkg/test/inject"
|
|
)
|
|
|
|
const (
|
|
SyncBlobUploadDir = ".sync"
|
|
)
|
|
|
|
// Get sync.FileCredentials from file.
|
|
func getFileCredentials(filepath string) (syncconf.CredentialsFile, error) {
|
|
credsFile, err := os.ReadFile(filepath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var creds syncconf.CredentialsFile
|
|
|
|
err = json.Unmarshal(credsFile, &creds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return creds, nil
|
|
}
|
|
|
|
func getUpstreamContext(certDir, username, password string, tlsVerify bool) *types.SystemContext {
|
|
upstreamCtx := &types.SystemContext{}
|
|
upstreamCtx.DockerCertPath = certDir
|
|
upstreamCtx.DockerDaemonCertPath = certDir
|
|
|
|
if tlsVerify {
|
|
upstreamCtx.DockerDaemonInsecureSkipTLSVerify = false
|
|
upstreamCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(false)
|
|
} else {
|
|
upstreamCtx.DockerDaemonInsecureSkipTLSVerify = true
|
|
upstreamCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true)
|
|
}
|
|
|
|
if username != "" && password != "" {
|
|
upstreamCtx.DockerAuthConfig = &types.DockerAuthConfig{
|
|
Username: username,
|
|
Password: password,
|
|
}
|
|
}
|
|
|
|
return upstreamCtx
|
|
}
|
|
|
|
// sync needs transport to be stripped to not be wrongly interpreted as an image reference
|
|
// at a non-fully qualified registry (hostname as image and port as tag).
|
|
func StripRegistryTransport(url string) string {
|
|
return strings.Replace(strings.Replace(url, "http://", "", 1), "https://", "", 1)
|
|
}
|
|
|
|
// getRepoTags lists all tags in a repository.
|
|
// It returns a string slice of tags and any error encountered.
|
|
func getRepoTags(ctx context.Context, sysCtx *types.SystemContext, host, repo string) ([]string, error) {
|
|
repoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", host, repo))
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
dockerRef, err := docker.NewReference(reference.TagNameOnly(repoRef))
|
|
// hard to reach test case, injected error, see pkg/test/dev.go
|
|
if err = inject.Error(err); err != nil {
|
|
return nil, err // Should never happen for a reference with tag and no digest
|
|
}
|
|
|
|
tags, err := docker.GetRepositoryTags(ctx, sysCtx, dockerRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tags, nil
|
|
}
|
|
|
|
// parseRepositoryReference parses input into a reference.Named, and verifies that it names a repository, not an image.
|
|
func parseRepositoryReference(input string) (reference.Named, error) {
|
|
ref, err := reference.ParseNormalizedNamed(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !reference.IsNameOnly(ref) {
|
|
return nil, zerr.ErrInvalidRepositoryName
|
|
}
|
|
|
|
return ref, nil
|
|
}
|
|
|
|
// parse a reference, return its digest and if it's valid.
|
|
func parseReference(reference string) (digest.Digest, bool) {
|
|
var ok bool
|
|
|
|
d, err := digest.Parse(reference)
|
|
if err == nil {
|
|
ok = true
|
|
}
|
|
|
|
return d, ok
|
|
}
|
|
|
|
func getCopyOptions(upstreamCtx, localCtx *types.SystemContext) copy.Options {
|
|
options := copy.Options{
|
|
DestinationCtx: localCtx,
|
|
SourceCtx: upstreamCtx,
|
|
ReportWriter: io.Discard,
|
|
ForceManifestMIMEType: ispec.MediaTypeImageManifest, // force only oci manifest MIME type
|
|
ImageListSelection: copy.CopyAllImages,
|
|
}
|
|
|
|
return options
|
|
}
|
|
|
|
func getPolicyContext(log log.Logger) (*signature.PolicyContext, error) {
|
|
policy := &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
|
|
|
policyContext, err := signature.NewPolicyContext(policy)
|
|
if err := inject.Error(err); err != nil {
|
|
log.Error().Str("errorType", common.TypeOf(err)).
|
|
Err(err).Msg("couldn't create policy context")
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return policyContext, nil
|
|
}
|
|
|
|
func getSupportedMediaType() []string {
|
|
return []string{
|
|
ispec.MediaTypeImageIndex,
|
|
ispec.MediaTypeImageManifest,
|
|
manifest.DockerV2ListMediaType,
|
|
manifest.DockerV2Schema2MediaType,
|
|
}
|
|
}
|
|
|
|
func isSupportedMediaType(mediaType string) bool {
|
|
mediaTypes := getSupportedMediaType()
|
|
for _, m := range mediaTypes {
|
|
if m == mediaType {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|