Merge lp:~elopio/snappy/failover-tests-merge-trunk into lp:~fgimenez/snappy/failover-tests

Proposed by Leo Arias
Status: Merged
Approved by: Federico Gimenez
Approved revision: 538
Merged at revision: 530
Proposed branch: lp:~elopio/snappy/failover-tests-merge-trunk
Merge into: lp:~fgimenez/snappy/failover-tests
Diff against target: 2260 lines (+642/-625)
32 files modified
_integration-tests/README (+0/-1)
_integration-tests/main.go (+8/-26)
_integration-tests/snappy-selftest (+4/-4)
_integration-tests/tests/common_test.go (+4/-3)
_integration-tests/tests/failover_test.go (+2/-2)
_integration-tests/tests/install_test.go (+6/-5)
cmd/snappy/cmd_internal_unpack.go (+3/-3)
cmd/snappy/cmd_low_level_unpack_test.go (+5/-5)
debian/control (+0/-7)
debian/integration-tests/control (+1/-1)
debian/rules (+0/-6)
debian/ubuntu-snappy-tests.install (+0/-1)
helpers/touch.go (+2/-0)
partition/assets.go (+87/-0)
partition/assets_test.go (+36/-0)
partition/bootloader.go (+11/-61)
partition/bootloader_grub.go (+6/-20)
partition/bootloader_grub_test.go (+5/-14)
partition/bootloader_uboot.go (+23/-30)
partition/bootloader_uboot_test.go (+17/-27)
partition/dirs.go (+41/-0)
partition/mount.go (+153/-0)
partition/mount_test.go (+64/-0)
partition/partition.go (+66/-299)
partition/partition_test.go (+50/-98)
partition/utils.go (+19/-0)
snappy/errors.go (+2/-0)
snappy/install.go (+2/-2)
snappy/security.go (+5/-0)
snappy/security_test.go (+7/-0)
snappy/systemimage.go (+8/-1)
snappy/systemimage_test.go (+5/-9)
To merge this branch: bzr merge lp:~elopio/snappy/failover-tests-merge-trunk
Reviewer Review Type Date Requested Status
Federico Gimenez Approve
Review via email: mp+263017@code.launchpad.net

Commit message

Merged with trunk.
Changed the os.Unset to be compatible with go 1.3.
Do not move the test binary to tmp, because it will be removed after reboots.

To post a comment you must log in.
531. By Leo Arias

