Files
gluetun/internal/storage/storage.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

73 lines
1.9 KiB
Go

package storage
import (
"os"
"path/filepath"
"sync"
"github.com/qdm12/gluetun/internal/models"
)
type Storage struct {
mergedServers models.AllServers
mergedMutex sync.RWMutex
// this is stored in memory to avoid re-parsing
// the embedded JSON file on every call to the
// SyncServers method.
hardcodedServers models.AllServers
logger Logger
disk bool
directoryPath string
legacyFilepath string
}
const manifestFilename = "manifest.json"
type Logger interface {
Info(s string)
Infof(format string, args ...any)
Warn(s string)
}
// New creates a new storage and reads the servers from the
// embedded servers files and the files on disk.
// Passing an empty directoryPath disables the reading and writing of
// servers.
func New(logger Logger, disk bool, directoryPath, legacyFilepath string) (storage *Storage, err error) {
// A unit test prevents [parseHardcodedServers] from ever failing,
// and ensures all providers are part of the servers returned.
hardcodedServers := parseHardcodedServers()
storage = &Storage{
hardcodedServers: hardcodedServers,
mergedServers: hardcodedServers,
logger: logger,
disk: disk,
directoryPath: directoryPath,
legacyFilepath: legacyFilepath,
}
if disk {
if err := storage.syncServers(); err != nil {
return nil, err
}
}
return storage, nil
}
// hasLegacy returns true if the legacy file `legacyFilepath` exists AND is
// different from the manifest file defined by `directoryPath`/[manifestFilename].
// This is used to determine if the legacy file should be read and removed after flushing servers data.
func (s *Storage) hasLegacy() bool {
if !s.disk {
return false
}
if filepath.Clean(filepath.Join(s.directoryPath, manifestFilename)) ==
filepath.Clean(s.legacyFilepath) {
return false
}
stat, err := os.Stat(s.legacyFilepath)
return err == nil && !stat.IsDir()
}