all: Hide implementations behind interfaces for mocked testing (#5548)
* lib/model: Hide implementations behind interfaces for mocked testing * review
This commit is contained in:
parent
8a05492622
commit
722b3fce6a
|
@ -40,11 +40,9 @@ import (
|
||||||
"github.com/syncthing/syncthing/lib/model"
|
"github.com/syncthing/syncthing/lib/model"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/rand"
|
"github.com/syncthing/syncthing/lib/rand"
|
||||||
"github.com/syncthing/syncthing/lib/stats"
|
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
"github.com/syncthing/syncthing/lib/sync"
|
||||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||||
"github.com/syncthing/syncthing/lib/upgrade"
|
"github.com/syncthing/syncthing/lib/upgrade"
|
||||||
"github.com/syncthing/syncthing/lib/versioner"
|
|
||||||
"github.com/vitrun/qart/qr"
|
"github.com/vitrun/qart/qr"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
@ -64,15 +62,15 @@ const (
|
||||||
|
|
||||||
type apiService struct {
|
type apiService struct {
|
||||||
id protocol.DeviceID
|
id protocol.DeviceID
|
||||||
cfg configIntf
|
cfg config.Wrapper
|
||||||
httpsCertFile string
|
httpsCertFile string
|
||||||
httpsKeyFile string
|
httpsKeyFile string
|
||||||
statics *staticsServer
|
statics *staticsServer
|
||||||
model modelIntf
|
model model.Model
|
||||||
eventSubs map[events.EventType]events.BufferedSubscription
|
eventSubs map[events.EventType]events.BufferedSubscription
|
||||||
eventSubsMut sync.Mutex
|
eventSubsMut sync.Mutex
|
||||||
discoverer discover.CachingMux
|
discoverer discover.CachingMux
|
||||||
connectionsService connectionsIntf
|
connectionsService connections.Service
|
||||||
fss *folderSummaryService
|
fss *folderSummaryService
|
||||||
systemConfigMut sync.Mutex // serializes posts to /rest/system/config
|
systemConfigMut sync.Mutex // serializes posts to /rest/system/config
|
||||||
stop chan struct{} // signals intentional stop
|
stop chan struct{} // signals intentional stop
|
||||||
|
@ -86,69 +84,11 @@ type apiService struct {
|
||||||
systemLog logger.Recorder
|
systemLog logger.Recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
type modelIntf interface {
|
|
||||||
GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}
|
|
||||||
Completion(device protocol.DeviceID, folder string) model.FolderCompletion
|
|
||||||
Override(folder string)
|
|
||||||
Revert(folder string)
|
|
||||||
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated)
|
|
||||||
RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error)
|
|
||||||
LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated
|
|
||||||
NeedSize(folder string) db.Counts
|
|
||||||
ConnectionStats() map[string]interface{}
|
|
||||||
DeviceStatistics() map[string]stats.DeviceStatistics
|
|
||||||
FolderStatistics() map[string]stats.FolderStatistics
|
|
||||||
CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool)
|
|
||||||
CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool)
|
|
||||||
ResetFolder(folder string)
|
|
||||||
Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []model.Availability
|
|
||||||
GetIgnores(folder string) ([]string, []string, error)
|
|
||||||
GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error)
|
|
||||||
RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error)
|
|
||||||
SetIgnores(folder string, content []string) error
|
|
||||||
DelayScan(folder string, next time.Duration)
|
|
||||||
ScanFolder(folder string) error
|
|
||||||
ScanFolders() map[string]error
|
|
||||||
ScanFolderSubdirs(folder string, subs []string) error
|
|
||||||
BringToFront(folder, file string)
|
|
||||||
Connection(deviceID protocol.DeviceID) (connections.Connection, bool)
|
|
||||||
GlobalSize(folder string) db.Counts
|
|
||||||
LocalSize(folder string) db.Counts
|
|
||||||
ReceiveOnlyChangedSize(folder string) db.Counts
|
|
||||||
CurrentSequence(folder string) (int64, bool)
|
|
||||||
RemoteSequence(folder string) (int64, bool)
|
|
||||||
State(folder string) (string, time.Time, error)
|
|
||||||
UsageReportingStats(version int, preview bool) map[string]interface{}
|
|
||||||
FolderErrors(folder string) ([]model.FileError, error)
|
|
||||||
WatchError(folder string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type configIntf interface {
|
|
||||||
GUI() config.GUIConfiguration
|
|
||||||
LDAP() config.LDAPConfiguration
|
|
||||||
RawCopy() config.Configuration
|
|
||||||
Options() config.OptionsConfiguration
|
|
||||||
Replace(cfg config.Configuration) (config.Waiter, error)
|
|
||||||
Subscribe(c config.Committer)
|
|
||||||
Folders() map[string]config.FolderConfiguration
|
|
||||||
Devices() map[protocol.DeviceID]config.DeviceConfiguration
|
|
||||||
SetDevice(config.DeviceConfiguration) (config.Waiter, error)
|
|
||||||
SetDevices([]config.DeviceConfiguration) (config.Waiter, error)
|
|
||||||
Save() error
|
|
||||||
ListenAddresses() []string
|
|
||||||
RequiresRestart() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type connectionsIntf interface {
|
|
||||||
Status() map[string]interface{}
|
|
||||||
NATType() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type rater interface {
|
type rater interface {
|
||||||
Rate() float64
|
Rate() float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connectionsIntf, errors, systemLog logger.Recorder, cpu rater) *apiService {
|
func newAPIService(id protocol.DeviceID, cfg config.Wrapper, httpsCertFile, httpsKeyFile, assetDir string, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, errors, systemLog logger.Recorder, cpu rater) *apiService {
|
||||||
service := &apiService{
|
service := &apiService{
|
||||||
id: id,
|
id: id,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
@ -719,7 +659,7 @@ func (s *apiService) getDBStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func folderSummary(cfg configIntf, m modelIntf, folder string) (map[string]interface{}, error) {
|
func folderSummary(cfg config.Wrapper, m model.Model, folder string) (map[string]interface{}, error) {
|
||||||
var res = make(map[string]interface{})
|
var res = make(map[string]interface{})
|
||||||
|
|
||||||
errors, err := m.FolderErrors(folder)
|
errors, err := m.FolderErrors(folder)
|
||||||
|
|
|
@ -956,7 +956,7 @@ func setupSignalHandling() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadOrDefaultConfig() (*config.Wrapper, error) {
|
func loadOrDefaultConfig() (config.Wrapper, error) {
|
||||||
cfgFile := locations.Get(locations.ConfigFile)
|
cfgFile := locations.Get(locations.ConfigFile)
|
||||||
cfg, err := config.Load(cfgFile, myID)
|
cfg, err := config.Load(cfgFile, myID)
|
||||||
|
|
||||||
|
@ -967,7 +967,7 @@ func loadOrDefaultConfig() (*config.Wrapper, error) {
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfigAtStartup() (*config.Wrapper, error) {
|
func loadConfigAtStartup() (config.Wrapper, error) {
|
||||||
cfgFile := locations.Get(locations.ConfigFile)
|
cfgFile := locations.Get(locations.ConfigFile)
|
||||||
cfg, err := config.Load(cfgFile, myID)
|
cfg, err := config.Load(cfgFile, myID)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -996,7 +996,7 @@ func loadConfigAtStartup() (*config.Wrapper, error) {
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func archiveAndSaveConfig(cfg *config.Wrapper) error {
|
func archiveAndSaveConfig(cfg config.Wrapper) error {
|
||||||
// Copy the existing config to an archive copy
|
// Copy the existing config to an archive copy
|
||||||
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", cfg.RawCopy().OriginalVersion)
|
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", cfg.RawCopy().OriginalVersion)
|
||||||
l.Infoln("Archiving a copy of old config file format at:", archivePath)
|
l.Infoln("Archiving a copy of old config file format at:", archivePath)
|
||||||
|
@ -1061,7 +1061,7 @@ func startAuditing(mainService *suture.Supervisor, auditFile string) {
|
||||||
l.Infoln("Audit log in", auditDest)
|
l.Infoln("Audit log in", auditDest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) {
|
func setupGUI(mainService *suture.Supervisor, cfg config.Wrapper, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) {
|
||||||
guiCfg := cfg.GUI()
|
guiCfg := cfg.GUI()
|
||||||
|
|
||||||
if !guiCfg.Enabled {
|
if !guiCfg.Enabled {
|
||||||
|
@ -1091,7 +1091,7 @@ func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultConfig(cfgFile string) (*config.Wrapper, error) {
|
func defaultConfig(cfgFile string) (config.Wrapper, error) {
|
||||||
newCfg, err := config.NewWithFreePorts(myID)
|
newCfg, err := config.NewWithFreePorts(myID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1154,7 +1154,7 @@ func standbyMonitor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoUpgrade(cfg *config.Wrapper) {
|
func autoUpgrade(cfg config.Wrapper) {
|
||||||
timer := time.NewTimer(0)
|
timer := time.NewTimer(0)
|
||||||
sub := events.Default.Subscribe(events.DeviceConnected)
|
sub := events.Default.Subscribe(events.DeviceConnected)
|
||||||
for {
|
for {
|
||||||
|
@ -1256,7 +1256,7 @@ func cleanConfigDirectory() {
|
||||||
// checkShortIDs verifies that the configuration won't result in duplicate
|
// checkShortIDs verifies that the configuration won't result in duplicate
|
||||||
// short ID:s; that is, that the devices in the cluster all have unique
|
// short ID:s; that is, that the devices in the cluster all have unique
|
||||||
// initial 64 bits.
|
// initial 64 bits.
|
||||||
func checkShortIDs(cfg *config.Wrapper) error {
|
func checkShortIDs(cfg config.Wrapper) error {
|
||||||
exists := make(map[protocol.ShortID]protocol.DeviceID)
|
exists := make(map[protocol.ShortID]protocol.DeviceID)
|
||||||
for deviceID := range cfg.Devices() {
|
for deviceID := range cfg.Devices() {
|
||||||
shortID := deviceID.Short()
|
shortID := deviceID.Short()
|
||||||
|
@ -1278,7 +1278,7 @@ func showPaths(options RuntimeOptions) {
|
||||||
fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
|
fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPauseState(cfg *config.Wrapper, paused bool) {
|
func setPauseState(cfg config.Wrapper, paused bool) {
|
||||||
raw := cfg.RawCopy()
|
raw := cfg.RawCopy()
|
||||||
for i := range raw.Devices {
|
for i := range raw.Devices {
|
||||||
raw.Devices[i].Paused = paused
|
raw.Devices[i].Paused = paused
|
||||||
|
|
|
@ -44,6 +44,8 @@ func (c *mockedConfig) Replace(cfg config.Configuration) (config.Waiter, error)
|
||||||
|
|
||||||
func (c *mockedConfig) Subscribe(cm config.Committer) {}
|
func (c *mockedConfig) Subscribe(cm config.Committer) {}
|
||||||
|
|
||||||
|
func (c *mockedConfig) Unsubscribe(cm config.Committer) {}
|
||||||
|
|
||||||
func (c *mockedConfig) Folders() map[string]config.FolderConfiguration {
|
func (c *mockedConfig) Folders() map[string]config.FolderConfiguration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -68,6 +70,58 @@ func (c *mockedConfig) RequiresRestart() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *mockedConfig) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) {}
|
||||||
|
|
||||||
|
func (c *mockedConfig) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) {}
|
||||||
|
|
||||||
|
func (m *mockedConfig) MyName() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) ConfigPath() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) SetGUI(gui config.GUIConfiguration) (config.Waiter, error) {
|
||||||
|
return noopWaiter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) SetOptions(opts config.OptionsConfiguration) (config.Waiter, error) {
|
||||||
|
return noopWaiter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) Folder(id string) (config.FolderConfiguration, bool) {
|
||||||
|
return config.FolderConfiguration{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) FolderList() []config.FolderConfiguration {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) SetFolder(fld config.FolderConfiguration) (config.Waiter, error) {
|
||||||
|
return noopWaiter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) Device(id protocol.DeviceID) (config.DeviceConfiguration, bool) {
|
||||||
|
return config.DeviceConfiguration{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) RemoveDevice(id protocol.DeviceID) (config.Waiter, error) {
|
||||||
|
return noopWaiter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) IgnoredDevice(id protocol.DeviceID) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) IgnoredFolder(device protocol.DeviceID, folder string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedConfig) GlobalDiscoveryServers() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type noopWaiter struct{}
|
type noopWaiter struct{}
|
||||||
|
|
||||||
func (noopWaiter) Wait() {}
|
func (noopWaiter) Wait() {}
|
||||||
|
|
|
@ -15,3 +15,7 @@ func (m *mockedConnections) Status() map[string]interface{} {
|
||||||
func (m *mockedConnections) NATType() string {
|
func (m *mockedConnections) NATType() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockedConnections) Serve() {}
|
||||||
|
|
||||||
|
func (m *mockedConnections) Stop() {}
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/connections"
|
"github.com/syncthing/syncthing/lib/connections"
|
||||||
"github.com/syncthing/syncthing/lib/db"
|
"github.com/syncthing/syncthing/lib/db"
|
||||||
"github.com/syncthing/syncthing/lib/model"
|
"github.com/syncthing/syncthing/lib/model"
|
||||||
|
@ -150,3 +152,38 @@ func (m *mockedModel) WatchError(folder string) error {
|
||||||
func (m *mockedModel) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
|
func (m *mockedModel) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) Serve() {}
|
||||||
|
func (m *mockedModel) Stop() {}
|
||||||
|
func (m *mockedModel) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {}
|
||||||
|
func (m *mockedModel) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (protocol.RequestResponse, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfig) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) Closed(conn protocol.Connection, err error) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) DownloadProgress(deviceID protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) AddConnection(conn connections.Connection, hello protocol.HelloResult) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) GetHello(protocol.DeviceID) protocol.HelloIntf {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedModel) AddFolder(cfg config.FolderConfiguration) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) RestartFolder(from, to config.FolderConfiguration) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) StartFolder(folder string) {}
|
||||||
|
|
||||||
|
func (m *mockedModel) StartDeadlockDetector(timeout time.Duration) {}
|
||||||
|
|
|
@ -9,7 +9,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/events"
|
"github.com/syncthing/syncthing/lib/events"
|
||||||
|
"github.com/syncthing/syncthing/lib/model"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
"github.com/syncthing/syncthing/lib/sync"
|
||||||
"github.com/thejerf/suture"
|
"github.com/thejerf/suture"
|
||||||
|
@ -20,8 +22,8 @@ import (
|
||||||
type folderSummaryService struct {
|
type folderSummaryService struct {
|
||||||
*suture.Supervisor
|
*suture.Supervisor
|
||||||
|
|
||||||
cfg configIntf
|
cfg config.Wrapper
|
||||||
model modelIntf
|
model model.Model
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
immediate chan string
|
immediate chan string
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ type folderSummaryService struct {
|
||||||
lastEventReqMut sync.Mutex
|
lastEventReqMut sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFolderSummaryService(cfg configIntf, m modelIntf) *folderSummaryService {
|
func newFolderSummaryService(cfg config.Wrapper, m model.Model) *folderSummaryService {
|
||||||
service := &folderSummaryService{
|
service := &folderSummaryService{
|
||||||
Supervisor: suture.New("folderSummaryService", suture.Spec{
|
Supervisor: suture.New("folderSummaryService", suture.Spec{
|
||||||
PassThroughPanics: true,
|
PassThroughPanics: true,
|
||||||
|
|
|
@ -37,7 +37,7 @@ const usageReportVersion = 3
|
||||||
|
|
||||||
// reportData returns the data to be sent in a usage report. It's used in
|
// reportData returns the data to be sent in a usage report. It's used in
|
||||||
// various places, so not part of the usageReportingManager object.
|
// various places, so not part of the usageReportingManager object.
|
||||||
func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf, version int, preview bool) map[string]interface{} {
|
func reportData(cfg config.Wrapper, m model.Model, connectionsService connections.Service, version int, preview bool) map[string]interface{} {
|
||||||
opts := cfg.Options()
|
opts := cfg.Options()
|
||||||
res := make(map[string]interface{})
|
res := make(map[string]interface{})
|
||||||
res["urVersion"] = version
|
res["urVersion"] = version
|
||||||
|
@ -323,16 +323,16 @@ func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf,
|
||||||
}
|
}
|
||||||
|
|
||||||
type usageReportingService struct {
|
type usageReportingService struct {
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
model *model.Model
|
model model.Model
|
||||||
connectionsService *connections.Service
|
connectionsService connections.Service
|
||||||
forceRun chan struct{}
|
forceRun chan struct{}
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
stopped chan struct{}
|
stopped chan struct{}
|
||||||
stopMut sync.RWMutex
|
stopMut sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUsageReportingService(cfg *config.Wrapper, model *model.Model, connectionsService *connections.Service) *usageReportingService {
|
func newUsageReportingService(cfg config.Wrapper, model model.Model, connectionsService connections.Service) *usageReportingService {
|
||||||
svc := &usageReportingService{
|
svc := &usageReportingService{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
model: model,
|
model: model,
|
||||||
|
|
|
@ -93,7 +93,7 @@ func TestDeviceConfig(t *testing.T) {
|
||||||
t.Fatal("Unexpected file")
|
t.Fatal("Unexpected file")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := wr.cfg
|
cfg := wr.(*wrapper).cfg
|
||||||
|
|
||||||
expectedFolders := []FolderConfiguration{
|
expectedFolders := []FolderConfiguration{
|
||||||
{
|
{
|
||||||
|
@ -515,7 +515,7 @@ func TestNewSaveLoad(t *testing.T) {
|
||||||
cfg := Wrap(path, intCfg)
|
cfg := Wrap(path, intCfg)
|
||||||
|
|
||||||
// To make the equality pass later
|
// To make the equality pass later
|
||||||
cfg.cfg.XMLName.Local = "configuration"
|
cfg.(*wrapper).cfg.XMLName.Local = "configuration"
|
||||||
|
|
||||||
if exists(path) {
|
if exists(path) {
|
||||||
t.Error(path, "exists")
|
t.Error(path, "exists")
|
||||||
|
@ -827,7 +827,7 @@ func TestIgnoredFolders(t *testing.T) {
|
||||||
|
|
||||||
// 2 for folder2, 1 for folder1, as non-existing device and device the folder is shared with is removed.
|
// 2 for folder2, 1 for folder1, as non-existing device and device the folder is shared with is removed.
|
||||||
expectedIgnoredFolders := 3
|
expectedIgnoredFolders := 3
|
||||||
for _, dev := range wrapper.cfg.Devices {
|
for _, dev := range wrapper.Devices() {
|
||||||
expectedIgnoredFolders -= len(dev.IgnoredFolders)
|
expectedIgnoredFolders -= len(dev.IgnoredFolders)
|
||||||
}
|
}
|
||||||
if expectedIgnoredFolders != 0 {
|
if expectedIgnoredFolders != 0 {
|
||||||
|
|
|
@ -52,10 +52,48 @@ type noopWaiter struct{}
|
||||||
|
|
||||||
func (noopWaiter) Wait() {}
|
func (noopWaiter) Wait() {}
|
||||||
|
|
||||||
// A wrapper around a Configuration that manages loads, saves and published
|
// A Wrapper around a Configuration that manages loads, saves and published
|
||||||
// notifications of changes to registered Handlers
|
// notifications of changes to registered Handlers
|
||||||
|
type Wrapper interface {
|
||||||
|
MyName() string
|
||||||
|
ConfigPath() string
|
||||||
|
|
||||||
type Wrapper struct {
|
RawCopy() Configuration
|
||||||
|
Replace(cfg Configuration) (Waiter, error)
|
||||||
|
RequiresRestart() bool
|
||||||
|
Save() error
|
||||||
|
|
||||||
|
GUI() GUIConfiguration
|
||||||
|
SetGUI(gui GUIConfiguration) (Waiter, error)
|
||||||
|
LDAP() LDAPConfiguration
|
||||||
|
|
||||||
|
Options() OptionsConfiguration
|
||||||
|
SetOptions(opts OptionsConfiguration) (Waiter, error)
|
||||||
|
|
||||||
|
Folder(id string) (FolderConfiguration, bool)
|
||||||
|
Folders() map[string]FolderConfiguration
|
||||||
|
FolderList() []FolderConfiguration
|
||||||
|
SetFolder(fld FolderConfiguration) (Waiter, error)
|
||||||
|
|
||||||
|
Device(id protocol.DeviceID) (DeviceConfiguration, bool)
|
||||||
|
Devices() map[protocol.DeviceID]DeviceConfiguration
|
||||||
|
RemoveDevice(id protocol.DeviceID) (Waiter, error)
|
||||||
|
SetDevice(DeviceConfiguration) (Waiter, error)
|
||||||
|
SetDevices([]DeviceConfiguration) (Waiter, error)
|
||||||
|
|
||||||
|
AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string)
|
||||||
|
AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID)
|
||||||
|
IgnoredDevice(id protocol.DeviceID) bool
|
||||||
|
IgnoredFolder(device protocol.DeviceID, folder string) bool
|
||||||
|
|
||||||
|
ListenAddresses() []string
|
||||||
|
GlobalDiscoveryServers() []string
|
||||||
|
|
||||||
|
Subscribe(c Committer)
|
||||||
|
Unsubscribe(c Committer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrapper struct {
|
||||||
cfg Configuration
|
cfg Configuration
|
||||||
path string
|
path string
|
||||||
|
|
||||||
|
@ -69,8 +107,8 @@ type Wrapper struct {
|
||||||
|
|
||||||
// Wrap wraps an existing Configuration structure and ties it to a file on
|
// Wrap wraps an existing Configuration structure and ties it to a file on
|
||||||
// disk.
|
// disk.
|
||||||
func Wrap(path string, cfg Configuration) *Wrapper {
|
func Wrap(path string, cfg Configuration) Wrapper {
|
||||||
w := &Wrapper{
|
w := &wrapper{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
path: path,
|
path: path,
|
||||||
mut: sync.NewMutex(),
|
mut: sync.NewMutex(),
|
||||||
|
@ -80,7 +118,7 @@ func Wrap(path string, cfg Configuration) *Wrapper {
|
||||||
|
|
||||||
// Load loads an existing file on disk and returns a new configuration
|
// Load loads an existing file on disk and returns a new configuration
|
||||||
// wrapper.
|
// wrapper.
|
||||||
func Load(path string, myID protocol.DeviceID) (*Wrapper, error) {
|
func Load(path string, myID protocol.DeviceID) (Wrapper, error) {
|
||||||
fd, err := os.Open(path)
|
fd, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -95,13 +133,13 @@ func Load(path string, myID protocol.DeviceID) (*Wrapper, error) {
|
||||||
return Wrap(path, cfg), nil
|
return Wrap(path, cfg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) ConfigPath() string {
|
func (w *wrapper) ConfigPath() string {
|
||||||
return w.path
|
return w.path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe registers the given handler to be called on any future
|
// Subscribe registers the given handler to be called on any future
|
||||||
// configuration changes.
|
// configuration changes.
|
||||||
func (w *Wrapper) Subscribe(c Committer) {
|
func (w *wrapper) Subscribe(c Committer) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
w.subs = append(w.subs, c)
|
w.subs = append(w.subs, c)
|
||||||
w.mut.Unlock()
|
w.mut.Unlock()
|
||||||
|
@ -109,7 +147,7 @@ func (w *Wrapper) Subscribe(c Committer) {
|
||||||
|
|
||||||
// Unsubscribe de-registers the given handler from any future calls to
|
// Unsubscribe de-registers the given handler from any future calls to
|
||||||
// configuration changes
|
// configuration changes
|
||||||
func (w *Wrapper) Unsubscribe(c Committer) {
|
func (w *wrapper) Unsubscribe(c Committer) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
for i := range w.subs {
|
for i := range w.subs {
|
||||||
if w.subs[i] == c {
|
if w.subs[i] == c {
|
||||||
|
@ -123,20 +161,20 @@ func (w *Wrapper) Unsubscribe(c Committer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawCopy returns a copy of the currently wrapped Configuration object.
|
// RawCopy returns a copy of the currently wrapped Configuration object.
|
||||||
func (w *Wrapper) RawCopy() Configuration {
|
func (w *wrapper) RawCopy() Configuration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.cfg.Copy()
|
return w.cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace swaps the current configuration object for the given one.
|
// Replace swaps the current configuration object for the given one.
|
||||||
func (w *Wrapper) Replace(cfg Configuration) (Waiter, error) {
|
func (w *wrapper) Replace(cfg Configuration) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.replaceLocked(cfg.Copy())
|
return w.replaceLocked(cfg.Copy())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
|
func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) {
|
||||||
from := w.cfg
|
from := w.cfg
|
||||||
|
|
||||||
if err := to.clean(); err != nil {
|
if err := to.clean(); err != nil {
|
||||||
|
@ -158,7 +196,7 @@ func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
|
||||||
return w.notifyListeners(from.Copy(), to.Copy()), nil
|
return w.notifyListeners(from.Copy(), to.Copy()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) notifyListeners(from, to Configuration) Waiter {
|
func (w *wrapper) notifyListeners(from, to Configuration) Waiter {
|
||||||
wg := sync.NewWaitGroup()
|
wg := sync.NewWaitGroup()
|
||||||
wg.Add(len(w.subs))
|
wg.Add(len(w.subs))
|
||||||
for _, sub := range w.subs {
|
for _, sub := range w.subs {
|
||||||
|
@ -170,7 +208,7 @@ func (w *Wrapper) notifyListeners(from, to Configuration) Waiter {
|
||||||
return wg
|
return wg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) {
|
func (w *wrapper) notifyListener(sub Committer, from, to Configuration) {
|
||||||
l.Debugln(sub, "committing configuration")
|
l.Debugln(sub, "committing configuration")
|
||||||
if !sub.CommitConfiguration(from, to) {
|
if !sub.CommitConfiguration(from, to) {
|
||||||
l.Debugln(sub, "requires restart")
|
l.Debugln(sub, "requires restart")
|
||||||
|
@ -179,7 +217,7 @@ func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devices returns a map of devices.
|
// Devices returns a map of devices.
|
||||||
func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration {
|
func (w *wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
if w.deviceMap == nil {
|
if w.deviceMap == nil {
|
||||||
|
@ -193,7 +231,7 @@ func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration {
|
||||||
|
|
||||||
// SetDevices adds new devices to the configuration, or overwrites existing
|
// SetDevices adds new devices to the configuration, or overwrites existing
|
||||||
// devices with the same ID.
|
// devices with the same ID.
|
||||||
func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
|
func (w *wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
|
|
||||||
|
@ -218,12 +256,12 @@ func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
|
||||||
|
|
||||||
// SetDevice adds a new device to the configuration, or overwrites an existing
|
// SetDevice adds a new device to the configuration, or overwrites an existing
|
||||||
// device with the same ID.
|
// device with the same ID.
|
||||||
func (w *Wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) {
|
func (w *wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) {
|
||||||
return w.SetDevices([]DeviceConfiguration{dev})
|
return w.SetDevices([]DeviceConfiguration{dev})
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveDevice removes the device from the configuration
|
// RemoveDevice removes the device from the configuration
|
||||||
func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
|
func (w *wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
|
|
||||||
|
@ -240,7 +278,7 @@ func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
|
||||||
|
|
||||||
// Folders returns a map of folders. Folder structures should not be changed,
|
// Folders returns a map of folders. Folder structures should not be changed,
|
||||||
// other than for the purpose of updating via SetFolder().
|
// other than for the purpose of updating via SetFolder().
|
||||||
func (w *Wrapper) Folders() map[string]FolderConfiguration {
|
func (w *wrapper) Folders() map[string]FolderConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
if w.folderMap == nil {
|
if w.folderMap == nil {
|
||||||
|
@ -253,7 +291,7 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FolderList returns a slice of folders.
|
// FolderList returns a slice of folders.
|
||||||
func (w *Wrapper) FolderList() []FolderConfiguration {
|
func (w *wrapper) FolderList() []FolderConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.cfg.Copy().Folders
|
return w.cfg.Copy().Folders
|
||||||
|
@ -261,7 +299,7 @@ func (w *Wrapper) FolderList() []FolderConfiguration {
|
||||||
|
|
||||||
// SetFolder adds a new folder to the configuration, or overwrites an existing
|
// SetFolder adds a new folder to the configuration, or overwrites an existing
|
||||||
// folder with the same ID.
|
// folder with the same ID.
|
||||||
func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
|
func (w *wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
|
|
||||||
|
@ -280,14 +318,14 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options returns the current options configuration object.
|
// Options returns the current options configuration object.
|
||||||
func (w *Wrapper) Options() OptionsConfiguration {
|
func (w *wrapper) Options() OptionsConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.cfg.Options.Copy()
|
return w.cfg.Options.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions replaces the current options configuration object.
|
// SetOptions replaces the current options configuration object.
|
||||||
func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
|
func (w *wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
newCfg := w.cfg.Copy()
|
newCfg := w.cfg.Copy()
|
||||||
|
@ -295,21 +333,21 @@ func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
|
||||||
return w.replaceLocked(newCfg)
|
return w.replaceLocked(newCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) LDAP() LDAPConfiguration {
|
func (w *wrapper) LDAP() LDAPConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.cfg.LDAP.Copy()
|
return w.cfg.LDAP.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI returns the current GUI configuration object.
|
// GUI returns the current GUI configuration object.
|
||||||
func (w *Wrapper) GUI() GUIConfiguration {
|
func (w *wrapper) GUI() GUIConfiguration {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
return w.cfg.GUI.Copy()
|
return w.cfg.GUI.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGUI replaces the current GUI configuration object.
|
// SetGUI replaces the current GUI configuration object.
|
||||||
func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
|
func (w *wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
newCfg := w.cfg.Copy()
|
newCfg := w.cfg.Copy()
|
||||||
|
@ -319,7 +357,7 @@ func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
|
||||||
|
|
||||||
// IgnoredDevice returns whether or not connection attempts from the given
|
// IgnoredDevice returns whether or not connection attempts from the given
|
||||||
// device should be silently ignored.
|
// device should be silently ignored.
|
||||||
func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool {
|
func (w *wrapper) IgnoredDevice(id protocol.DeviceID) bool {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
for _, device := range w.cfg.IgnoredDevices {
|
for _, device := range w.cfg.IgnoredDevices {
|
||||||
|
@ -332,7 +370,7 @@ func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool {
|
||||||
|
|
||||||
// IgnoredFolder returns whether or not share attempts for the given
|
// IgnoredFolder returns whether or not share attempts for the given
|
||||||
// folder should be silently ignored.
|
// folder should be silently ignored.
|
||||||
func (w *Wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool {
|
func (w *wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool {
|
||||||
dev, ok := w.Device(device)
|
dev, ok := w.Device(device)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
@ -341,7 +379,7 @@ func (w *Wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device returns the configuration for the given device and an "ok" bool.
|
// Device returns the configuration for the given device and an "ok" bool.
|
||||||
func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
|
func (w *wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
for _, device := range w.cfg.Devices {
|
for _, device := range w.cfg.Devices {
|
||||||
|
@ -353,7 +391,7 @@ func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Folder returns the configuration for the given folder and an "ok" bool.
|
// Folder returns the configuration for the given folder and an "ok" bool.
|
||||||
func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) {
|
func (w *wrapper) Folder(id string) (FolderConfiguration, bool) {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
for _, folder := range w.cfg.Folders {
|
for _, folder := range w.cfg.Folders {
|
||||||
|
@ -365,7 +403,7 @@ func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save writes the configuration to disk, and generates a ConfigSaved event.
|
// Save writes the configuration to disk, and generates a ConfigSaved event.
|
||||||
func (w *Wrapper) Save() error {
|
func (w *wrapper) Save() error {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
|
|
||||||
|
@ -390,7 +428,7 @@ func (w *Wrapper) Save() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) GlobalDiscoveryServers() []string {
|
func (w *wrapper) GlobalDiscoveryServers() []string {
|
||||||
var servers []string
|
var servers []string
|
||||||
for _, srv := range w.Options().GlobalAnnServers {
|
for _, srv := range w.Options().GlobalAnnServers {
|
||||||
switch srv {
|
switch srv {
|
||||||
|
@ -407,7 +445,7 @@ func (w *Wrapper) GlobalDiscoveryServers() []string {
|
||||||
return util.UniqueStrings(servers)
|
return util.UniqueStrings(servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) ListenAddresses() []string {
|
func (w *wrapper) ListenAddresses() []string {
|
||||||
var addresses []string
|
var addresses []string
|
||||||
for _, addr := range w.Options().ListenAddresses {
|
for _, addr := range w.Options().ListenAddresses {
|
||||||
switch addr {
|
switch addr {
|
||||||
|
@ -420,15 +458,15 @@ func (w *Wrapper) ListenAddresses() []string {
|
||||||
return util.UniqueStrings(addresses)
|
return util.UniqueStrings(addresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) RequiresRestart() bool {
|
func (w *wrapper) RequiresRestart() bool {
|
||||||
return atomic.LoadUint32(&w.requiresRestart) != 0
|
return atomic.LoadUint32(&w.requiresRestart) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) setRequiresRestart() {
|
func (w *wrapper) setRequiresRestart() {
|
||||||
atomic.StoreUint32(&w.requiresRestart, 1)
|
atomic.StoreUint32(&w.requiresRestart, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) MyName() string {
|
func (w *wrapper) MyName() string {
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
myID := w.cfg.MyID
|
myID := w.cfg.MyID
|
||||||
w.mut.Unlock()
|
w.mut.Unlock()
|
||||||
|
@ -436,7 +474,7 @@ func (w *Wrapper) MyName() string {
|
||||||
return cfg.Name
|
return cfg.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) {
|
func (w *wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) {
|
||||||
defer w.Save()
|
defer w.Save()
|
||||||
|
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
|
@ -459,7 +497,7 @@ func (w *Wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, addre
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) {
|
func (w *wrapper) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) {
|
||||||
defer w.Save()
|
defer w.Save()
|
||||||
|
|
||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestIsLANHost(t *testing.T) {
|
||||||
AlwaysLocalNets: []string{"10.20.30.0/24"},
|
AlwaysLocalNets: []string{"10.20.30.0/24"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
s := &Service{cfg: cfg}
|
s := &service{cfg: cfg}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
res := s.isLANHost(tc.addr)
|
res := s.isLANHost(tc.addr)
|
||||||
|
|
|
@ -36,7 +36,7 @@ type waiter interface {
|
||||||
|
|
||||||
const limiterBurstSize = 4 * 128 << 10
|
const limiterBurstSize = 4 * 128 << 10
|
||||||
|
|
||||||
func newLimiter(cfg *config.Wrapper) *limiter {
|
func newLimiter(cfg config.Wrapper) *limiter {
|
||||||
l := &limiter{
|
l := &limiter{
|
||||||
write: rate.NewLimiter(rate.Inf, limiterBurstSize),
|
write: rate.NewLimiter(rate.Inf, limiterBurstSize),
|
||||||
read: rate.NewLimiter(rate.Inf, limiterBurstSize),
|
read: rate.NewLimiter(rate.Inf, limiterBurstSize),
|
||||||
|
|
|
@ -24,7 +24,7 @@ func init() {
|
||||||
device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
|
device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() *config.Wrapper {
|
func initConfig() config.Wrapper {
|
||||||
cfg := config.Wrap("/dev/null", config.New(device1))
|
cfg := config.Wrap("/dev/null", config.New(device1))
|
||||||
dev1Conf = config.NewDeviceConfiguration(device1, "device1")
|
dev1Conf = config.NewDeviceConfiguration(device1, "device1")
|
||||||
dev2Conf = config.NewDeviceConfiguration(device2, "device2")
|
dev2Conf = config.NewDeviceConfiguration(device2, "device2")
|
||||||
|
|
|
@ -22,7 +22,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type relayDialer struct {
|
type relayDialer struct {
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
tlsCfg *tls.Config
|
tlsCfg *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func (d *relayDialer) RedialFrequency() time.Duration {
|
||||||
|
|
||||||
type relayDialerFactory struct{}
|
type relayDialerFactory struct{}
|
||||||
|
|
||||||
func (relayDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer {
|
func (relayDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer {
|
||||||
return &relayDialer{
|
return &relayDialer{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
tlsCfg: tlsCfg,
|
tlsCfg: tlsCfg,
|
||||||
|
|
|
@ -29,7 +29,7 @@ type relayListener struct {
|
||||||
onAddressesChangedNotifier
|
onAddressesChangedNotifier
|
||||||
|
|
||||||
uri *url.URL
|
uri *url.URL
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
tlsCfg *tls.Config
|
tlsCfg *tls.Config
|
||||||
conns chan internalConn
|
conns chan internalConn
|
||||||
factory listenerFactory
|
factory listenerFactory
|
||||||
|
@ -180,7 +180,7 @@ func (t *relayListener) NATType() string {
|
||||||
|
|
||||||
type relayListenerFactory struct{}
|
type relayListenerFactory struct{}
|
||||||
|
|
||||||
func (f *relayListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
|
func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
|
||||||
return &relayListener{
|
return &relayListener{
|
||||||
uri: uri,
|
uri: uri,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
|
@ -77,9 +77,15 @@ var tlsCipherSuiteNames = map[uint16]string{
|
||||||
|
|
||||||
// Service listens and dials all configured unconnected devices, via supported
|
// Service listens and dials all configured unconnected devices, via supported
|
||||||
// dialers. Successful connections are handed to the model.
|
// dialers. Successful connections are handed to the model.
|
||||||
type Service struct {
|
type Service interface {
|
||||||
|
suture.Service
|
||||||
|
Status() map[string]interface{}
|
||||||
|
NATType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
*suture.Supervisor
|
*suture.Supervisor
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
myID protocol.DeviceID
|
myID protocol.DeviceID
|
||||||
model Model
|
model Model
|
||||||
tlsCfg *tls.Config
|
tlsCfg *tls.Config
|
||||||
|
@ -97,10 +103,10 @@ type Service struct {
|
||||||
listenerSupervisor *suture.Supervisor
|
listenerSupervisor *suture.Supervisor
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder,
|
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder,
|
||||||
bepProtocolName string, tlsDefaultCommonName string) *Service {
|
bepProtocolName string, tlsDefaultCommonName string) *service {
|
||||||
|
|
||||||
service := &Service{
|
service := &service{
|
||||||
Supervisor: suture.New("connections.Service", suture.Spec{
|
Supervisor: suture.New("connections.Service", suture.Spec{
|
||||||
Log: func(line string) {
|
Log: func(line string) {
|
||||||
l.Infoln(line)
|
l.Infoln(line)
|
||||||
|
@ -156,7 +162,7 @@ func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) handle() {
|
func (s *service) handle() {
|
||||||
next:
|
next:
|
||||||
for c := range s.conns {
|
for c := range s.conns {
|
||||||
cs := c.ConnectionState()
|
cs := c.ConnectionState()
|
||||||
|
@ -282,7 +288,7 @@ next:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) connect() {
|
func (s *service) connect() {
|
||||||
nextDial := make(map[string]time.Time)
|
nextDial := make(map[string]time.Time)
|
||||||
|
|
||||||
// Used as delay for the first few connection attempts, increases
|
// Used as delay for the first few connection attempts, increases
|
||||||
|
@ -433,7 +439,7 @@ func (s *Service) connect() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) isLANHost(host string) bool {
|
func (s *service) isLANHost(host string) bool {
|
||||||
// Probably we are called with an ip:port combo which we can resolve as
|
// Probably we are called with an ip:port combo which we can resolve as
|
||||||
// a TCP address.
|
// a TCP address.
|
||||||
if addr, err := net.ResolveTCPAddr("tcp", host); err == nil {
|
if addr, err := net.ResolveTCPAddr("tcp", host); err == nil {
|
||||||
|
@ -447,7 +453,7 @@ func (s *Service) isLANHost(host string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) isLAN(addr net.Addr) bool {
|
func (s *service) isLAN(addr net.Addr) bool {
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
|
|
||||||
switch addr := addr.(type) {
|
switch addr := addr.(type) {
|
||||||
|
@ -488,7 +494,7 @@ func (s *Service) isLAN(addr net.Addr) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) createListener(factory listenerFactory, uri *url.URL) bool {
|
func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
|
||||||
// must be called with listenerMut held
|
// must be called with listenerMut held
|
||||||
|
|
||||||
l.Debugln("Starting listener", uri)
|
l.Debugln("Starting listener", uri)
|
||||||
|
@ -500,7 +506,7 @@ func (s *Service) createListener(factory listenerFactory, uri *url.URL) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) logListenAddressesChangedEvent(l genericListener) {
|
func (s *service) logListenAddressesChangedEvent(l genericListener) {
|
||||||
events.Default.Log(events.ListenAddressesChanged, map[string]interface{}{
|
events.Default.Log(events.ListenAddressesChanged, map[string]interface{}{
|
||||||
"address": l.URI(),
|
"address": l.URI(),
|
||||||
"lan": l.LANAddresses(),
|
"lan": l.LANAddresses(),
|
||||||
|
@ -508,11 +514,11 @@ func (s *Service) logListenAddressesChangedEvent(l genericListener) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) VerifyConfiguration(from, to config.Configuration) error {
|
func (s *service) VerifyConfiguration(from, to config.Configuration) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CommitConfiguration(from, to config.Configuration) bool {
|
func (s *service) CommitConfiguration(from, to config.Configuration) bool {
|
||||||
newDevices := make(map[protocol.DeviceID]bool, len(to.Devices))
|
newDevices := make(map[protocol.DeviceID]bool, len(to.Devices))
|
||||||
for _, dev := range to.Devices {
|
for _, dev := range to.Devices {
|
||||||
newDevices[dev.DeviceID] = true
|
newDevices[dev.DeviceID] = true
|
||||||
|
@ -589,7 +595,7 @@ func (s *Service) CommitConfiguration(from, to config.Configuration) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) AllAddresses() []string {
|
func (s *service) AllAddresses() []string {
|
||||||
s.listenersMut.RLock()
|
s.listenersMut.RLock()
|
||||||
var addrs []string
|
var addrs []string
|
||||||
for _, listener := range s.listeners {
|
for _, listener := range s.listeners {
|
||||||
|
@ -604,7 +610,7 @@ func (s *Service) AllAddresses() []string {
|
||||||
return util.UniqueStrings(addrs)
|
return util.UniqueStrings(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ExternalAddresses() []string {
|
func (s *service) ExternalAddresses() []string {
|
||||||
s.listenersMut.RLock()
|
s.listenersMut.RLock()
|
||||||
var addrs []string
|
var addrs []string
|
||||||
for _, listener := range s.listeners {
|
for _, listener := range s.listeners {
|
||||||
|
@ -616,7 +622,7 @@ func (s *Service) ExternalAddresses() []string {
|
||||||
return util.UniqueStrings(addrs)
|
return util.UniqueStrings(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Status() map[string]interface{} {
|
func (s *service) Status() map[string]interface{} {
|
||||||
s.listenersMut.RLock()
|
s.listenersMut.RLock()
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
for addr, listener := range s.listeners {
|
for addr, listener := range s.listeners {
|
||||||
|
@ -636,7 +642,7 @@ func (s *Service) Status() map[string]interface{} {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) NATType() string {
|
func (s *service) NATType() string {
|
||||||
s.listenersMut.RLock()
|
s.listenersMut.RLock()
|
||||||
defer s.listenersMut.RUnlock()
|
defer s.listenersMut.RUnlock()
|
||||||
for _, listener := range s.listeners {
|
for _, listener := range s.listeners {
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (c internalConn) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dialerFactory interface {
|
type dialerFactory interface {
|
||||||
New(*config.Wrapper, *tls.Config) genericDialer
|
New(config.Wrapper, *tls.Config) genericDialer
|
||||||
Priority() int
|
Priority() int
|
||||||
AlwaysWAN() bool
|
AlwaysWAN() bool
|
||||||
Valid(config.Configuration) error
|
Valid(config.Configuration) error
|
||||||
|
@ -135,7 +135,7 @@ type genericDialer interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type listenerFactory interface {
|
type listenerFactory interface {
|
||||||
New(*url.URL, *config.Wrapper, *tls.Config, chan internalConn, *nat.Service) genericListener
|
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service) genericListener
|
||||||
Valid(config.Configuration) error
|
Valid(config.Configuration) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpDialer struct {
|
type tcpDialer struct {
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
tlsCfg *tls.Config
|
tlsCfg *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ func (d *tcpDialer) RedialFrequency() time.Duration {
|
||||||
|
|
||||||
type tcpDialerFactory struct{}
|
type tcpDialerFactory struct{}
|
||||||
|
|
||||||
func (tcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer {
|
func (tcpDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer {
|
||||||
return &tcpDialer{
|
return &tcpDialer{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
tlsCfg: tlsCfg,
|
tlsCfg: tlsCfg,
|
||||||
|
|
|
@ -29,7 +29,7 @@ type tcpListener struct {
|
||||||
onAddressesChangedNotifier
|
onAddressesChangedNotifier
|
||||||
|
|
||||||
uri *url.URL
|
uri *url.URL
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
tlsCfg *tls.Config
|
tlsCfg *tls.Config
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
conns chan internalConn
|
conns chan internalConn
|
||||||
|
@ -195,7 +195,7 @@ func (t *tcpListener) NATType() string {
|
||||||
|
|
||||||
type tcpListenerFactory struct{}
|
type tcpListenerFactory struct{}
|
||||||
|
|
||||||
func (f *tcpListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
|
func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
|
||||||
return &tcpListener{
|
return &tcpListener{
|
||||||
uri: fixupPort(uri, config.DefaultTCPPort),
|
uri: fixupPort(uri, config.DefaultTCPPort),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
|
@ -39,7 +39,7 @@ type folder struct {
|
||||||
config.FolderConfiguration
|
config.FolderConfiguration
|
||||||
localFlags uint32
|
localFlags uint32
|
||||||
|
|
||||||
model *Model
|
model *model
|
||||||
shortID protocol.ShortID
|
shortID protocol.ShortID
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
@ -73,7 +73,7 @@ type puller interface {
|
||||||
pull() bool // true when successfull and should not be retried
|
pull() bool // true when successfull and should not be retried
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFolder(model *Model, cfg config.FolderConfiguration) folder {
|
func newFolder(model *model, cfg config.FolderConfiguration) folder {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
return folder{
|
return folder{
|
||||||
|
|
|
@ -56,7 +56,7 @@ type receiveOnlyFolder struct {
|
||||||
*sendReceiveFolder
|
*sendReceiveFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReceiveOnlyFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
|
func newReceiveOnlyFolder(model *model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
|
||||||
sr := newSendReceiveFolder(model, cfg, ver, fs).(*sendReceiveFolder)
|
sr := newSendReceiveFolder(model, cfg, ver, fs).(*sendReceiveFolder)
|
||||||
sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
|
sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
|
||||||
return &receiveOnlyFolder{sr}
|
return &receiveOnlyFolder{sr}
|
||||||
|
|
|
@ -336,7 +336,7 @@ func setupKnownFiles(t *testing.T, data []byte) []protocol.FileInfo {
|
||||||
return knownFiles
|
return knownFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupROFolder() *Model {
|
func setupROFolder() *model {
|
||||||
fcfg := config.NewFolderConfiguration(myID, "ro", "receive only test", fs.FilesystemTypeBasic, "_recvonly")
|
fcfg := config.NewFolderConfiguration(myID, "ro", "receive only test", fs.FilesystemTypeBasic, "_recvonly")
|
||||||
fcfg.Type = config.FolderTypeReceiveOnly
|
fcfg.Type = config.FolderTypeReceiveOnly
|
||||||
fcfg.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}}
|
fcfg.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}}
|
||||||
|
@ -349,7 +349,7 @@ func setupROFolder() *Model {
|
||||||
wrp := createTmpWrapper(cfg)
|
wrp := createTmpWrapper(cfg)
|
||||||
|
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(wrp, myID, "syncthing", "dev", db, nil)
|
m := newModel(wrp, myID, "syncthing", "dev", db, nil)
|
||||||
|
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
m.AddFolder(fcfg)
|
m.AddFolder(fcfg)
|
||||||
|
|
|
@ -22,7 +22,7 @@ type sendOnlyFolder struct {
|
||||||
folder
|
folder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSendOnlyFolder(model *Model, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service {
|
func newSendOnlyFolder(model *model, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service {
|
||||||
f := &sendOnlyFolder{
|
f := &sendOnlyFolder{
|
||||||
folder: newFolder(model, cfg),
|
folder: newFolder(model, cfg),
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ type sendReceiveFolder struct {
|
||||||
pullErrorsMut sync.Mutex
|
pullErrorsMut sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSendReceiveFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
|
func newSendReceiveFolder(model *model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
|
||||||
f := &sendReceiveFolder{
|
f := &sendReceiveFolder{
|
||||||
folder: newFolder(model, cfg),
|
folder: newFolder(model, cfg),
|
||||||
fs: fs,
|
fs: fs,
|
||||||
|
|
|
@ -75,9 +75,9 @@ func setUpFile(filename string, blockNumbers []int) protocol.FileInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupSendReceiveFolder(files ...protocol.FileInfo) (*Model, *sendReceiveFolder, string) {
|
func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFolder, string) {
|
||||||
w := createTmpWrapper(defaultCfg)
|
w := createTmpWrapper(defaultCfg)
|
||||||
model := NewModel(w, myID, "syncthing", "dev", db.OpenMemory(), nil)
|
model := newModel(w, myID, "syncthing", "dev", db.OpenMemory(), nil)
|
||||||
fcfg, tmpDir := testFolderConfigTmp()
|
fcfg, tmpDir := testFolderConfigTmp()
|
||||||
model.AddFolder(fcfg)
|
model.AddFolder(fcfg)
|
||||||
|
|
||||||
|
|
|
@ -76,10 +76,60 @@ type Availability struct {
|
||||||
FromTemporary bool `json:"fromTemporary"`
|
FromTemporary bool `json:"fromTemporary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model interface {
|
||||||
|
suture.Service
|
||||||
|
|
||||||
|
connections.Model
|
||||||
|
|
||||||
|
AddFolder(cfg config.FolderConfiguration)
|
||||||
|
RestartFolder(from, to config.FolderConfiguration)
|
||||||
|
StartFolder(folder string)
|
||||||
|
ResetFolder(folder string)
|
||||||
|
DelayScan(folder string, next time.Duration)
|
||||||
|
ScanFolder(folder string) error
|
||||||
|
ScanFolders() map[string]error
|
||||||
|
ScanFolderSubdirs(folder string, subs []string) error
|
||||||
|
State(folder string) (string, time.Time, error)
|
||||||
|
FolderErrors(folder string) ([]FileError, error)
|
||||||
|
WatchError(folder string) error
|
||||||
|
Override(folder string)
|
||||||
|
Revert(folder string)
|
||||||
|
BringToFront(folder, file string)
|
||||||
|
GetIgnores(folder string) ([]string, []string, error)
|
||||||
|
SetIgnores(folder string, content []string) error
|
||||||
|
|
||||||
|
GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error)
|
||||||
|
RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error)
|
||||||
|
|
||||||
|
LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated
|
||||||
|
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated)
|
||||||
|
RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error)
|
||||||
|
CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool)
|
||||||
|
CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool)
|
||||||
|
Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability
|
||||||
|
|
||||||
|
GlobalSize(folder string) db.Counts
|
||||||
|
LocalSize(folder string) db.Counts
|
||||||
|
NeedSize(folder string) db.Counts
|
||||||
|
ReceiveOnlyChangedSize(folder string) db.Counts
|
||||||
|
|
||||||
|
CurrentSequence(folder string) (int64, bool)
|
||||||
|
RemoteSequence(folder string) (int64, bool)
|
||||||
|
|
||||||
|
Completion(device protocol.DeviceID, folder string) FolderCompletion
|
||||||
|
ConnectionStats() map[string]interface{}
|
||||||
|
DeviceStatistics() map[string]stats.DeviceStatistics
|
||||||
|
FolderStatistics() map[string]stats.FolderStatistics
|
||||||
|
UsageReportingStats(version int, preview bool) map[string]interface{}
|
||||||
|
|
||||||
|
StartDeadlockDetector(timeout time.Duration)
|
||||||
|
GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type model struct {
|
||||||
*suture.Supervisor
|
*suture.Supervisor
|
||||||
|
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
db *db.Lowlevel
|
db *db.Lowlevel
|
||||||
finder *db.BlockFinder
|
finder *db.BlockFinder
|
||||||
progressEmitter *ProgressEmitter
|
progressEmitter *ProgressEmitter
|
||||||
|
@ -112,7 +162,7 @@ type Model struct {
|
||||||
foldersRunning int32 // for testing only
|
foldersRunning int32 // for testing only
|
||||||
}
|
}
|
||||||
|
|
||||||
type folderFactory func(*Model, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service
|
type folderFactory func(*model, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service
|
||||||
|
|
||||||
var (
|
var (
|
||||||
folderFactories = make(map[config.FolderType]folderFactory)
|
folderFactories = make(map[config.FolderType]folderFactory)
|
||||||
|
@ -134,8 +184,8 @@ var (
|
||||||
// NewModel creates and starts a new model. The model starts in read-only mode,
|
// NewModel creates and starts a new model. The model starts in read-only mode,
|
||||||
// where it sends index information to connected peers and responds to requests
|
// where it sends index information to connected peers and responds to requests
|
||||||
// for file data without altering the local folder in any way.
|
// for file data without altering the local folder in any way.
|
||||||
func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *Model {
|
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) Model {
|
||||||
m := &Model{
|
m := &model{
|
||||||
Supervisor: suture.New("model", suture.Spec{
|
Supervisor: suture.New("model", suture.Spec{
|
||||||
Log: func(line string) {
|
Log: func(line string) {
|
||||||
l.Debugln(line)
|
l.Debugln(line)
|
||||||
|
@ -180,7 +230,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersi
|
||||||
// StartDeadlockDetector starts a deadlock detector on the models locks which
|
// StartDeadlockDetector starts a deadlock detector on the models locks which
|
||||||
// causes panics in case the locks cannot be acquired in the given timeout
|
// causes panics in case the locks cannot be acquired in the given timeout
|
||||||
// period.
|
// period.
|
||||||
func (m *Model) StartDeadlockDetector(timeout time.Duration) {
|
func (m *model) StartDeadlockDetector(timeout time.Duration) {
|
||||||
l.Infof("Starting deadlock detector with %v timeout", timeout)
|
l.Infof("Starting deadlock detector with %v timeout", timeout)
|
||||||
detector := newDeadlockDetector(timeout)
|
detector := newDeadlockDetector(timeout)
|
||||||
detector.Watch("fmut", m.fmut)
|
detector.Watch("fmut", m.fmut)
|
||||||
|
@ -188,7 +238,7 @@ func (m *Model) StartDeadlockDetector(timeout time.Duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartFolder constructs the folder service and starts it.
|
// StartFolder constructs the folder service and starts it.
|
||||||
func (m *Model) StartFolder(folder string) {
|
func (m *model) StartFolder(folder string) {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
folderType := m.startFolderLocked(folder)
|
folderType := m.startFolderLocked(folder)
|
||||||
|
@ -199,7 +249,7 @@ func (m *Model) StartFolder(folder string) {
|
||||||
l.Infof("Ready to synchronize %s (%s)", folderCfg.Description(), folderType)
|
l.Infof("Ready to synchronize %s (%s)", folderCfg.Description(), folderType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) startFolderLocked(folder string) config.FolderType {
|
func (m *model) startFolderLocked(folder string) config.FolderType {
|
||||||
if err := m.checkFolderRunningLocked(folder); err == errFolderMissing {
|
if err := m.checkFolderRunningLocked(folder); err == errFolderMissing {
|
||||||
panic("cannot start nonexistent folder " + folder)
|
panic("cannot start nonexistent folder " + folder)
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
|
@ -274,7 +324,7 @@ func (m *Model) startFolderLocked(folder string) config.FolderType {
|
||||||
return cfg.Type
|
return cfg.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) warnAboutOverwritingProtectedFiles(folder string) {
|
func (m *model) warnAboutOverwritingProtectedFiles(folder string) {
|
||||||
if m.folderCfgs[folder].Type == config.FolderTypeSendOnly {
|
if m.folderCfgs[folder].Type == config.FolderTypeSendOnly {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -308,7 +358,7 @@ func (m *Model) warnAboutOverwritingProtectedFiles(folder string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) AddFolder(cfg config.FolderConfiguration) {
|
func (m *model) AddFolder(cfg config.FolderConfiguration) {
|
||||||
if len(cfg.ID) == 0 {
|
if len(cfg.ID) == 0 {
|
||||||
panic("cannot add empty folder id")
|
panic("cannot add empty folder id")
|
||||||
}
|
}
|
||||||
|
@ -322,7 +372,7 @@ func (m *Model) AddFolder(cfg config.FolderConfiguration) {
|
||||||
m.fmut.Unlock()
|
m.fmut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) addFolderLocked(cfg config.FolderConfiguration) {
|
func (m *model) addFolderLocked(cfg config.FolderConfiguration) {
|
||||||
m.folderCfgs[cfg.ID] = cfg
|
m.folderCfgs[cfg.ID] = cfg
|
||||||
folderFs := cfg.Filesystem()
|
folderFs := cfg.Filesystem()
|
||||||
m.folderFiles[cfg.ID] = db.NewFileSet(cfg.ID, folderFs, m.db)
|
m.folderFiles[cfg.ID] = db.NewFileSet(cfg.ID, folderFs, m.db)
|
||||||
|
@ -334,7 +384,7 @@ func (m *Model) addFolderLocked(cfg config.FolderConfiguration) {
|
||||||
m.folderIgnores[cfg.ID] = ignores
|
m.folderIgnores[cfg.ID] = ignores
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) RemoveFolder(cfg config.FolderConfiguration) {
|
func (m *model) RemoveFolder(cfg config.FolderConfiguration) {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
// Delete syncthing specific files
|
// Delete syncthing specific files
|
||||||
|
@ -348,7 +398,7 @@ func (m *Model) RemoveFolder(cfg config.FolderConfiguration) {
|
||||||
m.fmut.Unlock()
|
m.fmut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) {
|
func (m *model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) {
|
||||||
// Close connections to affected devices
|
// Close connections to affected devices
|
||||||
// Must happen before stopping the folder service to abort ongoing
|
// Must happen before stopping the folder service to abort ongoing
|
||||||
// transmissions and thus allow timely service termination.
|
// transmissions and thus allow timely service termination.
|
||||||
|
@ -376,7 +426,7 @@ func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration, err error)
|
||||||
delete(m.folderStatRefs, cfg.ID)
|
delete(m.folderStatRefs, cfg.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) RestartFolder(from, to config.FolderConfiguration) {
|
func (m *model) RestartFolder(from, to config.FolderConfiguration) {
|
||||||
if len(to.ID) == 0 {
|
if len(to.ID) == 0 {
|
||||||
panic("bug: cannot restart empty folder ID")
|
panic("bug: cannot restart empty folder ID")
|
||||||
}
|
}
|
||||||
|
@ -421,7 +471,7 @@ func (m *Model) RestartFolder(from, to config.FolderConfiguration) {
|
||||||
l.Infof("%v folder %v (%v)", infoMsg, to.Description(), to.Type)
|
l.Infof("%v folder %v (%v)", infoMsg, to.Description(), to.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) UsageReportingStats(version int, preview bool) map[string]interface{} {
|
func (m *model) UsageReportingStats(version int, preview bool) map[string]interface{} {
|
||||||
stats := make(map[string]interface{})
|
stats := make(map[string]interface{})
|
||||||
if version >= 3 {
|
if version >= 3 {
|
||||||
// Block stats
|
// Block stats
|
||||||
|
@ -540,7 +590,7 @@ func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectionStats returns a map with connection statistics for each device.
|
// ConnectionStats returns a map with connection statistics for each device.
|
||||||
func (m *Model) ConnectionStats() map[string]interface{} {
|
func (m *model) ConnectionStats() map[string]interface{} {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
|
|
||||||
|
@ -587,7 +637,7 @@ func (m *Model) ConnectionStats() map[string]interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceStatistics returns statistics about each device
|
// DeviceStatistics returns statistics about each device
|
||||||
func (m *Model) DeviceStatistics() map[string]stats.DeviceStatistics {
|
func (m *model) DeviceStatistics() map[string]stats.DeviceStatistics {
|
||||||
res := make(map[string]stats.DeviceStatistics)
|
res := make(map[string]stats.DeviceStatistics)
|
||||||
for id := range m.cfg.Devices() {
|
for id := range m.cfg.Devices() {
|
||||||
res[id.String()] = m.deviceStatRef(id).GetStatistics()
|
res[id.String()] = m.deviceStatRef(id).GetStatistics()
|
||||||
|
@ -596,7 +646,7 @@ func (m *Model) DeviceStatistics() map[string]stats.DeviceStatistics {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FolderStatistics returns statistics about each folder
|
// FolderStatistics returns statistics about each folder
|
||||||
func (m *Model) FolderStatistics() map[string]stats.FolderStatistics {
|
func (m *model) FolderStatistics() map[string]stats.FolderStatistics {
|
||||||
res := make(map[string]stats.FolderStatistics)
|
res := make(map[string]stats.FolderStatistics)
|
||||||
for id := range m.cfg.Folders() {
|
for id := range m.cfg.Folders() {
|
||||||
res[id] = m.folderStatRef(id).GetStatistics()
|
res[id] = m.folderStatRef(id).GetStatistics()
|
||||||
|
@ -614,7 +664,7 @@ type FolderCompletion struct {
|
||||||
|
|
||||||
// Completion returns the completion status, in percent, for the given device
|
// Completion returns the completion status, in percent, for the given device
|
||||||
// and folder.
|
// and folder.
|
||||||
func (m *Model) Completion(device protocol.DeviceID, folder string) FolderCompletion {
|
func (m *model) Completion(device protocol.DeviceID, folder string) FolderCompletion {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
rf, ok := m.folderFiles[folder]
|
rf, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -696,7 +746,7 @@ func addSizeOfFile(s *db.Counts, f db.FileIntf) {
|
||||||
|
|
||||||
// GlobalSize returns the number of files, deleted files and total bytes for all
|
// GlobalSize returns the number of files, deleted files and total bytes for all
|
||||||
// files in the global model.
|
// files in the global model.
|
||||||
func (m *Model) GlobalSize(folder string) db.Counts {
|
func (m *model) GlobalSize(folder string) db.Counts {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
if rf, ok := m.folderFiles[folder]; ok {
|
if rf, ok := m.folderFiles[folder]; ok {
|
||||||
|
@ -707,7 +757,7 @@ func (m *Model) GlobalSize(folder string) db.Counts {
|
||||||
|
|
||||||
// LocalSize returns the number of files, deleted files and total bytes for all
|
// LocalSize returns the number of files, deleted files and total bytes for all
|
||||||
// files in the local folder.
|
// files in the local folder.
|
||||||
func (m *Model) LocalSize(folder string) db.Counts {
|
func (m *model) LocalSize(folder string) db.Counts {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
if rf, ok := m.folderFiles[folder]; ok {
|
if rf, ok := m.folderFiles[folder]; ok {
|
||||||
|
@ -719,7 +769,7 @@ func (m *Model) LocalSize(folder string) db.Counts {
|
||||||
// ReceiveOnlyChangedSize returns the number of files, deleted files and
|
// ReceiveOnlyChangedSize returns the number of files, deleted files and
|
||||||
// total bytes for all files that have changed locally in a receieve only
|
// total bytes for all files that have changed locally in a receieve only
|
||||||
// folder.
|
// folder.
|
||||||
func (m *Model) ReceiveOnlyChangedSize(folder string) db.Counts {
|
func (m *model) ReceiveOnlyChangedSize(folder string) db.Counts {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
if rf, ok := m.folderFiles[folder]; ok {
|
if rf, ok := m.folderFiles[folder]; ok {
|
||||||
|
@ -729,7 +779,7 @@ func (m *Model) ReceiveOnlyChangedSize(folder string) db.Counts {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NeedSize returns the number and total size of currently needed files.
|
// NeedSize returns the number and total size of currently needed files.
|
||||||
func (m *Model) NeedSize(folder string) db.Counts {
|
func (m *model) NeedSize(folder string) db.Counts {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
|
|
||||||
|
@ -753,7 +803,7 @@ func (m *Model) NeedSize(folder string) db.Counts {
|
||||||
// NeedFolderFiles returns paginated list of currently needed files in
|
// NeedFolderFiles returns paginated list of currently needed files in
|
||||||
// progress, queued, and to be queued on next puller iteration, as well as the
|
// progress, queued, and to be queued on next puller iteration, as well as the
|
||||||
// total number of files currently needed.
|
// total number of files currently needed.
|
||||||
func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) {
|
func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
|
|
||||||
|
@ -820,7 +870,7 @@ func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfo
|
||||||
// LocalChangedFiles returns a paginated list of currently needed files in
|
// LocalChangedFiles returns a paginated list of currently needed files in
|
||||||
// progress, queued, and to be queued on next puller iteration, as well as the
|
// progress, queued, and to be queued on next puller iteration, as well as the
|
||||||
// total number of files currently needed.
|
// total number of files currently needed.
|
||||||
func (m *Model) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
|
func (m *model) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
|
|
||||||
|
@ -861,7 +911,7 @@ func (m *Model) LocalChangedFiles(folder string, page, perpage int) []db.FileInf
|
||||||
// RemoteNeedFolderFiles returns paginated list of currently needed files in
|
// RemoteNeedFolderFiles returns paginated list of currently needed files in
|
||||||
// progress, queued, and to be queued on next puller iteration, as well as the
|
// progress, queued, and to be queued on next puller iteration, as well as the
|
||||||
// total number of files currently needed.
|
// total number of files currently needed.
|
||||||
func (m *Model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) {
|
func (m *model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
if err := m.checkDeviceFolderConnectedLocked(device, folder); err != nil {
|
if err := m.checkDeviceFolderConnectedLocked(device, folder); err != nil {
|
||||||
|
@ -891,17 +941,17 @@ func (m *Model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, p
|
||||||
|
|
||||||
// Index is called when a new device is connected and we receive their full index.
|
// Index is called when a new device is connected and we receive their full index.
|
||||||
// Implements the protocol.Model interface.
|
// Implements the protocol.Model interface.
|
||||||
func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
|
func (m *model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
|
||||||
m.handleIndex(deviceID, folder, fs, false)
|
m.handleIndex(deviceID, folder, fs, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexUpdate is called for incremental updates to connected devices' indexes.
|
// IndexUpdate is called for incremental updates to connected devices' indexes.
|
||||||
// Implements the protocol.Model interface.
|
// Implements the protocol.Model interface.
|
||||||
func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
|
func (m *model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
|
||||||
m.handleIndex(deviceID, folder, fs, true)
|
m.handleIndex(deviceID, folder, fs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) {
|
func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) {
|
||||||
op := "Index"
|
op := "Index"
|
||||||
if update {
|
if update {
|
||||||
op += " update"
|
op += " update"
|
||||||
|
@ -956,7 +1006,7 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) {
|
func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) {
|
||||||
// Check the peer device's announced folders against our own. Emits events
|
// Check the peer device's announced folders against our own. Emits events
|
||||||
// for folders that we don't expect (unknown or not shared).
|
// for folders that we don't expect (unknown or not shared).
|
||||||
// Also, collect a list of folders we do share, and if he's interested in
|
// Also, collect a list of folders we do share, and if he's interested in
|
||||||
|
@ -1136,7 +1186,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleIntroductions handles adding devices/shares that are shared by an introducer device
|
// handleIntroductions handles adding devices/shares that are shared by an introducer device
|
||||||
func (m *Model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (folderDeviceSet, bool) {
|
func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (folderDeviceSet, bool) {
|
||||||
// This device is an introducer. Go through the announced lists of folders
|
// This device is an introducer. Go through the announced lists of folders
|
||||||
// and devices and add what we are missing, remove what we have extra that
|
// and devices and add what we are missing, remove what we have extra that
|
||||||
// has been introducer by the introducer.
|
// has been introducer by the introducer.
|
||||||
|
@ -1192,7 +1242,7 @@ func (m *Model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDeintroductions handles removals of devices/shares that are removed by an introducer device
|
// handleDeintroductions handles removals of devices/shares that are removed by an introducer device
|
||||||
func (m *Model) handleDeintroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, foldersDevices folderDeviceSet) bool {
|
func (m *model) handleDeintroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, foldersDevices folderDeviceSet) bool {
|
||||||
changed := false
|
changed := false
|
||||||
devicesNotIntroduced := make(map[protocol.DeviceID]struct{})
|
devicesNotIntroduced := make(map[protocol.DeviceID]struct{})
|
||||||
|
|
||||||
|
@ -1249,7 +1299,7 @@ func (m *Model) handleDeintroductions(introducerCfg config.DeviceConfiguration,
|
||||||
|
|
||||||
// handleAutoAccepts handles adding and sharing folders for devices that have
|
// handleAutoAccepts handles adding and sharing folders for devices that have
|
||||||
// AutoAcceptFolders set to true.
|
// AutoAcceptFolders set to true.
|
||||||
func (m *Model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder protocol.Folder) bool {
|
func (m *model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder protocol.Folder) bool {
|
||||||
if cfg, ok := m.cfg.Folder(folder.ID); !ok {
|
if cfg, ok := m.cfg.Folder(folder.ID); !ok {
|
||||||
defaultPath := m.cfg.Options().DefaultFolderPath
|
defaultPath := m.cfg.Options().DefaultFolderPath
|
||||||
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
|
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
|
||||||
|
@ -1295,7 +1345,7 @@ func (m *Model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) {
|
func (m *model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) {
|
||||||
addresses := []string{"dynamic"}
|
addresses := []string{"dynamic"}
|
||||||
for _, addr := range device.Addresses {
|
for _, addr := range device.Addresses {
|
||||||
if addr != "dynamic" {
|
if addr != "dynamic" {
|
||||||
|
@ -1324,7 +1374,7 @@ func (m *Model) introduceDevice(device protocol.Device, introducerCfg config.Dev
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closed is called when a connection has been closed
|
// Closed is called when a connection has been closed
|
||||||
func (m *Model) Closed(conn protocol.Connection, err error) {
|
func (m *model) Closed(conn protocol.Connection, err error) {
|
||||||
device := conn.ID()
|
device := conn.ID()
|
||||||
|
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
|
@ -1350,14 +1400,14 @@ func (m *Model) Closed(conn protocol.Connection, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// close will close the underlying connection for a given device
|
// close will close the underlying connection for a given device
|
||||||
func (m *Model) close(device protocol.DeviceID, err error) {
|
func (m *model) close(device protocol.DeviceID, err error) {
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
m.closeLocked(device, err)
|
m.closeLocked(device, err)
|
||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeLocked will close the underlying connection for a given device
|
// closeLocked will close the underlying connection for a given device
|
||||||
func (m *Model) closeLocked(device protocol.DeviceID, err error) {
|
func (m *model) closeLocked(device protocol.DeviceID, err error) {
|
||||||
conn, ok := m.conn[device]
|
conn, ok := m.conn[device]
|
||||||
if !ok {
|
if !ok {
|
||||||
// There is no connection to close
|
// There is no connection to close
|
||||||
|
@ -1398,7 +1448,7 @@ func (r *requestResponse) Wait() {
|
||||||
|
|
||||||
// Request returns the specified data segment by reading it from local disk.
|
// Request returns the specified data segment by reading it from local disk.
|
||||||
// Implements the protocol.Model interface.
|
// Implements the protocol.Model interface.
|
||||||
func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) {
|
func (m *model) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) {
|
||||||
if size < 0 || offset < 0 {
|
if size < 0 || offset < 0 {
|
||||||
return nil, protocol.ErrInvalid
|
return nil, protocol.ErrInvalid
|
||||||
}
|
}
|
||||||
|
@ -1519,7 +1569,7 @@ func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, size in
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, folder, name string, blockIndex int, hash []byte) {
|
func (m *model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, folder, name string, blockIndex int, hash []byte) {
|
||||||
cf, ok := m.CurrentFolderFile(folder, name)
|
cf, ok := m.CurrentFolderFile(folder, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
l.Debugf("%v recheckFile: %s: %q / %q: no current file", m, deviceID, folder, name)
|
l.Debugf("%v recheckFile: %s: %q / %q: no current file", m, deviceID, folder, name)
|
||||||
|
@ -1560,7 +1610,7 @@ func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) {
|
func (m *model) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
fs, ok := m.folderFiles[folder]
|
fs, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -1570,7 +1620,7 @@ func (m *Model) CurrentFolderFile(folder string, file string) (protocol.FileInfo
|
||||||
return fs.Get(protocol.LocalDeviceID, file)
|
return fs.Get(protocol.LocalDeviceID, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) {
|
func (m *model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
fs, ok := m.folderFiles[folder]
|
fs, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -1581,7 +1631,7 @@ func (m *Model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
type cFiler struct {
|
type cFiler struct {
|
||||||
m *Model
|
m Model
|
||||||
r string
|
r string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1591,7 +1641,7 @@ func (cf cFiler) CurrentFile(file string) (protocol.FileInfo, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection returns the current connection for device, and a boolean whether a connection was found.
|
// Connection returns the current connection for device, and a boolean whether a connection was found.
|
||||||
func (m *Model) Connection(deviceID protocol.DeviceID) (connections.Connection, bool) {
|
func (m *model) Connection(deviceID protocol.DeviceID) (connections.Connection, bool) {
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
cn, ok := m.conn[deviceID]
|
cn, ok := m.conn[deviceID]
|
||||||
m.pmut.RUnlock()
|
m.pmut.RUnlock()
|
||||||
|
@ -1601,7 +1651,7 @@ func (m *Model) Connection(deviceID protocol.DeviceID) (connections.Connection,
|
||||||
return cn, ok
|
return cn, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) GetIgnores(folder string) ([]string, []string, error) {
|
func (m *model) GetIgnores(folder string) ([]string, []string, error) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
|
|
||||||
|
@ -1630,7 +1680,7 @@ func (m *Model) GetIgnores(folder string) ([]string, []string, error) {
|
||||||
return ignores.Lines(), ignores.Patterns(), nil
|
return ignores.Lines(), ignores.Patterns(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetIgnores(folder string, content []string) error {
|
func (m *model) SetIgnores(folder string, content []string) error {
|
||||||
cfg, ok := m.cfg.Folders()[folder]
|
cfg, ok := m.cfg.Folders()[folder]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("folder %s does not exist", cfg.Description())
|
return fmt.Errorf("folder %s does not exist", cfg.Description())
|
||||||
|
@ -1664,7 +1714,7 @@ func (m *Model) SetIgnores(folder string, content []string) error {
|
||||||
// OnHello is called when an device connects to us.
|
// OnHello is called when an device connects to us.
|
||||||
// This allows us to extract some information from the Hello message
|
// This allows us to extract some information from the Hello message
|
||||||
// and add it to a list of known devices ahead of any checks.
|
// and add it to a list of known devices ahead of any checks.
|
||||||
func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error {
|
func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error {
|
||||||
if m.cfg.IgnoredDevice(remoteID) {
|
if m.cfg.IgnoredDevice(remoteID) {
|
||||||
return errDeviceIgnored
|
return errDeviceIgnored
|
||||||
}
|
}
|
||||||
|
@ -1694,7 +1744,7 @@ func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHello is called when we are about to connect to some remote device.
|
// GetHello is called when we are about to connect to some remote device.
|
||||||
func (m *Model) GetHello(id protocol.DeviceID) protocol.HelloIntf {
|
func (m *model) GetHello(id protocol.DeviceID) protocol.HelloIntf {
|
||||||
name := ""
|
name := ""
|
||||||
if _, ok := m.cfg.Device(id); ok {
|
if _, ok := m.cfg.Device(id); ok {
|
||||||
name = m.cfg.MyName()
|
name = m.cfg.MyName()
|
||||||
|
@ -1709,7 +1759,7 @@ func (m *Model) GetHello(id protocol.DeviceID) protocol.HelloIntf {
|
||||||
// AddConnection adds a new peer connection to the model. An initial index will
|
// AddConnection adds a new peer connection to the model. An initial index will
|
||||||
// be sent to the connected peer, thereafter index updates whenever the local
|
// be sent to the connected peer, thereafter index updates whenever the local
|
||||||
// folder changes.
|
// folder changes.
|
||||||
func (m *Model) AddConnection(conn connections.Connection, hello protocol.HelloResult) {
|
func (m *model) AddConnection(conn connections.Connection, hello protocol.HelloResult) {
|
||||||
deviceID := conn.ID()
|
deviceID := conn.ID()
|
||||||
device, ok := m.cfg.Device(deviceID)
|
device, ok := m.cfg.Device(deviceID)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1778,7 +1828,7 @@ func (m *Model) AddConnection(conn connections.Connection, hello protocol.HelloR
|
||||||
m.deviceWasSeen(deviceID)
|
m.deviceWasSeen(deviceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
|
func (m *model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
cfg, ok := m.folderCfgs[folder]
|
cfg, ok := m.folderCfgs[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -1799,7 +1849,7 @@ func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, update
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference {
|
func (m *model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
defer m.fmut.Unlock()
|
defer m.fmut.Unlock()
|
||||||
|
|
||||||
|
@ -1812,11 +1862,11 @@ func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatistic
|
||||||
return sr
|
return sr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) deviceWasSeen(deviceID protocol.DeviceID) {
|
func (m *model) deviceWasSeen(deviceID protocol.DeviceID) {
|
||||||
m.deviceStatRef(deviceID).WasSeen()
|
m.deviceStatRef(deviceID).WasSeen()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) folderStatRef(folder string) *stats.FolderStatisticsReference {
|
func (m *model) folderStatRef(folder string) *stats.FolderStatisticsReference {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
defer m.fmut.Unlock()
|
defer m.fmut.Unlock()
|
||||||
|
|
||||||
|
@ -1828,7 +1878,7 @@ func (m *Model) folderStatRef(folder string) *stats.FolderStatisticsReference {
|
||||||
return sr
|
return sr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) receivedFile(folder string, file protocol.FileInfo) {
|
func (m *model) receivedFile(folder string, file protocol.FileInfo) {
|
||||||
m.folderStatRef(folder).ReceivedFile(file.Name, file.IsDeleted())
|
m.folderStatRef(folder).ReceivedFile(file.Name, file.IsDeleted())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1949,7 +1999,7 @@ func sendIndexTo(prevSequence int64, conn protocol.Connection, folder string, fs
|
||||||
return f.Sequence, err
|
return f.Sequence, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) {
|
func (m *model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) {
|
||||||
m.updateLocals(folder, fs)
|
m.updateLocals(folder, fs)
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
|
@ -1959,7 +2009,7 @@ func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo)
|
||||||
m.diskChangeDetected(folderCfg, fs, events.LocalChangeDetected)
|
m.diskChangeDetected(folderCfg, fs, events.LocalChangeDetected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) {
|
func (m *model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) {
|
||||||
m.updateLocals(folder, fs)
|
m.updateLocals(folder, fs)
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
|
@ -1969,7 +2019,7 @@ func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) {
|
||||||
m.diskChangeDetected(folderCfg, fs, events.RemoteChangeDetected)
|
m.diskChangeDetected(folderCfg, fs, events.RemoteChangeDetected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) {
|
func (m *model) updateLocals(folder string, fs []protocol.FileInfo) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
files := m.folderFiles[folder]
|
files := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -1992,7 +2042,7 @@ func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) diskChangeDetected(folderCfg config.FolderConfiguration, files []protocol.FileInfo, typeOfEvent events.EventType) {
|
func (m *model) diskChangeDetected(folderCfg config.FolderConfiguration, files []protocol.FileInfo, typeOfEvent events.EventType) {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.IsInvalid() {
|
if file.IsInvalid() {
|
||||||
continue
|
continue
|
||||||
|
@ -2035,7 +2085,7 @@ func (m *Model) diskChangeDetected(folderCfg config.FolderConfiguration, files [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) requestGlobal(deviceID protocol.DeviceID, folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
|
func (m *model) requestGlobal(deviceID protocol.DeviceID, folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
nc, ok := m.conn[deviceID]
|
nc, ok := m.conn[deviceID]
|
||||||
m.pmut.RUnlock()
|
m.pmut.RUnlock()
|
||||||
|
@ -2049,7 +2099,7 @@ func (m *Model) requestGlobal(deviceID protocol.DeviceID, folder, name string, o
|
||||||
return nc.Request(folder, name, offset, size, hash, weakHash, fromTemporary)
|
return nc.Request(folder, name, offset, size, hash, weakHash, fromTemporary)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ScanFolders() map[string]error {
|
func (m *model) ScanFolders() map[string]error {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
folders := make([]string, 0, len(m.folderCfgs))
|
folders := make([]string, 0, len(m.folderCfgs))
|
||||||
for folder := range m.folderCfgs {
|
for folder := range m.folderCfgs {
|
||||||
|
@ -2087,11 +2137,11 @@ func (m *Model) ScanFolders() map[string]error {
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ScanFolder(folder string) error {
|
func (m *model) ScanFolder(folder string) error {
|
||||||
return m.ScanFolderSubdirs(folder, nil)
|
return m.ScanFolderSubdirs(folder, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ScanFolderSubdirs(folder string, subs []string) error {
|
func (m *model) ScanFolderSubdirs(folder string, subs []string) error {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
if err := m.checkFolderRunningLocked(folder); err != nil {
|
if err := m.checkFolderRunningLocked(folder); err != nil {
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -2103,7 +2153,7 @@ func (m *Model) ScanFolderSubdirs(folder string, subs []string) error {
|
||||||
return runner.Scan(subs)
|
return runner.Scan(subs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) DelayScan(folder string, next time.Duration) {
|
func (m *model) DelayScan(folder string, next time.Duration) {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
runner, ok := m.folderRunners[folder]
|
runner, ok := m.folderRunners[folder]
|
||||||
m.fmut.Unlock()
|
m.fmut.Unlock()
|
||||||
|
@ -2115,7 +2165,7 @@ func (m *Model) DelayScan(folder string, next time.Duration) {
|
||||||
|
|
||||||
// numHashers returns the number of hasher routines to use for a given folder,
|
// numHashers returns the number of hasher routines to use for a given folder,
|
||||||
// taking into account configuration and available CPU cores.
|
// taking into account configuration and available CPU cores.
|
||||||
func (m *Model) numHashers(folder string) int {
|
func (m *model) numHashers(folder string) int {
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
folderCfg := m.folderCfgs[folder]
|
folderCfg := m.folderCfgs[folder]
|
||||||
numFolders := len(m.folderCfgs)
|
numFolders := len(m.folderCfgs)
|
||||||
|
@ -2144,7 +2194,7 @@ func (m *Model) numHashers(folder string) int {
|
||||||
|
|
||||||
// generateClusterConfig returns a ClusterConfigMessage that is correct for
|
// generateClusterConfig returns a ClusterConfigMessage that is correct for
|
||||||
// the given peer device
|
// the given peer device
|
||||||
func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig {
|
func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig {
|
||||||
var message protocol.ClusterConfig
|
var message protocol.ClusterConfig
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
|
@ -2201,7 +2251,7 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) State(folder string) (string, time.Time, error) {
|
func (m *model) State(folder string) (string, time.Time, error) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
runner, ok := m.folderRunners[folder]
|
runner, ok := m.folderRunners[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -2215,7 +2265,7 @@ func (m *Model) State(folder string) (string, time.Time, error) {
|
||||||
return state.String(), changed, err
|
return state.String(), changed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) FolderErrors(folder string) ([]FileError, error) {
|
func (m *model) FolderErrors(folder string) ([]FileError, error) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
if err := m.checkFolderRunningLocked(folder); err != nil {
|
if err := m.checkFolderRunningLocked(folder); err != nil {
|
||||||
|
@ -2224,7 +2274,7 @@ func (m *Model) FolderErrors(folder string) ([]FileError, error) {
|
||||||
return m.folderRunners[folder].Errors(), nil
|
return m.folderRunners[folder].Errors(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) WatchError(folder string) error {
|
func (m *model) WatchError(folder string) error {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
if err := m.checkFolderRunningLocked(folder); err != nil {
|
if err := m.checkFolderRunningLocked(folder); err != nil {
|
||||||
|
@ -2233,7 +2283,7 @@ func (m *Model) WatchError(folder string) error {
|
||||||
return m.folderRunners[folder].WatchError()
|
return m.folderRunners[folder].WatchError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) Override(folder string) {
|
func (m *model) Override(folder string) {
|
||||||
// Grab the runner and the file set.
|
// Grab the runner and the file set.
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
|
@ -2251,7 +2301,7 @@ func (m *Model) Override(folder string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) Revert(folder string) {
|
func (m *model) Revert(folder string) {
|
||||||
// Grab the runner and the file set.
|
// Grab the runner and the file set.
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
|
@ -2272,7 +2322,7 @@ func (m *Model) Revert(folder string) {
|
||||||
// CurrentSequence returns the change version for the given folder.
|
// CurrentSequence returns the change version for the given folder.
|
||||||
// This is guaranteed to increment if the contents of the local folder has
|
// This is guaranteed to increment if the contents of the local folder has
|
||||||
// changed.
|
// changed.
|
||||||
func (m *Model) CurrentSequence(folder string) (int64, bool) {
|
func (m *model) CurrentSequence(folder string) (int64, bool) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
fs, ok := m.folderFiles[folder]
|
fs, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -2288,7 +2338,7 @@ func (m *Model) CurrentSequence(folder string) (int64, bool) {
|
||||||
// RemoteSequence returns the change version for the given folder, as
|
// RemoteSequence returns the change version for the given folder, as
|
||||||
// sent by remote peers. This is guaranteed to increment if the contents of
|
// sent by remote peers. This is guaranteed to increment if the contents of
|
||||||
// the remote or global folder has changed.
|
// the remote or global folder has changed.
|
||||||
func (m *Model) RemoteSequence(folder string) (int64, bool) {
|
func (m *model) RemoteSequence(folder string) (int64, bool) {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
defer m.fmut.RUnlock()
|
defer m.fmut.RUnlock()
|
||||||
|
|
||||||
|
@ -2308,7 +2358,7 @@ func (m *Model) RemoteSequence(folder string) (int64, bool) {
|
||||||
return ver, true
|
return ver, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} {
|
func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
files, ok := m.folderFiles[folder]
|
files, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
|
@ -2372,7 +2422,7 @@ func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) {
|
func (m *model) GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) {
|
||||||
fcfg, ok := m.cfg.Folder(folder)
|
fcfg, ok := m.cfg.Folder(folder)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errFolderMissing
|
return nil, errFolderMissing
|
||||||
|
@ -2432,7 +2482,7 @@ func (m *Model) GetFolderVersions(folder string) (map[string][]versioner.FileVer
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) {
|
func (m *model) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) {
|
||||||
fcfg, ok := m.cfg.Folder(folder)
|
fcfg, ok := m.cfg.Folder(folder)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errFolderMissing
|
return nil, errFolderMissing
|
||||||
|
@ -2503,7 +2553,7 @@ func (m *Model) RestoreFolderVersions(folder string, versions map[string]time.Ti
|
||||||
return errors, nil
|
return errors, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
func (m *model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
||||||
// The slightly unusual locking sequence here is because we need to hold
|
// The slightly unusual locking sequence here is because we need to hold
|
||||||
// pmut for the duration (as the value returned from foldersFiles can
|
// pmut for the duration (as the value returned from foldersFiles can
|
||||||
// get heavily modified on Close()), but also must acquire fmut before
|
// get heavily modified on Close()), but also must acquire fmut before
|
||||||
|
@ -2544,7 +2594,7 @@ next:
|
||||||
}
|
}
|
||||||
|
|
||||||
// BringToFront bumps the given files priority in the job queue.
|
// BringToFront bumps the given files priority in the job queue.
|
||||||
func (m *Model) BringToFront(folder, file string) {
|
func (m *model) BringToFront(folder, file string) {
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
defer m.pmut.RUnlock()
|
defer m.pmut.RUnlock()
|
||||||
|
|
||||||
|
@ -2554,20 +2604,20 @@ func (m *Model) BringToFront(folder, file string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ResetFolder(folder string) {
|
func (m *model) ResetFolder(folder string) {
|
||||||
l.Infof("Cleaning data for folder %q", folder)
|
l.Infof("Cleaning data for folder %q", folder)
|
||||||
db.DropFolder(m.db, folder)
|
db.DropFolder(m.db, folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) String() string {
|
func (m *model) String() string {
|
||||||
return fmt.Sprintf("model@%p", m)
|
return fmt.Sprintf("model@%p", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) VerifyConfiguration(from, to config.Configuration) error {
|
func (m *model) VerifyConfiguration(from, to config.Configuration) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
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.
|
// TODO: This should not use reflect, and should take more care to try to handle stuff without restart.
|
||||||
|
|
||||||
// Go through the folder configs and figure out if we need to restart or not.
|
// Go through the folder configs and figure out if we need to restart or not.
|
||||||
|
@ -2661,7 +2711,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||||
// checkFolderRunningLocked returns nil if the folder is up and running and a
|
// checkFolderRunningLocked returns nil if the folder is up and running and a
|
||||||
// descriptive error if not.
|
// descriptive error if not.
|
||||||
// Need to hold (read) lock on m.fmut when calling this.
|
// Need to hold (read) lock on m.fmut when calling this.
|
||||||
func (m *Model) checkFolderRunningLocked(folder string) error {
|
func (m *model) checkFolderRunningLocked(folder string) error {
|
||||||
_, ok := m.folderRunners[folder]
|
_, ok := m.folderRunners[folder]
|
||||||
if ok {
|
if ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -2679,7 +2729,7 @@ func (m *Model) checkFolderRunningLocked(folder string) error {
|
||||||
// checkFolderDeviceStatusLocked first checks the folder and then whether the
|
// checkFolderDeviceStatusLocked first checks the folder and then whether the
|
||||||
// given device is connected and shares this folder.
|
// given device is connected and shares this folder.
|
||||||
// Need to hold (read) lock on both m.fmut and m.pmut when calling this.
|
// Need to hold (read) lock on both m.fmut and m.pmut when calling this.
|
||||||
func (m *Model) checkDeviceFolderConnectedLocked(device protocol.DeviceID, folder string) error {
|
func (m *model) checkDeviceFolderConnectedLocked(device protocol.DeviceID, folder string) error {
|
||||||
if err := m.checkFolderRunningLocked(folder); err != nil {
|
if err := m.checkFolderRunningLocked(folder); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var myID, device1, device2 protocol.DeviceID
|
var myID, device1, device2 protocol.DeviceID
|
||||||
var defaultCfgWrapper *config.Wrapper
|
var defaultCfgWrapper config.Wrapper
|
||||||
var defaultFolderConfig config.FolderConfiguration
|
var defaultFolderConfig config.FolderConfiguration
|
||||||
var defaultFs fs.Filesystem
|
var defaultFs fs.Filesystem
|
||||||
var defaultCfg config.Configuration
|
var defaultCfg config.Configuration
|
||||||
|
@ -161,7 +161,7 @@ func prepareTmpFile(to fs.Filesystem) (string, error) {
|
||||||
return tmpName, nil
|
return tmpName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTmpWrapper(cfg config.Configuration) *config.Wrapper {
|
func createTmpWrapper(cfg config.Configuration) config.Wrapper {
|
||||||
tmpFile, err := ioutil.TempFile(tmpLocation, "syncthing-testConfig-")
|
tmpFile, err := ioutil.TempFile(tmpLocation, "syncthing-testConfig-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -171,7 +171,11 @@ func createTmpWrapper(cfg config.Configuration) *config.Wrapper {
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func newState(cfg config.Configuration) (*config.Wrapper, *Model) {
|
func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model {
|
||||||
|
return NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles).(*model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newState(cfg config.Configuration) (config.Wrapper, *model) {
|
||||||
wcfg := createTmpWrapper(cfg)
|
wcfg := createTmpWrapper(cfg)
|
||||||
|
|
||||||
m := setupModel(wcfg)
|
m := setupModel(wcfg)
|
||||||
|
@ -183,9 +187,9 @@ func newState(cfg config.Configuration) (*config.Wrapper, *Model) {
|
||||||
return wcfg, m
|
return wcfg, m
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupModel(w *config.Wrapper) *Model {
|
func setupModel(w config.Wrapper) *model {
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(w, myID, "syncthing", "dev", db, nil)
|
m := newModel(w, myID, "syncthing", "dev", db, nil)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
for id, cfg := range w.Folders() {
|
for id, cfg := range w.Folders() {
|
||||||
if !cfg.Paused {
|
if !cfg.Paused {
|
||||||
|
@ -322,7 +326,7 @@ type fakeConnection struct {
|
||||||
files []protocol.FileInfo
|
files []protocol.FileInfo
|
||||||
fileData map[string][]byte
|
fileData map[string][]byte
|
||||||
folder string
|
folder string
|
||||||
model *Model
|
model *model
|
||||||
indexFn func(string, []protocol.FileInfo)
|
indexFn func(string, []protocol.FileInfo)
|
||||||
requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error)
|
requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error)
|
||||||
mut sync.Mutex
|
mut sync.Mutex
|
||||||
|
@ -563,7 +567,7 @@ func TestDeviceRename(t *testing.T) {
|
||||||
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg)
|
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg)
|
||||||
|
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(cfg, myID, "syncthing", "dev", db, nil)
|
m := newModel(cfg, myID, "syncthing", "dev", db, nil)
|
||||||
|
|
||||||
if cfg.Devices()[device1].Name != "" {
|
if cfg.Devices()[device1].Name != "" {
|
||||||
t.Errorf("Device already has a name")
|
t.Errorf("Device already has a name")
|
||||||
|
@ -662,7 +666,7 @@ func TestClusterConfig(t *testing.T) {
|
||||||
|
|
||||||
wrapper := createTmpWrapper(cfg)
|
wrapper := createTmpWrapper(cfg)
|
||||||
defer os.Remove(wrapper.ConfigPath())
|
defer os.Remove(wrapper.ConfigPath())
|
||||||
m := NewModel(wrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.AddFolder(cfg.Folders[0])
|
m.AddFolder(cfg.Folders[0])
|
||||||
m.AddFolder(cfg.Folders[1])
|
m.AddFolder(cfg.Folders[1])
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
@ -1644,7 +1648,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeIgnores(t *testing.T, m *Model, expected []string) {
|
func changeIgnores(t *testing.T, m *model, expected []string) {
|
||||||
arrEqual := func(a, b []string) bool {
|
arrEqual := func(a, b []string) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
|
@ -1796,7 +1800,7 @@ func TestROScanRecovery(t *testing.T) {
|
||||||
|
|
||||||
testOs.RemoveAll(fcfg.Path)
|
testOs.RemoveAll(fcfg.Path)
|
||||||
|
|
||||||
m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
||||||
m.AddFolder(fcfg)
|
m.AddFolder(fcfg)
|
||||||
m.StartFolder("default")
|
m.StartFolder("default")
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
@ -1883,7 +1887,7 @@ func TestRWScanRecovery(t *testing.T) {
|
||||||
|
|
||||||
testOs.RemoveAll(fcfg.Path)
|
testOs.RemoveAll(fcfg.Path)
|
||||||
|
|
||||||
m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
||||||
m.AddFolder(fcfg)
|
m.AddFolder(fcfg)
|
||||||
m.StartFolder("default")
|
m.StartFolder("default")
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
@ -1941,7 +1945,7 @@ func TestRWScanRecovery(t *testing.T) {
|
||||||
|
|
||||||
func TestGlobalDirectoryTree(t *testing.T) {
|
func TestGlobalDirectoryTree(t *testing.T) {
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.AddFolder(defaultFolderConfig)
|
m.AddFolder(defaultFolderConfig)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
|
@ -2193,7 +2197,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
|
||||||
|
|
||||||
func TestGlobalDirectorySelfFixing(t *testing.T) {
|
func TestGlobalDirectorySelfFixing(t *testing.T) {
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.AddFolder(defaultFolderConfig)
|
m.AddFolder(defaultFolderConfig)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
|
||||||
|
@ -2368,7 +2372,7 @@ func BenchmarkTree_100_10(b *testing.B) {
|
||||||
|
|
||||||
func benchmarkTree(b *testing.B, n1, n2 int) {
|
func benchmarkTree(b *testing.B, n1, n2 int) {
|
||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.AddFolder(defaultFolderConfig)
|
m.AddFolder(defaultFolderConfig)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
|
||||||
|
@ -2438,7 +2442,7 @@ func TestIssue4357(t *testing.T) {
|
||||||
// Create a separate wrapper not to pollute other tests.
|
// Create a separate wrapper not to pollute other tests.
|
||||||
wrapper := createTmpWrapper(config.Configuration{})
|
wrapper := createTmpWrapper(config.Configuration{})
|
||||||
defer os.Remove(wrapper.ConfigPath())
|
defer os.Remove(wrapper.ConfigPath())
|
||||||
m := NewModel(wrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
|
|
||||||
|
@ -2568,7 +2572,7 @@ func TestIndexesForUnknownDevicesDropped(t *testing.T) {
|
||||||
t.Error("expected two devices")
|
t.Error("expected two devices")
|
||||||
}
|
}
|
||||||
|
|
||||||
m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", dbi, nil)
|
m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", dbi, nil)
|
||||||
m.AddFolder(defaultFolderConfig)
|
m.AddFolder(defaultFolderConfig)
|
||||||
m.StartFolder("default")
|
m.StartFolder("default")
|
||||||
|
|
||||||
|
@ -3055,7 +3059,7 @@ func TestCustomMarkerName(t *testing.T) {
|
||||||
testOs.RemoveAll(fcfg.Path)
|
testOs.RemoveAll(fcfg.Path)
|
||||||
defer testOs.RemoveAll(fcfg.Path)
|
defer testOs.RemoveAll(fcfg.Path)
|
||||||
|
|
||||||
m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
|
||||||
m.AddFolder(fcfg)
|
m.AddFolder(fcfg)
|
||||||
m.StartFolder("default")
|
m.StartFolder("default")
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
@ -3466,7 +3470,7 @@ func TestIssue4094(t *testing.T) {
|
||||||
// Create a separate wrapper not to pollute other tests.
|
// Create a separate wrapper not to pollute other tests.
|
||||||
wrapper := createTmpWrapper(config.Configuration{})
|
wrapper := createTmpWrapper(config.Configuration{})
|
||||||
defer os.Remove(wrapper.ConfigPath())
|
defer os.Remove(wrapper.ConfigPath())
|
||||||
m := NewModel(wrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
|
|
||||||
|
@ -3505,7 +3509,7 @@ func TestIssue4903(t *testing.T) {
|
||||||
// Create a separate wrapper not to pollute other tests.
|
// Create a separate wrapper not to pollute other tests.
|
||||||
wrapper := createTmpWrapper(config.Configuration{})
|
wrapper := createTmpWrapper(config.Configuration{})
|
||||||
defer os.Remove(wrapper.ConfigPath())
|
defer os.Remove(wrapper.ConfigPath())
|
||||||
m := NewModel(wrapper, myID, "syncthing", "dev", db, nil)
|
m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
|
|
||||||
|
@ -3575,7 +3579,7 @@ func TestParentOfUnignored(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFakeConn(m *Model, dev protocol.DeviceID) *fakeConnection {
|
func addFakeConn(m *model, dev protocol.DeviceID) *fakeConnection {
|
||||||
fc := &fakeConnection{id: dev, model: m}
|
fc := &fakeConnection{id: dev, model: m}
|
||||||
m.AddConnection(fc, protocol.HelloResult{})
|
m.AddConnection(fc, protocol.HelloResult{})
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ type ProgressEmitter struct {
|
||||||
|
|
||||||
// NewProgressEmitter creates a new progress emitter which emits
|
// NewProgressEmitter creates a new progress emitter which emits
|
||||||
// DownloadProgress events every interval.
|
// DownloadProgress events every interval.
|
||||||
func NewProgressEmitter(cfg *config.Wrapper) *ProgressEmitter {
|
func NewProgressEmitter(cfg config.Wrapper) *ProgressEmitter {
|
||||||
t := &ProgressEmitter{
|
t := &ProgressEmitter{
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
registry: make(map[string]*sharedPullerState),
|
registry: make(map[string]*sharedPullerState),
|
||||||
|
|
|
@ -684,7 +684,7 @@ func TestRequestSymlinkWindows(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tmpDefaultWrapper() (*config.Wrapper, string) {
|
func tmpDefaultWrapper() (config.Wrapper, string) {
|
||||||
w := createTmpWrapper(defaultCfgWrapper.RawCopy())
|
w := createTmpWrapper(defaultCfgWrapper.RawCopy())
|
||||||
fcfg, tmpDir := testFolderConfigTmp()
|
fcfg, tmpDir := testFolderConfigTmp()
|
||||||
w.SetFolder(fcfg)
|
w.SetFolder(fcfg)
|
||||||
|
@ -703,13 +703,13 @@ func testFolderConfig(path string) config.FolderConfiguration {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupModelWithConnection() (*Model, *fakeConnection, string, *config.Wrapper) {
|
func setupModelWithConnection() (*model, *fakeConnection, string, config.Wrapper) {
|
||||||
w, tmpDir := tmpDefaultWrapper()
|
w, tmpDir := tmpDefaultWrapper()
|
||||||
m, fc := setupModelWithConnectionFromWrapper(w)
|
m, fc := setupModelWithConnectionFromWrapper(w)
|
||||||
return m, fc, tmpDir, w
|
return m, fc, tmpDir, w
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupModelWithConnectionFromWrapper(w *config.Wrapper) (*Model, *fakeConnection) {
|
func setupModelWithConnectionFromWrapper(w config.Wrapper) (*model, *fakeConnection) {
|
||||||
m := setupModel(w)
|
m := setupModel(w)
|
||||||
|
|
||||||
fc := addFakeConn(m, device1)
|
fc := addFakeConn(m, device1)
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
// setup/renewal of a port mapping.
|
// setup/renewal of a port mapping.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
id protocol.DeviceID
|
id protocol.DeviceID
|
||||||
cfg *config.Wrapper
|
cfg config.Wrapper
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
|
|
||||||
mappings []*Mapping
|
mappings []*Mapping
|
||||||
|
@ -31,7 +31,7 @@ type Service struct {
|
||||||
mut sync.RWMutex
|
mut sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(id protocol.DeviceID, cfg *config.Wrapper) *Service {
|
func NewService(id protocol.DeviceID, cfg config.Wrapper) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
id: id,
|
id: id,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
|
@ -125,14 +125,14 @@ func newAggregator(folderCfg config.FolderConfiguration, ctx context.Context) *a
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg *config.Wrapper, ctx context.Context) {
|
func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, ctx context.Context) {
|
||||||
a := newAggregator(folderCfg, ctx)
|
a := newAggregator(folderCfg, ctx)
|
||||||
|
|
||||||
// Necessary for unit tests where the backend is mocked
|
// Necessary for unit tests where the backend is mocked
|
||||||
go a.mainLoop(in, out, cfg)
|
go a.mainLoop(in, out, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg *config.Wrapper) {
|
func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper) {
|
||||||
a.notifyTimer = time.NewTimer(a.notifyDelay)
|
a.notifyTimer = time.NewTimer(a.notifyDelay)
|
||||||
defer a.notifyTimer.Stop()
|
defer a.notifyTimer.Stop()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue