mirror of
https://github.com/qdm12/gluetun.git
synced 2026-06-15 16:04:08 +02:00
refactor(storage): new storage file structure
- new directory structure containing manifest.json and one json file per provider, by default. - the manifest.json file can specify a filepath for each vpn provider - each vpn provider json data file can contain the `"preferred": true` field to enforce it is used even if outdated, unless there is a version mismatch - `STORAGE_SERVERS_DIRECTORY_PATH` replaces `STORAGE_FILEPATH` (which is now a migration source only). It sets the directory where server manifest and per-provider JSON files are stored (default: `/gluetun/servers/`). - First-run migration: On startup, gluetun checks for the old /gluetun/servers.json file; if found and no new manifest exists, it automatically migrates all data to /gluetun/servers/ directory structure - Silent fallback: If legacy file isn't found, uses the new directory path normally - Legacy cleanup: After successful migration, attempts to remove the old fat JSON file (logs warning only if removal fails, e.g., read-only bind mounts) Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -27,10 +28,15 @@ func populateProviderToVersion(providerToVersion map[string]uint16) map[string]u
|
||||
func Test_extractServersFromBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type logLine struct {
|
||||
level log.Level
|
||||
message string
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
b []byte
|
||||
hardcodedVersions map[string]uint16
|
||||
logged []string
|
||||
logged []logLine
|
||||
persisted models.AllServers
|
||||
errMessage string
|
||||
}{
|
||||
@@ -42,7 +48,9 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
b: []byte(`{"cyberghost": "garbage"}`),
|
||||
hardcodedVersions: populateProviderToVersion(map[string]uint16{}),
|
||||
errMessage: "decoding servers version for provider Cyberghost: " +
|
||||
"json: cannot unmarshal string into Go value of type struct { Version uint16 \"json:\\\"version\\\"\" }",
|
||||
"json: cannot unmarshal string into Go value of type struct { Version uint16 \"json:\\\"version\\\"\"; " +
|
||||
"Timestamp int64 \"json:\\\"timestamp\\\"\"; " +
|
||||
"Filepath string \"json:\\\"filepath\\\"\" }",
|
||||
},
|
||||
"bad servers array JSON": {
|
||||
b: []byte(`{"cyberghost": {"version": 1, "servers": "garbage"}}`),
|
||||
@@ -81,13 +89,30 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
hardcodedVersions: populateProviderToVersion(map[string]uint16{
|
||||
providers.Cyberghost: 2,
|
||||
}),
|
||||
logged: []string{
|
||||
"Cyberghost servers from file discarded because they have version 1 and hardcoded servers have version 2",
|
||||
logged: []logLine{
|
||||
{level: log.LevelInfo, message: "Cyberghost servers from file discarded because they have version 1" +
|
||||
" and hardcoded servers have version 2"},
|
||||
},
|
||||
persisted: models.AllServers{
|
||||
ProviderToServers: map[string]models.Servers{},
|
||||
},
|
||||
},
|
||||
"preferred_different_versions": {
|
||||
b: []byte(`{
|
||||
"cyberghost": {"version": 1, "timestamp": 1, "preferred": true}
|
||||
}`),
|
||||
hardcodedVersions: populateProviderToVersion(map[string]uint16{
|
||||
providers.Cyberghost: 2,
|
||||
}),
|
||||
logged: []logLine{
|
||||
{level: log.LevelWarn, message: "Cyberghost preferred servers from file discarded because they have version 1" +
|
||||
" and hardcoded servers have version 2"},
|
||||
},
|
||||
persisted: models.AllServers{
|
||||
ProviderToServers: map[string]models.Servers{},
|
||||
},
|
||||
errMessage: "",
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
@@ -98,7 +123,15 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
logger := NewMockLogger(ctrl)
|
||||
var previousLogCall *gomock.Call
|
||||
for _, logged := range testCase.logged {
|
||||
call := logger.EXPECT().Info(logged)
|
||||
var call *gomock.Call
|
||||
switch logged.level { //nolint:exhaustive
|
||||
case log.LevelInfo:
|
||||
call = logger.EXPECT().Info(logged.message)
|
||||
case log.LevelWarn:
|
||||
call = logger.EXPECT().Warn(logged.message)
|
||||
default:
|
||||
t.Fatalf("invalid log level %d in test case", logged.level)
|
||||
}
|
||||
if previousLogCall != nil {
|
||||
call.After(previousLogCall)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user