2021-06-08 15:11:18 -05:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2021-10-28 04:10:01 -05:00
|
|
|
"os"
|
|
|
|
"path"
|
2021-06-08 15:11:18 -05:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"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"
|
2021-10-28 04:10:01 -05:00
|
|
|
"github.com/containers/image/v5/oci/layout"
|
|
|
|
guuid "github.com/gofrs/uuid"
|
2021-12-03 22:50:58 -05:00
|
|
|
"zotregistry.io/zot/pkg/log"
|
|
|
|
"zotregistry.io/zot/pkg/storage"
|
2021-06-08 15:11:18 -05:00
|
|
|
)
|
|
|
|
|
2021-12-02 12:45:26 -05:00
|
|
|
func OneImage(cfg Config, storeController storage.StoreController,
|
|
|
|
repo, tag string, log log.Logger) error {
|
2021-06-08 15:11:18 -05:00
|
|
|
var credentialsFile CredentialsFile
|
|
|
|
|
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) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
if cfg.CredentialsFile != "" {
|
2021-10-28 04:10:01 -05:00
|
|
|
var err error
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
credentialsFile, err = getFileCredentials(cfg.CredentialsFile)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("couldn't get registry credentials from %s", cfg.CredentialsFile)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
localCtx, policyCtx, err := getLocalContexts(log)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
imageStore := storeController.GetImageStore(repo)
|
|
|
|
|
|
|
|
var copyErr error
|
|
|
|
|
|
|
|
uuid, err := guuid.NewV4()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-12-07 13:26:26 -05:00
|
|
|
for _, registryCfg := range cfg.Registries {
|
|
|
|
regCfg := registryCfg
|
2021-06-08 15:11:18 -05:00
|
|
|
if !regCfg.OnDemand {
|
|
|
|
log.Info().Msgf("skipping syncing on demand from %s, onDemand flag is false", regCfg.URL)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-10-25 07:05:03 -05:00
|
|
|
// if content config is not specified, then don't filter, just sync demanded image
|
|
|
|
if len(regCfg.Content) != 0 {
|
|
|
|
repos := filterRepos([]string{repo}, regCfg.Content, log)
|
|
|
|
if len(repos) == 0 {
|
|
|
|
log.Info().Msgf("skipping syncing on demand %s from %s registry because it's filtered out by content config",
|
|
|
|
repo, regCfg.URL)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-25 07:05:03 -05:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 15:11:18 -05:00
|
|
|
registryConfig := regCfg
|
|
|
|
log.Info().Msgf("syncing on demand with %s", registryConfig.URL)
|
|
|
|
|
|
|
|
upstreamRegistryName := strings.Replace(strings.Replace(regCfg.URL, "http://", "", 1), "https://", "", 1)
|
|
|
|
|
|
|
|
upstreamCtx := getUpstreamContext(®istryConfig, credentialsFile[upstreamRegistryName])
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
upstreamRepoRef, err := parseRepositoryReference(fmt.Sprintf("%s/%s", upstreamRegistryName, repo))
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("error parsing repository reference %s/%s", upstreamRegistryName, repo)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
|
|
|
}
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
upstreamTaggedRef, err := reference.WithTag(upstreamRepoRef, tag)
|
|
|
|
if err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Msgf("error creating a reference for repository %s and tag %q",
|
|
|
|
upstreamRepoRef.Name(), tag)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
upstreamRef, err := docker.NewReference(upstreamTaggedRef)
|
2021-10-28 04:10:01 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("error creating docker reference for repository %s and tag %q",
|
|
|
|
upstreamRepoRef.Name(), tag)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
imageName := strings.Replace(upstreamTaggedRef.Name(), upstreamRegistryName, "", 1)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
localRepo := path.Join(imageStore.RootDir(), imageName, SyncBlobUploadDir, uuid.String(), imageName)
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
if err = os.MkdirAll(localRepo, storage.DefaultDirPerms); err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Str("dir", localRepo).Msg("couldn't create temporary dir")
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-18 07:52:32 -05:00
|
|
|
defer os.RemoveAll(path.Join(imageStore.RootDir(), imageName, SyncBlobUploadDir, uuid.String()))
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
localTaggedRepo := fmt.Sprintf("%s:%s", localRepo, tag)
|
|
|
|
|
|
|
|
localRef, err := layout.ParseReference(localTaggedRepo)
|
2021-06-08 15:11:18 -05:00
|
|
|
if err != nil {
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Error().Err(err).Msgf("cannot obtain a valid image reference for reference %q", localRepo)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
log.Info().Msgf("copying image %s:%s to %s", upstreamTaggedRef.Name(),
|
|
|
|
upstreamTaggedRef.Tag(), localRepo)
|
2021-06-08 15:11:18 -05:00
|
|
|
|
|
|
|
options := getCopyOptions(upstreamCtx, localCtx)
|
|
|
|
|
|
|
|
retryOptions := &retry.RetryOptions{
|
|
|
|
MaxRetry: maxRetries,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
2021-10-28 04:10:01 -05:00
|
|
|
_, copyErr = copy.Image(context.Background(), policyCtx, localRef, upstreamRef, &options)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-12-07 13:26:26 -05:00
|
|
|
return copyErr
|
|
|
|
}, retryOptions); err != nil {
|
|
|
|
log.Error().Err(err).Msgf("error while copying image %s to %s",
|
2021-10-28 04:10:01 -05:00
|
|
|
upstreamRef.DockerReference().Name(), localTaggedRepo)
|
2021-06-08 15:11:18 -05:00
|
|
|
} else {
|
|
|
|
log.Info().Msgf("successfully synced %s", upstreamRef.DockerReference().Name())
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
err := pushSyncedLocalImage(repo, tag, uuid.String(), storeController, log)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("error while pushing synced cached image %s",
|
|
|
|
localTaggedRepo)
|
2021-12-13 14:23:31 -05:00
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-12-07 13:26:26 -05:00
|
|
|
httpClient, err := getHTTPClient(®Cfg, credentialsFile[upstreamRegistryName], log)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = retry.RetryIfNecessary(context.Background(), func() error {
|
|
|
|
err = syncSignatures(httpClient, storeController, regCfg.URL, imageName, upstreamTaggedRef.Tag(), log)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}, retryOptions); err != nil {
|
|
|
|
log.Error().Err(err).Msgf("Couldn't copy image signature %s", upstreamRef.DockerReference().Name())
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return nil
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-28 04:10:01 -05:00
|
|
|
return copyErr
|
2021-06-08 15:11:18 -05:00
|
|
|
}
|