lib: Apply config changes sequentially (ref #5298) (#7188)

This commit is contained in:
Simon Frei
2021-01-15 15:43:34 +01:00
committed by GitHub
parent b2d82da20d
commit f63cdbfcfa
20 changed files with 1032 additions and 790 deletions
+14 -9
View File
@@ -25,7 +25,8 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
// Get us a model up and running
m, f := setupROFolder(t)
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
@@ -105,7 +106,8 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
// Get us a model up and running
m, f := setupROFolder(t)
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
@@ -193,7 +195,8 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
func TestRecvOnlyUndoChanges(t *testing.T) {
// Get us a model up and running
m, f := setupROFolder(t)
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
@@ -261,7 +264,8 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
// Get us a model up and running
m, f := setupROFolder(t)
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
@@ -324,7 +328,8 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
// Get us a model up and running
m, f := setupROFolder(t)
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
@@ -443,17 +448,17 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
return knownFiles
}
func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder) {
func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.CancelFunc) {
t.Helper()
w := createTmpWrapper(defaultCfg)
w, cancel := createTmpWrapper(defaultCfg)
cfg := w.RawCopy()
fcfg := testFolderConfigFake()
fcfg.ID = "ro"
fcfg.Label = "ro"
fcfg.Type = config.FolderTypeReceiveOnly
cfg.Folders = []config.FolderConfiguration{fcfg}
w.Replace(cfg)
replace(t, w, cfg)
m := newModel(t, w, myID, "syncthing", "dev", nil)
m.ServeBackground()
@@ -464,7 +469,7 @@ func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder) {
defer m.fmut.RUnlock()
f := m.folderRunners["ro"].(*receiveOnlyFolder)
return m, f
return m, f, cancel
}
func writeFile(fs fs.Filesystem, filename string, data []byte, perm fs.FileMode) error {
+49 -48
View File
@@ -92,8 +92,8 @@ func createFile(t *testing.T, name string, fs fs.Filesystem) protocol.FileInfo {
}
// Sets up a folder and model, but makes sure the services aren't actually running.
func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testModel, *sendReceiveFolder) {
w, fcfg := tmpDefaultWrapper()
func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testModel, *sendReceiveFolder, context.CancelFunc) {
w, fcfg, wCancel := tmpDefaultWrapper()
// Initialise model and stop immediately.
model := setupModel(t, w)
model.cancel()
@@ -107,10 +107,11 @@ func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testMode
f.updateLocalsFromScanning(files)
}
return model, f
return model, f, wCancel
}
func cleanupSRFolder(f *sendReceiveFolder, m *testModel) {
func cleanupSRFolder(f *sendReceiveFolder, m *testModel, wrapperCancel context.CancelFunc) {
wrapperCancel()
os.Remove(m.cfg.ConfigPath())
os.RemoveAll(f.Filesystem().URI())
}
@@ -130,8 +131,8 @@ func TestHandleFile(t *testing.T) {
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m, wcfgCancel)
copyChan := make(chan copyBlocksState, 1)
@@ -172,8 +173,8 @@ func TestHandleFileWithTemp(t *testing.T) {
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
defer cleanupSRFolder(f, m, wcfgCancel)
if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err)
@@ -228,10 +229,10 @@ func TestCopierFinder(t *testing.T) {
requiredFile.Blocks = blocks[1:]
requiredFile.Name = "file2"
m, f := setupSendReceiveFolder(t, existingFile)
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
f.CopyRangeMethod = method
defer cleanupSRFolder(f, m)
defer cleanupSRFolder(f, m, wcfgCancel)
if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err)
@@ -309,8 +310,8 @@ func TestCopierFinder(t *testing.T) {
func TestWeakHash(t *testing.T) {
// Setup the model/pull environment
model, fo := setupSendReceiveFolder(t)
defer cleanupSRFolder(fo, model)
model, fo, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(fo, model, wcfgCancel)
ffs := fo.Filesystem()
tempFile := fs.TempName("weakhash")
@@ -438,8 +439,8 @@ func TestCopierCleanup(t *testing.T) {
// Create a file
file := setupFile("test", []int{0})
file.Size = 1
m, f := setupSendReceiveFolder(t, file)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t, file)
defer cleanupSRFolder(f, m, wcfgCancel)
file.Blocks = []protocol.BlockInfo{blocks[1]}
file.Version = file.Version.Update(myID.Short())
@@ -471,8 +472,8 @@ func TestCopierCleanup(t *testing.T) {
func TestDeregisterOnFailInCopy(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
// Set up our evet subscription early
s := m.evLogger.Subscribe(events.ItemFinished)
@@ -571,8 +572,8 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
func TestDeregisterOnFailInPull(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
// Set up our evet subscription early
s := m.evLogger.Subscribe(events.ItemFinished)
@@ -674,8 +675,8 @@ func TestDeregisterOnFailInPull(t *testing.T) {
}
func TestIssue3164(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
tmpDir := ffs.URI()
@@ -765,8 +766,8 @@ func TestDiffEmpty(t *testing.T) {
// option is true and the permissions do not match between the file on disk and
// in the db.
func TestDeleteIgnorePerms(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
f.IgnorePerms = true
@@ -803,8 +804,8 @@ func TestCopyOwner(t *testing.T) {
// Set up a folder with the CopyParentOwner bit and backed by a fake
// filesystem.
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
f.folder.FolderConfiguration = config.NewFolderConfiguration(m.id, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner")
f.folder.FolderConfiguration.CopyOwnershipFromParent = true
@@ -906,8 +907,8 @@ func TestCopyOwner(t *testing.T) {
// TestSRConflictReplaceFileByDir checks that a conflict is created when an existing file
// is replaced with a directory and versions are conflicting
func TestSRConflictReplaceFileByDir(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
name := "foo"
@@ -938,8 +939,8 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
// TestSRConflictReplaceFileByLink checks that a conflict is created when an existing file
// is replaced with a link and versions are conflicting
func TestSRConflictReplaceFileByLink(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
name := "foo"
@@ -971,8 +972,8 @@ func TestSRConflictReplaceFileByLink(t *testing.T) {
// TestDeleteBehindSymlink checks that we don't delete or schedule a scan
// when trying to delete a file behind a symlink.
func TestDeleteBehindSymlink(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
destDir := createTmpDir()
@@ -1022,8 +1023,8 @@ func TestDeleteBehindSymlink(t *testing.T) {
// Reproduces https://github.com/syncthing/syncthing/issues/6559
func TestPullCtxCancel(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
pullChan := make(chan pullBlockState)
finisherChan := make(chan *sharedPullerState)
@@ -1064,8 +1065,8 @@ func TestPullCtxCancel(t *testing.T) {
}
func TestPullDeleteUnscannedDir(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
dir := "foobar"
@@ -1093,8 +1094,8 @@ func TestPullDeleteUnscannedDir(t *testing.T) {
}
func TestPullCaseOnlyPerformFinish(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
name := "foo"
@@ -1154,8 +1155,8 @@ func TestPullCaseOnlySymlink(t *testing.T) {
}
func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
ffs := f.Filesystem()
name := "foo"
@@ -1209,8 +1210,8 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
}
func TestPullTempFileCaseConflict(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
copyChan := make(chan copyBlocksState, 1)
@@ -1235,8 +1236,8 @@ func TestPullTempFileCaseConflict(t *testing.T) {
}
func TestPullCaseOnlyRename(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
// tempNameConfl := fs.TempName(confl)
@@ -1278,8 +1279,8 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
t.Skip()
}
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
name := "foo"
if fd, err := f.mtimefs.Create(name); err != nil {
@@ -1318,8 +1319,8 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
}
func TestPullDeleteCaseConflict(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
name := "foo"
fi := protocol.FileInfo{Name: "Foo"}
@@ -1352,8 +1353,8 @@ func TestPullDeleteCaseConflict(t *testing.T) {
}
func TestPullDeleteIgnoreChildDir(t *testing.T) {
m, f := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m)
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
parent := "parent"
del := "ignored"
+59 -52
View File
@@ -136,6 +136,7 @@ type model struct {
// such as scans and pulls.
folderIOLimiter *byteSemaphore
fatalChan chan error
started chan struct{}
// fields protected by fmut
fmut sync.RWMutex
@@ -162,7 +163,6 @@ type model struct {
// for testing only
foldersRunning int32
started chan struct{}
}
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *byteSemaphore) service
@@ -221,6 +221,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
globalRequestLimiter: newByteSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
folderIOLimiter: newByteSemaphore(cfg.Options().MaxFolderConcurrency()),
fatalChan: make(chan error),
started: make(chan struct{}),
// fields protected by fmut
fmut: sync.NewRWMutex(),
@@ -243,9 +244,6 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
remotePausedFolders: make(map[protocol.DeviceID]map[string]struct{}),
indexSenders: make(map[protocol.DeviceID]*indexSenderRegistry),
// for testing only
started: make(chan struct{}),
}
for devID := range cfg.Devices() {
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID)
@@ -259,10 +257,10 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
func (m *model) serve(ctx context.Context) error {
defer m.closeAllConnectionsAndWait()
m.cfg.Subscribe(m)
cfg := m.cfg.Subscribe(m)
defer m.cfg.Unsubscribe(m)
if err := m.initFolders(); err != nil {
if err := m.initFolders(cfg); err != nil {
close(m.started)
return svcutil.AsFatalErr(err, svcutil.ExitError)
}
@@ -277,17 +275,14 @@ func (m *model) serve(ctx context.Context) error {
}
}
func (m *model) initFolders() error {
cacheIgnoredFiles := m.cfg.Options().CacheIgnoredFiles
existingDevices := m.cfg.Devices()
existingFolders := m.cfg.Folders()
clusterConfigDevices := make(deviceIDSet, len(existingDevices))
for _, folderCfg := range existingFolders {
func (m *model) initFolders(cfg config.Configuration) error {
clusterConfigDevices := make(deviceIDSet, len(cfg.Devices))
for _, folderCfg := range cfg.Folders {
if folderCfg.Paused {
folderCfg.CreateRoot()
continue
}
err := m.newFolder(folderCfg, cacheIgnoredFiles)
err := m.newFolder(folderCfg, cfg.Options.CacheIgnoredFiles)
if err != nil {
return err
}
@@ -295,7 +290,7 @@ func (m *model) initFolders() error {
}
ignoredDevices := observedDeviceSet(m.cfg.IgnoredDevices())
m.cleanPending(existingDevices, existingFolders, ignoredDevices, nil)
m.cleanPending(cfg.DeviceMap(), cfg.FolderMap(), ignoredDevices, nil)
m.resendClusterConfig(clusterConfigDevices.AsSlice())
return nil
@@ -1184,7 +1179,6 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
panic("bug: ClusterConfig called on closed or nonexistent connection")
}
changed := false
deviceCfg, ok := m.cfg.Device(deviceID)
if !ok {
l.Debugln("Device disappeared from config while processing cluster-config")
@@ -1219,21 +1213,33 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
// Needs to happen outside of the fmut, as can cause CommitConfiguration
if deviceCfg.AutoAcceptFolders {
changedFolders := make([]config.FolderConfiguration, 0, len(cm.Folders))
for _, folder := range cm.Folders {
if fcfg, fchanged := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID]); fchanged {
changedFolders = append(changedFolders, fcfg)
w, _ := m.cfg.Modify(func(cfg *config.Configuration) {
changedFcfg := make(map[string]config.FolderConfiguration)
haveFcfg := cfg.FolderMap()
for _, folder := range cm.Folders {
from, ok := haveFcfg[folder.ID]
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Options.DefaultFolderPath); changed {
changedFcfg[folder.ID] = to
}
}
}
if len(changedFolders) > 0 {
// Need to wait for the waiter, as this calls CommitConfiguration,
// which sets up the folder and as we return from this call,
// ClusterConfig starts poking at m.folderFiles and other things
// that might not exist until the config is committed.
w, _ := m.cfg.SetFolders(changedFolders)
w.Wait()
changed = true
}
if len(changedFcfg) == 0 {
return
}
for i := range cfg.Folders {
if fcfg, ok := changedFcfg[cfg.Folders[i].ID]; ok {
cfg.Folders[i] = fcfg
delete(changedFcfg, cfg.Folders[i].ID)
}
}
for _, fcfg := range changedFcfg {
cfg.Folders = append(cfg.Folders, fcfg)
}
})
// Need to wait for the waiter, as this calls CommitConfiguration,
// which sets up the folder and as we return from this call,
// ClusterConfig starts poking at m.folderFiles and other things
// that might not exist until the config is committed.
w.Wait()
}
tempIndexFolders, paused, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexSenderRegistry)
@@ -1257,11 +1263,12 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
}
if deviceCfg.Introducer {
folders, devices, foldersDevices, introduced := m.handleIntroductions(deviceCfg, cm)
folders, devices, deintroduced := m.handleDeintroductions(deviceCfg, foldersDevices, folders, devices)
if introduced || deintroduced {
changed = true
cfg := m.cfg.RawCopy()
m.cfg.Modify(func(cfg *config.Configuration) {
folders, devices, foldersDevices, introduced := m.handleIntroductions(deviceCfg, cm, cfg.FolderMap(), cfg.DeviceMap())
folders, devices, deintroduced := m.handleDeintroductions(deviceCfg, foldersDevices, folders, devices)
if !introduced && !deintroduced {
return
}
cfg.Folders = make([]config.FolderConfiguration, 0, len(folders))
for _, fcfg := range folders {
cfg.Folders = append(cfg.Folders, fcfg)
@@ -1270,14 +1277,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
for _, dcfg := range devices {
cfg.Devices = append(cfg.Devices, dcfg)
}
m.cfg.Replace(cfg)
}
}
if changed {
if err := m.cfg.Save(); err != nil {
l.Warnln("Failed to save config", err)
}
})
}
return nil
@@ -1492,10 +1492,8 @@ func (m *model) resendClusterConfig(ids []protocol.DeviceID) {
}
// handleIntroductions handles adding devices/folders that are shared by an introducer device
func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (map[string]config.FolderConfiguration, map[protocol.DeviceID]config.DeviceConfiguration, folderDeviceSet, bool) {
func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, folders map[string]config.FolderConfiguration, devices map[protocol.DeviceID]config.DeviceConfiguration) (map[string]config.FolderConfiguration, map[protocol.DeviceID]config.DeviceConfiguration, folderDeviceSet, bool) {
changed := false
folders := m.cfg.Folders()
devices := m.cfg.Devices()
foldersDevices := make(folderDeviceSet)
@@ -1521,7 +1519,7 @@ func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm
foldersDevices.set(device.ID, folder.ID)
if _, ok := m.cfg.Device(device.ID); !ok {
if _, ok := devices[device.ID]; !ok {
// The device is currently unknown. Add it to the config.
devices[device.ID] = m.introduceDevice(device, introducerCfg)
} else if fcfg.SharedWith(device.ID) {
@@ -1602,9 +1600,8 @@ func (m *model) handleDeintroductions(introducerCfg config.DeviceConfiguration,
// handleAutoAccepts handles adding and sharing folders for devices that have
// AutoAcceptFolders set to true.
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *indexSenderStartInfo) (config.FolderConfiguration, bool) {
if cfg, ok := m.cfg.Folder(folder.ID); !ok {
defaultPath := m.cfg.Options().DefaultFolderPath
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *indexSenderStartInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
if !haveCfg {
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
pathAlternatives := []string{
fs.SanitizePath(folder.Label),
@@ -2179,9 +2176,16 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
conn.ClusterConfig(cm)
if (device.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" {
device.Name = hello.DeviceName
m.cfg.SetDevice(device)
m.cfg.Save()
m.cfg.Modify(func(cfg *config.Configuration) {
for i := range cfg.Devices {
if cfg.Devices[i].DeviceID == deviceID {
if cfg.Devices[i].Name == "" || cfg.Options.OverwriteRemoteDevNames {
cfg.Devices[i].Name = hello.DeviceName
}
return
}
}
})
}
m.deviceWasSeen(deviceID)
@@ -2651,6 +2655,9 @@ func (m *model) VerifyConfiguration(from, to config.Configuration) error {
func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// TODO: This should not use reflect, and should take more care to try to handle stuff without restart.
// Delay processing config changes until after the initial setup
<-m.started
// Go through the folder configs and figure out if we need to restart or not.
// Tracks devices affected by any configuration change to resend ClusterConfig.
+196 -231
View File
File diff suppressed because it is too large Load Diff
+17 -7
View File
@@ -60,11 +60,16 @@ func TestProgressEmitter(t *testing.T) {
w := evLogger.Subscribe(events.DownloadProgress)
c := createTmpWrapper(config.Configuration{})
c, cfgCancel := createTmpWrapper(config.Configuration{})
defer os.Remove(c.ConfigPath())
c.SetOptions(config.OptionsConfiguration{
ProgressUpdateIntervalS: 60, // irrelevant, but must be positive
defer cfgCancel()
waiter, err := c.Modify(func(cfg *config.Configuration) {
cfg.Options.ProgressUpdateIntervalS = 60 // irrelevant, but must be positive
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
p := NewProgressEmitter(c, evLogger)
go p.Serve(ctx)
@@ -109,12 +114,17 @@ func TestProgressEmitter(t *testing.T) {
}
func TestSendDownloadProgressMessages(t *testing.T) {
c := createTmpWrapper(config.Configuration{})
c, cfgCancel := createTmpWrapper(config.Configuration{})
defer os.Remove(c.ConfigPath())
c.SetOptions(config.OptionsConfiguration{
ProgressUpdateIntervalS: 60, // irrelevant, but must be positive
TempIndexMinBlocks: 10,
defer cfgCancel()
waiter, err := c.Modify(func(cfg *config.Configuration) {
cfg.Options.ProgressUpdateIntervalS = 60 // irrelevant, but must be positive
cfg.Options.TempIndexMinBlocks = 10
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
fc := &fakeConnection{}
+45 -37
View File
@@ -29,7 +29,8 @@ func TestRequestSimple(t *testing.T) {
// Verify that the model performs a request and creates a file based on
// an incoming index update.
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -72,7 +73,8 @@ func TestSymlinkTraversalRead(t *testing.T) {
return
}
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
// We listen for incoming index updates and trigger when we see one for
@@ -115,7 +117,8 @@ func TestSymlinkTraversalWrite(t *testing.T) {
return
}
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
// We listen for incoming index updates and trigger when we see one for
@@ -174,7 +177,8 @@ func TestSymlinkTraversalWrite(t *testing.T) {
func TestRequestCreateTmpSymlink(t *testing.T) {
// Test that an update for a temporary file is invalidated
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
// We listen for incoming index updates and trigger when we see one for
@@ -216,14 +220,15 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) {
// Sets up a folder with trashcan versioning and tries to use a
// deleted symlink to escape
w, fcfg := tmpDefaultWrapper()
w, fcfg, wCancel := tmpDefaultWrapper()
defer wCancel()
defer func() {
os.RemoveAll(fcfg.Filesystem().URI())
os.Remove(w.ConfigPath())
}()
fcfg.Versioning = config.VersioningConfiguration{Type: "trashcan"}
w.SetFolder(fcfg)
setFolder(t, w, fcfg)
m, fc := setupModelWithConnectionFromWrapper(t, w)
defer cleanupModel(m)
@@ -293,11 +298,12 @@ func TestPullInvalidIgnoredSR(t *testing.T) {
// This test checks that (un-)ignored/invalid/deleted files are treated as expected.
func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
w := createTmpWrapper(defaultCfgWrapper.RawCopy())
w, wCancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
defer wCancel()
fcfg := testFolderConfigTmp()
fss := fcfg.Filesystem()
fcfg.Type = ft
w.SetFolder(fcfg)
setFolder(t, w, fcfg)
m := setupModel(t, w)
defer cleanupModelAndRemoveDir(m, fss.URI())
@@ -420,7 +426,8 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
}
func TestIssue4841(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
received := make(chan []protocol.FileInfo)
@@ -464,7 +471,8 @@ func TestIssue4841(t *testing.T) {
}
func TestRescanIfHaveInvalidContent(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -530,7 +538,8 @@ func TestRescanIfHaveInvalidContent(t *testing.T) {
}
func TestParentDeletion(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
testFs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, testFs.URI())
@@ -609,7 +618,8 @@ func TestRequestSymlinkWindows(t *testing.T) {
t.Skip("windows specific test")
}
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
received := make(chan []protocol.FileInfo)
@@ -677,7 +687,8 @@ func equalContents(path string, contents []byte) error {
}
func TestRequestRemoteRenameChanged(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -812,7 +823,8 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
}
func TestRequestRemoteRenameConflict(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
@@ -903,7 +915,8 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
}
func TestRequestDeleteChanged(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -972,7 +985,8 @@ func TestRequestDeleteChanged(t *testing.T) {
}
func TestNeedFolderFiles(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
@@ -1020,7 +1034,8 @@ func TestNeedFolderFiles(t *testing.T) {
// propagated upon un-ignoring.
// https://github.com/syncthing/syncthing/issues/6038
func TestIgnoreDeleteUnignore(t *testing.T) {
w, fcfg := tmpDefaultWrapper()
w, fcfg, wCancel := tmpDefaultWrapper()
defer wCancel()
m := setupModel(t, w)
fss := fcfg.Filesystem()
tmpDir := fss.URI()
@@ -1120,7 +1135,8 @@ func TestIgnoreDeleteUnignore(t *testing.T) {
// TestRequestLastFileProgress checks that the last pulled file (here only) is registered
// as in progress.
func TestRequestLastFileProgress(t *testing.T) {
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -1156,7 +1172,8 @@ func TestRequestIndexSenderPause(t *testing.T) {
done := make(chan struct{})
defer close(done)
m, fc, fcfg := setupModelWithConnection(t)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
@@ -1215,13 +1232,8 @@ func TestRequestIndexSenderPause(t *testing.T) {
// Local paused and resume
fcfg.Paused = true
waiter, _ := m.cfg.SetFolder(fcfg)
waiter.Wait()
fcfg.Paused = false
waiter, _ = m.cfg.SetFolder(fcfg)
waiter.Wait()
pauseFolder(t, m.cfg, fcfg.ID, true)
pauseFolder(t, m.cfg, fcfg.ID, false)
seq++
files[0].Sequence = seq
@@ -1238,16 +1250,12 @@ func TestRequestIndexSenderPause(t *testing.T) {
cc.Folders[0].Paused = true
m.ClusterConfig(device1, cc)
fcfg.Paused = true
waiter, _ = m.cfg.SetFolder(fcfg)
waiter.Wait()
pauseFolder(t, m.cfg, fcfg.ID, true)
cc.Folders[0].Paused = false
m.ClusterConfig(device1, cc)
fcfg.Paused = false
waiter, _ = m.cfg.SetFolder(fcfg)
waiter.Wait()
pauseFolder(t, m.cfg, fcfg.ID, false)
seq++
files[0].Sequence = seq
@@ -1277,7 +1285,8 @@ func TestRequestIndexSenderPause(t *testing.T) {
}
func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
w, fcfg := tmpDefaultWrapper()
w, fcfg, wCancel := tmpDefaultWrapper()
defer wCancel()
tfs := fcfg.Filesystem()
dir1 := "foo"
dir2 := "bar"
@@ -1340,12 +1349,11 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
}
func TestRequestReceiveEncryptedLocalNoSend(t *testing.T) {
w, fcfg := tmpDefaultWrapper()
w, fcfg, wCancel := tmpDefaultWrapper()
defer wCancel()
tfs := fcfg.Filesystem()
fcfg.Type = config.FolderTypeReceiveEncrypted
waiter, err := w.SetFolder(fcfg)
must(t, err)
waiter.Wait()
setFolder(t, w, fcfg)
encToken := protocol.PasswordToken(fcfg.ID, "pw")
must(t, tfs.Mkdir(config.DefaultMarkerName, 0777))
+107 -19
View File
@@ -13,6 +13,8 @@ import (
"testing"
"time"
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
@@ -24,12 +26,13 @@ import (
)
var (
myID, device1, device2 protocol.DeviceID
defaultCfgWrapper config.Wrapper
defaultFolderConfig config.FolderConfiguration
defaultFs fs.Filesystem
defaultCfg config.Configuration
defaultAutoAcceptCfg config.Configuration
myID, device1, device2 protocol.DeviceID
defaultCfgWrapper config.Wrapper
defaultCfgWrapperCancel context.CancelFunc
defaultFolderConfig config.FolderConfiguration
defaultFs fs.Filesystem
defaultCfg config.Configuration
defaultAutoAcceptCfg config.Configuration
)
func init() {
@@ -40,12 +43,13 @@ func init() {
defaultFolderConfig = testFolderConfig("testdata")
defaultFs = defaultFolderConfig.Filesystem()
defaultCfgWrapper = createTmpWrapper(config.New(myID))
_, _ = defaultCfgWrapper.SetDevice(config.NewDeviceConfiguration(device1, "device1"))
_, _ = defaultCfgWrapper.SetFolder(defaultFolderConfig)
opts := defaultCfgWrapper.Options()
opts.KeepTemporariesH = 1
_, _ = defaultCfgWrapper.SetOptions(opts)
defaultCfgWrapper, defaultCfgWrapperCancel = createTmpWrapper(config.New(myID))
waiter, _ := defaultCfgWrapper.Modify(func(cfg *config.Configuration) {
cfg.SetDevice(config.NewDeviceConfiguration(device1, "device1"))
cfg.SetFolder(defaultFolderConfig)
cfg.Options.KeepTemporariesH = 1
})
waiter.Wait()
defaultCfg = defaultCfgWrapper.RawCopy()
@@ -70,11 +74,28 @@ func init() {
}
}
func tmpDefaultWrapper() (config.Wrapper, config.FolderConfiguration) {
w := createTmpWrapper(defaultCfgWrapper.RawCopy())
func createTmpWrapper(cfg config.Configuration) (config.Wrapper, context.CancelFunc) {
tmpFile, err := ioutil.TempFile("", "syncthing-testConfig-")
if err != nil {
panic(err)
}
wrapper := config.Wrap(tmpFile.Name(), cfg, myID, events.NoopLogger)
tmpFile.Close()
if cfgService, ok := wrapper.(suture.Service); ok {
ctx, cancel := context.WithCancel(context.Background())
go cfgService.Serve(ctx)
return wrapper, cancel
}
return wrapper, func() {}
}
func tmpDefaultWrapper() (config.Wrapper, config.FolderConfiguration, context.CancelFunc) {
w, cancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
fcfg := testFolderConfigTmp()
_, _ = w.SetFolder(fcfg)
return w, fcfg
_, _ = w.Modify(func(cfg *config.Configuration) {
cfg.SetFolder(fcfg)
})
return w, fcfg, cancel
}
func testFolderConfigTmp() config.FolderConfiguration {
@@ -96,11 +117,11 @@ func testFolderConfigFake() config.FolderConfiguration {
return cfg
}
func setupModelWithConnection(t testing.TB) (*testModel, *fakeConnection, config.FolderConfiguration) {
func setupModelWithConnection(t testing.TB) (*testModel, *fakeConnection, config.FolderConfiguration, context.CancelFunc) {
t.Helper()
w, fcfg := tmpDefaultWrapper()
w, fcfg, cancel := tmpDefaultWrapper()
m, fc := setupModelWithConnectionFromWrapper(t, w)
return m, fc, fcfg
return m, fc, fcfg, cancel
}
func setupModelWithConnectionFromWrapper(t testing.TB, w config.Wrapper) (*testModel, *fakeConnection) {
@@ -313,3 +334,70 @@ func newFileSet(t testing.TB, folder string, fs fs.Filesystem, ldb *db.Lowlevel)
}
return fset
}
func replace(t testing.TB, w config.Wrapper, to config.Configuration) {
t.Helper()
waiter, err := w.Modify(func(cfg *config.Configuration) {
*cfg = to
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
}
func pauseFolder(t testing.TB, w config.Wrapper, id string, paused bool) {
t.Helper()
waiter, err := w.Modify(func(cfg *config.Configuration) {
_, i, _ := cfg.Folder(id)
cfg.Folders[i].Paused = paused
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
}
func setFolder(t testing.TB, w config.Wrapper, fcfg config.FolderConfiguration) {
t.Helper()
waiter, err := w.Modify(func(cfg *config.Configuration) {
cfg.SetFolder(fcfg)
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
}
func pauseDevice(t testing.TB, w config.Wrapper, id protocol.DeviceID, paused bool) {
t.Helper()
waiter, err := w.Modify(func(cfg *config.Configuration) {
_, i, _ := cfg.Device(id)
cfg.Devices[i].Paused = paused
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
}
func setDevice(t testing.TB, w config.Wrapper, device config.DeviceConfiguration) {
t.Helper()
waiter, err := w.Modify(func(cfg *config.Configuration) {
cfg.SetDevice(device)
})
if err != nil {
t.Fatal(err)
}
waiter.Wait()
}
func addDevice2(t testing.TB, w config.Wrapper, fcfg config.FolderConfiguration) {
waiter, err := w.Modify(func(cfg *config.Configuration) {
cfg.SetDevice(config.NewDeviceConfiguration(device2, "device2"))
fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
cfg.SetFolder(fcfg)
})
must(t, err)
waiter.Wait()
}