Merge lp:~mvo/snappy/15.04-systemd-sockets into lp:~snappy-dev/snappy/snappy-moved-to-github
- 15.04-systemd-sockets
- Merge into snappy-moved-to-github
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Snappy Developers | Pending | ||
Review via email: mp+270359@code.launchpad.net |
Commit message
Description of the change
Backport of the branch that adds systemd socket file generation which is important for docker.
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
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' |
147 | Binary 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' |
160 | Binary 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' |
182 | Binary 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' |
2484 | Binary 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, |