diff --git a/internal/model/model.go b/internal/model/model.go index 964af8120..d1fe4f3b2 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -1370,11 +1370,12 @@ func (m *Model) Override(folder string) { // We are missing the file need.Flags |= protocol.FlagDeleted need.Blocks = nil + need.Version = need.Version.Update(m.shortID) } else { // We have the file, replace with our version + have.Version = have.Version.Merge(need.Version).Update(m.shortID) need = have } - need.Version = need.Version.Update(m.shortID) need.LocalVersion = 0 batch = append(batch, need) return true diff --git a/test/override_test.go b/test/override_test.go new file mode 100644 index 000000000..e4f0f7f31 --- /dev/null +++ b/test/override_test.go @@ -0,0 +1,222 @@ +// Copyright (C) 2015 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// +build integration + +package integration + +import ( + "io/ioutil" + "log" + "os" + "strings" + "testing" + "time" + + "github.com/syncthing/protocol" + "github.com/syncthing/syncthing/internal/config" +) + +func TestOverride(t *testing.T) { + // Enable "Master" on s1/default + id, _ := protocol.DeviceIDFromString(id1) + cfg, _ := config.Load("h1/config.xml", id) + fld := cfg.Folders()["default"] + fld.ReadOnly = true + cfg.SetFolder(fld) + os.Rename("h1/config.xml", "h1/config.xml.orig") + defer os.Rename("h1/config.xml.orig", "h1/config.xml") + cfg.Save() + + log.Println("Cleaning...") + err := removeAll("s1", "s2", "h1/index*", "h2/index*") + if err != nil { + t.Fatal(err) + } + + log.Println("Generating files...") + err = generateFiles("s1", 100, 20, "../LICENSE") + if err != nil { + t.Fatal(err) + } + + fd, err := os.Create("s1/testfile.txt") + if err != nil { + t.Fatal(err) + } + _, err = fd.WriteString("hello\n") + if err != nil { + t.Fatal(err) + } + err = fd.Close() + if err != nil { + t.Fatal(err) + } + + expected, err := directoryContents("s1") + if err != nil { + t.Fatal(err) + } + + log.Println("Starting master...") + master := syncthingProcess{ // id1 + instance: "1", + argv: []string{"-home", "h1"}, + port: 8081, + apiKey: apiKey, + } + err = master.start() + if err != nil { + t.Fatal(err) + } + defer master.stop() + + // Wait for one scan to succeed, or up to 20 seconds... This is to let + // startup, UPnP etc complete and make sure the master has the full index + // before they connect. + for i := 0; i < 20; i++ { + err := master.rescan("default") + if err != nil { + time.Sleep(time.Second) + continue + } + break + } + + log.Println("Starting slave...") + slave := syncthingProcess{ // id2 + instance: "2", + argv: []string{"-home", "h2"}, + port: 8082, + apiKey: apiKey, + } + err = slave.start() + if err != nil { + master.stop() + t.Fatal(err) + } + defer slave.stop() + + log.Println("Syncing...") + + if err = ovCompletion(100, master, slave); err != nil { + t.Fatal(err) + } + + log.Println("Verifying...") + + actual, err := directoryContents("s2") + if err != nil { + t.Fatal(err) + } + err = compareDirectoryContents(actual, expected) + if err != nil { + t.Fatal(err) + } + + log.Println("Changing file on slave side...") + + fd, err = os.OpenFile("s2/testfile.txt", os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + t.Fatal(err) + } + _, err = fd.WriteString("text added to s2\n") + if err != nil { + t.Fatal(err) + } + err = fd.Close() + if err != nil { + t.Fatal(err) + } + + err = slave.rescan("default") + if err != nil { + t.Fatal(err) + } + + log.Println("Syncing...") + + time.Sleep(5 * time.Second) + + // Expect ~99% completion since the change will be rejected by the master side + if err = ovCompletion(99, master, slave); err != nil { + t.Fatal(err) + } + + log.Println("Hitting Override on master...") + + resp, err := master.post("/rest/model/override?folder=default", nil) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != 200 { + t.Fatal(resp.Status) + } + + log.Println("Syncing...") + + if err = ovCompletion(100, master, slave); err != nil { + t.Fatal(err) + } + + // Verify that the override worked + + fd, err = os.Open("s1/testfile.txt") + if err != nil { + t.Fatal(err) + } + bs, err := ioutil.ReadAll(fd) + if err != nil { + t.Fatal(err) + } + fd.Close() + + if strings.Contains(string(bs), "added to s2") { + t.Error("Change should not have been synced to master") + } + + fd, err = os.Open("s2/testfile.txt") + if err != nil { + t.Fatal(err) + } + bs, err = ioutil.ReadAll(fd) + if err != nil { + t.Fatal(err) + } + fd.Close() + + if strings.Contains(string(bs), "added to s2") { + t.Error("Change should have been overridden on slave") + } +} + +func ovCompletion(expected int, p ...syncthingProcess) error { +mainLoop: + for { + time.Sleep(2500 * time.Millisecond) + + tot := 0 + for i := range p { + comp, err := p[i].peerCompletion() + if err != nil { + if isTimeout(err) { + continue mainLoop + } + return err + } + + for _, pct := range comp { + tot += pct + } + } + + if tot >= expected*len(p) { + return nil + } + + log.Printf("%d / %d...", tot, expected*len(p)) + } +}