Merge lp:~elopio/snappy/fix1496515-go_vet_missing_return into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/snappy/fix1496515-go_vet_missing_return
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Diff against target: 6958 lines (+5442/-67) (has conflicts)
75 files modified
_integration-tests/README.md (+117/-0)
_integration-tests/data/snaps/basic/meta/package.yaml (+4/-0)
_integration-tests/data/snaps/basic/meta/readme.md (+3/-0)
_integration-tests/data/snaps/missing-readme/meta/package.yaml (+4/-0)
_integration-tests/data/snaps/wrong-yaml/meta/package.yaml (+5/-0)
_integration-tests/data/snaps/wrong-yaml/meta/readme.md (+3/-0)
_integration-tests/data/tpl/control (+4/-0)
_integration-tests/main.go (+105/-0)
_integration-tests/reboot-wrapper (+35/-0)
_integration-tests/tests/apt_test.go (+39/-0)
_integration-tests/tests/base_test.go (+29/-0)
_integration-tests/tests/build_test.go (+90/-0)
_integration-tests/tests/failover_rclocal_crash_test.go (+54/-0)
_integration-tests/tests/failover_systemd_loop_test.go (+114/-0)
_integration-tests/tests/failover_test.go (+58/-0)
_integration-tests/tests/failover_zero_size_file_test.go (+258/-0)
_integration-tests/tests/info_test.go (+79/-0)
_integration-tests/tests/installApp_test.go (+128/-0)
_integration-tests/tests/installFramework_test.go (+93/-0)
_integration-tests/tests/list_test.go (+78/-0)
_integration-tests/tests/rollback_test.go (+53/-0)
_integration-tests/tests/search_test.go (+44/-0)
_integration-tests/tests/update_test.go (+51/-0)
_integration-tests/tests/writablePaths_test.go (+106/-0)
_integration-tests/testutils/autopkgtest/autopkgtest.go (+111/-0)
_integration-tests/testutils/autopkgtest/ssh.go (+46/-0)
_integration-tests/testutils/build/build.go (+76/-0)
_integration-tests/testutils/common/common.go (+379/-0)
_integration-tests/testutils/common/common_test.go (+68/-0)
_integration-tests/testutils/config/config.go (+75/-0)
_integration-tests/testutils/config/config_test.go (+121/-0)
_integration-tests/testutils/image/image.go (+86/-0)
_integration-tests/testutils/testutils.go (+61/-0)
cmd/snappy/cmd_internal_unpack.go (+4/-0)
cmd/snappy/cmd_internal_unpack_test.go (+20/-0)
cmd/snappy/cmd_rollback.go (+9/-0)
cmd/snappy/cmd_set.go (+14/-0)
cmd/snappy/cmd_set_test.go (+18/-1)
cmd/snappy/main.go (+4/-0)
coreconfig/config_test.go (+38/-0)
debian/control (+9/-0)
debian/rules (+20/-0)
debian/snappy-wait4network.service (+10/-0)
dependencies.tsv (+8/-0)
etc/grub.d/09_snappy (+441/-0)
helpers/helpers.go (+170/-8)
helpers/helpers_test.go (+104/-0)
logger/logger_test.go (+331/-0)
partition/bootloader_grub.go (+20/-0)
partition/bootloader_grub_test.go (+5/-0)
partition/bootloader_uboot.go (+343/-0)
partition/bootloader_uboot_test.go (+90/-0)
partition/partition.go (+20/-0)
run-checks (+19/-0)
snappy/auth_test.go (+12/-12)
snappy/build_test.go (+5/-0)
snappy/click.go (+353/-1)
snappy/click_test.go (+119/-2)
snappy/common_test.go (+7/-1)
snappy/dirs.go (+9/-0)
snappy/errors.go (+41/-0)
snappy/firstboot_test.go (+4/-0)
snappy/install_test.go (+5/-0)
snappy/parts.go (+47/-1)
snappy/remove_test.go (+4/-0)
snappy/rollback_test.go (+4/-0)
snappy/security.go (+7/-0)
snappy/security_test.go (+4/-0)
snappy/snapp.go (+127/-2)
snappy/snapp_test.go (+146/-17)
snappy/sort_test.go (+4/-0)
snappy/systemimage.go (+36/-0)
snappy/systemimage_test.go (+58/-0)
snappy/udev_test.go.OTHER (+49/-0)
systemd/systemd.go (+127/-22)
Conflict adding file _integration-tests.  Moved existing file to _integration-tests.moved.
Text conflict in cmd/snappy/cmd_internal_unpack.go
Text conflict in cmd/snappy/cmd_internal_unpack_test.go
Text conflict in cmd/snappy/cmd_set.go
Text conflict in cmd/snappy/cmd_set_test.go
Text conflict in cmd/snappy/main.go
Text conflict in coreconfig/config_test.go
Text conflict in debian/control
Text conflict in debian/rules
Conflict adding file debian/snappy-wait4network.service.  Moved existing file to debian/snappy-wait4network.service.moved.
Text conflict in dependencies.tsv
Conflict adding files to etc.  Created directory.
Conflict because etc is not versioned, but has versioned children.  Versioned directory.
Conflict adding file etc.  Moved existing file to etc.moved.
Conflict adding files to etc/grub.d.  Created directory.
Conflict because etc/grub.d is not versioned, but has versioned children.  Versioned directory.
Text conflict in helpers/helpers.go
Text conflict in helpers/helpers_test.go
Conflict adding files to logger.  Created directory.
Conflict because logger is not versioned, but has versioned children.  Versioned directory.
Conflict adding file logger.  Moved existing file to logger.moved.
Text conflict in partition/bootloader_grub.go
Text conflict in partition/bootloader_grub_test.go
Text conflict in partition/bootloader_uboot.go
Text conflict in partition/partition.go
Text conflict in run-checks
Text conflict in snappy/build_test.go
Text conflict in snappy/click.go
Text conflict in snappy/click_test.go
Text conflict in snappy/common_test.go
Text conflict in snappy/dirs.go
Text conflict in snappy/errors.go
Text conflict in snappy/firstboot_test.go
Text conflict in snappy/install_test.go
Text conflict in snappy/parts.go
Text conflict in snappy/remove_test.go
Text conflict in snappy/rollback_test.go
Text conflict in snappy/security.go
Text conflict in snappy/security_test.go
Text conflict in snappy/snapp.go
Text conflict in snappy/snapp_test.go
Text conflict in snappy/sort_test.go
Text conflict in snappy/systemimage.go
Contents conflict in snappy/udev_test.go
Text conflict in systemd/systemd.go
To merge this branch: bzr merge lp:~elopio/snappy/fix1496515-go_vet_missing_return
Reviewer Review Type Date Requested Status
Snappy Developers Pending
Review via email: mp+271351@code.launchpad.net

This proposal has been superseded by a proposal from 2015-09-16.

Commit message

Fixed the vet check: added the missing return of the error in rsync helper.

To post a comment you must log in.

Unmerged revisions

480. By Leo Arias

Fixed the vet check: added the missing return of the error in rsync helper.

479. By Michael Vogt

reorganizes the ubuntu-core network config by mvo approved by mvo,sergiusens

478. By Michael Vogt

Add support for "forking" by mvo approved by mvo,chipaca

477. By Michael Vogt

Fix off-by-one error in the path handlng by mvo approved by sergiusens

476. By Michael Vogt

Enable watchdog configuration via ubuntu-core-config by mvo approved by mvo

475. By Michael Vogt

Add configuration for ppp via ubuntu-core-config by mvo approved by mvo

474. By Michael Vogt

Unblock network configuration with pass-through networking suppport by mvo approved by mvo

473. By Michael Vogt

Add support for ubuntu-core.modprobe configuration. by mvo approved by mvo,chipaca

472. By Michael Vogt

Fix typo (USer -> User) by mvo approved by sergiusens

471. By Michael Vogt

snappy/hwaccess.go: make valid gpio access broader by mvo approved by sergiusens

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '_integration-tests'
2=== renamed directory '_integration-tests' => '_integration-tests.moved'
3=== added file '_integration-tests/README.md'
4--- _integration-tests/README.md 1970-01-01 00:00:00 +0000
5+++ _integration-tests/README.md 2015-09-16 17:51:11 +0000
6@@ -0,0 +1,117 @@
7+# Integration testing for snappy
8+
9+## Requirements
10+
11+ * autopkgtest (>= 3.15.1)
12+
13+ Get the latest autopkgtest deb from
14+ https://packages.debian.org/sid/all/autopkgtest/download
15+
16+ * Internet access in the test bed.
17+
18+## Setting up the project
19+
20+First you need to set up the GOPATH, get the snappy sources and the
21+dependencies as explained in the `README.md` that is located at the root of the
22+branch.
23+
24+## Testing a virtual machine
25+
26+You can execute the full integration suite in a local virtual machine with:
27+
28+ go run _integration-tests/main.go
29+
30+The test runner will create the snappy images with `ubuntu-device-flash`, so it
31+will ask for your password to run this command with `sudo`.
32+
33+You can also especify more options to customize the image being created, including
34+the release, the channel and the revision to use. This parameters will be passed
35+to `ubuntu-device-flash`:
36+
37+ go run _integration-tests/main.go -release 15.04 -channel stable -revision 3
38+
39+The default values are suited to testing the most recent version, `rolling` for
40+release, `edge` for channel and an empty revision, which picks the latest
41+available.
42+
43+## Testing snappy from a branch
44+
45+With the --snappy-from-branch flag, the snappy CLI command will be compiled
46+from the current branch, copied to the test bed and used during the integration
47+tests:
48+
49+ go run _integration-tests/main.go --snappy-from-branch
50+
51+You can use this flag to test in a remote machine too.
52+
53+## Filtering the tests to run
54+
55+With the --filter flag you can select the tests to run. For instance you can
56+pass MyTestSuite, MyTestSuite.FirstCustomTest or MyTestSuite.*CustomTest:
57+
58+ go run _integration-tests/main.go --filter MyTestSuite.FirstCustomTest
59+
60+## Testing a remote machine
61+
62+You can execute the integration suite in a remote snappy machine with:
63+
64+ go run _integration-tests/main.go --ip {testbed-ip} --port {testbed-port} \
65+ --arch {testbed-arch}
66+
67+The test runner will use `ssh-copy-id` to send your identity file to the
68+testbed, so it will ask for the password of the ubuntu user in the test bed.
69+
70+When running in a remote machine, the test runner assumes the test bed is in
71+the latest rolling edge version, and it will skip all the tests that
72+require a different version. See the following section for instructions for
73+setting up a BeagleBone Black as the test bed.
74+
75+## Testing a BeagleBone Black
76+
77+First flash the latest 15.04 edge version into the sd card
78+(replacing /dev/sdX with the path to your card):
79+
80+ sudo ubuntu-device-flash core 15.04 --channel edge --oem beagleblack \
81+ --developer-mode --enable-ssh -o ubuntu-15.04-edge-armhf-bbb.img
82+
83+ sudo dd if=ubuntu-15.04-edge-armhf-bbb.img of=/dev/sdX bs=32M
84+ sync
85+
86+Then boot the board with the sd card, make sure that it is connected to the
87+same network as the test runner host, and find the {beaglebone-ip}.
88+
89+Run the tests with:
90+
91+ go run _integration-tests/main.go --ip {beaglebone-ip} --arch arm
92+
93+## Testing an update
94+
95+With the --update flag you can flash an old image, update to the latest and
96+then run the whole suite on the updated system. The release, the channel and
97+the revision flags specify the image that will be flashed, and the
98+target-release and target-channel flags specify the values to be used in the
99+update if they are different from the flashed values.
100+
101+For example, to update from 15.04 edge -1 to the latest and then run the
102+integration tests:
103+
104+ go run _integration-tests/main.go --snappy-from-branch \
105+ --release=15.04 --revision=-1 --update
106+
107+To update from 15.04 alpha to 15.04 edge and then run the integration tests:
108+
109+ go run _integration-tests/main.go --snappy-from-branch \
110+ --release=15.04 --channel=alpha \
111+ --update --target-channel=edge
112+
113+## Testing a rollback
114+
115+With the --rollback flag you can flash an old image, update to the latest,
116+rollback again to the old image and then run the whole suite on the rolled
117+back system. You should use the release, channel, revision, target-release and
118+target-channel flags as when testing an update.
119+
120+For example, to test a rollback from latest 15.04 edge to 15.04 edge -1:
121+
122+ go run _integration-tests/main.go \
123+ --release=15.04 --revision=-1 --rollback
124
125=== added directory '_integration-tests/data'
126=== added directory '_integration-tests/data/snaps'
127=== added directory '_integration-tests/data/snaps/basic'
128=== added directory '_integration-tests/data/snaps/basic/meta'
129=== added file '_integration-tests/data/snaps/basic/meta/package.yaml'
130--- _integration-tests/data/snaps/basic/meta/package.yaml 1970-01-01 00:00:00 +0000
131+++ _integration-tests/data/snaps/basic/meta/package.yaml 2015-09-16 17:51:11 +0000
132@@ -0,0 +1,4 @@
133+name: basic
134+version: 1.0
135+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
136+icon: meta/snappy64.png
137
138=== added file '_integration-tests/data/snaps/basic/meta/readme.md'
139--- _integration-tests/data/snaps/basic/meta/readme.md 1970-01-01 00:00:00 +0000
140+++ _integration-tests/data/snaps/basic/meta/readme.md 2015-09-16 17:51:11 +0000
141@@ -0,0 +1,3 @@
142+Basic snap
143+
144+A basic buildable snap
145
146=== added file '_integration-tests/data/snaps/basic/meta/snappy64.png'
147Binary files _integration-tests/data/snaps/basic/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/basic/meta/snappy64.png 2015-09-16 17:51:11 +0000 differ
148=== added directory '_integration-tests/data/snaps/missing-readme'
149=== added directory '_integration-tests/data/snaps/missing-readme/meta'
150=== added file '_integration-tests/data/snaps/missing-readme/meta/package.yaml'
151--- _integration-tests/data/snaps/missing-readme/meta/package.yaml 1970-01-01 00:00:00 +0000
152+++ _integration-tests/data/snaps/missing-readme/meta/package.yaml 2015-09-16 17:51:11 +0000
153@@ -0,0 +1,4 @@
154+name: missing-readme
155+version: 1.0
156+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
157+icon: meta/snappy64.png
158
159=== added file '_integration-tests/data/snaps/missing-readme/meta/snappy64.png'
160Binary files _integration-tests/data/snaps/missing-readme/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/missing-readme/meta/snappy64.png 2015-09-16 17:51:11 +0000 differ
161=== added directory '_integration-tests/data/snaps/wrong-yaml'
162=== added directory '_integration-tests/data/snaps/wrong-yaml/meta'
163=== added file '_integration-tests/data/snaps/wrong-yaml/meta/package.yaml'
164--- _integration-tests/data/snaps/wrong-yaml/meta/package.yaml 1970-01-01 00:00:00 +0000
165+++ _integration-tests/data/snaps/wrong-yaml/meta/package.yaml 2015-09-16 17:51:11 +0000
166@@ -0,0 +1,5 @@
167+# This is an invalid yaml, there's a missing colon after the 'name' field
168+name wrong-yaml
169+version: 1.0
170+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
171+icon: meta/snappy64.png
172
173=== added file '_integration-tests/data/snaps/wrong-yaml/meta/readme.md'
174--- _integration-tests/data/snaps/wrong-yaml/meta/readme.md 1970-01-01 00:00:00 +0000
175+++ _integration-tests/data/snaps/wrong-yaml/meta/readme.md 2015-09-16 17:51:11 +0000
176@@ -0,0 +1,3 @@
177+Wrong metadata snap
178+
179+A snap with an invalid meta/package.yaml
180
181=== added file '_integration-tests/data/snaps/wrong-yaml/meta/snappy64.png'
182Binary files _integration-tests/data/snaps/wrong-yaml/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/wrong-yaml/meta/snappy64.png 2015-09-16 17:51:11 +0000 differ
183=== added directory '_integration-tests/data/tpl'
184=== added file '_integration-tests/data/tpl/control'
185--- _integration-tests/data/tpl/control 1970-01-01 00:00:00 +0000
186+++ _integration-tests/data/tpl/control 2015-09-16 17:51:11 +0000
187@@ -0,0 +1,4 @@
188+{{ $filter := .Filter }}
189+{{ $test := .Test }}
190+Test-Command: ./_integration-tests/reboot-wrapper {{ $test }} {{ if $filter }}-gocheck.f {{ $filter }}{{ end }}
191+Restrictions: allow-stderr
192
193=== added file '_integration-tests/main.go'
194--- _integration-tests/main.go 1970-01-01 00:00:00 +0000
195+++ _integration-tests/main.go 2015-09-16 17:51:11 +0000
196@@ -0,0 +1,105 @@
197+// -*- Mode: Go; indent-tabs-mode: t -*-
198+
199+/*
200+ * Copyright (C) 2015 Canonical Ltd
201+ *
202+ * This program is free software: you can redistribute it and/or modify
203+ * it under the terms of the GNU General Public License version 3 as
204+ * published by the Free Software Foundation.
205+ *
206+ * This program is distributed in the hope that it will be useful,
207+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
208+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
209+ * GNU General Public License for more details.
210+ *
211+ * You should have received a copy of the GNU General Public License
212+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
213+ *
214+ */
215+
216+package main
217+
218+import (
219+ "flag"
220+ "log"
221+ "os"
222+ "path/filepath"
223+ "strconv"
224+
225+ "launchpad.net/snappy/_integration-tests/testutils"
226+ "launchpad.net/snappy/_integration-tests/testutils/autopkgtest"
227+ "launchpad.net/snappy/_integration-tests/testutils/build"
228+ "launchpad.net/snappy/_integration-tests/testutils/config"
229+ "launchpad.net/snappy/_integration-tests/testutils/image"
230+)
231+
232+const (
233+ baseDir = "/tmp/snappy-test"
234+ defaultRelease = "rolling"
235+ defaultChannel = "edge"
236+ defaultSSHPort = 22
237+ dataOutputDir = "_integration-tests/data/output/"
238+)
239+
240+var configFileName = filepath.Join(dataOutputDir, "testconfig.json")
241+
242+func main() {
243+ var (
244+ useSnappyFromBranch = flag.Bool("snappy-from-branch", false,
245+ "If this flag is used, snappy will be compiled from this branch, copied to the testbed and used for the tests. Otherwise, the snappy installed with the image will be used.")
246+ arch = flag.String("arch", "",
247+ "Architecture of the test bed. Defaults to use the same architecture as the host.")
248+ testbedIP = flag.String("ip", "",
249+ "IP of the testbed. If no IP is passed, a virtual machine will be created for the test.")
250+ testbedPort = flag.Int("port", defaultSSHPort,
251+ "SSH port of the testbed. Defaults to use port "+strconv.Itoa(defaultSSHPort))
252+ testFilter = flag.String("filter", "",
253+ "Suites or tests to run, for instance MyTestSuite, MyTestSuite.FirstCustomTest or MyTestSuite.*CustomTest")
254+ imgRelease = flag.String("release", defaultRelease,
255+ "Release of the image to be built, defaults to "+defaultRelease)
256+ imgChannel = flag.String("channel", defaultChannel,
257+ "Channel of the image to be built, defaults to "+defaultChannel)
258+ imgRevision = flag.String("revision", "",
259+ "Revision of the image to be built (can be relative to the latest available revision in the given release and channel as in -1), defaults to the empty string")
260+ update = flag.Bool("update", false,
261+ "If this flag is used, the image will be updated before running the tests.")
262+ targetRelease = flag.String("target-release", "",
263+ "If the update flag is used, the image will be updated to this release before running the tests.")
264+ targetChannel = flag.String("target-channel", "",
265+ "If the update flag is used, the image will be updated to this channel before running the tests.")
266+ rollback = flag.Bool("rollback", false,
267+ "If this flag is used, the image will be updated and then rolled back before running the tests.")
268+ )
269+
270+ flag.Parse()
271+
272+ build.Assets(*useSnappyFromBranch, *arch)
273+
274+ // TODO: generate the files out of the source tree. --elopio - 2015-07-15
275+ testutils.PrepareTargetDir(dataOutputDir)
276+ defer os.RemoveAll(dataOutputDir)
277+
278+ remoteTestbed := *testbedIP != ""
279+
280+ // TODO: pass the config as arguments to the test binaries.
281+ // --elopio - 2015-07-15
282+ cfg := config.NewConfig(
283+ configFileName, *imgRelease, *imgChannel, *targetRelease, *targetChannel,
284+ remoteTestbed, *update, *rollback)
285+ cfg.Write()
286+
287+ rootPath := testutils.RootPath()
288+
289+ test := autopkgtest.NewAutopkgtest(rootPath, baseDir, *testFilter, build.IntegrationTestName)
290+ if !remoteTestbed {
291+ img := image.NewImage(*imgRelease, *imgChannel, *imgRevision, baseDir)
292+
293+ if imagePath, err := img.UdfCreate(); err == nil {
294+ test.AdtRunLocal(imagePath)
295+ } else {
296+ log.Panic(err.Error())
297+ }
298+ } else {
299+ test.AdtRunRemote(*testbedIP, *testbedPort)
300+ }
301+}
302
303=== added file '_integration-tests/reboot-wrapper'
304--- _integration-tests/reboot-wrapper 1970-01-01 00:00:00 +0000
305+++ _integration-tests/reboot-wrapper 2015-09-16 17:51:11 +0000
306@@ -0,0 +1,35 @@
307+#!/bin/sh
308+
309+# Copyright (C) 2015 Canonical Ltd
310+#
311+# This program is free software: you can redistribute it and/or modify
312+# it under the terms of the GNU General Public License version 3 as
313+# published by the Free Software Foundation.
314+#
315+# This program is distributed in the hope that it will be useful,
316+# but WITHOUT ANY WARRANTY; without even the implied warranty of
317+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
318+# GNU General Public License for more details.
319+#
320+# You should have received a copy of the GNU General Public License
321+# along with this program. If not, see <http://www.gnu.org/licenses/>.
322+
323+# This runs the $1 go test binary and reboots if necessary.
324+
325+set -e
326+
327+TEST=$1
328+NEEDS_REBOOT=/tmp/needs-reboot
329+
330+export PATH=$(pwd)/_integration-tests/bin:$PATH
331+
332+# shift to remove the test binary name (first argument) and be able to pass the rest
333+# of them to it
334+shift
335+${TEST} -check.vv -test.outputdir=$ADT_ARTIFACTS-$ADT_REBOOT_MARK "$@"
336+
337+if [ -e ${NEEDS_REBOOT} ]; then
338+ mark=`cat ${NEEDS_REBOOT}`
339+ echo "Rebooting..."
340+ sudo /tmp/autopkgtest-reboot "$mark"
341+fi
342
343=== added directory '_integration-tests/tests'
344=== added file '_integration-tests/tests/apt_test.go'
345--- _integration-tests/tests/apt_test.go 1970-01-01 00:00:00 +0000
346+++ _integration-tests/tests/apt_test.go 2015-09-16 17:51:11 +0000
347@@ -0,0 +1,39 @@
348+// -*- Mode: Go; indent-tabs-mode: t -*-
349+
350+/*
351+ * Copyright (C) 2015 Canonical Ltd
352+ *
353+ * This program is free software: you can redistribute it and/or modify
354+ * it under the terms of the GNU General Public License version 3 as
355+ * published by the Free Software Foundation.
356+ *
357+ * This program is distributed in the hope that it will be useful,
358+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
359+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
360+ * GNU General Public License for more details.
361+ *
362+ * You should have received a copy of the GNU General Public License
363+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
364+ *
365+ */
366+
367+package tests
368+
369+import (
370+ . "launchpad.net/snappy/_integration-tests/testutils/common"
371+
372+ check "gopkg.in/check.v1"
373+)
374+
375+var _ = check.Suite(&aptSuite{})
376+
377+type aptSuite struct {
378+ SnappySuite
379+}
380+
381+func (s *aptSuite) TestAptGetMustPrintError(c *check.C) {
382+ aptOutput := ExecCommand(c, "apt-get", "update")
383+
384+ expected := "Ubuntu Core does not use apt-get, see 'snappy --help'!\n"
385+ c.Assert(aptOutput, check.Equals, expected)
386+}
387
388=== added file '_integration-tests/tests/base_test.go'
389--- _integration-tests/tests/base_test.go 1970-01-01 00:00:00 +0000
390+++ _integration-tests/tests/base_test.go 2015-09-16 17:51:11 +0000
391@@ -0,0 +1,29 @@
392+// -*- Mode: Go; indent-tabs-mode: t -*-
393+
394+/*
395+ * Copyright (C) 2015 Canonical Ltd
396+ *
397+ * This program is free software: you can redistribute it and/or modify
398+ * it under the terms of the GNU General Public License version 3 as
399+ * published by the Free Software Foundation.
400+ *
401+ * This program is distributed in the hope that it will be useful,
402+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
403+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
404+ * GNU General Public License for more details.
405+ *
406+ * You should have received a copy of the GNU General Public License
407+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
408+ *
409+ */
410+
411+package tests
412+
413+import (
414+ "testing"
415+
416+ . "gopkg.in/check.v1"
417+)
418+
419+// Hook up gocheck into the "go test" runner.
420+func Test(t *testing.T) { TestingT(t) }
421
422=== added file '_integration-tests/tests/build_test.go'
423--- _integration-tests/tests/build_test.go 1970-01-01 00:00:00 +0000
424+++ _integration-tests/tests/build_test.go 2015-09-16 17:51:11 +0000
425@@ -0,0 +1,90 @@
426+// -*- Mode: Go; indent-tabs-mode: t -*-
427+
428+/*
429+ * Copyright (C) 2015 Canonical Ltd
430+ *
431+ * This program is free software: you can redistribute it and/or modify
432+ * it under the terms of the GNU General Public License version 3 as
433+ * published by the Free Software Foundation.
434+ *
435+ * This program is distributed in the hope that it will be useful,
436+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
437+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
438+ * GNU General Public License for more details.
439+ *
440+ * You should have received a copy of the GNU General Public License
441+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
442+ *
443+ */
444+
445+package tests
446+
447+import (
448+ "fmt"
449+ "os"
450+ "os/exec"
451+
452+ . "launchpad.net/snappy/_integration-tests/testutils/common"
453+
454+ . "gopkg.in/check.v1"
455+)
456+
457+const (
458+ baseSnapPath = "_integration-tests/data/snaps"
459+ basicSnapName = "basic"
460+ wrongYamlSnapName = "wrong-yaml"
461+ missingReadmeSnapName = "missing-readme"
462+)
463+
464+var _ = Suite(&buildSuite{})
465+
466+type buildSuite struct {
467+ SnappySuite
468+}
469+
470+func buildSnap(c *C, snapPath string) string {
471+ return ExecCommand(c, "snappy", "build", snapPath)
472+}
473+
474+func (s *buildSuite) TestBuildBasicSnapOnSnappy(c *C) {
475+ // build basic snap and check output
476+ snapPath := baseSnapPath + "/" + basicSnapName
477+ buildOutput := buildSnap(c, snapPath)
478+ snapName := basicSnapName + "_1.0_all.snap"
479+ expected := fmt.Sprintf("Generated '%s' snap\n", snapName)
480+ c.Check(buildOutput, Equals, expected)
481+ defer os.Remove(snapPath + "/" + snapName)
482+
483+ // install built snap and check output
484+ installOutput := InstallSnap(c, snapName)
485+ defer RemoveSnap(c, basicSnapName)
486+ expected = "(?ms)" +
487+ "Installing " + snapName + "\n" +
488+ ".*Signature check failed, but installing anyway as requested\n" +
489+ "Name +Date +Version +Developer \n" +
490+ ".*\n" +
491+ basicSnapName + " +.* +.* +sideload \n" +
492+ ".*\n"
493+
494+ c.Check(installOutput, Matches, expected)
495+
496+ // teardown, remove snap file
497+ c.Assert(os.Remove(snapName), IsNil, Commentf("Error removing %s", snapName))
498+}
499+
500+func (s *buildSuite) TestBuildWrongYamlSnapOnSnappy(c *C) {
501+ commonWrongTest(c, wrongYamlSnapName, "(?msi).*Can not parse.*yaml: line 2: mapping values are not allowed in this context.*")
502+}
503+
504+func (s *buildSuite) TestBuildMissingReadmeSnapOnSnappy(c *C) {
505+ commonWrongTest(c, missingReadmeSnapName, ".*readme.md: no such file or directory\n")
506+}
507+
508+func commonWrongTest(c *C, testName, expected string) {
509+ // build wrong snap and check output
510+ cmd := exec.Command("snappy", "build", fmt.Sprintf("%s/%s", baseSnapPath, testName))
511+ echoOutput, err := cmd.CombinedOutput()
512+ c.Assert(err, NotNil, Commentf("%s should not be built", testName))
513+
514+ c.Assert(string(echoOutput), Matches, expected)
515+}
516
517=== added file '_integration-tests/tests/failover_rclocal_crash_test.go'
518--- _integration-tests/tests/failover_rclocal_crash_test.go 1970-01-01 00:00:00 +0000
519+++ _integration-tests/tests/failover_rclocal_crash_test.go 2015-09-16 17:51:11 +0000
520@@ -0,0 +1,54 @@
521+// -*- Mode: Go; indent-tabs-mode: t -*-
522+
523+/*
524+ * Copyright (C) 2015 Canonical Ltd
525+ *
526+ * This program is free software: you can redistribute it and/or modify
527+ * it under the terms of the GNU General Public License version 3 as
528+ * published by the Free Software Foundation.
529+ *
530+ * This program is distributed in the hope that it will be useful,
531+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
532+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
533+ * GNU General Public License for more details.
534+ *
535+ * You should have received a copy of the GNU General Public License
536+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
537+ *
538+ */
539+
540+package tests
541+
542+import (
543+ "fmt"
544+
545+ . "launchpad.net/snappy/_integration-tests/testutils/common"
546+
547+ check "gopkg.in/check.v1"
548+)
549+
550+type rcLocalCrash struct{}
551+
552+func (rcLocalCrash) set(c *check.C) {
553+ MakeWritable(c, BaseAltPartitionPath)
554+ defer MakeReadonly(c, BaseAltPartitionPath)
555+ targetFile := fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath)
556+ ExecCommand(c, "sudo", "chmod", "a+xw", targetFile)
557+ ExecCommandToFile(c, targetFile,
558+ "sudo", "echo", "#!bin/sh\nprintf c > /proc/sysrq-trigger")
559+}
560+
561+func (rcLocalCrash) unset(c *check.C) {
562+ MakeWritable(c, BaseAltPartitionPath)
563+ defer MakeReadonly(c, BaseAltPartitionPath)
564+ ExecCommand(c, "sudo", "rm", fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath))
565+}
566+
567+/*
568+TODO: uncomment when bug https://bugs.launchpad.net/snappy/+bug/1476129 is fixed
569+(fgimenez 20150728)
570+
571+func (s *failoverSuite) TestRCLocalCrash(c *check.C) {
572+ commonFailoverTest(c, rcLocalCrash{})
573+}
574+*/
575
576=== added file '_integration-tests/tests/failover_systemd_loop_test.go'
577--- _integration-tests/tests/failover_systemd_loop_test.go 1970-01-01 00:00:00 +0000
578+++ _integration-tests/tests/failover_systemd_loop_test.go 2015-09-16 17:51:11 +0000
579@@ -0,0 +1,114 @@
580+// -*- Mode: Go; indent-tabs-mode: t -*-
581+
582+/*
583+ * Copyright (C) 2015 Canonical Ltd
584+ *
585+ * This program is free software: you can redistribute it and/or modify
586+ * it under the terms of the GNU General Public License version 3 as
587+ * published by the Free Software Foundation.
588+ *
589+ * This program is distributed in the hope that it will be useful,
590+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
591+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
592+ * GNU General Public License for more details.
593+ *
594+ * You should have received a copy of the GNU General Public License
595+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
596+ *
597+ */
598+
599+package tests
600+
601+import (
602+ "fmt"
603+
604+ . "launchpad.net/snappy/_integration-tests/testutils/common"
605+
606+ check "gopkg.in/check.v1"
607+)
608+
609+const (
610+ deadlockService = `[Unit]
611+Before=sysinit.target
612+DefaultDependencies=no
613+
614+[Service]
615+Type=oneshot
616+ExecStartPre=-/bin/sh -c "echo 'DEBUG: $(date): deadlocked system' >/dev/console"
617+ExecStartPre=-/bin/sh -c "echo 'DEBUG: $(date): deadlocked system' >/dev/ttyS0"
618+ExecStart=/bin/systemctl start deadlock.service
619+RemainAfterExit=yes
620+
621+[Install]
622+RequiredBy=sysinit.target
623+`
624+ rebootService = `[Unit]
625+DefaultDependencies=no
626+Description=Hack to force reboot if booting did not finish after 20s
627+
628+[Service]
629+Type=oneshot
630+ExecStartPre=/bin/sleep 20
631+ExecStart=-/bin/sh -c 'if ! systemctl is-active default.target; then wall "EMERGENCY REBOOT"; reboot -f; fi'
632+
633+[Install]
634+RequiredBy=sysinit.target
635+`
636+ baseSystemdPath = "/lib/systemd/system"
637+ systemdTargetRequiresDir = "sysinit.target.requires"
638+)
639+
640+type systemdDependencyLoop struct{}
641+
642+func (systemdDependencyLoop) set(c *check.C) {
643+ installService(c, "deadlock", deadlockService, BaseAltPartitionPath)
644+ installService(c, "emerg-reboot", rebootService, BaseAltPartitionPath)
645+}
646+
647+func (systemdDependencyLoop) unset(c *check.C) {
648+ unInstallService(c, "deadlock", BaseAltPartitionPath)
649+ unInstallService(c, "emerg-reboot", BaseAltPartitionPath)
650+}
651+
652+func installService(c *check.C, serviceName, serviceCfg, basePath string) {
653+ MakeWritable(c, basePath)
654+ defer MakeReadonly(c, basePath)
655+
656+ // Create service file
657+ serviceFile := fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName)
658+ ExecCommand(c, "sudo", "chmod", "a+w", fmt.Sprintf("%s%s", basePath, baseSystemdPath))
659+ ExecCommandToFile(c, serviceFile, "sudo", "echo", serviceCfg)
660+
661+ // Create requires directory
662+ requiresDirPart := fmt.Sprintf("%s/%s", baseSystemdPath, systemdTargetRequiresDir)
663+ requiresDir := fmt.Sprintf("%s%s", basePath, requiresDirPart)
664+ ExecCommand(c, "sudo", "mkdir", "-p", requiresDir)
665+
666+ // Symlink from the requires dir to the service file (with chroot for being
667+ // usable in the other partition)
668+ ExecCommand(c, "sudo", "chroot", basePath, "ln", "-s",
669+ fmt.Sprintf("%s/%s.service", baseSystemdPath, serviceName),
670+ fmt.Sprintf("%s/%s.service", requiresDirPart, serviceName),
671+ )
672+}
673+
674+func unInstallService(c *check.C, serviceName, basePath string) {
675+ MakeWritable(c, basePath)
676+ defer MakeReadonly(c, basePath)
677+
678+ // Disable the service
679+ ExecCommand(c, "sudo", "chroot", basePath,
680+ "systemctl", "disable", fmt.Sprintf("%s.service", serviceName))
681+
682+ // Remove the service file
683+ ExecCommand(c, "sudo", "rm",
684+ fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName))
685+
686+ // Remove the requires symlink
687+ ExecCommand(c, "sudo", "rm",
688+ fmt.Sprintf("%s%s/%s/%s.service", basePath, baseSystemdPath, systemdTargetRequiresDir, serviceName))
689+}
690+
691+func (s *failoverSuite) TestSystemdDependencyLoop(c *check.C) {
692+ commonFailoverTest(c, systemdDependencyLoop{})
693+}
694
695=== added file '_integration-tests/tests/failover_test.go'
696--- _integration-tests/tests/failover_test.go 1970-01-01 00:00:00 +0000
697+++ _integration-tests/tests/failover_test.go 2015-09-16 17:51:11 +0000
698@@ -0,0 +1,58 @@
699+// -*- Mode: Go; indent-tabs-mode: t -*-
700+
701+/*
702+ * Copyright (C) 2015 Canonical Ltd
703+ *
704+ * This program is free software: you can redistribute it and/or modify
705+ * it under the terms of the GNU General Public License version 3 as
706+ * published by the Free Software Foundation.
707+ *
708+ * This program is distributed in the hope that it will be useful,
709+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
710+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
711+ * GNU General Public License for more details.
712+ *
713+ * You should have received a copy of the GNU General Public License
714+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
715+ *
716+ */
717+
718+package tests
719+
720+import (
721+ check "gopkg.in/check.v1"
722+
723+ . "launchpad.net/snappy/_integration-tests/testutils/common"
724+)
725+
726+var _ = check.Suite(&failoverSuite{})
727+
728+type failoverSuite struct {
729+ SnappySuite
730+}
731+
732+// The types that implement this interface can be used in the test logic
733+type failer interface {
734+ // Sets the failure conditions
735+ set(c *check.C)
736+ // Unsets the failure conditions
737+ unset(c *check.C)
738+}
739+
740+// This is the logic common to all the failover tests. Each of them has define a
741+// type implementing the failer interface and call this function with an instance
742+// of it
743+func commonFailoverTest(c *check.C, f failer) {
744+ currentVersion := GetCurrentUbuntuCoreVersion(c)
745+
746+ if AfterReboot(c) {
747+ RemoveRebootMark(c)
748+ f.unset(c)
749+ c.Assert(GetSavedVersion(c), check.Equals, currentVersion)
750+ } else {
751+ SetSavedVersion(c, currentVersion-1)
752+ CallFakeUpdate(c)
753+ f.set(c)
754+ Reboot(c)
755+ }
756+}
757
758=== added file '_integration-tests/tests/failover_zero_size_file_test.go'
759--- _integration-tests/tests/failover_zero_size_file_test.go 1970-01-01 00:00:00 +0000
760+++ _integration-tests/tests/failover_zero_size_file_test.go 2015-09-16 17:51:11 +0000
761@@ -0,0 +1,258 @@
762+// -*- Mode: Go; indent-tabs-mode: t -*-
763+
764+/*
765+ * Copyright (C) 2015 Canonical Ltd
766+ *
767+ * This program is free software: you can redistribute it and/or modify
768+ * it under the terms of the GNU General Public License version 3 as
769+ * published by the Free Software Foundation.
770+ *
771+ * This program is distributed in the hope that it will be useful,
772+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
773+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
774+ * GNU General Public License for more details.
775+ *
776+ * You should have received a copy of the GNU General Public License
777+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
778+ *
779+ */
780+
781+package tests
782+
783+import (
784+ "bufio"
785+ "fmt"
786+ "os"
787+ "path/filepath"
788+ "strings"
789+
790+ . "launchpad.net/snappy/_integration-tests/testutils/common"
791+
792+ check "gopkg.in/check.v1"
793+)
794+
795+const (
796+ origBootFilenamePattern = "boot/%s%s*"
797+ origSystemdFilenamePattern = "lib/systemd/%s%s"
798+ kernelFilename = "vmlinuz"
799+ initrdFilename = "initrd"
800+ systemdFilename = "systemd"
801+ destFilenamePrefix = "snappy-selftest-"
802+ bootBase = "/boot"
803+ ubootDir = bootBase + "/uboot"
804+ grubDir = bootBase + "/grub"
805+ ubootConfigFile = ubootDir + "/snappy-system.txt"
806+ grubConfigFile = grubDir + "/grubenv"
807+)
808+
809+type zeroSizeKernel struct{}
810+type zeroSizeInitrd struct{}
811+type zeroSizeSystemd struct{}
812+
813+func (zeroSizeKernel) set(c *check.C) {
814+ commonSet(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
815+}
816+
817+func (zeroSizeKernel) unset(c *check.C) {
818+ commonUnset(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
819+}
820+
821+func (zeroSizeInitrd) set(c *check.C) {
822+ if classicKernelFiles(c) {
823+ commonSet(c, BaseAltPartitionPath, origBootFilenamePattern, initrdFilename)
824+ } else {
825+ boot := bootSystem(c)
826+ dir := bootDirectory(boot)
827+ bootFileNamePattern := newKernelFilenamePattern(c, boot, true)
828+ commonSet(c, dir, bootFileNamePattern, initrdFilename)
829+ }
830+}
831+
832+func (zeroSizeInitrd) unset(c *check.C) {
833+ if classicKernelFiles(c) {
834+ commonUnset(c, BaseAltPartitionPath, origBootFilenamePattern, initrdFilename)
835+ } else {
836+ boot := bootSystem(c)
837+ dir := bootDirectory(boot)
838+ bootFileNamePattern := newKernelFilenamePattern(c, boot, false)
839+ commonUnset(c, dir, bootFileNamePattern, initrdFilename)
840+ }
841+}
842+
843+func (zeroSizeSystemd) set(c *check.C) {
844+ commonSet(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
845+}
846+
847+func (zeroSizeSystemd) unset(c *check.C) {
848+ commonUnset(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
849+}
850+
851+func commonSet(c *check.C, baseOtherPath, origPattern, filename string) {
852+ filenamePattern := fmt.Sprintf(origPattern, "", filename)
853+ completePattern := filepath.Join(
854+ baseOtherPath,
855+ filenamePattern)
856+ oldFilename := getSingleFilename(c, completePattern)
857+ filenameSuffix := fmt.Sprintf(
858+ strings.Replace(origPattern, "*", "", 1), destFilenamePrefix, filepath.Base(oldFilename))
859+ newFilename := fmt.Sprintf(
860+ "%s/%s", baseOtherPath, filenameSuffix)
861+
862+ renameFile(c, baseOtherPath, oldFilename, newFilename, true)
863+}
864+
865+func commonUnset(c *check.C, baseOtherPath, origPattern, filename string) {
866+ completePattern := filepath.Join(
867+ baseOtherPath,
868+ fmt.Sprintf(origPattern, destFilenamePrefix, filename))
869+ oldFilename := getSingleFilename(c, completePattern)
870+ newFilename := strings.Replace(oldFilename, destFilenamePrefix, "", 1)
871+
872+ renameFile(c, baseOtherPath, oldFilename, newFilename, false)
873+}
874+
875+func renameFile(c *check.C, basePath, oldFilename, newFilename string, keepOld bool) {
876+ // Only need to make writable and revert for BaseAltPartitionPath,
877+ // kernel files' boot directory is writable
878+ if basePath == BaseAltPartitionPath {
879+ MakeWritable(c, basePath)
880+ defer MakeReadonly(c, basePath)
881+ }
882+
883+ ExecCommand(c, "sudo", "mv", oldFilename, newFilename)
884+
885+ if keepOld {
886+ ExecCommand(c, "sudo", "touch", oldFilename)
887+ mode := getFileMode(c, newFilename)
888+ ExecCommand(c, "sudo", "chmod", fmt.Sprintf("%o", mode), oldFilename)
889+ }
890+}
891+
892+func getFileMode(c *check.C, filePath string) os.FileMode {
893+ info, err := os.Stat(filePath)
894+ c.Check(err, check.IsNil, check.Commentf("Error getting Stat of %s", filePath))
895+
896+ return info.Mode()
897+}
898+
899+func getSingleFilename(c *check.C, pattern string) string {
900+ matches, err := filepath.Glob(pattern)
901+
902+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
903+ c.Assert(len(matches), check.Equals, 1,
904+ check.Commentf("%d files matching %s, 1 expected", len(matches), pattern))
905+
906+ return matches[0]
907+}
908+
909+func classicKernelFiles(c *check.C) bool {
910+ initrdClassicFilenamePattern := fmt.Sprintf("/boot/%s*-generic", initrdFilename)
911+ matches, err := filepath.Glob(initrdClassicFilenamePattern)
912+
913+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
914+
915+ return len(matches) == 1
916+}
917+
918+func bootSystem(c *check.C) string {
919+ matches, err := filepath.Glob(bootBase + "/grub")
920+
921+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", err))
922+
923+ if len(matches) == 1 {
924+ return "grub"
925+ }
926+ return "uboot"
927+}
928+
929+func bootDirectory(bootSystem string) string {
930+ if bootSystem == "grub" {
931+ return grubDir
932+ }
933+ return ubootDir
934+}
935+
936+func bootConfigFile(bootSystem string) string {
937+ if bootSystem == "grub" {
938+ return grubConfigFile
939+ }
940+ return ubootConfigFile
941+}
942+
943+func currentPartition(c *check.C, bootSystem string) (partition string) {
944+ bootConfigFile := bootConfigFile(bootSystem)
945+ file, err := os.Open(bootConfigFile)
946+
947+ c.Assert(err, check.IsNil,
948+ check.Commentf("Error reading boot config file %s", bootConfigFile))
949+
950+ defer file.Close()
951+
952+ reader := bufio.NewReader(file)
953+ scanner := bufio.NewScanner(reader)
954+
955+ scanner.Split(bufio.ScanLines)
956+
957+ for scanner.Scan() {
958+ if strings.HasPrefix(scanner.Text(), "snappy_ab") {
959+ fields := strings.Split(scanner.Text(), "=")
960+ if len(fields) > 1 {
961+ if bootSystem == "grub" {
962+ partition = fields[1]
963+ } else {
964+ partition = otherPart(fields[1])
965+ }
966+ }
967+ return
968+ }
969+ }
970+ return
971+}
972+
973+func otherPart(current string) string {
974+ if current == "a" {
975+ return "b"
976+ }
977+ return "a"
978+}
979+
980+// newKernelFilenamePattern returns the filename pattern to modify files
981+// in the partition declared in the boot config file.
982+//
983+// After the update, the config file is already changed to point to the new partition.
984+// If we are on a and update, the config file would point to b
985+// and this function would return "b/%s%s*"
986+// If we are not in an update process (ie. we are unsetting the failover conditions)
987+// we want to change the files in the other partition
988+func newKernelFilenamePattern(c *check.C, bootSystem string, afterUpdate bool) string {
989+ var actualPartition string
990+ partition := currentPartition(c, bootSystem)
991+ if afterUpdate {
992+ actualPartition = partition
993+ } else {
994+ actualPartition = otherPart(partition)
995+ }
996+ return filepath.Join(actualPartition, "%s%s*")
997+}
998+
999+/*
1000+TODO: uncomment when bug https://bugs.launchpad.net/snappy/+bug/1467553 is fixed
1001+(fgimenez 20150729)
1002+
1003+func (s *failoverSuite) TestZeroSizeKernel(c *check.C) {
1004+ commonFailoverTest(c, zeroSizeKernel{})
1005+}
1006+*/
1007+
1008+func (s *failoverSuite) TestZeroSizeInitrd(c *check.C) {
1009+ // Skip if on uboot due to https://bugs.launchpad.net/snappy/+bug/1480248
1010+ // (fgimenez 20150731)
1011+ if bootSystem(c) == "uboot" {
1012+ c.Skip("Failover for empty initrd not working in uboot")
1013+ }
1014+ commonFailoverTest(c, zeroSizeInitrd{})
1015+}
1016+
1017+func (s *failoverSuite) TestZeroSizeSystemd(c *check.C) {
1018+ commonFailoverTest(c, zeroSizeSystemd{})
1019+}
1020
1021=== added file '_integration-tests/tests/info_test.go'
1022--- _integration-tests/tests/info_test.go 1970-01-01 00:00:00 +0000
1023+++ _integration-tests/tests/info_test.go 2015-09-16 17:51:11 +0000
1024@@ -0,0 +1,79 @@
1025+// -*- Mode: Go; indent-tabs-mode: t -*-
1026+
1027+/*
1028+ * Copyright (C) 2015 Canonical Ltd
1029+ *
1030+ * This program is free software: you can redistribute it and/or modify
1031+ * it under the terms of the GNU General Public License version 3 as
1032+ * published by the Free Software Foundation.
1033+ *
1034+ * This program is distributed in the hope that it will be useful,
1035+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1036+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1037+ * GNU General Public License for more details.
1038+ *
1039+ * You should have received a copy of the GNU General Public License
1040+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1041+ *
1042+ */
1043+
1044+package tests
1045+
1046+import (
1047+ "fmt"
1048+
1049+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1050+
1051+ check "gopkg.in/check.v1"
1052+)
1053+
1054+var _ = check.Suite(&infoSuite{})
1055+
1056+type infoSuite struct {
1057+ SnappySuite
1058+}
1059+
1060+func (s *infoSuite) TestInfoMustPrintReleaseAndChannel(c *check.C) {
1061+ // skip test when having a remote testbed (we can't know which the
1062+ // release and channels are)
1063+ if Cfg.RemoteTestbed {
1064+ c.Skip(fmt.Sprintf(
1065+ "Skipping %s while testing in remote testbed",
1066+ c.TestName()))
1067+ }
1068+
1069+ infoOutput := ExecCommand(c, "snappy", "info")
1070+
1071+ expected := "(?ms)" +
1072+ fmt.Sprintf("^release: ubuntu-core/%s/%s\n", Cfg.Release, Cfg.Channel) +
1073+ ".*"
1074+
1075+ c.Assert(infoOutput, check.Matches, expected)
1076+}
1077+
1078+func (s *infoSuite) TestInfoMustPrintInstalledApps(c *check.C) {
1079+ InstallSnap(c, "hello-world")
1080+ s.AddCleanup(func() {
1081+ RemoveSnap(c, "hello-world")
1082+ })
1083+ infoOutput := ExecCommand(c, "snappy", "info")
1084+
1085+ expected := "(?ms)" +
1086+ ".*" +
1087+ "^apps: .*hello-world.*\n"
1088+ c.Assert(infoOutput, check.Matches, expected)
1089+}
1090+
1091+func (s *infoSuite) TestInfoMustPrintInstalledFrameworks(c *check.C) {
1092+ InstallSnap(c, "hello-dbus-fwk.canonical")
1093+ s.AddCleanup(func() {
1094+ RemoveSnap(c, "hello-dbus-fwk.canonical")
1095+ })
1096+ infoOutput := ExecCommand(c, "snappy", "info")
1097+
1098+ expected := "(?ms)" +
1099+ ".*" +
1100+ "^frameworks: .*hello-dbus-fwk.*\n" +
1101+ ".*"
1102+ c.Assert(infoOutput, check.Matches, expected)
1103+}
1104
1105=== added file '_integration-tests/tests/installApp_test.go'
1106--- _integration-tests/tests/installApp_test.go 1970-01-01 00:00:00 +0000
1107+++ _integration-tests/tests/installApp_test.go 2015-09-16 17:51:11 +0000
1108@@ -0,0 +1,128 @@
1109+// -*- Mode: Go; indent-tabs-mode: t -*-
1110+
1111+/*
1112+ * Copyright (C) 2015 Canonical Ltd
1113+ *
1114+ * This program is free software: you can redistribute it and/or modify
1115+ * it under the terms of the GNU General Public License version 3 as
1116+ * published by the Free Software Foundation.
1117+ *
1118+ * This program is distributed in the hope that it will be useful,
1119+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1120+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1121+ * GNU General Public License for more details.
1122+ *
1123+ * You should have received a copy of the GNU General Public License
1124+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1125+ *
1126+ */
1127+
1128+package tests
1129+
1130+import (
1131+ "fmt"
1132+ "net/http"
1133+ "os/exec"
1134+ "time"
1135+
1136+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1137+
1138+ check "gopkg.in/check.v1"
1139+)
1140+
1141+var _ = check.Suite(&installAppSuite{})
1142+
1143+type installAppSuite struct {
1144+ SnappySuite
1145+}
1146+
1147+func (s *installAppSuite) TestInstallAppMustPrintPackageInformation(c *check.C) {
1148+ installOutput := InstallSnap(c, "hello-world")
1149+ s.AddCleanup(func() {
1150+ RemoveSnap(c, "hello-world")
1151+ })
1152+
1153+ expected := "(?ms)" +
1154+ "Installing hello-world\n" +
1155+ "Name +Date +Version +Developer \n" +
1156+ ".*" +
1157+ "^hello-world +.* +.* +canonical \n" +
1158+ ".*"
1159+
1160+ c.Assert(installOutput, check.Matches, expected)
1161+}
1162+
1163+func (s *installAppSuite) TestCallBinaryFromInstalledSnap(c *check.C) {
1164+ InstallSnap(c, "hello-world")
1165+ s.AddCleanup(func() {
1166+ RemoveSnap(c, "hello-world")
1167+ })
1168+
1169+ echoOutput := ExecCommand(c, "hello-world.echo")
1170+
1171+ c.Assert(echoOutput, check.Equals, "Hello World!\n")
1172+}
1173+
1174+func (s *installAppSuite) TestCallBinaryWithPermissionDeniedMustPrintError(c *check.C) {
1175+ InstallSnap(c, "hello-world")
1176+ s.AddCleanup(func() {
1177+ RemoveSnap(c, "hello-world")
1178+ })
1179+
1180+ cmd := exec.Command("hello-world.evil")
1181+ echoOutput, err := cmd.CombinedOutput()
1182+ c.Assert(err, check.NotNil, check.Commentf("hello-world.evil did not fail"))
1183+
1184+ expected := "" +
1185+ "Hello Evil World!\n" +
1186+ "This example demonstrates the app confinement\n" +
1187+ "You should see a permission denied error next\n" +
1188+ "/apps/hello-world.canonical/.*/bin/evil: \\d+: " +
1189+ "/apps/hello-world.canonical/.*/bin/evil: " +
1190+ "cannot create /var/tmp/myevil.txt: Permission denied\n"
1191+
1192+ c.Assert(string(echoOutput), check.Matches, expected)
1193+}
1194+
1195+func (s *installAppSuite) TestInfoMustPrintInstalledPackageInformation(c *check.C) {
1196+ InstallSnap(c, "hello-world")
1197+ s.AddCleanup(func() {
1198+ RemoveSnap(c, "hello-world")
1199+ })
1200+
1201+ infoOutput := ExecCommand(c, "snappy", "info")
1202+
1203+ expected := "(?ms).*^apps: hello-world\n"
1204+ c.Assert(infoOutput, check.Matches, expected)
1205+}
1206+
1207+func (s *installAppSuite) TestAppNetworkingServiceMustBeStarted(c *check.C) {
1208+ baseAppName := "xkcd-webserver"
1209+ appName := baseAppName + ".canonical"
1210+ InstallSnap(c, appName)
1211+ s.AddCleanup(func() {
1212+ RemoveSnap(c, appName)
1213+ })
1214+
1215+ appVersion := GetCurrentVersion(c, baseAppName)
1216+ appService := fmt.Sprintf("%s_%s_%s.service", baseAppName, baseAppName, appVersion)
1217+
1218+ err := WaitForActiveService(c, appService)
1219+ c.Assert(err, check.IsNil)
1220+
1221+ time.Sleep(1 * time.Second)
1222+ resp, err := http.Get("http://localhost")
1223+ c.Assert(err, check.IsNil)
1224+ c.Check(resp.Status, check.Equals, "200 OK")
1225+ c.Assert(resp.Proto, check.Equals, "HTTP/1.0")
1226+}
1227+
1228+func (s *installAppSuite) TestInstallUnexistingAppMustPrintError(c *check.C) {
1229+ cmd := exec.Command("sudo", "snappy", "install", "unexisting.canonical")
1230+ output, err := cmd.CombinedOutput()
1231+
1232+ c.Assert(err, check.NotNil)
1233+ c.Assert(string(output), check.Equals,
1234+ "Installing unexisting.canonical\n"+
1235+ "unexisting.canonical failed to install: snappy package not found\n")
1236+}
1237
1238=== added file '_integration-tests/tests/installFramework_test.go'
1239--- _integration-tests/tests/installFramework_test.go 1970-01-01 00:00:00 +0000
1240+++ _integration-tests/tests/installFramework_test.go 2015-09-16 17:51:11 +0000
1241@@ -0,0 +1,93 @@
1242+// -*- Mode: Go; indent-tabs-mode: t -*-
1243+
1244+/*
1245+ * Copyright (C) 2015 Canonical Ltd
1246+ *
1247+ * This program is free software: you can redistribute it and/or modify
1248+ * it under the terms of the GNU General Public License version 3 as
1249+ * published by the Free Software Foundation.
1250+ *
1251+ * This program is distributed in the hope that it will be useful,
1252+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1253+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1254+ * GNU General Public License for more details.
1255+ *
1256+ * You should have received a copy of the GNU General Public License
1257+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1258+ *
1259+ */
1260+
1261+package tests
1262+
1263+import (
1264+ "fmt"
1265+ "regexp"
1266+
1267+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1268+
1269+ check "gopkg.in/check.v1"
1270+)
1271+
1272+var _ = check.Suite(&installFrameworkSuite{})
1273+
1274+type installFrameworkSuite struct {
1275+ SnappySuite
1276+}
1277+
1278+func (s *installFrameworkSuite) TearDownTest(c *check.C) {
1279+ if !NeedsReboot() && CheckRebootMark("") {
1280+ RemoveSnap(c, "docker")
1281+ }
1282+ // run cleanup last
1283+ s.SnappySuite.TearDownTest(c)
1284+}
1285+
1286+func isDockerServiceRunning(c *check.C) bool {
1287+ dockerVersion := GetCurrentVersion(c, "docker")
1288+ dockerService := fmt.Sprintf("docker_docker-daemon_%s.service", dockerVersion)
1289+
1290+ err := WaitForActiveService(c, dockerService)
1291+ c.Assert(err, check.IsNil)
1292+
1293+ statusOutput := ExecCommand(
1294+ c, "systemctl", "status",
1295+ dockerService)
1296+
1297+ expected := "(?ms)" +
1298+ ".* docker_docker-daemon_.*\\.service .*\n" +
1299+ ".*Loaded: loaded .*\n" +
1300+ ".*Active: active \\(running\\) .*\n" +
1301+ ".*"
1302+
1303+ matched, err := regexp.MatchString(expected, statusOutput)
1304+ c.Assert(err, check.IsNil)
1305+ return matched
1306+}
1307+
1308+func (s *installFrameworkSuite) TestInstallFrameworkMustPrintPackageInformation(c *check.C) {
1309+ installOutput := InstallSnap(c, "docker")
1310+
1311+ expected := "(?ms)" +
1312+ "Installing docker\n" +
1313+ "Name +Date +Version +Developer \n" +
1314+ ".*" +
1315+ "^docker +.* +.* +canonical \n" +
1316+ ".*"
1317+
1318+ c.Assert(installOutput, check.Matches, expected)
1319+}
1320+
1321+func (s *installFrameworkSuite) TestInstalledFrameworkServiceMustBeStarted(c *check.C) {
1322+ InstallSnap(c, "docker")
1323+ c.Assert(isDockerServiceRunning(c), check.Equals, true)
1324+}
1325+
1326+func (s *installFrameworkSuite) TestFrameworkServiceMustBeStartedAfterReboot(c *check.C) {
1327+ if BeforeReboot() {
1328+ InstallSnap(c, "docker")
1329+ Reboot(c)
1330+ } else if AfterReboot(c) {
1331+ RemoveRebootMark(c)
1332+ c.Assert(isDockerServiceRunning(c), check.Equals, true)
1333+ }
1334+}
1335
1336=== added file '_integration-tests/tests/list_test.go'
1337--- _integration-tests/tests/list_test.go 1970-01-01 00:00:00 +0000
1338+++ _integration-tests/tests/list_test.go 2015-09-16 17:51:11 +0000
1339@@ -0,0 +1,78 @@
1340+// -*- Mode: Go; indent-tabs-mode: t -*-
1341+
1342+/*
1343+ * Copyright (C) 2015 Canonical Ltd
1344+ *
1345+ * This program is free software: you can redistribute it and/or modify
1346+ * it under the terms of the GNU General Public License version 3 as
1347+ * published by the Free Software Foundation.
1348+ *
1349+ * This program is distributed in the hope that it will be useful,
1350+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1351+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1352+ * GNU General Public License for more details.
1353+ *
1354+ * You should have received a copy of the GNU General Public License
1355+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1356+ *
1357+ */
1358+
1359+package tests
1360+
1361+import (
1362+ "fmt"
1363+ "os"
1364+
1365+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1366+
1367+ "github.com/mvo5/goconfigparser"
1368+ check "gopkg.in/check.v1"
1369+)
1370+
1371+var _ = check.Suite(&listSuite{})
1372+
1373+type listSuite struct {
1374+ SnappySuite
1375+}
1376+
1377+func getVersionFromConfig(c *check.C) string {
1378+ cfg := goconfigparser.New()
1379+ f, err := os.Open("/etc/system-image/channel.ini")
1380+ c.Assert(err, check.IsNil,
1381+ check.Commentf("Error opening the config file: %v:", err))
1382+ defer f.Close()
1383+ err = cfg.Read(f)
1384+ c.Assert(err, check.IsNil,
1385+ check.Commentf("Error parsing the config file: %v", err))
1386+ version, err := cfg.Get("service", "build_number")
1387+ c.Assert(err, check.IsNil,
1388+ check.Commentf("Error getting the build number: %v", err))
1389+ return version
1390+}
1391+
1392+func (s *listSuite) TestListMustPrintCoreVersion(c *check.C) {
1393+ listOutput := ExecCommand(c, "snappy", "list")
1394+
1395+ expected := "(?ms)" +
1396+ "Name +Date +Version +Developer *\n" +
1397+ ".*" +
1398+ fmt.Sprintf("^ubuntu-core +.* +%s +ubuntu *\n", getVersionFromConfig(c)) +
1399+ ".*"
1400+ c.Assert(listOutput, check.Matches, expected)
1401+}
1402+
1403+func (s *listSuite) TestListMustPrintAppVersion(c *check.C) {
1404+ InstallSnap(c, "hello-world")
1405+ s.AddCleanup(func() {
1406+ RemoveSnap(c, "hello-world")
1407+ })
1408+
1409+ listOutput := ExecCommand(c, "snappy", "list")
1410+ expected := "(?ms)" +
1411+ "Name +Date +Version +Developer *\n" +
1412+ ".*" +
1413+ "^hello-world +.* +(\\d+)(\\.\\d+)* +.* +.* *\n" +
1414+ ".*"
1415+
1416+ c.Assert(listOutput, check.Matches, expected)
1417+}
1418
1419=== added file '_integration-tests/tests/rollback_test.go'
1420--- _integration-tests/tests/rollback_test.go 1970-01-01 00:00:00 +0000
1421+++ _integration-tests/tests/rollback_test.go 2015-09-16 17:51:11 +0000
1422@@ -0,0 +1,53 @@
1423+// -*- Mode: Go; indent-tabs-mode: t -*-
1424+
1425+/*
1426+ * Copyright (C) 2015 Canonical Ltd
1427+ *
1428+ * This program is free software: you can redistribute it and/or modify
1429+ * it under the terms of the GNU General Public License version 3 as
1430+ * published by the Free Software Foundation.
1431+ *
1432+ * This program is distributed in the hope that it will be useful,
1433+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1434+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1435+ * GNU General Public License for more details.
1436+ *
1437+ * You should have received a copy of the GNU General Public License
1438+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1439+ *
1440+ */
1441+
1442+package tests
1443+
1444+import (
1445+ "strconv"
1446+
1447+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1448+
1449+ check "gopkg.in/check.v1"
1450+)
1451+
1452+var _ = check.Suite(&rollbackSuite{})
1453+
1454+type rollbackSuite struct {
1455+ SnappySuite
1456+}
1457+
1458+func (s *rollbackSuite) TestRollbackMustRebootToOtherVersion(c *check.C) {
1459+ if BeforeReboot() {
1460+ CallFakeUpdate(c)
1461+ Reboot(c)
1462+ } else if CheckRebootMark(c.TestName()) {
1463+ RemoveRebootMark(c)
1464+ currentVersion := GetCurrentUbuntuCoreVersion(c)
1465+ c.Assert(currentVersion > GetSavedVersion(c), check.Equals, true)
1466+ ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core",
1467+ strconv.Itoa(GetSavedVersion(c)))
1468+ SetSavedVersion(c, currentVersion)
1469+ RebootWithMark(c, c.TestName()+"-rollback")
1470+ } else if CheckRebootMark(c.TestName() + "-rollback") {
1471+ RemoveRebootMark(c)
1472+ c.Assert(
1473+ GetCurrentUbuntuCoreVersion(c) < GetSavedVersion(c), check.Equals, true)
1474+ }
1475+}
1476
1477=== added file '_integration-tests/tests/search_test.go'
1478--- _integration-tests/tests/search_test.go 1970-01-01 00:00:00 +0000
1479+++ _integration-tests/tests/search_test.go 2015-09-16 17:51:11 +0000
1480@@ -0,0 +1,44 @@
1481+// -*- Mode: Go; indent-tabs-mode: t -*-
1482+
1483+/*
1484+ * Copyright (C) 2015 Canonical Ltd
1485+ *
1486+ * This program is free software: you can redistribute it and/or modify
1487+ * it under the terms of the GNU General Public License version 3 as
1488+ * published by the Free Software Foundation.
1489+ *
1490+ * This program is distributed in the hope that it will be useful,
1491+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1492+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1493+ * GNU General Public License for more details.
1494+ *
1495+ * You should have received a copy of the GNU General Public License
1496+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1497+ *
1498+ */
1499+
1500+package tests
1501+
1502+import (
1503+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1504+
1505+ . "gopkg.in/check.v1"
1506+)
1507+
1508+var _ = Suite(&searchSuite{})
1509+
1510+type searchSuite struct {
1511+ SnappySuite
1512+}
1513+
1514+func (s *searchSuite) TestSearchFrameworkMustPrintMatch(c *C) {
1515+ searchOutput := ExecCommand(c, "snappy", "search", "hello-dbus-fwk")
1516+
1517+ expected := "(?ms)" +
1518+ "Name +Version +Summary *\n" +
1519+ ".*" +
1520+ "^hello-dbus-fwk +.* +hello-dbus-fwk *\n" +
1521+ ".*"
1522+
1523+ c.Assert(searchOutput, Matches, expected)
1524+}
1525
1526=== added file '_integration-tests/tests/update_test.go'
1527--- _integration-tests/tests/update_test.go 1970-01-01 00:00:00 +0000
1528+++ _integration-tests/tests/update_test.go 2015-09-16 17:51:11 +0000
1529@@ -0,0 +1,51 @@
1530+// -*- Mode: Go; indent-tabs-mode: t -*-
1531+
1532+/*
1533+ * Copyright (C) 2015 Canonical Ltd
1534+ *
1535+ * This program is free software: you can redistribute it and/or modify
1536+ * it under the terms of the GNU General Public License version 3 as
1537+ * published by the Free Software Foundation.
1538+ *
1539+ * This program is distributed in the hope that it will be useful,
1540+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1541+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1542+ * GNU General Public License for more details.
1543+ *
1544+ * You should have received a copy of the GNU General Public License
1545+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1546+ *
1547+ */
1548+
1549+package tests
1550+
1551+import (
1552+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1553+
1554+ check "gopkg.in/check.v1"
1555+)
1556+
1557+var _ = check.Suite(&updateSuite{})
1558+
1559+type updateSuite struct {
1560+ SnappySuite
1561+}
1562+
1563+// Test that the update to the same release and channel must install a newer
1564+// version. If there is no update available, the channel version will be
1565+// modified to fake an update. If there is a version available, the image will
1566+// be up-to-date after running this test.
1567+func (s *updateSuite) TestUpdateToSameReleaseAndChannel(c *check.C) {
1568+ if BeforeReboot() {
1569+ updateOutput := CallFakeUpdate(c)
1570+ expected := "(?ms)" +
1571+ ".*" +
1572+ "^Reboot to use .*ubuntu-core.\n"
1573+ c.Assert(updateOutput, check.Matches, expected)
1574+ Reboot(c)
1575+ } else if AfterReboot(c) {
1576+ RemoveRebootMark(c)
1577+ c.Assert(GetCurrentUbuntuCoreVersion(c) > GetSavedVersion(c),
1578+ check.Equals, true)
1579+ }
1580+}
1581
1582=== added file '_integration-tests/tests/writablePaths_test.go'
1583--- _integration-tests/tests/writablePaths_test.go 1970-01-01 00:00:00 +0000
1584+++ _integration-tests/tests/writablePaths_test.go 2015-09-16 17:51:11 +0000
1585@@ -0,0 +1,106 @@
1586+// -*- Mode: Go; indent-tabs-mode: t -*-
1587+
1588+/*
1589+ * Copyright (C) 2015 Canonical Ltd
1590+ *
1591+ * This program is free software: you can redistribute it and/or modify
1592+ * it under the terms of the GNU General Public License version 3 as
1593+ * published by the Free Software Foundation.
1594+ *
1595+ * This program is distributed in the hope that it will be useful,
1596+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1597+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1598+ * GNU General Public License for more details.
1599+ *
1600+ * You should have received a copy of the GNU General Public License
1601+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1602+ *
1603+ */
1604+
1605+package tests
1606+
1607+import (
1608+ "bufio"
1609+ "fmt"
1610+ "os"
1611+ "os/exec"
1612+ "path/filepath"
1613+ "strings"
1614+
1615+ . "launchpad.net/snappy/_integration-tests/testutils/common"
1616+
1617+ check "gopkg.in/check.v1"
1618+)
1619+
1620+const writablePathsListFile = "/etc/system-image/writable-paths"
1621+
1622+var _ = check.Suite(&writablePathsSuite{})
1623+
1624+type writablePathsSuite struct {
1625+ SnappySuite
1626+}
1627+
1628+var IsWritable check.Checker = &isWritable{}
1629+
1630+type isWritable struct {
1631+}
1632+
1633+func (is *isWritable) Info() *check.CheckerInfo {
1634+ return &check.CheckerInfo{Name: "IsWritable", Params: []string{"path"}}
1635+}
1636+
1637+func (is *isWritable) Check(params []interface{}, names []string) (result bool, error string) {
1638+ if path, ok := params[0].(string); ok {
1639+ filename := filepath.Join(path, "tmpfile")
1640+
1641+ cmd := exec.Command("sudo", "touch", filename)
1642+ rmCmd := exec.Command("sudo", "rm", filename)
1643+ defer rmCmd.Run()
1644+
1645+ if _, err := cmd.CombinedOutput(); err == nil {
1646+ result = true
1647+ } else {
1648+ error = fmt.Sprintf("Error creating file %s", filename)
1649+ }
1650+ } else {
1651+ error = fmt.Sprintf("First param of checker %v is of type %T and it should be a string", params[0], params[0])
1652+ }
1653+ return result, error
1654+}
1655+
1656+func (s *writablePathsSuite) TestWritablePathsAreWritable(c *check.C) {
1657+ for writablePath := range generateWritablePaths(c) {
1658+ c.Logf("Checking if %s is writable", writablePath)
1659+ c.Check(writablePath, IsWritable)
1660+ }
1661+}
1662+
1663+func generateWritablePaths(c *check.C) chan string {
1664+ ch := make(chan string)
1665+
1666+ go func() {
1667+ file, err := os.Open(writablePathsListFile)
1668+
1669+ c.Assert(err, check.IsNil,
1670+ check.Commentf("Error reading writable files list %s", writablePathsListFile))
1671+
1672+ defer file.Close()
1673+
1674+ reader := bufio.NewReader(file)
1675+ scanner := bufio.NewScanner(reader)
1676+
1677+ scanner.Split(bufio.ScanLines)
1678+
1679+ for scanner.Scan() {
1680+ fields := strings.Fields(scanner.Text())
1681+ if len(fields) > 0 && fields[0] != "#" {
1682+ if src, err := os.Stat(fields[0]); err == nil && src.IsDir() {
1683+ ch <- fields[0]
1684+ }
1685+ }
1686+ }
1687+ close(ch)
1688+ }()
1689+
1690+ return ch
1691+}
1692
1693=== added directory '_integration-tests/testutils'
1694=== added directory '_integration-tests/testutils/autopkgtest'
1695=== added file '_integration-tests/testutils/autopkgtest/autopkgtest.go'
1696--- _integration-tests/testutils/autopkgtest/autopkgtest.go 1970-01-01 00:00:00 +0000
1697+++ _integration-tests/testutils/autopkgtest/autopkgtest.go 2015-09-16 17:51:11 +0000
1698@@ -0,0 +1,111 @@
1699+// -*- Mode: Go; indent-tabs-mode: t -*-
1700+
1701+/*
1702+ * Copyright (C) 2015 Canonical Ltd
1703+ *
1704+ * This program is free software: you can redistribute it and/or modify
1705+ * it under the terms of the GNU General Public License version 3 as
1706+ * published by the Free Software Foundation.
1707+ *
1708+ * This program is distributed in the hope that it will be useful,
1709+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1710+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1711+ * GNU General Public License for more details.
1712+ *
1713+ * You should have received a copy of the GNU General Public License
1714+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1715+ *
1716+ */
1717+
1718+package autopkgtest
1719+
1720+import (
1721+ "fmt"
1722+ "os"
1723+ "path/filepath"
1724+ "strconv"
1725+ "text/template"
1726+
1727+ "log"
1728+
1729+ "launchpad.net/snappy/_integration-tests/testutils"
1730+)
1731+
1732+const (
1733+ controlTpl = "_integration-tests/data/tpl/control"
1734+ dataOutputDir = "_integration-tests/data/output/"
1735+)
1736+
1737+var controlFile = filepath.Join(dataOutputDir, "control")
1738+
1739+// Autopkgtest is the type that knows how to call adt-run
1740+type Autopkgtest struct {
1741+ sourceCodePath string // location of the source code on the host
1742+ testArtifactsPath string // location of the test artifacts on the host
1743+ testFilter string
1744+ integrationTestName string
1745+}
1746+
1747+// NewAutopkgtest is the Autopkgtest constructor
1748+func NewAutopkgtest(sourceCodePath, testArtifactsPath, testFilter, integrationTestName string) *Autopkgtest {
1749+ return &Autopkgtest{
1750+ sourceCodePath: sourceCodePath,
1751+ testArtifactsPath: testArtifactsPath,
1752+ testFilter: testFilter,
1753+ integrationTestName: integrationTestName}
1754+}
1755+
1756+// AdtRunLocal starts a kvm running the image passed as argument and runs the
1757+// autopkgtests using it as the testbed.
1758+func (a *Autopkgtest) AdtRunLocal(imgPath string) {
1759+ // Run the tests on the latest rolling edge image.
1760+ a.adtRun(kvmSSHOptions(imgPath))
1761+}
1762+
1763+// AdtRunRemote runs the autopkgtests using a remote machine as the testbed.
1764+func (a *Autopkgtest) AdtRunRemote(testbedIP string, testbedPort int) {
1765+ testutils.ExecCommand("ssh-copy-id", "-p", strconv.Itoa(testbedPort),
1766+ "ubuntu@"+testbedIP)
1767+ a.adtRun(remoteTestbedSSHOptions(testbedIP, testbedPort))
1768+}
1769+
1770+func (a *Autopkgtest) adtRun(testbedOptions []string) {
1771+ a.createControlFile()
1772+
1773+ fmt.Println("Calling adt-run...")
1774+ outputDir := filepath.Join(a.testArtifactsPath, "output")
1775+ testutils.PrepareTargetDir(outputDir)
1776+
1777+ cmd := []string{
1778+ "adt-run", "-B",
1779+ "--setup-commands", "touch /run/autopkgtest_no_reboot.stamp",
1780+ "--override-control", controlFile,
1781+ "--built-tree", a.sourceCodePath,
1782+ "--output-dir", outputDir}
1783+
1784+ testutils.ExecCommand(append(cmd, testbedOptions...)...)
1785+}
1786+
1787+func (a *Autopkgtest) createControlFile() {
1788+ type controlData struct {
1789+ Filter string
1790+ Test string
1791+ }
1792+
1793+ tpl, err := template.ParseFiles(controlTpl)
1794+ if err != nil {
1795+ log.Panicf("Error reading adt-run control template %s", controlTpl)
1796+ }
1797+
1798+ outputFile, err := os.Create(controlFile)
1799+ if err != nil {
1800+ log.Panicf("Error creating control file %s", controlFile)
1801+ }
1802+ defer outputFile.Close()
1803+
1804+ err = tpl.Execute(outputFile,
1805+ controlData{Test: a.integrationTestName, Filter: a.testFilter})
1806+ if err != nil {
1807+ log.Panicf("execution: %s", err)
1808+ }
1809+}
1810
1811=== added file '_integration-tests/testutils/autopkgtest/ssh.go'
1812--- _integration-tests/testutils/autopkgtest/ssh.go 1970-01-01 00:00:00 +0000
1813+++ _integration-tests/testutils/autopkgtest/ssh.go 2015-09-16 17:51:11 +0000
1814@@ -0,0 +1,46 @@
1815+// -*- Mode: Go; indent-tabs-mode: t -*-
1816+
1817+/*
1818+ * Copyright (C) 2015 Canonical Ltd
1819+ *
1820+ * This program is free software: you can redistribute it and/or modify
1821+ * it under the terms of the GNU General Public License version 3 as
1822+ * published by the Free Software Foundation.
1823+ *
1824+ * This program is distributed in the hope that it will be useful,
1825+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1826+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1827+ * GNU General Public License for more details.
1828+ *
1829+ * You should have received a copy of the GNU General Public License
1830+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1831+ *
1832+ */
1833+
1834+package autopkgtest
1835+
1836+import (
1837+ "os"
1838+ "path/filepath"
1839+ "strconv"
1840+)
1841+
1842+var commonSSHOptions = []string{"---", "ssh"}
1843+
1844+func kvmSSHOptions(imagePath string) []string {
1845+ return append(
1846+ commonSSHOptions,
1847+ []string{
1848+ "-s", "/usr/share/autopkgtest/ssh-setup/snappy",
1849+ "--", "-i", imagePath}...)
1850+}
1851+
1852+func remoteTestbedSSHOptions(testbedIP string, testbedPort int) []string {
1853+ options := []string{
1854+ "-H", testbedIP,
1855+ "-p", strconv.Itoa(testbedPort),
1856+ "-l", "ubuntu",
1857+ "-i", filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa"),
1858+ "--reboot"}
1859+ return append(commonSSHOptions, options...)
1860+}
1861
1862=== added directory '_integration-tests/testutils/build'
1863=== added file '_integration-tests/testutils/build/build.go'
1864--- _integration-tests/testutils/build/build.go 1970-01-01 00:00:00 +0000
1865+++ _integration-tests/testutils/build/build.go 2015-09-16 17:51:11 +0000
1866@@ -0,0 +1,76 @@
1867+// -*- Mode: Go; indent-tabs-mode: t -*-
1868+
1869+/*
1870+ * Copyright (C) 2015 Canonical Ltd
1871+ *
1872+ * This program is free software: you can redistribute it and/or modify
1873+ * it under the terms of the GNU General Public License version 3 as
1874+ * published by the Free Software Foundation.
1875+ *
1876+ * This program is distributed in the hope that it will be useful,
1877+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1878+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1879+ * GNU General Public License for more details.
1880+ *
1881+ * You should have received a copy of the GNU General Public License
1882+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1883+ *
1884+ */
1885+
1886+package build
1887+
1888+import (
1889+ "fmt"
1890+ "os"
1891+
1892+ "launchpad.net/snappy/_integration-tests/testutils"
1893+)
1894+
1895+const (
1896+ // IntegrationTestName is the name of the test binary.
1897+ IntegrationTestName = "integration.test"
1898+ defaultGoArm = "7"
1899+ testsBinDir = "_integration-tests/bin/"
1900+)
1901+
1902+// Assets builds the snappy and integration tests binaries for the target
1903+// architecture.
1904+func Assets(useSnappyFromBranch bool, arch string) {
1905+ testutils.PrepareTargetDir(testsBinDir)
1906+
1907+ if useSnappyFromBranch {
1908+ // FIXME We need to build an image that has the snappy from the branch
1909+ // installed. --elopio - 2015-06-25.
1910+ buildSnappyCLI(arch)
1911+ }
1912+ buildTests(arch)
1913+}
1914+
1915+func buildSnappyCLI(arch string) {
1916+ fmt.Println("Building snappy CLI...")
1917+ // On the root of the project we have a directory called snappy, so we
1918+ // output the binary for the tests in the tests directory.
1919+ goCall(arch, "build", "-o", testsBinDir+"snappy", "./cmd/snappy")
1920+}
1921+
1922+func buildTests(arch string) {
1923+ fmt.Println("Building tests...")
1924+
1925+ goCall(arch, "test", "-c", "./_integration-tests/tests")
1926+ // XXX Go test 1.3 does not have the output flag, so we move the
1927+ // binaries after they are generated.
1928+ os.Rename("tests.test", testsBinDir+IntegrationTestName)
1929+}
1930+
1931+func goCall(arch string, cmds ...string) {
1932+ if arch != "" {
1933+ defer os.Setenv("GOARCH", os.Getenv("GOARCH"))
1934+ os.Setenv("GOARCH", arch)
1935+ if arch == "arm" {
1936+ defer os.Setenv("GOARM", os.Getenv("GOARM"))
1937+ os.Setenv("GOARM", defaultGoArm)
1938+ }
1939+ }
1940+ goCmd := append([]string{"go"}, cmds...)
1941+ testutils.ExecCommand(goCmd...)
1942+}
1943
1944=== added directory '_integration-tests/testutils/common'
1945=== added file '_integration-tests/testutils/common/common.go'
1946--- _integration-tests/testutils/common/common.go 1970-01-01 00:00:00 +0000
1947+++ _integration-tests/testutils/common/common.go 2015-09-16 17:51:11 +0000
1948@@ -0,0 +1,379 @@
1949+// -*- Mode: Go; indent-tabs-mode: t -*-
1950+
1951+/*
1952+ * Copyright (C) 2015 Canonical Ltd
1953+ *
1954+ * This program is free software: you can redistribute it and/or modify
1955+ * it under the terms of the GNU General Public License version 3 as
1956+ * published by the Free Software Foundation.
1957+ *
1958+ * This program is distributed in the hope that it will be useful,
1959+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1960+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1961+ * GNU General Public License for more details.
1962+ *
1963+ * You should have received a copy of the GNU General Public License
1964+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1965+ *
1966+ */
1967+
1968+package common
1969+
1970+import (
1971+ "fmt"
1972+ "io/ioutil"
1973+ "os"
1974+ "os/exec"
1975+ "path/filepath"
1976+ "regexp"
1977+ "strconv"
1978+ "strings"
1979+ "time"
1980+
1981+ check "gopkg.in/check.v1"
1982+
1983+ "launchpad.net/snappy/_integration-tests/testutils/config"
1984+)
1985+
1986+const (
1987+ // BaseAltPartitionPath is the path to the B system partition.
1988+ BaseAltPartitionPath = "/writable/cache/system"
1989+ needsRebootFile = "/tmp/needs-reboot"
1990+ channelCfgFile = "/etc/system-image/channel.ini"
1991+)
1992+
1993+// Cfg is a struct that contains the configuration values passed from the
1994+// host to the testbed.
1995+var Cfg *config.Config
1996+
1997+// SnappySuite is a structure used as a base test suite for all the snappy
1998+// integration tests.
1999+type SnappySuite struct {
2000+ cleanupHandlers []func()
2001+}
2002+
2003+// SetUpSuite disables the snappy autopilot. It will run before all the
2004+// integration suites.
2005+func (s *SnappySuite) SetUpSuite(c *check.C) {
2006+ ExecCommand(c, "sudo", "systemctl", "stop", "snappy-autopilot.timer")
2007+ ExecCommand(c, "sudo", "systemctl", "disable", "snappy-autopilot.timer")
2008+ if !isInRebootProcess() {
2009+ var err error
2010+ Cfg, err = config.ReadConfig(
2011+ "_integration-tests/data/output/testconfig.json")
2012+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2013+ if Cfg.Update || Cfg.Rollback {
2014+ switchSystemImageConf(c, Cfg.TargetRelease, Cfg.TargetChannel, "0")
2015+ // Always use the installed snappy because we are updating from an old
2016+ // image, so we should not use the snappy from the branch.
2017+ output := ExecCommand(c, "sudo", "/usr/bin/snappy", "update")
2018+ if output != "" {
2019+ RebootWithMark(c, "setupsuite-update")
2020+ }
2021+ }
2022+ } else if CheckRebootMark("setupsuite-update") {
2023+ RemoveRebootMark(c)
2024+ if Cfg.Rollback {
2025+ ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core")
2026+ RebootWithMark(c, "setupsuite-rollback")
2027+ }
2028+ } else if CheckRebootMark("setupsuite-rollback") {
2029+ RemoveRebootMark(c)
2030+ }
2031+}
2032+
2033+// SetUpTest handles reboots and stores version information. It will run before
2034+// all the integration tests. Before running a test, it will save the
2035+// ubuntu-core version. If a reboot was requested by a previous test, it
2036+// will skip all the following tests. If the suite is being called after the
2037+// test bed was rebooted, it will resume the test that requested the reboot.
2038+func (s *SnappySuite) SetUpTest(c *check.C) {
2039+ if NeedsReboot() {
2040+ contents, err := ioutil.ReadFile(needsRebootFile)
2041+ c.Assert(err, check.IsNil, check.Commentf("Error reading needs-reboot file %v", err))
2042+ c.Skip(fmt.Sprintf("****** Skipped %s during reboot caused by %s",
2043+ c.TestName(), contents))
2044+ } else {
2045+ if CheckRebootMark("") {
2046+ c.Logf("****** Running %s", c.TestName())
2047+ SetSavedVersion(c, GetCurrentUbuntuCoreVersion(c))
2048+ } else {
2049+ if AfterReboot(c) {
2050+ c.Logf("****** Resuming %s after reboot", c.TestName())
2051+ } else {
2052+ c.Skip(fmt.Sprintf("****** Skipped %s after reboot caused by %s",
2053+ c.TestName(), os.Getenv("ADT_REBOOT_MARK")))
2054+ }
2055+ }
2056+ }
2057+ // clear slice
2058+ s.cleanupHandlers = nil
2059+}
2060+
2061+// TearDownTest cleans up the channel.ini files in case they were changed by
2062+// the test.
2063+// It also runs the cleanup handlers
2064+func (s *SnappySuite) TearDownTest(c *check.C) {
2065+ if !NeedsReboot() && CheckRebootMark("") {
2066+ // Only restore the channel config files if the reboot has been handled.
2067+ m := make(map[string]string)
2068+ m[channelCfgBackupFile()] = "/"
2069+ m[channelCfgOtherBackupFile()] = BaseAltPartitionPath
2070+ for backup, target := range m {
2071+ if _, err := os.Stat(backup); err == nil {
2072+ MakeWritable(c, target)
2073+ defer MakeReadonly(c, target)
2074+ original := filepath.Join(target, channelCfgFile)
2075+ c.Logf("Restoring %s...", original)
2076+ ExecCommand(c, "sudo", "mv", backup, original)
2077+ }
2078+ }
2079+ }
2080+
2081+ // run cleanup handlers and clear the slice
2082+ for _, f := range s.cleanupHandlers {
2083+ f()
2084+ }
2085+ s.cleanupHandlers = nil
2086+}
2087+
2088+// AddCleanup adds a new cleanup function to the test
2089+func (s *SnappySuite) AddCleanup(f func()) {
2090+ s.cleanupHandlers = append(s.cleanupHandlers, f)
2091+}
2092+
2093+func switchSystemImageConf(c *check.C, release, channel, version string) {
2094+ targets := []string{"/", BaseAltPartitionPath}
2095+ for _, target := range targets {
2096+ file := filepath.Join(target, channelCfgFile)
2097+ if _, err := os.Stat(file); err == nil {
2098+ MakeWritable(c, target)
2099+ defer MakeReadonly(c, target)
2100+ replaceSystemImageValues(c, file, release, channel, version)
2101+ }
2102+ }
2103+}
2104+
2105+func replaceSystemImageValues(c *check.C, file, release, channel, version string) {
2106+ c.Log("Switching the system image conf...")
2107+ replaceRegex := map[string]string{
2108+ release: `s#channel: ubuntu-core/.*/\(.*\)#channel: ubuntu-core/%s/\1#`,
2109+ channel: `s#channel: ubuntu-core/\(.*\)/.*#channel: ubuntu-core/\1/%s#`,
2110+ version: `s/build_number: .*/build_number: %s/`,
2111+ }
2112+ for value, regex := range replaceRegex {
2113+ if value != "" {
2114+ ExecCommand(c,
2115+ "sudo", "sed", "-i", fmt.Sprintf(regex, value), file)
2116+ }
2117+ }
2118+ // Leave the new file in the test log.
2119+ ExecCommand(c, "cat", file)
2120+}
2121+
2122+func channelCfgBackupFile() string {
2123+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini")
2124+}
2125+
2126+func channelCfgOtherBackupFile() string {
2127+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini.other")
2128+}
2129+
2130+// ExecCommand executes a shell command and returns a string with the output
2131+// of the command. In case of error, it will fail the test.
2132+func ExecCommand(c *check.C, cmds ...string) string {
2133+ fmt.Println(strings.Join(cmds, " "))
2134+ cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
2135+ output, err := cmd.CombinedOutput()
2136+ stringOutput := string(output)
2137+ fmt.Print(stringOutput)
2138+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", stringOutput))
2139+ return stringOutput
2140+}
2141+
2142+// ExecCommandToFile executes a shell command and saves the output of the
2143+// command to a file. In case of error, it will fail the test.
2144+func ExecCommandToFile(c *check.C, filename string, cmds ...string) {
2145+ cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
2146+ outfile, err := os.Create(filename)
2147+ c.Assert(err, check.IsNil, check.Commentf("Error creating output file %s", filename))
2148+
2149+ defer outfile.Close()
2150+ cmd.Stdout = outfile
2151+
2152+ err = cmd.Run()
2153+ c.Assert(err, check.IsNil, check.Commentf("Error executing command '%v': %v", cmds, err))
2154+}
2155+
2156+// GetCurrentVersion returns the version of the installed and active package.
2157+func GetCurrentVersion(c *check.C, packageName string) string {
2158+ output := ExecCommand(c, "snappy", "list")
2159+ pattern := "(?mU)^" + packageName + " +(.*)$"
2160+ re := regexp.MustCompile(pattern)
2161+ match := re.FindStringSubmatch(string(output))
2162+ c.Assert(match, check.NotNil, check.Commentf("Version not found in %s", output))
2163+
2164+ // match is like "ubuntu-core 2015-06-18 93 ubuntu"
2165+ items := strings.Fields(match[0])
2166+ return items[2]
2167+}
2168+
2169+// GetCurrentUbuntuCoreVersion returns the version number of the installed and
2170+// active ubuntu-core.
2171+func GetCurrentUbuntuCoreVersion(c *check.C) int {
2172+ versionString := GetCurrentVersion(c, "ubuntu-core")
2173+ version, err := strconv.Atoi(versionString)
2174+ c.Assert(err, check.IsNil, check.Commentf("Error converting version to int %v", version))
2175+ return version
2176+}
2177+
2178+// CallFakeUpdate calls snappy update after faking the current version
2179+func CallFakeUpdate(c *check.C) string {
2180+ c.Log("Preparing fake and calling update.")
2181+ fakeAvailableUpdate(c)
2182+ return ExecCommand(c, "sudo", "snappy", "update")
2183+}
2184+
2185+func fakeAvailableUpdate(c *check.C) {
2186+ c.Log("Faking an available update...")
2187+ currentVersion := GetCurrentUbuntuCoreVersion(c)
2188+ switchChannelVersionWithBackup(c, currentVersion-1)
2189+ SetSavedVersion(c, currentVersion-1)
2190+}
2191+
2192+func switchChannelVersionWithBackup(c *check.C, newVersion int) {
2193+ m := make(map[string]string)
2194+ m["/"] = channelCfgBackupFile()
2195+ m[BaseAltPartitionPath] = channelCfgOtherBackupFile()
2196+ for target, backup := range m {
2197+ file := filepath.Join(target, channelCfgFile)
2198+ if _, err := os.Stat(file); err == nil {
2199+ MakeWritable(c, target)
2200+ defer MakeReadonly(c, target)
2201+ // Back up the file. It will be restored during the test tear down.
2202+ ExecCommand(c, "cp", file, backup)
2203+ replaceSystemImageValues(c, file, "", "", strconv.Itoa(newVersion))
2204+ }
2205+ }
2206+}
2207+
2208+// MakeWritable remounts a path with read and write permissions.
2209+func MakeWritable(c *check.C, path string) {
2210+ ExecCommand(c, "sudo", "mount", "-o", "remount,rw", path)
2211+}
2212+
2213+// MakeReadonly remounts a path with only read permissions.
2214+func MakeReadonly(c *check.C, path string) {
2215+ ExecCommand(c, "sudo", "mount", "-o", "remount,ro", path)
2216+}
2217+
2218+// Reboot requests a reboot using the test name as the mark.
2219+func Reboot(c *check.C) {
2220+ RebootWithMark(c, c.TestName())
2221+}
2222+
2223+// RebootWithMark requests a reboot using a specified mark.
2224+func RebootWithMark(c *check.C, mark string) {
2225+ c.Log("Preparing reboot with mark " + mark)
2226+ err := ioutil.WriteFile(needsRebootFile, []byte(mark), 0777)
2227+ c.Assert(err, check.IsNil, check.Commentf("Error writing needs-reboot file: %v", err))
2228+}
2229+
2230+// NeedsReboot returns True if a reboot has been requested by a test.
2231+func NeedsReboot() bool {
2232+ _, err := os.Stat(needsRebootFile)
2233+ return err == nil
2234+}
2235+
2236+// BeforeReboot returns True if the test is running before the test bed has
2237+// been rebooted, or after the test that requested the reboot handled it.
2238+func BeforeReboot() bool {
2239+ return CheckRebootMark("")
2240+}
2241+
2242+// AfterReboot returns True if the test is running after the test bed has been
2243+// rebooted.
2244+func AfterReboot(c *check.C) bool {
2245+ // $ADT_REBOOT_MARK contains the reboot mark, if we have rebooted it'll be the test name
2246+ return strings.HasPrefix(os.Getenv("ADT_REBOOT_MARK"), c.TestName())
2247+}
2248+
2249+// CheckRebootMark returns True if the reboot mark matches the string passed as
2250+// argument.
2251+func CheckRebootMark(mark string) bool {
2252+ return os.Getenv("ADT_REBOOT_MARK") == mark
2253+}
2254+
2255+func isInRebootProcess() bool {
2256+ return !CheckRebootMark("") || NeedsReboot()
2257+}
2258+
2259+// RemoveRebootMark removes the reboot mark to signal that the reboot has been
2260+// handled.
2261+func RemoveRebootMark(c *check.C) {
2262+ os.Setenv("ADT_REBOOT_MARK", "")
2263+}
2264+
2265+// SetSavedVersion saves a version number into a file so it can be used on
2266+// tests after reboots.
2267+func SetSavedVersion(c *check.C, version int) {
2268+ versionFile := getVersionFile()
2269+ err := ioutil.WriteFile(versionFile, []byte(strconv.Itoa(version)), 0777)
2270+ c.Assert(err, check.IsNil, check.Commentf("Error writing version file %s with %s", versionFile, version))
2271+}
2272+
2273+// GetSavedVersion returns the saved version number.
2274+func GetSavedVersion(c *check.C) int {
2275+ versionFile := getVersionFile()
2276+ contents, err := ioutil.ReadFile(versionFile)
2277+ c.Assert(err, check.IsNil, check.Commentf("Error reading version file %s", versionFile))
2278+
2279+ version, err := strconv.Atoi(string(contents))
2280+ c.Assert(err, check.IsNil, check.Commentf("Error converting version %v", contents))
2281+
2282+ return version
2283+}
2284+
2285+func getVersionFile() string {
2286+ return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "version")
2287+}
2288+
2289+// InstallSnap executes the required command to install the specified snap
2290+func InstallSnap(c *check.C, packageName string) string {
2291+ return ExecCommand(c, "sudo", "snappy", "install", packageName, "--allow-unauthenticated")
2292+}
2293+
2294+// RemoveSnap executes the required command to remove the specified snap
2295+func RemoveSnap(c *check.C, packageName string) string {
2296+ return ExecCommand(c, "sudo", "snappy", "remove", packageName)
2297+}
2298+
2299+// WaitForActiveService keeps asking for the active state of the given service until
2300+// it is active or the maximun waiting time expires, in which case an error is returned
2301+func WaitForActiveService(c *check.C, serviceName string) error {
2302+ maxWait := time.Second * 10
2303+ checkInterval := time.Millisecond * 500
2304+
2305+ timer := time.NewTimer(maxWait)
2306+ timeChan := timer.C
2307+
2308+ ticker := time.NewTicker(checkInterval)
2309+ tickChan := ticker.C
2310+
2311+ for {
2312+ select {
2313+ case <-timeChan:
2314+ ticker.Stop()
2315+ journalctlOutput := ExecCommand(c, "sudo", "journalctl", "-u", serviceName)
2316+ return fmt.Errorf("Service %s not active after %s, journalctl output: %s",
2317+ serviceName, maxWait, journalctlOutput)
2318+ case <-tickChan:
2319+ statusOutput := ExecCommand(
2320+ c, "systemctl", "show", "-p", "ActiveState", serviceName)
2321+ if statusOutput == "ActiveState=active\n" {
2322+ timer.Stop()
2323+ return nil
2324+ }
2325+ }
2326+ }
2327+}
2328
2329=== added file '_integration-tests/testutils/common/common_test.go'
2330--- _integration-tests/testutils/common/common_test.go 1970-01-01 00:00:00 +0000
2331+++ _integration-tests/testutils/common/common_test.go 2015-09-16 17:51:11 +0000
2332@@ -0,0 +1,68 @@
2333+// -*- Mode: Go; indent-tabs-mode: t -*-
2334+
2335+/*
2336+ * Copyright (C) 2014-2015 Canonical Ltd
2337+ *
2338+ * This program is free software: you can redistribute it and/or modify
2339+ * it under the terms of the GNU General Public License version 3 as
2340+ * published by the Free Software Foundation.
2341+ *
2342+ * This program is distributed in the hope that it will be useful,
2343+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2344+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2345+ * GNU General Public License for more details.
2346+ *
2347+ * You should have received a copy of the GNU General Public License
2348+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2349+ *
2350+ */
2351+
2352+package common
2353+
2354+import (
2355+ "testing"
2356+
2357+ . "gopkg.in/check.v1"
2358+)
2359+
2360+// Hook up check.v1 into the "go test" runner
2361+func Test(t *testing.T) { TestingT(t) }
2362+
2363+// testing a testsuite - thats so meta
2364+type MetaTestSuite struct {
2365+}
2366+
2367+var _ = Suite(&MetaTestSuite{})
2368+
2369+// test trivial cleanup
2370+func (m *MetaTestSuite) TestCleanupSimple(c *C) {
2371+ canary := "not-called"
2372+ s := SnappySuite{}
2373+
2374+ s.AddCleanup(func() {
2375+ canary = "was-called"
2376+ })
2377+ s.TearDownTest(c)
2378+
2379+ c.Assert(canary, Equals, "was-called")
2380+}
2381+
2382+// a mock method that takes a parameter
2383+func mockCleanupMethodWithParameters(s *string) {
2384+ *s = "was-called"
2385+}
2386+
2387+// test that whle AddCleanup() does not take any parameters itself,
2388+// functions that need parameters can be passed by creating an
2389+// anonymous function as a wrapper
2390+func (m *MetaTestSuite) TestCleanupWithParameters(c *C) {
2391+ canary := "not-called"
2392+ s := SnappySuite{}
2393+
2394+ s.AddCleanup(func() {
2395+ mockCleanupMethodWithParameters(&canary)
2396+ })
2397+ s.TearDownTest(c)
2398+
2399+ c.Assert(canary, Equals, "was-called")
2400+}
2401
2402=== added directory '_integration-tests/testutils/config'
2403=== added file '_integration-tests/testutils/config/config.go'
2404--- _integration-tests/testutils/config/config.go 1970-01-01 00:00:00 +0000
2405+++ _integration-tests/testutils/config/config.go 2015-09-16 17:51:11 +0000
2406@@ -0,0 +1,75 @@
2407+// -*- Mode: Go; indent-tabs-mode: t -*-
2408+
2409+/*
2410+ * Copyright (C) 2015 Canonical Ltd
2411+ *
2412+ * This program is free software: you can redistribute it and/or modify
2413+ * it under the terms of the GNU General Public License version 3 as
2414+ * published by the Free Software Foundation.
2415+ *
2416+ * This program is distributed in the hope that it will be useful,
2417+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2418+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2419+ * GNU General Public License for more details.
2420+ *
2421+ * You should have received a copy of the GNU General Public License
2422+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2423+ *
2424+ */
2425+
2426+package config
2427+
2428+import (
2429+ "encoding/json"
2430+ "fmt"
2431+ "io/ioutil"
2432+ "log"
2433+)
2434+
2435+// Config contains the values to pass to the test bed from the host.
2436+type Config struct {
2437+ FileName string
2438+ Release string
2439+ Channel string
2440+ TargetRelease string
2441+ TargetChannel string
2442+ RemoteTestbed bool
2443+ Update bool
2444+ Rollback bool
2445+}
2446+
2447+// NewConfig is the Config constructor
2448+func NewConfig(fileName, release, channel, targetRelease, targetChannel string, remoteTestbed, update, rollback bool) *Config {
2449+ return &Config{
2450+ FileName: fileName, Release: release, Channel: channel,
2451+ TargetRelease: targetRelease, TargetChannel: targetChannel,
2452+ RemoteTestbed: remoteTestbed, Update: update, Rollback: rollback,
2453+ }
2454+}
2455+
2456+// Write writes the config to a file that will be copied to the test bed.
2457+func (cfg Config) Write() {
2458+ fmt.Println("Writing test config...")
2459+ fmt.Println(cfg)
2460+ encoded, err := json.Marshal(cfg)
2461+ if err != nil {
2462+ log.Panicf("Error encoding the test config: %v", err)
2463+ }
2464+ err = ioutil.WriteFile(cfg.FileName, encoded, 0644)
2465+ if err != nil {
2466+ log.Panicf("Error writing the test config: %v", err)
2467+ }
2468+}
2469+
2470+// ReadConfig the config from a file
2471+func ReadConfig(fileName string) (*Config, error) {
2472+ b, err := ioutil.ReadFile(fileName)
2473+ if err != nil {
2474+ return nil, err
2475+ }
2476+ var decoded Config
2477+ if err = json.Unmarshal(b, &decoded); err != nil {
2478+ return nil, err
2479+ }
2480+ return &decoded, nil
2481+}
2482
2483=== added file '_integration-tests/testutils/config/config.test'
2484Binary files _integration-tests/testutils/config/config.test 1970-01-01 00:00:00 +0000 and _integration-tests/testutils/config/config.test 2015-09-16 17:51:11 +0000 differ
2485=== added file '_integration-tests/testutils/config/config_test.go'
2486--- _integration-tests/testutils/config/config_test.go 1970-01-01 00:00:00 +0000
2487+++ _integration-tests/testutils/config/config_test.go 2015-09-16 17:51:11 +0000
2488@@ -0,0 +1,121 @@
2489+// -*- Mode: Go; indent-tabs-mode: t -*-
2490+
2491+/*
2492+ * Copyright (C) 2014-2015 Canonical Ltd
2493+ *
2494+ * This program is free software: you can redistribute it and/or modify
2495+ * it under the terms of the GNU General Public License version 3 as
2496+ * published by the Free Software Foundation.
2497+ *
2498+ * This program is distributed in the hope that it will be useful,
2499+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2500+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2501+ * GNU General Public License for more details.
2502+ *
2503+ * You should have received a copy of the GNU General Public License
2504+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2505+ *
2506+ */
2507+
2508+package config
2509+
2510+import (
2511+ "fmt"
2512+ "io/ioutil"
2513+ "os"
2514+ "path/filepath"
2515+ "testing"
2516+
2517+ check "gopkg.in/check.v1"
2518+)
2519+
2520+// Hook up check.v1 into the "go test" runner
2521+func Test(t *testing.T) { check.TestingT(t) }
2522+
2523+type ConfigSuite struct{}
2524+
2525+var _ = check.Suite(&ConfigSuite{})
2526+
2527+func testConfigFileName(c *check.C) string {
2528+ tmpDir, err := ioutil.TempDir("", "")
2529+ c.Assert(err, check.IsNil, check.Commentf(
2530+ "Error creating a temporary directory: %v", err))
2531+ return filepath.Join(tmpDir, "test.config")
2532+}
2533+
2534+func testConfigStruct(fileName string) *Config {
2535+ return NewConfig(
2536+ fileName,
2537+ "testrelease", "testchannel", "testtargetrelease", "testtargetchannel",
2538+ true, true, true)
2539+}
2540+func testConfigContents(fileName string) string {
2541+ return `{` +
2542+ fmt.Sprintf(`"FileName":"%s",`, fileName) +
2543+ `"Release":"testrelease",` +
2544+ `"Channel":"testchannel",` +
2545+ `"TargetRelease":"testtargetrelease",` +
2546+ `"TargetChannel":"testtargetchannel",` +
2547+ `"RemoteTestbed":true,` +
2548+ `"Update":true,` +
2549+ `"Rollback":true` +
2550+ `}`
2551+}
2552+
2553+func (s *ConfigSuite) TestWriteConfig(c *check.C) {
2554+ // Do not print to stdout.
2555+ devnull, err := os.Open(os.DevNull)
2556+ c.Assert(err, check.IsNil)
2557+ oldStdout := os.Stdout
2558+ os.Stdout = devnull
2559+ defer func() {
2560+ os.Stdout = oldStdout
2561+ }()
2562+ configFileName := testConfigFileName(c)
2563+
2564+ cfg := testConfigStruct(configFileName)
2565+ cfg.Write()
2566+
2567+ writtenConfig, err := ioutil.ReadFile(configFileName)
2568+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2569+ c.Assert(string(writtenConfig), check.Equals, testConfigContents(configFileName))
2570+}
2571+
2572+func (s *ConfigSuite) TestReadConfig(c *check.C) {
2573+ configFileName := testConfigFileName(c)
2574+
2575+ configContents := testConfigContents(configFileName)
2576+ ioutil.WriteFile(configFileName, []byte(configContents), 0644)
2577+
2578+ cfg, err := ReadConfig(configFileName)
2579+
2580+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2581+ c.Assert(cfg, check.DeepEquals, testConfigStruct(configFileName))
2582+}
2583+
2584+func (s *ConfigSuite) TestReadConfigLocalTestBed(c *check.C) {
2585+ configFileName := testConfigFileName(c)
2586+
2587+ configContents := `{` +
2588+ fmt.Sprintf(`"FileName":"%s",`, configFileName) +
2589+ `"Release":"testrelease",` +
2590+ `"Channel":"testchannel",` +
2591+ `"TargetRelease":"testtargetrelease",` +
2592+ `"TargetChannel":"testtargetchannel",` +
2593+ `"RemoteTestbed":false,` +
2594+ `"Update":true,` +
2595+ `"Rollback":true` +
2596+ `}`
2597+
2598+ ioutil.WriteFile(configFileName, []byte(configContents), 0644)
2599+
2600+ cfg, err := ReadConfig(configFileName)
2601+
2602+ testConfigStruct := NewConfig(
2603+ configFileName,
2604+ "testrelease", "testchannel", "testtargetrelease", "testtargetchannel",
2605+ false, true, true)
2606+
2607+ c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
2608+ c.Assert(cfg, check.DeepEquals, testConfigStruct)
2609+}
2610
2611=== added directory '_integration-tests/testutils/image'
2612=== added file '_integration-tests/testutils/image/image.go'
2613--- _integration-tests/testutils/image/image.go 1970-01-01 00:00:00 +0000
2614+++ _integration-tests/testutils/image/image.go 2015-09-16 17:51:11 +0000
2615@@ -0,0 +1,86 @@
2616+// -*- Mode: Go; indent-tabs-mode: t -*-
2617+
2618+/*
2619+ * Copyright (C) 2015 Canonical Ltd
2620+ *
2621+ * This program is free software: you can redistribute it and/or modify
2622+ * it under the terms of the GNU General Public License version 3 as
2623+ * published by the Free Software Foundation.
2624+ *
2625+ * This program is distributed in the hope that it will be useful,
2626+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2627+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2628+ * GNU General Public License for more details.
2629+ *
2630+ * You should have received a copy of the GNU General Public License
2631+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2632+ *
2633+ */
2634+
2635+package image
2636+
2637+import (
2638+ "fmt"
2639+ "path/filepath"
2640+ "strings"
2641+
2642+ "launchpad.net/snappy/_integration-tests/testutils"
2643+)
2644+
2645+// Image type encapsulates image actions
2646+type Image struct {
2647+ release string
2648+ channel string
2649+ revision string
2650+ baseDir string
2651+}
2652+
2653+// NewImage is the Image constructor
2654+func NewImage(release, channel, revision, baseDir string) *Image {
2655+ return &Image{release: release, channel: channel, revision: revision, baseDir: baseDir}
2656+}
2657+
2658+// UdfCreate forms and executes the UDF command for creating the image
2659+func (img Image) UdfCreate() (string, error) {
2660+ fmt.Println("Creating image...")
2661+
2662+ imageDir := filepath.Join(img.baseDir, "image")
2663+
2664+ testutils.PrepareTargetDir(imageDir)
2665+
2666+ udfCommand := []string{"sudo", "ubuntu-device-flash", "--verbose"}
2667+
2668+ if img.revision != "" {
2669+ udfCommand = append(udfCommand, "--revision", img.revision)
2670+ }
2671+
2672+ imagePath := img.imagePath(imageDir)
2673+
2674+ coreOptions := []string{
2675+ "core", img.release,
2676+ "--output", imagePath,
2677+ "--channel", img.channel,
2678+ "--developer-mode",
2679+ }
2680+
2681+ err := testutils.ExecCommand(append(udfCommand, coreOptions...)...)
2682+
2683+ return imagePath, err
2684+}
2685+
2686+func (img Image) imagePath(imageDir string) string {
2687+ revisionTag := img.revision
2688+ if revisionTag == "" {
2689+ revisionTag = "latest"
2690+ }
2691+
2692+ imageName := strings.Join(
2693+ []string{"snappy", img.release, img.channel, revisionTag}, "-") + ".img"
2694+
2695+ return filepath.Join(imageDir, imageName)
2696+}
2697+
2698+// SetRevision is the setter method for revision
2699+func (img Image) SetRevision(rev string) {
2700+ img.revision = rev
2701+}
2702
2703=== added file '_integration-tests/testutils/testutils.go'
2704--- _integration-tests/testutils/testutils.go 1970-01-01 00:00:00 +0000
2705+++ _integration-tests/testutils/testutils.go 2015-09-16 17:51:11 +0000
2706@@ -0,0 +1,61 @@
2707+// -*- Mode: Go; indent-tabs-mode: t -*-
2708+
2709+/*
2710+ * Copyright (C) 2015 Canonical Ltd
2711+ *
2712+ * This program is free software: you can redistribute it and/or modify
2713+ * it under the terms of the GNU General Public License version 3 as
2714+ * published by the Free Software Foundation.
2715+ *
2716+ * This program is distributed in the hope that it will be useful,
2717+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2718+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2719+ * GNU General Public License for more details.
2720+ *
2721+ * You should have received a copy of the GNU General Public License
2722+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2723+ *
2724+ */
2725+
2726+package testutils
2727+
2728+import (
2729+ "fmt"
2730+ "log"
2731+ "os"
2732+ "os/exec"
2733+ "strings"
2734+)
2735+
2736+// PrepareTargetDir creates the given target directory, removing it previously if it didn't exist
2737+func PrepareTargetDir(targetDir string) {
2738+ if _, err := os.Stat(targetDir); err == nil {
2739+ // dir exists, remove it
2740+ os.RemoveAll(targetDir)
2741+ }
2742+ os.MkdirAll(targetDir, 0777)
2743+}
2744+
2745+// RootPath return the test current working directory.
2746+func RootPath() string {
2747+ dir, err := os.Getwd()
2748+ if err != nil {
2749+ log.Panic(err)
2750+ }
2751+ return dir
2752+}
2753+
2754+// ExecCommand executes the given command and pipes the results to os.Stdout and os.Stderr, returning the resulting error
2755+func ExecCommand(cmds ...string) error {
2756+ fmt.Println(strings.Join(cmds, " "))
2757+
2758+ cmd := exec.Command(cmds[0], cmds[1:]...)
2759+ cmd.Stdout = os.Stdout
2760+ cmd.Stderr = os.Stderr
2761+
2762+ err := cmd.Run()
2763+ if err != nil {
2764+ log.Panicf("Error while running %s: %s\n", cmd.Args, err)
2765+ }
2766+ return err
2767+}
2768
2769=== modified file 'clickdeb/deb.go'
2770=== modified file 'clickdeb/deb_test.go'
2771=== modified file 'cmd/snappy/cmd_internal_unpack.go'
2772--- cmd/snappy/cmd_internal_unpack.go 2015-07-02 16:01:11 +0000
2773+++ cmd/snappy/cmd_internal_unpack.go 2015-09-16 17:51:11 +0000
2774@@ -129,6 +129,7 @@
2775
2776 // first find out what user to use
2777 passFile := passwdFile(rootDir, "passwd")
2778+<<<<<<< TREE
2779 for _, dropPrivsUser = range dropPrivsUsers {
2780 _, err := readUID(dropPrivsUser, passFile)
2781 if err == nil {
2782@@ -138,6 +139,9 @@
2783
2784 // then get uid/gid
2785 uid, err := readUID(dropPrivsUser, passFile)
2786+=======
2787+ uid, err := readUID(dropPrivsUser, passFile)
2788+>>>>>>> MERGE-SOURCE
2789 if err != nil {
2790 return err
2791 }
2792
2793=== modified file 'cmd/snappy/cmd_internal_unpack_test.go'
2794--- cmd/snappy/cmd_internal_unpack_test.go 2015-07-02 16:01:11 +0000
2795+++ cmd/snappy/cmd_internal_unpack_test.go 2015-09-16 17:51:11 +0000
2796@@ -42,7 +42,11 @@
2797 snappypkg:x:101:104::/nonexistent:/bin/false
2798 `)
2799
2800+<<<<<<< TREE
2801 uid, err := readUID("snappypkg", f.Name())
2802+=======
2803+ uid, err := readUID("clickpkg", f.Name())
2804+>>>>>>> MERGE-SOURCE
2805 c.Assert(err, IsNil)
2806 c.Assert(uid, Equals, 101)
2807 }
2808@@ -53,7 +57,11 @@
2809 snappypkg:x:104:
2810 `)
2811
2812+<<<<<<< TREE
2813 gid, err := readUID("snappypkg", f.Name())
2814+=======
2815+ gid, err := readUID("clickpkg", f.Name())
2816+>>>>>>> MERGE-SOURCE
2817 c.Assert(err, IsNil)
2818 c.Assert(gid, Equals, 104)
2819 }
2820@@ -66,7 +74,11 @@
2821 `)
2822 defer os.Remove(f.Name())
2823
2824+<<<<<<< TREE
2825 uid, err := readUID("snappypkg", f.Name())
2826+=======
2827+ uid, err := readUID("clickpkg", f.Name())
2828+>>>>>>> MERGE-SOURCE
2829 c.Assert(err, IsNil)
2830 c.Assert(uid, Equals, 102)
2831 }
2832@@ -77,7 +89,11 @@
2833 snappypkg:x:
2834 `)
2835
2836+<<<<<<< TREE
2837 _, err := readUID("snappypkg", f.Name())
2838+=======
2839+ _, err := readUID("clickpkg", f.Name())
2840+>>>>>>> MERGE-SOURCE
2841 c.Assert(err, NotNil)
2842 }
2843
2844@@ -86,6 +102,10 @@
2845 daemon:
2846 `)
2847
2848+<<<<<<< TREE
2849 _, err := readUID("snappypkg", f.Name())
2850+=======
2851+ _, err := readUID("clickpkg", f.Name())
2852+>>>>>>> MERGE-SOURCE
2853 c.Assert(err, NotNil)
2854 }
2855
2856=== modified file 'cmd/snappy/cmd_rollback.go'
2857--- cmd/snappy/cmd_rollback.go 2015-07-09 06:05:53 +0000
2858+++ cmd/snappy/cmd_rollback.go 2015-09-16 17:51:11 +0000
2859@@ -79,5 +79,14 @@
2860 parts := snappy.FindSnapsByNameAndVersion(pkg, nowVersion, installed)
2861 showVerboseList(parts, os.Stdout)
2862
2863+ m := snappy.NewMetaRepository()
2864+ installed, err := m.Installed()
2865+ if err != nil {
2866+ return err
2867+ }
2868+
2869+ parts := snappy.FindSnapsByNameAndVersion(pkg, nowVersion, installed)
2870+ showVerboseList(parts, os.Stdout)
2871+
2872 return nil
2873 }
2874
2875=== modified file 'cmd/snappy/cmd_set.go'
2876--- cmd/snappy/cmd_set.go 2015-06-30 12:52:52 +0000
2877+++ cmd/snappy/cmd_set.go 2015-09-16 17:51:11 +0000
2878@@ -23,8 +23,12 @@
2879 "fmt"
2880 "strings"
2881
2882+<<<<<<< TREE
2883 "launchpad.net/snappy/i18n"
2884 "launchpad.net/snappy/logger"
2885+=======
2886+ "launchpad.net/snappy/priv"
2887+>>>>>>> MERGE-SOURCE
2888 "launchpad.net/snappy/progress"
2889 "launchpad.net/snappy/snappy"
2890 )
2891@@ -53,8 +57,18 @@
2892 }
2893
2894 func (x *cmdSet) Execute(args []string) (err error) {
2895+<<<<<<< TREE
2896 x.args = args
2897 return withMutex(x.doSet)
2898+=======
2899+ privMutex := priv.New()
2900+ if err := privMutex.TryLock(); err != nil {
2901+ return err
2902+ }
2903+ defer privMutex.Unlock()
2904+
2905+ return set(args)
2906+>>>>>>> MERGE-SOURCE
2907 }
2908
2909 func (x *cmdSet) doSet() (err error) {
2910
2911=== modified file 'cmd/snappy/cmd_set_test.go'
2912--- cmd/snappy/cmd_set_test.go 2015-07-08 07:58:40 +0000
2913+++ cmd/snappy/cmd_set_test.go 2015-09-16 17:51:11 +0000
2914@@ -20,9 +20,26 @@
2915 package main
2916
2917 import (
2918- . "gopkg.in/check.v1"
2919+<<<<<<< TREE
2920+ . "gopkg.in/check.v1"
2921+=======
2922+ "testing"
2923+
2924+ . "gopkg.in/check.v1"
2925+>>>>>>> MERGE-SOURCE
2926 )
2927
2928+<<<<<<< TREE
2929+=======
2930+// Hook up check.v1 into the "go test" runner
2931+func Test(t *testing.T) { TestingT(t) }
2932+
2933+type CmdTestSuite struct {
2934+}
2935+
2936+var _ = Suite(&CmdTestSuite{})
2937+
2938+>>>>>>> MERGE-SOURCE
2939 func (s *CmdTestSuite) TestParseSetPropertyCmdline(c *C) {
2940
2941 // simple case
2942
2943=== modified file 'cmd/snappy/main.go'
2944--- cmd/snappy/main.go 2015-06-11 20:30:46 +0000
2945+++ cmd/snappy/main.go 2015-09-16 17:51:11 +0000
2946@@ -52,10 +52,14 @@
2947 // the CLI user.
2948 err = snappy.ErrNeedRoot
2949 }
2950+<<<<<<< TREE
2951 fmt.Fprintln(os.Stderr, err)
2952 if _, ok := err.(*flags.Error); !ok {
2953 logger.Debugf("%v failed: %v", os.Args, err)
2954 }
2955+=======
2956+ fmt.Fprintln(os.Stderr, err)
2957+>>>>>>> MERGE-SOURCE
2958 os.Exit(1)
2959 }
2960 }
2961
2962=== modified file 'coreconfig/config.go'
2963=== modified file 'coreconfig/config_test.go'
2964--- coreconfig/config_test.go 2015-09-15 21:36:22 +0000
2965+++ coreconfig/config_test.go 2015-09-16 17:51:11 +0000
2966@@ -26,9 +26,14 @@
2967 "path/filepath"
2968 "testing"
2969
2970+<<<<<<< TREE
2971 "launchpad.net/snappy/helpers"
2972
2973 . "gopkg.in/check.v1"
2974+=======
2975+ . "gopkg.in/check.v1"
2976+ "launchpad.net/snappy/helpers"
2977+>>>>>>> MERGE-SOURCE
2978 )
2979
2980 // Hook up check.v1 into the "go test" runner.
2981@@ -50,12 +55,20 @@
2982 originalCmdAutopilotEnabled = cmdAutopilotEnabled
2983 originalCmdSystemctl = cmdSystemctl
2984 originalHostnamePath = hostnamePath
2985+<<<<<<< TREE
2986 originalModprobePath = modprobePath
2987 originalInterfacesRoot = interfacesRoot
2988 originalPppRoot = pppRoot
2989 originalWatchdogStartupPath = watchdogStartupPath
2990 originalWatchdogConfigPath = watchdogConfigPath
2991 originalTzZoneInfoTarget = tzZoneInfoTarget
2992+=======
2993+ originalModprobePath = modprobePath
2994+ originalInterfacesRoot = interfacesRoot
2995+ originalPppRoot = pppRoot
2996+ originalWatchdogStartupPath = watchdogStartupPath
2997+ originalWatchdogConfigPath = watchdogConfigPath
2998+>>>>>>> MERGE-SOURCE
2999 )
3000
3001 type ConfigTestSuite struct {
3002@@ -82,12 +95,20 @@
3003 hostname = host
3004 return nil
3005 }
3006+<<<<<<< TREE
3007 tzZoneInfoTarget = filepath.Join(c.MkDir(), "localtime")
3008
3009 interfacesRoot = c.MkDir() + "/"
3010 pppRoot = c.MkDir() + "/"
3011 watchdogConfigPath = filepath.Join(c.MkDir(), "watchdog-config")
3012 watchdogStartupPath = filepath.Join(c.MkDir(), "watchdog-startup")
3013+=======
3014+
3015+ interfacesRoot = c.MkDir() + "/"
3016+ pppRoot = c.MkDir() + "/"
3017+ watchdogConfigPath = filepath.Join(c.MkDir(), "watchdog-config")
3018+ watchdogStartupPath = filepath.Join(c.MkDir(), "watchdog-startup")
3019+>>>>>>> MERGE-SOURCE
3020 }
3021
3022 func (cts *ConfigTestSuite) TearDownTest(c *C) {
3023@@ -106,12 +127,20 @@
3024 cmdStopAutopilot = originalCmdStopAutopilot
3025 cmdAutopilotEnabled = originalCmdAutopilotEnabled
3026 cmdSystemctl = originalCmdSystemctl
3027+<<<<<<< TREE
3028 modprobePath = originalModprobePath
3029 interfacesRoot = originalInterfacesRoot
3030 pppRoot = originalPppRoot
3031 watchdogStartupPath = originalWatchdogStartupPath
3032 watchdogConfigPath = originalWatchdogConfigPath
3033 tzZoneInfoTarget = originalTzZoneInfoTarget
3034+=======
3035+ modprobePath = originalModprobePath
3036+ interfacesRoot = originalInterfacesRoot
3037+ pppRoot = originalPppRoot
3038+ watchdogStartupPath = originalWatchdogStartupPath
3039+ watchdogConfigPath = originalWatchdogConfigPath
3040+>>>>>>> MERGE-SOURCE
3041 }
3042
3043 // TestGet is a broad test, close enough to be an integration test for
3044@@ -156,6 +185,7 @@
3045 autopilot: false
3046 timezone: America/Argentina/Mendoza
3047 hostname: testhost
3048+<<<<<<< TREE
3049 modprobe: ""
3050 `
3051
3052@@ -184,6 +214,14 @@
3053 content, err := ioutil.ReadFile(tzZoneInfoTarget)
3054 c.Assert(err, IsNil)
3055 c.Assert(content, Not(DeepEquals), []byte(canary))
3056+=======
3057+ modprobe: ""
3058+`
3059+
3060+ rawConfig, err := Set(expected)
3061+ c.Assert(err, IsNil)
3062+ c.Assert(rawConfig, Equals, expected)
3063+>>>>>>> MERGE-SOURCE
3064 }
3065
3066 // TestSetAutopilot is a broad test, close enough to be an integration test.
3067
3068=== modified file 'debian/control'
3069--- debian/control 2015-09-14 19:04:47 +0000
3070+++ debian/control 2015-09-16 17:51:11 +0000
3071@@ -9,13 +9,18 @@
3072 fakeroot,
3073 gettext,
3074 golang-ar-dev,
3075+<<<<<<< TREE
3076 golang-check.v1-dev,
3077 golang-gettext-dev,
3078+=======
3079+ golang-check.v1-dev,
3080+>>>>>>> MERGE-SOURCE
3081 golang-go,
3082 golang-go-flags-dev,
3083 golang-go.crypto-dev,
3084 golang-goconfigparser-dev,
3085 golang-pb-dev,
3086+<<<<<<< TREE
3087 golang-uboot-go-dev,
3088 golang-yaml.v2-dev,
3089 golang-mux-dev,
3090@@ -24,6 +29,10 @@
3091 lsb-release,
3092 python3,
3093 python3-markdown
3094+=======
3095+ golang-uboot-go-dev,
3096+ golang-yaml.v2-dev
3097+>>>>>>> MERGE-SOURCE
3098 Standards-Version: 3.9.6
3099 Homepage: https://launchpad.net/snappy
3100 Vcs-Browser: http://bazaar.launchpad.net/~snappy-dev/snappy/trunk/files
3101
3102=== modified file 'debian/rules'
3103--- debian/rules 2015-09-16 11:32:44 +0000
3104+++ debian/rules 2015-09-16 17:51:11 +0000
3105@@ -4,9 +4,13 @@
3106 #export DH_VERBOSE=1
3107 export DH_OPTIONS
3108 export DH_GOPKG := launchpad.net/snappy
3109+<<<<<<< TREE
3110 #export DEB_BUILD_OPTIONS=nocheck
3111
3112 RELEASE = $(shell lsb_release -c -s)
3113+=======
3114+#export DEB_BUILD_OPTIONS=nocheck
3115+>>>>>>> MERGE-SOURCE
3116
3117 %:
3118 dh $@ --buildsystem=golang --with=golang --fail-missing --with systemd
3119@@ -37,6 +41,7 @@
3120 --no-enable \
3121 -pubuntu-snappy \
3122 snappy-autopilot.service
3123+<<<<<<< TREE
3124 # enable wait4network
3125 dh_systemd_enable \
3126 -pubuntu-snappy \
3127@@ -45,6 +50,12 @@
3128 dh_systemd_enable \
3129 -pubuntu-snappy \
3130 ubuntu-snappy.snapd.socket
3131+=======
3132+ # enable wait4network
3133+ dh_systemd_enable \
3134+ -pubuntu-snappy \
3135+ snappy-wait4network.service
3136+>>>>>>> MERGE-SOURCE
3137
3138 override_dh_systemd_start:
3139 # start boot-ok
3140@@ -68,6 +79,7 @@
3141 --no-start \
3142 -pubuntu-snappy \
3143 snappy-autopilot.service
3144+<<<<<<< TREE
3145 # start wait4network
3146 dh_systemd_start \
3147 -pubuntu-snappy \
3148@@ -90,6 +102,14 @@
3149 fi;
3150
3151 override_dh_auto_install: snappy.8
3152+=======
3153+ # start wait4network
3154+ dh_systemd_start \
3155+ -pubuntu-snappy \
3156+ snappy-wait4network.service
3157+
3158+override_dh_auto_install:
3159+>>>>>>> MERGE-SOURCE
3160 dh_auto_install -O--buildsystem=golang
3161 # we do not need this in the package, its just needed during build
3162 rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go
3163
3164=== added file 'debian/snappy-wait4network.service'
3165--- debian/snappy-wait4network.service 1970-01-01 00:00:00 +0000
3166+++ debian/snappy-wait4network.service 2015-09-16 17:51:11 +0000
3167@@ -0,0 +1,10 @@
3168+[Unit]
3169+Description=Wait for network
3170+After=network-online.target
3171+
3172+[Service]
3173+Type=oneshot
3174+RemainAfterExit=yes
3175+TimeoutStartSec=0
3176+ExecStart=/bin/sh -ec 'while [ -z "$( /sbin/ip route show 0/0 )" ]; do sleep 5; done'
3177+
3178
3179=== renamed file 'debian/snappy-wait4network.service' => 'debian/snappy-wait4network.service.moved'
3180=== modified file 'dependencies.tsv'
3181--- dependencies.tsv 2015-09-16 12:04:05 +0000
3182+++ dependencies.tsv 2015-09-16 17:51:11 +0000
3183@@ -6,9 +6,17 @@
3184 github.com/gosexy/gettext git 98b7b91596d20b96909e6b60d57411547dd9959c 2013-02-21T11:21:43Z
3185 github.com/jessevdk/go-flags git 1acbbaff2f347c412a0c7884873bd72cc9c1f5b4 2015-08-16T10:05:21Z
3186 github.com/mvo5/goconfigparser git 26426272dda20cc76aa1fa44286dc743d2972fe8 2015-02-12T09:37:50Z
3187+<<<<<<< TREE
3188 github.com/mvo5/uboot-go git 361f6ebcbb54f389d15dc9faefa000e996ba3e37 2015-07-22T06:53:46Z
3189 golang.org/x/crypto git 60052bd85f2d91293457e8811b0cf26b773de469 2015-06-22T23:34:07Z
3190 gopkg.in/check.v1 git 64131543e7896d5bcc6bd5a76287eb75ea96c673 2014-10-24T13:38:53Z
3191 gopkg.in/tomb.v2 git 14b3d72120e8d10ea6e6b7f87f7175734b1faab8 2014-06-26T14:46:23Z
3192+=======
3193+github.com/mvo5/uboot-go git 361f6ebcbb54f389d15dc9faefa000e996ba3e37 2015-07-22T06:53:46Z
3194+gopkg.in/check.v1 git 64131543e7896d5bcc6bd5a76287eb75ea96c673 2014-10-24T13:38:53Z
3195+>>>>>>> MERGE-SOURCE
3196 gopkg.in/yaml.v2 git 49c95bdc21843256fb6c4e0d370a05f24a0bf213 2015-02-24T22:57:58Z
3197+<<<<<<< TREE
3198 code.google.com/p/go.crypto hg 69e2a90ed92d03812364aeb947b7068dc42e561e 235
3199+=======
3200+>>>>>>> MERGE-SOURCE
3201
3202=== added directory 'etc'
3203=== renamed directory 'etc' => 'etc.moved'
3204=== added directory 'etc/grub.d'
3205=== added file 'etc/grub.d/09_snappy'
3206--- etc/grub.d/09_snappy 1970-01-01 00:00:00 +0000
3207+++ etc/grub.d/09_snappy 2015-09-16 17:51:11 +0000
3208@@ -0,0 +1,441 @@
3209+#!/bin/sh
3210+#---------------------------------------------------------------------
3211+# Summary: Grub bootloader logic for Ubuntu Snappy systems.
3212+# Description: This is a heavily modified "10_linux" grub snippet that
3213+# deals with Snappy dual-rootfs systems.
3214+#
3215+# XXX: Note that this script is called from within a chroot environment
3216+# on snappy systems!
3217+#
3218+#---------------------------------------------------------------------
3219+
3220+set -e
3221+
3222+prefix="/usr"
3223+exec_prefix="/usr"
3224+datarootdir="/usr/share"
3225+
3226+# Utility functions
3227+. "${datarootdir}/grub/grub-mkconfig_lib"
3228+
3229+# Globals
3230+machine=`uname -m`
3231+
3232+SNAPPY_OS="Ubuntu Core Snappy"
3233+SNAPPY_TYPE=simple
3234+
3235+#---------------------------------------------------------------------
3236+
3237+# Display message and exit
3238+die()
3239+{
3240+ msg="$*"
3241+ echo "ERROR: $msg" >&2
3242+ exit 1
3243+}
3244+
3245+# Create a grub menu entry by writing a menuentry to stdout.
3246+linux_entry_ext()
3247+{
3248+ local name="$1"
3249+ local os="$2"
3250+ local version="$3"
3251+ local type="$4"
3252+ local args="$5"
3253+ local device="$6"
3254+ local kernel="$7"
3255+ local initrd="$8"
3256+
3257+ local boot_device_id=
3258+ local prepare_root_cache=
3259+ local prepare_boot_cache=
3260+
3261+ if [ -z "$boot_device_id" ]; then
3262+ boot_device_id="$(grub_get_device_id "${device}")"
3263+ fi
3264+
3265+ echo "menuentry '$name' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
3266+
3267+ if [ x$type != xrecovery ] ; then
3268+ save_default_entry | grub_add_tab
3269+ fi
3270+
3271+ # Use ELILO's generic "efifb" when it's known to be available.
3272+ # FIXME: We need an interface to select vesafb in case efifb can't be used.
3273+ if [ "x$GRUB_GFXPAYLOAD_LINUX" = x ]; then
3274+ echo " load_video" | sed "s/^/$submenu_indentation/"
3275+ else
3276+ if [ "x$GRUB_GFXPAYLOAD_LINUX" != xtext ]; then
3277+ echo " load_video" | sed "s/^/$submenu_indentation/"
3278+ fi
3279+ fi
3280+ if ([ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]) && \
3281+ ([ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 1 ]); then
3282+ echo " gfxmode \$linux_gfx_mode" | sed "s/^/$submenu_indentation/"
3283+ fi
3284+
3285+ echo " insmod gzio" | sed "s/^/$submenu_indentation/"
3286+ echo " if [ x\$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi" | sed "s/^/$submenu_indentation/"
3287+
3288+ # device may be a label (LABEL=name), so convert back to full path
3289+ label_name=$(echo "$device"|sed 's/^LABEL=//g')
3290+ if [ "$device" = "$label_name" ]
3291+ then
3292+ device_path="$device"
3293+ else
3294+ # found a label
3295+ device_path=$(get_partition_from_label "$label_name")
3296+ fi
3297+
3298+ if [ x$dirname = x/ ]; then
3299+ if [ -z "${prepare_root_cache}" ]; then
3300+
3301+ prepare_root_cache="$(prepare_grub_to_access_device ${device_path} | grub_add_tab)"
3302+ fi
3303+ printf '%s\n' "${prepare_root_cache}" | sed "s/^/$submenu_indentation/"
3304+ else
3305+ if [ -z "${prepare_boot_cache}" ]; then
3306+ prepare_boot_cache="$(prepare_grub_to_access_device ${device_path} | grub_add_tab)"
3307+ fi
3308+ printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/"
3309+ fi
3310+
3311+ if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then
3312+ message="$(gettext_printf "Loading Linux %s ..." ${version})"
3313+ sed "s/^/$submenu_indentation/" << EOF
3314+ echo '$(echo "$message" | grub_quote)'
3315+EOF
3316+ fi
3317+
3318+ sed "s/^/$submenu_indentation/" << EOF
3319+ linux ${kernel} root=${device} ro ${args}
3320+EOF
3321+
3322+ if test -n "${initrd}"; then
3323+ # TRANSLATORS: ramdisk isn't identifier. Should be translated.
3324+ if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then
3325+ message="$(gettext_printf "Loading initial ramdisk ...")"
3326+ sed "s/^/$submenu_indentation/" << EOF
3327+ echo '$(echo "$message" | grub_quote)'
3328+EOF
3329+ fi
3330+ sed "s/^/$submenu_indentation/" << EOF
3331+ initrd ${initrd}
3332+EOF
3333+ fi
3334+
3335+ sed "s/^/$submenu_indentation/" << EOF
3336+}
3337+EOF
3338+}
3339+
3340+# Returns a list of the currently available kernels.
3341+# $1: If set, look for kernel below "$1/boot/".
3342+get_kernels()
3343+{
3344+ local prefix_dir="$1"
3345+ local list
3346+
3347+ case "x$machine" in
3348+ xi?86 | xx86_64)
3349+ list=`for i in $prefix_dir/boot/vmlinuz-* \
3350+ $prefix_dir/vmlinuz-* \
3351+ $prefix_dir/boot/kernel-* ; do
3352+ if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
3353+ done` ;;
3354+ *)
3355+ list=`for i in $prefix_dir/boot/vmlinuz-* \
3356+ $prefix_dir/boot/vmlinux-* \
3357+ $prefix_dir/vmlinuz-* \
3358+ $prefix_dir/vmlinux-* \
3359+ $prefix_dir/boot/kernel-* ; do
3360+ if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
3361+ done` ;;
3362+ esac
3363+ echo "$list"
3364+}
3365+
3366+# Composes and returns a kernel cmd line
3367+get_cmdline() {
3368+ channel_ini="/etc/system-image/channel.ini"
3369+ grep -q 'device: azure_amd64' "$channel_ini" && azure_cmdline="rootdelay=300"
3370+
3371+ echo "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} $azure_cmdline"
3372+}
3373+
3374+# Returns the path to the initrd for the kernel specified by $1.
3375+# $1: kernel version.
3376+# $2: directory to look in.
3377+get_initrd()
3378+{
3379+ local version="$1"
3380+ local dir="$2"
3381+
3382+ local alt_version=`echo $version | sed -e "s,\.old$,,g"`
3383+ local initrd=
3384+ local i=
3385+
3386+ for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
3387+ "initrd-${version}" "initramfs-${version}.img" \
3388+ "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
3389+ "initrd-${alt_version}" "initramfs-${alt_version}.img" \
3390+ "initramfs-genkernel-${version}" \
3391+ "initramfs-genkernel-${alt_version}" \
3392+ "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
3393+ "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
3394+ if test -e "${dir}/${i}" ; then
3395+ initrd="${dir}/${i}"
3396+ break
3397+ fi
3398+ done
3399+ echo "$initrd"
3400+}
3401+
3402+# Determine full path to disk partition given a filesystem label.
3403+get_partition_from_label()
3404+{
3405+ local label="$1"
3406+ local part=
3407+ local path=
3408+
3409+ [ -n "$label" ] || grub_warn "need FS label"
3410+
3411+ part=$(find /dev -name "$label"|tail -1)
3412+ [ -z "$part" ] && return
3413+ path=$(readlink -f "$part")
3414+ [ -n "$path" ] && echo "$path"
3415+}
3416+
3417+# Return the partition label for the given partition device.
3418+# $1: full path to partition device.
3419+get_label_from_device()
3420+{
3421+ local root="$1"
3422+
3423+ local label=
3424+ local std_label=
3425+ local label_rootfs=
3426+
3427+ for std_label in system-a system-b; do
3428+ label_rootfs=$(findfs "PARTLABEL=$std_label" 2>/dev/null || :)
3429+ if [ "$label_rootfs" = "$root" ]; then
3430+ label="$std_label"
3431+ break
3432+ fi
3433+ done
3434+
3435+ echo "$label"
3436+}
3437+
3438+# Return the full path to the device corresponding to the given
3439+# partition label.
3440+#
3441+# $1: partition label.
3442+get_device_from_label()
3443+{
3444+ local label="$1"
3445+ local device=
3446+
3447+ device=$(findfs "PARTLABEL=$label" 2>/dev/null || :)
3448+ echo "$device"
3449+}
3450+
3451+# Given a rootfs label, return the rootfs label corresponding to the
3452+# "other" rootfs partition.
3453+get_other_rootfs_label()
3454+{
3455+ local label="$1"
3456+
3457+ if [ "$label" = "system-a" ]; then
3458+ echo "system-b"
3459+ else
3460+ echo "system-a"
3461+ fi
3462+}
3463+
3464+# Given a mountpoint, return the corresponding device path
3465+# $1: mountpoint.
3466+get_device_from_mountpoint()
3467+{
3468+ local mountpoint="$1"
3469+ local device=
3470+
3471+ # XXX: Parse mount output rather than looking in /proc/mounts to
3472+ # avoid seeing the mounts outside the chroot.
3473+ device=$(/bin/mount | grep "^/dev/.* on ${mountpoint}[[:space:]]" 2>/dev/null |\
3474+ awk '{print $1}' || :)
3475+
3476+ echo "$device"
3477+}
3478+
3479+# Convert a partition label name to a menuentry name
3480+make_name()
3481+{
3482+ local label="$1"
3483+
3484+ echo "$SNAPPY_OS $label rootfs" | grub_quote
3485+}
3486+
3487+# Arrange for a grub menuentry to be created for the given partition.
3488+#
3489+# $1: full path to rootfs partition device
3490+# $2: partition label associated with $1
3491+# $3: mountpoint of $1.
3492+handle_menu_entry()
3493+{
3494+ local rootfs_device="$1"
3495+ local label="$2"
3496+ local mountpoint="$3"
3497+
3498+ local name=
3499+ local device=
3500+ local mount_prefix=
3501+ local list=
3502+ local linux=
3503+ local basename=
3504+ local dirname=
3505+ local rel_dirname=
3506+ local version=
3507+ local initrd=
3508+ local cmdline=
3509+
3510+ # boot by label
3511+ device="LABEL=$label"
3512+
3513+ name=$(make_name "$label")
3514+
3515+ # avoid double-leading slashes and the subsequent need to call
3516+ # 'readlink -f'.
3517+ if [ "$mountpoint" = "/" ]; then
3518+ mount_prefix=""
3519+ else
3520+ mount_prefix="$mountpoint"
3521+ fi
3522+ list=$(get_kernels "$mount_prefix")
3523+
3524+ linux=$(version_find_latest $list)
3525+ basename=$(basename "$linux")
3526+ dirname=$(dirname "$linux")
3527+ rel_dirname=$(make_system_path_relative_to_its_root "$dirname")
3528+ version=$(echo "$basename" | sed -e "s,^[^0-9]*-,,g")
3529+
3530+ initrd=$(get_initrd "$version" "$dirname")
3531+
3532+ cmdline=$(get_cmdline)
3533+
3534+ # convert the path to the mounted "other" rootfs back to a
3535+ # a standard one by removing the mountpoint prefix.
3536+ if [ "$mountpoint" != "/" ]; then
3537+ linux=$(echo "$linux" | sed "s!^${mountpoint}!!g")
3538+ initrd=$(echo "$initrd" | sed "s!^${mountpoint}!!g")
3539+ fi
3540+
3541+ # Create menu entries for the 2 snappy rootfs's.
3542+ linux_entry_ext "$name" "$SNAPPY_OS" "$version" \
3543+ "$SNAPPY_TYPE" "$cmdline" "$device" \
3544+ "$linux" "$initrd"
3545+}
3546+
3547+#---------------------------------------------------------------------
3548+# main
3549+
3550+case "$machine" in
3551+ i?86) GENKERNEL_ARCH="x86" ;;
3552+ mips|mips64) GENKERNEL_ARCH="mips" ;;
3553+ mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;;
3554+ arm*) GENKERNEL_ARCH="arm" ;;
3555+ *) GENKERNEL_ARCH="$machine" ;;
3556+esac
3557+
3558+# Determine which partition label is being used for the current root
3559+# directory. This is slightly convoluted but required since this code
3560+# runs within a chroot environment (where lsblk does not work).
3561+#
3562+# XXX: Note that since this code is run from within a chroot (where the
3563+# "other" rootfs is mounted), it might appear that the logic is
3564+# inverted. However, the code below simply
3565+this_mountpoint="/"
3566+this_root=$(get_device_from_mountpoint "$this_mountpoint")
3567+[ -z "$this_root" ] && {
3568+ die "cannot determine rootfs for $this_mountpoint"
3569+}
3570+
3571+this_label=$(get_label_from_device "$this_root")
3572+[ -z "$this_label" ] && {
3573+ die "cannot determine partition label for rootfs $this_root"
3574+}
3575+
3576+handle_menu_entry "$this_root" "$this_label" "$this_mountpoint"
3577+
3578+other_mountpoint="/writable/cache/system"
3579+
3580+# When this script is run on a real snappy system, even if there is only
3581+# a single rootfs provisioned, the other rootfs partition is expected to
3582+# be formatted and mounted.
3583+#
3584+# However this script is also run at provisioning time where
3585+# $other_mountpoint will not be a mountpoint. Therefore in the provisioning
3586+# scenario, only a single menuentry will be generated if only a single
3587+# rootfs is provisioned.
3588+if $(mountpoint -q "$other_mountpoint"); then
3589+ other_label=$(get_other_rootfs_label "$this_label")
3590+
3591+ other_root=$(get_device_from_label "$other_label")
3592+ [ -z "$other_root" ] && {
3593+ die "cannot determine rootfs"
3594+ }
3595+
3596+ handle_menu_entry "$other_root" "$other_label" "$other_mountpoint"
3597+fi
3598+
3599+# Toggle rootfs if previous boot failed.
3600+#
3601+# Since grub sets snappy_trial_boot, if it is _already_ set when grub starts
3602+# and we're in try mode, the previous boot must have failed to unset it,
3603+# so toggle the rootfs.
3604+sed "s/^/$submenu_indentation/" << EOF
3605+ # set defaults
3606+ if [ -z "\$snappy_mode" ]; then
3607+ set snappy_mode=regular
3608+ save_env snappy_mode
3609+ fi
3610+ if [ -z "\$snappy_ab" ]; then
3611+ set snappy_ab=a
3612+ save_env snappy_ab
3613+ fi
3614+
3615+ if [ "\$snappy_mode" = "try" ]; then
3616+ if [ "\$snappy_trial_boot" = "1" ]; then
3617+ # Previous boot failed to unset snappy_trial_boot, so toggle
3618+ # rootfs.
3619+ if [ "\$snappy_ab" = "a" ]; then
3620+ set default="$(make_name system-b)"
3621+ set snappy_ab=b
3622+ else
3623+ set snappy_ab=a
3624+ set default="$(make_name system-a)"
3625+ fi
3626+ save_env snappy_ab
3627+ else
3628+ # Trial mode so set the snappy_trial_boot (which snappy is
3629+ # expected to unset).
3630+ #
3631+ # Note: don't use the standard recordfail variable since that forces
3632+ # the menu to be displayed and sets an infinite timeout if set.
3633+ set snappy_trial_boot=1
3634+ save_env snappy_trial_boot
3635+
3636+ if [ "\$snappy_ab" = "a" ]; then
3637+ set default="$(make_name system-a)"
3638+ else
3639+ set default="$(make_name system-b)"
3640+ fi
3641+ fi
3642+ else
3643+ if [ "\$snappy_ab" = "a" ]; then
3644+ set default="$(make_name system-a)"
3645+ else
3646+ set default="$(make_name system-b)"
3647+ fi
3648+ fi
3649+EOF
3650
3651=== modified file 'helpers/cmp_test.go'
3652=== modified file 'helpers/helpers.go'
3653--- helpers/helpers.go 2015-09-01 10:00:49 +0000
3654+++ helpers/helpers.go 2015-09-16 17:51:11 +0000
3655@@ -26,6 +26,7 @@
3656 "encoding/hex"
3657 "fmt"
3658 "io"
3659+ "io/ioutil"
3660 "math/rand"
3661 "os"
3662 "os/exec"
3663@@ -198,14 +199,37 @@
3664 }
3665 }
3666
3667-// IsSupportedArchitecture returns true if the system architecture is in the
3668-// list of architectures.
3669-func IsSupportedArchitecture(architectures []string) bool {
3670- systemArch := UbuntuArchitecture()
3671-
3672- for _, arch := range architectures {
3673- if arch == "all" || arch == systemArch {
3674- return true
3675+<<<<<<< TREE
3676+// IsSupportedArchitecture returns true if the system architecture is in the
3677+// list of architectures.
3678+func IsSupportedArchitecture(architectures []string) bool {
3679+ systemArch := UbuntuArchitecture()
3680+
3681+ for _, arch := range architectures {
3682+ if arch == "all" || arch == systemArch {
3683+ return true
3684+=======
3685+// IsSupportedArchitecture returns true if the system architecture is in the
3686+// list of architectures.
3687+func IsSupportedArchitecture(architectures []string) bool {
3688+ systemArch := UbuntuArchitecture()
3689+
3690+ for _, arch := range architectures {
3691+ if arch == "all" || arch == systemArch {
3692+ return true
3693+ }
3694+ }
3695+
3696+ return false
3697+}
3698+
3699+// EnsureDir ensures that the given directory exists and if
3700+// not creates it with the given permissions.
3701+func EnsureDir(dir string, perm os.FileMode) (err error) {
3702+ if _, err := os.Stat(dir); os.IsNotExist(err) {
3703+ if err := os.MkdirAll(dir, perm); err != nil {
3704+ return err
3705+>>>>>>> MERGE-SOURCE
3706 }
3707 }
3708
3709@@ -360,6 +384,7 @@
3710 return syscall.Getuid() == 0 || syscall.Getgid() == 0
3711
3712 }
3713+<<<<<<< TREE
3714
3715 // MajorMinor returns the major/minor number of the given os.FileInfo
3716 func MajorMinor(info os.FileInfo) (uint32, uint32, error) {
3717@@ -530,3 +555,140 @@
3718
3719 return nil
3720 }
3721+=======
3722+
3723+func makeDirMap(dirName string) (map[string]struct{}, error) {
3724+ dir, err := ioutil.ReadDir(dirName)
3725+ if err != nil {
3726+ return nil, err
3727+ }
3728+
3729+ dirMap := make(map[string]struct{})
3730+ for _, dent := range dir {
3731+ if dent.IsDir() {
3732+ return nil, fmt.Errorf("subdirs are not supported")
3733+ }
3734+ dirMap[dent.Name()] = struct{}{}
3735+ }
3736+
3737+ return dirMap, nil
3738+}
3739+
3740+// RSyncWithDelete syncs srcDir to destDir
3741+func RSyncWithDelete(srcDirName, destDirName string) error {
3742+
3743+ // first remove everything thats not in srcdir
3744+ err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error {
3745+ if err != nil {
3746+ return err
3747+ }
3748+
3749+ // relative to the root "destDirName"
3750+ relPath := path[len(destDirName):]
3751+ if !FileExists(filepath.Join(srcDirName, relPath)) {
3752+ if err := os.RemoveAll(path); err != nil {
3753+ return err
3754+ }
3755+ if info.IsDir() {
3756+ return filepath.SkipDir
3757+ }
3758+ }
3759+ return nil
3760+ })
3761+ if err != nil {
3762+ return err
3763+ }
3764+
3765+ // then copy or update the data from srcdir to destdir
3766+ err = filepath.Walk(srcDirName, func(path string, info os.FileInfo, err error) error {
3767+ if err != nil {
3768+ return err
3769+ }
3770+
3771+ // relative to the root "srcDirName"
3772+ relPath := path[len(srcDirName):]
3773+ if info.IsDir() {
3774+ return os.MkdirAll(filepath.Join(destDirName, relPath), info.Mode())
3775+ }
3776+ src := path
3777+ dst := filepath.Join(destDirName, relPath)
3778+ if !FilesAreEqual(src, dst) {
3779+ // XXX: on snappy-trunk we can use CopyFile here
3780+ output, err := exec.Command("cp", "-va", src, dst).CombinedOutput()
3781+ if err != nil {
3782+ return fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output)
3783+ }
3784+ }
3785+ return nil
3786+ })
3787+
3788+ return err
3789+}
3790+
3791+func fillSnapEnvVars(desc interface{}, vars []string) []string {
3792+ for i, v := range vars {
3793+ var templateOut bytes.Buffer
3794+ t := template.Must(template.New("wrapper").Parse(v))
3795+ if err := t.Execute(&templateOut, desc); err != nil {
3796+ // this can never happen, except we forget a variable
3797+ logger.LogAndPanic(err)
3798+ }
3799+ vars[i] = templateOut.String()
3800+ }
3801+ return vars
3802+}
3803+
3804+// GetBasicSnapEnvVars returns the app-level environment variables for a snap.
3805+// Despite this being a bit snap-specific, this is in helpers.go because it's
3806+// used by so many other modules, we run into circular dependencies if it's
3807+// somewhere more reasonable like the snappy module.
3808+func GetBasicSnapEnvVars(desc interface{}) []string {
3809+ return fillSnapEnvVars(desc, []string{
3810+ "TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3811+ "TEMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3812+ "SNAP_APP_PATH={{.AppPath}}",
3813+ "SNAP_APP_DATA_PATH=/var/lib{{.AppPath}}",
3814+ "SNAP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3815+ "SNAP_NAME={{.AppName}}",
3816+ "SNAP_VERSION={{.Version}}",
3817+ "SNAP_ORIGIN={{.Namespace}}",
3818+ "SNAP_FULLNAME={{.UdevAppName}}",
3819+ "SNAP_ARCH={{.AppArch}}",
3820+ })
3821+}
3822+
3823+// GetUserSnapEnvVars returns the user-level environment variables for a snap.
3824+// Despite this being a bit snap-specific, this is in helpers.go because it's
3825+// used by so many other modules, we run into circular dependencies if it's
3826+// somewhere more reasonable like the snappy module.
3827+func GetUserSnapEnvVars(desc interface{}) []string {
3828+ return fillSnapEnvVars(desc, []string{
3829+ "SNAP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
3830+ })
3831+}
3832+
3833+// GetDeprecatedBasicSnapEnvVars returns the app-level deprecated environment
3834+// variables for a snap.
3835+// Despite this being a bit snap-specific, this is in helpers.go because it's
3836+// used by so many other modules, we run into circular dependencies if it's
3837+// somewhere more reasonable like the snappy module.
3838+func GetDeprecatedBasicSnapEnvVars(desc interface{}) []string {
3839+ return fillSnapEnvVars(desc, []string{
3840+ "SNAPP_APP_PATH={{.AppPath}}",
3841+ "SNAPP_APP_DATA_PATH=/var/lib{{.AppPath}}",
3842+ "SNAPP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3843+ "SNAPPY_APP_ARCH={{.AppArch}}",
3844+ })
3845+}
3846+
3847+// GetDeprecatedUserSnapEnvVars returns the user-level deprecated environment
3848+// variables for a snap.
3849+// Despite this being a bit snap-specific, this is in helpers.go because it's
3850+// used by so many other modules, we run into circular dependencies if it's
3851+// somewhere more reasonable like the snappy module.
3852+func GetDeprecatedUserSnapEnvVars(desc interface{}) []string {
3853+ return fillSnapEnvVars(desc, []string{
3854+ "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
3855+ })
3856+}
3857+>>>>>>> MERGE-SOURCE
3858
3859=== modified file 'helpers/helpers_test.go'
3860--- helpers/helpers_test.go 2015-08-21 12:14:16 +0000
3861+++ helpers/helpers_test.go 2015-09-16 17:51:11 +0000
3862@@ -271,6 +271,7 @@
3863 c.Assert(err, IsNil)
3864 c.Assert(home, Equals, oldHome)
3865 }
3866+<<<<<<< TREE
3867
3868 func skipOnMissingDevKmsg(c *C) {
3869 _, err := os.Stat("/dev/kmsg")
3870@@ -483,3 +484,106 @@
3871 err := CopyIfDifferent(src, dst)
3872 c.Assert(err, NotNil)
3873 }
3874+=======
3875+
3876+func makeTestFiles(c *C, srcDir, destDir string) {
3877+ // a new file
3878+ err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644)
3879+ c.Assert(err, IsNil)
3880+
3881+ // a existing file that needs update
3882+ err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644)
3883+ c.Assert(err, IsNil)
3884+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644)
3885+ c.Assert(err, IsNil)
3886+
3887+ // existing file that needs no update
3888+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644)
3889+ c.Assert(err, IsNil)
3890+ err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run()
3891+ c.Assert(err, IsNil)
3892+
3893+ // a file that needs removal
3894+ err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644)
3895+ c.Assert(err, IsNil)
3896+}
3897+
3898+func compareDirs(c *C, srcDir, destDir string) {
3899+ d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput()
3900+ c.Assert(err, IsNil)
3901+ d2, err := exec.Command("ls", "-al", destDir).CombinedOutput()
3902+ c.Assert(err, IsNil)
3903+ c.Assert(string(d1), Equals, string(d2))
3904+ // ensure content got updated
3905+ c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput()
3906+ c.Assert(err, IsNil)
3907+ c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput()
3908+ c.Assert(err, IsNil)
3909+ c.Assert(string(c1), Equals, string(c2))
3910+}
3911+
3912+func (ts *HTestSuite) TestSyncDirs(c *C) {
3913+
3914+ for _, l := range [][2]string{
3915+ [2]string{"src-short", "dst-loooooooooooong"},
3916+ [2]string{"src-loooooooooooong", "dst-short"},
3917+ [2]string{"src-eq", "dst-eq"},
3918+ } {
3919+
3920+ // ensure we have src, dest dirs with different length
3921+ srcDir := filepath.Join(c.MkDir(), l[0])
3922+ err := os.MkdirAll(srcDir, 0755)
3923+ c.Assert(err, IsNil)
3924+ destDir := filepath.Join(c.MkDir(), l[1])
3925+ err = os.MkdirAll(destDir, 0755)
3926+ c.Assert(err, IsNil)
3927+
3928+ // add a src subdir
3929+ subdir := filepath.Join(srcDir, "subdir")
3930+ err = os.Mkdir(subdir, 0755)
3931+ c.Assert(err, IsNil)
3932+ makeTestFiles(c, subdir, destDir)
3933+
3934+ // add a dst subdir that needs to get deleted
3935+ subdir2 := filepath.Join(destDir, "to-be-deleted-subdir")
3936+ err = os.Mkdir(subdir2, 0755)
3937+ subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir")
3938+ err = os.Mkdir(subdir3, 0755)
3939+
3940+ // and a toplevel
3941+ makeTestFiles(c, srcDir, destDir)
3942+
3943+ // do it
3944+ err = RSyncWithDelete(srcDir, destDir)
3945+ c.Assert(err, IsNil)
3946+
3947+ // ensure meta-data is identical
3948+ compareDirs(c, srcDir, destDir)
3949+ compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir"))
3950+ }
3951+}
3952+
3953+func (ts *HTestSuite) TestSyncDirFails(c *C) {
3954+ srcDir := c.MkDir()
3955+ err := os.MkdirAll(srcDir, 0755)
3956+ c.Assert(err, IsNil)
3957+
3958+ destDir := c.MkDir()
3959+ err = os.MkdirAll(destDir, 0755)
3960+ c.Assert(err, IsNil)
3961+
3962+ err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644)
3963+ c.Assert(err, IsNil)
3964+
3965+ // ensure remove fails
3966+ err = os.Chmod(destDir, 0100)
3967+ c.Assert(err, IsNil)
3968+ // make tempdir cleanup work again
3969+ defer os.Chmod(destDir, 0755)
3970+
3971+ // do it
3972+ err = RSyncWithDelete(srcDir, destDir)
3973+ c.Check(err, NotNil)
3974+ c.Check(err, ErrorMatches, ".*permission denied.*")
3975+}
3976+>>>>>>> MERGE-SOURCE
3977
3978=== modified file 'helpers/touch.go'
3979=== modified file 'helpers/touch_test.go'
3980=== added directory 'logger'
3981=== renamed directory 'logger' => 'logger.moved'
3982=== added file 'logger/logger_test.go'
3983--- logger/logger_test.go 1970-01-01 00:00:00 +0000
3984+++ logger/logger_test.go 2015-09-16 17:51:11 +0000
3985@@ -0,0 +1,331 @@
3986+/*
3987+ * Copyright (C) 2014-2015 Canonical Ltd
3988+ *
3989+ * This program is free software: you can redistribute it and/or modify
3990+ * it under the terms of the GNU General Public License version 3 as
3991+ * published by the Free Software Foundation.
3992+ *
3993+ * This program is distributed in the hope that it will be useful,
3994+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3995+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3996+ * GNU General Public License for more details.
3997+ *
3998+ * You should have received a copy of the GNU General Public License
3999+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4000+ *
4001+ */
4002+
4003+package logger
4004+
4005+import (
4006+ "bytes"
4007+ "errors"
4008+ "fmt"
4009+ "log/syslog"
4010+ "regexp"
4011+ "strings"
4012+ "testing"
4013+ "time"
4014+
4015+ "github.com/juju/loggo"
4016+ . "gopkg.in/check.v1"
4017+)
4018+
4019+func Test(t *testing.T) { TestingT(t) }
4020+
4021+type LoggerTestSuite struct {
4022+}
4023+
4024+var _ = Suite(&LoggerTestSuite{})
4025+
4026+type MockLogWriter struct {
4027+ buf bytes.Buffer
4028+}
4029+
4030+var mockWriter *MockLogWriter
4031+
4032+var loggoLevels = []string{"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
4033+
4034+func mockGetSyslog(priority syslog.Priority, tag string) (w logWriterInterface, err error) {
4035+ mockWriter = &MockLogWriter{}
4036+ return mockWriter, nil
4037+}
4038+
4039+func readLines() (lines []string) {
4040+ lines = strings.Split(string(mockWriter.buf.Bytes()), "\n")
4041+
4042+ // remove the last line if empty due to split.
4043+ length := len(lines)
4044+ last := lines[length-1]
4045+ if last == "" {
4046+ lines = lines[:length-1]
4047+ }
4048+ // clear the buffer to avoid contents accumulating indefinitely
4049+ mockWriter.buf.Reset()
4050+ return lines
4051+}
4052+
4053+func (ts *LoggerTestSuite) SetUpTest(c *C) {
4054+ getSyslog = mockGetSyslog
4055+}
4056+
4057+func (w *MockLogWriter) Debug(m string) error {
4058+ _, err := w.buf.Write([]byte(fmt.Sprintf("DEBUG: %s\n", m)))
4059+ return err
4060+}
4061+
4062+func (w *MockLogWriter) Info(m string) error {
4063+ _, err := w.buf.Write([]byte(fmt.Sprintf("INFO: %s\n", m)))
4064+ return err
4065+}
4066+
4067+func (w *MockLogWriter) Warning(m string) error {
4068+ _, err := w.buf.Write([]byte(fmt.Sprintf("WARNING: %s\n", m)))
4069+ return err
4070+}
4071+
4072+func (w *MockLogWriter) Err(m string) error {
4073+ _, err := w.buf.Write([]byte(fmt.Sprintf("ERROR: %s\n", m)))
4074+ return err
4075+}
4076+
4077+func (w *MockLogWriter) Crit(m string) error {
4078+ _, err := w.buf.Write([]byte(fmt.Sprintf("CRITICAL: %s\n", m)))
4079+ return err
4080+}
4081+
4082+// Search for value in array and return true if found
4083+func sliceContainsString(array []string, value string) bool {
4084+ str := string(strings.Join(array, ""))
4085+
4086+ return strings.Contains(str, value)
4087+}
4088+
4089+// Return true if array contains the pattern regex.
4090+func sliceContainsRegex(array []string, regex string) bool {
4091+ str := string(strings.Join(array, ""))
4092+
4093+ pattern := regexp.MustCompile(regex)
4094+
4095+ matches := pattern.FindAllStringSubmatch(str, -1)
4096+
4097+ return matches != nil
4098+}
4099+
4100+func (ts *LoggerTestSuite) TestNewLogWriter(c *C) {
4101+ var w, w2 *LogWriter
4102+ var err error
4103+
4104+ w, err = newLogWriter()
4105+ c.Assert(err, IsNil)
4106+ c.Assert(w, Not(IsNil))
4107+ c.Assert(w.systemLog, Not(IsNil))
4108+
4109+ w2, err = newLogWriter()
4110+ c.Assert(err, IsNil)
4111+ c.Assert(w2, Not(IsNil))
4112+ c.Assert(w2.systemLog, Not(IsNil))
4113+
4114+ // There should be a single shared syslog connection, hence the
4115+ // systemLog objects should be identical.
4116+ c.Assert(w.systemLog, Equals, w2.systemLog)
4117+ c.Assert(w.systemLog, DeepEquals, w2.systemLog)
4118+}
4119+
4120+func (ts *LoggerTestSuite) TestWrite(c *C) {
4121+ w, err := newLogWriter()
4122+ c.Assert(err, IsNil)
4123+ c.Assert(w, Not(IsNil))
4124+ c.Assert(w.systemLog, Not(IsNil))
4125+
4126+ t := time.Now()
4127+ strTime := fmt.Sprintf("%s", t)
4128+
4129+ for _, l := range loggoLevels {
4130+ level := stringToLogLevel(l)
4131+
4132+ w.Write(level, "module", "filename", 1234, t, "a message")
4133+ lines := readLines()
4134+
4135+ if level < loggo.ERROR {
4136+ c.Assert(len(lines), Equals, 1)
4137+ } else {
4138+ c.Assert(len(lines) > 1, Equals, true)
4139+
4140+ c.Assert(sliceContainsString(lines, "filename"), Equals, true)
4141+ c.Assert(sliceContainsString(lines, "1234"), Equals, true)
4142+ }
4143+
4144+ c.Assert(sliceContainsString(lines, "module"), Equals, true)
4145+
4146+ // We discard the timestamp as syslog adds that itself
4147+ c.Assert(sliceContainsString(lines, strTime), Equals, false)
4148+
4149+ c.Assert(sliceContainsString(lines, "a message"), Equals, true)
4150+ }
4151+
4152+}
4153+
4154+// Convert a loggo log level string representation into a real log
4155+// level.
4156+func stringToLogLevel(name string) loggo.Level {
4157+ level, ok := loggo.ParseLevel(name)
4158+
4159+ if !ok {
4160+ panic(fmt.Sprintf("unknown loggo level string: %q", name))
4161+ }
4162+
4163+ return level
4164+}
4165+
4166+func (ts *LoggerTestSuite) TestFormat(c *C) {
4167+ w, err := newLogWriter()
4168+ c.Assert(err, IsNil)
4169+ c.Assert(w, Not(IsNil))
4170+ c.Assert(w.systemLog, Not(IsNil))
4171+
4172+ for _, l := range loggoLevels {
4173+ level := stringToLogLevel(l)
4174+
4175+ if level < loggo.ERROR {
4176+ out := w.Format(level, "module", "filename", 1234, time.Now(), "a message")
4177+ c.Assert(out, Equals, fmt.Sprintf("%s:%s:%s", l, "module", "a message"))
4178+ } else {
4179+ out := w.Format(level, "module", "filename", 1234, time.Now(), "a message")
4180+ c.Assert(out, Equals, fmt.Sprintf("%s:%s:%s:%d:%s", l, "module", "filename", 1234, "a message"))
4181+ }
4182+ }
4183+}
4184+
4185+func (ts *LoggerTestSuite) TestLogStackTrace(c *C) {
4186+ var output []string
4187+
4188+ w, err := newLogWriter()
4189+ c.Assert(err, IsNil)
4190+ c.Assert(w, Not(IsNil))
4191+
4192+ f := func(s string) error {
4193+ output = append(output, s)
4194+ return nil
4195+ }
4196+
4197+ t := time.Now()
4198+ strTime := fmt.Sprintf("%s", t)
4199+
4200+ w.logStacktrace(loggo.DEBUG, "name", "filename", 9876, t, f)
4201+
4202+ c.Assert(sliceContainsString(output, "Stack trace"), Equals, true)
4203+ c.Assert(sliceContainsString(output, "name"), Equals, true)
4204+ c.Assert(sliceContainsString(output, "filename"), Equals, true)
4205+ c.Assert(sliceContainsString(output, "9876"), Equals, true)
4206+
4207+ // We discard the timestamp as syslog adds that itself
4208+ c.Assert(sliceContainsString(output, strTime), Equals, false)
4209+}
4210+
4211+func (ts *LoggerTestSuite) checkLogLevel(c *C, level, msg string) {
4212+ err := ActivateLogger()
4213+ c.Assert(err, IsNil)
4214+
4215+ expectBacktrace := (level == "ERROR" || level == "CRITICAL")
4216+
4217+ logger := loggo.GetLogger("snappy")
4218+ c.Assert(logger, Not(IsNil))
4219+
4220+ switch level {
4221+ case "DEBUG":
4222+ c.Assert(logger.IsDebugEnabled(), Equals, true)
4223+ logger.Debugf(msg)
4224+
4225+ case "INFO":
4226+ c.Assert(logger.IsInfoEnabled(), Equals, true)
4227+ logger.Infof(msg)
4228+
4229+ case "WARNING":
4230+ c.Assert(logger.IsWarningEnabled(), Equals, true)
4231+ logger.Warningf(msg)
4232+
4233+ case "ERROR":
4234+ c.Assert(logger.IsErrorEnabled(), Equals, true)
4235+ logger.Errorf(msg)
4236+
4237+ case "CRITICAL":
4238+ // loggo doesn't provide a IsCriticalEnabled()
4239+ c.Assert(logger.IsErrorEnabled(), Equals, true)
4240+ logger.Criticalf(msg)
4241+ }
4242+
4243+ lines := readLines()
4244+
4245+ if expectBacktrace {
4246+ c.Assert(len(lines) > 1, Equals, true)
4247+ } else {
4248+ c.Assert(len(lines), Equals, 1)
4249+ }
4250+
4251+ needle := fmt.Sprintf("%s.*%s", level, msg)
4252+ c.Assert(sliceContainsRegex(lines, needle), Equals, true)
4253+
4254+ c.Assert(sliceContainsString(lines, "Stack trace"), Equals, expectBacktrace)
4255+}
4256+
4257+func (ts *LoggerTestSuite) TestLogLevels(c *C) {
4258+ msg := "an error message"
4259+
4260+ for _, level := range loggoLevels {
4261+ ts.checkLogLevel(c, level, msg)
4262+ }
4263+}
4264+
4265+func (ts *LoggerTestSuite) TestLogError(c *C) {
4266+ level := "ERROR"
4267+ msg := "I am an error"
4268+
4269+ err := ActivateLogger()
4270+ c.Assert(err, IsNil)
4271+
4272+ result := LogError(nil)
4273+ c.Assert(result, IsNil)
4274+
4275+ err = errors.New(msg)
4276+ c.Assert(err, Not(IsNil))
4277+
4278+ // We expect to get back exactly what was passsed...
4279+ result = LogError(err)
4280+ c.Assert(result, DeepEquals, err)
4281+
4282+ // ... but also to have the error logged
4283+ ts.checkLogLevel(c, level, msg)
4284+}
4285+
4286+func (ts *LoggerTestSuite) TestLogAndPanic(c *C) {
4287+ level := "CRITICAL"
4288+ msg := "I am a fatal error"
4289+
4290+ panicked := false
4291+
4292+ err := ActivateLogger()
4293+ c.Assert(err, IsNil)
4294+
4295+ // If the specified error is nil, no panic is expected and no
4296+ // log entry should be added.
4297+ func() {
4298+ defer func() {
4299+ if r := recover(); r != nil {
4300+ panicked = true
4301+ }
4302+ }()
4303+ LogAndPanic(nil)
4304+ }()
4305+
4306+ c.Assert(panicked, Equals, false)
4307+ c.Assert(len(readLines()), Equals, 0)
4308+
4309+ err = errors.New(msg)
4310+
4311+ // expect a panic...
4312+ c.Assert(func() { LogAndPanic(err) }, Panics, err)
4313+
4314+ // ... and a log entry
4315+ ts.checkLogLevel(c, level, msg)
4316+}
4317
4318=== modified file 'partition/bootloader.go'
4319=== modified file 'partition/bootloader_grub.go'
4320--- partition/bootloader_grub.go 2015-07-23 11:54:14 +0000
4321+++ partition/bootloader_grub.go 2015-09-16 17:51:11 +0000
4322@@ -32,7 +32,12 @@
4323 bootloaderGrubConfigFileReal = "/boot/grub/grub.cfg"
4324 bootloaderGrubEnvFileReal = "/boot/grub/grubenv"
4325
4326+<<<<<<< TREE
4327 bootloaderGrubEnvCmdReal = "/usr/bin/grub-editenv"
4328+=======
4329+ bootloaderGrubEnvCmdReal = "/usr/bin/grub-editenv"
4330+ bootloaderGrubUpdateCmdReal = "/usr/sbin/update-grub"
4331+>>>>>>> MERGE-SOURCE
4332 )
4333
4334 // var to make it testable
4335@@ -115,9 +120,24 @@
4336 return g.GetBootVar(bootloaderRootfsVar)
4337 }
4338
4339+<<<<<<< TREE
4340 func (g *grub) MarkCurrentBootSuccessful(currentRootfs string) (err error) {
4341 // Clear the variable set on boot to denote a good boot.
4342 if err := g.setBootVar(bootloaderTrialBootVar, "0"); err != nil {
4343+=======
4344+func (g *grub) GetRootFSName() string {
4345+ return g.currentRootfs
4346+}
4347+
4348+func (g *grub) GetOtherRootFSName() string {
4349+ return g.otherRootfs
4350+}
4351+
4352+func (g *grub) MarkCurrentBootSuccessful() (err error) {
4353+ // Clear the variable set by grub on boot to denote a good
4354+ // boot.
4355+ if err := g.unsetBootVar(bootloaderTrialBootVar); err != nil {
4356+>>>>>>> MERGE-SOURCE
4357 return err
4358 }
4359
4360
4361=== modified file 'partition/bootloader_grub_test.go'
4362--- partition/bootloader_grub_test.go 2015-07-23 11:54:14 +0000
4363+++ partition/bootloader_grub_test.go 2015-09-16 17:51:11 +0000
4364@@ -23,11 +23,16 @@
4365 "fmt"
4366 "io/ioutil"
4367 "os"
4368+<<<<<<< TREE
4369 "path/filepath"
4370
4371 "launchpad.net/snappy/helpers"
4372
4373 . "gopkg.in/check.v1"
4374+=======
4375+
4376+ . "gopkg.in/check.v1"
4377+>>>>>>> MERGE-SOURCE
4378 )
4379
4380 func mockGrubFile(c *C, newPath string, mode os.FileMode) {
4381
4382=== modified file 'partition/bootloader_uboot.go'
4383--- partition/bootloader_uboot.go 2015-07-24 12:00:01 +0000
4384+++ partition/bootloader_uboot.go 2015-09-16 17:51:11 +0000
4385@@ -55,9 +55,14 @@
4386 bootloaderUbootConfigFile = bootloaderUbootConfigFileReal
4387 bootloaderUbootStampFile = bootloaderUbootStampFileReal
4388 bootloaderUbootEnvFile = bootloaderUbootEnvFileReal
4389+<<<<<<< TREE
4390 bootloaderUbootFwEnvFile = bootloaderUbootFwEnvFileReal
4391
4392 atomicWriteFile = helpers.AtomicWriteFile
4393+=======
4394+ bootloaderUbootFwEnvFile = bootloaderUbootFwEnvFileReal
4395+ atomicFileUpdate = atomicFileUpdateImpl
4396+>>>>>>> MERGE-SOURCE
4397 )
4398
4399 const bootloaderNameUboot bootloaderName = "u-boot"
4400@@ -103,15 +108,64 @@
4401 return bootloaderNameUboot
4402 }
4403
4404+<<<<<<< TREE
4405 func (u *uboot) ToggleRootFS(otherRootfs string) (err error) {
4406 if err := setBootVar(bootloaderRootfsVar, string(otherRootfs)); err != nil {
4407 return err
4408+=======
4409+// ToggleRootFS make the U-Boot bootloader switch rootfs's.
4410+//
4411+// Approach:
4412+//
4413+// - Assume the device's installed version of u-boot supports
4414+// CONFIG_SUPPORT_RAW_INITRD (that allows u-boot to boot a
4415+// standard initrd+kernel on the fat32 disk partition).
4416+// - Copy the "other" rootfs's kernel+initrd to the boot partition,
4417+// renaming them in the process to ensure the next boot uses the
4418+// correct versions.
4419+func (u *uboot) ToggleRootFS() (err error) {
4420+ // modern system
4421+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4422+ return u.toggleRootFSFwEnv()
4423+ }
4424+
4425+ // legacy
4426+ return u.toggleRootFSLegacy()
4427+}
4428+
4429+func (u *uboot) toggleRootFSFwEnv() (err error) {
4430+ if err := u.setBootVar(bootloaderRootfsVar, string(u.otherRootfs)); err != nil {
4431+ return err
4432+ }
4433+
4434+ return u.setBootVar(bootloaderBootmodeVar, bootloaderBootmodeTry)
4435+}
4436+
4437+func (u *uboot) toggleRootFSLegacy() (err error) {
4438+ // If the file exists, update it. Otherwise create it.
4439+ //
4440+ // The file _should_ always exist, but since it's on a writable
4441+ // partition, it's possible the admin removed it by mistake. So
4442+ // recreate to allow the system to boot!
4443+ changes := []configFileChange{
4444+ configFileChange{Name: bootloaderRootfsVar,
4445+ Value: string(u.otherRootfs),
4446+ },
4447+ configFileChange{Name: bootloaderBootmodeVar,
4448+ Value: bootloaderBootmodeTry,
4449+ },
4450+>>>>>>> MERGE-SOURCE
4451 }
4452
4453 return setBootVar(bootloaderBootmodeVar, bootloaderBootmodeTry)
4454 }
4455
4456+<<<<<<< TREE
4457 func getBootVarLegacy(name string) (value string, err error) {
4458+=======
4459+func (u *uboot) getBootVarLegacy(name string) (value string, err error) {
4460+
4461+>>>>>>> MERGE-SOURCE
4462 cfg := goconfigparser.New()
4463 cfg.AllowNoSectionHeader = true
4464 if err := cfg.ReadFile(bootloaderUbootEnvFile); err != nil {
4465@@ -121,6 +175,7 @@
4466 return cfg.Get("", name)
4467 }
4468
4469+<<<<<<< TREE
4470 func setBootVarLegacy(name, value string) error {
4471 curVal, err := getBootVarLegacy(name)
4472 if err == nil && curVal == value {
4473@@ -165,6 +220,16 @@
4474 return getBootVar(name)
4475 }
4476
4477+=======
4478+func (u *uboot) GetBootVar(name string) (value string, err error) {
4479+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4480+ return u.getBootVar(name)
4481+ }
4482+
4483+ return u.getBootVarLegacy(name)
4484+}
4485+
4486+>>>>>>> MERGE-SOURCE
4487 func (u *uboot) GetNextBootRootFSName() (label string, err error) {
4488 value, err := u.GetBootVar(bootloaderRootfsVar)
4489 if err != nil {
4490@@ -175,6 +240,7 @@
4491 return value, nil
4492 }
4493
4494+<<<<<<< TREE
4495 // FIXME: this is super similar to grub now, refactor to extract the
4496 // common code
4497 func (u *uboot) MarkCurrentBootSuccessful(currentRootfs string) error {
4498@@ -197,6 +263,260 @@
4499
4500 func (u *uboot) BootDir() string {
4501 return bootloaderUbootDir
4502+=======
4503+func (u *uboot) GetRootFSName() string {
4504+ return u.currentRootfs
4505+}
4506+
4507+func (u *uboot) GetOtherRootFSName() string {
4508+ return u.otherRootfs
4509+}
4510+
4511+// FIXME: put into utils package
4512+func readLines(path string) (lines []string, err error) {
4513+
4514+ file, err := os.Open(path)
4515+
4516+ if err != nil {
4517+ return nil, err
4518+ }
4519+
4520+ defer file.Close()
4521+
4522+ scanner := bufio.NewScanner(file)
4523+ for scanner.Scan() {
4524+ lines = append(lines, scanner.Text())
4525+ }
4526+
4527+ return lines, scanner.Err()
4528+}
4529+
4530+// FIXME: put into utils package
4531+func writeLines(lines []string, path string) (err error) {
4532+
4533+ file, err := os.Create(path)
4534+
4535+ if err != nil {
4536+ return err
4537+ }
4538+
4539+ defer func() {
4540+ e := file.Close()
4541+ if err == nil {
4542+ err = e
4543+ }
4544+ }()
4545+
4546+ writer := bufio.NewWriter(file)
4547+
4548+ for _, line := range lines {
4549+ if _, err := fmt.Fprintln(writer, line); err != nil {
4550+ return err
4551+ }
4552+ }
4553+
4554+ if err := writer.Flush(); err != nil {
4555+ return err
4556+ }
4557+
4558+ return file.Sync()
4559+}
4560+
4561+func (u *uboot) SyncBootFiles() (err error) {
4562+ srcDir := u.currentBootPath
4563+ destDir := u.otherBootPath
4564+
4565+ return helpers.RSyncWithDelete(srcDir, destDir)
4566+}
4567+
4568+func (u *uboot) HandleAssets() (err error) {
4569+ // check if we have anything, if there is no hardware yaml, there is nothing
4570+ // to process.
4571+ hardware, err := u.partition.hardwareSpec()
4572+ if err == ErrNoHardwareYaml {
4573+ return nil
4574+ } else if err != nil {
4575+ return err
4576+ }
4577+ // ensure to remove the file once we are done
4578+ defer os.Remove(u.partition.hardwareSpecFile)
4579+
4580+ // validate bootloader
4581+ if hardware.Bootloader != u.Name() {
4582+ return fmt.Errorf(
4583+ "bootloader is of type %s but hardware spec requires %s",
4584+ u.Name(),
4585+ hardware.Bootloader)
4586+ }
4587+
4588+ // validate partition layout
4589+ if u.partition.dualRootPartitions() && hardware.PartitionLayout != bootloaderSystemAB {
4590+ return fmt.Errorf("hardware spec requires dual root partitions")
4591+ }
4592+
4593+ // ensure we have the destdir
4594+ destDir := u.otherBootPath
4595+ if err := os.MkdirAll(destDir, dirMode); err != nil {
4596+ return err
4597+ }
4598+
4599+ // install kernel+initrd
4600+ for _, file := range []string{hardware.Kernel, hardware.Initrd} {
4601+
4602+ if file == "" {
4603+ continue
4604+ }
4605+
4606+ // expand path
4607+ path := path.Join(u.partition.cacheDir(), file)
4608+
4609+ if !helpers.FileExists(path) {
4610+ return fmt.Errorf("can not find file %s", path)
4611+ }
4612+
4613+ // ensure we remove the dir later
4614+ defer os.RemoveAll(filepath.Dir(path))
4615+
4616+ if err := runCommand("/bin/cp", path, destDir); err != nil {
4617+ return err
4618+ }
4619+ }
4620+
4621+ // TODO: look at the OEM package for dtb changes too once that is
4622+ // fully speced
4623+
4624+ // install .dtb files
4625+ dtbSrcDir := filepath.Join(u.partition.cacheDir(), hardware.DtbDir)
4626+ if helpers.FileExists(dtbSrcDir) {
4627+ // ensure we cleanup the source dir
4628+ defer os.RemoveAll(dtbSrcDir)
4629+
4630+ dtbDestDir := path.Join(destDir, "dtbs")
4631+ if err := os.MkdirAll(dtbDestDir, dirMode); err != nil {
4632+ return err
4633+ }
4634+
4635+ files, err := filepath.Glob(path.Join(dtbSrcDir, "*"))
4636+ if err != nil {
4637+ return err
4638+ }
4639+
4640+ for _, file := range files {
4641+ if err := runCommand("/bin/cp", file, dtbDestDir); err != nil {
4642+ return err
4643+ }
4644+ }
4645+ }
4646+
4647+ flashAssetsDir := u.partition.flashAssetsDir()
4648+
4649+ if helpers.FileExists(flashAssetsDir) {
4650+ // FIXME: we don't currently do anything with the
4651+ // MLO + uImage files since they are not specified in
4652+ // the hardware spec. So for now, just remove them.
4653+
4654+ if err := os.RemoveAll(flashAssetsDir); err != nil {
4655+ return err
4656+ }
4657+ }
4658+
4659+ return err
4660+}
4661+
4662+func (u *uboot) setBootVar(name, value string) error {
4663+ env, err := uenv.Open(bootloaderUbootFwEnvFile)
4664+ if err != nil {
4665+ return err
4666+ }
4667+
4668+ // already set, nothing to do
4669+ if env.Get(name) == value {
4670+ return nil
4671+ }
4672+
4673+ env.Set(name, value)
4674+ return env.Save()
4675+}
4676+
4677+func (u *uboot) hasBootVar(name string) (bool, error) {
4678+ v, err := u.getBootVar(name)
4679+ return v != "", err
4680+}
4681+
4682+func (u *uboot) getBootVar(name string) (string, error) {
4683+ env, err := uenv.Open(bootloaderUbootFwEnvFile)
4684+ if err != nil {
4685+ return "", err
4686+ }
4687+
4688+ return env.Get(name), nil
4689+}
4690+
4691+// FIXME: this is super similar to grub now, refactor to extract the
4692+// common code
4693+func (u *uboot) markCurrentBootSuccessfulFwEnv(currentRootfs string) error {
4694+ // Clear the variable set on boot to denote a good boot.
4695+ if err := u.setBootVar(bootloaderTrialBootVar, "0"); err != nil {
4696+ return err
4697+ }
4698+
4699+ if err := u.setBootVar(bootloaderRootfsVar, currentRootfs); err != nil {
4700+ return err
4701+ }
4702+
4703+ return u.setBootVar(bootloaderBootmodeVar, bootloaderBootmodeSuccess)
4704+}
4705+
4706+func (u *uboot) markCurrentBootSuccessfulLegacy(currentRootfs string) error {
4707+ changes := []configFileChange{
4708+ configFileChange{Name: bootloaderBootmodeVar,
4709+ Value: bootloaderBootmodeSuccess,
4710+ },
4711+ configFileChange{Name: bootloaderRootfsVar,
4712+ Value: string(u.currentRootfs),
4713+ },
4714+ }
4715+
4716+ if err := modifyNameValueFile(bootloaderUbootEnvFile, changes); err != nil {
4717+ return err
4718+ }
4719+
4720+ return os.RemoveAll(bootloaderUbootStampFile)
4721+}
4722+
4723+func (u *uboot) MarkCurrentBootSuccessful() error {
4724+ // modern system
4725+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4726+ return u.markCurrentBootSuccessfulFwEnv(u.currentRootfs)
4727+ }
4728+
4729+ // legacy
4730+ return u.markCurrentBootSuccessfulLegacy(u.currentRootfs)
4731+}
4732+
4733+// Write lines to file atomically. File does not have to preexist.
4734+// FIXME: put into utils package
4735+func atomicFileUpdateImpl(file string, lines []string) (err error) {
4736+ tmpFile := fmt.Sprintf("%s.NEW", file)
4737+
4738+ // XXX: if go switches to use aio_fsync, we need to open the dir for writing
4739+ dir, err := os.Open(filepath.Dir(file))
4740+ if err != nil {
4741+ return err
4742+ }
4743+ defer dir.Close()
4744+
4745+ if err := writeLines(lines, tmpFile); err != nil {
4746+ return err
4747+ }
4748+
4749+ // atomic update
4750+ if err := os.Rename(tmpFile, file); err != nil {
4751+ return err
4752+ }
4753+
4754+ return dir.Sync()
4755+>>>>>>> MERGE-SOURCE
4756 }
4757
4758 // Rewrite the specified file, applying the specified set of changes.
4759@@ -206,8 +526,13 @@
4760 // appended to the file.
4761 //
4762 // FIXME: put into utils package
4763+<<<<<<< TREE
4764 // FIXME: improve logic
4765 func modifyNameValueFile(path string, changes []configFileChange) error {
4766+=======
4767+// FIXME: improve logic
4768+func modifyNameValueFile(file string, changes []configFileChange) (err error) {
4769+>>>>>>> MERGE-SOURCE
4770 var updated []configFileChange
4771
4772 // we won't write to a file if we don't need to.
4773@@ -217,12 +542,21 @@
4774 if err != nil {
4775 return err
4776 }
4777+<<<<<<< TREE
4778 defer file.Close()
4779
4780 buf := bytes.NewBuffer(nil)
4781 scanner := bufio.NewScanner(file)
4782 for scanner.Scan() {
4783 line := scanner.Text()
4784+=======
4785+
4786+ var new []string
4787+ // we won't write to a file if we don't need to.
4788+ updateNeeded := false
4789+
4790+ for _, line := range lines {
4791+>>>>>>> MERGE-SOURCE
4792 for _, change := range changes {
4793 if strings.HasPrefix(line, fmt.Sprintf("%s=", change.Name)) {
4794 value := strings.SplitN(line, "=", 2)[1]
4795@@ -260,9 +594,18 @@
4796 }
4797 }
4798
4799+<<<<<<< TREE
4800 if updateNeeded {
4801 return atomicWriteFile(path, buf.Bytes(), 0644)
4802 }
4803+=======
4804+ if updateNeeded {
4805+ return atomicFileUpdate(file, lines)
4806+ }
4807+
4808+ return nil
4809+}
4810+>>>>>>> MERGE-SOURCE
4811
4812 return nil
4813 }
4814
4815=== modified file 'partition/bootloader_uboot_test.go'
4816--- partition/bootloader_uboot_test.go 2015-07-24 11:58:04 +0000
4817+++ partition/bootloader_uboot_test.go 2015-09-16 17:51:11 +0000
4818@@ -382,3 +382,93 @@
4819 c.Assert(err, IsNil)
4820 c.Assert(st.ModTime(), Equals, st2.ModTime())
4821 }
4822+
4823+func (s *PartitionTestSuite) TestNoWriteNotNeeded(c *C) {
4824+ s.makeFakeUbootEnv(c)
4825+
4826+ atomiCall := false
4827+ atomicFileUpdate = func(a string, b []string) error { atomiCall = true; return atomicFileUpdateImpl(a, b) }
4828+
4829+ partition := New()
4830+ u := newUboot(partition)
4831+ c.Assert(u, NotNil)
4832+
4833+ c.Check(u.MarkCurrentBootSuccessful(), IsNil)
4834+ c.Assert(atomiCall, Equals, false)
4835+}
4836+
4837+func (s *PartitionTestSuite) TestWriteDueToMissingValues(c *C) {
4838+ s.makeFakeUbootEnv(c)
4839+
4840+ // this file needs specific data
4841+ c.Assert(ioutil.WriteFile(bootloaderUbootEnvFile, []byte(""), 0644), IsNil)
4842+
4843+ atomiCall := false
4844+ atomicFileUpdate = func(a string, b []string) error { atomiCall = true; return atomicFileUpdateImpl(a, b) }
4845+
4846+ partition := New()
4847+ u := newUboot(partition)
4848+ c.Assert(u, NotNil)
4849+
4850+ c.Check(u.MarkCurrentBootSuccessful(), IsNil)
4851+ c.Assert(atomiCall, Equals, true)
4852+
4853+ bytes, err := ioutil.ReadFile(bootloaderUbootEnvFile)
4854+ c.Assert(err, IsNil)
4855+ c.Check(strings.Contains(string(bytes), "snappy_mode=try"), Equals, false)
4856+ c.Check(strings.Contains(string(bytes), "snappy_mode=regular"), Equals, true)
4857+ c.Check(strings.Contains(string(bytes), "snappy_ab=a"), Equals, true)
4858+}
4859+
4860+func (s *PartitionTestSuite) TestUbootMarkCurrentBootSuccessfulFwEnv(c *C) {
4861+ s.makeFakeUbootEnv(c)
4862+
4863+ env, err := uenv.Create(bootloaderUbootFwEnvFile, 4096)
4864+ c.Assert(err, IsNil)
4865+ env.Set("snappy_ab", "a")
4866+ env.Set("snappy_mode", "try")
4867+ env.Set("snappy_trial_boot", "1")
4868+ err = env.Save()
4869+ c.Assert(err, IsNil)
4870+
4871+ partition := New()
4872+ u := newUboot(partition)
4873+ c.Assert(u, NotNil)
4874+
4875+ err = u.MarkCurrentBootSuccessful()
4876+ c.Assert(err, IsNil)
4877+
4878+ env, err = uenv.Open(bootloaderUbootFwEnvFile)
4879+ c.Assert(err, IsNil)
4880+ c.Assert(env.String(), Equals, "snappy_ab=a\nsnappy_mode=regular\nsnappy_trial_boot=0\n")
4881+}
4882+
4883+func (s *PartitionTestSuite) TestUbootSetEnvNoUselessWrites(c *C) {
4884+ s.makeFakeUbootEnv(c)
4885+
4886+ env, err := uenv.Create(bootloaderUbootFwEnvFile, 4096)
4887+ c.Assert(err, IsNil)
4888+ env.Set("snappy_ab", "a")
4889+ env.Set("snappy_mode", "regular")
4890+ err = env.Save()
4891+ c.Assert(err, IsNil)
4892+
4893+ st, err := os.Stat(bootloaderUbootFwEnvFile)
4894+ c.Assert(err, IsNil)
4895+ time.Sleep(100 * time.Millisecond)
4896+
4897+ partition := New()
4898+ u := newUboot(partition)
4899+ c.Assert(u, NotNil)
4900+
4901+ err = u.(*uboot).setBootVar(bootloaderRootfsVar, "a")
4902+ c.Assert(err, IsNil)
4903+
4904+ env, err = uenv.Open(bootloaderUbootFwEnvFile)
4905+ c.Assert(err, IsNil)
4906+ c.Assert(env.String(), Equals, "snappy_ab=a\nsnappy_mode=regular\n")
4907+
4908+ st2, err := os.Stat(bootloaderUbootFwEnvFile)
4909+ c.Assert(err, IsNil)
4910+ c.Assert(st.ModTime(), Equals, st2.ModTime())
4911+}
4912
4913=== modified file 'partition/partition.go'
4914--- partition/partition.go 2015-07-15 07:53:43 +0000
4915+++ partition/partition.go 2015-09-16 17:51:11 +0000
4916@@ -596,6 +596,7 @@
4917 return errors.New("System is not dual root")
4918 }
4919
4920+<<<<<<< TREE
4921 bootloader, err := bootloader(p)
4922 if err != nil {
4923 return err
4924@@ -619,4 +620,23 @@
4925 }
4926
4927 return bootloader.BootDir()
4928+=======
4929+ bootloader, err := getBootloader(p)
4930+ if err != nil {
4931+ return err
4932+ }
4933+
4934+ // XXX: first toggle roofs and then handle assets? that seems
4935+ // wrong given that handleAssets may fails and we will
4936+ // knowingly boot into a broken system
4937+ err = p.RunWithOther(RW, func(otherRoot string) (err error) {
4938+ return bootloader.ToggleRootFS()
4939+ })
4940+
4941+ if err != nil {
4942+ return err
4943+ }
4944+
4945+ return bootloader.HandleAssets()
4946+>>>>>>> MERGE-SOURCE
4947 }
4948
4949=== modified file 'partition/partition_test.go'
4950=== modified file 'partition/utils_test.go'
4951=== modified file 'policy/policy_test.go'
4952=== modified file 'priv/priv_test.go'
4953=== modified file 'progress/progress.go'
4954=== modified file 'progress/progress_test.go'
4955=== modified file 'release/release_test.go'
4956=== modified file 'run-checks'
4957--- run-checks 2015-09-02 18:11:49 +0000
4958+++ run-checks 2015-09-16 17:51:11 +0000
4959@@ -53,6 +53,7 @@
4960 lint=$(golint ./... && golint ./_integration-tests/testutils/... && golint ./_integration-tests/tests/...)
4961 if [ -n "$lint" ]; then
4962 echo "Lint complains:"
4963+<<<<<<< TREE
4964 echo "$lint"
4965 exit 1
4966 fi
4967@@ -83,6 +84,24 @@
4968 if which subunit2pyunit >/dev/null 2>&1; then
4969 subunit-1to2 /tmp/snappy-test/output/artifacts/results.subunit | subunit2pyunit
4970 fi
4971+=======
4972+ echo "$lint"
4973+ exit 1
4974+>>>>>>> MERGE-SOURCE
4975+fi
4976+
4977+# integration tests
4978+echo Building the integration tests
4979+go build _integration-tests/main.go
4980+
4981+# the rabbit hole
4982+echo Running the tests for the integration testutils
4983+$goctest -v -cover ./_integration-tests/testutils/...
4984+
4985+# integration suite in kvm
4986+if which adt-run >/dev/null 2>&1; then
4987+ echo "Running integration tests on 15.04 edge"
4988+ go run _integration-tests/main.go --snappy-from-branch --release=15.04
4989 fi
4990
4991 echo "All good, what could possibly go wrong"
4992
4993=== modified file 'snappy/auth.go'
4994=== modified file 'snappy/auth_test.go'
4995--- snappy/auth_test.go 2015-06-09 12:56:34 +0000
4996+++ snappy/auth_test.go 2015-09-16 17:51:11 +0000
4997@@ -36,8 +36,8 @@
4998 const mockStoreInvalidLoginCode = 401
4999 const mockStoreInvalidLogin = `
5000 {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches