gui, lib/config, lib/model: Support auto-accepting folders (fixes #2299)
Also introduces a new Waiter interface for config changes and segments the configuration GUI. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4551
This commit is contained in:
committed by
Jakob Borg
parent
c005b8dcb0
commit
445c4edeca
@@ -52,7 +52,7 @@ func TestReplaceCommit(t *testing.T) {
|
||||
// Replace config. We should get back a clean response and the config
|
||||
// should change.
|
||||
|
||||
err := w.Replace(Configuration{Version: 1})
|
||||
_, err := w.Replace(Configuration{Version: 1})
|
||||
if err != nil {
|
||||
t.Fatal("Should not have a validation error:", err)
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func TestReplaceCommit(t *testing.T) {
|
||||
sub0 := requiresRestart{committed: make(chan struct{}, 1)}
|
||||
w.Subscribe(sub0)
|
||||
|
||||
err = w.Replace(Configuration{Version: 2})
|
||||
_, err = w.Replace(Configuration{Version: 2})
|
||||
if err != nil {
|
||||
t.Fatal("Should not have a validation error:", err)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func TestReplaceCommit(t *testing.T) {
|
||||
|
||||
w.Subscribe(validationError{})
|
||||
|
||||
err = w.Replace(Configuration{Version: 3})
|
||||
_, err = w.Replace(Configuration{Version: 3})
|
||||
if err == nil {
|
||||
t.Fatal("Should have a validation error")
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
|
||||
t.Error("Should have less devices")
|
||||
}
|
||||
|
||||
err = wrapper.Replace(raw)
|
||||
_, err = wrapper.Replace(raw)
|
||||
if err != nil {
|
||||
t.Errorf("Failed: %s", err)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type DeviceConfiguration struct {
|
||||
IntroducedBy protocol.DeviceID `xml:"introducedBy,attr" json:"introducedBy"`
|
||||
Paused bool `xml:"paused" json:"paused"`
|
||||
AllowedNetworks []string `xml:"allowedNetwork,omitempty" json:"allowedNetworks"`
|
||||
AutoAcceptFolders bool `xml:"autoAcceptFolders" json:"autoAcceptFolders"`
|
||||
}
|
||||
|
||||
func NewDeviceConfiguration(id protocol.DeviceID, name string) DeviceConfiguration {
|
||||
|
||||
@@ -24,7 +24,7 @@ var (
|
||||
const DefaultMarkerName = ".stfolder"
|
||||
|
||||
type FolderConfiguration struct {
|
||||
ID string `xml:"id,attr" json:"id"`
|
||||
ID string `xml:"id,attr" json:"id" restart:"false"`
|
||||
Label string `xml:"label,attr" json:"label"`
|
||||
FilesystemType fs.FilesystemType `xml:"filesystemType" json:"filesystemType"`
|
||||
Path string `xml:"path,attr" json:"path"`
|
||||
@@ -62,11 +62,18 @@ type FolderDeviceConfiguration struct {
|
||||
IntroducedBy protocol.DeviceID `xml:"introducedBy,attr" json:"introducedBy"`
|
||||
}
|
||||
|
||||
func NewFolderConfiguration(id string, fsType fs.FilesystemType, path string) FolderConfiguration {
|
||||
func NewFolderConfiguration(myID protocol.DeviceID, id, label string, fsType fs.FilesystemType, path string) FolderConfiguration {
|
||||
f := FolderConfiguration{
|
||||
ID: id,
|
||||
FilesystemType: fsType,
|
||||
Path: path,
|
||||
ID: id,
|
||||
Label: label,
|
||||
RescanIntervalS: 60,
|
||||
FSWatcherDelayS: 10,
|
||||
MinDiskFree: Size{Value: 1, Unit: "%"},
|
||||
Devices: []FolderDeviceConfiguration{{DeviceID: myID}},
|
||||
AutoNormalize: true,
|
||||
MaxConflicts: -1,
|
||||
FilesystemType: fsType,
|
||||
Path: path,
|
||||
}
|
||||
f.prepare()
|
||||
return f
|
||||
|
||||
@@ -96,11 +96,11 @@ func (WeakHashSelectionMethod) ParseDefault(value string) (interface{}, error) {
|
||||
|
||||
type OptionsConfiguration struct {
|
||||
ListenAddresses []string `xml:"listenAddress" json:"listenAddresses" default:"default"`
|
||||
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"default"`
|
||||
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true"`
|
||||
LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true"`
|
||||
LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21027"`
|
||||
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" json:"localAnnounceMCAddr" default:"[ff12::8384]:21027"`
|
||||
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"default" restart:"true"`
|
||||
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true" restart:"true"`
|
||||
LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true" restart:"true"`
|
||||
LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21027" restart:"true"`
|
||||
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" json:"localAnnounceMCAddr" default:"[ff12::8384]:21027" restart:"true"`
|
||||
MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
|
||||
MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
|
||||
ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"`
|
||||
@@ -117,21 +117,21 @@ type OptionsConfiguration struct {
|
||||
URURL string `xml:"urURL" json:"urURL" default:"https://data.syncthing.net/newdata"`
|
||||
URPostInsecurely bool `xml:"urPostInsecurely" json:"urPostInsecurely" default:"false"` // For testing
|
||||
URInitialDelayS int `xml:"urInitialDelayS" json:"urInitialDelayS" default:"1800"`
|
||||
RestartOnWakeup bool `xml:"restartOnWakeup" json:"restartOnWakeup" default:"true"`
|
||||
AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" json:"autoUpgradeIntervalH" default:"12"` // 0 for off
|
||||
UpgradeToPreReleases bool `xml:"upgradeToPreReleases" json:"upgradeToPreReleases"` // when auto upgrades are enabled
|
||||
KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
|
||||
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"false"`
|
||||
RestartOnWakeup bool `xml:"restartOnWakeup" json:"restartOnWakeup" default:"true" restart:"true"`
|
||||
AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" json:"autoUpgradeIntervalH" default:"12" restart:"true"` // 0 for off
|
||||
UpgradeToPreReleases bool `xml:"upgradeToPreReleases" json:"upgradeToPreReleases" restart:"true"` // when auto upgrades are enabled
|
||||
KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
|
||||
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"false" restart:"true"`
|
||||
ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" json:"progressUpdateIntervalS" default:"5"`
|
||||
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
|
||||
MinHomeDiskFree Size `xml:"minHomeDiskFree" json:"minHomeDiskFree" default:"1 %"`
|
||||
ReleasesURL string `xml:"releasesURL" json:"releasesURL" default:"https://upgrades.syncthing.net/meta.json"`
|
||||
ReleasesURL string `xml:"releasesURL" json:"releasesURL" default:"https://upgrades.syncthing.net/meta.json" restart:"true"`
|
||||
AlwaysLocalNets []string `xml:"alwaysLocalNet" json:"alwaysLocalNets"`
|
||||
OverwriteRemoteDevNames bool `xml:"overwriteRemoteDeviceNamesOnConnect" json:"overwriteRemoteDeviceNamesOnConnect" default:"false"`
|
||||
TempIndexMinBlocks int `xml:"tempIndexMinBlocks" json:"tempIndexMinBlocks" default:"10"`
|
||||
UnackedNotificationIDs []string `xml:"unackedNotificationID" json:"unackedNotificationIDs"`
|
||||
TrafficClass int `xml:"trafficClass" json:"trafficClass"`
|
||||
WeakHashSelectionMethod WeakHashSelectionMethod `xml:"weakHashSelectionMethod" json:"weakHashSelectionMethod"`
|
||||
WeakHashSelectionMethod WeakHashSelectionMethod `xml:"weakHashSelectionMethod" json:"weakHashSelectionMethod" restart:"true"`
|
||||
StunServers []string `xml:"stunServer" json:"stunServers" default:"default"`
|
||||
StunKeepaliveS int `xml:"stunKeepaliveSeconds" json:"stunKeepaliveSeconds" default:"24"`
|
||||
KCPNoDelay bool `xml:"kcpNoDelay" json:"kcpNoDelay" default:"false"`
|
||||
|
||||
@@ -45,6 +45,15 @@ type Committer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Waiter allows to wait for the given config operation to complete.
|
||||
type Waiter interface {
|
||||
Wait()
|
||||
}
|
||||
|
||||
type noopWaiter struct{}
|
||||
|
||||
func (noopWaiter) Wait() {}
|
||||
|
||||
// A wrapper around a Configuration that manages loads, saves and published
|
||||
// notifications of changes to registered Handlers
|
||||
|
||||
@@ -130,37 +139,25 @@ func (w *Wrapper) RawCopy() Configuration {
|
||||
return w.cfg.Copy()
|
||||
}
|
||||
|
||||
// ReplaceBlocking swaps the current configuration object for the given one,
|
||||
// and waits for subscribers to be notified.
|
||||
func (w *Wrapper) ReplaceBlocking(cfg Configuration) error {
|
||||
w.mut.Lock()
|
||||
wg := sync.NewWaitGroup()
|
||||
err := w.replaceLocked(cfg, wg)
|
||||
w.mut.Unlock()
|
||||
wg.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
// Replace swaps the current configuration object for the given one.
|
||||
func (w *Wrapper) Replace(cfg Configuration) error {
|
||||
func (w *Wrapper) Replace(cfg Configuration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
return w.replaceLocked(cfg, nil)
|
||||
return w.replaceLocked(cfg)
|
||||
}
|
||||
|
||||
func (w *Wrapper) replaceLocked(to Configuration, wg sync.WaitGroup) error {
|
||||
func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
|
||||
from := w.cfg
|
||||
|
||||
if err := to.clean(); err != nil {
|
||||
return err
|
||||
return noopWaiter{}, err
|
||||
}
|
||||
|
||||
for _, sub := range w.subs {
|
||||
l.Debugln(sub, "verifying configuration")
|
||||
if err := sub.VerifyConfiguration(from, to); err != nil {
|
||||
l.Debugln(sub, "rejected config:", err)
|
||||
return err
|
||||
return noopWaiter{}, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,23 +165,19 @@ func (w *Wrapper) replaceLocked(to Configuration, wg sync.WaitGroup) error {
|
||||
w.deviceMap = nil
|
||||
w.folderMap = nil
|
||||
|
||||
w.notifyListeners(from, to, wg)
|
||||
|
||||
return nil
|
||||
return w.notifyListeners(from, to), nil
|
||||
}
|
||||
|
||||
func (w *Wrapper) notifyListeners(from, to Configuration, wg sync.WaitGroup) {
|
||||
if wg != nil {
|
||||
wg.Add(len(w.subs))
|
||||
}
|
||||
func (w *Wrapper) notifyListeners(from, to Configuration) Waiter {
|
||||
wg := sync.NewWaitGroup()
|
||||
wg.Add(len(w.subs))
|
||||
for _, sub := range w.subs {
|
||||
go func(commiter Committer) {
|
||||
w.notifyListener(commiter, from.Copy(), to.Copy())
|
||||
if wg != nil {
|
||||
wg.Done()
|
||||
}
|
||||
wg.Done()
|
||||
}(sub)
|
||||
}
|
||||
return wg
|
||||
}
|
||||
|
||||
func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) {
|
||||
@@ -211,7 +204,7 @@ func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration {
|
||||
|
||||
// SetDevices adds new devices to the configuration, or overwrites existing
|
||||
// devices with the same ID.
|
||||
func (w *Wrapper) SetDevices(devs []DeviceConfiguration) error {
|
||||
func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
@@ -231,17 +224,17 @@ func (w *Wrapper) SetDevices(devs []DeviceConfiguration) error {
|
||||
}
|
||||
}
|
||||
|
||||
return w.replaceLocked(newCfg, nil)
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// SetDevice adds a new device to the configuration, or overwrites an existing
|
||||
// device with the same ID.
|
||||
func (w *Wrapper) SetDevice(dev DeviceConfiguration) error {
|
||||
func (w *Wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) {
|
||||
return w.SetDevices([]DeviceConfiguration{dev})
|
||||
}
|
||||
|
||||
// RemoveDevice removes the device from the configuration
|
||||
func (w *Wrapper) RemoveDevice(id protocol.DeviceID) error {
|
||||
func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
@@ -255,10 +248,10 @@ func (w *Wrapper) RemoveDevice(id protocol.DeviceID) error {
|
||||
}
|
||||
}
|
||||
if !removed {
|
||||
return nil
|
||||
return noopWaiter{}, nil
|
||||
}
|
||||
|
||||
return w.replaceLocked(newCfg, nil)
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// Folders returns a map of folders. Folder structures should not be changed,
|
||||
@@ -277,7 +270,7 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration {
|
||||
|
||||
// SetFolder adds a new folder to the configuration, or overwrites an existing
|
||||
// folder with the same ID.
|
||||
func (w *Wrapper) SetFolder(fld FolderConfiguration) error {
|
||||
func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
|
||||
@@ -294,7 +287,7 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) error {
|
||||
newCfg.Folders = append(w.cfg.Folders, fld)
|
||||
}
|
||||
|
||||
return w.replaceLocked(newCfg, nil)
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// Options returns the current options configuration object.
|
||||
@@ -305,12 +298,12 @@ func (w *Wrapper) Options() OptionsConfiguration {
|
||||
}
|
||||
|
||||
// SetOptions replaces the current options configuration object.
|
||||
func (w *Wrapper) SetOptions(opts OptionsConfiguration) error {
|
||||
func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
newCfg := w.cfg.Copy()
|
||||
newCfg.Options = opts
|
||||
return w.replaceLocked(newCfg, nil)
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// GUI returns the current GUI configuration object.
|
||||
@@ -321,12 +314,12 @@ func (w *Wrapper) GUI() GUIConfiguration {
|
||||
}
|
||||
|
||||
// SetGUI replaces the current GUI configuration object.
|
||||
func (w *Wrapper) SetGUI(gui GUIConfiguration) error {
|
||||
func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
|
||||
w.mut.Lock()
|
||||
defer w.mut.Unlock()
|
||||
newCfg := w.cfg.Copy()
|
||||
newCfg.GUI = gui
|
||||
return w.replaceLocked(newCfg, nil)
|
||||
return w.replaceLocked(newCfg)
|
||||
}
|
||||
|
||||
// IgnoredDevice returns whether or not connection attempts from the given
|
||||
|
||||
Reference in New Issue
Block a user