Merge lp:~mvo/snappy/snappy-lp1449032-poor-mans-rsync into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Michael Vogt on 2015-06-03
Status: Merged
Approved by: Michael Vogt on 2015-06-08
Approved revision: 484
Merged at revision: 488
Proposed branch: lp:~mvo/snappy/snappy-lp1449032-poor-mans-rsync
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Diff against target: 287 lines (+177/-7)
8 files modified
helpers/helpers.go (+52/-0)
helpers/helpers_test.go (+102/-0)
partition/bootloader_uboot.go (+1/-4)
partition/partition.go (+3/-0)
progress/progress.go (+6/-2)
progress/progress_test.go (+1/-1)
snappy/systemimage.go (+10/-0)
snappy/systemimage_native.go (+2/-0)
To merge this branch: bzr merge lp:~mvo/snappy/snappy-lp1449032-poor-mans-rsync
Reviewer Review Type Date Requested Status
Michael Vogt Approve on 2015-06-08
John Lenton 2015-06-03 Approve on 2015-06-03
Review via email: mp+260931@code.launchpad.net

Commit Message

Only update changed files in SyncBootloader().

Description of the Change

This is lp:~mvo/snappy/snappy-lp1449032-poor-mans-rsync-15.04 for trunk

This branch changes SyncBootloader() to only update files that actually are different instead of just copying everything everytime. It also fixes some visual progress artefacts. Please let me know if you prefer that in a different branch.

To post a comment you must log in.
John Lenton (chipaca) :
review: Approve
Snappy Tarmac (snappydevtarmac) wrote :

Attempt to merge into lp:snappy failed due to conflicts:

text conflict in helpers/helpers.go

484. By Michael Vogt on 2015-06-08

merged lp:snappy and resolved conflicts

