mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 21:17:58 +08:00
dfb5d1df54
* fix: make config read/write thread safe and fix some other similar issues 1. The config config has a lock, and safe methods to update and read the attributes 2. The config has methods to retrieve copies of specific attributes, such as the extyensions config, the auth config, and the authz config. These are needed, as the config object may mutate in the middle of an auth/authz requests, and we avoid partial configuration being applied for that request. 3. Fix an issue with the monitoring server not stopping when the controller is shut down. 4. Fix an issue with the HTPasswdWatcher not stopping when the background tasks are supposed to finish. 5. Fix some tests using hardcoded ports. Moved some of the methods which were on the main config to the auth, access control and extension configs Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
174 lines
4.3 KiB
Go
174 lines
4.3 KiB
Go
//go:build sync
|
|
// +build sync
|
|
|
|
package extensions
|
|
|
|
import (
|
|
"net"
|
|
"net/url"
|
|
"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() {
|
|
onDemand := sync.NewOnDemand(log)
|
|
syncConfig := extensionsConfig.GetSyncConfig()
|
|
|
|
for _, registryConfig := range syncConfig.Registries {
|
|
if len(registryConfig.URLs) > 1 {
|
|
if err := removeSelfURLs(httpAddress, httpPort, ®istryConfig, 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 := len(registryConfig.URLs) - 1; idx >= 0; idx-- {
|
|
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())
|
|
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
|
|
}
|