2021-06-08 15:11:18 -05:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2021-10-28 04:10:01 -05:00
|
|
|
"io"
|
2021-06-08 15:11:18 -05:00
|
|
|
"os"
|
|
|
|
"regexp"
|
2021-12-02 12:45:26 -05:00
|
|
|
goSync "sync"
|
2021-06-08 15:11:18 -05:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Masterminds/semver"
|
|
|
|
"github.com/containers/common/pkg/retry"
|
|
|
|
"github.com/containers/image/v5/copy"
|
|
|
|
"github.com/containers/image/v5/docker"
|
|
|
|
"github.com/containers/image/v5/docker/reference"
|
|
|
|
"github.com/containers/image/v5/signature"
|
|
|
|
"github.com/containers/image/v5/types"
|
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
"gopkg.in/resty.v1"
|
2021-12-03 22:50:58 -05:00
|
|
|
"zotregistry.io/zot/errors"
|
|
|
|
"zotregistry.io/zot/pkg/log"
|
|
|
|
"zotregistry.io/zot/pkg/storage"
|
2022-02-18 06:36:50 -05:00
|
|
|
"zotregistry.io/zot/pkg/test"
|
2021-06-08 15:11:18 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-10-28 04:10:01 -05:00
|
|
|
SyncBlobUploadDir = ".sync"
|
2021-06-08 15:11:18 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// /v2/_catalog struct.
|
|
|
|
type catalog struct {
|
|
|
|
Repositories []string `json:"repositories"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// key is registry address.
|
|
|
|
type CredentialsFile map[string]Credentials
|
|
|
|
|
|
|
|
type Credentials struct {
|
|
|
|
Username string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Config struct {
|
2021-12-28 08:29:30 -05:00
|
|
|
Enable *bool
|
2021-06-08 15:11:18 -05:00
|
|
|
CredentialsFile string
|
|
|
|
Registries []RegistryConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
type RegistryConfig struct {
|
2021-12-29 10:14:56 -05:00
|
|
|
URLs []string
|
2021-06-08 15:11:18 -05:00
|
|
|
PollInterval time.Duration
|
|
|
|
Content []Content
|
|
|
|
TLSVerify *bool
|
|
|
|
OnDemand bool
|
|
|
|
CertDir string
|
2022-01-10 11:06:12 -05:00
|
|
|
MaxRetries *int
|
|
|
|
RetryDelay *time.Duration
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type Content struct {
|
|
|
|
Prefix string
|
|
|
|
Tags *Tags
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tags struct {
|
|
|
|
Regex *string
|
|
|
|
Semver *bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// getUpstreamCatalog gets all repos from a registry.
|
2021-12-29 10:14:56 -05:00
|
|
|
func getUpstreamCatalog(client *resty.Client, upstreamURL string, log log.Logger) (catalog, error) {
|
2021-06-08 15:11:18 -05:00
|
|
|
var c catalog
|
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
registryCatalogURL := fmt.Sprintf("%s%s", upstreamURL, "/v2/_catalog")
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
resp, err := client.R().SetHeader("Content-Type", "application/json").Get(registryCatalogURL)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Msgf("couldn't query %s", registryCatalogURL)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return c, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.IsError() {
|
|
|
|
log.Error().Msgf("couldn't query %s, status code: %d, body: %s", registryCatalogURL,
|
|
|
|
resp.StatusCode(), resp.Body())
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return c, errors.ErrSyncMissingCatalog
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(resp.Body(), &c)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Str("body", string(resp.Body())).Msg("couldn't unmarshal registry's catalog")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return c, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getImageTags lists all tags in a repository.
|
|
|
|
// It returns a string slice of tags and any error encountered.
|
|
|
|
func getImageTags(ctx context.Context, sysCtx *types.SystemContext, repoRef reference.Named) ([]string, error) {
|
|
|
|
dockerRef, err := docker.NewReference(reference.TagNameOnly(repoRef))
|
2022-02-18 06:36:50 -05:00
|
|
|
// hard to reach test case, injected error, see pkg/test/dev.go
|
|
|
|
if err = test.Error(err); err != nil {
|
2021-06-08 15:11:18 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
// filterImagesByTagRegex filters images by tag regex given in the config.
|
2021-06-08 15:11:18 -05:00
|
|
|
func filterImagesByTagRegex(upstreamReferences *[]types.ImageReference, content Content, log log.Logger) error {
|
|
|
|
refs := *upstreamReferences
|
|
|
|
|
|
|
|
if content.Tags == nil {
|
|
|
|
// no need to filter anything
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if content.Tags.Regex != nil {
|
|
|
|
log.Info().Msgf("start filtering using the regular expression: %s", *content.Tags.Regex)
|
|
|
|
|
|
|
|
tagReg, err := regexp.Compile(*content.Tags.Regex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
numTags := 0
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
for _, ref := range refs {
|
|
|
|
tagged := getTagFromRef(ref, log)
|
|
|
|
if tagged != nil {
|
|
|
|
if tagReg.MatchString(tagged.Tag()) {
|
2021-12-13 14:23:31 -05:00
|
|
|
refs[numTags] = ref
|
|
|
|
numTags++
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
refs = refs[:numTags]
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
*upstreamReferences = refs
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// filterImagesBySemver filters images by checking if their tags are semver compliant.
|
|
|
|
func filterImagesBySemver(upstreamReferences *[]types.ImageReference, content Content, log log.Logger) {
|
|
|
|
refs := *upstreamReferences
|
|
|
|
|
|
|
|
if content.Tags == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if content.Tags.Semver != nil && *content.Tags.Semver {
|
|
|
|
log.Info().Msg("start filtering using semver compliant rule")
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
numTags := 0
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
for _, ref := range refs {
|
|
|
|
tagged := getTagFromRef(ref, log)
|
|
|
|
if tagged != nil {
|
|
|
|
_, ok := semver.NewVersion(tagged.Tag())
|
|
|
|
if ok == nil {
|
2021-12-13 14:23:31 -05:00
|
|
|
refs[numTags] = ref
|
|
|
|
numTags++
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
refs = refs[:numTags]
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
*upstreamReferences = refs
|
|
|
|
}
|
|
|
|
|
|
|
|
// imagesToCopyFromRepos lists all images given a registry name and its repos.
|
2021-10-28 04:10:01 -05:00
|
|
|
func imagesToCopyFromUpstream(registryName string, repos []string, upstreamCtx *types.SystemContext,
|
2021-06-08 15:11:18 -05:00
|
|
|
content Content, log log.Logger) ([]types.ImageReference, error) {
|
|
|
|
var upstreamReferences []types.ImageReference
|
|
|
|
|
|
|
|
for _, repoName := range repos {
|
|
|
|
repoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", registryName, repoName))
|
|
|
|
if err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Msgf("couldn't parse repository reference: %s", repoRef)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
tags, err := getImageTags(context.Background(), upstreamCtx, repoRef)
|
2021-06-08 15:11:18 -05:00
|
|
|
if err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Msgf("couldn't fetch tags for %s", repoRef)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tag := range tags {
|
2021-12-07 13:26:26 -05:00
|
|
|
// don't copy cosign signature, containers/image doesn't support it
|
|
|
|
// we will copy it manually later
|
|
|
|
if isCosignTag(tag) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
taggedRef, err := reference.WithTag(repoRef, tag)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Msgf("error creating a reference for repository %s and tag %q", repoRef.Name(), tag)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ref, err := docker.NewReference(taggedRef)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Msgf("cannot obtain a valid image reference for transport %q and reference %s",
|
|
|
|
docker.Transport.Name(), taggedRef.String())
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
upstreamReferences = append(upstreamReferences, ref)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debug().Msgf("upstream refs to be copied: %v", upstreamReferences)
|
|
|
|
|
|
|
|
err := filterImagesByTagRegex(&upstreamReferences, content, log)
|
|
|
|
if err != nil {
|
|
|
|
return []types.ImageReference{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debug().Msgf("remaining upstream refs to be copied: %v", upstreamReferences)
|
2022-01-10 11:06:12 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
filterImagesBySemver(&upstreamReferences, content, log)
|
|
|
|
|
|
|
|
log.Debug().Msgf("remaining upstream refs to be copied: %v", upstreamReferences)
|
|
|
|
|
|
|
|
return upstreamReferences, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCopyOptions(upstreamCtx, localCtx *types.SystemContext) copy.Options {
|
|
|
|
options := copy.Options{
|
2021-12-07 13:26:26 -05:00
|
|
|
DestinationCtx: localCtx,
|
|
|
|
SourceCtx: upstreamCtx,
|
|
|
|
ReportWriter: io.Discard,
|
|
|
|
ForceManifestMIMEType: ispec.MediaTypeImageManifest, // force only oci manifest MIME type
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return options
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUpstreamContext(regCfg *RegistryConfig, credentials Credentials) *types.SystemContext {
|
|
|
|
upstreamCtx := &types.SystemContext{}
|
|
|
|
upstreamCtx.DockerCertPath = regCfg.CertDir
|
|
|
|
upstreamCtx.DockerDaemonCertPath = regCfg.CertDir
|
|
|
|
|
|
|
|
if regCfg.TLSVerify != nil && *regCfg.TLSVerify {
|
|
|
|
upstreamCtx.DockerDaemonInsecureSkipTLSVerify = false
|
|
|
|
upstreamCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(false)
|
|
|
|
} else {
|
|
|
|
upstreamCtx.DockerDaemonInsecureSkipTLSVerify = true
|
|
|
|
upstreamCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
if credentials != (Credentials{}) {
|
|
|
|
upstreamCtx.DockerAuthConfig = &types.DockerAuthConfig{
|
|
|
|
Username: credentials.Username,
|
|
|
|
Password: credentials.Password,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return upstreamCtx
|
|
|
|
}
|
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
func syncRegistry(regCfg RegistryConfig, upstreamURL string, storeController storage.StoreController,
|
2022-01-10 11:06:12 -05:00
|
|
|
localCtx *types.SystemContext, policyCtx *signature.PolicyContext, credentials Credentials, log log.Logger) error {
|
2021-12-29 10:14:56 -05:00
|
|
|
log.Info().Msgf("syncing registry: %s", upstreamURL)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
log.Debug().Msg("getting upstream context")
|
|
|
|
|
|
|
|
upstreamCtx := getUpstreamContext(®Cfg, credentials)
|
|
|
|
options := getCopyOptions(upstreamCtx, localCtx)
|
|
|
|
|
2022-01-10 11:06:12 -05:00
|
|
|
retryOptions := &retry.RetryOptions{}
|
|
|
|
|
|
|
|
if regCfg.MaxRetries != nil {
|
|
|
|
retryOptions.MaxRetry = *regCfg.MaxRetries
|
|
|
|
if regCfg.RetryDelay != nil {
|
|
|
|
retryOptions.Delay = *regCfg.RetryDelay
|
|
|
|
}
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var catalog catalog
|
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
httpClient, err := getHTTPClient(®Cfg, upstreamURL, credentials, log)
|
2021-12-07 13:26:26 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
2021-12-29 10:14:56 -05:00
|
|
|
catalog, err = getUpstreamCatalog(httpClient, upstreamURL, log)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}, retryOptions); err != nil {
|
|
|
|
log.Error().Err(err).Msg("error while getting upstream catalog, retrying...")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-25 07:04:39 -05:00
|
|
|
log.Info().Msgf("filtering %d repos based on sync prefixes", len(catalog.Repositories))
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-11-25 07:04:39 -05:00
|
|
|
repos := filterRepos(catalog.Repositories, regCfg.Content, log)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
log.Info().Msgf("got repos: %v", repos)
|
|
|
|
|
|
|
|
var images []types.ImageReference
|
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
upstreamAddr := StripRegistryTransport(upstreamURL)
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
for contentID, repos := range repos {
|
|
|
|
r := repos
|
|
|
|
id := contentID
|
|
|
|
|
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
2021-12-29 10:14:56 -05:00
|
|
|
refs, err := imagesToCopyFromUpstream(upstreamAddr, r, upstreamCtx, regCfg.Content[id], log)
|
2021-06-08 15:11:18 -05:00
|
|
|
images = append(images, refs...)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}, retryOptions); err != nil {
|
|
|
|
log.Error().Err(err).Msg("error while getting images references from upstream, retrying...")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(images) == 0 {
|
|
|
|
log.Info().Msg("no images to copy, no need to sync")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ref := range images {
|
2022-01-10 11:06:12 -05:00
|
|
|
upstreamImageRef := ref
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2022-01-10 11:06:12 -05:00
|
|
|
repo := getRepoFromRef(upstreamImageRef, upstreamAddr)
|
|
|
|
tag := getTagFromRef(upstreamImageRef, log).Tag()
|
2021-10-28 04:10:01 -05:00
|
|
|
|
2022-01-10 11:06:12 -05:00
|
|
|
imageStore := storeController.GetImageStore(repo)
|
2021-10-28 04:10:01 -05:00
|
|
|
|
2021-12-17 11:34:22 -05:00
|
|
|
canBeSkipped, err := canSkipImage(repo, tag, upstreamImageRef, imageStore, upstreamCtx, log)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("couldn't check if the upstream image %s can be skipped",
|
|
|
|
upstreamImageRef.DockerReference())
|
|
|
|
}
|
|
|
|
|
|
|
|
if canBeSkipped {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-02-18 06:36:50 -05:00
|
|
|
localImageRef, localCachePath, err := getLocalImageRef(imageStore, repo, tag)
|
2021-06-08 15:11:18 -05:00
|
|
|
if err != nil {
|
2022-01-10 11:06:12 -05:00
|
|
|
log.Error().Err(err).Msgf("couldn't obtain a valid image reference for reference %s/%s:%s",
|
|
|
|
localCachePath, repo, tag)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-18 06:36:50 -05:00
|
|
|
defer os.RemoveAll(localCachePath)
|
|
|
|
|
2021-12-17 11:34:22 -05:00
|
|
|
log.Info().Msgf("copying image %s to %s", upstreamImageRef.DockerReference(), localCachePath)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
2022-01-10 11:06:12 -05:00
|
|
|
_, err = copy.Image(context.Background(), policyCtx, localImageRef, upstreamImageRef, &options)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}, retryOptions); err != nil {
|
2021-12-17 11:34:22 -05:00
|
|
|
log.Error().Err(err).Msgf("error while copying image %s to %s",
|
|
|
|
upstreamImageRef.DockerReference(), localCachePath)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-01-10 11:06:12 -05:00
|
|
|
err = pushSyncedLocalImage(repo, tag, localCachePath, storeController, log)
|
2021-10-28 04:10:01 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("error while pushing synced cached image %s",
|
2022-01-10 11:06:12 -05:00
|
|
|
fmt.Sprintf("%s/%s:%s", localCachePath, repo, tag))
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2021-12-07 13:26:26 -05:00
|
|
|
|
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
2022-01-10 11:06:12 -05:00
|
|
|
err = syncSignatures(httpClient, storeController, upstreamURL, repo, tag, log)
|
2021-12-07 13:26:26 -05:00
|
|
|
|
|
|
|
return err
|
|
|
|
}, retryOptions); err != nil {
|
2021-12-17 11:34:22 -05:00
|
|
|
log.Error().Err(err).Msgf("couldn't copy image signature %s", upstreamImageRef.DockerReference())
|
2021-12-07 13:26:26 -05:00
|
|
|
}
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
log.Info().Msgf("finished syncing %s", upstreamAddr)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
func getLocalContexts(log log.Logger) (*types.SystemContext, *signature.PolicyContext, error) {
|
2021-06-08 15:11:18 -05:00
|
|
|
log.Debug().Msg("getting local context")
|
|
|
|
|
|
|
|
var policy *signature.Policy
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
localCtx := &types.SystemContext{}
|
2021-12-07 13:26:26 -05:00
|
|
|
// preserve compression
|
|
|
|
localCtx.OCIAcceptUncompressedLayers = true
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
// accept any image with or without signature
|
|
|
|
policy = &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
policyContext, err := signature.NewPolicyContext(policy)
|
2022-02-18 06:36:50 -05:00
|
|
|
if err := test.Error(err); err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Msg("couldn't create policy context")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return &types.SystemContext{}, &signature.PolicyContext{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return localCtx, policyContext, nil
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
func Run(cfg Config, storeController storage.StoreController, wtgrp *goSync.WaitGroup, logger log.Logger) error {
|
2021-06-08 15:11:18 -05:00
|
|
|
var credentialsFile CredentialsFile
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
var err error
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
if cfg.CredentialsFile != "" {
|
|
|
|
credentialsFile, err = getFileCredentials(cfg.CredentialsFile)
|
|
|
|
if err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
logger.Error().Err(err).Msgf("couldn't get registry credentials from %s", cfg.CredentialsFile)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
localCtx, policyCtx, err := getLocalContexts(logger)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// for each upstream registry, start a go routine.
|
2021-06-08 15:11:18 -05:00
|
|
|
for _, regCfg := range cfg.Registries {
|
2021-10-25 07:05:03 -05:00
|
|
|
// if content not provided, don't run periodically sync
|
2021-11-25 07:04:39 -05:00
|
|
|
if len(regCfg.Content) == 0 {
|
2021-12-29 10:14:56 -05:00
|
|
|
logger.Info().Msgf("sync config content not configured for %v, will not run periodically sync", regCfg.URLs)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-25 07:05:03 -05:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// if pollInterval is not provided, don't run periodically sync
|
|
|
|
if regCfg.PollInterval == 0 {
|
2021-12-29 10:14:56 -05:00
|
|
|
logger.Warn().Msgf("sync config PollInterval not configured for %v, will not run periodically sync", regCfg.URLs)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-11-25 07:04:39 -05:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-10-22 04:56:55 -05:00
|
|
|
ticker := time.NewTicker(regCfg.PollInterval)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
// fork a new zerolog child to avoid data race
|
2021-12-13 14:23:31 -05:00
|
|
|
tlogger := log.Logger{Logger: logger.With().Caller().Timestamp().Logger()}
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-10-25 07:05:03 -05:00
|
|
|
// schedule each registry sync
|
2021-12-13 14:23:31 -05:00
|
|
|
go func(regCfg RegistryConfig, logger log.Logger) {
|
2021-06-08 15:11:18 -05:00
|
|
|
// run on intervals
|
2021-10-28 04:10:01 -05:00
|
|
|
for ; true; <-ticker.C {
|
2021-12-17 11:37:02 -05:00
|
|
|
// increment reference since will be busy, so shutdown has to wait
|
2021-12-13 14:23:31 -05:00
|
|
|
wtgrp.Add(1)
|
2021-12-17 11:37:02 -05:00
|
|
|
|
2021-12-29 10:14:56 -05:00
|
|
|
for _, upstreamURL := range regCfg.URLs {
|
|
|
|
upstreamAddr := StripRegistryTransport(upstreamURL)
|
|
|
|
// first try syncing main registry
|
|
|
|
if err := syncRegistry(regCfg, upstreamURL, storeController, localCtx, policyCtx,
|
2022-01-10 11:06:12 -05:00
|
|
|
credentialsFile[upstreamAddr], logger); err != nil {
|
2021-12-29 10:14:56 -05:00
|
|
|
logger.Error().Err(err).Str("registry", upstreamURL).
|
|
|
|
Msg("sync exited with error, falling back to auxiliary registries")
|
|
|
|
} else {
|
|
|
|
// if success fall back to main registry
|
|
|
|
break
|
|
|
|
}
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
2021-12-02 12:45:26 -05:00
|
|
|
// mark as done after a single sync run
|
2021-12-13 14:23:31 -05:00
|
|
|
wtgrp.Done()
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
2021-12-13 14:23:31 -05:00
|
|
|
}(regCfg, tlogger)
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
logger.Info().Msg("finished setting up sync")
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|