Added missing {

532. By Leo Arias

Fixed the call to execCommand.

533. By Leo Arias

Replace UnsetEnv for Setenv empty, because Unset was added in go 1.4

534. By Leo Arias

Recovered the string output.

535. By Leo Arias

Typo.

536. By Leo Arias

Updated the return value.

537. By Leo Arias

Updated the return value.

538. By Leo Arias

No need to cast to string.

Revision history for this message
Federico Gimenez (fgimenez) wrote :

Thanks a lot :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '_integration-tests/README'
2--- _integration-tests/README 2015-06-17 15:42:00 +0000
3+++ _integration-tests/README 2015-06-25 19:16:26 +0000
4@@ -19,4 +19,3 @@
5 shell tests) with:
6
7 $ go run _integration-test/main.go
8-
9
10=== modified file '_integration-tests/main.go'
11--- _integration-tests/main.go 2015-06-19 05:01:44 +0000
12+++ _integration-tests/main.go 2015-06-25 19:16:26 +0000
13@@ -32,18 +32,18 @@
14 debsTestBedPath = "/tmp/snappy-debs"
15 defaultRelease = "rolling"
16 defaultChannel = "edge"
17- defaultArch = "amd64"
18 )
19
20 var (
21 debsDir = filepath.Join(baseDir, "debs")
22+ testsDir = filepath.Join(baseDir, "tests")
23 imageDir = filepath.Join(baseDir, "image")
24 outputDir = filepath.Join(baseDir, "output")
25 imageTarget = filepath.Join(imageDir, "snappy.img")
26 )
27
28 func execCommand(cmds ...string) {
29- cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
30+ cmd := exec.Command(cmds[0], cmds[1:]...)
31 cmd.Stdout = os.Stdout
32 cmd.Stderr = os.Stderr
33 if err := cmd.Run(); err != nil {
34@@ -51,15 +51,11 @@
35 }
36 }
37
38-func buildDebs(rootPath string) {
39- fmt.Println("Building debs...")
40- prepareTargetDir(debsDir)
41- execCommand(
42- "bzr", "bd",
43- fmt.Sprintf("--result-dir=%s", debsDir),
44- "--split",
45- rootPath,
46- "--", "-uc", "-us")
47+func buildTests() {
48+ fmt.Println("Building tests")
49+ prepareTargetDir(testsDir)
50+ execCommand("go", "test", "-c", "./_integration-tests/tests")
51+ os.Rename("tests.test", "snappy.tests")
52 }
53
54 func createImage(release, channel string) {
55@@ -80,15 +76,9 @@
56 "adt-run",
57 "-B",
58 "--setup-commands", "touch /run/autopkgtest_no_reboot.stamp",
59- "--setup-commands", "mount -o remount,rw /",
60- "--setup-commands",
61- fmt.Sprintf("dpkg -i %s/*deb", debsTestBedPath),
62- "--setup-commands",
63- "sync; sleep 2; mount -o remount,ro /",
64 "--override-control", "debian/integration-tests/control",
65 "--built-tree", rootPath,
66 "--output-dir", outputDir,
67- fmt.Sprintf("--copy=%s:%s", debsDir, debsTestBedPath),
68 "---",
69 "ssh", "-s", "/usr/share/autopkgtest/ssh-setup/snappy",
70 "--", "-i", imageTarget)
71@@ -110,18 +100,10 @@
72 return dir
73 }
74
75-func getArchForImage() string {
76- return fmt.Sprintf("generic-%s", defaultArch)
77-}
78-
79 func main() {
80 rootPath := getRootPath()
81
82- if len(os.Args) == 2 {
83- debsDir = os.Args[1]
84- } else {
85- buildDebs(rootPath)
86- }
87+ buildTests()
88
89 createImage(defaultRelease, defaultChannel)
90
91
92=== modified file '_integration-tests/snappy-selftest'
93--- _integration-tests/snappy-selftest 2015-06-16 06:47:24 +0000
94+++ _integration-tests/snappy-selftest 2015-06-25 19:16:26 +0000
95@@ -4,7 +4,7 @@
96
97 MYDIR=$(dirname $0)
98
99-. $MYDIR/tests/framework
100+. "$MYDIR"/tests/framework
101
102 SNAPPY=snappy
103
104@@ -15,9 +15,9 @@
105 exit 1
106 fi
107
108-. $MYDIR/tests/settings
109+. "$MYDIR"/tests/settings
110
111-. $MYDIR/tests/common.sh
112+. "$MYDIR"/tests/common.sh
113
114 # prepare the environment
115 if [ -z "$ADT_REBOOT_MARK" ]; then
116@@ -27,7 +27,7 @@
117 sudo snappy remove xkcd-webserver 2>&1 || true
118 fi
119
120-for test in $MYDIR/tests/*_test_*[!~#]; do
121+for test in "$MYDIR"/tests/*_test_*[!~#]; do
122 CURRENT_TEST=$(basename $test)
123
124 # after a reboot, skip ahead to the test that triggered it
125
126=== modified file '_integration-tests/tests/common_test.go'
127--- _integration-tests/tests/common_test.go 2015-06-22 09:31:21 +0000
128+++ _integration-tests/tests/common_test.go 2015-06-25 19:16:26 +0000
129@@ -33,11 +33,12 @@
130 // Hook up gocheck into the "go test" runner
131 func Test(t *testing.T) { TestingT(t) }
132
133-func execCommand(c *C, cmds ...string) []byte {
134+func execCommand(c *C, cmds ...string) string {
135 cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
136 output, err := cmd.CombinedOutput()
137- c.Assert(err, IsNil, Commentf("Error: %v", string(output)))
138- return output
139+ stringOutput := string(output)
140+ c.Assert(err, IsNil, Commentf("Error: %v", stringOutput))
141+ return stringOutput
142 }
143
144 func execCommandToFile(c *C, filename string, cmds ...string) {
145
146=== modified file '_integration-tests/tests/failover_test.go'
147--- _integration-tests/tests/failover_test.go 2015-06-22 09:31:21 +0000
148+++ _integration-tests/tests/failover_test.go 2015-06-25 19:16:26 +0000
149@@ -76,7 +76,7 @@
150 }
151
152 func removeRebootMark(c *C) {
153- err := os.Unsetenv("ADT_REBOOT_MARK")
154+ err := os.Setenv("ADT_REBOOT_MARK", "")
155 c.Assert(err, IsNil, Commentf("Error unsetting ADT_REBOOT_MARK"))
156 }
157
158@@ -89,7 +89,7 @@
159 output := execCommand(c, "snappy", "list")
160 pattern := "(?mU)^ubuntu-core (.*)$"
161 re := regexp.MustCompile(pattern)
162- match := re.FindStringSubmatch(string(output))
163+ match := re.FindStringSubmatch(output)
164 c.Assert(match, NotNil, Commentf("Version not found in %s", output))
165
166 // match is like "ubuntu-core 2015-06-18 93 ubuntu"
167
168=== modified file '_integration-tests/tests/install_test.go'
169--- _integration-tests/tests/install_test.go 2015-06-22 09:31:21 +0000
170+++ _integration-tests/tests/install_test.go 2015-06-25 19:16:26 +0000
171@@ -27,7 +27,7 @@
172 CommonSuite
173 }
174
175-func installSnap(c *C, packageName string) []byte {
176+func installSnap(c *C, packageName string) string {
177 return execCommand(c, "sudo", "snappy", "install", packageName)
178 }
179
180@@ -44,7 +44,7 @@
181 ".*\n" +
182 "hello-world .* .* canonical \n" +
183 ".*\n"
184- c.Assert(string(installOutput), Matches, expected)
185+ c.Assert(installOutput, Matches, expected)
186 }
187
188 func (s *InstallSuite) TestCallBinaryFromInstalledSnap(c *C) {
189@@ -52,14 +52,15 @@
190
191 echoOutput := execCommand(c, "hello-world.echo")
192
193- c.Assert(string(echoOutput), Equals, "Hello World!\n")
194+ c.Assert(echoOutput, Equals, "Hello World!\n")
195 }
196
197 func (s *InstallSuite) TestInfoMustPrintInstalledPackageInformation(c *C) {
198 installSnap(c, "hello-world")
199
200- infoOutput := execCommand(c, "sudo", "snappy", "info")
201+ infoOutput := execCommand(c, "snappy", "info")
202
203 expected := "(?ms).*^apps: hello-world\n"
204- c.Assert(string(infoOutput), Matches, expected)
205+
206+ c.Assert(infoOutput, Matches, expected)
207 }
208
209=== modified file 'cmd/snappy/cmd_internal_unpack.go'
210--- cmd/snappy/cmd_internal_unpack.go 2015-06-08 13:03:35 +0000
211+++ cmd/snappy/cmd_internal_unpack.go 2015-06-25 19:16:26 +0000
212@@ -70,7 +70,7 @@
213 return filepath.Join("/etc/", file)
214 }
215
216-func readUid(user, passwdFile string) (uid int, err error) {
217+func readUID(user, passwdFile string) (uid int, err error) {
218 f, err := os.Open(passwdFile)
219 if err != nil {
220 return -1, err
221@@ -125,13 +125,13 @@
222 if helpers.ShouldDropPrivs() {
223
224 passFile := passwdFile(rootDir, "passwd")
225- uid, err := readUid(dropPrivsUser, passFile)
226+ uid, err := readUID(dropPrivsUser, passFile)
227 if err != nil {
228 return err
229 }
230
231 groupFile := passwdFile(rootDir, "group")
232- gid, err := readUid(dropPrivsUser, groupFile)
233+ gid, err := readUID(dropPrivsUser, groupFile)
234 if err != nil {
235 return err
236 }
237
238=== modified file 'cmd/snappy/cmd_low_level_unpack_test.go'
239--- cmd/snappy/cmd_low_level_unpack_test.go 2015-06-02 20:46:07 +0000
240+++ cmd/snappy/cmd_low_level_unpack_test.go 2015-06-25 19:16:26 +0000
241@@ -42,7 +42,7 @@
242 clickpkg:x:101:104::/nonexistent:/bin/false
243 `)
244
245- uid, err := readUid("clickpkg", f.Name())
246+ uid, err := readUID("clickpkg", f.Name())
247 c.Assert(err, IsNil)
248 c.Assert(uid, Equals, 101)
249 }
250@@ -53,7 +53,7 @@
251 clickpkg:x:104:
252 `)
253
254- gid, err := readUid("clickpkg", f.Name())
255+ gid, err := readUID("clickpkg", f.Name())
256 c.Assert(err, IsNil)
257 c.Assert(gid, Equals, 104)
258 }
259@@ -66,7 +66,7 @@
260 `)
261 defer os.Remove(f.Name())
262
263- uid, err := readUid("clickpkg", f.Name())
264+ uid, err := readUID("clickpkg", f.Name())
265 c.Assert(err, IsNil)
266 c.Assert(uid, Equals, 102)
267 }
268@@ -77,7 +77,7 @@
269 clickpkg:x:
270 `)
271
272- _, err := readUid("clickpkg", f.Name())
273+ _, err := readUID("clickpkg", f.Name())
274 c.Assert(err, NotNil)
275 }
276
277@@ -86,6 +86,6 @@
278 daemon:
279 `)
280
281- _, err := readUid("clickpkg", f.Name())
282+ _, err := readUID("clickpkg", f.Name())
283 c.Assert(err, NotNil)
284 }
285
286=== modified file 'debian/control'
287--- debian/control 2015-06-16 15:06:45 +0000
288+++ debian/control 2015-06-25 19:16:26 +0000
289@@ -51,10 +51,3 @@
290 Built-Using: ${misc:Built-Using}
291 Description: Tool to interact with Ubuntu Core Snappy.
292 Manage an Ubuntu system with snappy.
293-
294-Package: ubuntu-snappy-tests
295-Architecture: any
296-Depends: ubuntu-snappy-cli (= ${binary:Version}),
297- ${misc:Depends}
298-Description: snappy selftests
299- Installs snappy selftests binary
300
301=== modified file 'debian/integration-tests/control'
302--- debian/integration-tests/control 2015-06-16 04:52:54 +0000
303+++ debian/integration-tests/control 2015-06-25 19:16:26 +0000
304@@ -1,4 +1,4 @@
305-Test-Command: snappy.test -gocheck.vv -test.outputdir=$ADT_ARTIFACTS
306+Test-Command: ./snappy.tests -gocheck.vv -test.outputdir=$ADT_ARTIFACTS
307 Restrictions: allow-stderr
308 Depends: ubuntu-snappy-tests
309
310
311=== modified file 'debian/rules'
312--- debian/rules 2015-06-17 12:06:39 +0000
313+++ debian/rules 2015-06-25 19:16:26 +0000
314@@ -4,7 +4,6 @@
315 #export DH_VERBOSE=1
316 export DH_OPTIONS
317 export DH_GOPKG := launchpad.net/snappy
318-DH_BUILDDIR = obj-$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
319
320 %:
321 dh $@ --buildsystem=golang --with=golang --fail-missing --with systemd
322@@ -51,11 +50,6 @@
323 -pubuntu-snappy \
324 snappy-autopilot.service
325
326-override_dh_auto_build:
327- dh_auto_build
328- GOPATH=$$PWD/$(DH_BUILDDIR) go test -c ./_integration-tests/tests
329- mv tests.test $$PWD/$(DH_BUILDDIR)/bin/snappy.test
330-
331 override_dh_auto_install:
332 dh_auto_install -O--buildsystem=golang
333 # Making the packages private
334
335=== removed file 'debian/ubuntu-snappy-tests.install'
336--- debian/ubuntu-snappy-tests.install 2015-06-13 18:10:49 +0000
337+++ debian/ubuntu-snappy-tests.install 1970-01-01 00:00:00 +0000
338@@ -1,1 +0,0 @@
339-/usr/bin/snappy.test
340
341=== modified file 'helpers/touch.go'
342--- helpers/touch.go 2015-05-15 13:33:27 +0000
343+++ helpers/touch.go 2015-06-25 19:16:26 +0000
344@@ -38,6 +38,8 @@
345 "unsafe"
346 )
347
348+// ErrNotAbsPath is returned when an absolute path is needed but the received
349+// path is not.
350 var ErrNotAbsPath = errors.New("not an absolute path")
351
352 // UpdateTimestamp updates the timestamp of the file at pathname. It does not
353
354=== added file 'partition/assets.go'
355--- partition/assets.go 1970-01-01 00:00:00 +0000
356+++ partition/assets.go 2015-06-25 19:16:26 +0000
357@@ -0,0 +1,87 @@
358+// -*- Mode: Go; indent-tabs-mode: t -*-
359+
360+/*
361+ * Copyright (C) 2014-2015 Canonical Ltd
362+ *
363+ * This program is free software: you can redistribute it and/or modify
364+ * it under the terms of the GNU General Public License version 3 as
365+ * published by the Free Software Foundation.
366+ *
367+ * This program is distributed in the hope that it will be useful,
368+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
369+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
370+ * GNU General Public License for more details.
371+ *
372+ * You should have received a copy of the GNU General Public License
373+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
374+ *
375+ */
376+
377+package partition
378+
379+import (
380+ "errors"
381+ "io/ioutil"
382+ "os"
383+ "path/filepath"
384+
385+ "gopkg.in/yaml.v2"
386+)
387+
388+// Representation of the yaml in the hardwareSpecFile
389+type hardwareSpecType struct {
390+ Kernel string `yaml:"kernel"`
391+ Initrd string `yaml:"initrd"`
392+ DtbDir string `yaml:"dtbs"`
393+ PartitionLayout string `yaml:"partition-layout"`
394+ Bootloader bootloaderName `yaml:"bootloader"`
395+}
396+
397+var (
398+ // ErrNoHardwareYaml is returned when no hardware yaml is found in
399+ // the update, this means that there is nothing to process with regards
400+ // to device parts.
401+ ErrNoHardwareYaml = errors.New("no hardware.yaml")
402+
403+ // Declarative specification of the type of system which specifies such
404+ // details as:
405+ //
406+ // - the location of initrd+kernel within the system-image archive.
407+ // - the location of hardware-specific .dtb files within the
408+ // system-image archive.
409+ // - the type of bootloader that should be used for this system.
410+ // - expected system partition layout (single or dual rootfs's).
411+ hardwareSpecFileReal = filepath.Join(cacheDir, "hardware.yaml")
412+
413+ // useful to override in the tests
414+ hardwareSpecFile = hardwareSpecFileReal
415+
416+ // Directory that _may_ get automatically created on unpack that
417+ // contains updated hardware-specific boot assets (such as initrd,
418+ // kernel)
419+ assetsDir = filepath.Join(cacheDir, "assets")
420+
421+ // Directory that _may_ get automatically created on unpack that
422+ // contains updated hardware-specific assets that require flashing
423+ // to the disk (such as uBoot, MLO)
424+ flashAssetsDir = filepath.Join(cacheDir, "flashtool-assets")
425+)
426+
427+func readHardwareSpec() (*hardwareSpecType, error) {
428+ var h hardwareSpecType
429+
430+ data, err := ioutil.ReadFile(hardwareSpecFile)
431+ // if hardware.yaml does not exist it just means that there was no
432+ // device part in the update.
433+ if os.IsNotExist(err) {
434+ return nil, ErrNoHardwareYaml
435+ } else if err != nil {
436+ return nil, err
437+ }
438+
439+ if err := yaml.Unmarshal([]byte(data), &h); err != nil {
440+ return nil, err
441+ }
442+
443+ return &h, nil
444+}
445
446=== added file 'partition/assets_test.go'
447--- partition/assets_test.go 1970-01-01 00:00:00 +0000
448+++ partition/assets_test.go 2015-06-25 19:16:26 +0000
449@@ -0,0 +1,36 @@
450+// -*- Mode: Go; indent-tabs-mode: t -*-
451+
452+/*
453+ * Copyright (C) 2014-2015 Canonical Ltd
454+ *
455+ * This program is free software: you can redistribute it and/or modify
456+ * it under the terms of the GNU General Public License version 3 as
457+ * published by the Free Software Foundation.
458+ *
459+ * This program is distributed in the hope that it will be useful,
460+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
461+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
462+ * GNU General Public License for more details.
463+ *
464+ * You should have received a copy of the GNU General Public License
465+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
466+ *
467+ */
468+
469+package partition
470+
471+import (
472+ . "gopkg.in/check.v1"
473+)
474+
475+func (s *PartitionTestSuite) TestHardwareSpec(c *C) {
476+
477+ hardwareSpecFile = makeHardwareYaml(c, "")
478+ hw, err := readHardwareSpec()
479+ c.Assert(err, IsNil)
480+ c.Assert(hw.Kernel, Equals, "assets/vmlinuz")
481+ c.Assert(hw.Initrd, Equals, "assets/initrd.img")
482+ c.Assert(hw.DtbDir, Equals, "assets/dtbs")
483+ c.Assert(hw.PartitionLayout, Equals, bootloaderSystemAB)
484+ c.Assert(hw.Bootloader, Equals, bootloaderNameUboot)
485+}
486
487=== modified file 'partition/bootloader.go'
488--- partition/bootloader.go 2015-06-09 17:43:20 +0000
489+++ partition/bootloader.go 2015-06-25 19:16:26 +0000
490@@ -45,7 +45,7 @@
491
492 // Switch bootloader configuration so that the "other" root
493 // filesystem partition will be used on next boot.
494- ToggleRootFS() error
495+ ToggleRootFS(otherRootfs string) error
496
497 // Hook function called before system-image starts downloading
498 // and applying archives that allows files to be copied between
499@@ -60,14 +60,6 @@
500 GetBootVar(name string) (string, error)
501
502 // Return the 1-character name corresponding to the
503- // rootfs currently being used.
504- GetRootFSName() string
505-
506- // Return the 1-character name corresponding to the
507- // other rootfs.
508- GetOtherRootFSName() string
509-
510- // Return the 1-character name corresponding to the
511 // rootfs that will be used on _next_ boot.
512 //
513 // XXX: Note the distinction between this method and
514@@ -78,7 +70,7 @@
515
516 // Update the bootloader configuration to mark the
517 // currently-booted rootfs as having booted successfully.
518- MarkCurrentBootSuccessful() error
519+ MarkCurrentBootSuccessful(currentRootfs string) error
520
521 // Return the additional required chroot bind mounts for this bootloader
522 AdditionalBindMounts() []string
523@@ -88,15 +80,6 @@
524 BootDir() string
525 }
526
527-type bootloaderType struct {
528- partition *Partition
529-
530- // each rootfs partition has a corresponding u-boot directory named
531- // from the last character of the partition name ('a' or 'b').
532- currentRootfs string
533- otherRootfs string
534-}
535-
536 // Factory method that returns a new bootloader for the given partition
537 var bootloader = bootloaderImpl
538
539@@ -115,46 +98,13 @@
540 return nil, ErrBootloader
541 }
542
543-func newBootLoader(partition *Partition) *bootloaderType {
544- b := new(bootloaderType)
545-
546- b.partition = partition
547-
548- currentLabel := partition.rootPartition().name
549-
550- // FIXME: is this the right thing to do? i.e. what should we do
551- // on a single partition system?
552- if partition.otherRootPartition() == nil {
553- return nil
554- }
555- otherLabel := partition.otherRootPartition().name
556-
557- b.currentRootfs = string(currentLabel[len(currentLabel)-1])
558- b.otherRootfs = string(otherLabel[len(otherLabel)-1])
559-
560- return b
561-}
562-
563-// Return true if the next boot will use the other rootfs
564-// partition.
565-func isNextBootOther(bootloader bootLoader) bool {
566- value, err := bootloader.GetBootVar(bootloaderBootmodeVar)
567- if err != nil {
568- return false
569- }
570-
571- if value != bootloaderBootmodeTry {
572- return false
573- }
574-
575- fsname, err := bootloader.GetNextBootRootFSName()
576- if err != nil {
577- return false
578- }
579-
580- if fsname == bootloader.GetOtherRootFSName() {
581- return true
582- }
583-
584- return false
585+// BootloaderDir returns the full path to the (mounted and writable)
586+// bootloader-specific boot directory.
587+func BootloaderDir() string {
588+ b, err := bootloader(nil)
589+ if err != nil {
590+ return ""
591+ }
592+
593+ return b.BootDir()
594 }
595
596=== modified file 'partition/bootloader_grub.go'
597--- partition/bootloader_grub.go 2015-06-09 17:22:59 +0000
598+++ partition/bootloader_grub.go 2015-06-25 19:16:26 +0000
599@@ -49,7 +49,6 @@
600 )
601
602 type grub struct {
603- *bootloaderType
604 }
605
606 const bootloaderNameGrub bootloaderName = "grub"
607@@ -59,13 +58,8 @@
608 if !helpers.FileExists(bootloaderGrubConfigFile) || !helpers.FileExists(bootloaderGrubUpdateCmd) {
609 return nil
610 }
611- b := newBootLoader(partition)
612- if b == nil {
613- return nil
614- }
615- g := &grub{bootloaderType: b}
616
617- return g
618+ return &grub{}
619 }
620
621 func (g *grub) Name() bootloaderName {
622@@ -77,10 +71,10 @@
623 // Approach:
624 //
625 // Update the grub configuration.
626-func (g *grub) ToggleRootFS() (err error) {
627+func (g *grub) ToggleRootFS(otherRootfs string) (err error) {
628
629 // create the grub config
630- if err := runInChroot(g.partition.MountTarget(), bootloaderGrubUpdateCmd); err != nil {
631+ if err := runInChroot(mountTarget, bootloaderGrubUpdateCmd); err != nil {
632 return err
633 }
634
635@@ -91,7 +85,7 @@
636 // Record the partition that will be used for next boot. This
637 // isn't necessary for correct operation under grub, but allows
638 // us to query the next boot device easily.
639- return g.setBootVar(bootloaderRootfsVar, g.otherRootfs)
640+ return g.setBootVar(bootloaderRootfsVar, otherRootfs)
641 }
642
643 func (g *grub) GetBootVar(name string) (value string, err error) {
644@@ -127,22 +121,14 @@
645 return g.GetBootVar(bootloaderRootfsVar)
646 }
647
648-func (g *grub) GetRootFSName() string {
649- return g.currentRootfs
650-}
651-
652-func (g *grub) GetOtherRootFSName() string {
653- return g.otherRootfs
654-}
655-
656-func (g *grub) MarkCurrentBootSuccessful() (err error) {
657+func (g *grub) MarkCurrentBootSuccessful(currentRootfs string) (err error) {
658 // Clear the variable set by grub on boot to denote a good
659 // boot.
660 if err := g.unsetBootVar(bootloaderGrubTrialBootVar); err != nil {
661 return err
662 }
663
664- if err := g.setBootVar(bootloaderRootfsVar, g.currentRootfs); err != nil {
665+ if err := g.setBootVar(bootloaderRootfsVar, currentRootfs); err != nil {
666 return err
667 }
668
669
670=== modified file 'partition/bootloader_grub_test.go'
671--- partition/bootloader_grub_test.go 2015-06-09 17:43:20 +0000
672+++ partition/bootloader_grub_test.go 2015-06-25 19:16:26 +0000
673@@ -63,15 +63,6 @@
674 c.Assert(g.Name(), Equals, bootloaderNameGrub)
675 }
676
677-func (s *PartitionTestSuite) TestNewGrubSinglePartition(c *C) {
678- runLsblk = mockRunLsblkSingleRootSnappy
679- s.makeFakeGrubEnv(c)
680-
681- partition := New()
682- g := newGrub(partition)
683- c.Assert(g, IsNil)
684-}
685-
686 type singleCommand []string
687
688 var allCommands = []singleCommand{}
689@@ -88,14 +79,14 @@
690 partition := New()
691 g := newGrub(partition)
692 c.Assert(g, NotNil)
693- err := g.ToggleRootFS()
694+ err := g.ToggleRootFS("b")
695 c.Assert(err, IsNil)
696
697 // this is always called
698- mp := singleCommand{"/bin/mountpoint", "/writable/cache/system"}
699+ mp := singleCommand{"/bin/mountpoint", mountTarget}
700 c.Assert(allCommands[0], DeepEquals, mp)
701
702- expectedGrubUpdate := singleCommand{"/usr/sbin/chroot", "/writable/cache/system", bootloaderGrubUpdateCmd}
703+ expectedGrubUpdate := singleCommand{"/usr/sbin/chroot", mountTarget, bootloaderGrubUpdateCmd}
704 c.Assert(allCommands[1], DeepEquals, expectedGrubUpdate)
705
706 expectedGrubSet := singleCommand{bootloaderGrubEnvCmd, bootloaderGrubEnvFile, "set", "snappy_mode=try"}
707@@ -141,11 +132,11 @@
708 partition := New()
709 g := newGrub(partition)
710 c.Assert(g, NotNil)
711- err := g.MarkCurrentBootSuccessful()
712+ err := g.MarkCurrentBootSuccessful("a")
713 c.Assert(err, IsNil)
714
715 // this is always called
716- mp := singleCommand{"/bin/mountpoint", "/writable/cache/system"}
717+ mp := singleCommand{"/bin/mountpoint", mountTarget}
718 c.Assert(allCommands[0], DeepEquals, mp)
719
720 expectedGrubSet := singleCommand{bootloaderGrubEnvCmd, bootloaderGrubEnvFile, "unset", "snappy_trial_boot"}
721
722=== modified file 'partition/bootloader_uboot.go'
723--- partition/bootloader_uboot.go 2015-06-09 17:22:59 +0000
724+++ partition/bootloader_uboot.go 2015-06-25 19:16:26 +0000
725@@ -58,8 +58,6 @@
726 const bootloaderNameUboot bootloaderName = "u-boot"
727
728 type uboot struct {
729- *bootloaderType
730-
731 // full path to rootfs-specific assets on boot partition
732 currentBootPath string
733 otherBootPath string
734@@ -78,13 +76,13 @@
735 return nil
736 }
737
738- b := newBootLoader(partition)
739- if b == nil {
740- return nil
741- }
742- u := uboot{bootloaderType: b}
743- u.currentBootPath = filepath.Join(bootloaderUbootDir, u.currentRootfs)
744- u.otherBootPath = filepath.Join(bootloaderUbootDir, u.otherRootfs)
745+ u := uboot{}
746+
747+ currentRootfs := partition.rootPartition().shortName
748+ u.currentBootPath = filepath.Join(bootloaderUbootDir, currentRootfs)
749+
750+ otherRootfs := partition.otherRootPartition().shortName
751+ u.otherBootPath = filepath.Join(bootloaderUbootDir, otherRootfs)
752
753 return &u
754 }
755@@ -103,7 +101,7 @@
756 // - Copy the "other" rootfs's kernel+initrd to the boot partition,
757 // renaming them in the process to ensure the next boot uses the
758 // correct versions.
759-func (u *uboot) ToggleRootFS() (err error) {
760+func (u *uboot) ToggleRootFS(otherRootfs string) (err error) {
761
762 // If the file exists, update it. Otherwise create it.
763 //
764@@ -112,7 +110,7 @@
765 // recreate to allow the system to boot!
766 changes := []configFileChange{
767 configFileChange{Name: bootloaderRootfsVar,
768- Value: string(u.otherRootfs),
769+ Value: string(otherRootfs),
770 },
771 configFileChange{Name: bootloaderBootmodeVar,
772 Value: bootloaderBootmodeTry,
773@@ -142,14 +140,6 @@
774 return value, nil
775 }
776
777-func (u *uboot) GetRootFSName() string {
778- return u.currentRootfs
779-}
780-
781-func (u *uboot) GetOtherRootFSName() string {
782- return u.otherRootfs
783-}
784-
785 // FIXME: put into utils package
786 func readLines(path string) (lines []string, err error) {
787
788@@ -200,13 +190,13 @@
789 return file.Sync()
790 }
791
792-func (u *uboot) MarkCurrentBootSuccessful() (err error) {
793+func (u *uboot) MarkCurrentBootSuccessful(currentRootfs string) (err error) {
794 changes := []configFileChange{
795 configFileChange{Name: bootloaderBootmodeVar,
796 Value: bootloaderBootmodeSuccess,
797 },
798 configFileChange{Name: bootloaderRootfsVar,
799- Value: string(u.currentRootfs),
800+ Value: string(currentRootfs),
801 },
802 }
803
804@@ -227,14 +217,18 @@
805 func (u *uboot) HandleAssets() (err error) {
806 // check if we have anything, if there is no hardware yaml, there is nothing
807 // to process.
808- hardware, err := u.partition.hardwareSpec()
809+ hardware, err := readHardwareSpec()
810 if err == ErrNoHardwareYaml {
811 return nil
812 } else if err != nil {
813 return err
814 }
815- // ensure to remove the file once we are done
816- defer os.Remove(u.partition.hardwareSpecFile)
817+ // ensure to remove the file once we are done (and all was good)
818+ defer func() {
819+ if err == nil {
820+ os.Remove(hardwareSpecFile)
821+ }
822+ }()
823
824 // validate bootloader
825 if hardware.Bootloader != u.Name() {
826@@ -244,8 +238,9 @@
827 hardware.Bootloader)
828 }
829
830- // validate partition layout
831- if u.partition.dualRootPartitions() && hardware.PartitionLayout != bootloaderSystemAB {
832+ // validate partition layout, we ONLY support bootloaderSystemAB
833+ // currently
834+ if hardware.PartitionLayout != bootloaderSystemAB {
835 return fmt.Errorf("hardware spec requires dual root partitions")
836 }
837
838@@ -263,7 +258,7 @@
839 }
840
841 // expand path
842- path := filepath.Join(u.partition.cacheDir(), file)
843+ path := filepath.Join(cacheDir, file)
844
845 if !helpers.FileExists(path) {
846 return fmt.Errorf("can not find file %s", path)
847@@ -281,7 +276,7 @@
848 // fully speced
849
850 // install .dtb files
851- dtbSrcDir := filepath.Join(u.partition.cacheDir(), hardware.DtbDir)
852+ dtbSrcDir := filepath.Join(cacheDir, hardware.DtbDir)
853 if helpers.FileExists(dtbSrcDir) {
854 // ensure we cleanup the source dir
855 defer os.RemoveAll(dtbSrcDir)
856@@ -303,8 +298,6 @@
857 }
858 }
859
860- flashAssetsDir := u.partition.flashAssetsDir()
861-
862 if helpers.FileExists(flashAssetsDir) {
863 // FIXME: we don't currently do anything with the
864 // MLO + uImage files since they are not specified in
865
866=== modified file 'partition/bootloader_uboot_test.go'
867--- partition/bootloader_uboot_test.go 2015-06-12 05:23:40 +0000
868+++ partition/bootloader_uboot_test.go 2015-06-25 19:16:26 +0000
869@@ -89,15 +89,6 @@
870 c.Assert(u.Name(), Equals, bootloaderNameUboot)
871 }
872
873-func (s *PartitionTestSuite) TestNewUbootSinglePartition(c *C) {
874- runLsblk = mockRunLsblkSingleRootSnappy
875- s.makeFakeUbootEnv(c)
876-
877- partition := New()
878- u := newUboot(partition)
879- c.Assert(u, IsNil)
880-}
881-
882 func (s *PartitionTestSuite) TestUbootGetBootVar(c *C) {
883 s.makeFakeUbootEnv(c)
884
885@@ -111,7 +102,7 @@
886 c.Assert(nextBoot, Equals, "a")
887
888 // ensure that nextBootIsOther works too
889- c.Assert(isNextBootOther(u), Equals, false)
890+ c.Assert(partition.IsNextBootOther(), Equals, false)
891 }
892
893 func (s *PartitionTestSuite) TestUbootToggleRootFS(c *C) {
894@@ -121,7 +112,7 @@
895 u := newUboot(partition)
896 c.Assert(u, NotNil)
897
898- err := u.ToggleRootFS()
899+ err := u.ToggleRootFS("b")
900 c.Assert(err, IsNil)
901
902 nextBoot, err := u.GetBootVar(bootloaderRootfsVar)
903@@ -129,7 +120,7 @@
904 c.Assert(nextBoot, Equals, "b")
905
906 // ensure that nextBootIsOther works too
907- c.Assert(isNextBootOther(u), Equals, true)
908+ c.Assert(partition.IsNextBootOther(), Equals, true)
909 }
910
911 func (s *PartitionTestSuite) TestUbootGetEnvVar(c *C) {
912@@ -158,7 +149,7 @@
913
914 func makeMockAssetsDir(c *C) {
915 for _, f := range []string{"assets/vmlinuz", "assets/initrd.img", "assets/dtbs/foo.dtb", "assets/dtbs/bar.dtb"} {
916- p := filepath.Join(defaultCacheDir, f)
917+ p := filepath.Join(cacheDir, f)
918 os.MkdirAll(filepath.Dir(p), 0755)
919 err := ioutil.WriteFile(p, []byte(f), 0644)
920 c.Assert(err, IsNil)
921@@ -172,8 +163,8 @@
922 c.Assert(err, IsNil)
923
924 // mock the hardwareYaml and the cacheDir
925- p.hardwareSpecFile = makeHardwareYaml(c, "")
926- defaultCacheDir = c.MkDir()
927+ hardwareSpecFile = makeHardwareYaml(c, "")
928+ cacheDir = c.MkDir()
929
930 // create mock assets/
931 makeMockAssetsDir(c)
932@@ -192,8 +183,8 @@
933 }
934
935 // ensure nothing left behind
936- c.Assert(helpers.FileExists(filepath.Join(defaultCacheDir, "assets")), Equals, false)
937- c.Assert(helpers.FileExists(p.hardwareSpecFile), Equals, false)
938+ c.Assert(helpers.FileExists(filepath.Join(cacheDir, "assets")), Equals, false)
939+ c.Assert(helpers.FileExists(hardwareSpecFile), Equals, false)
940 }
941
942 func (s *PartitionTestSuite) TestHandleAssetsVerifyBootloader(c *C) {
943@@ -203,7 +194,8 @@
944 c.Assert(err, IsNil)
945
946 // mock the hardwareYaml and the cacheDir
947- p.hardwareSpecFile = makeHardwareYaml(c, "bootloader: grub")
948+ hardwareSpecFile = makeHardwareYaml(c, "bootloader: grub")
949+ cacheDir = c.MkDir()
950
951 err = bootloader.HandleAssets()
952 c.Assert(err, NotNil)
953@@ -216,18 +208,16 @@
954 c.Assert(err, IsNil)
955
956 // mock the hardwareYaml and the cacheDir
957- p.hardwareSpecFile = makeHardwareYaml(c, `
958+ hardwareSpecFile = makeHardwareYaml(c, `
959 bootloader: u-boot
960 partition-layout: inplace
961 `)
962-
963 err = bootloader.HandleAssets()
964 c.Assert(err, NotNil)
965 }
966
967 func (s *PartitionTestSuite) TestHandleAssetsNoHardwareYaml(c *C) {
968 s.makeFakeUbootEnv(c)
969- defaultCacheDir = c.MkDir()
970
971 p := New()
972 bootloader, err := bootloader(p)
973@@ -242,7 +232,7 @@
974 bootloader, err := bootloader(p)
975 c.Assert(err, IsNil)
976
977- p.hardwareSpecFile = makeHardwareYaml(c, `
978+ hardwareSpecFile = makeHardwareYaml(c, `
979 bootloader u-boot
980 `)
981
982@@ -268,7 +258,7 @@
983 // enter "try" mode so that we check to ensure that snappy
984 // correctly modifies the snappy_mode variable from "try" to
985 // "regular" to denote a good boot.
986- err = u.ToggleRootFS()
987+ err = u.ToggleRootFS("b")
988 c.Assert(err, IsNil)
989
990 c.Assert(helpers.FileExists(bootloaderUbootEnvFile), Equals, true)
991@@ -278,7 +268,7 @@
992 c.Assert(strings.Contains(string(bytes), "snappy_mode=regular"), Equals, false)
993 c.Assert(strings.Contains(string(bytes), "snappy_ab=b"), Equals, true)
994
995- err = u.MarkCurrentBootSuccessful()
996+ err = u.MarkCurrentBootSuccessful("b")
997 c.Assert(err, IsNil)
998
999 c.Assert(helpers.FileExists(bootloaderUbootStampFile), Equals, false)
1000@@ -288,7 +278,7 @@
1001 c.Assert(err, IsNil)
1002 c.Assert(strings.Contains(string(bytes), "snappy_mode=try"), Equals, false)
1003 c.Assert(strings.Contains(string(bytes), "snappy_mode=regular"), Equals, true)
1004- c.Assert(strings.Contains(string(bytes), "snappy_ab=a"), Equals, true)
1005+ c.Assert(strings.Contains(string(bytes), "snappy_ab=b"), Equals, true)
1006 }
1007
1008 func (s *PartitionTestSuite) TestNoWriteNotNeeded(c *C) {
1009@@ -301,7 +291,7 @@
1010 u := newUboot(partition)
1011 c.Assert(u, NotNil)
1012
1013- c.Check(u.MarkCurrentBootSuccessful(), IsNil)
1014+ c.Check(u.MarkCurrentBootSuccessful("a"), IsNil)
1015 c.Assert(atomiCall, Equals, false)
1016 }
1017
1018@@ -318,7 +308,7 @@
1019 u := newUboot(partition)
1020 c.Assert(u, NotNil)
1021
1022- c.Check(u.MarkCurrentBootSuccessful(), IsNil)
1023+ c.Check(u.MarkCurrentBootSuccessful("a"), IsNil)
1024 c.Assert(atomiCall, Equals, true)
1025
1026 bytes, err := ioutil.ReadFile(bootloaderUbootEnvFile)
1027
1028=== added file 'partition/dirs.go'
1029--- partition/dirs.go 1970-01-01 00:00:00 +0000
1030+++ partition/dirs.go 2015-06-25 19:16:26 +0000
1031@@ -0,0 +1,41 @@
1032+// -*- Mode: Go; indent-tabs-mode: t -*-
1033+
1034+/*
1035+ * Copyright (C) 2014-2015 Canonical Ltd
1036+ *
1037+ * This program is free software: you can redistribute it and/or modify
1038+ * it under the terms of the GNU General Public License version 3 as
1039+ * published by the Free Software Foundation.
1040+ *
1041+ * This program is distributed in the hope that it will be useful,
1042+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1043+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1044+ * GNU General Public License for more details.
1045+ *
1046+ * You should have received a copy of the GNU General Public License
1047+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1048+ *
1049+ */
1050+
1051+package partition
1052+
1053+import (
1054+ "path/filepath"
1055+)
1056+
1057+// The full path to the cache directory, which is used as a
1058+// scratch pad, for downloading new images to and bind mounting the
1059+// rootfs.
1060+const cacheDirReal = "/writable/cache"
1061+
1062+var (
1063+ // useful for overwriting in the tests
1064+ cacheDir = cacheDirReal
1065+
1066+ // Directory to mount writable root filesystem below the cache
1067+ // diretory.
1068+ mountTargetReal = filepath.Join(cacheDir, "system")
1069+
1070+ // useful to override in tests
1071+ mountTarget = mountTargetReal
1072+)
1073
1074=== added file 'partition/mount.go'
1075--- partition/mount.go 1970-01-01 00:00:00 +0000
1076+++ partition/mount.go 2015-06-25 19:16:26 +0000
1077@@ -0,0 +1,153 @@
1078+// -*- Mode: Go; indent-tabs-mode: t -*-
1079+
1080+/*
1081+ * Copyright (C) 2014-2015 Canonical Ltd
1082+ *
1083+ * This program is free software: you can redistribute it and/or modify
1084+ * it under the terms of the GNU General Public License version 3 as
1085+ * published by the Free Software Foundation.
1086+ *
1087+ * This program is distributed in the hope that it will be useful,
1088+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1089+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1090+ * GNU General Public License for more details.
1091+ *
1092+ * You should have received a copy of the GNU General Public License
1093+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1094+ *
1095+ */
1096+
1097+package partition
1098+
1099+import (
1100+ "fmt"
1101+ "sort"
1102+)
1103+
1104+// MountOption represents how the partition should be mounted, currently
1105+// RO (read-only) and RW (read-write) are supported
1106+type MountOption int
1107+
1108+const (
1109+ // RO mounts the partition read-only
1110+ RO MountOption = iota
1111+ // RW mounts the partition read-only
1112+ RW
1113+)
1114+
1115+// mountEntry represents a mount this package has created.
1116+type mountEntry struct {
1117+ source string
1118+ target string
1119+
1120+ options string
1121+
1122+ // true if target refers to a bind mount. We could derive this
1123+ // from options, but this field saves the effort.
1124+ bindMount bool
1125+}
1126+
1127+// mountEntryArray represents an array of mountEntry objects.
1128+type mountEntryArray []mountEntry
1129+
1130+// current mounts that this package has created.
1131+var mounts mountEntryArray
1132+
1133+// Len is part of the sort interface, required to allow sort to work
1134+// with an array of Mount objects.
1135+func (mounts mountEntryArray) Len() int {
1136+ return len(mounts)
1137+}
1138+
1139+// Less is part of the sort interface, required to allow sort to work
1140+// with an array of Mount objects.
1141+func (mounts mountEntryArray) Less(i, j int) bool {
1142+ return mounts[i].target < mounts[j].target
1143+}
1144+
1145+// Swap is part of the sort interface, required to allow sort to work
1146+// with an array of Mount objects.
1147+func (mounts mountEntryArray) Swap(i, j int) {
1148+ mounts[i], mounts[j] = mounts[j], mounts[i]
1149+}
1150+
1151+// removeMountByTarget removes the Mount specified by the target from
1152+// the global mounts array.
1153+func removeMountByTarget(mnts mountEntryArray, target string) (results mountEntryArray) {
1154+
1155+ for _, m := range mnts {
1156+ if m.target != target {
1157+ results = append(results, m)
1158+ }
1159+ }
1160+
1161+ return results
1162+}
1163+
1164+// undoMounts unmounts all mounts this package has mounted optionally
1165+// only unmounting bind mounts and leaving all remaining mounts.
1166+func undoMounts(bindMountsOnly bool) error {
1167+
1168+ mountsCopy := make(mountEntryArray, len(mounts), cap(mounts))
1169+ copy(mountsCopy, mounts)
1170+
1171+ // reverse sort to ensure unmounts are handled in the correct
1172+ // order.
1173+ sort.Sort(sort.Reverse(mountsCopy))
1174+
1175+ // Iterate backwards since we want a reverse-sorted list of
1176+ // mounts to ensure we can unmount in order.
1177+ for _, mount := range mountsCopy {
1178+ if bindMountsOnly && !mount.bindMount {
1179+ continue
1180+ }
1181+
1182+ if err := unmountAndRemoveFromGlobalMountList(mount.target); err != nil {
1183+ return err
1184+ }
1185+ }
1186+
1187+ return nil
1188+}
1189+
1190+// FIXME: use syscall.Mount() here
1191+func mount(source, target, options string) (err error) {
1192+ var args []string
1193+
1194+ args = append(args, "/bin/mount")
1195+ if options != "" {
1196+ args = append(args, fmt.Sprintf("-o%s", options))
1197+ }
1198+
1199+ args = append(args, source)
1200+ args = append(args, target)
1201+
1202+ return runCommand(args...)
1203+}
1204+
1205+// Mount the given directory and add it to the global mounts slice
1206+func mountAndAddToGlobalMountList(m mountEntry) (err error) {
1207+
1208+ err = mount(m.source, m.target, m.options)
1209+ if err == nil {
1210+ mounts = append(mounts, m)
1211+ }
1212+
1213+ return err
1214+}
1215+
1216+// Unmount the given directory and remove it from the global "mounts" slice
1217+func unmountAndRemoveFromGlobalMountList(target string) (err error) {
1218+ err = runCommand("/bin/umount", target)
1219+ if err != nil {
1220+ return err
1221+
1222+ }
1223+
1224+ results := removeMountByTarget(mounts, target)
1225+
1226+ // Update global
1227+ mounts = results
1228+
1229+ return nil
1230+}
1231
1232=== added file 'partition/mount_test.go'
1233--- partition/mount_test.go 1970-01-01 00:00:00 +0000
1234+++ partition/mount_test.go 2015-06-25 19:16:26 +0000
1235@@ -0,0 +1,64 @@
1236+// -*- Mode: Go; indent-tabs-mode: t -*-
1237+
1238+/*
1239+ * Copyright (C) 2014-2015 Canonical Ltd
1240+ *
1241+ * This program is free software: you can redistribute it and/or modify
1242+ * it under the terms of the GNU General Public License version 3 as
1243+ * published by the Free Software Foundation.
1244+ *
1245+ * This program is distributed in the hope that it will be useful,
1246+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1247+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1248+ * GNU General Public License for more details.
1249+ *
1250+ * You should have received a copy of the GNU General Public License
1251+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1252+ *
1253+ */
1254+
1255+package partition
1256+
1257+import (
1258+ . "gopkg.in/check.v1"
1259+)
1260+
1261+func (s *PartitionTestSuite) TestMountEntryArray(c *C) {
1262+ mea := mountEntryArray{}
1263+
1264+ c.Assert(mea.Len(), Equals, 0)
1265+
1266+ me := mountEntry{source: "/dev",
1267+ target: "/dev",
1268+ options: "bind",
1269+ bindMount: true}
1270+
1271+ mea = append(mea, me)
1272+ c.Assert(mea.Len(), Equals, 1)
1273+
1274+ me = mountEntry{source: "/foo",
1275+ target: "/foo",
1276+ options: "",
1277+ bindMount: false}
1278+
1279+ mea = append(mea, me)
1280+ c.Assert(mea.Len(), Equals, 2)
1281+
1282+ c.Assert(mea.Less(0, 1), Equals, true)
1283+ c.Assert(mea.Less(1, 0), Equals, false)
1284+
1285+ mea.Swap(0, 1)
1286+ c.Assert(mea.Less(0, 1), Equals, false)
1287+ c.Assert(mea.Less(1, 0), Equals, true)
1288+
1289+ results := removeMountByTarget(mea, "invalid")
1290+
1291+ // No change expected
1292+ c.Assert(results, DeepEquals, mea)
1293+
1294+ results = removeMountByTarget(mea, "/dev")
1295+
1296+ c.Assert(len(results), Equals, 1)
1297+ c.Assert(results[0], Equals, mountEntry{source: "/foo",
1298+ target: "/foo", options: "", bindMount: false})
1299+}
1300
1301=== modified file 'partition/partition.go'
1302--- partition/partition.go 2015-06-09 17:43:20 +0000
1303+++ partition/partition.go 2015-06-25 19:16:26 +0000
1304@@ -23,50 +23,37 @@
1305 import (
1306 "errors"
1307 "fmt"
1308- "io/ioutil"
1309 "os"
1310 "os/signal"
1311 "path/filepath"
1312 "regexp"
1313- "sort"
1314 "strings"
1315+ "sync"
1316 "syscall"
1317
1318- "gopkg.in/yaml.v2"
1319-
1320 "launchpad.net/snappy/logger"
1321 )
1322
1323-var signalHandlerRegistered = false
1324-
1325-// Name of writable user data partition label as created by
1326-// ubuntu-device-flash(1).
1327-const writablePartitionLabel = "writable"
1328-
1329-// Name of primary root filesystem partition label as created by
1330-// ubuntu-device-flash(1).
1331-const rootfsAlabel = "system-a"
1332-
1333-// Name of primary root filesystem partition label as created by
1334-// ubuntu-device-flash(1). Note that this partition will
1335-// only be present if this is an A/B upgrade system.
1336-const rootfsBlabel = "system-b"
1337-
1338-// name of boot partition label as created by ubuntu-device-flash(1).
1339-const bootPartitionLabel = "system-boot"
1340-
1341-// its useful to override this in tests
1342-const realDefaultCacheDir = "/writable/cache"
1343-
1344-// FIXME: Should query system-image-cli (see bug LP:#1380574).
1345-var defaultCacheDir = realDefaultCacheDir
1346-
1347-// Directory to mount writable root filesystem below the cache
1348-// diretory.
1349-const mountTarget = "system"
1350-
1351-// File creation mode used when any directories are created
1352-const dirMode = 0750
1353+const (
1354+ // Name of writable user data partition label as created by
1355+ // ubuntu-device-flash(1).
1356+ writablePartitionLabel = "writable"
1357+
1358+ // Name of primary root filesystem partition label as created by
1359+ // ubuntu-device-flash(1).
1360+ rootfsAlabel = "system-a"
1361+
1362+ // Name of primary root filesystem partition label as created by
1363+ // ubuntu-device-flash(1). Note that this partition will
1364+ // only be present if this is an A/B upgrade system.
1365+ rootfsBlabel = "system-b"
1366+
1367+ // name of boot partition label as created by ubuntu-device-flash(1).
1368+ bootPartitionLabel = "system-boot"
1369+
1370+ // File creation mode used when any directories are created
1371+ dirMode = 0750
1372+)
1373
1374 var (
1375 // ErrBootloader is returned if the bootloader can not be determined
1376@@ -79,41 +66,6 @@
1377 // ErrNoDualPartition is returned if you try to use a dual
1378 // partition feature on a single partition
1379 ErrNoDualPartition = errors.New("No dual partition")
1380-
1381- // ErrNoHardwareYaml is returned when no hardware yaml is found in
1382- // the update, this means that there is nothing to process with regards
1383- // to device parts.
1384- ErrNoHardwareYaml = errors.New("no hardware.yaml")
1385-)
1386-
1387-// Declarative specification of the type of system which specifies such
1388-// details as:
1389-//
1390-// - the location of initrd+kernel within the system-image archive.
1391-// - the location of hardware-specific .dtb files within the
1392-// system-image archive.
1393-// - the type of bootloader that should be used for this system.
1394-// - expected system partition layout (single or dual rootfs's).
1395-const hardwareSpecFile = "hardware.yaml"
1396-
1397-// Directory that _may_ get automatically created on unpack that
1398-// contains updated hardware-specific boot assets (such as initrd, kernel)
1399-const assetsDir = "assets"
1400-
1401-// Directory that _may_ get automatically created on unpack that
1402-// contains updated hardware-specific assets that require flashing
1403-// to the disk (such as uBoot, MLO)
1404-const flashAssetsDir = "flashtool-assets"
1405-
1406-// MountOption represents how the partition should be mounted, currently
1407-// RO (read-only) and RW (read-write) are supported
1408-type MountOption int
1409-
1410-const (
1411- // RO mounts the partition read-only
1412- RO MountOption = iota
1413- // RW mounts the partition read-only
1414- RW
1415 )
1416
1417 // Interface provides the interface to interact with a partition
1418@@ -128,29 +80,7 @@
1419
1420 // run the function f with the otherRoot mounted
1421 RunWithOther(rw MountOption, f func(otherRoot string) (err error)) (err error)
1422-
1423- // Returns the full path to the (mounted and writable)
1424- // bootloader-specific boot directory.
1425- BootloaderDir() string
1426-}
1427-
1428-// mountEntry represents a mount this package has created.
1429-type mountEntry struct {
1430- source string
1431- target string
1432-
1433- options string
1434-
1435- // true if target refers to a bind mount. We could derive this
1436- // from options, but this field saves the effort.
1437- bindMount bool
1438-}
1439-
1440-// mountEntryArray represents an array of mountEntry objects.
1441-type mountEntryArray []mountEntry
1442-
1443-// current mounts that this package has created.
1444-var mounts mountEntryArray
1445+}
1446
1447 // Partition is the type to interact with the partition
1448 type Partition struct {
1449@@ -159,14 +89,15 @@
1450
1451 // just root partitions
1452 roots []string
1453-
1454- hardwareSpecFile string
1455 }
1456
1457 type blockDevice struct {
1458 // label for partition
1459 name string
1460
1461+ // the last char of the partition label
1462+ shortName string
1463+
1464 // full path to device on which partition exists
1465 // (for example "/dev/sda3")
1466 device string
1467@@ -178,77 +109,10 @@
1468 mountpoint string
1469 }
1470
1471-// Representation of HARDWARE_SPEC_FILE
1472-type hardwareSpecType struct {
1473- Kernel string `yaml:"kernel"`
1474- Initrd string `yaml:"initrd"`
1475- DtbDir string `yaml:"dtbs"`
1476- PartitionLayout string `yaml:"partition-layout"`
1477- Bootloader bootloaderName `yaml:"bootloader"`
1478-}
1479-
1480-// Len is part of the sort interface, required to allow sort to work
1481-// with an array of Mount objects.
1482-func (mounts mountEntryArray) Len() int {
1483- return len(mounts)
1484-}
1485-
1486-// Less is part of the sort interface, required to allow sort to work
1487-// with an array of Mount objects.
1488-func (mounts mountEntryArray) Less(i, j int) bool {
1489- return mounts[i].target < mounts[j].target
1490-}
1491-
1492-// Swap is part of the sort interface, required to allow sort to work
1493-// with an array of Mount objects.
1494-func (mounts mountEntryArray) Swap(i, j int) {
1495- mounts[i], mounts[j] = mounts[j], mounts[i]
1496-}
1497-
1498-// removeMountByTarget removes the Mount specified by the target from
1499-// the global mounts array.
1500-func removeMountByTarget(mnts mountEntryArray, target string) (results mountEntryArray) {
1501-
1502- for _, m := range mnts {
1503- if m.target != target {
1504- results = append(results, m)
1505- }
1506- }
1507-
1508- return results
1509-}
1510+var once sync.Once
1511
1512 func init() {
1513- if !signalHandlerRegistered {
1514- setupSignalHandler()
1515- signalHandlerRegistered = true
1516- }
1517-}
1518-
1519-// undoMounts unmounts all mounts this package has mounted optionally
1520-// only unmounting bind mounts and leaving all remaining mounts.
1521-func undoMounts(bindMountsOnly bool) error {
1522-
1523- mountsCopy := make(mountEntryArray, len(mounts), cap(mounts))
1524- copy(mountsCopy, mounts)
1525-
1526- // reverse sort to ensure unmounts are handled in the correct
1527- // order.
1528- sort.Sort(sort.Reverse(mountsCopy))
1529-
1530- // Iterate backwards since we want a reverse-sorted list of
1531- // mounts to ensure we can unmount in order.
1532- for _, mount := range mountsCopy {
1533- if bindMountsOnly && !mount.bindMount {
1534- continue
1535- }
1536-
1537- if err := unmountAndRemoveFromGlobalMountList(mount.target); err != nil {
1538- return err
1539- }
1540- }
1541-
1542- return nil
1543+ once.Do(setupSignalHandler)
1544 }
1545
1546 func signalHandler(sig os.Signal) {
1547@@ -291,66 +155,6 @@
1548 return labels
1549 }
1550
1551-func mount(source, target, options string) (err error) {
1552- var args []string
1553-
1554- args = append(args, "/bin/mount")
1555- if options != "" {
1556- args = append(args, fmt.Sprintf("-o%s", options))
1557- }
1558-
1559- args = append(args, source)
1560- args = append(args, target)
1561-
1562- return runCommand(args...)
1563-}
1564-
1565-// Mount the given directory and add it to the global mounts slice
1566-func mountAndAddToGlobalMountList(m mountEntry) (err error) {
1567-
1568- err = mount(m.source, m.target, m.options)
1569- if err == nil {
1570- mounts = append(mounts, m)
1571- }
1572-
1573- return err
1574-}
1575-
1576-// Unmount the given directory and remove it from the global "mounts" slice
1577-func unmountAndRemoveFromGlobalMountList(target string) (err error) {
1578- err = runCommand("/bin/umount", target)
1579- if err != nil {
1580- return err
1581-
1582- }
1583-
1584- results := removeMountByTarget(mounts, target)
1585-
1586- // Update global
1587- mounts = results
1588-
1589- return nil
1590-}
1591-
1592-// Run fsck(8) on specified device.
1593-func fsck(device string) (err error) {
1594- return runCommand(
1595- "/sbin/fsck",
1596- "-M", // Paranoia - don't fsck if already mounted
1597- "-av", device)
1598-}
1599-
1600-// Returns the position of the string in the given slice or -1 if its not found
1601-func stringInSlice(slice []string, value string) int {
1602- for i, s := range slice {
1603- if s == value {
1604- return i
1605- }
1606- }
1607-
1608- return -1
1609-}
1610-
1611 var runLsblk = func() (out []string, err error) {
1612 output, err := runCommandWithStdout(
1613 "/bin/lsblk",
1614@@ -430,8 +234,10 @@
1615 continue
1616 }
1617 */
1618+ shortName := string(name[len(name)-1])
1619 bd := blockDevice{
1620 name: fields["LABEL"],
1621+ shortName: shortName,
1622 device: device,
1623 mountpoint: fields["MOUNTPOINT"],
1624 parentName: disk,
1625@@ -443,20 +249,11 @@
1626 return partitions, nil
1627 }
1628
1629-var makeDirectory = func(path string, mode os.FileMode) error {
1630- return os.MkdirAll(path, mode)
1631-}
1632-
1633-func (p *Partition) makeMountPoint() (err error) {
1634- return makeDirectory(p.MountTarget(), dirMode)
1635-}
1636-
1637 // New creates a new partition type
1638 func New() *Partition {
1639 p := new(Partition)
1640
1641 p.getPartitionDetails()
1642- p.hardwareSpecFile = filepath.Join(p.cacheDir(), hardwareSpecFile)
1643
1644 return p
1645 }
1646@@ -500,7 +297,7 @@
1647 }()
1648 }
1649
1650- err = f(p.MountTarget())
1651+ err = f(mountTarget)
1652 return err
1653 }
1654
1655@@ -529,7 +326,8 @@
1656 return err
1657 }
1658
1659- return bootloader.MarkCurrentBootSuccessful()
1660+ currentRootfs := p.rootPartition().shortName
1661+ return bootloader.MarkCurrentBootSuccessful(currentRootfs)
1662 }
1663
1664 // IsNextBootOther return true if the next boot will use the other rootfs
1665@@ -539,48 +337,27 @@
1666 if err != nil {
1667 return false
1668 }
1669- return isNextBootOther(bootloader)
1670-}
1671-
1672-// Returns the full path to the cache directory, which is used as a
1673-// scratch pad, for downloading new images to and bind mounting the
1674-// rootfs.
1675-func (p *Partition) cacheDir() string {
1676- return defaultCacheDir
1677-}
1678-
1679-func (p *Partition) hardwareSpec() (hardwareSpecType, error) {
1680- h := hardwareSpecType{}
1681-
1682- data, err := ioutil.ReadFile(p.hardwareSpecFile)
1683- // if hardware.yaml does not exist it just means that there was no
1684- // device part in the update.
1685- if os.IsNotExist(err) {
1686- return h, ErrNoHardwareYaml
1687- } else if err != nil {
1688- return h, err
1689- }
1690-
1691- if err := yaml.Unmarshal([]byte(data), &h); err != nil {
1692- return h, err
1693- }
1694-
1695- return h, nil
1696-}
1697-
1698-// Return full path to the main assets directory
1699-func (p *Partition) assetsDir() string {
1700- return filepath.Join(p.cacheDir(), assetsDir)
1701-}
1702-
1703-// Return the full path to the hardware-specific flash assets directory.
1704-func (p *Partition) flashAssetsDir() string {
1705- return filepath.Join(p.cacheDir(), flashAssetsDir)
1706-}
1707-
1708-// MountTarget gets the full path to the mount target directory
1709-func (p *Partition) MountTarget() string {
1710- return filepath.Join(p.cacheDir(), mountTarget)
1711+
1712+ value, err := bootloader.GetBootVar(bootloaderBootmodeVar)
1713+ if err != nil {
1714+ return false
1715+ }
1716+
1717+ if value != bootloaderBootmodeTry {
1718+ return false
1719+ }
1720+
1721+ fsname, err := bootloader.GetNextBootRootFSName()
1722+ if err != nil {
1723+ return false
1724+ }
1725+
1726+ otherRootfs := p.otherRootPartition().shortName
1727+ if fsname == otherRootfs {
1728+ return true
1729+ }
1730+
1731+ return false
1732 }
1733
1734 func (p *Partition) getPartitionDetails() (err error) {
1735@@ -676,11 +453,13 @@
1736 func (p *Partition) mountOtherRootfs(readOnly bool) (err error) {
1737 var other *blockDevice
1738
1739- p.makeMountPoint()
1740+ if err := os.MkdirAll(mountTarget, dirMode); err != nil {
1741+ return err
1742+ }
1743
1744 other = p.otherRootPartition()
1745
1746- m := mountEntry{source: other.device, target: p.MountTarget()}
1747+ m := mountEntry{source: other.device, target: mountTarget}
1748
1749 if readOnly {
1750 m.options = "ro"
1751@@ -707,9 +486,7 @@
1752
1753 // Ensure the other partition is mounted read-only.
1754 func (p *Partition) ensureOtherMountedRO() (err error) {
1755- mountpoint := p.MountTarget()
1756-
1757- if err = runCommand("/bin/mountpoint", mountpoint); err == nil {
1758+ if err = runCommand("/bin/mountpoint", mountTarget); err == nil {
1759 // already mounted
1760 return err
1761 }
1762@@ -741,14 +518,14 @@
1763
1764 return mountAndAddToGlobalMountList(mountEntry{
1765 source: other.device,
1766- target: p.MountTarget()})
1767+ target: mountTarget})
1768 }
1769 // r/w -> r/o: no fsck required.
1770- return mount(other.device, p.MountTarget(), "remount,ro")
1771+ return mount(other.device, mountTarget, "remount,ro")
1772 }
1773
1774 func (p *Partition) unmountOtherRootfs() (err error) {
1775- return unmountAndRemoveFromGlobalMountList(p.MountTarget())
1776+ return unmountAndRemoveFromGlobalMountList(mountTarget)
1777 }
1778
1779 // The bootloader requires a few filesystems to be mounted when
1780@@ -773,7 +550,7 @@
1781 }
1782
1783 for _, fs := range requiredChrootMounts {
1784- target := filepath.Join(p.MountTarget(), fs)
1785+ target := filepath.Join(mountTarget, fs)
1786
1787 err := mountAndAddToGlobalMountList(mountEntry{source: fs,
1788 target: target,
1789@@ -787,11 +564,11 @@
1790 // Grub also requires access to both rootfs's when run from
1791 // within a chroot (to allow it to create menu entries for
1792 // both), so bindmount the real rootfs.
1793- targetInChroot := filepath.Join(p.MountTarget(), p.MountTarget())
1794+ targetInChroot := filepath.Join(mountTarget, mountTarget)
1795
1796 // FIXME: we should really remove this after the unmount
1797
1798- if err = makeDirectory(targetInChroot, dirMode); err != nil {
1799+ if err = os.MkdirAll(targetInChroot, dirMode); err != nil {
1800 return err
1801 }
1802
1803@@ -822,7 +599,8 @@
1804 // wrong given that handleAssets may fails and we will
1805 // knowingly boot into a broken system
1806 err = p.RunWithOther(RW, func(otherRoot string) (err error) {
1807- return bootloader.ToggleRootFS()
1808+ otherRootfs := p.otherRootPartition().shortName
1809+ return bootloader.ToggleRootFS(otherRootfs)
1810 })
1811
1812 if err != nil {
1813@@ -831,14 +609,3 @@
1814
1815 return bootloader.HandleAssets()
1816 }
1817-
1818-// BootloaderDir returns the full path to the (mounted and writable)
1819-// bootloader-specific boot directory.
1820-func (p *Partition) BootloaderDir() string {
1821- bootloader, err := bootloader(p)
1822- if err != nil {
1823- return ""
1824- }
1825-
1826- return bootloader.BootDir()
1827-}
1828
1829=== modified file 'partition/partition_test.go'
1830--- partition/partition_test.go 2015-06-09 17:43:20 +0000
1831+++ partition/partition_test.go 2015-06-25 19:16:26 +0000
1832@@ -44,14 +44,13 @@
1833 return err
1834 }
1835
1836-func mockMakeDirectory(path string, mode os.FileMode) error {
1837- return nil
1838-}
1839-
1840 func (s *PartitionTestSuite) SetUpTest(c *C) {
1841 s.tempdir = c.MkDir()
1842 runLsblk = mockRunLsblkDualSnappy
1843
1844+ // custom mount target
1845+ mountTarget = c.MkDir()
1846+
1847 // setup fake paths for grub
1848 bootloaderGrubDir = filepath.Join(s.tempdir, "boot", "grub")
1849 bootloaderGrubConfigFile = filepath.Join(bootloaderGrubDir, "grub.cfg")
1850@@ -72,8 +71,10 @@
1851
1852 // always restore what we might have mocked away
1853 runCommand = runCommandImpl
1854- defaultCacheDir = realDefaultCacheDir
1855 bootloader = bootloaderImpl
1856+ cacheDir = cacheDirReal
1857+ hardwareSpecFile = hardwareSpecFileReal
1858+ mountTarget = mountTargetReal
1859
1860 // grub vars
1861 bootloaderGrubConfigFile = bootloaderGrubConfigFileReal
1862@@ -109,20 +110,6 @@
1863 return tmp.Name()
1864 }
1865
1866-func (s *PartitionTestSuite) TestHardwareSpec(c *C) {
1867- p := New()
1868- c.Assert(p, NotNil)
1869-
1870- p.hardwareSpecFile = makeHardwareYaml(c, "")
1871- hw, err := p.hardwareSpec()
1872- c.Assert(err, IsNil)
1873- c.Assert(hw.Kernel, Equals, "assets/vmlinuz")
1874- c.Assert(hw.Initrd, Equals, "assets/initrd.img")
1875- c.Assert(hw.DtbDir, Equals, "assets/dtbs")
1876- c.Assert(hw.PartitionLayout, Equals, bootloaderSystemAB)
1877- c.Assert(hw.Bootloader, Equals, bootloaderNameUboot)
1878-}
1879-
1880 func mockRunLsblkDualSnappy() (output []string, err error) {
1881 dualData := `
1882 NAME="sda" LABEL="" PKNAME="" MOUNTPOINT=""
1883@@ -136,46 +123,6 @@
1884 return strings.Split(dualData, "\n"), err
1885 }
1886
1887-func (s *PartitionTestSuite) TestMountEntryArray(c *C) {
1888- mea := mountEntryArray{}
1889-
1890- c.Assert(mea.Len(), Equals, 0)
1891-
1892- me := mountEntry{source: "/dev",
1893- target: "/dev",
1894- options: "bind",
1895- bindMount: true}
1896-
1897- mea = append(mea, me)
1898- c.Assert(mea.Len(), Equals, 1)
1899-
1900- me = mountEntry{source: "/foo",
1901- target: "/foo",
1902- options: "",
1903- bindMount: false}
1904-
1905- mea = append(mea, me)
1906- c.Assert(mea.Len(), Equals, 2)
1907-
1908- c.Assert(mea.Less(0, 1), Equals, true)
1909- c.Assert(mea.Less(1, 0), Equals, false)
1910-
1911- mea.Swap(0, 1)
1912- c.Assert(mea.Less(0, 1), Equals, false)
1913- c.Assert(mea.Less(1, 0), Equals, true)
1914-
1915- results := removeMountByTarget(mea, "invalid")
1916-
1917- // No change expected
1918- c.Assert(results, DeepEquals, mea)
1919-
1920- results = removeMountByTarget(mea, "/dev")
1921-
1922- c.Assert(len(results), Equals, 1)
1923- c.Assert(results[0], Equals, mountEntry{source: "/foo",
1924- target: "/foo", options: "", bindMount: false})
1925-}
1926-
1927 func (s *PartitionTestSuite) TestSnappyDualRoot(c *C) {
1928 p := New()
1929 c.Assert(p.dualRootPartitions(), Equals, true)
1930@@ -218,14 +165,13 @@
1931 return nil
1932 })
1933 c.Assert(err, IsNil)
1934- c.Assert(reportedRoot, Equals, (&Partition{}).MountTarget())
1935+ c.Assert(reportedRoot, Equals, mountTarget)
1936 }
1937
1938 func (s *PartitionTestSuite) TestRunWithOtherDualParitionRWFuncErr(c *C) {
1939 c.Assert(mounts, DeepEquals, mountEntryArray(nil))
1940
1941 runCommand = mockRunCommand
1942- makeDirectory = mockMakeDirectory
1943
1944 p := New()
1945 err := p.RunWithOther(RW, func(otherRoot string) (err error) {
1946@@ -239,8 +185,12 @@
1947 // ensure cleanup happend
1948
1949 // FIXME: mounts are global
1950- expected := mountEntry{source: "/dev/sda4",
1951- target: "/writable/cache/system", options: "", bindMount: false}
1952+ expected := mountEntry{
1953+ source: "/dev/sda4",
1954+ target: mountTarget,
1955+ options: "",
1956+ bindMount: false,
1957+ }
1958
1959 // At program exit, "other" should still be mounted
1960 c.Assert(mounts, DeepEquals, mountEntryArray{expected})
1961@@ -295,8 +245,12 @@
1962 c.Assert(p, NotNil)
1963
1964 p.mountOtherRootfs(false)
1965- expected := mountEntry{source: "/dev/sda4",
1966- target: "/writable/cache/system", options: "", bindMount: false}
1967+ expected := mountEntry{
1968+ source: "/dev/sda4",
1969+ target: mountTarget,
1970+ options: "",
1971+ bindMount: false,
1972+ }
1973
1974 c.Assert(mounts, DeepEquals, mountEntryArray{expected})
1975
1976@@ -313,26 +267,26 @@
1977
1978 p.bindmountRequiredFilesystems()
1979 c.Assert(mounts, DeepEquals, mountEntryArray{
1980- mountEntry{source: "/dev", target: p.MountTarget() + "/dev",
1981- options: "bind", bindMount: true},
1982-
1983- mountEntry{source: "/proc", target: p.MountTarget() + "/proc",
1984- options: "bind", bindMount: true},
1985-
1986- mountEntry{source: "/sys", target: p.MountTarget() + "/sys",
1987- options: "bind", bindMount: true},
1988- mountEntry{source: "/boot/efi", target: p.MountTarget() + "/boot/efi",
1989+ mountEntry{source: "/dev", target: mountTarget + "/dev",
1990+ options: "bind", bindMount: true},
1991+
1992+ mountEntry{source: "/proc", target: mountTarget + "/proc",
1993+ options: "bind", bindMount: true},
1994+
1995+ mountEntry{source: "/sys", target: mountTarget + "/sys",
1996+ options: "bind", bindMount: true},
1997+ mountEntry{source: "/boot/efi", target: mountTarget + "/boot/efi",
1998 options: "bind", bindMount: true},
1999
2000 // this comes from the grub bootloader via AdditionalBindMounts
2001- mountEntry{source: "/boot/grub", target: p.MountTarget() + "/boot/grub",
2002+ mountEntry{source: "/boot/grub", target: mountTarget + "/boot/grub",
2003 options: "bind", bindMount: true},
2004
2005 // Required to allow grub inside the chroot to access
2006 // the "current" rootfs outside the chroot (used
2007 // to generate the grub menuitems).
2008 mountEntry{source: "/",
2009- target: p.MountTarget() + p.MountTarget(),
2010+ target: mountTarget + mountTarget,
2011 options: "bind,ro", bindMount: true},
2012 })
2013 p.unmountRequiredFilesystems()
2014@@ -351,23 +305,23 @@
2015 p.bindmountRequiredFilesystems()
2016 c.Assert(mounts, DeepEquals, mountEntryArray{
2017
2018- mountEntry{source: "/dev/sda4", target: "/writable/cache/system",
2019+ mountEntry{source: "/dev/sda4", target: mountTarget,
2020 options: "", bindMount: false},
2021
2022- mountEntry{source: "/dev", target: p.MountTarget() + "/dev",
2023- options: "bind", bindMount: true},
2024-
2025- mountEntry{source: "/proc", target: p.MountTarget() + "/proc",
2026- options: "bind", bindMount: true},
2027-
2028- mountEntry{source: "/sys", target: p.MountTarget() + "/sys",
2029- options: "bind", bindMount: true},
2030-
2031- mountEntry{source: "/boot/efi", target: p.MountTarget() + "/boot/efi",
2032+ mountEntry{source: "/dev", target: mountTarget + "/dev",
2033+ options: "bind", bindMount: true},
2034+
2035+ mountEntry{source: "/proc", target: mountTarget + "/proc",
2036+ options: "bind", bindMount: true},
2037+
2038+ mountEntry{source: "/sys", target: mountTarget + "/sys",
2039+ options: "bind", bindMount: true},
2040+
2041+ mountEntry{source: "/boot/efi", target: mountTarget + "/boot/efi",
2042 options: "bind", bindMount: true},
2043
2044 mountEntry{source: "/",
2045- target: p.MountTarget() + p.MountTarget(),
2046+ target: mountTarget + mountTarget,
2047 options: "bind,ro", bindMount: true},
2048 })
2049
2050@@ -375,8 +329,12 @@
2051 undoMounts(true)
2052
2053 c.Assert(mounts, DeepEquals, mountEntryArray{
2054- mountEntry{source: "/dev/sda4", target: "/writable/cache/system",
2055- options: "", bindMount: false},
2056+ mountEntry{
2057+ source: "/dev/sda4",
2058+ target: mountTarget,
2059+ options: "",
2060+ bindMount: false,
2061+ },
2062 })
2063
2064 // should unmount everything
2065@@ -421,7 +379,7 @@
2066 func (b *mockBootloader) Name() bootloaderName {
2067 return "mocky"
2068 }
2069-func (b *mockBootloader) ToggleRootFS() error {
2070+func (b *mockBootloader) ToggleRootFS(otherRootfs string) error {
2071 b.ToggleRootFSCalled = true
2072 return nil
2073 }
2074@@ -436,16 +394,10 @@
2075 func (b *mockBootloader) GetBootVar(name string) (string, error) {
2076 return "", nil
2077 }
2078-func (b *mockBootloader) GetRootFSName() string {
2079- return ""
2080-}
2081-func (b *mockBootloader) GetOtherRootFSName() string {
2082- return ""
2083-}
2084 func (b *mockBootloader) GetNextBootRootFSName() (string, error) {
2085 return "", nil
2086 }
2087-func (b *mockBootloader) MarkCurrentBootSuccessful() error {
2088+func (b *mockBootloader) MarkCurrentBootSuccessful(currentRootfs string) error {
2089 b.MarkCurrentBootSuccessfulCalled = true
2090 return nil
2091 }
2092
2093=== modified file 'partition/utils.go'
2094--- partition/utils.go 2015-05-19 14:09:19 +0000
2095+++ partition/utils.go 2015-06-25 19:16:26 +0000
2096@@ -69,3 +69,22 @@
2097
2098 // This is a var instead of a function to making mocking in the tests easier
2099 var runCommandWithStdout = runCommandWithStdoutImpl
2100+
2101+// Run fsck(8) on specified device.
2102+func fsck(device string) (err error) {
2103+ return runCommand(
2104+ "/sbin/fsck",
2105+ "-M", // Paranoia - don't fsck if already mounted
2106+ "-av", device)
2107+}
2108+
2109+// Returns the position of the string in the given slice or -1 if its not found
2110+func stringInSlice(slice []string, value string) int {
2111+ for i, s := range slice {
2112+ if s == value {
2113+ return i
2114+ }
2115+ }
2116+
2117+ return -1
2118+}
2119
2120=== modified file 'snappy/errors.go'
2121--- snappy/errors.go 2015-06-04 22:21:51 +0000
2122+++ snappy/errors.go 2015-06-25 19:16:26 +0000
2123@@ -140,6 +140,8 @@
2124
2125 // ErrInvalidSeccompPolicy is returned when policy-version and policy-vender are not set together
2126 ErrInvalidSeccompPolicy = errors.New("policy-version and policy-vendor must be specified together")
2127+ // ErrNoSeccompPolicy is returned when an expected seccomp policy is not provided.
2128+ ErrNoSeccompPolicy = errors.New("no seccomp policy provided")
2129 )
2130
2131 // ErrArchitectureNotSupported is returned when trying to install a snappy package that
2132
2133=== modified file 'snappy/install.go'
2134--- snappy/install.go 2015-06-11 06:33:42 +0000
2135+++ snappy/install.go 2015-06-25 19:16:26 +0000
2136@@ -25,6 +25,7 @@
2137 "sort"
2138
2139 "launchpad.net/snappy/logger"
2140+ "launchpad.net/snappy/partition"
2141 "launchpad.net/snappy/progress"
2142 "launchpad.net/snappy/provisioning"
2143 )
2144@@ -96,8 +97,7 @@
2145 // FIXME: this is terrible, we really need a single
2146 // bootloader dir like /boot or /boot/loader
2147 // instead of having to query the partition code
2148- p := newPartition()
2149- if provisioning.InDeveloperMode(p.BootloaderDir()) {
2150+ if provisioning.InDeveloperMode(partition.BootloaderDir()) {
2151 flags |= AllowUnauthenticated
2152 }
2153
2154
2155=== modified file 'snappy/security.go'
2156--- snappy/security.go 2015-06-02 16:31:01 +0000
2157+++ snappy/security.go 2015-06-25 19:16:26 +0000
2158@@ -151,6 +151,11 @@
2159 syscalls := []string{}
2160
2161 if sd.SecurityOverride != nil {
2162+ if sd.SecurityOverride.Seccomp == "" {
2163+ logger.Noticef("No seccomp policy found")
2164+ return nil, ErrNoSeccompPolicy
2165+ }
2166+
2167 fn := filepath.Join(baseDir, sd.SecurityOverride.Seccomp)
2168 var s securitySeccompOverride
2169 err := readSeccompOverride(fn, &s)
2170
2171=== modified file 'snappy/security_test.go'
2172--- snappy/security_test.go 2015-06-08 16:26:25 +0000
2173+++ snappy/security_test.go 2015-06-25 19:16:26 +0000
2174@@ -58,6 +58,13 @@
2175 c.Assert(string(content), Equals, expected)
2176 }
2177
2178+func (a *SecurityTestSuite) TestSnappyNoSeccompOverrideEntry(c *C) {
2179+ sd := SecurityDefinitions{SecurityOverride: &SecurityOverrideDefinition{}}
2180+
2181+ _, err := generateSeccompPolicy(c.MkDir(), "appName", sd)
2182+ c.Assert(err, Equals, ErrNoSeccompPolicy)
2183+}
2184+
2185 // no special security settings generate the default
2186 func (a *SecurityTestSuite) TestSnappyHandleApparmorSecurityDefault(c *C) {
2187 sec := &SecurityDefinitions{}
2188
2189=== modified file 'snappy/systemimage.go'
2190--- snappy/systemimage.go 2015-06-10 13:38:01 +0000
2191+++ snappy/systemimage.go 2015-06-25 19:16:26 +0000
2192@@ -169,9 +169,16 @@
2193 return s.partition.ToggleNextBoot()
2194 }
2195
2196+// override in tests
2197+var bootloaderDir = bootloaderDirImpl
2198+
2199+func bootloaderDirImpl() string {
2200+ return partition.BootloaderDir()
2201+}
2202+
2203 // Install installs the snap
2204 func (s *SystemImagePart) Install(pb progress.Meter, flags InstallFlags) (name string, err error) {
2205- if provisioning.IsSideLoaded(s.partition.BootloaderDir()) {
2206+ if provisioning.IsSideLoaded(bootloaderDir()) {
2207 return "", ErrSideLoaded
2208 }
2209
2210
2211=== modified file 'snappy/systemimage_test.go'
2212--- snappy/systemimage_test.go 2015-06-09 17:43:20 +0000
2213+++ snappy/systemimage_test.go 2015-06-25 19:16:26 +0000
2214@@ -28,7 +28,7 @@
2215 "strings"
2216 "testing"
2217
2218- partition "launchpad.net/snappy/partition"
2219+ "launchpad.net/snappy/partition"
2220 "launchpad.net/snappy/provisioning"
2221
2222 . "gopkg.in/check.v1"
2223@@ -72,6 +72,7 @@
2224 func (s *SITestSuite) TearDownTest(c *C) {
2225 s.mockSystemImageWebServer.Close()
2226 systemImageRoot = "/"
2227+ bootloaderDir = bootloaderDirImpl
2228 }
2229
2230 func makeMockSystemImageCli(c *C, tempdir string) string {
2231@@ -390,18 +391,16 @@
2232 device-part: /some/path/file.tgz
2233 developer-mode: true
2234 `
2235- tempBootDir, err = ioutil.TempDir("", "")
2236- c.Assert(err, IsNil)
2237-
2238+ tempBootDir := c.MkDir()
2239 parts, err := s.systemImage.Updates()
2240
2241 sp := parts[0].(*SystemImagePart)
2242 mockPartition := MockPartition{}
2243 sp.partition = &mockPartition
2244
2245- bootDir := sp.partition.BootloaderDir()
2246+ bootloaderDir = func() string { return tempBootDir }
2247
2248- sideLoaded := filepath.Join(bootDir, provisioning.InstallYamlFile)
2249+ sideLoaded := filepath.Join(tempBootDir, provisioning.InstallYamlFile)
2250
2251 err = os.MkdirAll(filepath.Dir(sideLoaded), 0775)
2252 c.Assert(err, IsNil)
2253@@ -414,7 +413,4 @@
2254 // Ensure the install fails if the system is sideloaded
2255 _, err = sp.Install(pb, 0)
2256 c.Assert(err, Equals, ErrSideLoaded)
2257-
2258- os.Remove(tempBootDir)
2259- tempBootDir = ""
2260 }

Subscribers

People subscribed via source and target branches

to all changes: