Merge lp:~mvo/snappy/15.04-systemd-sockets into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Michael Vogt
Status: Superseded
Proposed branch: lp:~mvo/snappy/15.04-systemd-sockets
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Diff against target: 6555 lines (+5232/-42) (has conflicts)
74 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)
debian/control (+9/-0)
debian/rules (+9/-0)
debian/snappy-wait4network.service (+10/-0)
dependencies.tsv (+5/-0)
etc/grub.d/09_snappy.OTHER (+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 (+311/-0)
snappy/click_test.go (+85/-0)
snappy/common_test.go (+7/-1)
snappy/dirs.go (+9/-0)
snappy/errors.go (+23/-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 (+110/-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 (+80/-0)
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 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 files to etc/grub.d.  Created directory.
Conflict because etc/grub.d is not versioned, but has versioned children.  Versioned directory.
Contents conflict in etc/grub.d/09_snappy
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:~mvo/snappy/15.04-systemd-sockets
Reviewer Review Type Date Requested Status
Snappy Developers Pending
Review via email: mp+270359@code.launchpad.net

Description of the change

Backport of the branch that adds systemd socket file generation which is important for docker.

To post a comment you must log in.

Unmerged revisions

469. By Michael Vogt

cherry pick -r 649..653 lp:~mvo/snappy/snappy-systemd-socket

468. By Michael Vogt

Backport of the trunk branch by mvo approved by chipaca

467. By Michael Vogt

Allows hwassign/unassign for /sys/class/gpio/{,un}export to fix lp1488618 by mvo approved by jdstrand

466. By Leo Arias

Backported the integration tests. by elopio approved by fgimenez

465. By Leo Arias

Backported the change to use gopkg.in/check.v1. by elopio approved by sergiusens,fgimenez

464. By Michael Vogt

Add regression tests for bug #1474125 by mvo approved by chipaca

463. By Michael Vogt

Another missing uboot piece to move from uEnv.txt to uboot.env. I will do some more cleanup in trunk, i.e. I dislike that there is no single bootvar etc. by mvo approved by rsalveti

462. By Michael Vogt

Set snappy_trial_boot to "0" instead of unsetting it

he reason is that uboot is acting really funny when vars are unset.
I.e. "if "${snappy_trial_boot}" = "1"; then echo yes; fi" will echo yes
if snappy_trial_boot is unset on older u-boot systems (e.g. odroid).

Only changing uboot in 15.04 (not grub as we do in wily). by mvo approved by mvo,chipaca

461. By Michael Vogt

Backports of the uboot.env changes from trunk. by mvo approved by chipaca

460. By Sergio Schvezov

Only update bootloader files on newer versions or channel changes. by sergiusens approved by mvo

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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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-08 06:47:52 +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_test.go'
2963=== modified file 'debian/control'
2964--- debian/control 2015-07-21 19:07:56 +0000
2965+++ debian/control 2015-09-08 06:47:52 +0000
2966@@ -9,17 +9,26 @@
2967 fakeroot,
2968 gettext,
2969 golang-ar-dev,
2970+<<<<<<< TREE
2971 golang-check.v1-dev,
2972 golang-gettext-dev,
2973+=======
2974+ golang-check.v1-dev,
2975+>>>>>>> MERGE-SOURCE
2976 golang-go,
2977 golang-go-flags-dev,
2978 golang-go.crypto-dev,
2979 golang-goconfigparser-dev,
2980 golang-pb-dev,
2981+<<<<<<< TREE
2982 golang-uboot-go-dev,
2983 golang-yaml.v2-dev,
2984 python3,
2985 python3-markdown
2986+=======
2987+ golang-uboot-go-dev,
2988+ golang-yaml.v2-dev
2989+>>>>>>> MERGE-SOURCE
2990 Standards-Version: 3.9.6
2991 Homepage: https://launchpad.net/snappy
2992 Vcs-Browser: http://bazaar.launchpad.net/~snappy-dev/snappy/trunk/files
2993
2994=== modified file 'debian/rules'
2995--- debian/rules 2015-08-24 08:27:39 +0000
2996+++ debian/rules 2015-09-08 06:47:52 +0000
2997@@ -54,6 +54,7 @@
2998 --no-start \
2999 -pubuntu-snappy \
3000 snappy-autopilot.service
3001+<<<<<<< TREE
3002 # start wait4network
3003 dh_systemd_start \
3004 -pubuntu-snappy \
3005@@ -68,6 +69,14 @@
3006 GOPATH=${BUILDDIR} go generate ./i18n
3007
3008 override_dh_auto_install: snappy.8
3009+=======
3010+ # start wait4network
3011+ dh_systemd_start \
3012+ -pubuntu-snappy \
3013+ snappy-wait4network.service
3014+
3015+override_dh_auto_install:
3016+>>>>>>> MERGE-SOURCE
3017 dh_auto_install -O--buildsystem=golang
3018 # Making the packages private
3019 rm -rf ${CURDIR}/debian/ubuntu-snappy/usr/share/gocode
3020
3021=== added file 'debian/snappy-wait4network.service'
3022--- debian/snappy-wait4network.service 1970-01-01 00:00:00 +0000
3023+++ debian/snappy-wait4network.service 2015-09-08 06:47:52 +0000
3024@@ -0,0 +1,10 @@
3025+[Unit]
3026+Description=Wait for network
3027+After=network-online.target
3028+
3029+[Service]
3030+Type=oneshot
3031+RemainAfterExit=yes
3032+TimeoutStartSec=0
3033+ExecStart=/bin/sh -ec 'while [ -z "$( /sbin/ip route show 0/0 )" ]; do sleep 5; done'
3034+
3035
3036=== renamed file 'debian/snappy-wait4network.service' => 'debian/snappy-wait4network.service.moved'
3037=== modified file 'dependencies.tsv'
3038--- dependencies.tsv 2015-08-18 13:34:46 +0000
3039+++ dependencies.tsv 2015-09-08 06:47:52 +0000
3040@@ -3,7 +3,12 @@
3041 github.com/gosexy/gettext git 98b7b91596d20b96909e6b60d57411547dd9959c 2013-02-21T11:21:43Z
3042 github.com/jessevdk/go-flags git 1acbbaff2f347c412a0c7884873bd72cc9c1f5b4 2015-08-16T10:05:21Z
3043 github.com/mvo5/goconfigparser git 26426272dda20cc76aa1fa44286dc743d2972fe8 2015-02-12T09:37:50Z
3044+<<<<<<< TREE
3045 github.com/mvo5/uboot-go git 361f6ebcbb54f389d15dc9faefa000e996ba3e37 2015-07-22T06:53:46Z
3046 golang.org/x/crypto git 60052bd85f2d91293457e8811b0cf26b773de469 2015-06-22T23:34:07Z
3047 gopkg.in/check.v1 git 64131543e7896d5bcc6bd5a76287eb75ea96c673 2014-10-24T13:38:53Z
3048+=======
3049+github.com/mvo5/uboot-go git 361f6ebcbb54f389d15dc9faefa000e996ba3e37 2015-07-22T06:53:46Z
3050+gopkg.in/check.v1 git 64131543e7896d5bcc6bd5a76287eb75ea96c673 2014-10-24T13:38:53Z
3051+>>>>>>> MERGE-SOURCE
3052 gopkg.in/yaml.v2 git 49c95bdc21843256fb6c4e0d370a05f24a0bf213 2015-02-24T22:57:58Z
3053
3054=== added directory 'etc'
3055=== added directory 'etc/grub.d'
3056=== added file 'etc/grub.d/09_snappy.OTHER'
3057--- etc/grub.d/09_snappy.OTHER 1970-01-01 00:00:00 +0000
3058+++ etc/grub.d/09_snappy.OTHER 2015-09-08 06:47:52 +0000
3059@@ -0,0 +1,441 @@
3060+#!/bin/sh
3061+#---------------------------------------------------------------------
3062+# Summary: Grub bootloader logic for Ubuntu Snappy systems.
3063+# Description: This is a heavily modified "10_linux" grub snippet that
3064+# deals with Snappy dual-rootfs systems.
3065+#
3066+# XXX: Note that this script is called from within a chroot environment
3067+# on snappy systems!
3068+#
3069+#---------------------------------------------------------------------
3070+
3071+set -e
3072+
3073+prefix="/usr"
3074+exec_prefix="/usr"
3075+datarootdir="/usr/share"
3076+
3077+# Utility functions
3078+. "${datarootdir}/grub/grub-mkconfig_lib"
3079+
3080+# Globals
3081+machine=`uname -m`
3082+
3083+SNAPPY_OS="Ubuntu Core Snappy"
3084+SNAPPY_TYPE=simple
3085+
3086+#---------------------------------------------------------------------
3087+
3088+# Display message and exit
3089+die()
3090+{
3091+ msg="$*"
3092+ echo "ERROR: $msg" >&2
3093+ exit 1
3094+}
3095+
3096+# Create a grub menu entry by writing a menuentry to stdout.
3097+linux_entry_ext()
3098+{
3099+ local name="$1"
3100+ local os="$2"
3101+ local version="$3"
3102+ local type="$4"
3103+ local args="$5"
3104+ local device="$6"
3105+ local kernel="$7"
3106+ local initrd="$8"
3107+
3108+ local boot_device_id=
3109+ local prepare_root_cache=
3110+ local prepare_boot_cache=
3111+
3112+ if [ -z "$boot_device_id" ]; then
3113+ boot_device_id="$(grub_get_device_id "${device}")"
3114+ fi
3115+
3116+ echo "menuentry '$name' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
3117+
3118+ if [ x$type != xrecovery ] ; then
3119+ save_default_entry | grub_add_tab
3120+ fi
3121+
3122+ # Use ELILO's generic "efifb" when it's known to be available.
3123+ # FIXME: We need an interface to select vesafb in case efifb can't be used.
3124+ if [ "x$GRUB_GFXPAYLOAD_LINUX" = x ]; then
3125+ echo " load_video" | sed "s/^/$submenu_indentation/"
3126+ else
3127+ if [ "x$GRUB_GFXPAYLOAD_LINUX" != xtext ]; then
3128+ echo " load_video" | sed "s/^/$submenu_indentation/"
3129+ fi
3130+ fi
3131+ if ([ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]) && \
3132+ ([ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 1 ]); then
3133+ echo " gfxmode \$linux_gfx_mode" | sed "s/^/$submenu_indentation/"
3134+ fi
3135+
3136+ echo " insmod gzio" | sed "s/^/$submenu_indentation/"
3137+ echo " if [ x\$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi" | sed "s/^/$submenu_indentation/"
3138+
3139+ # device may be a label (LABEL=name), so convert back to full path
3140+ label_name=$(echo "$device"|sed 's/^LABEL=//g')
3141+ if [ "$device" = "$label_name" ]
3142+ then
3143+ device_path="$device"
3144+ else
3145+ # found a label
3146+ device_path=$(get_partition_from_label "$label_name")
3147+ fi
3148+
3149+ if [ x$dirname = x/ ]; then
3150+ if [ -z "${prepare_root_cache}" ]; then
3151+
3152+ prepare_root_cache="$(prepare_grub_to_access_device ${device_path} | grub_add_tab)"
3153+ fi
3154+ printf '%s\n' "${prepare_root_cache}" | sed "s/^/$submenu_indentation/"
3155+ else
3156+ if [ -z "${prepare_boot_cache}" ]; then
3157+ prepare_boot_cache="$(prepare_grub_to_access_device ${device_path} | grub_add_tab)"
3158+ fi
3159+ printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/"
3160+ fi
3161+
3162+ if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then
3163+ message="$(gettext_printf "Loading Linux %s ..." ${version})"
3164+ sed "s/^/$submenu_indentation/" << EOF
3165+ echo '$(echo "$message" | grub_quote)'
3166+EOF
3167+ fi
3168+
3169+ sed "s/^/$submenu_indentation/" << EOF
3170+ linux ${kernel} root=${device} ro ${args}
3171+EOF
3172+
3173+ if test -n "${initrd}"; then
3174+ # TRANSLATORS: ramdisk isn't identifier. Should be translated.
3175+ if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then
3176+ message="$(gettext_printf "Loading initial ramdisk ...")"
3177+ sed "s/^/$submenu_indentation/" << EOF
3178+ echo '$(echo "$message" | grub_quote)'
3179+EOF
3180+ fi
3181+ sed "s/^/$submenu_indentation/" << EOF
3182+ initrd ${initrd}
3183+EOF
3184+ fi
3185+
3186+ sed "s/^/$submenu_indentation/" << EOF
3187+}
3188+EOF
3189+}
3190+
3191+# Returns a list of the currently available kernels.
3192+# $1: If set, look for kernel below "$1/boot/".
3193+get_kernels()
3194+{
3195+ local prefix_dir="$1"
3196+ local list
3197+
3198+ case "x$machine" in
3199+ xi?86 | xx86_64)
3200+ list=`for i in $prefix_dir/boot/vmlinuz-* \
3201+ $prefix_dir/vmlinuz-* \
3202+ $prefix_dir/boot/kernel-* ; do
3203+ if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
3204+ done` ;;
3205+ *)
3206+ list=`for i in $prefix_dir/boot/vmlinuz-* \
3207+ $prefix_dir/boot/vmlinux-* \
3208+ $prefix_dir/vmlinuz-* \
3209+ $prefix_dir/vmlinux-* \
3210+ $prefix_dir/boot/kernel-* ; do
3211+ if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
3212+ done` ;;
3213+ esac
3214+ echo "$list"
3215+}
3216+
3217+# Composes and returns a kernel cmd line
3218+get_cmdline() {
3219+ channel_ini="/etc/system-image/channel.ini"
3220+ grep -q 'device: azure_amd64' "$channel_ini" && azure_cmdline="rootdelay=300"
3221+
3222+ echo "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} $azure_cmdline"
3223+}
3224+
3225+# Returns the path to the initrd for the kernel specified by $1.
3226+# $1: kernel version.
3227+# $2: directory to look in.
3228+get_initrd()
3229+{
3230+ local version="$1"
3231+ local dir="$2"
3232+
3233+ local alt_version=`echo $version | sed -e "s,\.old$,,g"`
3234+ local initrd=
3235+ local i=
3236+
3237+ for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
3238+ "initrd-${version}" "initramfs-${version}.img" \
3239+ "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
3240+ "initrd-${alt_version}" "initramfs-${alt_version}.img" \
3241+ "initramfs-genkernel-${version}" \
3242+ "initramfs-genkernel-${alt_version}" \
3243+ "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
3244+ "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
3245+ if test -e "${dir}/${i}" ; then
3246+ initrd="${dir}/${i}"
3247+ break
3248+ fi
3249+ done
3250+ echo "$initrd"
3251+}
3252+
3253+# Determine full path to disk partition given a filesystem label.
3254+get_partition_from_label()
3255+{
3256+ local label="$1"
3257+ local part=
3258+ local path=
3259+
3260+ [ -n "$label" ] || grub_warn "need FS label"
3261+
3262+ part=$(find /dev -name "$label"|tail -1)
3263+ [ -z "$part" ] && return
3264+ path=$(readlink -f "$part")
3265+ [ -n "$path" ] && echo "$path"
3266+}
3267+
3268+# Return the partition label for the given partition device.
3269+# $1: full path to partition device.
3270+get_label_from_device()
3271+{
3272+ local root="$1"
3273+
3274+ local label=
3275+ local std_label=
3276+ local label_rootfs=
3277+
3278+ for std_label in system-a system-b; do
3279+ label_rootfs=$(findfs "PARTLABEL=$std_label" 2>/dev/null || :)
3280+ if [ "$label_rootfs" = "$root" ]; then
3281+ label="$std_label"
3282+ break
3283+ fi
3284+ done
3285+
3286+ echo "$label"
3287+}
3288+
3289+# Return the full path to the device corresponding to the given
3290+# partition label.
3291+#
3292+# $1: partition label.
3293+get_device_from_label()
3294+{
3295+ local label="$1"
3296+ local device=
3297+
3298+ device=$(findfs "PARTLABEL=$label" 2>/dev/null || :)
3299+ echo "$device"
3300+}
3301+
3302+# Given a rootfs label, return the rootfs label corresponding to the
3303+# "other" rootfs partition.
3304+get_other_rootfs_label()
3305+{
3306+ local label="$1"
3307+
3308+ if [ "$label" = "system-a" ]; then
3309+ echo "system-b"
3310+ else
3311+ echo "system-a"
3312+ fi
3313+}
3314+
3315+# Given a mountpoint, return the corresponding device path
3316+# $1: mountpoint.
3317+get_device_from_mountpoint()
3318+{
3319+ local mountpoint="$1"
3320+ local device=
3321+
3322+ # XXX: Parse mount output rather than looking in /proc/mounts to
3323+ # avoid seeing the mounts outside the chroot.
3324+ device=$(/bin/mount | grep "^/dev/.* on ${mountpoint}[[:space:]]" 2>/dev/null |\
3325+ awk '{print $1}' || :)
3326+
3327+ echo "$device"
3328+}
3329+
3330+# Convert a partition label name to a menuentry name
3331+make_name()
3332+{
3333+ local label="$1"
3334+
3335+ echo "$SNAPPY_OS $label rootfs" | grub_quote
3336+}
3337+
3338+# Arrange for a grub menuentry to be created for the given partition.
3339+#
3340+# $1: full path to rootfs partition device
3341+# $2: partition label associated with $1
3342+# $3: mountpoint of $1.
3343+handle_menu_entry()
3344+{
3345+ local rootfs_device="$1"
3346+ local label="$2"
3347+ local mountpoint="$3"
3348+
3349+ local name=
3350+ local device=
3351+ local mount_prefix=
3352+ local list=
3353+ local linux=
3354+ local basename=
3355+ local dirname=
3356+ local rel_dirname=
3357+ local version=
3358+ local initrd=
3359+ local cmdline=
3360+
3361+ # boot by label
3362+ device="LABEL=$label"
3363+
3364+ name=$(make_name "$label")
3365+
3366+ # avoid double-leading slashes and the subsequent need to call
3367+ # 'readlink -f'.
3368+ if [ "$mountpoint" = "/" ]; then
3369+ mount_prefix=""
3370+ else
3371+ mount_prefix="$mountpoint"
3372+ fi
3373+ list=$(get_kernels "$mount_prefix")
3374+
3375+ linux=$(version_find_latest $list)
3376+ basename=$(basename "$linux")
3377+ dirname=$(dirname "$linux")
3378+ rel_dirname=$(make_system_path_relative_to_its_root "$dirname")
3379+ version=$(echo "$basename" | sed -e "s,^[^0-9]*-,,g")
3380+
3381+ initrd=$(get_initrd "$version" "$dirname")
3382+
3383+ cmdline=$(get_cmdline)
3384+
3385+ # convert the path to the mounted "other" rootfs back to a
3386+ # a standard one by removing the mountpoint prefix.
3387+ if [ "$mountpoint" != "/" ]; then
3388+ linux=$(echo "$linux" | sed "s!^${mountpoint}!!g")
3389+ initrd=$(echo "$initrd" | sed "s!^${mountpoint}!!g")
3390+ fi
3391+
3392+ # Create menu entries for the 2 snappy rootfs's.
3393+ linux_entry_ext "$name" "$SNAPPY_OS" "$version" \
3394+ "$SNAPPY_TYPE" "$cmdline" "$device" \
3395+ "$linux" "$initrd"
3396+}
3397+
3398+#---------------------------------------------------------------------
3399+# main
3400+
3401+case "$machine" in
3402+ i?86) GENKERNEL_ARCH="x86" ;;
3403+ mips|mips64) GENKERNEL_ARCH="mips" ;;
3404+ mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;;
3405+ arm*) GENKERNEL_ARCH="arm" ;;
3406+ *) GENKERNEL_ARCH="$machine" ;;
3407+esac
3408+
3409+# Determine which partition label is being used for the current root
3410+# directory. This is slightly convoluted but required since this code
3411+# runs within a chroot environment (where lsblk does not work).
3412+#
3413+# XXX: Note that since this code is run from within a chroot (where the
3414+# "other" rootfs is mounted), it might appear that the logic is
3415+# inverted. However, the code below simply
3416+this_mountpoint="/"
3417+this_root=$(get_device_from_mountpoint "$this_mountpoint")
3418+[ -z "$this_root" ] && {
3419+ die "cannot determine rootfs for $this_mountpoint"
3420+}
3421+
3422+this_label=$(get_label_from_device "$this_root")
3423+[ -z "$this_label" ] && {
3424+ die "cannot determine partition label for rootfs $this_root"
3425+}
3426+
3427+handle_menu_entry "$this_root" "$this_label" "$this_mountpoint"
3428+
3429+other_mountpoint="/writable/cache/system"
3430+
3431+# When this script is run on a real snappy system, even if there is only
3432+# a single rootfs provisioned, the other rootfs partition is expected to
3433+# be formatted and mounted.
3434+#
3435+# However this script is also run at provisioning time where
3436+# $other_mountpoint will not be a mountpoint. Therefore in the provisioning
3437+# scenario, only a single menuentry will be generated if only a single
3438+# rootfs is provisioned.
3439+if $(mountpoint -q "$other_mountpoint"); then
3440+ other_label=$(get_other_rootfs_label "$this_label")
3441+
3442+ other_root=$(get_device_from_label "$other_label")
3443+ [ -z "$other_root" ] && {
3444+ die "cannot determine rootfs"
3445+ }
3446+
3447+ handle_menu_entry "$other_root" "$other_label" "$other_mountpoint"
3448+fi
3449+
3450+# Toggle rootfs if previous boot failed.
3451+#
3452+# Since grub sets snappy_trial_boot, if it is _already_ set when grub starts
3453+# and we're in try mode, the previous boot must have failed to unset it,
3454+# so toggle the rootfs.
3455+sed "s/^/$submenu_indentation/" << EOF
3456+ # set defaults
3457+ if [ -z "\$snappy_mode" ]; then
3458+ set snappy_mode=regular
3459+ save_env snappy_mode
3460+ fi
3461+ if [ -z "\$snappy_ab" ]; then
3462+ set snappy_ab=a
3463+ save_env snappy_ab
3464+ fi
3465+
3466+ if [ "\$snappy_mode" = "try" ]; then
3467+ if [ "\$snappy_trial_boot" = "1" ]; then
3468+ # Previous boot failed to unset snappy_trial_boot, so toggle
3469+ # rootfs.
3470+ if [ "\$snappy_ab" = "a" ]; then
3471+ set default="$(make_name system-b)"
3472+ set snappy_ab=b
3473+ else
3474+ set snappy_ab=a
3475+ set default="$(make_name system-a)"
3476+ fi
3477+ save_env snappy_ab
3478+ else
3479+ # Trial mode so set the snappy_trial_boot (which snappy is
3480+ # expected to unset).
3481+ #
3482+ # Note: don't use the standard recordfail variable since that forces
3483+ # the menu to be displayed and sets an infinite timeout if set.
3484+ set snappy_trial_boot=1
3485+ save_env snappy_trial_boot
3486+
3487+ if [ "\$snappy_ab" = "a" ]; then
3488+ set default="$(make_name system-a)"
3489+ else
3490+ set default="$(make_name system-b)"
3491+ fi
3492+ fi
3493+ else
3494+ if [ "\$snappy_ab" = "a" ]; then
3495+ set default="$(make_name system-a)"
3496+ else
3497+ set default="$(make_name system-b)"
3498+ fi
3499+ fi
3500+EOF
3501
3502=== modified file 'helpers/cmp_test.go'
3503=== modified file 'helpers/helpers.go'
3504--- helpers/helpers.go 2015-09-01 10:00:49 +0000
3505+++ helpers/helpers.go 2015-09-08 06:47:52 +0000
3506@@ -26,6 +26,7 @@
3507 "encoding/hex"
3508 "fmt"
3509 "io"
3510+ "io/ioutil"
3511 "math/rand"
3512 "os"
3513 "os/exec"
3514@@ -198,14 +199,37 @@
3515 }
3516 }
3517
3518-// IsSupportedArchitecture returns true if the system architecture is in the
3519-// list of architectures.
3520-func IsSupportedArchitecture(architectures []string) bool {
3521- systemArch := UbuntuArchitecture()
3522-
3523- for _, arch := range architectures {
3524- if arch == "all" || arch == systemArch {
3525- return true
3526+<<<<<<< TREE
3527+// IsSupportedArchitecture returns true if the system architecture is in the
3528+// list of architectures.
3529+func IsSupportedArchitecture(architectures []string) bool {
3530+ systemArch := UbuntuArchitecture()
3531+
3532+ for _, arch := range architectures {
3533+ if arch == "all" || arch == systemArch {
3534+ return true
3535+=======
3536+// IsSupportedArchitecture returns true if the system architecture is in the
3537+// list of architectures.
3538+func IsSupportedArchitecture(architectures []string) bool {
3539+ systemArch := UbuntuArchitecture()
3540+
3541+ for _, arch := range architectures {
3542+ if arch == "all" || arch == systemArch {
3543+ return true
3544+ }
3545+ }
3546+
3547+ return false
3548+}
3549+
3550+// EnsureDir ensures that the given directory exists and if
3551+// not creates it with the given permissions.
3552+func EnsureDir(dir string, perm os.FileMode) (err error) {
3553+ if _, err := os.Stat(dir); os.IsNotExist(err) {
3554+ if err := os.MkdirAll(dir, perm); err != nil {
3555+ return err
3556+>>>>>>> MERGE-SOURCE
3557 }
3558 }
3559
3560@@ -360,6 +384,7 @@
3561 return syscall.Getuid() == 0 || syscall.Getgid() == 0
3562
3563 }
3564+<<<<<<< TREE
3565
3566 // MajorMinor returns the major/minor number of the given os.FileInfo
3567 func MajorMinor(info os.FileInfo) (uint32, uint32, error) {
3568@@ -530,3 +555,140 @@
3569
3570 return nil
3571 }
3572+=======
3573+
3574+func makeDirMap(dirName string) (map[string]struct{}, error) {
3575+ dir, err := ioutil.ReadDir(dirName)
3576+ if err != nil {
3577+ return nil, err
3578+ }
3579+
3580+ dirMap := make(map[string]struct{})
3581+ for _, dent := range dir {
3582+ if dent.IsDir() {
3583+ return nil, fmt.Errorf("subdirs are not supported")
3584+ }
3585+ dirMap[dent.Name()] = struct{}{}
3586+ }
3587+
3588+ return dirMap, nil
3589+}
3590+
3591+// RSyncWithDelete syncs srcDir to destDir
3592+func RSyncWithDelete(srcDirName, destDirName string) error {
3593+
3594+ // first remove everything thats not in srcdir
3595+ err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error {
3596+ if err != nil {
3597+ return err
3598+ }
3599+
3600+ // relative to the root "destDirName"
3601+ relPath := path[len(destDirName):]
3602+ if !FileExists(filepath.Join(srcDirName, relPath)) {
3603+ if err := os.RemoveAll(path); err != nil {
3604+ return err
3605+ }
3606+ if info.IsDir() {
3607+ return filepath.SkipDir
3608+ }
3609+ }
3610+ return nil
3611+ })
3612+ if err != nil {
3613+ return err
3614+ }
3615+
3616+ // then copy or update the data from srcdir to destdir
3617+ err = filepath.Walk(srcDirName, func(path string, info os.FileInfo, err error) error {
3618+ if err != nil {
3619+ return err
3620+ }
3621+
3622+ // relative to the root "srcDirName"
3623+ relPath := path[len(srcDirName):]
3624+ if info.IsDir() {
3625+ return os.MkdirAll(filepath.Join(destDirName, relPath), info.Mode())
3626+ }
3627+ src := path
3628+ dst := filepath.Join(destDirName, relPath)
3629+ if !FilesAreEqual(src, dst) {
3630+ // XXX: on snappy-trunk we can use CopyFile here
3631+ output, err := exec.Command("cp", "-va", src, dst).CombinedOutput()
3632+ if err != nil {
3633+ fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output)
3634+ }
3635+ }
3636+ return nil
3637+ })
3638+
3639+ return err
3640+}
3641+
3642+func fillSnapEnvVars(desc interface{}, vars []string) []string {
3643+ for i, v := range vars {
3644+ var templateOut bytes.Buffer
3645+ t := template.Must(template.New("wrapper").Parse(v))
3646+ if err := t.Execute(&templateOut, desc); err != nil {
3647+ // this can never happen, except we forget a variable
3648+ logger.LogAndPanic(err)
3649+ }
3650+ vars[i] = templateOut.String()
3651+ }
3652+ return vars
3653+}
3654+
3655+// GetBasicSnapEnvVars returns the app-level environment variables for a snap.
3656+// Despite this being a bit snap-specific, this is in helpers.go because it's
3657+// used by so many other modules, we run into circular dependencies if it's
3658+// somewhere more reasonable like the snappy module.
3659+func GetBasicSnapEnvVars(desc interface{}) []string {
3660+ return fillSnapEnvVars(desc, []string{
3661+ "TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3662+ "TEMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3663+ "SNAP_APP_PATH={{.AppPath}}",
3664+ "SNAP_APP_DATA_PATH=/var/lib{{.AppPath}}",
3665+ "SNAP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3666+ "SNAP_NAME={{.AppName}}",
3667+ "SNAP_VERSION={{.Version}}",
3668+ "SNAP_ORIGIN={{.Namespace}}",
3669+ "SNAP_FULLNAME={{.UdevAppName}}",
3670+ "SNAP_ARCH={{.AppArch}}",
3671+ })
3672+}
3673+
3674+// GetUserSnapEnvVars returns the user-level environment variables for a snap.
3675+// Despite this being a bit snap-specific, this is in helpers.go because it's
3676+// used by so many other modules, we run into circular dependencies if it's
3677+// somewhere more reasonable like the snappy module.
3678+func GetUserSnapEnvVars(desc interface{}) []string {
3679+ return fillSnapEnvVars(desc, []string{
3680+ "SNAP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
3681+ })
3682+}
3683+
3684+// GetDeprecatedBasicSnapEnvVars returns the app-level deprecated environment
3685+// variables for a snap.
3686+// Despite this being a bit snap-specific, this is in helpers.go because it's
3687+// used by so many other modules, we run into circular dependencies if it's
3688+// somewhere more reasonable like the snappy module.
3689+func GetDeprecatedBasicSnapEnvVars(desc interface{}) []string {
3690+ return fillSnapEnvVars(desc, []string{
3691+ "SNAPP_APP_PATH={{.AppPath}}",
3692+ "SNAPP_APP_DATA_PATH=/var/lib{{.AppPath}}",
3693+ "SNAPP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp",
3694+ "SNAPPY_APP_ARCH={{.AppArch}}",
3695+ })
3696+}
3697+
3698+// GetDeprecatedUserSnapEnvVars returns the user-level deprecated environment
3699+// variables for a snap.
3700+// Despite this being a bit snap-specific, this is in helpers.go because it's
3701+// used by so many other modules, we run into circular dependencies if it's
3702+// somewhere more reasonable like the snappy module.
3703+func GetDeprecatedUserSnapEnvVars(desc interface{}) []string {
3704+ return fillSnapEnvVars(desc, []string{
3705+ "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
3706+ })
3707+}
3708+>>>>>>> MERGE-SOURCE
3709
3710=== modified file 'helpers/helpers_test.go'
3711--- helpers/helpers_test.go 2015-08-21 12:14:16 +0000
3712+++ helpers/helpers_test.go 2015-09-08 06:47:52 +0000
3713@@ -271,6 +271,7 @@
3714 c.Assert(err, IsNil)
3715 c.Assert(home, Equals, oldHome)
3716 }
3717+<<<<<<< TREE
3718
3719 func skipOnMissingDevKmsg(c *C) {
3720 _, err := os.Stat("/dev/kmsg")
3721@@ -483,3 +484,106 @@
3722 err := CopyIfDifferent(src, dst)
3723 c.Assert(err, NotNil)
3724 }
3725+=======
3726+
3727+func makeTestFiles(c *C, srcDir, destDir string) {
3728+ // a new file
3729+ err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644)
3730+ c.Assert(err, IsNil)
3731+
3732+ // a existing file that needs update
3733+ err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644)
3734+ c.Assert(err, IsNil)
3735+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644)
3736+ c.Assert(err, IsNil)
3737+
3738+ // existing file that needs no update
3739+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644)
3740+ c.Assert(err, IsNil)
3741+ err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run()
3742+ c.Assert(err, IsNil)
3743+
3744+ // a file that needs removal
3745+ err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644)
3746+ c.Assert(err, IsNil)
3747+}
3748+
3749+func compareDirs(c *C, srcDir, destDir string) {
3750+ d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput()
3751+ c.Assert(err, IsNil)
3752+ d2, err := exec.Command("ls", "-al", destDir).CombinedOutput()
3753+ c.Assert(err, IsNil)
3754+ c.Assert(string(d1), Equals, string(d2))
3755+ // ensure content got updated
3756+ c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput()
3757+ c.Assert(err, IsNil)
3758+ c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput()
3759+ c.Assert(err, IsNil)
3760+ c.Assert(string(c1), Equals, string(c2))
3761+}
3762+
3763+func (ts *HTestSuite) TestSyncDirs(c *C) {
3764+
3765+ for _, l := range [][2]string{
3766+ [2]string{"src-short", "dst-loooooooooooong"},
3767+ [2]string{"src-loooooooooooong", "dst-short"},
3768+ [2]string{"src-eq", "dst-eq"},
3769+ } {
3770+
3771+ // ensure we have src, dest dirs with different length
3772+ srcDir := filepath.Join(c.MkDir(), l[0])
3773+ err := os.MkdirAll(srcDir, 0755)
3774+ c.Assert(err, IsNil)
3775+ destDir := filepath.Join(c.MkDir(), l[1])
3776+ err = os.MkdirAll(destDir, 0755)
3777+ c.Assert(err, IsNil)
3778+
3779+ // add a src subdir
3780+ subdir := filepath.Join(srcDir, "subdir")
3781+ err = os.Mkdir(subdir, 0755)
3782+ c.Assert(err, IsNil)
3783+ makeTestFiles(c, subdir, destDir)
3784+
3785+ // add a dst subdir that needs to get deleted
3786+ subdir2 := filepath.Join(destDir, "to-be-deleted-subdir")
3787+ err = os.Mkdir(subdir2, 0755)
3788+ subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir")
3789+ err = os.Mkdir(subdir3, 0755)
3790+
3791+ // and a toplevel
3792+ makeTestFiles(c, srcDir, destDir)
3793+
3794+ // do it
3795+ err = RSyncWithDelete(srcDir, destDir)
3796+ c.Assert(err, IsNil)
3797+
3798+ // ensure meta-data is identical
3799+ compareDirs(c, srcDir, destDir)
3800+ compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir"))
3801+ }
3802+}
3803+
3804+func (ts *HTestSuite) TestSyncDirFails(c *C) {
3805+ srcDir := c.MkDir()
3806+ err := os.MkdirAll(srcDir, 0755)
3807+ c.Assert(err, IsNil)
3808+
3809+ destDir := c.MkDir()
3810+ err = os.MkdirAll(destDir, 0755)
3811+ c.Assert(err, IsNil)
3812+
3813+ err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644)
3814+ c.Assert(err, IsNil)
3815+
3816+ // ensure remove fails
3817+ err = os.Chmod(destDir, 0100)
3818+ c.Assert(err, IsNil)
3819+ // make tempdir cleanup work again
3820+ defer os.Chmod(destDir, 0755)
3821+
3822+ // do it
3823+ err = RSyncWithDelete(srcDir, destDir)
3824+ c.Check(err, NotNil)
3825+ c.Check(err, ErrorMatches, ".*permission denied.*")
3826+}
3827+>>>>>>> MERGE-SOURCE
3828
3829=== modified file 'helpers/touch.go'
3830=== modified file 'helpers/touch_test.go'
3831=== added directory 'logger'
3832=== renamed directory 'logger' => 'logger.moved'
3833=== added file 'logger/logger_test.go'
3834--- logger/logger_test.go 1970-01-01 00:00:00 +0000
3835+++ logger/logger_test.go 2015-09-08 06:47:52 +0000
3836@@ -0,0 +1,331 @@
3837+/*
3838+ * Copyright (C) 2014-2015 Canonical Ltd
3839+ *
3840+ * This program is free software: you can redistribute it and/or modify
3841+ * it under the terms of the GNU General Public License version 3 as
3842+ * published by the Free Software Foundation.
3843+ *
3844+ * This program is distributed in the hope that it will be useful,
3845+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3846+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3847+ * GNU General Public License for more details.
3848+ *
3849+ * You should have received a copy of the GNU General Public License
3850+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3851+ *
3852+ */
3853+
3854+package logger
3855+
3856+import (
3857+ "bytes"
3858+ "errors"
3859+ "fmt"
3860+ "log/syslog"
3861+ "regexp"
3862+ "strings"
3863+ "testing"
3864+ "time"
3865+
3866+ "github.com/juju/loggo"
3867+ . "gopkg.in/check.v1"
3868+)
3869+
3870+func Test(t *testing.T) { TestingT(t) }
3871+
3872+type LoggerTestSuite struct {
3873+}
3874+
3875+var _ = Suite(&LoggerTestSuite{})
3876+
3877+type MockLogWriter struct {
3878+ buf bytes.Buffer
3879+}
3880+
3881+var mockWriter *MockLogWriter
3882+
3883+var loggoLevels = []string{"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
3884+
3885+func mockGetSyslog(priority syslog.Priority, tag string) (w logWriterInterface, err error) {
3886+ mockWriter = &MockLogWriter{}
3887+ return mockWriter, nil
3888+}
3889+
3890+func readLines() (lines []string) {
3891+ lines = strings.Split(string(mockWriter.buf.Bytes()), "\n")
3892+
3893+ // remove the last line if empty due to split.
3894+ length := len(lines)
3895+ last := lines[length-1]
3896+ if last == "" {
3897+ lines = lines[:length-1]
3898+ }
3899+ // clear the buffer to avoid contents accumulating indefinitely
3900+ mockWriter.buf.Reset()
3901+ return lines
3902+}
3903+
3904+func (ts *LoggerTestSuite) SetUpTest(c *C) {
3905+ getSyslog = mockGetSyslog
3906+}
3907+
3908+func (w *MockLogWriter) Debug(m string) error {
3909+ _, err := w.buf.Write([]byte(fmt.Sprintf("DEBUG: %s\n", m)))
3910+ return err
3911+}
3912+
3913+func (w *MockLogWriter) Info(m string) error {
3914+ _, err := w.buf.Write([]byte(fmt.Sprintf("INFO: %s\n", m)))
3915+ return err
3916+}
3917+
3918+func (w *MockLogWriter) Warning(m string) error {
3919+ _, err := w.buf.Write([]byte(fmt.Sprintf("WARNING: %s\n", m)))
3920+ return err
3921+}
3922+
3923+func (w *MockLogWriter) Err(m string) error {
3924+ _, err := w.buf.Write([]byte(fmt.Sprintf("ERROR: %s\n", m)))
3925+ return err
3926+}
3927+
3928+func (w *MockLogWriter) Crit(m string) error {
3929+ _, err := w.buf.Write([]byte(fmt.Sprintf("CRITICAL: %s\n", m)))
3930+ return err
3931+}
3932+
3933+// Search for value in array and return true if found
3934+func sliceContainsString(array []string, value string) bool {
3935+ str := string(strings.Join(array, ""))
3936+
3937+ return strings.Contains(str, value)
3938+}
3939+
3940+// Return true if array contains the pattern regex.
3941+func sliceContainsRegex(array []string, regex string) bool {
3942+ str := string(strings.Join(array, ""))
3943+
3944+ pattern := regexp.MustCompile(regex)
3945+
3946+ matches := pattern.FindAllStringSubmatch(str, -1)
3947+
3948+ return matches != nil
3949+}
3950+
3951+func (ts *LoggerTestSuite) TestNewLogWriter(c *C) {
3952+ var w, w2 *LogWriter
3953+ var err error
3954+
3955+ w, err = newLogWriter()
3956+ c.Assert(err, IsNil)
3957+ c.Assert(w, Not(IsNil))
3958+ c.Assert(w.systemLog, Not(IsNil))
3959+
3960+ w2, err = newLogWriter()
3961+ c.Assert(err, IsNil)
3962+ c.Assert(w2, Not(IsNil))
3963+ c.Assert(w2.systemLog, Not(IsNil))
3964+
3965+ // There should be a single shared syslog connection, hence the
3966+ // systemLog objects should be identical.
3967+ c.Assert(w.systemLog, Equals, w2.systemLog)
3968+ c.Assert(w.systemLog, DeepEquals, w2.systemLog)
3969+}
3970+
3971+func (ts *LoggerTestSuite) TestWrite(c *C) {
3972+ w, err := newLogWriter()
3973+ c.Assert(err, IsNil)
3974+ c.Assert(w, Not(IsNil))
3975+ c.Assert(w.systemLog, Not(IsNil))
3976+
3977+ t := time.Now()
3978+ strTime := fmt.Sprintf("%s", t)
3979+
3980+ for _, l := range loggoLevels {
3981+ level := stringToLogLevel(l)
3982+
3983+ w.Write(level, "module", "filename", 1234, t, "a message")
3984+ lines := readLines()
3985+
3986+ if level < loggo.ERROR {
3987+ c.Assert(len(lines), Equals, 1)
3988+ } else {
3989+ c.Assert(len(lines) > 1, Equals, true)
3990+
3991+ c.Assert(sliceContainsString(lines, "filename"), Equals, true)
3992+ c.Assert(sliceContainsString(lines, "1234"), Equals, true)
3993+ }
3994+
3995+ c.Assert(sliceContainsString(lines, "module"), Equals, true)
3996+
3997+ // We discard the timestamp as syslog adds that itself
3998+ c.Assert(sliceContainsString(lines, strTime), Equals, false)
3999+
4000+ c.Assert(sliceContainsString(lines, "a message"), Equals, true)
4001+ }
4002+
4003+}
4004+
4005+// Convert a loggo log level string representation into a real log
4006+// level.
4007+func stringToLogLevel(name string) loggo.Level {
4008+ level, ok := loggo.ParseLevel(name)
4009+
4010+ if !ok {
4011+ panic(fmt.Sprintf("unknown loggo level string: %q", name))
4012+ }
4013+
4014+ return level
4015+}
4016+
4017+func (ts *LoggerTestSuite) TestFormat(c *C) {
4018+ w, err := newLogWriter()
4019+ c.Assert(err, IsNil)
4020+ c.Assert(w, Not(IsNil))
4021+ c.Assert(w.systemLog, Not(IsNil))
4022+
4023+ for _, l := range loggoLevels {
4024+ level := stringToLogLevel(l)
4025+
4026+ if level < loggo.ERROR {
4027+ out := w.Format(level, "module", "filename", 1234, time.Now(), "a message")
4028+ c.Assert(out, Equals, fmt.Sprintf("%s:%s:%s", l, "module", "a message"))
4029+ } else {
4030+ out := w.Format(level, "module", "filename", 1234, time.Now(), "a message")
4031+ c.Assert(out, Equals, fmt.Sprintf("%s:%s:%s:%d:%s", l, "module", "filename", 1234, "a message"))
4032+ }
4033+ }
4034+}
4035+
4036+func (ts *LoggerTestSuite) TestLogStackTrace(c *C) {
4037+ var output []string
4038+
4039+ w, err := newLogWriter()
4040+ c.Assert(err, IsNil)
4041+ c.Assert(w, Not(IsNil))
4042+
4043+ f := func(s string) error {
4044+ output = append(output, s)
4045+ return nil
4046+ }
4047+
4048+ t := time.Now()
4049+ strTime := fmt.Sprintf("%s", t)
4050+
4051+ w.logStacktrace(loggo.DEBUG, "name", "filename", 9876, t, f)
4052+
4053+ c.Assert(sliceContainsString(output, "Stack trace"), Equals, true)
4054+ c.Assert(sliceContainsString(output, "name"), Equals, true)
4055+ c.Assert(sliceContainsString(output, "filename"), Equals, true)
4056+ c.Assert(sliceContainsString(output, "9876"), Equals, true)
4057+
4058+ // We discard the timestamp as syslog adds that itself
4059+ c.Assert(sliceContainsString(output, strTime), Equals, false)
4060+}
4061+
4062+func (ts *LoggerTestSuite) checkLogLevel(c *C, level, msg string) {
4063+ err := ActivateLogger()
4064+ c.Assert(err, IsNil)
4065+
4066+ expectBacktrace := (level == "ERROR" || level == "CRITICAL")
4067+
4068+ logger := loggo.GetLogger("snappy")
4069+ c.Assert(logger, Not(IsNil))
4070+
4071+ switch level {
4072+ case "DEBUG":
4073+ c.Assert(logger.IsDebugEnabled(), Equals, true)
4074+ logger.Debugf(msg)
4075+
4076+ case "INFO":
4077+ c.Assert(logger.IsInfoEnabled(), Equals, true)
4078+ logger.Infof(msg)
4079+
4080+ case "WARNING":
4081+ c.Assert(logger.IsWarningEnabled(), Equals, true)
4082+ logger.Warningf(msg)
4083+
4084+ case "ERROR":
4085+ c.Assert(logger.IsErrorEnabled(), Equals, true)
4086+ logger.Errorf(msg)
4087+
4088+ case "CRITICAL":
4089+ // loggo doesn't provide a IsCriticalEnabled()
4090+ c.Assert(logger.IsErrorEnabled(), Equals, true)
4091+ logger.Criticalf(msg)
4092+ }
4093+
4094+ lines := readLines()
4095+
4096+ if expectBacktrace {
4097+ c.Assert(len(lines) > 1, Equals, true)
4098+ } else {
4099+ c.Assert(len(lines), Equals, 1)
4100+ }
4101+
4102+ needle := fmt.Sprintf("%s.*%s", level, msg)
4103+ c.Assert(sliceContainsRegex(lines, needle), Equals, true)
4104+
4105+ c.Assert(sliceContainsString(lines, "Stack trace"), Equals, expectBacktrace)
4106+}
4107+
4108+func (ts *LoggerTestSuite) TestLogLevels(c *C) {
4109+ msg := "an error message"
4110+
4111+ for _, level := range loggoLevels {
4112+ ts.checkLogLevel(c, level, msg)
4113+ }
4114+}
4115+
4116+func (ts *LoggerTestSuite) TestLogError(c *C) {
4117+ level := "ERROR"
4118+ msg := "I am an error"
4119+
4120+ err := ActivateLogger()
4121+ c.Assert(err, IsNil)
4122+
4123+ result := LogError(nil)
4124+ c.Assert(result, IsNil)
4125+
4126+ err = errors.New(msg)
4127+ c.Assert(err, Not(IsNil))
4128+
4129+ // We expect to get back exactly what was passsed...
4130+ result = LogError(err)
4131+ c.Assert(result, DeepEquals, err)
4132+
4133+ // ... but also to have the error logged
4134+ ts.checkLogLevel(c, level, msg)
4135+}
4136+
4137+func (ts *LoggerTestSuite) TestLogAndPanic(c *C) {
4138+ level := "CRITICAL"
4139+ msg := "I am a fatal error"
4140+
4141+ panicked := false
4142+
4143+ err := ActivateLogger()
4144+ c.Assert(err, IsNil)
4145+
4146+ // If the specified error is nil, no panic is expected and no
4147+ // log entry should be added.
4148+ func() {
4149+ defer func() {
4150+ if r := recover(); r != nil {
4151+ panicked = true
4152+ }
4153+ }()
4154+ LogAndPanic(nil)
4155+ }()
4156+
4157+ c.Assert(panicked, Equals, false)
4158+ c.Assert(len(readLines()), Equals, 0)
4159+
4160+ err = errors.New(msg)
4161+
4162+ // expect a panic...
4163+ c.Assert(func() { LogAndPanic(err) }, Panics, err)
4164+
4165+ // ... and a log entry
4166+ ts.checkLogLevel(c, level, msg)
4167+}
4168
4169=== modified file 'partition/bootloader.go'
4170=== modified file 'partition/bootloader_grub.go'
4171--- partition/bootloader_grub.go 2015-07-23 11:54:14 +0000
4172+++ partition/bootloader_grub.go 2015-09-08 06:47:52 +0000
4173@@ -32,7 +32,12 @@
4174 bootloaderGrubConfigFileReal = "/boot/grub/grub.cfg"
4175 bootloaderGrubEnvFileReal = "/boot/grub/grubenv"
4176
4177+<<<<<<< TREE
4178 bootloaderGrubEnvCmdReal = "/usr/bin/grub-editenv"
4179+=======
4180+ bootloaderGrubEnvCmdReal = "/usr/bin/grub-editenv"
4181+ bootloaderGrubUpdateCmdReal = "/usr/sbin/update-grub"
4182+>>>>>>> MERGE-SOURCE
4183 )
4184
4185 // var to make it testable
4186@@ -115,9 +120,24 @@
4187 return g.GetBootVar(bootloaderRootfsVar)
4188 }
4189
4190+<<<<<<< TREE
4191 func (g *grub) MarkCurrentBootSuccessful(currentRootfs string) (err error) {
4192 // Clear the variable set on boot to denote a good boot.
4193 if err := g.setBootVar(bootloaderTrialBootVar, "0"); err != nil {
4194+=======
4195+func (g *grub) GetRootFSName() string {
4196+ return g.currentRootfs
4197+}
4198+
4199+func (g *grub) GetOtherRootFSName() string {
4200+ return g.otherRootfs
4201+}
4202+
4203+func (g *grub) MarkCurrentBootSuccessful() (err error) {
4204+ // Clear the variable set by grub on boot to denote a good
4205+ // boot.
4206+ if err := g.unsetBootVar(bootloaderTrialBootVar); err != nil {
4207+>>>>>>> MERGE-SOURCE
4208 return err
4209 }
4210
4211
4212=== modified file 'partition/bootloader_grub_test.go'
4213--- partition/bootloader_grub_test.go 2015-07-23 11:54:14 +0000
4214+++ partition/bootloader_grub_test.go 2015-09-08 06:47:52 +0000
4215@@ -23,11 +23,16 @@
4216 "fmt"
4217 "io/ioutil"
4218 "os"
4219+<<<<<<< TREE
4220 "path/filepath"
4221
4222 "launchpad.net/snappy/helpers"
4223
4224 . "gopkg.in/check.v1"
4225+=======
4226+
4227+ . "gopkg.in/check.v1"
4228+>>>>>>> MERGE-SOURCE
4229 )
4230
4231 func mockGrubFile(c *C, newPath string, mode os.FileMode) {
4232
4233=== modified file 'partition/bootloader_uboot.go'
4234--- partition/bootloader_uboot.go 2015-07-24 12:00:01 +0000
4235+++ partition/bootloader_uboot.go 2015-09-08 06:47:52 +0000
4236@@ -55,9 +55,14 @@
4237 bootloaderUbootConfigFile = bootloaderUbootConfigFileReal
4238 bootloaderUbootStampFile = bootloaderUbootStampFileReal
4239 bootloaderUbootEnvFile = bootloaderUbootEnvFileReal
4240+<<<<<<< TREE
4241 bootloaderUbootFwEnvFile = bootloaderUbootFwEnvFileReal
4242
4243 atomicWriteFile = helpers.AtomicWriteFile
4244+=======
4245+ bootloaderUbootFwEnvFile = bootloaderUbootFwEnvFileReal
4246+ atomicFileUpdate = atomicFileUpdateImpl
4247+>>>>>>> MERGE-SOURCE
4248 )
4249
4250 const bootloaderNameUboot bootloaderName = "u-boot"
4251@@ -103,15 +108,64 @@
4252 return bootloaderNameUboot
4253 }
4254
4255+<<<<<<< TREE
4256 func (u *uboot) ToggleRootFS(otherRootfs string) (err error) {
4257 if err := setBootVar(bootloaderRootfsVar, string(otherRootfs)); err != nil {
4258 return err
4259+=======
4260+// ToggleRootFS make the U-Boot bootloader switch rootfs's.
4261+//
4262+// Approach:
4263+//
4264+// - Assume the device's installed version of u-boot supports
4265+// CONFIG_SUPPORT_RAW_INITRD (that allows u-boot to boot a
4266+// standard initrd+kernel on the fat32 disk partition).
4267+// - Copy the "other" rootfs's kernel+initrd to the boot partition,
4268+// renaming them in the process to ensure the next boot uses the
4269+// correct versions.
4270+func (u *uboot) ToggleRootFS() (err error) {
4271+ // modern system
4272+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4273+ return u.toggleRootFSFwEnv()
4274+ }
4275+
4276+ // legacy
4277+ return u.toggleRootFSLegacy()
4278+}
4279+
4280+func (u *uboot) toggleRootFSFwEnv() (err error) {
4281+ if err := u.setBootVar(bootloaderRootfsVar, string(u.otherRootfs)); err != nil {
4282+ return err
4283+ }
4284+
4285+ return u.setBootVar(bootloaderBootmodeVar, bootloaderBootmodeTry)
4286+}
4287+
4288+func (u *uboot) toggleRootFSLegacy() (err error) {
4289+ // If the file exists, update it. Otherwise create it.
4290+ //
4291+ // The file _should_ always exist, but since it's on a writable
4292+ // partition, it's possible the admin removed it by mistake. So
4293+ // recreate to allow the system to boot!
4294+ changes := []configFileChange{
4295+ configFileChange{Name: bootloaderRootfsVar,
4296+ Value: string(u.otherRootfs),
4297+ },
4298+ configFileChange{Name: bootloaderBootmodeVar,
4299+ Value: bootloaderBootmodeTry,
4300+ },
4301+>>>>>>> MERGE-SOURCE
4302 }
4303
4304 return setBootVar(bootloaderBootmodeVar, bootloaderBootmodeTry)
4305 }
4306
4307+<<<<<<< TREE
4308 func getBootVarLegacy(name string) (value string, err error) {
4309+=======
4310+func (u *uboot) getBootVarLegacy(name string) (value string, err error) {
4311+
4312+>>>>>>> MERGE-SOURCE
4313 cfg := goconfigparser.New()
4314 cfg.AllowNoSectionHeader = true
4315 if err := cfg.ReadFile(bootloaderUbootEnvFile); err != nil {
4316@@ -121,6 +175,7 @@
4317 return cfg.Get("", name)
4318 }
4319
4320+<<<<<<< TREE
4321 func setBootVarLegacy(name, value string) error {
4322 curVal, err := getBootVarLegacy(name)
4323 if err == nil && curVal == value {
4324@@ -165,6 +220,16 @@
4325 return getBootVar(name)
4326 }
4327
4328+=======
4329+func (u *uboot) GetBootVar(name string) (value string, err error) {
4330+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4331+ return u.getBootVar(name)
4332+ }
4333+
4334+ return u.getBootVarLegacy(name)
4335+}
4336+
4337+>>>>>>> MERGE-SOURCE
4338 func (u *uboot) GetNextBootRootFSName() (label string, err error) {
4339 value, err := u.GetBootVar(bootloaderRootfsVar)
4340 if err != nil {
4341@@ -175,6 +240,7 @@
4342 return value, nil
4343 }
4344
4345+<<<<<<< TREE
4346 // FIXME: this is super similar to grub now, refactor to extract the
4347 // common code
4348 func (u *uboot) MarkCurrentBootSuccessful(currentRootfs string) error {
4349@@ -197,6 +263,260 @@
4350
4351 func (u *uboot) BootDir() string {
4352 return bootloaderUbootDir
4353+=======
4354+func (u *uboot) GetRootFSName() string {
4355+ return u.currentRootfs
4356+}
4357+
4358+func (u *uboot) GetOtherRootFSName() string {
4359+ return u.otherRootfs
4360+}
4361+
4362+// FIXME: put into utils package
4363+func readLines(path string) (lines []string, err error) {
4364+
4365+ file, err := os.Open(path)
4366+
4367+ if err != nil {
4368+ return nil, err
4369+ }
4370+
4371+ defer file.Close()
4372+
4373+ scanner := bufio.NewScanner(file)
4374+ for scanner.Scan() {
4375+ lines = append(lines, scanner.Text())
4376+ }
4377+
4378+ return lines, scanner.Err()
4379+}
4380+
4381+// FIXME: put into utils package
4382+func writeLines(lines []string, path string) (err error) {
4383+
4384+ file, err := os.Create(path)
4385+
4386+ if err != nil {
4387+ return err
4388+ }
4389+
4390+ defer func() {
4391+ e := file.Close()
4392+ if err == nil {
4393+ err = e
4394+ }
4395+ }()
4396+
4397+ writer := bufio.NewWriter(file)
4398+
4399+ for _, line := range lines {
4400+ if _, err := fmt.Fprintln(writer, line); err != nil {
4401+ return err
4402+ }
4403+ }
4404+
4405+ if err := writer.Flush(); err != nil {
4406+ return err
4407+ }
4408+
4409+ return file.Sync()
4410+}
4411+
4412+func (u *uboot) SyncBootFiles() (err error) {
4413+ srcDir := u.currentBootPath
4414+ destDir := u.otherBootPath
4415+
4416+ return helpers.RSyncWithDelete(srcDir, destDir)
4417+}
4418+
4419+func (u *uboot) HandleAssets() (err error) {
4420+ // check if we have anything, if there is no hardware yaml, there is nothing
4421+ // to process.
4422+ hardware, err := u.partition.hardwareSpec()
4423+ if err == ErrNoHardwareYaml {
4424+ return nil
4425+ } else if err != nil {
4426+ return err
4427+ }
4428+ // ensure to remove the file once we are done
4429+ defer os.Remove(u.partition.hardwareSpecFile)
4430+
4431+ // validate bootloader
4432+ if hardware.Bootloader != u.Name() {
4433+ return fmt.Errorf(
4434+ "bootloader is of type %s but hardware spec requires %s",
4435+ u.Name(),
4436+ hardware.Bootloader)
4437+ }
4438+
4439+ // validate partition layout
4440+ if u.partition.dualRootPartitions() && hardware.PartitionLayout != bootloaderSystemAB {
4441+ return fmt.Errorf("hardware spec requires dual root partitions")
4442+ }
4443+
4444+ // ensure we have the destdir
4445+ destDir := u.otherBootPath
4446+ if err := os.MkdirAll(destDir, dirMode); err != nil {
4447+ return err
4448+ }
4449+
4450+ // install kernel+initrd
4451+ for _, file := range []string{hardware.Kernel, hardware.Initrd} {
4452+
4453+ if file == "" {
4454+ continue
4455+ }
4456+
4457+ // expand path
4458+ path := path.Join(u.partition.cacheDir(), file)
4459+
4460+ if !helpers.FileExists(path) {
4461+ return fmt.Errorf("can not find file %s", path)
4462+ }
4463+
4464+ // ensure we remove the dir later
4465+ defer os.RemoveAll(filepath.Dir(path))
4466+
4467+ if err := runCommand("/bin/cp", path, destDir); err != nil {
4468+ return err
4469+ }
4470+ }
4471+
4472+ // TODO: look at the OEM package for dtb changes too once that is
4473+ // fully speced
4474+
4475+ // install .dtb files
4476+ dtbSrcDir := filepath.Join(u.partition.cacheDir(), hardware.DtbDir)
4477+ if helpers.FileExists(dtbSrcDir) {
4478+ // ensure we cleanup the source dir
4479+ defer os.RemoveAll(dtbSrcDir)
4480+
4481+ dtbDestDir := path.Join(destDir, "dtbs")
4482+ if err := os.MkdirAll(dtbDestDir, dirMode); err != nil {
4483+ return err
4484+ }
4485+
4486+ files, err := filepath.Glob(path.Join(dtbSrcDir, "*"))
4487+ if err != nil {
4488+ return err
4489+ }
4490+
4491+ for _, file := range files {
4492+ if err := runCommand("/bin/cp", file, dtbDestDir); err != nil {
4493+ return err
4494+ }
4495+ }
4496+ }
4497+
4498+ flashAssetsDir := u.partition.flashAssetsDir()
4499+
4500+ if helpers.FileExists(flashAssetsDir) {
4501+ // FIXME: we don't currently do anything with the
4502+ // MLO + uImage files since they are not specified in
4503+ // the hardware spec. So for now, just remove them.
4504+
4505+ if err := os.RemoveAll(flashAssetsDir); err != nil {
4506+ return err
4507+ }
4508+ }
4509+
4510+ return err
4511+}
4512+
4513+func (u *uboot) setBootVar(name, value string) error {
4514+ env, err := uenv.Open(bootloaderUbootFwEnvFile)
4515+ if err != nil {
4516+ return err
4517+ }
4518+
4519+ // already set, nothing to do
4520+ if env.Get(name) == value {
4521+ return nil
4522+ }
4523+
4524+ env.Set(name, value)
4525+ return env.Save()
4526+}
4527+
4528+func (u *uboot) hasBootVar(name string) (bool, error) {
4529+ v, err := u.getBootVar(name)
4530+ return v != "", err
4531+}
4532+
4533+func (u *uboot) getBootVar(name string) (string, error) {
4534+ env, err := uenv.Open(bootloaderUbootFwEnvFile)
4535+ if err != nil {
4536+ return "", err
4537+ }
4538+
4539+ return env.Get(name), nil
4540+}
4541+
4542+// FIXME: this is super similar to grub now, refactor to extract the
4543+// common code
4544+func (u *uboot) markCurrentBootSuccessfulFwEnv(currentRootfs string) error {
4545+ // Clear the variable set on boot to denote a good boot.
4546+ if err := u.setBootVar(bootloaderTrialBootVar, "0"); err != nil {
4547+ return err
4548+ }
4549+
4550+ if err := u.setBootVar(bootloaderRootfsVar, currentRootfs); err != nil {
4551+ return err
4552+ }
4553+
4554+ return u.setBootVar(bootloaderBootmodeVar, bootloaderBootmodeSuccess)
4555+}
4556+
4557+func (u *uboot) markCurrentBootSuccessfulLegacy(currentRootfs string) error {
4558+ changes := []configFileChange{
4559+ configFileChange{Name: bootloaderBootmodeVar,
4560+ Value: bootloaderBootmodeSuccess,
4561+ },
4562+ configFileChange{Name: bootloaderRootfsVar,
4563+ Value: string(u.currentRootfs),
4564+ },
4565+ }
4566+
4567+ if err := modifyNameValueFile(bootloaderUbootEnvFile, changes); err != nil {
4568+ return err
4569+ }
4570+
4571+ return os.RemoveAll(bootloaderUbootStampFile)
4572+}
4573+
4574+func (u *uboot) MarkCurrentBootSuccessful() error {
4575+ // modern system
4576+ if helpers.FileExists(bootloaderUbootFwEnvFile) {
4577+ return u.markCurrentBootSuccessfulFwEnv(u.currentRootfs)
4578+ }
4579+
4580+ // legacy
4581+ return u.markCurrentBootSuccessfulLegacy(u.currentRootfs)
4582+}
4583+
4584+// Write lines to file atomically. File does not have to preexist.
4585+// FIXME: put into utils package
4586+func atomicFileUpdateImpl(file string, lines []string) (err error) {
4587+ tmpFile := fmt.Sprintf("%s.NEW", file)
4588+
4589+ // XXX: if go switches to use aio_fsync, we need to open the dir for writing
4590+ dir, err := os.Open(filepath.Dir(file))
4591+ if err != nil {
4592+ return err
4593+ }
4594+ defer dir.Close()
4595+
4596+ if err := writeLines(lines, tmpFile); err != nil {
4597+ return err
4598+ }
4599+
4600+ // atomic update
4601+ if err := os.Rename(tmpFile, file); err != nil {
4602+ return err
4603+ }
4604+
4605+ return dir.Sync()
4606+>>>>>>> MERGE-SOURCE
4607 }
4608
4609 // Rewrite the specified file, applying the specified set of changes.
4610@@ -206,8 +526,13 @@
4611 // appended to the file.
4612 //
4613 // FIXME: put into utils package
4614+<<<<<<< TREE
4615 // FIXME: improve logic
4616 func modifyNameValueFile(path string, changes []configFileChange) error {
4617+=======
4618+// FIXME: improve logic
4619+func modifyNameValueFile(file string, changes []configFileChange) (err error) {
4620+>>>>>>> MERGE-SOURCE
4621 var updated []configFileChange
4622
4623 // we won't write to a file if we don't need to.
4624@@ -217,12 +542,21 @@
4625 if err != nil {
4626 return err
4627 }
4628+<<<<<<< TREE
4629 defer file.Close()
4630
4631 buf := bytes.NewBuffer(nil)
4632 scanner := bufio.NewScanner(file)
4633 for scanner.Scan() {
4634 line := scanner.Text()
4635+=======
4636+
4637+ var new []string
4638+ // we won't write to a file if we don't need to.
4639+ updateNeeded := false
4640+
4641+ for _, line := range lines {
4642+>>>>>>> MERGE-SOURCE
4643 for _, change := range changes {
4644 if strings.HasPrefix(line, fmt.Sprintf("%s=", change.Name)) {
4645 value := strings.SplitN(line, "=", 2)[1]
4646@@ -260,9 +594,18 @@
4647 }
4648 }
4649
4650+<<<<<<< TREE
4651 if updateNeeded {
4652 return atomicWriteFile(path, buf.Bytes(), 0644)
4653 }
4654+=======
4655+ if updateNeeded {
4656+ return atomicFileUpdate(file, lines)
4657+ }
4658+
4659+ return nil
4660+}
4661+>>>>>>> MERGE-SOURCE
4662
4663 return nil
4664 }
4665
4666=== modified file 'partition/bootloader_uboot_test.go'
4667--- partition/bootloader_uboot_test.go 2015-07-24 11:58:04 +0000
4668+++ partition/bootloader_uboot_test.go 2015-09-08 06:47:52 +0000
4669@@ -382,3 +382,93 @@
4670 c.Assert(err, IsNil)
4671 c.Assert(st.ModTime(), Equals, st2.ModTime())
4672 }
4673+
4674+func (s *PartitionTestSuite) TestNoWriteNotNeeded(c *C) {
4675+ s.makeFakeUbootEnv(c)
4676+
4677+ atomiCall := false
4678+ atomicFileUpdate = func(a string, b []string) error { atomiCall = true; return atomicFileUpdateImpl(a, b) }
4679+
4680+ partition := New()
4681+ u := newUboot(partition)
4682+ c.Assert(u, NotNil)
4683+
4684+ c.Check(u.MarkCurrentBootSuccessful(), IsNil)
4685+ c.Assert(atomiCall, Equals, false)
4686+}
4687+
4688+func (s *PartitionTestSuite) TestWriteDueToMissingValues(c *C) {
4689+ s.makeFakeUbootEnv(c)
4690+
4691+ // this file needs specific data
4692+ c.Assert(ioutil.WriteFile(bootloaderUbootEnvFile, []byte(""), 0644), IsNil)
4693+
4694+ atomiCall := false
4695+ atomicFileUpdate = func(a string, b []string) error { atomiCall = true; return atomicFileUpdateImpl(a, b) }
4696+
4697+ partition := New()
4698+ u := newUboot(partition)
4699+ c.Assert(u, NotNil)
4700+
4701+ c.Check(u.MarkCurrentBootSuccessful(), IsNil)
4702+ c.Assert(atomiCall, Equals, true)
4703+
4704+ bytes, err := ioutil.ReadFile(bootloaderUbootEnvFile)
4705+ c.Assert(err, IsNil)
4706+ c.Check(strings.Contains(string(bytes), "snappy_mode=try"), Equals, false)
4707+ c.Check(strings.Contains(string(bytes), "snappy_mode=regular"), Equals, true)
4708+ c.Check(strings.Contains(string(bytes), "snappy_ab=a"), Equals, true)
4709+}
4710+
4711+func (s *PartitionTestSuite) TestUbootMarkCurrentBootSuccessfulFwEnv(c *C) {
4712+ s.makeFakeUbootEnv(c)
4713+
4714+ env, err := uenv.Create(bootloaderUbootFwEnvFile, 4096)
4715+ c.Assert(err, IsNil)
4716+ env.Set("snappy_ab", "a")
4717+ env.Set("snappy_mode", "try")
4718+ env.Set("snappy_trial_boot", "1")
4719+ err = env.Save()
4720+ c.Assert(err, IsNil)
4721+
4722+ partition := New()
4723+ u := newUboot(partition)
4724+ c.Assert(u, NotNil)
4725+
4726+ err = u.MarkCurrentBootSuccessful()
4727+ c.Assert(err, IsNil)
4728+
4729+ env, err = uenv.Open(bootloaderUbootFwEnvFile)
4730+ c.Assert(err, IsNil)
4731+ c.Assert(env.String(), Equals, "snappy_ab=a\nsnappy_mode=regular\nsnappy_trial_boot=0\n")
4732+}
4733+
4734+func (s *PartitionTestSuite) TestUbootSetEnvNoUselessWrites(c *C) {
4735+ s.makeFakeUbootEnv(c)
4736+
4737+ env, err := uenv.Create(bootloaderUbootFwEnvFile, 4096)
4738+ c.Assert(err, IsNil)
4739+ env.Set("snappy_ab", "a")
4740+ env.Set("snappy_mode", "regular")
4741+ err = env.Save()
4742+ c.Assert(err, IsNil)
4743+
4744+ st, err := os.Stat(bootloaderUbootFwEnvFile)
4745+ c.Assert(err, IsNil)
4746+ time.Sleep(100 * time.Millisecond)
4747+
4748+ partition := New()
4749+ u := newUboot(partition)
4750+ c.Assert(u, NotNil)
4751+
4752+ err = u.(*uboot).setBootVar(bootloaderRootfsVar, "a")
4753+ c.Assert(err, IsNil)
4754+
4755+ env, err = uenv.Open(bootloaderUbootFwEnvFile)
4756+ c.Assert(err, IsNil)
4757+ c.Assert(env.String(), Equals, "snappy_ab=a\nsnappy_mode=regular\n")
4758+
4759+ st2, err := os.Stat(bootloaderUbootFwEnvFile)
4760+ c.Assert(err, IsNil)
4761+ c.Assert(st.ModTime(), Equals, st2.ModTime())
4762+}
4763
4764=== modified file 'partition/partition.go'
4765--- partition/partition.go 2015-07-15 07:53:43 +0000
4766+++ partition/partition.go 2015-09-08 06:47:52 +0000
4767@@ -596,6 +596,7 @@
4768 return errors.New("System is not dual root")
4769 }
4770
4771+<<<<<<< TREE
4772 bootloader, err := bootloader(p)
4773 if err != nil {
4774 return err
4775@@ -619,4 +620,23 @@
4776 }
4777
4778 return bootloader.BootDir()
4779+=======
4780+ bootloader, err := getBootloader(p)
4781+ if err != nil {
4782+ return err
4783+ }
4784+
4785+ // XXX: first toggle roofs and then handle assets? that seems
4786+ // wrong given that handleAssets may fails and we will
4787+ // knowingly boot into a broken system
4788+ err = p.RunWithOther(RW, func(otherRoot string) (err error) {
4789+ return bootloader.ToggleRootFS()
4790+ })
4791+
4792+ if err != nil {
4793+ return err
4794+ }
4795+
4796+ return bootloader.HandleAssets()
4797+>>>>>>> MERGE-SOURCE
4798 }
4799
4800=== modified file 'partition/partition_test.go'
4801=== modified file 'partition/utils_test.go'
4802=== modified file 'policy/policy_test.go'
4803=== modified file 'priv/priv_test.go'
4804=== modified file 'progress/progress.go'
4805=== modified file 'progress/progress_test.go'
4806=== modified file 'release/release_test.go'
4807=== modified file 'run-checks'
4808--- run-checks 2015-09-02 18:11:49 +0000
4809+++ run-checks 2015-09-08 06:47:52 +0000
4810@@ -53,6 +53,7 @@
4811 lint=$(golint ./... && golint ./_integration-tests/testutils/... && golint ./_integration-tests/tests/...)
4812 if [ -n "$lint" ]; then
4813 echo "Lint complains:"
4814+<<<<<<< TREE
4815 echo "$lint"
4816 exit 1
4817 fi
4818@@ -83,6 +84,24 @@
4819 if which subunit2pyunit >/dev/null 2>&1; then
4820 subunit-1to2 /tmp/snappy-test/output/artifacts/results.subunit | subunit2pyunit
4821 fi
4822+=======
4823+ echo "$lint"
4824+ exit 1
4825+>>>>>>> MERGE-SOURCE
4826+fi
4827+
4828+# integration tests
4829+echo Building the integration tests
4830+go build _integration-tests/main.go
4831+
4832+# the rabbit hole
4833+echo Running the tests for the integration testutils
4834+$goctest -v -cover ./_integration-tests/testutils/...
4835+
4836+# integration suite in kvm
4837+if which adt-run >/dev/null 2>&1; then
4838+ echo "Running integration tests on 15.04 edge"
4839+ go run _integration-tests/main.go --snappy-from-branch --release=15.04
4840 fi
4841
4842 echo "All good, what could possibly go wrong"
4843
4844=== modified file 'snappy/auth.go'
4845=== modified file 'snappy/auth_test.go'
4846--- snappy/auth_test.go 2015-06-09 12:56:34 +0000
4847+++ snappy/auth_test.go 2015-09-08 06:47:52 +0000
4848@@ -36,8 +36,8 @@
4849 const mockStoreInvalidLoginCode = 401
4850 const mockStoreInvalidLogin = `
4851 {
4852- "message": "Provided email/password is not correct.",
4853- "code": "INVALID_CREDENTIALS",
4854+ "message": "Provided email/password is not correct.",
4855+ "code": "INVALID_CREDENTIALS",
4856 "extra": {}
4857 }
4858 `
4859@@ -45,22 +45,22 @@
4860 const mockStoreNeeds2faHTTPCode = 401
4861 const mockStoreNeeds2fa = `
4862 {
4863- "message": "2-factor authentication required.",
4864- "code": "TWOFACTOR_REQUIRED",
4865+ "message": "2-factor authentication required.",
4866+ "code": "TWOFACTOR_REQUIRED",
4867 "extra": {}
4868 }
4869 `
4870
4871 const mockStoreReturnToken = `
4872 {
4873- "openid": "the-open-id-string-that-is-also-the-consumer-key-in-our-store",
4874- "token_name": "some-token-name",
4875- "date_updated": "2015-02-27T15:00:55.062",
4876- "token_key": "the-token-key",
4877- "consumer_secret": "the-consumer-secret",
4878- "href": "/api/v2/tokens/oauth/something",
4879- "date_created": "2015-02-27T14:54:30.863",
4880- "consumer_key": "the-consumer-key",
4881+ "openid": "the-open-id-string-that-is-also-the-consumer-key-in-our-store",
4882+ "token_name": "some-token-name",
4883+ "date_updated": "2015-02-27T15:00:55.062",
4884+ "token_key": "the-token-key",
4885+ "consumer_secret": "the-consumer-secret",
4886+ "href": "/api/v2/tokens/oauth/something",
4887+ "date_created": "2015-02-27T14:54:30.863",
4888+ "consumer_key": "the-consumer-key",
4889 "token_secret": "the-token-secret"
4890 }
4891 `
4892
4893=== modified file 'snappy/build_test.go'
4894--- snappy/build_test.go 2015-07-29 09:49:41 +0000
4895+++ snappy/build_test.go 2015-09-08 06:47:52 +0000
4896@@ -25,11 +25,16 @@
4897 "os/exec"
4898 "path/filepath"
4899 "strings"
4900+<<<<<<< TREE
4901 "syscall"
4902
4903 "launchpad.net/snappy/helpers"
4904
4905 . "gopkg.in/check.v1"
4906+=======
4907+
4908+ . "gopkg.in/check.v1"
4909+>>>>>>> MERGE-SOURCE
4910 )
4911
4912 func makeFakeDuCommand(c *C) string {
4913
4914=== modified file 'snappy/click.go'
4915--- snappy/click.go 2015-09-04 13:08:48 +0000
4916+++ snappy/click.go 2015-09-08 06:47:52 +0000
4917@@ -332,9 +332,15 @@
4918 AppArch string
4919 AppPath string
4920 Version string
4921+<<<<<<< TREE
4922 UdevAppName string
4923 Origin string
4924 Home string
4925+=======
4926+ UdevAppName string
4927+ Namespace string
4928+ Home string
4929+>>>>>>> MERGE-SOURCE
4930 Target string
4931 AaProfile string
4932 OldAppVars string
4933@@ -344,9 +350,15 @@
4934 AppArch: helpers.UbuntuArchitecture(),
4935 AppPath: pkgPath,
4936 Version: m.Version,
4937+<<<<<<< TREE
4938 UdevAppName: udevPartName,
4939 Origin: origin,
4940 Home: "$HOME",
4941+=======
4942+ UdevAppName: udevPartName,
4943+ Namespace: namespace,
4944+ Home: "$HOME",
4945+>>>>>>> MERGE-SOURCE
4946 Target: actualBinPath,
4947 AaProfile: aaProfile,
4948 }
4949@@ -436,8 +448,14 @@
4950 socketFileName = filepath.Base(generateSocketFileName(m, service))
4951 }
4952
4953+ socketFileName := ""
4954+ if service.Socket {
4955+ socketFileName = filepath.Base(generateSocketFileName(m, service))
4956+ }
4957+
4958 return systemd.New(globalRootDir, nil).GenServiceFile(
4959 &systemd.ServiceDescription{
4960+<<<<<<< TREE
4961 AppName: m.Name,
4962 ServiceName: service.Name,
4963 Version: m.Version,
4964@@ -474,6 +492,45 @@
4965 }
4966
4967 func generateServiceFileName(m *packageYaml, service ServiceYaml) string {
4968+=======
4969+ AppName: m.Name,
4970+ ServiceName: service.Name,
4971+ Version: m.Version,
4972+ Description: service.Description,
4973+ AppPath: baseDir,
4974+ Start: service.Start,
4975+ Stop: service.Stop,
4976+ PostStop: service.PostStop,
4977+ StopTimeout: time.Duration(service.StopTimeout),
4978+ AaProfile: aaProfile,
4979+ IsFramework: m.Type == SnapTypeFramework,
4980+ IsNetworked: service.Ports != nil && len(service.Ports.External) > 0,
4981+ BusName: service.BusName,
4982+ UdevAppName: udevPartName,
4983+ Socket: service.Socket,
4984+ SocketFileName: socketFileName,
4985+ }), nil
4986+}
4987+
4988+func generateSnapSocketFile(service Service, baseDir string, aaProfile string, m *packageYaml) (string, error) {
4989+ if err := verifyServiceYaml(service); err != nil {
4990+ return "", err
4991+ }
4992+
4993+ serviceFileName := filepath.Base(generateServiceFileName(m, service))
4994+
4995+ return systemd.New(globalRootDir, nil).GenSocketFile(
4996+ &systemd.ServiceDescription{
4997+ ServiceFileName: serviceFileName,
4998+ ListenStream: service.ListenStream,
4999+ SocketMode: service.SocketMode,
5000+ SocketUser: service.SocketUser,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches