Files
gluetun/internal/storage/merge.go
T
Quentin McGaw 8f82376996 feat(storage): storage file structure changes (#3301)
- migrate persisted server data storage from `/gluetun/servers.json` to `/gluetun/servers/`
- add `STORAGE_SERVERS_ENABLED=on` to enable or disable on-disk server data storage
- add `STORAGE_SERVERS_DIRECTORY_PATH=/gluetun/servers` to configure where per-provider server files are stored
- keep backward compatibility with legacy `STORAGE_FILEPATH=/gluetun/servers.json`
- automatically read and migrate legacy `/gluetun/servers.json` into the new `/gluetun/servers/` layout when needed
- try to remove the legacy servers file after a successful migration to the new storage directory
- switch persisted server data from one large JSON file to a manifest plus per-provider JSON files
- add `UPDATER_PREFER_DIRECT_DOWNLOAD` to allow preferring direct download of provider server data
- keep deprecated updater flags `-enduser` and `-maintainer` as no-op warnings for backward compatibility
- preserve compatibility checks so persisted server data is discarded when its schema version no longer matches the built-in data
- allow preferred persisted provider data to override built-in data when versions match
- servers data now lives at https://github.com/qdm12/gluetun-servers/tree/main/pkg/servers
2026-05-19 04:28:25 +02:00

96 lines
3.1 KiB
Go

package storage
import (
"fmt"
"sort"
"time"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/format"
"github.com/qdm12/gluetun/internal/models"
)
func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.AllServers {
allProviders := providers.All()
merged := models.AllServers{
Version: hardcoded.Version,
ProviderToServers: make(map[string]models.Servers, len(allProviders)),
}
for _, provider := range allProviders {
hardcodedServers := hardcoded.ProviderToServers[provider]
persistedServers := persisted.ProviderToServers[provider]
merged.ProviderToServers[provider] = s.mergeProviderServers(provider,
hardcodedServers, persistedServers)
}
return merged
}
func (s *Storage) mergeProviderServers(provider string,
hardcoded, persisted models.Servers,
) (merged models.Servers) {
if persisted.Preferred && persisted.Version != hardcoded.Version {
s.logger.Warn(fmt.Sprintf(
"persisted preferred %s servers are discarded because they have version %d and hardcoded servers have version %d",
provider, persisted.Version, hardcoded.Version))
}
// If persisted data is marked as preferred, use it regardless of timestamp
// (as long as versions match)
if persisted.Preferred && persisted.Version == hardcoded.Version && len(persisted.Servers) > 0 {
s.logger.Info(fmt.Sprintf(
"Using %s servers from file (marked as preferred)", provider))
return persisted
}
nowTimestamp := time.Now().Unix()
if persisted.Timestamp > nowTimestamp {
s.logger.Warn(fmt.Sprintf(
"persisted %s servers have a timestamp %d in the future, ignoring them",
provider, persisted.Timestamp))
} else if persisted.Timestamp > hardcoded.Timestamp {
diff := time.Unix(persisted.Timestamp, 0).Sub(time.Unix(hardcoded.Timestamp, 0))
if diff < 0 {
diff = -diff
}
diff = diff.Truncate(time.Second)
message := "Using " + provider + " servers from file which are " +
format.FriendlyDuration(diff) + " more recent"
s.logger.Info(message)
return persisted
}
persistedServerKeyToServer := make(map[string]models.Server)
for _, persistedServer := range persisted.Servers {
if persistedServer.Keep {
persistedServerKeyToServer[persistedServer.Key()] = persistedServer
}
}
merged = hardcoded // use all fields from hardcoded
merged.Servers = make([]models.Server, 0, len(hardcoded.Servers)+len(persistedServerKeyToServer))
for _, hardcodedServer := range hardcoded.Servers {
hardcodedServerKey := hardcodedServer.Key()
persistedServerToKeep, has := persistedServerKeyToServer[hardcodedServerKey]
if has {
// Drop hardcoded server and use persisted server matching the key.
merged.Servers = append(merged.Servers, persistedServerToKeep)
delete(persistedServerKeyToServer, hardcodedServerKey)
} else {
merged.Servers = append(merged.Servers, hardcodedServer)
}
}
// Add remaining persisted servers to keep
for _, persistedServer := range persistedServerKeyToServer {
merged.Servers = append(merged.Servers, persistedServer)
}
sort.Sort(models.SortableServers(merged.Servers))
return merged
}