Michael Vogt (mvo) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'helpers/helpers.go'
2--- helpers/helpers.go 2015-06-04 12:55:54 +0000
3+++ helpers/helpers.go 2015-06-08 14:25:18 +0000
4@@ -436,3 +436,55 @@
5 "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
6 })
7 }
8+
9+// RSyncWithDelete syncs srcDir to destDir
10+func RSyncWithDelete(srcDirName, destDirName string) error {
11+ // first remove everything thats not in srcdir
12+ err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error {
13+ if err != nil {
14+ return err
15+ }
16+
17+ // relative to the root "destDirName"
18+ relPath := path[len(destDirName):]
19+ if !FileExists(filepath.Join(srcDirName, relPath)) {
20+ if err := os.RemoveAll(path); err != nil {
21+ return err
22+ }
23+ if info.IsDir() {
24+ return filepath.SkipDir
25+ }
26+ }
27+ return nil
28+ })
29+ if err != nil {
30+ return err
31+ }
32+
33+ // then copy or update the data from srcdir to destdir
34+ err = filepath.Walk(srcDirName, func(path string, info os.FileInfo, err error) error {
35+ if err != nil {
36+ return err
37+ }
38+
39+ // relative to the root "srcDirName"
40+ relPath := path[len(srcDirName):]
41+ if info.IsDir() {
42+ return os.MkdirAll(filepath.Join(destDirName, relPath), info.Mode())
43+ }
44+ src := path
45+ dst := filepath.Join(destDirName, relPath)
46+ if !FilesAreEqual(src, dst) {
47+ // XXX: we should (eventually) use CopyFile here,
48+ // but we need to teach it about preserving
49+ // of atime/mtime and permissions
50+ output, err := exec.Command("cp", "-va", src, dst).CombinedOutput()
51+ if err != nil {
52+ fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output)
53+ }
54+ }
55+ return nil
56+ })
57+
58+ return err
59+}
60
61=== modified file 'helpers/helpers_test.go'
62--- helpers/helpers_test.go 2015-06-04 12:55:54 +0000
63+++ helpers/helpers_test.go 2015-06-08 14:25:18 +0000
64@@ -21,6 +21,7 @@
65
66 import (
67 "compress/gzip"
68+ "fmt"
69 "io/ioutil"
70 "math/rand"
71 "os"
72@@ -328,3 +329,104 @@
73 c.Assert(mknodWasCalled, Equals, true)
74
75 }
76+
77+func makeTestFiles(c *C, srcDir, destDir string) {
78+ // a new file
79+ err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644)
80+ c.Assert(err, IsNil)
81+
82+ // a existing file that needs update
83+ err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644)
84+ c.Assert(err, IsNil)
85+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644)
86+ c.Assert(err, IsNil)
87+
88+ // existing file that needs no update
89+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644)
90+ c.Assert(err, IsNil)
91+ err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run()
92+ c.Assert(err, IsNil)
93+
94+ // a file that needs removal
95+ err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644)
96+ c.Assert(err, IsNil)
97+}
98+
99+func compareDirs(c *C, srcDir, destDir string) {
100+ d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput()
101+ c.Assert(err, IsNil)
102+ d2, err := exec.Command("ls", "-al", destDir).CombinedOutput()
103+ c.Assert(err, IsNil)
104+ c.Assert(string(d1), Equals, string(d2))
105+ // ensure content got updated
106+ c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput()
107+ c.Assert(err, IsNil)
108+ c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput()
109+ c.Assert(err, IsNil)
110+ c.Assert(string(c1), Equals, string(c2))
111+}
112+
113+func (ts *HTestSuite) TestSyncDirs(c *C) {
114+
115+ for _, l := range [][2]string{
116+ [2]string{"src-short", "dst-loooooooooooong"},
117+ [2]string{"src-loooooooooooong", "dst-short"},
118+ [2]string{"src-eq", "dst-eq"},
119+ } {
120+
121+ // ensure we have src, dest dirs with different length
122+ srcDir := filepath.Join(c.MkDir(), l[0])
123+ err := os.MkdirAll(srcDir, 0755)
124+ c.Assert(err, IsNil)
125+ destDir := filepath.Join(c.MkDir(), l[1])
126+ err = os.MkdirAll(destDir, 0755)
127+ c.Assert(err, IsNil)
128+
129+ // add a src subdir
130+ subdir := filepath.Join(srcDir, "subdir")
131+ err = os.Mkdir(subdir, 0755)
132+ c.Assert(err, IsNil)
133+ makeTestFiles(c, subdir, destDir)
134+
135+ // add a dst subdir that needs to get deleted
136+ subdir2 := filepath.Join(destDir, "to-be-deleted-subdir")
137+ err = os.Mkdir(subdir2, 0755)
138+ subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir")
139+ err = os.Mkdir(subdir3, 0755)
140+
141+ // and a toplevel
142+ makeTestFiles(c, srcDir, destDir)
143+
144+ // do it
145+ err = RSyncWithDelete(srcDir, destDir)
146+ c.Assert(err, IsNil)
147+
148+ // ensure meta-data is identical
149+ compareDirs(c, srcDir, destDir)
150+ compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir"))
151+ }
152+}
153+
154+func (ts *HTestSuite) TestSyncDirFails(c *C) {
155+ srcDir := c.MkDir()
156+ err := os.MkdirAll(srcDir, 0755)
157+ c.Assert(err, IsNil)
158+
159+ destDir := c.MkDir()
160+ err = os.MkdirAll(destDir, 0755)
161+ c.Assert(err, IsNil)
162+
163+ err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644)
164+ c.Assert(err, IsNil)
165+
166+ // ensure remove fails
167+ err = os.Chmod(destDir, 0100)
168+ c.Assert(err, IsNil)
169+ // make tempdir cleanup work again
170+ defer os.Chmod(destDir, 0755)
171+
172+ // do it
173+ err = RSyncWithDelete(srcDir, destDir)
174+ c.Check(err, NotNil)
175+ c.Check(err, ErrorMatches, ".*permission denied.*")
176+}
177
178=== modified file 'partition/bootloader_uboot.go'
179--- partition/bootloader_uboot.go 2015-05-29 09:55:15 +0000
180+++ partition/bootloader_uboot.go 2015-06-08 14:25:18 +0000
181@@ -221,10 +221,7 @@
182 srcDir := u.currentBootPath
183 destDir := u.otherBootPath
184
185- // always start from scratch: all files here are owned by us.
186- os.RemoveAll(destDir)
187-
188- return runCommand("/bin/cp", "-a", srcDir, destDir)
189+ return helpers.RSyncWithDelete(srcDir, destDir)
190 }
191
192 func (u *uboot) HandleAssets() (err error) {
193
194=== modified file 'partition/partition.go'
195--- partition/partition.go 2015-05-29 09:55:15 +0000
196+++ partition/partition.go 2015-06-08 14:25:18 +0000
197@@ -814,6 +814,9 @@
198 return err
199 }
200
201+ // XXX: first toggle roofs and then handle assets? that seems
202+ // wrong given that handleAssets may fails and we will
203+ // knowingly boot into a broken system
204 err = p.RunWithOther(RW, func(otherRoot string) (err error) {
205 return bootloader.ToggleRootFS()
206 })
207
208=== modified file 'progress/progress.go'
209--- progress/progress.go 2015-05-15 13:33:27 +0000
210+++ progress/progress.go 2015-06-08 14:25:18 +0000
211@@ -129,8 +129,9 @@
212 // Finished stops displaying the progress
213 func (t *TextProgress) Finished() {
214 if t.pbar != nil {
215- t.pbar.FinishPrint("Done")
216+ t.pbar.Finish()
217 }
218+ fmt.Println("Done")
219 }
220
221 // Write is there so that progress can implment a Writer and can be
222@@ -143,7 +144,10 @@
223 // that have a unknown duration
224 func (t *TextProgress) Spin(msg string) {
225 states := `|/-\`
226- fmt.Printf("\r%s[%c]", msg, states[t.spinStep])
227+
228+ // clear until end of line
229+ clearUntilEOL := "\033[K"
230+ fmt.Printf("\r%s[%c]%s", msg, states[t.spinStep], clearUntilEOL)
231 t.spinStep++
232 if t.spinStep >= len(states) {
233 t.spinStep = 0
234
235=== modified file 'progress/progress_test.go'
236--- progress/progress_test.go 2015-06-02 20:53:10 +0000
237+++ progress/progress_test.go 2015-06-08 14:25:18 +0000
238@@ -60,7 +60,7 @@
239 f.Seek(0, 0)
240 progress, err := ioutil.ReadAll(f)
241 c.Assert(err, IsNil)
242- c.Assert(string(progress), Equals, "\rm[|]\rm[/]\rm[-]\rm[\\]\rm[|]\rm[/]")
243+ c.Assert(string(progress), Equals, "\rm[|]\x1b[K\rm[/]\x1b[K\rm[-]\x1b[K\rm[\\]\x1b[K\rm[|]\x1b[K\rm[/]\x1b[K")
244 }
245
246 func (ts *ProgressTestSuite) testAgreed(answer string, value bool, c *C) {
247
248=== modified file 'snappy/systemimage.go'
249--- snappy/systemimage.go 2015-05-20 17:24:29 +0000
250+++ snappy/systemimage.go 2015-06-08 14:25:18 +0000
251@@ -198,6 +198,9 @@
252
253 // Ensure there is always a kernel + initrd to boot with, even
254 // if the update does not provide new versions.
255+ if pb != nil {
256+ pb.Notify("Syncing boot files")
257+ }
258 err = s.partition.SyncBootloaderFiles()
259 if err != nil {
260 return "", err
261@@ -226,6 +229,13 @@
262 return "", err
263 }
264
265+ // XXX: ToggleNextBoot() calls handleAssets() (but not SyncBootloader
266+ // files :/) - handleAssets() may copy kernel/initramfs to the
267+ // sync mounted /boot/uboot, so its very slow, tell the user
268+ // at least that something is going on
269+ if pb != nil {
270+ pb.Notify("Updating boot files")
271+ }
272 if err = s.partition.ToggleNextBoot(); err != nil {
273 return "", err
274 }
275
276=== modified file 'snappy/systemimage_native.go'
277--- snappy/systemimage_native.go 2015-05-15 13:33:27 +0000
278+++ snappy/systemimage_native.go 2015-06-08 14:25:18 +0000
279@@ -158,6 +158,8 @@
280 pb.Set(genericData.Now)
281 }
282 }
283+ // ugly: avoid Spin() artifacts
284+ pb.Notify("\nApply done")
285
286 if err := scanner.Err(); err != nil {
287 return err

Subscribers

People subscribed via source and target branches