Merge lp:~mvo/snappy/experimental-simplify-the-upgrader-script into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Michael Vogt
Status: Superseded
Proposed branch: lp:~mvo/snappy/experimental-simplify-the-upgrader-script
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Prerequisite: lp:~mvo/snappy/si-progress
Diff against target: 440 lines (+174/-44)
5 files modified
snappy/progress.go (+22/-8)
snappy/progress_test.go (+32/-0)
snappy/snapp.go (+1/-1)
snappy/systemimage.go (+62/-21)
snappy/systemimage_test.go (+57/-14)
To merge this branch: bzr merge lp:~mvo/snappy/experimental-simplify-the-upgrader-script
Reviewer Review Type Date Requested Status
Michael Vogt Pending
Review via email: mp+248023@code.launchpad.net

This proposal supersedes a proposal from 2015-01-29.

Description of the change

This branch might be interessting to simplfy the way that ubuntu-core-upgrader
works. The idea is that the DownloadUpdate() call is run with the other
partition mounted RW (DownloadUpdate will also apply the update so its
probably a good idea to update the name to reflect this fact).

This should in turn make the ubuntu-core-upgrader much simpler and allow
us to remove all the mount/unmount logic in there as this is hanlded by
snappy already and it move the responsibility of all this into a single
component (partition.go). We still need to ensure that "sync_partition()"
is called of course in ubuntu-core-upgrader (that is done in _cmd_mount()
right now AFAICS).

To post a comment you must log in.

Unmerged revisions

132. By Michael Vogt

merged lp:snappy and resolved conflicts

131. By Michael Vogt

Run "DownloadUpdate()" with the other partition mounted RW. This should
allow us to simplify the ubuntu-core-upgrader script significantly by
removing all the mount/umount logic there (making _cmd_mount/_cmd_umount
no-ops). The only catch is that the empty-parititon detection needs to
be done still of course.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'snappy/progress.go'
2--- snappy/progress.go 2015-01-23 14:55:15 +0000
3+++ snappy/progress.go 2015-01-29 16:50:57 +0000
4@@ -7,18 +7,23 @@
5 )
6
7 type ProgressMeter interface {
8- Start(total int64)
9- Increment()
10+ Start(total float64)
11+ Set(current float64)
12 Finished()
13
14+ // FIXME: better name?
15+ // Show progress via a spinner
16+ Spin(msg string)
17+
18 // interface for writer
19 Write(p []byte) (n int, err error)
20 }
21
22 type TextProgress struct {
23 ProgressMeter
24- pbar *pb.ProgressBar
25- pkg string
26+ pbar *pb.ProgressBar
27+ pkg string
28+ spinStep int
29 }
30
31 func NewTextProgress(pkg string) *TextProgress {
32@@ -27,14 +32,14 @@
33 return &t
34 }
35
36-func (t *TextProgress) Start(total int64) {
37+func (t *TextProgress) Start(total float64) {
38 fmt.Println("Starting download of", t.pkg)
39- t.pbar.Total = total
40+ t.pbar.Total = int64(total)
41 t.pbar.Start()
42 }
43
44-func (t *TextProgress) Increment() {
45- t.pbar.Increment()
46+func (t *TextProgress) Set(current float64) {
47+ t.pbar.Set(int(current))
48 }
49
50 func (t *TextProgress) Finished() {
51@@ -44,3 +49,12 @@
52 func (t *TextProgress) Write(p []byte) (n int, err error) {
53 return t.pbar.Write(p)
54 }
55+
56+func (t *TextProgress) Spin(msg string) {
57+ states := `|/-\`
58+ fmt.Printf("\r%s[%c]", msg, states[t.spinStep])
59+ t.spinStep++
60+ if t.spinStep >= len(states) {
61+ t.spinStep = 0
62+ }
63+}
64
65=== added file 'snappy/progress_test.go'
66--- snappy/progress_test.go 1970-01-01 00:00:00 +0000
67+++ snappy/progress_test.go 2015-01-29 16:50:57 +0000
68@@ -0,0 +1,32 @@
69+package snappy
70+
71+import (
72+ "io/ioutil"
73+ "os"
74+
75+ . "gopkg.in/check.v1"
76+)
77+
78+type ProgressTestSuite struct{}
79+
80+var _ = Suite(&ProgressTestSuite{})
81+
82+func (ts *ProgressTestSuite) TestSpin(c *C) {
83+ f, err := ioutil.TempFile("", "progress-")
84+ c.Assert(err, IsNil)
85+ defer os.Remove(f.Name())
86+ oldStdout := os.Stdout
87+ os.Stdout = f
88+
89+ t := NewTextProgress("no-pkg")
90+ for i := 0; i < 6; i++ {
91+ t.Spin("m")
92+ }
93+
94+ os.Stdout = oldStdout
95+ f.Sync()
96+ f.Seek(0, 0)
97+ progress, err := ioutil.ReadAll(f)
98+ c.Assert(err, IsNil)
99+ c.Assert(string(progress), Equals, "\rm[|]\rm[/]\rm[-]\rm[\\]\rm[|]\rm[/]")
100+}
101
102=== modified file 'snappy/snapp.go'
103--- snappy/snapp.go 2015-01-27 23:32:36 +0000
104+++ snappy/snapp.go 2015-01-29 16:50:57 +0000
105@@ -269,7 +269,7 @@
106 defer resp.Body.Close()
107
108 if pbar != nil {
109- pbar.Start(resp.ContentLength)
110+ pbar.Start(float64(resp.ContentLength))
111 mw := io.MultiWriter(w, pbar)
112 _, err = io.Copy(mw, resp.Body)
113 pbar.Finished()
114
115=== modified file 'snappy/systemimage.go'
116--- snappy/systemimage.go 2015-01-29 15:28:06 +0000
117+++ snappy/systemimage.go 2015-01-29 16:50:57 +0000
118@@ -11,6 +11,7 @@
119 "fmt"
120 "log"
121 "os"
122+ "path"
123 "runtime"
124 "strings"
125 "time"
126@@ -93,6 +94,41 @@
127
128 func (s *SystemImagePart) Install(pb ProgressMeter) (err error) {
129
130+ quitCh := make(chan int)
131+ if pb != nil {
132+ // FIXME: we need to find a way to stop this watcher
133+ updateProgressCh, err := s.proxy.makeWatcher("UpdateProgress")
134+ if err != nil {
135+ log.Panic(fmt.Sprintf("ERROR: %v", err))
136+ return nil
137+ }
138+
139+ defer func() {
140+ quitCh <- 1
141+ }()
142+ pb.Start(100.0)
143+ go func() {
144+ for {
145+ var percent int32
146+ var eta float64
147+ select {
148+ case msg := <-updateProgressCh:
149+ err := msg.Args(&percent, &eta)
150+ if err == nil {
151+ if percent >= 0 {
152+ pb.Set(float64(percent))
153+ } else {
154+ pb.Spin("Applying")
155+ }
156+ }
157+ case <-quitCh:
158+ pb.Finished()
159+ break
160+ }
161+ }
162+ }()
163+ }
164+
165 // Ensure there is always a kernel + initrd to boot with, even
166 // if the update does not provide new versions.
167 err = s.partition.SyncBootloaderFiles()
168@@ -100,14 +136,21 @@
169 return err
170 }
171
172- err = s.proxy.DownloadUpdate()
173+ err = s.partition.RunWithOther(partition.RW, func(o string) (err error) {
174+ err = s.proxy.DownloadUpdate()
175+ return
176+ })
177 if err != nil {
178 return err
179 }
180
181 // FIXME: switch s-i daemon back to current partition
182+ err = s.partition.UpdateBootloader()
183
184- return s.partition.UpdateBootloader()
185+ if pb != nil {
186+ quitCh <- 1
187+ }
188+ return err
189 }
190
191 func (s *SystemImagePart) Uninstall() (err error) {
192@@ -160,6 +203,7 @@
193 updateApplied chan *dbus.Message
194 updateDownloaded chan *dbus.Message
195 updateFailed chan *dbus.Message
196+ quitCh chan int // can be used to stop the watchers
197 }
198
199 // this functions only exists to make testing easier, i.e. the testsuite
200@@ -184,6 +228,7 @@
201 return nil
202 }
203
204+ p.quitCh = make(chan int)
205 p.updateAvailableStatus, err = p.makeWatcher("UpdateAvailableStatus")
206 if err != nil {
207 log.Panic(fmt.Sprintf("ERROR: %v", err))
208@@ -261,8 +306,11 @@
209
210 received = make(chan *dbus.Message)
211 go func() {
212- for msg := range watch.C {
213- received <- msg
214+ for {
215+ select {
216+ case msg := <-watch.C:
217+ received <- msg
218+ }
219 }
220 }()
221
222@@ -314,7 +362,7 @@
223 // so once the D-Bus call completes, it no longer cares
224 // about configFile.
225 return s.partition.RunWithOther(partition.RO, func(otherRoot string) (err error) {
226- configFile := otherRoot + systemImageClientConfig
227+ configFile := path.Join(otherRoot, systemImageClientConfig)
228 // FIXME: replace with FileExists() call once it's in a utility
229 // package.
230 _, err = os.Stat(configFile)
231@@ -336,6 +384,7 @@
232 if err = s.ReloadConfiguration(false); err != nil {
233 return us, err
234 }
235+ // FIXME: we can not switch back or DownloadUpdate is unhappy
236
237 callName := "CheckForUpdate"
238 _, err = s.proxy.Call(systemImageBusName, callName)
239@@ -360,12 +409,6 @@
240 systemImageTimeoutSecs))
241 }
242
243- // switch back to using the current rootfs's system-image
244- // configuration.
245- if err = s.ReloadConfiguration(true); err != nil {
246- return us, err
247- }
248-
249 return s.us, err
250 }
251
252@@ -418,7 +461,7 @@
253 }
254
255 func (s *SystemImageRepository) currentPart() Part {
256- configFile := s.myroot + systemImageChannelConfig
257+ configFile := path.Join(s.myroot, systemImageChannelConfig)
258 part, err := s.makePartFromSystemImageConfigFile(configFile, true)
259 if err != nil {
260 log.Printf("Can not make system-image part for %s: %s", configFile, err)
261@@ -462,17 +505,15 @@
262 if _, err = s.proxy.CheckForUpdate(); err != nil {
263 return parts, err
264 }
265- info, err := s.proxy.Information()
266- if err != nil {
267- return parts, err
268- }
269- if VersionCompare(info["current_build_number"], info["target_build_number"]) < 0 {
270- version := info["target_build_number"]
271+ current := s.currentPart()
272+ current_version := current.Version()
273+ target_version := s.proxy.us.available_version
274+ if VersionCompare(current_version, target_version) < 0 {
275 parts = append(parts, &SystemImagePart{
276 proxy: s.proxy,
277- version: version,
278- versionDetails: info["version_details"],
279- channelName: info["channel_name"],
280+ version: target_version,
281+ versionDetails: "?",
282+ channelName: current.(*SystemImagePart).channelName,
283 partition: s.partition})
284 }
285
286
287=== modified file 'snappy/systemimage_test.go'
288--- snappy/systemimage_test.go 2015-01-28 10:31:55 +0000
289+++ snappy/systemimage_test.go 2015-01-29 16:50:57 +0000
290@@ -109,10 +109,11 @@
291 service *DBusService
292 partition *partition.Partition
293
294- info map[string]string
295+ fakeAvailableVersion string
296+ info map[string]string
297 }
298
299-func NewMockSystemImage() *MockSystemImage {
300+func newMockSystemImage() *MockSystemImage {
301 msi := new(MockSystemImage)
302 msi.info = make(map[string]string)
303
304@@ -140,9 +141,9 @@
305 // again instead
306 var size int32 = 1234
307 sig.AppendArgs(
308- true, // is_available
309- false, // downloading
310- "3.14", // available_version
311+ true, // is_available
312+ false, // downloading
313+ m.fakeAvailableVersion, // available_version
314 size, // update_size
315 "late_update_date", // laste update date
316 "") // error_reason
317@@ -155,6 +156,20 @@
318 }
319
320 func (m *MockSystemImage) DownloadUpdate() error {
321+ // send progress
322+ for i := 1; i <= 5; i++ {
323+ sig := dbus.NewSignalMessage(systemImageObjectPath, systemImageInterface, "UpdateProgress")
324+ sig.AppendArgs(
325+ int32(20*i), // percent (int32)
326+ float64(100.0-(20.0*i)), // eta (double)
327+ )
328+ if err := m.service.SendSignal(sig); err != nil {
329+ // FIXME: do something with the error
330+ panic(err)
331+ }
332+ }
333+
334+ // send done
335 sig := dbus.NewSignalMessage(systemImageObjectPath, systemImageInterface, "UpdateDownloaded")
336
337 sig.AppendArgs(
338@@ -192,7 +207,7 @@
339 s.conn, err = dbus.Connect(dbus.SessionBus)
340 c.Assert(err, IsNil)
341
342- s.mockSystemImage = NewMockSystemImage()
343+ s.mockSystemImage = newMockSystemImage()
344 s.mockService = NewDBusService(s.conn, systemImageInterface, systemImageObjectPath, systemImageBusName, s.mockSystemImage)
345 c.Assert(s.mockService, NotNil)
346
347@@ -202,9 +217,9 @@
348 tmpdir, err := ioutil.TempDir("", "si-root-")
349 c.Assert(err, IsNil)
350 s.systemImage.myroot = tmpdir
351- makeFakeSystemImageChannelConfig(c, tmpdir+systemImageChannelConfig, "2.71")
352+ makeFakeSystemImageChannelConfig(c, path.Join(tmpdir, systemImageChannelConfig), "2.71")
353 // setup fake /other partition
354- makeFakeSystemImageChannelConfig(c, tmpdir+"/other/"+systemImageChannelConfig, "3.14")
355+ makeFakeSystemImageChannelConfig(c, path.Join(tmpdir, "other", systemImageChannelConfig), "3.14")
356
357 s.tmpdir = tmpdir
358 }
359@@ -214,8 +229,8 @@
360 }
361
362 func makeFakeSystemImageChannelConfig(c *C, cfgPath, buildNumber string) {
363- os.MkdirAll(path.Dir(cfgPath), 0777)
364- f, err := os.OpenFile(cfgPath, os.O_CREATE|os.O_RDWR, 0666)
365+ os.MkdirAll(path.Dir(cfgPath), 0775)
366+ f, err := os.OpenFile(cfgPath, os.O_CREATE|os.O_RDWR, 0664)
367 c.Assert(err, IsNil)
368 defer f.Close()
369 f.Write([]byte(fmt.Sprintf(`
370@@ -263,6 +278,7 @@
371 }
372
373 func (s *SITestSuite) TestUpdateNoUpdate(c *C) {
374+ s.mockSystemImage.fakeAvailableVersion = "2.71"
375 parts, err := s.systemImage.Updates()
376 c.Assert(err, IsNil)
377 c.Assert(len(parts), Equals, 0)
378@@ -270,7 +286,7 @@
379
380 func (s *SITestSuite) TestUpdateHasUpdate(c *C) {
381 // add a update
382- s.mockSystemImage.info["target_build_number"] = "3.14"
383+ s.mockSystemImage.fakeAvailableVersion = "3.14"
384 parts, err := s.systemImage.Updates()
385 c.Assert(err, IsNil)
386 c.Assert(len(parts), Equals, 1)
387@@ -305,23 +321,50 @@
388 return f("/other")
389 }
390
391+type MockProgressMeter struct {
392+ total float64
393+ progress []float64
394+ finished bool
395+ spin bool
396+}
397+
398+func (m *MockProgressMeter) Start(total float64) {
399+ m.total = total
400+}
401+func (m *MockProgressMeter) Set(current float64) {
402+ m.progress = append(m.progress, current)
403+}
404+func (m *MockProgressMeter) Spin(msg string) {
405+ m.spin = true
406+}
407+func (m *MockProgressMeter) Write(buf []byte) (n int, err error) {
408+ return len(buf), err
409+}
410+func (m *MockProgressMeter) Finished() {
411+ m.finished = true
412+}
413+
414 func (s *SITestSuite) TestSystemImagePartInstallUpdatesPartition(c *C) {
415 // add a update
416- s.mockSystemImage.info["target_build_number"] = "3.14"
417+ s.mockSystemImage.fakeAvailableVersion = "3.14"
418 parts, err := s.systemImage.Updates()
419
420 sp := parts[0].(*SystemImagePart)
421 mockPartition := MockPartition{}
422 sp.partition = &mockPartition
423
424- err = sp.Install(nil)
425+ pb := &MockProgressMeter{}
426+ err = sp.Install(pb)
427 c.Assert(err, IsNil)
428 c.Assert(mockPartition.updateBootloaderCalled, Equals, true)
429+ c.Assert(pb.total, Equals, 100.0)
430+ c.Assert(pb.finished, Equals, true)
431+ c.Assert(pb.progress, DeepEquals, []float64{20.0, 40.0, 60.0, 80.0, 100.0})
432 }
433
434 func (s *SITestSuite) TestSystemImagePartInstall(c *C) {
435 // add a update
436- s.mockSystemImage.info["target_build_number"] = "3.14"
437+ s.mockSystemImage.fakeAvailableVersion = "3.14"
438 parts, err := s.systemImage.Updates()
439
440 sp := parts[0].(*SystemImagePart)

Subscribers

People subscribed via source and target branches