Refactor config commit stuff to support restartless updates better

Includes restartless updates of the GUI settings (listening port etc) as
a proof of concept.
This commit is contained in:
Jakob Borg
2015-06-03 09:47:39 +02:00
parent ef6f52f688
commit 76ad925842
13 changed files with 412 additions and 116 deletions

View File

@@ -17,6 +17,7 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
stdsync "sync"
@@ -1718,6 +1719,37 @@ func (m *Model) String() string {
return fmt.Sprintf("model@%p", m)
}
func (m *Model) VerifyConfiguration(from, to config.Configuration) error {
return nil
}
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.
// Adding, removing or changing folders requires restart
if !reflect.DeepEqual(from.Folders, to.Folders) {
return false
}
// Removing a device requres restart
toDevs := make(map[protocol.DeviceID]bool, len(from.Devices))
for _, dev := range to.Devices {
toDevs[dev.DeviceID] = true
}
for _, dev := range from.Devices {
if _, ok := toDevs[dev.DeviceID]; !ok {
return false
}
}
// All of the generic options require restart
if !reflect.DeepEqual(from.Options, to.Options) {
return false
}
return true
}
func symlinkInvalid(isLink bool) bool {
if !symlinks.Supported && isLink {
SymlinkWarning.Do(func() {

View File

@@ -317,21 +317,22 @@ func TestDeviceRename(t *testing.T) {
defer os.Remove("tmpconfig.xml")
cfg := config.New(device1)
cfg.Devices = []config.DeviceConfiguration{
rawCfg := config.New(device1)
rawCfg.Devices = []config.DeviceConfiguration{
{
DeviceID: device1,
},
}
cfg := config.Wrap("tmpconfig.xml", rawCfg)
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
m := NewModel(config.Wrap("tmpconfig.xml", cfg), protocol.LocalDeviceID, "device", "syncthing", "dev", db)
if cfg.Devices[0].Name != "" {
m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db)
if cfg.Devices()[device1].Name != "" {
t.Errorf("Device already has a name")
}
m.ClusterConfig(device1, ccm)
if cfg.Devices[0].Name != "" {
if cfg.Devices()[device1].Name != "" {
t.Errorf("Device already has a name")
}
@@ -342,13 +343,13 @@ func TestDeviceRename(t *testing.T) {
},
}
m.ClusterConfig(device1, ccm)
if cfg.Devices[0].Name != "tester" {
if cfg.Devices()[device1].Name != "tester" {
t.Errorf("Device did not get a name")
}
ccm.Options[0].Value = "tester2"
m.ClusterConfig(device1, ccm)
if cfg.Devices[0].Name != "tester" {
if cfg.Devices()[device1].Name != "tester" {
t.Errorf("Device name got overwritten")
}

View File

@@ -7,6 +7,7 @@
package model
import (
"fmt"
"path/filepath"
"reflect"
"time"
@@ -37,8 +38,10 @@ func NewProgressEmitter(cfg *config.Wrapper) *ProgressEmitter {
timer: time.NewTimer(time.Millisecond),
mut: sync.NewMutex(),
}
t.Changed(cfg.Raw())
t.CommitConfiguration(config.Configuration{}, cfg.Raw())
cfg.Subscribe(t)
return t
}
@@ -81,17 +84,22 @@ func (t *ProgressEmitter) Serve() {
}
}
// Changed implements the config.Handler Interface to handle configuration
// changes
func (t *ProgressEmitter) Changed(cfg config.Configuration) error {
// VerifyConfiguration implements the config.Committer interface
func (t *ProgressEmitter) VerifyConfiguration(from, to config.Configuration) error {
return nil
}
// CommitConfiguration implements the config.Committer interface
func (t *ProgressEmitter) CommitConfiguration(from, to config.Configuration) bool {
t.mut.Lock()
defer t.mut.Unlock()
t.interval = time.Duration(cfg.Options.ProgressUpdateIntervalS) * time.Second
t.interval = time.Duration(to.Options.ProgressUpdateIntervalS) * time.Second
if debug {
l.Debugln("progress emitter: updated interval", t.interval)
}
return nil
return true
}
// Stop stops the emitter.
@@ -138,3 +146,7 @@ func (t *ProgressEmitter) BytesCompleted(folder string) (bytes int64) {
}
return
}
func (t *ProgressEmitter) String() string {
return fmt.Sprintf("ProgressEmitter@%p", t)
}