all: Refactor preparing configuration (#7127)

This commit is contained in:
Simon Frei 2020-11-20 14:21:54 +01:00 committed by GitHub
parent 641b7aee38
commit 24af89c8e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 207 additions and 147 deletions

View File

@ -185,7 +185,7 @@ func main() {
log.Println("ID:", id)
}
wrapper := config.Wrap("config", config.New(id), events.NoopLogger)
wrapper := config.Wrap("config", config.New(id), id, events.NoopLogger)
wrapper.SetOptions(config.OptionsConfiguration{
NATLeaseM: natLease,
NATRenewalM: natRenewal,

View File

@ -113,7 +113,7 @@ func TestStopAfterBrokenConfig(t *testing.T) {
RawUseTLS: false,
},
}
w := config.Wrap("/dev/null", cfg, events.NoopLogger)
w := config.Wrap("/dev/null", cfg, protocol.LocalDeviceID, events.NoopLogger)
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token)
@ -1251,7 +1251,7 @@ func TestConfigChanges(t *testing.T) {
panic(err)
}
defer os.Remove(tmpFile.Name())
w := config.Wrap(tmpFile.Name(), cfg, events.NoopLogger)
w := config.Wrap(tmpFile.Name(), cfg, protocol.LocalDeviceID, events.NoopLogger)
tmpFile.Close()
baseURL, cancel, err := startHTTP(w)
if err != nil {

View File

@ -142,6 +142,10 @@ func (c *mockedConfig) StunServers() []string {
return nil
}
func (c *mockedConfig) MyID() protocol.DeviceID {
return protocol.DeviceID{}
}
type noopWaiter struct{}
func (noopWaiter) Wait() {}

View File

@ -44,7 +44,7 @@ func (validationError) String() string {
func TestReplaceCommit(t *testing.T) {
t.Skip("broken, fails randomly, #3834")
w := wrap("/dev/null", Configuration{Version: 0})
w := wrap("/dev/null", Configuration{Version: 0}, device1)
if w.RawCopy().Version != 0 {
t.Fatal("Config incorrect")
}

View File

@ -25,7 +25,6 @@ import (
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/util"
)
@ -105,8 +104,6 @@ func New(myID protocol.DeviceID) Configuration {
cfg.Options.UnackedNotificationIDs = []string{"authenticationUserAndPassword"}
util.SetDefaults(&cfg)
util.SetDefaults(&cfg.Options)
util.SetDefaults(&cfg.GUI)
// Can't happen.
if err := cfg.prepare(myID); err != nil {
@ -152,8 +149,6 @@ func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, int, error) {
var cfg xmlConfiguration
util.SetDefaults(&cfg)
util.SetDefaults(&cfg.Options)
util.SetDefaults(&cfg.GUI)
if err := xml.NewDecoder(r).Decode(&cfg); err != nil {
return Configuration{}, 0, err
@ -171,8 +166,6 @@ func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
var cfg Configuration
util.SetDefaults(&cfg)
util.SetDefaults(&cfg.Options)
util.SetDefaults(&cfg.GUI)
bs, err := ioutil.ReadAll(r)
if err != nil {
@ -230,38 +223,61 @@ func (cfg *Configuration) WriteXML(w io.Writer) error {
}
func (cfg *Configuration) prepare(myID protocol.DeviceID) error {
var myName string
cfg.ensureMyDevice(myID)
// Ensure this device is present in the config
for _, device := range cfg.Devices {
if device.DeviceID == myID {
goto found
}
}
myName, _ = os.Hostname()
cfg.Devices = append(cfg.Devices, DeviceConfiguration{
DeviceID: myID,
Name: myName,
})
found:
if err := cfg.clean(); err != nil {
existingDevices, err := cfg.prepareFoldersAndDevices(myID)
if err != nil {
return err
}
// Ensure that we are part of the devices
for i := range cfg.Folders {
cfg.Folders[i].Devices = ensureDevicePresent(cfg.Folders[i].Devices, myID)
}
cfg.GUI.prepare()
guiPWIsSet := cfg.GUI.User != "" && cfg.GUI.Password != ""
cfg.Options.prepare(guiPWIsSet)
ignoredDevices := cfg.prepareIgnoredDevices(existingDevices)
cfg.preparePendingDevices(existingDevices, ignoredDevices)
cfg.removeDeprecatedProtocols()
util.FillNilExceptDeprecated(cfg)
// TestIssue1750 relies on migrations happening after preparing options.
cfg.applyMigrations()
return nil
}
func (cfg *Configuration) clean() error {
util.FillNilSlices(&cfg.Options)
func (cfg *Configuration) ensureMyDevice(myID protocol.DeviceID) {
// Ensure this device is present in the config
for _, device := range cfg.Devices {
if device.DeviceID == myID {
return
}
}
myName, _ := os.Hostname()
cfg.Devices = append(cfg.Devices, DeviceConfiguration{
DeviceID: myID,
Name: myName,
})
}
func (cfg *Configuration) prepareFoldersAndDevices(myID protocol.DeviceID) (map[protocol.DeviceID]bool, error) {
existingDevices := cfg.prepareDeviceList()
sharedFolders, err := cfg.prepareFolders(myID, existingDevices)
if err != nil {
return nil, err
}
cfg.prepareDevices(sharedFolders)
return existingDevices, nil
}
func (cfg *Configuration) prepareDeviceList() map[protocol.DeviceID]bool {
// Ensure that the device list is
// - free from duplicates
// - no devices with empty ID
@ -272,89 +288,62 @@ func (cfg *Configuration) clean() error {
return cfg.Devices[a].DeviceID.Compare(cfg.Devices[b].DeviceID) == -1
})
// Build a list of available devices
existingDevices := make(map[protocol.DeviceID]bool, len(cfg.Devices))
for _, device := range cfg.Devices {
existingDevices[device.DeviceID] = true
}
return existingDevices
}
func (cfg *Configuration) prepareFolders(myID protocol.DeviceID, existingDevices map[protocol.DeviceID]bool) (map[protocol.DeviceID][]string, error) {
// Prepare folders and check for duplicates. Duplicates are bad and
// dangerous, can't currently be resolved in the GUI, and shouldn't
// happen when configured by the GUI. We return with an error in that
// situation.
existingFolders := make(map[string]*FolderConfiguration)
sharedFolders := make(map[protocol.DeviceID][]string, len(cfg.Devices))
existingFolders := make(map[string]*FolderConfiguration, len(cfg.Folders))
for i := range cfg.Folders {
folder := &cfg.Folders[i]
folder.prepare()
if folder.ID == "" {
return errFolderIDEmpty
return nil, errFolderIDEmpty
}
if folder.Path == "" {
return fmt.Errorf("folder %q: %w", folder.ID, errFolderPathEmpty)
return nil, fmt.Errorf("folder %q: %w", folder.ID, errFolderPathEmpty)
}
if _, ok := existingFolders[folder.ID]; ok {
return fmt.Errorf("folder %q: %w", folder.ID, errFolderIDDuplicate)
return nil, fmt.Errorf("folder %q: %w", folder.ID, errFolderIDDuplicate)
}
folder.prepare(myID, existingDevices)
existingFolders[folder.ID] = folder
for _, dev := range folder.Devices {
sharedFolders[dev.DeviceID] = append(sharedFolders[dev.DeviceID], folder.ID)
}
}
cfg.Options.RawListenAddresses = util.UniqueTrimmedStrings(cfg.Options.RawListenAddresses)
cfg.Options.RawGlobalAnnServers = util.UniqueTrimmedStrings(cfg.Options.RawGlobalAnnServers)
if cfg.Version > 0 && cfg.Version < OldestHandledVersion {
l.Warnf("Configuration version %d is deprecated. Attempting best effort conversion, but please verify manually.", cfg.Version)
}
// Upgrade configuration versions as appropriate
migrationsMut.Lock()
migrations.apply(cfg)
migrationsMut.Unlock()
// Build a list of available devices
existingDevices := make(map[protocol.DeviceID]bool)
for _, device := range cfg.Devices {
existingDevices[device.DeviceID] = true
}
// Ensure that the folder list is sorted by ID
sort.Slice(cfg.Folders, func(a, b int) bool {
return cfg.Folders[a].ID < cfg.Folders[b].ID
})
return sharedFolders, nil
}
// Ensure that in all folder configs
// - any loose devices are not present in the wrong places
// - there are no duplicate devices
// - the versioning configuration parameter map is not nil
sharedFolders := make(map[protocol.DeviceID][]string, len(cfg.Devices))
for i := range cfg.Folders {
cfg.Folders[i].Devices = ensureExistingDevices(cfg.Folders[i].Devices, existingDevices)
cfg.Folders[i].Devices = ensureNoDuplicateFolderDevices(cfg.Folders[i].Devices)
if cfg.Folders[i].Versioning.Params == nil {
cfg.Folders[i].Versioning.Params = map[string]string{}
}
sort.Slice(cfg.Folders[i].Devices, func(a, b int) bool {
return cfg.Folders[i].Devices[a].DeviceID.Compare(cfg.Folders[i].Devices[b].DeviceID) == -1
})
for _, dev := range cfg.Folders[i].Devices {
sharedFolders[dev.DeviceID] = append(sharedFolders[dev.DeviceID], cfg.Folders[i].ID)
}
}
func (cfg *Configuration) prepareDevices(sharedFolders map[protocol.DeviceID][]string) {
for i := range cfg.Devices {
cfg.Devices[i].prepare(sharedFolders[cfg.Devices[i].DeviceID])
}
}
// Very short reconnection intervals are annoying
if cfg.Options.ReconnectIntervalS < 5 {
cfg.Options.ReconnectIntervalS = 5
}
if cfg.GUI.APIKey == "" {
cfg.GUI.APIKey = rand.String(32)
}
func (cfg *Configuration) prepareIgnoredDevices(existingDevices map[protocol.DeviceID]bool) map[protocol.DeviceID]bool {
// The list of ignored devices should not contain any devices that have
// been manually added to the config.
var newIgnoredDevices []ObservedDevice
ignoredDevices := make(map[protocol.DeviceID]bool)
newIgnoredDevices := cfg.IgnoredDevices[:0]
ignoredDevices := make(map[protocol.DeviceID]bool, len(cfg.IgnoredDevices))
for _, dev := range cfg.IgnoredDevices {
if !existingDevices[dev.ID] {
ignoredDevices[dev.ID] = true
@ -362,7 +351,10 @@ func (cfg *Configuration) clean() error {
}
}
cfg.IgnoredDevices = newIgnoredDevices
return ignoredDevices
}
func (cfg *Configuration) preparePendingDevices(existingDevices, ignoredDevices map[protocol.DeviceID]bool) {
// The list of pending devices should not contain devices that were added manually, nor should it contain
// ignored devices.
@ -371,7 +363,7 @@ func (cfg *Configuration) clean() error {
return cfg.PendingDevices[i].Time.Before(cfg.PendingDevices[j].Time)
})
var newPendingDevices []ObservedDevice
newPendingDevices := cfg.PendingDevices[:0]
nextPendingDevice:
for _, pendingDevice := range cfg.PendingDevices {
if !existingDevices[pendingDevice.ID] && !ignoredDevices[pendingDevice.ID] {
@ -385,7 +377,9 @@ nextPendingDevice:
}
}
cfg.PendingDevices = newPendingDevices
}
func (cfg *Configuration) removeDeprecatedProtocols() {
// Deprecated protocols are removed from the list of listeners and
// device addresses. So far just kcp*.
for _, prefix := range []string{"kcp"} {
@ -395,35 +389,17 @@ nextPendingDevice:
dev.Addresses = filterURLSchemePrefix(dev.Addresses, prefix)
}
}
}
// Initialize any empty slices
if cfg.Folders == nil {
cfg.Folders = []FolderConfiguration{}
}
if cfg.IgnoredDevices == nil {
cfg.IgnoredDevices = []ObservedDevice{}
}
if cfg.PendingDevices == nil {
cfg.PendingDevices = []ObservedDevice{}
}
if cfg.Options.AlwaysLocalNets == nil {
cfg.Options.AlwaysLocalNets = []string{}
}
if cfg.Options.UnackedNotificationIDs == nil {
cfg.Options.UnackedNotificationIDs = []string{}
} else if cfg.GUI.User != "" && cfg.GUI.Password != "" {
for i, key := range cfg.Options.UnackedNotificationIDs {
if key == "authenticationUserAndPassword" {
cfg.Options.UnackedNotificationIDs = append(cfg.Options.UnackedNotificationIDs[:i], cfg.Options.UnackedNotificationIDs[i+1:]...)
break
}
}
}
if cfg.Options.FeatureFlags == nil {
cfg.Options.FeatureFlags = []string{}
func (cfg *Configuration) applyMigrations() {
if cfg.Version > 0 && cfg.Version < OldestHandledVersion {
l.Warnf("Configuration version %d is deprecated. Attempting best effort conversion, but please verify manually.", cfg.Version)
}
return nil
// Upgrade configuration versions as appropriate
migrationsMut.Lock()
migrations.apply(cfg)
migrationsMut.Unlock()
}
// DeviceMap returns a map of device ID to device configuration for the given configuration.

View File

@ -526,7 +526,7 @@ func TestNewSaveLoad(t *testing.T) {
}
intCfg := New(device1)
cfg := wrap(path, intCfg)
cfg := wrap(path, intCfg, device1)
if exists(path) {
t.Error(path, "exists")
@ -646,7 +646,7 @@ func TestPullOrder(t *testing.T) {
if err != nil {
t.Fatal(err)
}
wrapper = wrap("testdata/pullorder.xml", cfg)
wrapper = wrap("testdata/pullorder.xml", cfg, device1)
folders = wrapper.Folders()
for _, tc := range expected {
@ -941,7 +941,8 @@ func TestIssue4219(t *testing.T) {
]
}`))
cfg, err := ReadJSON(r, protocol.LocalDeviceID)
myID := protocol.LocalDeviceID
cfg, err := ReadJSON(r, myID)
if err != nil {
t.Fatal(err)
}
@ -959,7 +960,7 @@ func TestIssue4219(t *testing.T) {
t.Errorf("There should be three ignored folders, not %d", ignoredFolders)
}
w := wrap("/tmp/cfg", cfg)
w := wrap("/tmp/cfg", cfg, myID)
if !w.IgnoredFolder(device2, "t1") {
t.Error("Folder device2 t1 should be ignored")
}
@ -1119,12 +1120,16 @@ func TestRemoveDeviceWithEmptyID(t *testing.T) {
},
}
cfg.clean()
cfg.prepare(device1)
if len(cfg.Devices) != 0 {
if len(cfg.Devices) != 1 {
t.Error("Expected one device")
} else if cfg.Devices[0].DeviceID != device1 {
t.Error("Expected device with empty ID to be removed from config:", cfg.Devices)
}
if len(cfg.Folders[0].Devices) != 0 {
if len(cfg.Folders[0].Devices) != 1 {
t.Error("Expected one device in folder")
} else if cfg.Folders[0].Devices[0].DeviceID != device1 {
t.Error("Expected device with empty ID to be removed from folder")
}
}
@ -1175,8 +1180,8 @@ func load(path string, myID protocol.DeviceID) (Wrapper, error) {
return cfg, err
}
func wrap(path string, cfg Configuration) Wrapper {
return Wrap(path, cfg, events.NoopLogger)
func wrap(path string, cfg Configuration, myID protocol.DeviceID) Wrapper {
return Wrap(path, cfg, myID, events.NoopLogger)
}
func TestInternalVersioningConfiguration(t *testing.T) {

View File

@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"runtime"
"sort"
"strings"
"time"
@ -43,7 +44,7 @@ func NewFolderConfiguration(myID protocol.DeviceID, id, label string, fsType fs.
util.SetDefaults(&f)
f.prepare()
f.prepare(myID, nil)
return f
}
@ -182,7 +183,19 @@ func (f *FolderConfiguration) DeviceIDs() []protocol.DeviceID {
return deviceIDs
}
func (f *FolderConfiguration) prepare() {
func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices map[protocol.DeviceID]bool) {
// Ensure that
// - any loose devices are not present in the wrong places
// - there are no duplicate devices
// - we are part of the devices
f.Devices = ensureExistingDevices(f.Devices, existingDevices)
f.Devices = ensureNoDuplicateFolderDevices(f.Devices)
f.Devices = ensureDevicePresent(f.Devices, myID)
sort.Slice(f.Devices, func(a, b int) bool {
return f.Devices[a].DeviceID.Compare(f.Devices[b].DeviceID) == -1
})
if f.RescanIntervalS > MaxRescanIntervalS {
f.RescanIntervalS = MaxRescanIntervalS
} else if f.RescanIntervalS < 0 {
@ -194,9 +207,6 @@ func (f *FolderConfiguration) prepare() {
f.FSWatcherDelayS = 10
}
if f.Versioning.Params == nil {
f.Versioning.Params = make(map[string]string)
}
if f.Versioning.CleanupIntervalS > MaxRescanIntervalS {
f.Versioning.CleanupIntervalS = MaxRescanIntervalS
} else if f.Versioning.CleanupIntervalS < 0 {

View File

@ -11,6 +11,8 @@ import (
"os"
"strconv"
"strings"
"github.com/syncthing/syncthing/lib/rand"
)
func (c GUIConfiguration) IsAuthEnabled() bool {
@ -126,6 +128,12 @@ func (c GUIConfiguration) IsValidAPIKey(apiKey string) bool {
}
}
func (c *GUIConfiguration) prepare() {
if c.APIKey == "" {
c.APIKey = rand.String(32)
}
}
func (c GUIConfiguration) Copy() GUIConfiguration {
return c
}

View File

@ -28,6 +28,27 @@ func (opts OptionsConfiguration) Copy() OptionsConfiguration {
return optsCopy
}
func (opts *OptionsConfiguration) prepare(guiPWIsSet bool) {
util.FillNilSlices(opts)
opts.RawListenAddresses = util.UniqueTrimmedStrings(opts.RawListenAddresses)
opts.RawGlobalAnnServers = util.UniqueTrimmedStrings(opts.RawGlobalAnnServers)
// Very short reconnection intervals are annoying
if opts.ReconnectIntervalS < 5 {
opts.ReconnectIntervalS = 5
}
if guiPWIsSet && len(opts.UnackedNotificationIDs) > 0 {
for i, key := range opts.UnackedNotificationIDs {
if key == "authenticationUserAndPassword" {
opts.UnackedNotificationIDs = append(opts.UnackedNotificationIDs[:i], opts.UnackedNotificationIDs[i+1:]...)
break
}
}
}
}
// RequiresRestartOnly returns a copy with only the attributes that require
// restart on change.
func (opts OptionsConfiguration) RequiresRestartOnly() OptionsConfiguration {

View File

@ -55,6 +55,7 @@ func (noopWaiter) Wait() {}
// notifications of changes to registered Handlers
type Wrapper interface {
ConfigPath() string
MyID() protocol.DeviceID
RawCopy() Configuration
Replace(cfg Configuration) (Waiter, error)
@ -97,6 +98,7 @@ type wrapper struct {
cfg Configuration
path string
evLogger events.Logger
myID protocol.DeviceID
waiter Waiter // Latest ongoing config change
subs []Committer
@ -107,11 +109,12 @@ type wrapper struct {
// Wrap wraps an existing Configuration structure and ties it to a file on
// disk.
func Wrap(path string, cfg Configuration, evLogger events.Logger) Wrapper {
func Wrap(path string, cfg Configuration, myID protocol.DeviceID, evLogger events.Logger) Wrapper {
w := &wrapper{
cfg: cfg,
path: path,
evLogger: evLogger,
myID: myID,
waiter: noopWaiter{}, // Noop until first config change
mut: sync.NewMutex(),
}
@ -132,13 +135,17 @@ func Load(path string, myID protocol.DeviceID, evLogger events.Logger) (Wrapper,
return nil, 0, err
}
return Wrap(path, cfg, evLogger), originalVersion, nil
return Wrap(path, cfg, myID, evLogger), originalVersion, nil
}
func (w *wrapper) ConfigPath() string {
return w.path
}
func (w *wrapper) MyID() protocol.DeviceID {
return w.myID
}
// Subscribe registers the given handler to be called on any future
// configuration changes.
func (w *wrapper) Subscribe(c Committer) {
@ -184,7 +191,7 @@ func (w *wrapper) Replace(cfg Configuration) (Waiter, error) {
func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) {
from := w.cfg
if err := to.clean(); err != nil {
if err := to.prepare(w.myID); err != nil {
return noopWaiter{}, err
}

View File

@ -11,6 +11,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
)
func TestIsLANHost(t *testing.T) {
@ -36,7 +37,7 @@ func TestIsLANHost(t *testing.T) {
Options: config.OptionsConfiguration{
AlwaysLocalNets: []string{"10.20.30.0/24"},
},
}, events.NoopLogger)
}, protocol.LocalDeviceID, events.NoopLogger)
s := &service{cfg: cfg}
for _, tc := range cases {

View File

@ -30,7 +30,7 @@ func init() {
}
func initConfig() config.Wrapper {
cfg := config.Wrap("/dev/null", config.New(device1), events.NoopLogger)
cfg := config.Wrap("/dev/null", config.New(device1), device1, events.NoopLogger)
dev1Conf = config.NewDeviceConfiguration(device1, "device1")
dev2Conf = config.NewDeviceConfiguration(device2, "device2")
dev3Conf = config.NewDeviceConfiguration(device3, "device3")

View File

@ -23,7 +23,7 @@ func setupCache() *manager {
cfg.Options.LocalAnnEnabled = false
cfg.Options.GlobalAnnEnabled = false
return NewManager(protocol.LocalDeviceID, config.Wrap("", cfg, events.NoopLogger), tls.Certificate{}, events.NoopLogger, nil).(*manager)
return NewManager(protocol.LocalDeviceID, config.Wrap("", cfg, protocol.LocalDeviceID, events.NoopLogger), tls.Certificate{}, events.NoopLogger, nil).(*manager)
}
func TestCacheUnique(t *testing.T) {

View File

@ -115,7 +115,7 @@ func createTmpWrapper(cfg config.Configuration) config.Wrapper {
if err != nil {
panic(err)
}
wrapper := config.Wrap(tmpFile.Name(), cfg, events.NoopLogger)
wrapper := config.Wrap(tmpFile.Name(), cfg, myID, events.NoopLogger)
tmpFile.Close()
return wrapper
}
@ -331,7 +331,7 @@ func TestDeviceRename(t *testing.T) {
DeviceID: device1,
},
}
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg, events.NoopLogger)
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg, device1, events.NoopLogger)
db := db.NewLowlevel(backend.OpenMemory())
m := newModel(cfg, myID, "syncthing", "dev", db, nil)

View File

@ -64,7 +64,7 @@ func TestMappingClearAddresses(t *testing.T) {
if err != nil {
t.Fatal(err)
}
w := config.Wrap(tmpFile.Name(), config.Configuration{}, events.NoopLogger)
w := config.Wrap(tmpFile.Name(), config.Configuration{}, protocol.LocalDeviceID, events.NoopLogger)
defer os.RemoveAll(tmpFile.Name())
tmpFile.Close()

View File

@ -37,7 +37,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 0, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 1, 1}}, // first 56 bits same, differ in the first 64 bits
},
}, events.NoopLogger)
}, protocol.LocalDeviceID, events.NoopLogger)
defer os.Remove(cfg.ConfigPath())
if err := checkShortIDs(cfg); err != nil {
@ -49,7 +49,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 1}}, // first 64 bits same
},
}, events.NoopLogger)
}, protocol.LocalDeviceID, events.NoopLogger)
if err := checkShortIDs(cfg); err == nil {
t.Error("Should have gotten an error")
@ -76,7 +76,7 @@ func TestStartupFail(t *testing.T) {
{DeviceID: id},
{DeviceID: conflID},
},
}, events.NoopLogger)
}, protocol.LocalDeviceID, events.NoopLogger)
defer os.Remove(cfg.ConfigPath())
db := backend.OpenMemory()

View File

@ -49,12 +49,12 @@ func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger,
if noDefaultFolder {
l.Infoln("We will skip creation of a default folder on first start")
return config.Wrap(path, newCfg, evLogger), nil
return config.Wrap(path, newCfg, myID, evLogger), nil
}
newCfg.Folders = append(newCfg.Folders, config.NewFolderConfiguration(myID, "default", "Default Folder", fs.FilesystemTypeBasic, locations.Get(locations.DefFolder)))
l.Infoln("Default folder created and/or linked to new config")
return config.Wrap(path, newCfg, evLogger), nil
return config.Wrap(path, newCfg, myID, evLogger), nil
}
// LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it

View File

@ -83,6 +83,10 @@ func SetDefaults(data interface{}) {
default:
panic(f.Type())
}
} else if f.CanSet() && f.Kind() == reflect.Struct && f.CanAddr() {
if addr := f.Addr(); addr.CanInterface() {
SetDefaults(addr.Interface())
}
}
}
}
@ -137,9 +141,22 @@ func UniqueTrimmedStrings(ss []string) []string {
return us
}
func FillNilExceptDeprecated(data interface{}) {
fillNil(data, true)
}
func FillNil(data interface{}) {
fillNil(data, false)
}
func fillNil(data interface{}, skipDeprecated bool) {
s := reflect.ValueOf(data).Elem()
t := s.Type()
for i := 0; i < s.NumField(); i++ {
if skipDeprecated && strings.HasPrefix(t.Field(i).Name, "Deprecated") {
continue
}
f := s.Field(i)
for f.Kind() == reflect.Ptr && f.IsZero() && f.CanSet() {
@ -160,9 +177,19 @@ func FillNil(data interface{}) {
}
}
if f.Kind() == reflect.Struct && f.CanAddr() {
if addr := f.Addr(); addr.CanInterface() {
FillNil(addr.Interface())
switch f.Kind() {
case reflect.Slice:
if f.Type().Elem().Kind() != reflect.Struct {
continue
}
for i := 0; i < f.Len(); i++ {
fillNil(f.Index(i).Addr().Interface(), skipDeprecated)
}
case reflect.Struct:
if f.CanAddr() {
if addr := f.Addr(); addr.CanInterface() {
fillNil(addr.Interface(), skipDeprecated)
}
}
}
}

View File

@ -18,6 +18,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
func TestMain(m *testing.M) {
@ -47,7 +48,7 @@ var (
}
defaultCfg = config.Wrap("", config.Configuration{
Folders: []config.FolderConfiguration{defaultFolderCfg},
}, events.NoopLogger)
}, protocol.LocalDeviceID, events.NoopLogger)
)
// Represents possibly multiple (different event types) expected paths from