Files
zot/pkg/extensions/extension_sync.go
T
Ramkumar Chinchani 9aff5b8d08 chore: fix dependabot alerts (#4048)
* chore: fix dependabot alerts

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: fix dependabot alerts

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: fix dependabot alerts

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: fix golangci-lint findings from CI

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: fix golangci-lint gosec warnings

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update code to use slices package and address gosec linting issues

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* build: fix makefile target

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update tests to use context in HTTP requests and add gosec annotations

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update tests to use context in HTTP requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update tests to use context in HTTP requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update tests to use context in HTTP requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update tests to use context in HTTP requests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: bump zui version

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: update test helpers and improve security settings in tests

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

* chore: add gosec linting directive for test path construction

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>

---------

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
2026-05-11 09:29:05 +03:00

176 lines
4.4 KiB
Go

//go:build sync
package extensions
import (
"net"
"net/url"
"slices"
"strings"
zerr "zotregistry.dev/zot/v2/errors"
"zotregistry.dev/zot/v2/pkg/api/config"
syncconf "zotregistry.dev/zot/v2/pkg/extensions/config/sync"
"zotregistry.dev/zot/v2/pkg/extensions/sync"
"zotregistry.dev/zot/v2/pkg/log"
mTypes "zotregistry.dev/zot/v2/pkg/meta/types"
"zotregistry.dev/zot/v2/pkg/scheduler"
"zotregistry.dev/zot/v2/pkg/storage"
)
func EnableSyncExtension(config *config.Config, metaDB mTypes.MetaDB,
storeController storage.StoreController, sch *scheduler.Scheduler, log log.Logger,
) (*sync.BaseOnDemand, error) {
// Get extensions config safely
extensionsConfig := config.CopyExtensionsConfig()
httpAddress := config.GetHTTPAddress()
httpPort := config.GetHTTPPort()
if extensionsConfig.IsSyncEnabled() {
log.Info().Msg("sync extension is enabled")
onDemand := sync.NewOnDemand(log)
syncConfig := extensionsConfig.GetSyncConfig()
for _, registryConfig := range syncConfig.Registries {
if len(registryConfig.URLs) > 1 {
if err := removeSelfURLs(httpAddress, httpPort, &registryConfig, log); err != nil {
return nil, err
}
}
if len(registryConfig.URLs) == 0 {
log.Error().Err(zerr.ErrSyncNoURLsLeft).Msg("failed to start sync extension")
return nil, zerr.ErrSyncNoURLsLeft
}
isPeriodical := len(registryConfig.Content) != 0 && registryConfig.PollInterval != 0
isOnDemand := registryConfig.OnDemand
if !(isPeriodical || isOnDemand) {
continue
}
tmpDir := syncConfig.DownloadDir
credsPath := syncConfig.CredentialsFile
// Get cluster config safely
clusterConfig := config.CopyClusterConfig()
service, err := sync.New(registryConfig, credsPath, clusterConfig, tmpDir, storeController, metaDB, log)
if err != nil {
log.Error().Err(err).Msg("failed to initialize sync extension")
return nil, err
}
if isPeriodical {
// add to task scheduler periodic sync
interval := registryConfig.PollInterval
gen := sync.NewTaskGenerator(service, interval, log)
sch.SubmitGenerator(gen, interval, scheduler.MediumPriority)
}
if isOnDemand {
// onDemand services used in routes.go
onDemand.Add(service)
}
}
return onDemand, nil
}
log.Info().Msg("sync config not provided or disabled, so not enabling sync")
return nil, nil //nolint: nilnil
}
func getLocalIPs() ([]string, error) {
var localIPs []string
ifaces, err := net.Interfaces()
if err != nil {
return []string{}, err
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
return localIPs, err
}
for _, addr := range addrs {
if localIP, ok := addr.(*net.IPNet); ok {
localIPs = append(localIPs, localIP.IP.String())
}
}
}
return localIPs, nil
}
func removeSelfURLs(httpAddress, httpPort string, registryConfig *syncconf.RegistryConfig, log log.Logger) error {
// get IP from config
selfAddress := net.JoinHostPort(httpAddress, httpPort)
// get all local IPs from interfaces
localIPs, err := getLocalIPs()
if err != nil {
return err
}
for idx := range slices.Backward(registryConfig.URLs) {
registryURL := registryConfig.URLs[idx]
url, err := url.Parse(registryURL)
if err != nil {
log.Error().Str("url", registryURL).Msg("failed to parse sync registry url, removing it")
registryConfig.URLs = append(registryConfig.URLs[:idx], registryConfig.URLs[idx+1:]...)
continue
}
// check self address
if strings.Contains(registryURL, selfAddress) {
log.Info().Str("url", registryURL).Msg("removing local registry url")
registryConfig.URLs = append(registryConfig.URLs[:idx], registryConfig.URLs[idx+1:]...)
continue
}
// check dns
ips, err := net.LookupIP(url.Hostname()) //nolint: noctx
if err != nil {
// will not remove, maybe it will get resolved later after multiple retries
log.Warn().Str("url", registryURL).Msg("failed to lookup sync registry url's hostname")
continue
}
var removed bool
for _, localIP := range localIPs {
// if ip resolved from hostname/dns is equal with any local ip
for _, ip := range ips {
if (ip.IsLoopback() && (url.Port() == httpPort)) ||
(net.JoinHostPort(ip.String(), url.Port()) == net.JoinHostPort(localIP, httpPort)) {
registryConfig.URLs = append(registryConfig.URLs[:idx], registryConfig.URLs[idx+1:]...)
removed = true
break
}
}
if removed {
break
}
}
}
return nil
}