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

Proposed by Michael Vogt
Status: Merged
Approved by: John Lenton
Approved revision: 765
Merged at revision: 714
Proposed branch: lp:~snappy-dev/snappy/snappy-moved-to-github
Merge into: lp:snappy/15.04
Diff against target: 9111 lines (+4553/-1052)
109 files modified
_integration-tests/data/snaps/basic-binaries/bin/echo (+3/-0)
_integration-tests/data/snaps/basic-binaries/meta/package.yaml (+6/-0)
_integration-tests/data/snaps/basic-binaries/meta/readme.md (+3/-0)
_integration-tests/data/snaps/dev-kmsg/bin/reader (+3/-0)
_integration-tests/data/snaps/dev-kmsg/meta/package.yaml (+6/-0)
_integration-tests/data/snaps/dev-kmsg/meta/readme.md (+5/-0)
_integration-tests/tests/activate_test.go (+84/-0)
_integration-tests/tests/apt_test.go (+5/-4)
_integration-tests/tests/config_test.go (+114/-0)
_integration-tests/tests/examples_test.go (+72/-0)
_integration-tests/tests/failover_rclocal_crash_test.go (+13/-10)
_integration-tests/tests/failover_systemd_loop_test.go (+19/-17)
_integration-tests/tests/failover_test.go (+10/-10)
_integration-tests/tests/failover_zero_size_file_test.go (+12/-11)
_integration-tests/tests/hwAssign_test.go (+106/-0)
_integration-tests/tests/info_test.go (+22/-17)
_integration-tests/tests/installApp_test.go (+3/-33)
_integration-tests/tests/installFramework_test.go (+12/-13)
_integration-tests/tests/list_test.go (+8/-7)
_integration-tests/tests/rollback_test.go (+23/-17)
_integration-tests/tests/search_test.go (+8/-7)
_integration-tests/tests/service_test.go (+37/-37)
_integration-tests/tests/snapd_1_0_packages_test.go (+2/-0)
_integration-tests/tests/snapd_1_0_test.go (+41/-0)
_integration-tests/tests/ubuntuFan_test.go (+10/-9)
_integration-tests/tests/update_test.go (+8/-8)
_integration-tests/tests/writablePaths_test.go (+3/-3)
_integration-tests/testutils/autopkgtest/autopkgtest_test.go (+1/-1)
_integration-tests/testutils/autopkgtest/ssh.go (+1/-1)
_integration-tests/testutils/build/snap.go (+3/-3)
_integration-tests/testutils/build/snap_test.go (+3/-3)
_integration-tests/testutils/cli/cli.go (+64/-0)
_integration-tests/testutils/cli/cli_test.go (+113/-0)
_integration-tests/testutils/common/common.go (+20/-55)
_integration-tests/testutils/common/info.go (+2/-1)
_integration-tests/testutils/partition/bootloader.go (+4/-3)
_integration-tests/testutils/partition/bootloader_test.go (+1/-1)
_integration-tests/testutils/partition/partition.go (+89/-0)
_integration-tests/testutils/partition/partition_test.go (+170/-0)
_integration-tests/testutils/runner/runner.go (+1/-1)
_integration-tests/testutils/wait/wait.go (+3/-3)
cmd/snappy/cmd_activate.go (+60/-0)
cmd/snappy/cmd_build.go (+8/-2)
cmd/snappy/cmd_info.go (+2/-2)
cmd/snappy/cmd_internal_unpack.go (+1/-1)
daemon/api.go (+168/-164)
daemon/api_test.go (+174/-124)
daemon/daemon.go (+0/-7)
daemon/daemon_test.go (+95/-0)
daemon/response.go (+16/-15)
daemon/snappy_part_iface_test.go (+2/-2)
data/failure.txt (+8/-0)
data/success.txt (+20/-0)
debian/control (+3/-1)
debian/ubuntu-snappy.snapd.socket (+5/-1)
dirs/dirs.go (+44/-44)
gen-coverage.sh (+4/-18)
helpers/helpers.go (+8/-7)
helpers/helpers_test.go (+19/-3)
pkg/clickdeb/deb.go (+5/-0)
pkg/lightweight/example_test.go (+58/-0)
pkg/lightweight/lightweight.go (+527/-0)
pkg/lightweight/lightweight_test.go (+417/-0)
pkg/lightweight/split_test.go (+70/-0)
pkg/remote/remote.go (+45/-0)
pkg/removed/removed.go (+158/-0)
pkg/removed/removed_test.go (+137/-0)
pkg/snapfs/pkg.go (+142/-0)
pkg/snapfs/pkg_test.go (+173/-0)
po/de.po (+18/-3)
po/es.po (+18/-3)
po/gl.po (+57/-4)
po/snappy.pot (+13/-1)
run-checks (+28/-11)
snappy/build.go (+46/-15)
snappy/build_test.go (+67/-13)
snappy/click.go (+25/-28)
snappy/click_test.go (+60/-49)
snappy/common_test.go (+40/-5)
snappy/datadir.go (+6/-1)
snappy/datadir_test.go (+19/-15)
snappy/firstboot.go (+1/-1)
snappy/firstboot_test.go (+1/-1)
snappy/globals.go (+3/-2)
snappy/hwaccess.go (+5/-4)
snappy/hwaccess_test.go (+15/-14)
snappy/install_test.go (+7/-5)
snappy/oem.go (+8/-7)
snappy/oem_test.go (+5/-4)
snappy/parts.go (+23/-12)
snappy/parts_test.go (+37/-15)
snappy/pkgformat.go (+64/-0)
snappy/purge.go (+2/-1)
snappy/purge_test.go (+6/-4)
snappy/security.go (+3/-2)
snappy/security_test.go (+4/-3)
snappy/service.go (+6/-1)
snappy/service_test.go (+24/-6)
snappy/set_active.go (+53/-0)
snappy/set_active_test.go (+20/-0)
snappy/set_test.go (+9/-8)
snappy/snapp.go (+56/-63)
snappy/snapp_snapfs_test.go (+84/-0)
snappy/snapp_test.go (+61/-37)
snappy/sort_test.go (+5/-4)
snappy/systemimage.go (+49/-27)
snappy/systemimage_native.go (+12/-1)
snappy/systemimage_native_test.go (+12/-0)
snappy/systemimage_test.go (+99/-16)
To merge this branch: bzr merge lp:~snappy-dev/snappy/snappy-moved-to-github
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+274107@code.launchpad.net

Commit message

Merge trunk fixes.

Description of the change

Update 15.04 from trunk to bring in the husks^Wlightweight improvements from for the rest API

To post a comment you must log in.
760. By John Lenton

Lightweights didn't consider "current" in the datadir, which is now a thing. Fixed that. by chipaca approved by mvo

761. By John Lenton

Only run service commands on active parts. by chipaca approved by mvo

762. By John Lenton

Don't let people sideload over things that are installed; it breaks stuff. by chipaca approved by mvo

Revision history for this message
John Lenton (chipaca) wrote :

... studiously ignoring this mp until a few more branches land...

763. By John Lenton

Ignore toplevel DEBIAN in build. by chipaca approved by mvo

764. By John Lenton

Switch socket to be a named unix socket, /run/snapd.socket. by chipaca approved by mvo

765. By John Lenton

Include store id in /1.0 response. by chipaca approved by mvo

Revision history for this message
John Lenton (chipaca) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '_integration-tests/data/snaps/basic-binaries'
2=== added directory '_integration-tests/data/snaps/basic-binaries/bin'
3=== added file '_integration-tests/data/snaps/basic-binaries/bin/echo'
4--- _integration-tests/data/snaps/basic-binaries/bin/echo 1970-01-01 00:00:00 +0000
5+++ _integration-tests/data/snaps/basic-binaries/bin/echo 2015-10-12 09:23:01 +0000
6@@ -0,0 +1,3 @@
7+#!/bin/sh
8+
9+echo "From basic-binaries snap"
10
11=== added directory '_integration-tests/data/snaps/basic-binaries/meta'
12=== added file '_integration-tests/data/snaps/basic-binaries/meta/package.yaml'
13--- _integration-tests/data/snaps/basic-binaries/meta/package.yaml 1970-01-01 00:00:00 +0000
14+++ _integration-tests/data/snaps/basic-binaries/meta/package.yaml 2015-10-12 09:23:01 +0000
15@@ -0,0 +1,6 @@
16+name: basic-binaries
17+version: 1.0
18+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
19+icon: meta/snappy64.png
20+binaries:
21+ - name: bin/echo
22
23=== added file '_integration-tests/data/snaps/basic-binaries/meta/readme.md'
24--- _integration-tests/data/snaps/basic-binaries/meta/readme.md 1970-01-01 00:00:00 +0000
25+++ _integration-tests/data/snaps/basic-binaries/meta/readme.md 2015-10-12 09:23:01 +0000
26@@ -0,0 +1,3 @@
27+Basic Binary snap
28+
29+A basic snap with binary entries
30
31=== added file '_integration-tests/data/snaps/basic-binaries/meta/snappy64.png'
32Binary files _integration-tests/data/snaps/basic-binaries/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/basic-binaries/meta/snappy64.png 2015-10-12 09:23:01 +0000 differ
33=== added directory '_integration-tests/data/snaps/dev-kmsg'
34=== added directory '_integration-tests/data/snaps/dev-kmsg/bin'
35=== added file '_integration-tests/data/snaps/dev-kmsg/bin/reader'
36--- _integration-tests/data/snaps/dev-kmsg/bin/reader 1970-01-01 00:00:00 +0000
37+++ _integration-tests/data/snaps/dev-kmsg/bin/reader 2015-10-12 09:23:01 +0000
38@@ -0,0 +1,3 @@
39+#!/bin/sh
40+
41+dd if=/dev/kmsg iflag=nonblock
42\ No newline at end of file
43
44=== added directory '_integration-tests/data/snaps/dev-kmsg/meta'
45=== added file '_integration-tests/data/snaps/dev-kmsg/meta/package.yaml'
46--- _integration-tests/data/snaps/dev-kmsg/meta/package.yaml 1970-01-01 00:00:00 +0000
47+++ _integration-tests/data/snaps/dev-kmsg/meta/package.yaml 2015-10-12 09:23:01 +0000
48@@ -0,0 +1,6 @@
49+name: dev-kmsg
50+version: 1.0
51+vendor: Snappy Developers <snappy-devel@lists.ubuntu.com>
52+icon: meta/snappy64.png
53+binaries:
54+ - name: bin/reader
55
56=== added file '_integration-tests/data/snaps/dev-kmsg/meta/readme.md'
57--- _integration-tests/data/snaps/dev-kmsg/meta/readme.md 1970-01-01 00:00:00 +0000
58+++ _integration-tests/data/snaps/dev-kmsg/meta/readme.md 2015-10-12 09:23:01 +0000
59@@ -0,0 +1,5 @@
60+dev-kmsg snap
61+
62+This simple snap provides a binary to read and output real raw
63+data from /dev/kmsg using dd. For it to work it needs that hw
64+to be assigned before being run.
65
66=== added file '_integration-tests/data/snaps/dev-kmsg/meta/snappy64.png'
67Binary files _integration-tests/data/snaps/dev-kmsg/meta/snappy64.png 1970-01-01 00:00:00 +0000 and _integration-tests/data/snaps/dev-kmsg/meta/snappy64.png 2015-10-12 09:23:01 +0000 differ
68=== added file '_integration-tests/tests/activate_test.go'
69--- _integration-tests/tests/activate_test.go 1970-01-01 00:00:00 +0000
70+++ _integration-tests/tests/activate_test.go 2015-10-12 09:23:01 +0000
71@@ -0,0 +1,84 @@
72+// -*- Mode: Go; indent-tabs-mode: t -*-
73+
74+/*
75+ * Copyright (C) 2015 Canonical Ltd
76+ *
77+ * This program is free software: you can redistribute it and/or modify
78+ * it under the terms of the GNU General Public License version 3 as
79+ * published by the Free Software Foundation.
80+ *
81+ * This program is distributed in the hope that it will be useful,
82+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
83+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
84+ * GNU General Public License for more details.
85+ *
86+ * You should have received a copy of the GNU General Public License
87+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
88+ *
89+ */
90+
91+package tests
92+
93+import (
94+ "os"
95+
96+ "gopkg.in/check.v1"
97+
98+ "launchpad.net/snappy/_integration-tests/testutils/build"
99+ "launchpad.net/snappy/_integration-tests/testutils/cli"
100+ "launchpad.net/snappy/_integration-tests/testutils/common"
101+)
102+
103+const (
104+ activateSnapName = "basic-binaries"
105+ activateBinName = activateSnapName + ".echo"
106+ activateEchoOutput = "From basic-binaries snap\n"
107+ baseActivatePattern = "(?msU).*" + activateSnapName + `\s*.*\s*.*sideload`
108+ activatedPattern = baseActivatePattern + `\*\s*\n.*`
109+ deActivatedPattern = baseActivatePattern + `\s*\n.*`
110+)
111+
112+var _ = check.Suite(&activateSuite{})
113+
114+type activateSuite struct {
115+ common.SnappySuite
116+ snapPath string
117+}
118+
119+func (s *activateSuite) SetUpSuite(c *check.C) {
120+ s.SnappySuite.SetUpSuite(c)
121+ var err error
122+ s.snapPath, err = build.LocalSnap(c, activateSnapName)
123+ c.Assert(err, check.IsNil)
124+ common.InstallSnap(c, s.snapPath)
125+}
126+
127+func (s *activateSuite) TearDownSuite(c *check.C) {
128+ os.Remove(s.snapPath)
129+ common.RemoveSnap(c, activateSnapName)
130+}
131+
132+func (s *activateSuite) TestDeactivateRemovesBinary(c *check.C) {
133+ cli.ExecCommand(c, "sudo", "snappy", "deactivate", activateSnapName)
134+ defer cli.ExecCommand(c, "sudo", "snappy", "activate", activateSnapName)
135+ output, err := cli.ExecCommandErr(activateBinName)
136+
137+ c.Assert(err, check.NotNil)
138+ c.Assert(output, check.Not(check.Equals), activateEchoOutput)
139+
140+ list := cli.ExecCommand(c, "snappy", "list", "-v")
141+
142+ c.Assert(list, check.Matches, deActivatedPattern)
143+}
144+
145+func (s *activateSuite) TestActivateBringsBinaryBack(c *check.C) {
146+ cli.ExecCommand(c, "sudo", "snappy", "deactivate", activateSnapName)
147+ cli.ExecCommand(c, "sudo", "snappy", "activate", activateSnapName)
148+ output := cli.ExecCommand(c, activateBinName)
149+
150+ c.Assert(output, check.Equals, activateEchoOutput)
151+
152+ list := cli.ExecCommand(c, "snappy", "list", "-v")
153+
154+ c.Assert(list, check.Matches, activatedPattern)
155+}
156
157=== modified file '_integration-tests/tests/apt_test.go'
158--- _integration-tests/tests/apt_test.go 2015-07-28 04:03:52 +0000
159+++ _integration-tests/tests/apt_test.go 2015-10-12 09:23:01 +0000
160@@ -20,19 +20,20 @@
161 package tests
162
163 import (
164- . "launchpad.net/snappy/_integration-tests/testutils/common"
165+ "launchpad.net/snappy/_integration-tests/testutils/cli"
166+ "launchpad.net/snappy/_integration-tests/testutils/common"
167
168- check "gopkg.in/check.v1"
169+ "gopkg.in/check.v1"
170 )
171
172 var _ = check.Suite(&aptSuite{})
173
174 type aptSuite struct {
175- SnappySuite
176+ common.SnappySuite
177 }
178
179 func (s *aptSuite) TestAptGetMustPrintError(c *check.C) {
180- aptOutput := ExecCommand(c, "apt-get", "update")
181+ aptOutput := cli.ExecCommand(c, "apt-get", "update")
182
183 expected := "Ubuntu Core does not use apt-get, see 'snappy --help'!\n"
184 c.Assert(aptOutput, check.Equals, expected)
185
186=== added file '_integration-tests/tests/config_test.go'
187--- _integration-tests/tests/config_test.go 1970-01-01 00:00:00 +0000
188+++ _integration-tests/tests/config_test.go 2015-10-12 09:23:01 +0000
189@@ -0,0 +1,114 @@
190+// -*- Mode: Go; indent-tabs-mode: t -*-
191+
192+/*
193+ * Copyright (C) 2015 Canonical Ltd
194+ *
195+ * This program is free software: you can redistribute it and/or modify
196+ * it under the terms of the GNU General Public License version 3 as
197+ * published by the Free Software Foundation.
198+ *
199+ * This program is distributed in the hope that it will be useful,
200+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
201+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
202+ * GNU General Public License for more details.
203+ *
204+ * You should have received a copy of the GNU General Public License
205+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
206+ *
207+ */
208+
209+package tests
210+
211+import (
212+ "io/ioutil"
213+ "os"
214+ "strings"
215+
216+ "launchpad.net/snappy/_integration-tests/testutils/cli"
217+ "launchpad.net/snappy/_integration-tests/testutils/common"
218+
219+ "gopkg.in/check.v1"
220+)
221+
222+var _ = check.Suite(&configSuite{})
223+
224+type configSuite struct {
225+ common.SnappySuite
226+ backConfig string
227+}
228+
229+func (s *configSuite) SetUpTest(c *check.C) {
230+ s.SnappySuite.SetUpTest(c)
231+ s.backConfig = currentConfig(c)
232+}
233+
234+func (s *configSuite) TearDownTest(c *check.C) {
235+ s.SnappySuite.TearDownTest(c)
236+ s.setConfig(c, s.backConfig)
237+}
238+
239+func (s *configSuite) TestModprobe(c *check.C) {
240+ s.doTest(c, `modprobe: blacklist floppy`)
241+}
242+
243+func (s *configSuite) TestPPP(c *check.C) {
244+ s.doTest(c, `network:
245+ ppp:
246+ - name: chap-secrets
247+ content: secret`)
248+}
249+
250+func (s *configSuite) TestWatchdog(c *check.C) {
251+ s.doTest(c, `watchdog:
252+ startup: |
253+ run_watchdog=0
254+ run_wd_keepalive=0
255+ watchdog_module="none"`)
256+}
257+
258+func (s *configSuite) TestNetworkInterfaces(c *check.C) {
259+ s.doTest(c, `network:
260+ interfaces:
261+ - name: eth0
262+ content: config`)
263+}
264+
265+func (s *configSuite) doTest(c *check.C, targetCfg string) {
266+ config := configString(targetCfg)
267+
268+ err := s.setConfig(c, config)
269+ c.Assert(err, check.IsNil)
270+
271+ actualConfig := currentConfig(c)
272+
273+ // we admit any characters after a new line, this is needed because the reuse
274+ // of the config yaml string in the regex pattern, after setting a configuration
275+ // option any other sibling options can appear before the one set when displaying
276+ // the configuration (for instance, network-interfaces is always shown before
277+ // network-ppp)
278+ configPattern := "(?Usm).*" + strings.Replace(targetCfg, "\n", "\n.*", -1) + ".*"
279+ c.Assert(actualConfig, check.Matches, configPattern)
280+}
281+
282+func (s *configSuite) setConfig(c *check.C, config string) (err error) {
283+ configFile, err := ioutil.TempFile("", "snappy-cfg")
284+ defer func() { configFile.Close(); os.Remove(configFile.Name()) }()
285+ if err != nil {
286+ return
287+ }
288+ _, err = configFile.Write([]byte(config))
289+
290+ cli.ExecCommand(c, "sudo", "snappy", "config", "ubuntu-core", configFile.Name())
291+
292+ return
293+}
294+
295+func currentConfig(c *check.C) string {
296+ return cli.ExecCommand(c, "sudo", "snappy", "config", "ubuntu-core")
297+}
298+
299+func configString(cfg string) string {
300+ return `config:
301+ ubuntu-core:
302+ ` + cfg
303+}
304
305=== added file '_integration-tests/tests/examples_test.go'
306--- _integration-tests/tests/examples_test.go 1970-01-01 00:00:00 +0000
307+++ _integration-tests/tests/examples_test.go 2015-10-12 09:23:01 +0000
308@@ -0,0 +1,72 @@
309+// -*- Mode: Go; indent-tabs-mode: t -*-
310+
311+/*
312+ * Copyright (C) 2015 Canonical Ltd
313+ *
314+ * This program is free software: you can redistribute it and/or modify
315+ * it under the terms of the GNU General Public License version 3 as
316+ * published by the Free Software Foundation.
317+ *
318+ * This program is distributed in the hope that it will be useful,
319+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
320+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
321+ * GNU General Public License for more details.
322+ *
323+ * You should have received a copy of the GNU General Public License
324+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
325+ *
326+ */
327+
328+package tests
329+
330+import (
331+ "net/http"
332+
333+ "launchpad.net/snappy/_integration-tests/testutils/cli"
334+ "launchpad.net/snappy/_integration-tests/testutils/common"
335+ "launchpad.net/snappy/_integration-tests/testutils/wait"
336+
337+ "gopkg.in/check.v1"
338+)
339+
340+var _ = check.Suite(&webserverExampleSuite{})
341+
342+type webserverExampleSuite struct {
343+ common.SnappySuite
344+}
345+
346+func (s *webserverExampleSuite) TestNetworkingServiceMustBeStarted(c *check.C) {
347+ baseAppName := "xkcd-webserver"
348+ appName := baseAppName + ".canonical"
349+ common.InstallSnap(c, appName)
350+ defer common.RemoveSnap(c, appName)
351+
352+ err := wait.ForServerOnPort(c, 80)
353+ c.Assert(err, check.IsNil)
354+
355+ resp, err := http.Get("http://localhost")
356+ c.Assert(err, check.IsNil)
357+ c.Check(resp.Status, check.Equals, "200 OK")
358+ c.Assert(resp.Proto, check.Equals, "HTTP/1.0")
359+}
360+
361+var _ = check.Suite(&frameworkExampleSuite{})
362+
363+type frameworkExampleSuite struct {
364+ common.SnappySuite
365+}
366+
367+func (s *frameworkExampleSuite) TestFrameworkClient(c *check.C) {
368+ common.InstallSnap(c, "hello-dbus-fwk.canonical")
369+ defer common.RemoveSnap(c, "hello-dbus-fwk.canonical")
370+
371+ common.InstallSnap(c, "hello-dbus-app.canonical")
372+ defer common.RemoveSnap(c, "hello-dbus-app.canonical")
373+
374+ output := cli.ExecCommand(c, "hello-dbus-app.client")
375+
376+ expected := "PASS\n"
377+
378+ c.Assert(output, check.Equals, expected,
379+ check.Commentf("Expected output %s not found, %s", expected, output))
380+}
381
382=== modified file '_integration-tests/tests/failover_rclocal_crash_test.go'
383--- _integration-tests/tests/failover_rclocal_crash_test.go 2015-09-09 06:56:19 +0000
384+++ _integration-tests/tests/failover_rclocal_crash_test.go 2015-10-12 09:23:01 +0000
385@@ -22,26 +22,29 @@
386 import (
387 "fmt"
388
389- . "launchpad.net/snappy/_integration-tests/testutils/common"
390+ "launchpad.net/snappy/_integration-tests/testutils/cli"
391+ "launchpad.net/snappy/_integration-tests/testutils/common"
392+ "launchpad.net/snappy/_integration-tests/testutils/partition"
393
394- check "gopkg.in/check.v1"
395+ "gopkg.in/check.v1"
396 )
397
398 type rcLocalCrash struct{}
399
400 func (rcLocalCrash) set(c *check.C) {
401- MakeWritable(c, BaseAltPartitionPath)
402- defer MakeReadonly(c, BaseAltPartitionPath)
403- targetFile := fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath)
404- ExecCommand(c, "sudo", "chmod", "a+xw", targetFile)
405- ExecCommandToFile(c, targetFile,
406+ partition.MakeWritable(c, common.BaseAltPartitionPath)
407+ defer partition.MakeReadonly(c, common.BaseAltPartitionPath)
408+ targetFile := fmt.Sprintf("%s/etc/rc.local", common.BaseAltPartitionPath)
409+ cli.ExecCommand(c, "sudo", "chmod", "a+xw", targetFile)
410+
411+ cli.ExecCommandToFile(c, targetFile,
412 "sudo", "echo", "#!bin/sh\nprintf c > /proc/sysrq-trigger")
413 }
414
415 func (rcLocalCrash) unset(c *check.C) {
416- MakeWritable(c, BaseAltPartitionPath)
417- defer MakeReadonly(c, BaseAltPartitionPath)
418- ExecCommand(c, "sudo", "rm", fmt.Sprintf("%s/etc/rc.local", BaseAltPartitionPath))
419+ partition.MakeWritable(c, common.BaseAltPartitionPath)
420+ defer partition.MakeReadonly(c, common.BaseAltPartitionPath)
421+ cli.ExecCommand(c, "sudo", "rm", fmt.Sprintf("%s/etc/rc.local", common.BaseAltPartitionPath))
422 }
423
424 /*
425
426=== modified file '_integration-tests/tests/failover_systemd_loop_test.go'
427--- _integration-tests/tests/failover_systemd_loop_test.go 2015-07-30 16:22:30 +0000
428+++ _integration-tests/tests/failover_systemd_loop_test.go 2015-10-12 09:23:01 +0000
429@@ -22,9 +22,11 @@
430 import (
431 "fmt"
432
433- . "launchpad.net/snappy/_integration-tests/testutils/common"
434+ "launchpad.net/snappy/_integration-tests/testutils/cli"
435+ "launchpad.net/snappy/_integration-tests/testutils/common"
436+ "launchpad.net/snappy/_integration-tests/testutils/partition"
437
438- check "gopkg.in/check.v1"
439+ "gopkg.in/check.v1"
440 )
441
442 const (
443@@ -61,51 +63,51 @@
444 type systemdDependencyLoop struct{}
445
446 func (systemdDependencyLoop) set(c *check.C) {
447- installService(c, "deadlock", deadlockService, BaseAltPartitionPath)
448- installService(c, "emerg-reboot", rebootService, BaseAltPartitionPath)
449+ installService(c, "deadlock", deadlockService, common.BaseAltPartitionPath)
450+ installService(c, "emerg-reboot", rebootService, common.BaseAltPartitionPath)
451 }
452
453 func (systemdDependencyLoop) unset(c *check.C) {
454- unInstallService(c, "deadlock", BaseAltPartitionPath)
455- unInstallService(c, "emerg-reboot", BaseAltPartitionPath)
456+ unInstallService(c, "deadlock", common.BaseAltPartitionPath)
457+ unInstallService(c, "emerg-reboot", common.BaseAltPartitionPath)
458 }
459
460 func installService(c *check.C, serviceName, serviceCfg, basePath string) {
461- MakeWritable(c, basePath)
462- defer MakeReadonly(c, basePath)
463+ partition.MakeWritable(c, basePath)
464+ defer partition.MakeReadonly(c, basePath)
465
466 // Create service file
467 serviceFile := fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName)
468- ExecCommand(c, "sudo", "chmod", "a+w", fmt.Sprintf("%s%s", basePath, baseSystemdPath))
469- ExecCommandToFile(c, serviceFile, "sudo", "echo", serviceCfg)
470+ cli.ExecCommand(c, "sudo", "chmod", "a+w", fmt.Sprintf("%s%s", basePath, baseSystemdPath))
471+ cli.ExecCommandToFile(c, serviceFile, "sudo", "echo", serviceCfg)
472
473 // Create requires directory
474 requiresDirPart := fmt.Sprintf("%s/%s", baseSystemdPath, systemdTargetRequiresDir)
475 requiresDir := fmt.Sprintf("%s%s", basePath, requiresDirPart)
476- ExecCommand(c, "sudo", "mkdir", "-p", requiresDir)
477+ cli.ExecCommand(c, "sudo", "mkdir", "-p", requiresDir)
478
479 // Symlink from the requires dir to the service file (with chroot for being
480 // usable in the other partition)
481- ExecCommand(c, "sudo", "chroot", basePath, "ln", "-s",
482+ cli.ExecCommand(c, "sudo", "chroot", basePath, "ln", "-s",
483 fmt.Sprintf("%s/%s.service", baseSystemdPath, serviceName),
484 fmt.Sprintf("%s/%s.service", requiresDirPart, serviceName),
485 )
486 }
487
488 func unInstallService(c *check.C, serviceName, basePath string) {
489- MakeWritable(c, basePath)
490- defer MakeReadonly(c, basePath)
491+ partition.MakeWritable(c, basePath)
492+ defer partition.MakeReadonly(c, basePath)
493
494 // Disable the service
495- ExecCommand(c, "sudo", "chroot", basePath,
496+ cli.ExecCommand(c, "sudo", "chroot", basePath,
497 "systemctl", "disable", fmt.Sprintf("%s.service", serviceName))
498
499 // Remove the service file
500- ExecCommand(c, "sudo", "rm",
501+ cli.ExecCommand(c, "sudo", "rm",
502 fmt.Sprintf("%s%s/%s.service", basePath, baseSystemdPath, serviceName))
503
504 // Remove the requires symlink
505- ExecCommand(c, "sudo", "rm",
506+ cli.ExecCommand(c, "sudo", "rm",
507 fmt.Sprintf("%s%s/%s/%s.service", basePath, baseSystemdPath, systemdTargetRequiresDir, serviceName))
508 }
509
510
511=== modified file '_integration-tests/tests/failover_test.go'
512--- _integration-tests/tests/failover_test.go 2015-07-28 04:03:52 +0000
513+++ _integration-tests/tests/failover_test.go 2015-10-12 09:23:01 +0000
514@@ -20,15 +20,15 @@
515 package tests
516
517 import (
518- check "gopkg.in/check.v1"
519+ "gopkg.in/check.v1"
520
521- . "launchpad.net/snappy/_integration-tests/testutils/common"
522+ "launchpad.net/snappy/_integration-tests/testutils/common"
523 )
524
525 var _ = check.Suite(&failoverSuite{})
526
527 type failoverSuite struct {
528- SnappySuite
529+ common.SnappySuite
530 }
531
532 // The types that implement this interface can be used in the test logic
533@@ -43,16 +43,16 @@
534 // type implementing the failer interface and call this function with an instance
535 // of it
536 func commonFailoverTest(c *check.C, f failer) {
537- currentVersion := GetCurrentUbuntuCoreVersion(c)
538+ currentVersion := common.GetCurrentUbuntuCoreVersion(c)
539
540- if AfterReboot(c) {
541- RemoveRebootMark(c)
542+ if common.AfterReboot(c) {
543+ common.RemoveRebootMark(c)
544 f.unset(c)
545- c.Assert(GetSavedVersion(c), check.Equals, currentVersion)
546+ c.Assert(common.GetSavedVersion(c), check.Equals, currentVersion)
547 } else {
548- SetSavedVersion(c, currentVersion-1)
549- CallFakeUpdate(c)
550+ common.SetSavedVersion(c, currentVersion-1)
551+ common.CallFakeUpdate(c)
552 f.set(c)
553- Reboot(c)
554+ common.Reboot(c)
555 }
556 }
557
558=== modified file '_integration-tests/tests/failover_zero_size_file_test.go'
559--- _integration-tests/tests/failover_zero_size_file_test.go 2015-09-25 14:35:29 +0000
560+++ _integration-tests/tests/failover_zero_size_file_test.go 2015-10-12 09:23:01 +0000
561@@ -25,7 +25,8 @@
562 "path/filepath"
563 "strings"
564
565- . "launchpad.net/snappy/_integration-tests/testutils/common"
566+ "launchpad.net/snappy/_integration-tests/testutils/cli"
567+ "launchpad.net/snappy/_integration-tests/testutils/common"
568 "launchpad.net/snappy/_integration-tests/testutils/partition"
569
570 "gopkg.in/check.v1"
571@@ -45,11 +46,11 @@
572 type zeroSizeSystemd struct{}
573
574 func (zeroSizeKernel) set(c *check.C) {
575- commonSet(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
576+ commonSet(c, common.BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
577 }
578
579 func (zeroSizeKernel) unset(c *check.C) {
580- commonUnset(c, BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
581+ commonUnset(c, common.BaseAltPartitionPath, origBootFilenamePattern, kernelFilename)
582 }
583
584 func (zeroSizeInitrd) set(c *check.C) {
585@@ -71,11 +72,11 @@
586 }
587
588 func (zeroSizeSystemd) set(c *check.C) {
589- commonSet(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
590+ commonSet(c, common.BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
591 }
592
593 func (zeroSizeSystemd) unset(c *check.C) {
594- commonUnset(c, BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
595+ commonUnset(c, common.BaseAltPartitionPath, origSystemdFilenamePattern, systemdFilename)
596 }
597
598 func commonSet(c *check.C, baseOtherPath, origPattern, filename string) {
599@@ -105,17 +106,17 @@
600 func renameFile(c *check.C, basePath, oldFilename, newFilename string, keepOld bool) {
601 // Only need to make writable and revert for BaseAltPartitionPath,
602 // kernel files' boot directory is writable
603- if basePath == BaseAltPartitionPath {
604- MakeWritable(c, basePath)
605- defer MakeReadonly(c, basePath)
606+ if basePath == common.BaseAltPartitionPath {
607+ partition.MakeWritable(c, basePath)
608+ defer partition.MakeReadonly(c, basePath)
609 }
610
611- ExecCommand(c, "sudo", "mv", oldFilename, newFilename)
612+ cli.ExecCommand(c, "sudo", "mv", oldFilename, newFilename)
613
614 if keepOld {
615- ExecCommand(c, "sudo", "touch", oldFilename)
616+ cli.ExecCommand(c, "sudo", "touch", oldFilename)
617 mode := getFileMode(c, newFilename)
618- ExecCommand(c, "sudo", "chmod", fmt.Sprintf("%o", mode), oldFilename)
619+ cli.ExecCommand(c, "sudo", "chmod", fmt.Sprintf("%o", mode), oldFilename)
620 }
621 }
622
623
624=== added file '_integration-tests/tests/hwAssign_test.go'
625--- _integration-tests/tests/hwAssign_test.go 1970-01-01 00:00:00 +0000
626+++ _integration-tests/tests/hwAssign_test.go 2015-10-12 09:23:01 +0000
627@@ -0,0 +1,106 @@
628+// -*- Mode: Go; indent-tabs-mode: t -*-
629+
630+/*
631+ * Copyright (C) 2015 Canonical Ltd
632+ *
633+ * This program is free software: you can redistribute it and/or modify
634+ * it under the terms of the GNU General Public License version 3 as
635+ * published by the Free Software Foundation.
636+ *
637+ * This program is distributed in the hope that it will be useful,
638+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
639+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
640+ * GNU General Public License for more details.
641+ *
642+ * You should have received a copy of the GNU General Public License
643+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
644+ *
645+ */
646+
647+package tests
648+
649+import (
650+ "fmt"
651+ "os"
652+ "os/exec"
653+
654+ "launchpad.net/snappy/_integration-tests/testutils/build"
655+ "launchpad.net/snappy/_integration-tests/testutils/common"
656+
657+ "gopkg.in/check.v1"
658+)
659+
660+const (
661+ snapName = "dev-kmsg"
662+ binName = snapName + ".reader"
663+ installedSnapName = snapName + ".sideload"
664+ hwName = "/dev/kmsg"
665+ hwAssignError = "dd: failed to open ‘" + hwName + "’: Permission denied\n"
666+)
667+
668+var _ = check.Suite(&hwAssignSuite{})
669+
670+type hwAssignSuite struct {
671+ common.SnappySuite
672+ snapPath string
673+}
674+
675+func (s *hwAssignSuite) SetUpTest(c *check.C) {
676+ s.SnappySuite.SetUpTest(c)
677+ var err error
678+ s.snapPath, err = build.LocalSnap(c, snapName)
679+ c.Assert(err, check.IsNil)
680+ common.InstallSnap(c, s.snapPath)
681+}
682+
683+func (s *hwAssignSuite) TearDownTest(c *check.C) {
684+ s.SnappySuite.TearDownTest(c)
685+ os.Remove(s.snapPath)
686+ common.RemoveSnap(c, snapName)
687+}
688+
689+func (s *hwAssignSuite) TestErrorWithoutHwAssign(c *check.C) {
690+ cmd := exec.Command(binName)
691+ output, err := cmd.CombinedOutput()
692+
693+ c.Assert(err, check.NotNil)
694+ c.Assert(string(output), check.Equals, hwAssignError)
695+}
696+
697+func (s *hwAssignSuite) TestSuccessAfterHwAssign(c *check.C) {
698+ assign(c, snapName, hwName)
699+ defer unassign(c, snapName, hwName)
700+
701+ cmd := exec.Command(binName)
702+ output, _ := cmd.CombinedOutput()
703+
704+ c.Assert(string(output), check.Not(check.Equals), hwAssignError)
705+}
706+
707+func (s *hwAssignSuite) TestErrorAfterHwUnAssign(c *check.C) {
708+ assign(c, snapName, hwName)
709+ unassign(c, snapName, hwName)
710+
711+ cmd := exec.Command(binName)
712+ output, err := cmd.CombinedOutput()
713+
714+ c.Assert(err, check.NotNil)
715+ c.Assert(string(output), check.Equals, hwAssignError)
716+}
717+
718+func assign(c *check.C, snap, hw string) {
719+ cmd := exec.Command("sudo", "snappy", "hw-assign", installedSnapName, hwName)
720+ output, err := cmd.CombinedOutput()
721+ c.Assert(err, check.IsNil)
722+ c.Assert(string(output), check.Equals,
723+ fmt.Sprintf("'%s' is now allowed to access '%s'\n", installedSnapName, hwName))
724+}
725+
726+func unassign(c *check.C, snap, hw string) {
727+ cmd := exec.Command("sudo", "snappy", "hw-unassign", installedSnapName, hwName)
728+ output, err := cmd.CombinedOutput()
729+ c.Assert(err, check.IsNil)
730+ c.Assert(string(output), check.Equals,
731+ fmt.Sprintf("'%s' is no longer allowed to access '%s'\n", installedSnapName, hwName))
732+
733+}
734
735=== modified file '_integration-tests/tests/info_test.go'
736--- _integration-tests/tests/info_test.go 2015-07-31 07:55:33 +0000
737+++ _integration-tests/tests/info_test.go 2015-10-12 09:23:01 +0000
738@@ -21,55 +21,60 @@
739
740 import (
741 "fmt"
742-
743- . "launchpad.net/snappy/_integration-tests/testutils/common"
744-
745- check "gopkg.in/check.v1"
746+ "os"
747+
748+ "launchpad.net/snappy/_integration-tests/testutils/build"
749+ "launchpad.net/snappy/_integration-tests/testutils/cli"
750+ "launchpad.net/snappy/_integration-tests/testutils/common"
751+
752+ "gopkg.in/check.v1"
753 )
754
755 var _ = check.Suite(&infoSuite{})
756
757 type infoSuite struct {
758- SnappySuite
759+ common.SnappySuite
760 }
761
762 func (s *infoSuite) TestInfoMustPrintReleaseAndChannel(c *check.C) {
763 // skip test when having a remote testbed (we can't know which the
764 // release and channels are)
765- if Cfg.RemoteTestbed {
766+ if common.Cfg.RemoteTestbed {
767 c.Skip(fmt.Sprintf(
768 "Skipping %s while testing in remote testbed",
769 c.TestName()))
770 }
771
772- infoOutput := ExecCommand(c, "snappy", "info")
773+ infoOutput := cli.ExecCommand(c, "snappy", "info")
774
775 expected := "(?ms)" +
776- fmt.Sprintf("^release: ubuntu-core/%s/%s\n", Cfg.Release, Cfg.Channel) +
777+ fmt.Sprintf("^release: ubuntu-core/%s/%s\n", common.Cfg.Release, common.Cfg.Channel) +
778 ".*"
779
780 c.Assert(infoOutput, check.Matches, expected)
781 }
782
783 func (s *infoSuite) TestInfoMustPrintInstalledApps(c *check.C) {
784- InstallSnap(c, "hello-world")
785- s.AddCleanup(func() {
786- RemoveSnap(c, "hello-world")
787- })
788- infoOutput := ExecCommand(c, "snappy", "info")
789+ snapPath, err := build.LocalSnap(c, build.BasicSnapName)
790+ defer os.Remove(snapPath)
791+ c.Assert(err, check.IsNil)
792+ common.InstallSnap(c, snapPath)
793+ defer common.RemoveSnap(c, build.BasicSnapName)
794+
795+ infoOutput := cli.ExecCommand(c, "snappy", "info")
796
797 expected := "(?ms)" +
798 ".*" +
799- "^apps: .*hello-world.*\n"
800+ "^apps: .*" + build.BasicSnapName + "\\.sideload.*\n"
801 c.Assert(infoOutput, check.Matches, expected)
802 }
803
804 func (s *infoSuite) TestInfoMustPrintInstalledFrameworks(c *check.C) {
805- InstallSnap(c, "hello-dbus-fwk.canonical")
806+ common.InstallSnap(c, "hello-dbus-fwk.canonical")
807 s.AddCleanup(func() {
808- RemoveSnap(c, "hello-dbus-fwk.canonical")
809+ common.RemoveSnap(c, "hello-dbus-fwk.canonical")
810 })
811- infoOutput := ExecCommand(c, "snappy", "info")
812+ infoOutput := cli.ExecCommand(c, "snappy", "info")
813
814 expected := "(?ms)" +
815 ".*" +
816
817=== modified file '_integration-tests/tests/installApp_test.go'
818--- _integration-tests/tests/installApp_test.go 2015-09-24 19:51:26 +0000
819+++ _integration-tests/tests/installApp_test.go 2015-10-12 09:23:01 +0000
820@@ -20,13 +20,12 @@
821 package tests
822
823 import (
824- "net/http"
825 "os/exec"
826
827+ "launchpad.net/snappy/_integration-tests/testutils/cli"
828 "launchpad.net/snappy/_integration-tests/testutils/common"
829- "launchpad.net/snappy/_integration-tests/testutils/wait"
830
831- check "gopkg.in/check.v1"
832+ "gopkg.in/check.v1"
833 )
834
835 var _ = check.Suite(&installAppSuite{})
836@@ -57,7 +56,7 @@
837 common.RemoveSnap(c, "hello-world")
838 })
839
840- echoOutput := common.ExecCommand(c, "hello-world.echo")
841+ echoOutput := cli.ExecCommand(c, "hello-world.echo")
842
843 c.Assert(echoOutput, check.Equals, "Hello World!\n")
844 }
845@@ -83,35 +82,6 @@
846 c.Assert(string(echoOutput), check.Matches, expected)
847 }
848
849-func (s *installAppSuite) TestInfoMustPrintInstalledPackageInformation(c *check.C) {
850- common.InstallSnap(c, "hello-world")
851- s.AddCleanup(func() {
852- common.RemoveSnap(c, "hello-world")
853- })
854-
855- infoOutput := common.ExecCommand(c, "snappy", "info")
856-
857- expected := "(?ms).*^apps: hello-world.canonical\n"
858- c.Assert(infoOutput, check.Matches, expected)
859-}
860-
861-func (s *installAppSuite) TestAppNetworkingServiceMustBeStarted(c *check.C) {
862- baseAppName := "xkcd-webserver"
863- appName := baseAppName + ".canonical"
864- common.InstallSnap(c, appName)
865- s.AddCleanup(func() {
866- common.RemoveSnap(c, appName)
867- })
868-
869- err := wait.ForServerOnPort(c, 80)
870- c.Assert(err, check.IsNil)
871-
872- resp, err := http.Get("http://localhost")
873- c.Assert(err, check.IsNil)
874- c.Check(resp.Status, check.Equals, "200 OK")
875- c.Assert(resp.Proto, check.Equals, "HTTP/1.0")
876-}
877-
878 func (s *installAppSuite) TestInstallUnexistingAppMustPrintError(c *check.C) {
879 cmd := exec.Command("sudo", "snappy", "install", "unexisting.canonical")
880 output, err := cmd.CombinedOutput()
881
882=== renamed file '_integration-tests/tests/helloDbus_test.go' => '_integration-tests/tests/installFramework_test.go'
883--- _integration-tests/tests/helloDbus_test.go 2015-09-11 08:47:39 +0000
884+++ _integration-tests/tests/installFramework_test.go 2015-10-12 09:23:01 +0000
885@@ -25,23 +25,22 @@
886 "gopkg.in/check.v1"
887 )
888
889-var _ = check.Suite(&helloDbusSuite{})
890+var _ = check.Suite(&installFrameworkSuite{})
891
892-type helloDbusSuite struct {
893+type installFrameworkSuite struct {
894 common.SnappySuite
895 }
896
897-func (s *helloDbusSuite) TestCmdOutput(c *check.C) {
898- common.InstallSnap(c, "hello-dbus-fwk.canonical")
899+func (s *installFrameworkSuite) TestInstallFrameworkMustPrintPackageInformation(c *check.C) {
900+ installOutput := common.InstallSnap(c, "hello-dbus-fwk.canonical")
901 defer common.RemoveSnap(c, "hello-dbus-fwk.canonical")
902
903- common.InstallSnap(c, "hello-dbus-app.canonical")
904- defer common.RemoveSnap(c, "hello-dbus-app.canonical")
905-
906- output := common.ExecCommand(c, "hello-dbus-app.client")
907-
908- expected := "PASS\n"
909-
910- c.Assert(output, check.Equals, expected,
911- check.Commentf("Expected output %s not found, %s", expected, output))
912+ expected := "(?ms)" +
913+ "Installing hello-dbus-fwk.canonical\n" +
914+ "Name +Date +Version +Developer \n" +
915+ ".*" +
916+ "^hello-dbus-fwk +.* +.* +canonical \n" +
917+ ".*"
918+
919+ c.Assert(installOutput, check.Matches, expected)
920 }
921
922=== modified file '_integration-tests/tests/list_test.go'
923--- _integration-tests/tests/list_test.go 2015-07-28 04:03:52 +0000
924+++ _integration-tests/tests/list_test.go 2015-10-12 09:23:01 +0000
925@@ -23,16 +23,17 @@
926 "fmt"
927 "os"
928
929- . "launchpad.net/snappy/_integration-tests/testutils/common"
930+ "launchpad.net/snappy/_integration-tests/testutils/cli"
931+ "launchpad.net/snappy/_integration-tests/testutils/common"
932
933 "github.com/mvo5/goconfigparser"
934- check "gopkg.in/check.v1"
935+ "gopkg.in/check.v1"
936 )
937
938 var _ = check.Suite(&listSuite{})
939
940 type listSuite struct {
941- SnappySuite
942+ common.SnappySuite
943 }
944
945 func getVersionFromConfig(c *check.C) string {
946@@ -51,7 +52,7 @@
947 }
948
949 func (s *listSuite) TestListMustPrintCoreVersion(c *check.C) {
950- listOutput := ExecCommand(c, "snappy", "list")
951+ listOutput := cli.ExecCommand(c, "snappy", "list")
952
953 expected := "(?ms)" +
954 "Name +Date +Version +Developer *\n" +
955@@ -62,12 +63,12 @@
956 }
957
958 func (s *listSuite) TestListMustPrintAppVersion(c *check.C) {
959- InstallSnap(c, "hello-world")
960+ common.InstallSnap(c, "hello-world")
961 s.AddCleanup(func() {
962- RemoveSnap(c, "hello-world")
963+ common.RemoveSnap(c, "hello-world")
964 })
965
966- listOutput := ExecCommand(c, "snappy", "list")
967+ listOutput := cli.ExecCommand(c, "snappy", "list")
968 expected := "(?ms)" +
969 "Name +Date +Version +Developer *\n" +
970 ".*" +
971
972=== modified file '_integration-tests/tests/rollback_test.go'
973--- _integration-tests/tests/rollback_test.go 2015-07-30 09:07:31 +0000
974+++ _integration-tests/tests/rollback_test.go 2015-10-12 09:23:01 +0000
975@@ -22,32 +22,38 @@
976 import (
977 "strconv"
978
979- . "launchpad.net/snappy/_integration-tests/testutils/common"
980+ "launchpad.net/snappy/_integration-tests/testutils/cli"
981+ "launchpad.net/snappy/_integration-tests/testutils/common"
982+ "launchpad.net/snappy/_integration-tests/testutils/partition"
983+ "launchpad.net/snappy/_integration-tests/testutils/wait"
984
985- check "gopkg.in/check.v1"
986+ "gopkg.in/check.v1"
987 )
988
989 var _ = check.Suite(&rollbackSuite{})
990
991 type rollbackSuite struct {
992- SnappySuite
993+ common.SnappySuite
994 }
995
996 func (s *rollbackSuite) TestRollbackMustRebootToOtherVersion(c *check.C) {
997- if BeforeReboot() {
998- CallFakeUpdate(c)
999- Reboot(c)
1000- } else if CheckRebootMark(c.TestName()) {
1001- RemoveRebootMark(c)
1002- currentVersion := GetCurrentUbuntuCoreVersion(c)
1003- c.Assert(currentVersion > GetSavedVersion(c), check.Equals, true)
1004- ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core",
1005- strconv.Itoa(GetSavedVersion(c)))
1006- SetSavedVersion(c, currentVersion)
1007- RebootWithMark(c, c.TestName()+"-rollback")
1008- } else if CheckRebootMark(c.TestName() + "-rollback") {
1009- RemoveRebootMark(c)
1010+ if common.BeforeReboot() {
1011+ common.CallFakeUpdate(c)
1012+ common.Reboot(c)
1013+ } else if common.CheckRebootMark(c.TestName()) {
1014+ common.RemoveRebootMark(c)
1015+ // Workaround for bug https://bugs.launchpad.net/snappy/+bug/1498293
1016+ // TODO remove once the bug is fixed. --elopio - 2015-09-30
1017+ wait.ForFunction(c, "regular", partition.Mode)
1018+ currentVersion := common.GetCurrentUbuntuCoreVersion(c)
1019+ c.Assert(currentVersion > common.GetSavedVersion(c), check.Equals, true)
1020+ cli.ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core",
1021+ strconv.Itoa(common.GetSavedVersion(c)))
1022+ common.SetSavedVersion(c, currentVersion)
1023+ common.RebootWithMark(c, c.TestName()+"-rollback")
1024+ } else if common.CheckRebootMark(c.TestName() + "-rollback") {
1025+ common.RemoveRebootMark(c)
1026 c.Assert(
1027- GetCurrentUbuntuCoreVersion(c) < GetSavedVersion(c), check.Equals, true)
1028+ common.GetCurrentUbuntuCoreVersion(c) < common.GetSavedVersion(c), check.Equals, true)
1029 }
1030 }
1031
1032=== modified file '_integration-tests/tests/search_test.go'
1033--- _integration-tests/tests/search_test.go 2015-07-28 04:03:52 +0000
1034+++ _integration-tests/tests/search_test.go 2015-10-12 09:23:01 +0000
1035@@ -20,19 +20,20 @@
1036 package tests
1037
1038 import (
1039- . "launchpad.net/snappy/_integration-tests/testutils/common"
1040+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1041+ "launchpad.net/snappy/_integration-tests/testutils/common"
1042
1043- . "gopkg.in/check.v1"
1044+ "gopkg.in/check.v1"
1045 )
1046
1047-var _ = Suite(&searchSuite{})
1048+var _ = check.Suite(&searchSuite{})
1049
1050 type searchSuite struct {
1051- SnappySuite
1052+ common.SnappySuite
1053 }
1054
1055-func (s *searchSuite) TestSearchFrameworkMustPrintMatch(c *C) {
1056- searchOutput := ExecCommand(c, "snappy", "search", "hello-dbus-fwk")
1057+func (s *searchSuite) TestSearchFrameworkMustPrintMatch(c *check.C) {
1058+ searchOutput := cli.ExecCommand(c, "snappy", "search", "hello-dbus-fwk")
1059
1060 expected := "(?ms)" +
1061 "Name +Version +Summary *\n" +
1062@@ -40,5 +41,5 @@
1063 "^hello-dbus-fwk +.* +hello-dbus-fwk *\n" +
1064 ".*"
1065
1066- c.Assert(searchOutput, Matches, expected)
1067+ c.Assert(searchOutput, check.Matches, expected)
1068 }
1069
1070=== renamed file '_integration-tests/tests/installFramework_test.go' => '_integration-tests/tests/service_test.go'
1071--- _integration-tests/tests/installFramework_test.go 2015-09-03 10:46:21 +0000
1072+++ _integration-tests/tests/service_test.go 2015-10-12 09:23:01 +0000
1073@@ -23,39 +23,40 @@
1074 "fmt"
1075 "regexp"
1076
1077+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1078 "launchpad.net/snappy/_integration-tests/testutils/common"
1079 "launchpad.net/snappy/_integration-tests/testutils/wait"
1080
1081- check "gopkg.in/check.v1"
1082+ "gopkg.in/check.v1"
1083 )
1084
1085-var _ = check.Suite(&installFrameworkSuite{})
1086+var _ = check.Suite(&serviceSuite{})
1087
1088-type installFrameworkSuite struct {
1089+type serviceSuite struct {
1090 common.SnappySuite
1091 }
1092
1093-func (s *installFrameworkSuite) TearDownTest(c *check.C) {
1094+func (s *serviceSuite) TearDownTest(c *check.C) {
1095 if !common.NeedsReboot() && common.CheckRebootMark("") {
1096- common.RemoveSnap(c, "docker")
1097+ common.RemoveSnap(c, "hello-dbus-fwk.canonical")
1098 }
1099 // run cleanup last
1100 s.SnappySuite.TearDownTest(c)
1101 }
1102
1103-func isDockerServiceRunning(c *check.C) bool {
1104- dockerVersion := common.GetCurrentVersion(c, "docker")
1105- dockerService := fmt.Sprintf("docker_docker-daemon_%s.service", dockerVersion)
1106+func isServiceRunning(c *check.C) bool {
1107+ packageVersion := common.GetCurrentVersion(c, "hello-dbus-fwk")
1108+ service := fmt.Sprintf("hello-dbus-fwk_srv_%s.service", packageVersion)
1109
1110- err := wait.ForActiveService(c, dockerService)
1111+ err := wait.ForActiveService(c, service)
1112 c.Assert(err, check.IsNil)
1113
1114- statusOutput := common.ExecCommand(
1115+ statusOutput := cli.ExecCommand(
1116 c, "systemctl", "status",
1117- dockerService)
1118+ service)
1119
1120 expected := "(?ms)" +
1121- ".* docker_docker-daemon_.*\\.service .*\n" +
1122+ ".* hello-dbus-fwk_srv_.*\\.service .*\n" +
1123 ".*Loaded: loaded .*\n" +
1124 ".*Active: active \\(running\\) .*\n" +
1125 ".*"
1126@@ -65,30 +66,29 @@
1127 return matched
1128 }
1129
1130-func (s *installFrameworkSuite) TestInstallFrameworkMustPrintPackageInformation(c *check.C) {
1131- installOutput := common.InstallSnap(c, "docker")
1132-
1133- expected := "(?ms)" +
1134- "Installing docker\n" +
1135- "Name +Date +Version +Developer \n" +
1136- ".*" +
1137- "^docker +.* +.* +canonical \n" +
1138- ".*"
1139-
1140- c.Assert(installOutput, check.Matches, expected)
1141-}
1142-
1143-func (s *installFrameworkSuite) TestInstalledFrameworkServiceMustBeStarted(c *check.C) {
1144- common.InstallSnap(c, "docker")
1145- c.Assert(isDockerServiceRunning(c), check.Equals, true)
1146-}
1147-
1148-func (s *installFrameworkSuite) TestFrameworkServiceMustBeStartedAfterReboot(c *check.C) {
1149- if common.BeforeReboot() {
1150- common.InstallSnap(c, "docker")
1151- common.Reboot(c)
1152- } else if common.AfterReboot(c) {
1153- common.RemoveRebootMark(c)
1154- c.Assert(isDockerServiceRunning(c), check.Equals, true)
1155+func (s *serviceSuite) TestInstalledServiceMustBeStarted(c *check.C) {
1156+ common.InstallSnap(c, "hello-dbus-fwk.canonical")
1157+
1158+ c.Assert(isServiceRunning(c), check.Equals, true)
1159+}
1160+
1161+func (s *serviceSuite) TestServiceMustBeStartedAfterReboot(c *check.C) {
1162+ if common.BeforeReboot() {
1163+ common.InstallSnap(c, "hello-dbus-fwk.canonical")
1164+ common.Reboot(c)
1165+ } else if common.AfterReboot(c) {
1166+ common.RemoveRebootMark(c)
1167+ c.Assert(isServiceRunning(c), check.Equals, true)
1168+ }
1169+}
1170+
1171+func (s *serviceSuite) TestServiceMustBeStartedAfterUpdate(c *check.C) {
1172+ if common.BeforeReboot() {
1173+ common.InstallSnap(c, "hello-dbus-fwk.canonical")
1174+ common.CallFakeUpdate(c)
1175+ common.Reboot(c)
1176+ } else if common.AfterReboot(c) {
1177+ common.RemoveRebootMark(c)
1178+ c.Assert(isServiceRunning(c), check.Equals, true)
1179 }
1180 }
1181
1182=== modified file '_integration-tests/tests/snapd_1_0_packages_test.go'
1183--- _integration-tests/tests/snapd_1_0_packages_test.go 2015-09-28 11:39:16 +0000
1184+++ _integration-tests/tests/snapd_1_0_packages_test.go 2015-10-12 09:23:01 +0000
1185@@ -23,6 +23,7 @@
1186 "os"
1187
1188 "launchpad.net/snappy/_integration-tests/testutils/build"
1189+ "launchpad.net/snappy/_integration-tests/testutils/common"
1190
1191 "gopkg.in/check.v1"
1192 )
1193@@ -70,6 +71,7 @@
1194 func (s *snapd10PackagesTestSuite) TearDownTest(c *check.C) {
1195 s.snapdTestSuite.TearDownTest(c)
1196 os.Remove(s.snapPath)
1197+ common.RemoveSnap(c, build.BasicSnapName)
1198 }
1199
1200 func (s *snapd10PackagesTestSuite) resource() string {
1201
1202=== added file '_integration-tests/tests/snapd_1_0_test.go'
1203--- _integration-tests/tests/snapd_1_0_test.go 1970-01-01 00:00:00 +0000
1204+++ _integration-tests/tests/snapd_1_0_test.go 2015-10-12 09:23:01 +0000
1205@@ -0,0 +1,41 @@
1206+// -*- Mode: Go; indent-tabs-mode: t -*-
1207+
1208+/*
1209+ * Copyright (C) 2015 Canonical Ltd
1210+ *
1211+ * This program is free software: you can redistribute it and/or modify
1212+ * it under the terms of the GNU General Public License version 3 as
1213+ * published by the Free Software Foundation.
1214+ *
1215+ * This program is distributed in the hope that it will be useful,
1216+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1217+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1218+ * GNU General Public License for more details.
1219+ *
1220+ * You should have received a copy of the GNU General Public License
1221+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1222+ *
1223+ */
1224+
1225+package tests
1226+
1227+import "gopkg.in/check.v1"
1228+
1229+var _ = check.Suite(&snapd10TestSuite{})
1230+
1231+type snapd10TestSuite struct {
1232+ snapdTestSuite
1233+}
1234+
1235+func (s *snapd10TestSuite) TestResource(c *check.C) {
1236+ exerciseAPI(c, s)
1237+}
1238+
1239+func (s *snapd10TestSuite) resource() string {
1240+ return baseURL + "/1.0"
1241+}
1242+
1243+func (s *snapd10TestSuite) getInteractions() apiInteractions {
1244+ return []apiInteraction{{
1245+ responsePattern: `(?U){"result":{"api_compat":"0","default_channel":".*","flavor":".*","release":".*"},"status":"OK","status_code":200,"type":"sync"}`}}
1246+}
1247
1248=== modified file '_integration-tests/tests/ubuntuFan_test.go'
1249--- _integration-tests/tests/ubuntuFan_test.go 2015-09-24 21:06:01 +0000
1250+++ _integration-tests/tests/ubuntuFan_test.go 2015-10-12 09:23:01 +0000
1251@@ -25,6 +25,7 @@
1252 "os/exec"
1253 "strings"
1254
1255+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1256 "launchpad.net/snappy/_integration-tests/testutils/common"
1257 "launchpad.net/snappy/_integration-tests/testutils/wait"
1258
1259@@ -75,7 +76,7 @@
1260 }
1261
1262 func (s *fanTestSuite) TestFanCommandCreatesFanBridge(c *check.C) {
1263- output := common.ExecCommand(c, "ifconfig")
1264+ output := cli.ExecCommand(c, "ifconfig")
1265
1266 expectedPattern := fmt.Sprintf("(?msi).*%s.*%s.*", s.fanName(), s.bridgeIP)
1267
1268@@ -89,7 +90,7 @@
1269 s.configureDockerToUseBridge(c)
1270 defer s.removeBridgeFromDockerConf(c)
1271
1272- output := common.ExecCommand(c, "docker", "run", "-t", baseContainer, "ifconfig")
1273+ output := cli.ExecCommand(c, "docker", "run", "-t", baseContainer, "ifconfig")
1274
1275 expectedIP := strings.TrimRight(s.bridgeIP, ".1") + ".2"
1276 expectedPattern := fmt.Sprintf("(?ms).*inet addr:%s.*", expectedIP)
1277@@ -105,12 +106,12 @@
1278 defer s.removeBridgeFromDockerConf(c)
1279
1280 // spin up first container
1281- common.ExecCommand(c, "docker", "run", "-d", "-t", baseContainer)
1282+ cli.ExecCommand(c, "docker", "run", "-d", "-t", baseContainer)
1283 // the first assigned IP in the fan will end with ".2"
1284 firstIPAddr := strings.TrimRight(s.bridgeIP, ".1") + ".2"
1285
1286 // ping from a second container
1287- output := common.ExecCommand(c, "docker", "run", "-t", baseContainer, "ping", firstIPAddr, "-c", "1")
1288+ output := cli.ExecCommand(c, "docker", "run", "-t", baseContainer, "ping", firstIPAddr, "-c", "1")
1289
1290 expectedPattern := "(?ms).*1 packets transmitted, 1 packets received, 0% packet loss.*"
1291
1292@@ -142,14 +143,14 @@
1293 }
1294
1295 func (s *fanTestSuite) fanCtl(c *check.C, cmd string) string {
1296- return common.ExecCommand(c,
1297+ return cli.ExecCommand(c,
1298 "sudo", "fanctl", cmd, firstOverlaySegment+".0.0.0/8", s.subjectIP+"/16")
1299 }
1300
1301 func (s *fanTestSuite) configureDockerToUseBridge(c *check.C) {
1302 cfgFile := dockerCfgFile(c)
1303
1304- common.ExecCommand(c, "sudo", "sed", "-i",
1305+ cli.ExecCommand(c, "sudo", "sed", "-i",
1306 fmt.Sprintf(`s/DOCKER_OPTIONS=\"\"/DOCKER_OPTIONS=\"%s\"/`, s.dockerOptions()),
1307 cfgFile)
1308
1309@@ -159,7 +160,7 @@
1310 func (s *fanTestSuite) removeBridgeFromDockerConf(c *check.C) {
1311 cfgFile := dockerCfgFile(c)
1312
1313- common.ExecCommand(c, "sudo", "sed", "-i",
1314+ cli.ExecCommand(c, "sudo", "sed", "-i",
1315 `s/DOCKER_OPTIONS=\".*\"/DOCKER_OPTIONS=\"\"/`,
1316 cfgFile)
1317
1318@@ -175,7 +176,7 @@
1319 dockerVersion := common.GetCurrentVersion(c, "docker")
1320 dockerService := fmt.Sprintf("docker_docker-daemon_%s.service", dockerVersion)
1321
1322- common.ExecCommand(c, "sudo", "systemctl", "restart", dockerService)
1323+ cli.ExecCommand(c, "sudo", "systemctl", "restart", dockerService)
1324
1325 // we need to wait until the socket is ready, an active systemctl status is not enough
1326 err := wait.ForCommand(c, `(?ms).*docker\.sock\s.*`, "ls", "/run")
1327@@ -199,7 +200,7 @@
1328 err := wait.ForActiveService(c, dockerService)
1329 c.Assert(err, check.IsNil)
1330
1331- common.ExecCommand(c, "docker", "pull", baseContainer)
1332+ cli.ExecCommand(c, "docker", "pull", baseContainer)
1333 }
1334
1335 func tearDownDocker(c *check.C) {
1336
1337=== modified file '_integration-tests/tests/update_test.go'
1338--- _integration-tests/tests/update_test.go 2015-09-10 15:55:23 +0000
1339+++ _integration-tests/tests/update_test.go 2015-10-12 09:23:01 +0000
1340@@ -23,7 +23,7 @@
1341 "io/ioutil"
1342 "path"
1343
1344- . "launchpad.net/snappy/_integration-tests/testutils/common"
1345+ "launchpad.net/snappy/_integration-tests/testutils/common"
1346 "launchpad.net/snappy/_integration-tests/testutils/partition"
1347
1348 "gopkg.in/check.v1"
1349@@ -32,7 +32,7 @@
1350 var _ = check.Suite(&updateSuite{})
1351
1352 type updateSuite struct {
1353- SnappySuite
1354+ common.SnappySuite
1355 }
1356
1357 func (s *updateSuite) assertBootDirContents(c *check.C) {
1358@@ -62,17 +62,17 @@
1359 // modified to fake an update. If there is a version available, the image will
1360 // be up-to-date after running this test.
1361 func (s *updateSuite) TestUpdateToSameReleaseAndChannel(c *check.C) {
1362- if BeforeReboot() {
1363- updateOutput := CallFakeUpdate(c)
1364+ if common.BeforeReboot() {
1365+ updateOutput := common.CallFakeUpdate(c)
1366 expected := "(?ms)" +
1367 ".*" +
1368 "^Reboot to use .*ubuntu-core.\n"
1369 c.Assert(updateOutput, check.Matches, expected)
1370 s.assertBootDirContents(c)
1371- Reboot(c)
1372- } else if AfterReboot(c) {
1373- RemoveRebootMark(c)
1374- c.Assert(GetCurrentUbuntuCoreVersion(c) > GetSavedVersion(c),
1375+ common.Reboot(c)
1376+ } else if common.AfterReboot(c) {
1377+ common.RemoveRebootMark(c)
1378+ c.Assert(common.GetCurrentUbuntuCoreVersion(c) > common.GetSavedVersion(c),
1379 check.Equals, true)
1380 }
1381 }
1382
1383=== modified file '_integration-tests/tests/writablePaths_test.go'
1384--- _integration-tests/tests/writablePaths_test.go 2015-07-28 04:03:52 +0000
1385+++ _integration-tests/tests/writablePaths_test.go 2015-10-12 09:23:01 +0000
1386@@ -27,9 +27,9 @@
1387 "path/filepath"
1388 "strings"
1389
1390- . "launchpad.net/snappy/_integration-tests/testutils/common"
1391+ "launchpad.net/snappy/_integration-tests/testutils/common"
1392
1393- check "gopkg.in/check.v1"
1394+ "gopkg.in/check.v1"
1395 )
1396
1397 const writablePathsListFile = "/etc/system-image/writable-paths"
1398@@ -37,7 +37,7 @@
1399 var _ = check.Suite(&writablePathsSuite{})
1400
1401 type writablePathsSuite struct {
1402- SnappySuite
1403+ common.SnappySuite
1404 }
1405
1406 var IsWritable check.Checker = &isWritable{}
1407
1408=== modified file '_integration-tests/testutils/autopkgtest/autopkgtest_test.go'
1409--- _integration-tests/testutils/autopkgtest/autopkgtest_test.go 2015-08-31 10:09:21 +0000
1410+++ _integration-tests/testutils/autopkgtest/autopkgtest_test.go 2015-10-12 09:23:01 +0000
1411@@ -192,7 +192,7 @@
1412 }
1413
1414 func adtrunLocalCmd(controlFile, sourceCodePath, outputDir, imgPath string) string {
1415- options := fmt.Sprintf("--- ssh -s /usr/share/autopkgtest/ssh-setup/snappy -- -i %s", imgPath)
1416+ options := fmt.Sprintf("--- ssh -s /usr/share/autopkgtest/ssh-setup/snappy -- -b -i %s", imgPath)
1417 return adtrunCommonCmd(controlFile, sourceCodePath, outputDir, options)
1418 }
1419
1420
1421=== modified file '_integration-tests/testutils/autopkgtest/ssh.go'
1422--- _integration-tests/testutils/autopkgtest/ssh.go 2015-08-31 10:09:21 +0000
1423+++ _integration-tests/testutils/autopkgtest/ssh.go 2015-10-12 09:23:01 +0000
1424@@ -33,7 +33,7 @@
1425
1426 func kvmSSHOptions(imagePath string) string {
1427 return fmt.Sprint(commonSSHOptions,
1428- "-s /usr/share/autopkgtest/ssh-setup/snappy -- -i ", imagePath)
1429+ "-s /usr/share/autopkgtest/ssh-setup/snappy -- -b -i ", imagePath)
1430 }
1431
1432 func remoteTestbedSSHOptions(testbedIP string, testbedPort int) string {
1433
1434=== modified file '_integration-tests/testutils/build/snap.go'
1435--- _integration-tests/testutils/build/snap.go 2015-09-28 10:35:06 +0000
1436+++ _integration-tests/testutils/build/snap.go 2015-10-12 09:23:01 +0000
1437@@ -24,7 +24,7 @@
1438 "path/filepath"
1439
1440 "gopkg.in/check.v1"
1441- "launchpad.net/snappy/_integration-tests/testutils/common"
1442+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1443 )
1444
1445 const (
1446@@ -42,11 +42,11 @@
1447
1448 var (
1449 // dependency aliasing
1450- commonExecCommand = common.ExecCommand
1451+ cliExecCommand = cli.ExecCommand
1452 )
1453
1454 func buildSnap(c *check.C, snapPath string) string {
1455- return commonExecCommand(c, "snappy", "build", snapPath, "-o", snapPath)
1456+ return cliExecCommand(c, "snappy", "build", snapPath, "-o", snapPath)
1457 }
1458
1459 // LocalSnap issues the command to build a snap and returns the path of the generated file
1460
1461=== modified file '_integration-tests/testutils/build/snap_test.go'
1462--- _integration-tests/testutils/build/snap_test.go 2015-09-28 10:44:44 +0000
1463+++ _integration-tests/testutils/build/snap_test.go 2015-10-12 09:23:01 +0000
1464@@ -37,13 +37,13 @@
1465 var _ = check.Suite(&snapBuildTestSuite{})
1466
1467 func (s *snapBuildTestSuite) SetUpSuite(c *check.C) {
1468- s.backExecCommand = commonExecCommand
1469- commonExecCommand = s.fakeExecCommand
1470+ s.backExecCommand = cliExecCommand
1471+ cliExecCommand = s.fakeExecCommand
1472 s.defaultSnapName = "mySnapName"
1473 }
1474
1475 func (s *snapBuildTestSuite) TearDownSuite(c *check.C) {
1476- commonExecCommand = s.backExecCommand
1477+ cliExecCommand = s.backExecCommand
1478 }
1479
1480 func (s *snapBuildTestSuite) SetUpTest(c *check.C) {
1481
1482=== added directory '_integration-tests/testutils/cli'
1483=== added file '_integration-tests/testutils/cli/cli.go'
1484--- _integration-tests/testutils/cli/cli.go 1970-01-01 00:00:00 +0000
1485+++ _integration-tests/testutils/cli/cli.go 2015-10-12 09:23:01 +0000
1486@@ -0,0 +1,64 @@
1487+// -*- Mode: Go; indent-tabs-mode: t -*-
1488+
1489+/*
1490+ * Copyright (C) 2015 Canonical Ltd
1491+ *
1492+ * This program is free software: you can redistribute it and/or modify
1493+ * it under the terms of the GNU General Public License version 3 as
1494+ * published by the Free Software Foundation.
1495+ *
1496+ * This program is distributed in the hope that it will be useful,
1497+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1498+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1499+ * GNU General Public License for more details.
1500+ *
1501+ * You should have received a copy of the GNU General Public License
1502+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1503+ *
1504+ */
1505+
1506+package cli
1507+
1508+import (
1509+ "fmt"
1510+ "os"
1511+ "os/exec"
1512+ "strings"
1513+
1514+ "gopkg.in/check.v1"
1515+)
1516+
1517+var execCommand = exec.Command
1518+
1519+// ExecCommand executes a shell command and returns a string with the output
1520+// of the command. In case of error, it will fail the test.
1521+func ExecCommand(c *check.C, cmds ...string) string {
1522+ output, err := ExecCommandErr(cmds...)
1523+ c.Assert(err, check.IsNil, check.Commentf("Error: %v", output))
1524+ return output
1525+}
1526+
1527+// ExecCommandToFile executes a shell command and saves the output of the
1528+// command to a file. In case of error, it will fail the test.
1529+func ExecCommandToFile(c *check.C, filename string, cmds ...string) {
1530+ cmd := execCommand(cmds[0], cmds[1:]...)
1531+ outfile, err := os.Create(filename)
1532+ c.Assert(err, check.IsNil, check.Commentf("Error creating output file %s", filename))
1533+
1534+ defer outfile.Close()
1535+ cmd.Stdout = outfile
1536+
1537+ err = cmd.Run()
1538+ c.Assert(err, check.IsNil, check.Commentf("Error executing command '%v': %v", cmds, err))
1539+}
1540+
1541+// ExecCommandErr executes a shell command and returns a string with the output
1542+// of the command and eventually the obtained error
1543+func ExecCommandErr(cmds ...string) (output string, err error) {
1544+ fmt.Println(strings.Join(cmds, " "))
1545+ cmd := execCommand(cmds[0], cmds[1:]...)
1546+ outputByte, err := cmd.CombinedOutput()
1547+ output = string(outputByte)
1548+ fmt.Print(output)
1549+ return
1550+}
1551
1552=== added file '_integration-tests/testutils/cli/cli_test.go'
1553--- _integration-tests/testutils/cli/cli_test.go 1970-01-01 00:00:00 +0000
1554+++ _integration-tests/testutils/cli/cli_test.go 2015-10-12 09:23:01 +0000
1555@@ -0,0 +1,113 @@
1556+// -*- Mode: Go; indent-tabs-mode: t -*-
1557+
1558+/*
1559+ * Copyright (C) 2015 Canonical Ltd
1560+ *
1561+ * This program is free software: you can redistribute it and/or modify
1562+ * it under the terms of the GNU General Public License version 3 as
1563+ * published by the Free Software Foundation.
1564+ *
1565+ * This program is distributed in the hope that it will be useful,
1566+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1567+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1568+ * GNU General Public License for more details.
1569+ *
1570+ * You should have received a copy of the GNU General Public License
1571+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1572+ *
1573+ */
1574+
1575+package cli
1576+
1577+import (
1578+ "fmt"
1579+ "io/ioutil"
1580+ "os"
1581+ "os/exec"
1582+ "testing"
1583+
1584+ "gopkg.in/check.v1"
1585+)
1586+
1587+const execOutput = "myoutput"
1588+
1589+// Hook up check.v1 into the "go test" runner
1590+func Test(t *testing.T) { check.TestingT(t) }
1591+
1592+type cliTestSuite struct {
1593+ backExecCommand func(string, ...string) *exec.Cmd
1594+ helperProcess string
1595+}
1596+
1597+var _ = check.Suite(&cliTestSuite{})
1598+
1599+func (s *cliTestSuite) SetUpSuite(c *check.C) {
1600+ s.backExecCommand = execCommand
1601+ execCommand = s.fakeExecCommand
1602+}
1603+
1604+func (s *cliTestSuite) TearDownSuite(c *check.C) {
1605+ execCommand = s.backExecCommand
1606+}
1607+
1608+func (s *cliTestSuite) SetUpTest(c *check.C) {
1609+ s.helperProcess = "TestHelperProcess"
1610+}
1611+
1612+func (s *cliTestSuite) fakeExecCommand(command string, args ...string) *exec.Cmd {
1613+ cs := []string{"-check.f=cliTestSuite." + s.helperProcess, "--", command}
1614+ cs = append(cs, args...)
1615+ cmd := exec.Command(os.Args[0], cs...)
1616+ cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
1617+ return cmd
1618+}
1619+
1620+func (s *cliTestSuite) TestHelperProcess(c *check.C) {
1621+ baseHelperProcess(0)
1622+}
1623+
1624+func (s *cliTestSuite) TestHelperProcessErr(c *check.C) {
1625+ baseHelperProcess(1)
1626+}
1627+
1628+func baseHelperProcess(exitValue int) {
1629+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
1630+ return
1631+ }
1632+ fmt.Fprintf(os.Stdout, execOutput)
1633+ os.Exit(exitValue)
1634+}
1635+
1636+func (s *cliTestSuite) TestExecCommand(c *check.C) {
1637+ actualOutput := ExecCommand(c, "mycmd")
1638+
1639+ c.Assert(actualOutput, check.Equals, execOutput)
1640+}
1641+
1642+func (s *cliTestSuite) TestExecCommandToFile(c *check.C) {
1643+ outputFile, err := ioutil.TempFile("", "snappy-exec")
1644+ c.Assert(err, check.IsNil)
1645+ outputFile.Close()
1646+ defer os.Remove(outputFile.Name())
1647+
1648+ ExecCommandToFile(c, outputFile.Name(), "mycmd")
1649+
1650+ actualFileContents, err := ioutil.ReadFile(outputFile.Name())
1651+ c.Assert(err, check.IsNil)
1652+ c.Assert(string(actualFileContents), check.Equals, execOutput)
1653+}
1654+
1655+func (s *cliTestSuite) TestExecCommandErr(c *check.C) {
1656+ actualOutput, err := ExecCommandErr("mycmd")
1657+
1658+ c.Assert(actualOutput, check.Equals, execOutput)
1659+ c.Assert(err, check.IsNil)
1660+}
1661+
1662+func (s *cliTestSuite) TestExecCommandErrWithError(c *check.C) {
1663+ s.helperProcess = "TestHelperProcessErr"
1664+ actualOutput, err := ExecCommandErr("mycmd")
1665+
1666+ c.Assert(actualOutput, check.Equals, execOutput)
1667+ c.Assert(err, check.NotNil)
1668+}
1669
1670=== modified file '_integration-tests/testutils/common/common.go'
1671--- _integration-tests/testutils/common/common.go 2015-09-28 14:42:52 +0000
1672+++ _integration-tests/testutils/common/common.go 2015-10-12 09:23:01 +0000
1673@@ -23,7 +23,6 @@
1674 "fmt"
1675 "io/ioutil"
1676 "os"
1677- "os/exec"
1678 "path/filepath"
1679 "regexp"
1680 "strconv"
1681@@ -31,7 +30,9 @@
1682
1683 "gopkg.in/check.v1"
1684
1685+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1686 "launchpad.net/snappy/_integration-tests/testutils/config"
1687+ "launchpad.net/snappy/_integration-tests/testutils/partition"
1688 )
1689
1690 const (
1691@@ -54,8 +55,8 @@
1692 // SetUpSuite disables the snappy autopilot. It will run before all the
1693 // integration suites.
1694 func (s *SnappySuite) SetUpSuite(c *check.C) {
1695- ExecCommand(c, "sudo", "systemctl", "stop", "snappy-autopilot.timer")
1696- ExecCommand(c, "sudo", "systemctl", "disable", "snappy-autopilot.timer")
1697+ cli.ExecCommand(c, "sudo", "systemctl", "stop", "snappy-autopilot.timer")
1698+ cli.ExecCommand(c, "sudo", "systemctl", "disable", "snappy-autopilot.timer")
1699 var err error
1700 Cfg, err = config.ReadConfig(
1701 "_integration-tests/data/output/testconfig.json")
1702@@ -66,7 +67,7 @@
1703 switchSystemImageConf(c, Cfg.TargetRelease, Cfg.TargetChannel, "0")
1704 // Always use the installed snappy because we are updating from an old
1705 // image, so we should not use the snappy from the branch.
1706- output := ExecCommand(c, "sudo", "/usr/bin/snappy", "update")
1707+ output := cli.ExecCommand(c, "sudo", "/usr/bin/snappy", "update")
1708 if output != "" {
1709 RebootWithMark(c, "setupsuite-update")
1710 }
1711@@ -77,7 +78,7 @@
1712 Cfg.Update = false
1713 Cfg.Write()
1714 if Cfg.Rollback {
1715- ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core")
1716+ cli.ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core")
1717 RebootWithMark(c, "setupsuite-rollback")
1718 }
1719 } else if CheckRebootMark("setupsuite-rollback") {
1720@@ -127,11 +128,11 @@
1721 m[channelCfgOtherBackupFile()] = BaseAltPartitionPath
1722 for backup, target := range m {
1723 if _, err := os.Stat(backup); err == nil {
1724- MakeWritable(c, target)
1725- defer MakeReadonly(c, target)
1726+ partition.MakeWritable(c, target)
1727+ defer partition.MakeReadonly(c, target)
1728 original := filepath.Join(target, channelCfgFile)
1729 c.Logf("Restoring %s...", original)
1730- ExecCommand(c, "sudo", "mv", backup, original)
1731+ cli.ExecCommand(c, "sudo", "mv", backup, original)
1732 }
1733 }
1734 }
1735@@ -153,8 +154,8 @@
1736 for _, target := range targets {
1737 file := filepath.Join(target, channelCfgFile)
1738 if _, err := os.Stat(file); err == nil {
1739- MakeWritable(c, target)
1740- defer MakeReadonly(c, target)
1741+ partition.MakeWritable(c, target)
1742+ defer partition.MakeReadonly(c, target)
1743 replaceSystemImageValues(c, file, release, channel, version)
1744 }
1745 }
1746@@ -169,12 +170,12 @@
1747 }
1748 for value, regex := range replaceRegex {
1749 if value != "" {
1750- ExecCommand(c,
1751+ cli.ExecCommand(c,
1752 "sudo", "sed", "-i", fmt.Sprintf(regex, value), file)
1753 }
1754 }
1755 // Leave the new file in the test log.
1756- ExecCommand(c, "cat", file)
1757+ cli.ExecCommand(c, "cat", file)
1758 }
1759
1760 func channelCfgBackupFile() string {
1761@@ -185,35 +186,9 @@
1762 return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini.other")
1763 }
1764
1765-// ExecCommand executes a shell command and returns a string with the output
1766-// of the command. In case of error, it will fail the test.
1767-func ExecCommand(c *check.C, cmds ...string) string {
1768- fmt.Println(strings.Join(cmds, " "))
1769- cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
1770- output, err := cmd.CombinedOutput()
1771- stringOutput := string(output)
1772- fmt.Print(stringOutput)
1773- c.Assert(err, check.IsNil, check.Commentf("Error: %v", stringOutput))
1774- return stringOutput
1775-}
1776-
1777-// ExecCommandToFile executes a shell command and saves the output of the
1778-// command to a file. In case of error, it will fail the test.
1779-func ExecCommandToFile(c *check.C, filename string, cmds ...string) {
1780- cmd := exec.Command(cmds[0], cmds[1:len(cmds)]...)
1781- outfile, err := os.Create(filename)
1782- c.Assert(err, check.IsNil, check.Commentf("Error creating output file %s", filename))
1783-
1784- defer outfile.Close()
1785- cmd.Stdout = outfile
1786-
1787- err = cmd.Run()
1788- c.Assert(err, check.IsNil, check.Commentf("Error executing command '%v': %v", cmds, err))
1789-}
1790-
1791 // GetCurrentVersion returns the version of the installed and active package.
1792 func GetCurrentVersion(c *check.C, packageName string) string {
1793- output := ExecCommand(c, "snappy", "list")
1794+ output := cli.ExecCommand(c, "snappy", "list")
1795 pattern := "(?mU)^" + packageName + " +(.*)$"
1796 re := regexp.MustCompile(pattern)
1797 match := re.FindStringSubmatch(string(output))
1798@@ -237,7 +212,7 @@
1799 func CallFakeUpdate(c *check.C) string {
1800 c.Log("Preparing fake and calling update.")
1801 fakeAvailableUpdate(c)
1802- return ExecCommand(c, "sudo", "snappy", "update")
1803+ return cli.ExecCommand(c, "sudo", "snappy", "update")
1804 }
1805
1806 func fakeAvailableUpdate(c *check.C) {
1807@@ -254,25 +229,15 @@
1808 for target, backup := range m {
1809 file := filepath.Join(target, channelCfgFile)
1810 if _, err := os.Stat(file); err == nil {
1811- MakeWritable(c, target)
1812- defer MakeReadonly(c, target)
1813+ partition.MakeWritable(c, target)
1814+ defer partition.MakeReadonly(c, target)
1815 // Back up the file. It will be restored during the test tear down.
1816- ExecCommand(c, "cp", file, backup)
1817+ cli.ExecCommand(c, "cp", file, backup)
1818 replaceSystemImageValues(c, file, "", "", strconv.Itoa(newVersion))
1819 }
1820 }
1821 }
1822
1823-// MakeWritable remounts a path with read and write permissions.
1824-func MakeWritable(c *check.C, path string) {
1825- ExecCommand(c, "sudo", "mount", "-o", "remount,rw", path)
1826-}
1827-
1828-// MakeReadonly remounts a path with only read permissions.
1829-func MakeReadonly(c *check.C, path string) {
1830- ExecCommand(c, "sudo", "mount", "-o", "remount,ro", path)
1831-}
1832-
1833 // Reboot requests a reboot using the test name as the mark.
1834 func Reboot(c *check.C) {
1835 RebootWithMark(c, c.TestName())
1836@@ -346,10 +311,10 @@
1837
1838 // InstallSnap executes the required command to install the specified snap
1839 func InstallSnap(c *check.C, packageName string) string {
1840- return ExecCommand(c, "sudo", "snappy", "install", packageName, "--allow-unauthenticated")
1841+ return cli.ExecCommand(c, "sudo", "snappy", "install", packageName, "--allow-unauthenticated")
1842 }
1843
1844 // RemoveSnap executes the required command to remove the specified snap
1845 func RemoveSnap(c *check.C, packageName string) string {
1846- return ExecCommand(c, "sudo", "snappy", "remove", packageName)
1847+ return cli.ExecCommand(c, "sudo", "snappy", "remove", packageName)
1848 }
1849
1850=== modified file '_integration-tests/testutils/common/info.go'
1851--- _integration-tests/testutils/common/info.go 2015-09-24 21:06:01 +0000
1852+++ _integration-tests/testutils/common/info.go 2015-10-12 09:23:01 +0000
1853@@ -24,10 +24,11 @@
1854 "strings"
1855
1856 "gopkg.in/check.v1"
1857+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1858 )
1859
1860 // dependency aliasing
1861-var execCommand = ExecCommand
1862+var execCommand = cli.ExecCommand
1863
1864 // Release returns the release of the current snappy image
1865 func Release(c *check.C) string {
1866
1867=== modified file '_integration-tests/testutils/partition/bootloader.go'
1868--- _integration-tests/testutils/partition/bootloader.go 2015-09-13 02:52:52 +0000
1869+++ _integration-tests/testutils/partition/bootloader.go 2015-10-12 09:23:01 +0000
1870@@ -69,7 +69,7 @@
1871 // config file. For uboot systems the boot config file does not change, so that
1872 // we take the other partition in that case
1873 func NextBootPartition() (partition string, err error) {
1874- m, err := mode()
1875+ m, err := Mode()
1876 if err != nil {
1877 return
1878 }
1879@@ -96,7 +96,8 @@
1880 return
1881 }
1882
1883-func mode() (mode string, err error) {
1884+// Mode returns the current bootloader mode, regular or try.
1885+func Mode() (mode string, err error) {
1886 return confValue("snappy_mode")
1887 }
1888
1889@@ -144,7 +145,7 @@
1890 if err != nil {
1891 return
1892 }
1893- m, err := mode()
1894+ m, err := Mode()
1895 if err != nil {
1896 return
1897 }
1898
1899=== modified file '_integration-tests/testutils/partition/bootloader_test.go'
1900--- _integration-tests/testutils/partition/bootloader_test.go 2015-09-13 02:55:08 +0000
1901+++ _integration-tests/testutils/partition/bootloader_test.go 2015-10-12 09:23:01 +0000
1902@@ -244,7 +244,7 @@
1903 return "test-system", nil
1904 }
1905
1906- mode, err := mode()
1907+ mode, err := Mode()
1908
1909 c.Assert(err, check.IsNil, check.Commentf("Unexpected error %v", err))
1910 c.Assert(mode, check.Equals, "test_mode", check.Commentf("Wrong mode"))
1911
1912=== added file '_integration-tests/testutils/partition/partition.go'
1913--- _integration-tests/testutils/partition/partition.go 1970-01-01 00:00:00 +0000
1914+++ _integration-tests/testutils/partition/partition.go 2015-10-12 09:23:01 +0000
1915@@ -0,0 +1,89 @@
1916+// -*- Mode: Go; indent-tabs-mode: t -*-
1917+
1918+/*
1919+ * Copyright (C) 2015 Canonical Ltd
1920+ *
1921+ * This program is free software: you can redistribute it and/or modify
1922+ * it under the terms of the GNU General Public License version 3 as
1923+ * published by the Free Software Foundation.
1924+ *
1925+ * This program is distributed in the hope that it will be useful,
1926+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1927+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1928+ * GNU General Public License for more details.
1929+ *
1930+ * You should have received a copy of the GNU General Public License
1931+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1932+ *
1933+ */
1934+
1935+package partition
1936+
1937+import (
1938+ "bufio"
1939+ "fmt"
1940+ "regexp"
1941+ "strings"
1942+
1943+ "gopkg.in/check.v1"
1944+
1945+ "launchpad.net/snappy/_integration-tests/testutils/cli"
1946+ "launchpad.net/snappy/_integration-tests/testutils/wait"
1947+)
1948+
1949+const (
1950+ // beginning of the string returned by lsof -V when the partition
1951+ // is not being used, in this case the exit status of lsof is 1
1952+ lsofNotUsed = "lsof: no file system use located"
1953+ // custom pattern to be returned by the check functions
1954+ lsofNotBeingWritten = "volume-is-idle"
1955+)
1956+
1957+var (
1958+ execCommand = cli.ExecCommandErr
1959+ waitForFunction = wait.ForFunction
1960+)
1961+
1962+// MakeWritable remounts a path with read and write permissions.
1963+func MakeWritable(c *check.C, path string) (err error) {
1964+ return commonMount(c, path, "remount,rw")
1965+}
1966+
1967+// MakeReadonly remounts a path with only read permissions.
1968+func MakeReadonly(c *check.C, path string) (err error) {
1969+ return commonMount(c, path, "remount,ro")
1970+}
1971+
1972+func commonMount(c *check.C, path, mountOption string) (err error) {
1973+ err = waitForFunction(c, lsofNotBeingWritten, checkPathBusyFunc(path))
1974+
1975+ if err != nil {
1976+ return
1977+ }
1978+
1979+ execCommand("sudo", "mount", "-o", mountOption, path)
1980+ return
1981+}
1982+
1983+func checkPathBusyFunc(path string) func() (string, error) {
1984+ return func() (result string, err error) {
1985+ // lsof exit status is 1 for unused partitions
1986+ var info string
1987+ if info, err = execCommand("lsof", "-V", path); err != nil {
1988+ // check if the output matches lsofNotUsed
1989+ if !strings.HasPrefix(info, lsofNotUsed) {
1990+ return info, err
1991+ }
1992+ }
1993+ reader := strings.NewReader(info)
1994+ scanner := bufio.NewScanner(reader)
1995+ for scanner.Scan() {
1996+ fields := strings.Fields(scanner.Text())
1997+ if match, _ := regexp.MatchString("^[0-9]+w$", fields[3]); match {
1998+ fmt.Printf("match! %s", fields[3])
1999+ return fields[3], nil
2000+ }
2001+ }
2002+ return lsofNotBeingWritten, nil
2003+ }
2004+}
2005
2006=== added file '_integration-tests/testutils/partition/partition_test.go'
2007--- _integration-tests/testutils/partition/partition_test.go 1970-01-01 00:00:00 +0000
2008+++ _integration-tests/testutils/partition/partition_test.go 2015-10-12 09:23:01 +0000
2009@@ -0,0 +1,170 @@
2010+// -*- Mode: Go; indent-tabs-mode: t -*-
2011+
2012+/*
2013+ * Copyright (C) 2014-2015 Canonical Ltd
2014+ *
2015+ * This program is free software: you can redistribute it and/or modify
2016+ * it under the terms of the GNU General Public License version 3 as
2017+ * published by the Free Software Foundation.
2018+ *
2019+ * This program is distributed in the hope that it will be useful,
2020+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2021+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2022+ * GNU General Public License for more details.
2023+ *
2024+ * You should have received a copy of the GNU General Public License
2025+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2026+ *
2027+ */
2028+
2029+package partition
2030+
2031+import (
2032+ "fmt"
2033+ "strings"
2034+
2035+ "gopkg.in/check.v1"
2036+)
2037+
2038+const (
2039+ path = "mypath"
2040+ writableCmd = "sudo mount -o remount,rw " + path
2041+ readonlyCmd = "sudo mount -o remount,ro " + path
2042+ waitCmd = lsofNotBeingWritten
2043+)
2044+
2045+type partitionTestSuite struct {
2046+ execCalls map[string]int
2047+ waitCalls map[string]int
2048+ execOutput string
2049+ execError bool
2050+ backExecCommand func(...string) (string, error)
2051+ backWaitForFunction func(*check.C, string, func() (string, error)) error
2052+ waitError bool
2053+}
2054+
2055+var _ = check.Suite(&partitionTestSuite{})
2056+
2057+func (s *partitionTestSuite) SetUpSuite(c *check.C) {
2058+ s.backExecCommand = execCommand
2059+ s.backWaitForFunction = waitForFunction
2060+ execCommand = s.fakeExecCommand
2061+ waitForFunction = s.fakeWaitForFunction
2062+}
2063+
2064+func (s *partitionTestSuite) TearDownSuite(c *check.C) {
2065+ execCommand = s.backExecCommand
2066+ waitForFunction = s.backWaitForFunction
2067+}
2068+
2069+func (s *partitionTestSuite) SetUpTest(c *check.C) {
2070+ s.execCalls = make(map[string]int)
2071+ s.waitCalls = make(map[string]int)
2072+ s.waitError = false
2073+ s.execOutput = ""
2074+ s.execError = false
2075+}
2076+
2077+func (s *partitionTestSuite) fakeExecCommand(args ...string) (output string, err error) {
2078+ s.execCalls[strings.Join(args, " ")]++
2079+
2080+ if s.execError {
2081+ err = fmt.Errorf("Exec error!")
2082+ }
2083+
2084+ return s.execOutput, err
2085+}
2086+
2087+func (s *partitionTestSuite) fakeWaitForFunction(c *check.C, pattern string, f func() (string, error)) (err error) {
2088+ s.waitCalls[pattern]++
2089+
2090+ if s.waitError {
2091+ err = fmt.Errorf("Wait error!")
2092+ }
2093+
2094+ return
2095+}
2096+
2097+func (s *partitionTestSuite) TestMakeWritableCallsExecCommand(c *check.C) {
2098+ err := MakeWritable(c, path)
2099+
2100+ c.Assert(err, check.IsNil)
2101+ c.Assert(s.execCalls[writableCmd], check.Equals, 1)
2102+}
2103+
2104+func (s *partitionTestSuite) TestMakeWritableWaitsForIdlePartition(c *check.C) {
2105+ err := MakeWritable(c, path)
2106+
2107+ c.Assert(err, check.IsNil)
2108+ c.Assert(s.waitCalls[waitCmd], check.Equals, 1)
2109+}
2110+
2111+func (s *partitionTestSuite) TestMakeWritableReturnsWaitError(c *check.C) {
2112+ s.waitError = true
2113+ err := MakeWritable(c, path)
2114+
2115+ c.Assert(err, check.NotNil)
2116+ c.Assert(s.waitCalls[waitCmd], check.Equals, 1)
2117+ c.Assert(s.execCalls[writableCmd], check.Equals, 0)
2118+}
2119+
2120+func (s *partitionTestSuite) TestMakeReadOnlyCallsExecCommand(c *check.C) {
2121+ err := MakeReadonly(c, path)
2122+
2123+ c.Assert(err, check.IsNil)
2124+ c.Assert(s.execCalls[readonlyCmd], check.Equals, 1)
2125+}
2126+
2127+func (s *partitionTestSuite) TestMakeReadonlyWaitsForIdlePartition(c *check.C) {
2128+ err := MakeReadonly(c, path)
2129+
2130+ c.Assert(err, check.IsNil)
2131+ c.Assert(s.waitCalls[waitCmd], check.Equals, 1)
2132+}
2133+
2134+func (s *partitionTestSuite) TestMakeReadonlyReturnsWaitError(c *check.C) {
2135+ s.waitError = true
2136+ err := MakeReadonly(c, path)
2137+
2138+ c.Assert(err, check.NotNil)
2139+ c.Assert(s.waitCalls[waitCmd], check.Equals, 1)
2140+ c.Assert(s.execCalls[readonlyCmd], check.Equals, 0)
2141+}
2142+
2143+func (s *partitionTestSuite) TestCheckPartitionBusyFunc(c *check.C) {
2144+ testCases := []struct {
2145+ execCommandOutput string
2146+ expected string
2147+ }{
2148+ {`prg 4827 user mem REG 8,2 3339 10354893 /usr/share/prg
2149+prg 4827 user 15w REG 8,2 197132 12452026 /home/user/prg`, "15w"},
2150+ {`prg 4827 user mem REG 8,2 3339 10354893 /usr/share/prg
2151+prg 4827 user 15w REG 8,2 197132 12452026 /home/user/prg
2152+prg 4827 user 1w REG 8,2 197132 12452026 /home/user/prg`, "15w"},
2153+ {`prg 4827 user mem REG 8,2 3339 10354893 /usr/share/prg`, lsofNotBeingWritten},
2154+ {`prg 4827 user cwd REG 8,2 3339 10354893 /usr/share/prg`, lsofNotBeingWritten},
2155+ }
2156+
2157+ for _, testCase := range testCases {
2158+ s.execOutput = testCase.execCommandOutput
2159+ f := checkPathBusyFunc(path)
2160+
2161+ actual, err := f()
2162+ c.Check(err, check.IsNil)
2163+ c.Check(actual, check.Equals, testCase.expected,
2164+ check.Commentf("input text %s, expected output %s, obtained %s",
2165+ testCase.execCommandOutput, testCase.expected, actual))
2166+ }
2167+}
2168+
2169+func (s *partitionTestSuite) TestCheckPartitionBusyFuncReturnsErrorOnLsofError(c *check.C) {
2170+ s.execOutput = "not a lsof common output on not used partitions"
2171+ s.execError = true
2172+
2173+ f := checkPathBusyFunc(path)
2174+
2175+ actual, err := f()
2176+
2177+ c.Check(err, check.NotNil)
2178+ c.Check(actual, check.Equals, s.execOutput)
2179+}
2180
2181=== modified file '_integration-tests/testutils/runner/runner.go'
2182--- _integration-tests/testutils/runner/runner.go 2015-08-12 15:50:33 +0000
2183+++ _integration-tests/testutils/runner/runner.go 2015-10-12 09:23:01 +0000
2184@@ -25,7 +25,7 @@
2185 "strings"
2186 "testing"
2187
2188- check "gopkg.in/check.v1"
2189+ "gopkg.in/check.v1"
2190 )
2191
2192 var (
2193
2194=== modified file '_integration-tests/testutils/wait/wait.go'
2195--- _integration-tests/testutils/wait/wait.go 2015-09-28 10:54:40 +0000
2196+++ _integration-tests/testutils/wait/wait.go 2015-10-12 09:23:01 +0000
2197@@ -24,14 +24,14 @@
2198 "regexp"
2199 "time"
2200
2201- check "gopkg.in/check.v1"
2202+ "gopkg.in/check.v1"
2203
2204- "launchpad.net/snappy/_integration-tests/testutils/common"
2205+ "launchpad.net/snappy/_integration-tests/testutils/cli"
2206 )
2207
2208 var (
2209 // dependency aliasing
2210- execCommand = common.ExecCommand
2211+ execCommand = cli.ExecCommand
2212 // ForCommand dep alias
2213 ForCommand = forCommand
2214 // ForFunction dep alias
2215
2216=== added file 'cmd/snappy/cmd_activate.go'
2217--- cmd/snappy/cmd_activate.go 1970-01-01 00:00:00 +0000
2218+++ cmd/snappy/cmd_activate.go 2015-10-12 09:23:01 +0000
2219@@ -0,0 +1,60 @@
2220+// -*- Mode: Go; indent-tabs-mode: t -*-
2221+
2222+/*
2223+ * Copyright (C) 2015 Canonical Ltd
2224+ *
2225+ * This program is free software: you can redistribute it and/or modify
2226+ * it under the terms of the GNU General Public License version 3 as
2227+ * published by the Free Software Foundation.
2228+ *
2229+ * This program is distributed in the hope that it will be useful,
2230+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2231+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2232+ * GNU General Public License for more details.
2233+ *
2234+ * You should have received a copy of the GNU General Public License
2235+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2236+ *
2237+ */
2238+
2239+package main
2240+
2241+import (
2242+ "launchpad.net/snappy/i18n"
2243+ "launchpad.net/snappy/logger"
2244+ "launchpad.net/snappy/progress"
2245+ "launchpad.net/snappy/snappy"
2246+)
2247+
2248+type cmdActivate struct {
2249+ Args struct {
2250+ Snap string `positional-arg-name:"snap"`
2251+ } `positional-args:"yes" required:"very yes"`
2252+ activate bool
2253+}
2254+
2255+func init() {
2256+ _, err := parser.AddCommand("activate",
2257+ i18n.G(`Activate a package`),
2258+ i18n.G(`Activate a package that has previously been deactivated. If the package is already activated, do nothing.`),
2259+ &cmdActivate{activate: true})
2260+ if err != nil {
2261+ logger.Panicf("Unable to activate: %v", err)
2262+ }
2263+
2264+ _, err = parser.AddCommand("deactivate",
2265+ i18n.G(`Deactivate a package`),
2266+ i18n.G(`Deactivate a package. If the package is already deactivated, do nothing.`),
2267+ &cmdActivate{activate: false})
2268+ if err != nil {
2269+ logger.Panicf("Unable to deactivate: %v", err)
2270+ }
2271+}
2272+
2273+func (x *cmdActivate) Execute(args []string) error {
2274+ return withMutex(x.doActivate)
2275+}
2276+
2277+func (x *cmdActivate) doActivate() error {
2278+ return snappy.SetActive(x.Args.Snap, x.activate, progress.MakeProgressBar())
2279+}
2280
2281=== modified file 'cmd/snappy/cmd_build.go'
2282--- cmd/snappy/cmd_build.go 2015-07-09 06:05:53 +0000
2283+++ cmd/snappy/cmd_build.go 2015-10-12 09:23:01 +0000
2284@@ -30,7 +30,8 @@
2285 const clickReview = "click-review"
2286
2287 type cmdBuild struct {
2288- Output string `long:"output" short:"o"`
2289+ Output string `long:"output" short:"o"`
2290+ BuildSnapfs bool `long:"snapfs"`
2291 }
2292
2293 var longBuildHelp = i18n.G("Creates a snap package and if available, runs the review scripts.")
2294@@ -53,7 +54,12 @@
2295 args = []string{"."}
2296 }
2297
2298- snapPackage, err := snappy.Build(args[0], x.Output)
2299+ var snapPackage string
2300+ if x.BuildSnapfs {
2301+ snapPackage, err = snappy.BuildSnapfsSnap(args[0], x.Output)
2302+ } else {
2303+ snapPackage, err = snappy.BuildLegacySnap(args[0], x.Output)
2304+ }
2305 if err != nil {
2306 return err
2307 }
2308
2309=== modified file 'cmd/snappy/cmd_info.go'
2310--- cmd/snappy/cmd_info.go 2015-07-24 11:52:14 +0000
2311+++ cmd/snappy/cmd_info.go 2015-10-12 09:23:01 +0000
2312@@ -103,8 +103,8 @@
2313
2314 func info() error {
2315 release := ubuntuCoreChannel()
2316- frameworks, _ := snappy.ActiveSnapNamesByType(pkg.TypeFramework)
2317- apps, _ := snappy.ActiveSnapNamesByType(pkg.TypeApp)
2318+ frameworks, _ := snappy.ActiveSnapIterByType(snappy.FullName, pkg.TypeFramework)
2319+ apps, _ := snappy.ActiveSnapIterByType(snappy.FullName, pkg.TypeApp)
2320
2321 // TRANSLATORS: the %s release string
2322 fmt.Printf(i18n.G("release: %s\n"), release)
2323
2324=== modified file 'cmd/snappy/cmd_internal_unpack.go'
2325--- cmd/snappy/cmd_internal_unpack.go 2015-07-02 16:01:11 +0000
2326+++ cmd/snappy/cmd_internal_unpack.go 2015-10-12 09:23:01 +0000
2327@@ -30,9 +30,9 @@
2328 "strings"
2329 "syscall"
2330
2331- "launchpad.net/snappy/clickdeb"
2332 "launchpad.net/snappy/helpers"
2333 "launchpad.net/snappy/logger"
2334+ "launchpad.net/snappy/pkg/clickdeb"
2335 )
2336
2337 // #include <sys/prctl.h>
2338
2339=== modified file 'daemon/api.go'
2340--- daemon/api.go 2015-09-15 12:57:04 +0000
2341+++ daemon/api.go 2015-10-12 09:23:01 +0000
2342@@ -27,13 +27,15 @@
2343 "mime/multipart"
2344 "net/http"
2345 "os"
2346+ "path/filepath"
2347 "sort"
2348- "strconv"
2349 "strings"
2350
2351 "github.com/gorilla/mux"
2352
2353+ "launchpad.net/snappy/dirs"
2354 "launchpad.net/snappy/logger"
2355+ "launchpad.net/snappy/pkg/lightweight"
2356 "launchpad.net/snappy/progress"
2357 "launchpad.net/snappy/release"
2358 "launchpad.net/snappy/snappy"
2359@@ -42,6 +44,8 @@
2360 var api = []*Command{
2361 rootCmd,
2362 v1Cmd,
2363+ metaIconCmd,
2364+ appIconCmd,
2365 packagesCmd,
2366 packageCmd,
2367 packageConfigCmd,
2368@@ -62,6 +66,16 @@
2369 GET: v1Get,
2370 }
2371
2372+ metaIconCmd = &Command{
2373+ Path: "/1.0/icons/{icon}",
2374+ GET: metaIconGet,
2375+ }
2376+
2377+ appIconCmd = &Command{
2378+ Path: "/1.0/icons/{name}.{origin}/icon",
2379+ GET: appIconGet,
2380+ }
2381+
2382 packagesCmd = &Command{
2383 Path: "/1.0/packages",
2384 GET: getPackagesInfo,
2385@@ -105,53 +119,49 @@
2386
2387 func v1Get(c *Command, r *http.Request) Response {
2388 rel := release.Get()
2389- return SyncResponse(map[string]string{
2390+ m := map[string]string{
2391 "flavor": rel.Flavor,
2392 "release": rel.Series,
2393 "default_channel": rel.Channel,
2394 "api_compat": "0",
2395- })
2396+ }
2397+
2398+ if store := snappy.StoreID(); store != "" {
2399+ m["store"] = store
2400+ }
2401+
2402+ return SyncResponse(m)
2403 }
2404
2405 type metarepo interface {
2406 Details(string, string) ([]snappy.Part, error)
2407 All() ([]snappy.Part, error)
2408-}
2409-
2410-var newRepo = func() metarepo {
2411- return snappy.NewMetaRepository()
2412-}
2413-
2414-var newLocalRepo = func() metarepo {
2415- return snappy.NewMetaLocalRepository()
2416+ Updates() ([]snappy.Part, error)
2417 }
2418
2419 var newRemoteRepo = func() metarepo {
2420 return snappy.NewMetaStoreRepository()
2421 }
2422
2423+var newSystemRepo = func() metarepo {
2424+ return snappy.NewSystemImageRepository()
2425+}
2426+
2427 var muxVars = mux.Vars
2428
2429 func getPackageInfo(c *Command, r *http.Request) Response {
2430 vars := muxVars(r)
2431 name := vars["name"]
2432 origin := vars["origin"]
2433- if name == "" || origin == "" {
2434- // can't happen, i think? mux won't let it
2435- return BadRequest(nil, "missing name or origin")
2436- }
2437-
2438- repo := newRepo()
2439- found, err := repo.Details(name, origin)
2440- if err != nil {
2441- if err == snappy.ErrPackageNotFound {
2442- return NotFound
2443- }
2444-
2445- return InternalError(err, "unable to list packages: %v", err)
2446- }
2447-
2448- if len(found) == 0 {
2449+
2450+ repo := newRemoteRepo()
2451+ var part snappy.Part
2452+ if parts, _ := repo.Details(name, origin); len(parts) > 0 {
2453+ part = parts[0]
2454+ }
2455+
2456+ bag := lightweight.PartBagByName(name, origin)
2457+ if bag == nil && part == nil {
2458 return NotFound
2459 }
2460
2461@@ -165,51 +175,37 @@
2462 return InternalError(err, "route can't build URL for package %s.%s: %v", name, origin, err)
2463 }
2464
2465- result := parts2map(found, url.String())
2466+ result := webify(bag.Map(part), url.String())
2467
2468 return SyncResponse(result)
2469 }
2470
2471-// parts2map takes a slice of parts with the same name and returns a
2472-// single map with that part's metadata (including rollback_available
2473-// & etc).
2474-func parts2map(parts []snappy.Part, resource string) map[string]string {
2475- if len(parts) == 0 {
2476- return nil
2477- }
2478-
2479- // TODO: handle multiple results in parts; set rollback_available; set update_available
2480- part := parts[0]
2481- var status string
2482- if part.IsInstalled() {
2483- if part.IsActive() {
2484- status = "active"
2485- } else {
2486- // can't really happen
2487- status = "installed"
2488+func webify(result map[string]string, resource string) map[string]string {
2489+ result["resource"] = resource
2490+
2491+ icon := result["icon"]
2492+ if icon == "" || strings.HasPrefix(icon, "http") {
2493+ return result
2494+ }
2495+
2496+ result["icon"] = ""
2497+
2498+ var route *mux.Route
2499+ var args []string
2500+
2501+ if strings.HasPrefix(icon, dirs.SnapIconsDir) {
2502+ route = metaIconCmd.d.router.Get(metaIconCmd.Path)
2503+ args = []string{"icon", icon[len(dirs.SnapIconsDir)+1:]}
2504+ } else {
2505+ route = appIconCmd.d.router.Get(appIconCmd.Path)
2506+ args = []string{"name", result["name"], "origin", result["origin"]}
2507+ }
2508+
2509+ if route != nil {
2510+ url, err := route.URL(args...)
2511+ if err == nil {
2512+ result["icon"] = url.String()
2513 }
2514- } else {
2515- status = "not installed"
2516- }
2517- // TODO: check for removed and transients (extend the Part interface for removed; check ops for transients)
2518-
2519- icon := part.Icon()
2520- if strings.HasPrefix(icon, iconPath) {
2521- icon = iconPrefix + icon[len(iconPath):]
2522- }
2523-
2524- result := map[string]string{
2525- "icon": icon,
2526- "name": part.Name(),
2527- "origin": part.Origin(),
2528- "resource": resource,
2529- "status": status,
2530- "type": string(part.Type()),
2531- "vendor": part.Vendor(),
2532- "version": part.Version(),
2533- "description": part.Description(),
2534- "installed_size": strconv.FormatInt(part.InstalledSize(), 10),
2535- "download_size": strconv.FormatInt(part.DownloadSize(), 10),
2536 }
2537
2538 return result
2539@@ -223,17 +219,6 @@
2540 return snappy.QualifiedName(ps[a]) < snappy.QualifiedName(ps[b])
2541 }
2542
2543-func addPart(results map[string]map[string]string, current []snappy.Part, name string, origin string, route *mux.Route) error {
2544- url, err := route.URL("name", name, "origin", origin)
2545- if err != nil {
2546- return err
2547- }
2548-
2549- results[name+"."+origin] = parts2map(current, url.String())
2550-
2551- return nil
2552-}
2553-
2554 // plural!
2555 func getPackagesInfo(c *Command, r *http.Request) Response {
2556 route := c.d.router.Get(packageCmd.Path)
2557@@ -241,58 +226,58 @@
2558 return InternalError(nil, "router can't find route for packages")
2559 }
2560
2561- sources := r.URL.Query().Get("sources")
2562- var repo metarepo
2563- switch sources {
2564- case "local":
2565- repo = newLocalRepo()
2566- case "remote":
2567- repo = newRemoteRepo()
2568- default:
2569- repo = newRepo()
2570- }
2571-
2572- found, err := repo.All()
2573- if err != nil {
2574- if err == snappy.ErrPackageNotFound {
2575- return NotFound
2576- }
2577-
2578- return InternalError(err, "unable to list packages: %v", err)
2579- }
2580-
2581- if len(found) == 0 {
2582- return NotFound
2583- }
2584+ sources := make([]string, 1, 3)
2585+ sources[0] = "local"
2586+ // we're not worried if the remote repos error out
2587+ found, _ := newRemoteRepo().All()
2588+ if len(found) > 0 {
2589+ sources = append(sources, "store")
2590+ }
2591+
2592+ upd, _ := newSystemRepo().Updates()
2593+ if len(upd) > 0 {
2594+ sources = append(sources, "system-image")
2595+ }
2596+
2597+ found = append(found, upd...)
2598
2599 sort.Sort(byQN(found))
2600
2601+ bags := lightweight.AllPartBags()
2602+
2603 results := make(map[string]map[string]string)
2604- var current []snappy.Part
2605- var oldName string
2606- var oldOrigin string
2607- for i := range found {
2608- name := found[i].Name()
2609- origin := found[i].Origin()
2610- if (name != oldName || origin != oldOrigin) && len(current) > 0 {
2611- if err := addPart(results, current, oldName, oldOrigin, route); err != nil {
2612- return InternalError(err, "can't get details for %s.%s: %v", oldName, oldOrigin, err)
2613- }
2614- current = nil
2615+ for _, part := range found {
2616+ name := part.Name()
2617+ origin := part.Origin()
2618+
2619+ url, err := route.URL("name", name, "origin", origin)
2620+ if err != nil {
2621+ return InternalError(err, "can't get route to details for %s.%s: %v", name, origin, err)
2622 }
2623- oldName = name
2624- oldOrigin = origin
2625- current = append(current, found[i])
2626+
2627+ fullname := name + "." + origin
2628+ qn := snappy.QualifiedName(part)
2629+ results[fullname] = webify(bags[qn].Map(part), url.String())
2630+ delete(bags, qn)
2631 }
2632
2633- if len(current) > 0 {
2634- if err := addPart(results, current, oldName, oldOrigin, route); err != nil {
2635- return InternalError(err, "can't get details for %s.%s: %v", oldName, oldOrigin, err)
2636+ for _, v := range bags {
2637+ m := v.Map(nil)
2638+ name := m["name"]
2639+ origin := m["origin"]
2640+
2641+ resource := "no resource URL for this resource"
2642+ url, _ := route.URL("name", name, "origin", origin)
2643+ if url != nil {
2644+ resource = url.String()
2645 }
2646+
2647+ results[name+"."+origin] = webify(m, resource)
2648 }
2649
2650 return SyncResponse(map[string]interface{}{
2651 "packages": results,
2652+ "sources": sources,
2653 "paging": map[string]interface{}{
2654 "pages": 1,
2655 "page": 1,
2656@@ -301,25 +286,6 @@
2657 })
2658 }
2659
2660-func getActivePkg(fullname string) ([]snappy.Part, error) {
2661- // TODO: below should be rolled into ActiveSnapByName
2662- // (specifically: ActiveSnapByname should know about origins)
2663- repo := newLocalRepo()
2664- all, err := repo.All()
2665- if err != nil {
2666- return nil, err
2667- }
2668-
2669- parts := all[:0]
2670- for _, part := range snappy.FindSnapsByName(fullname, all) {
2671- if part.IsActive() {
2672- parts = append(parts, part)
2673- }
2674- }
2675-
2676- return parts, nil
2677-}
2678-
2679 var findServices = snappy.FindServices
2680
2681 type svcDesc struct {
2682@@ -362,23 +328,20 @@
2683 return BadRequest(nil, "unknown action %s", action)
2684 }
2685
2686- parts, err := getActivePkg(pkgName)
2687+ bag := lightweight.PartBagByName(name, origin)
2688+ idx := bag.ActiveIndex()
2689+ if idx < 0 {
2690+ return NotFound
2691+ }
2692+
2693+ ipart, err := bag.Load(idx)
2694 if err != nil {
2695- return InternalError(err, "unable to get package list: %v", err)
2696- }
2697-
2698- switch len(parts) {
2699- default:
2700- return InternalError(nil, "found %d active for %s", len(parts), pkgName)
2701- case 0:
2702- return NotFound
2703- case 1:
2704- // yay, or something
2705- }
2706-
2707- part, ok := parts[0].(snappy.ServiceYamler)
2708+ return InternalError(err, "unable to get load active package: %v", err)
2709+ }
2710+
2711+ part, ok := ipart.(*snappy.SnapPart)
2712 if !ok {
2713- return InternalError(nil, "active package of type %T does not implement snappy.ServiceYamler", parts[0])
2714+ return InternalError(nil, "active package is not a *snappy.SnapPart: %T", ipart)
2715 }
2716 svcs := part.ServiceYamls()
2717
2718@@ -460,18 +423,15 @@
2719 }
2720 pkgName := name + "." + origin
2721
2722- parts, err := getActivePkg(pkgName)
2723+ bag := lightweight.PartBagByName(name, origin)
2724+ idx := bag.ActiveIndex()
2725+ if idx < 0 {
2726+ return NotFound
2727+ }
2728+
2729+ part, err := bag.Load(idx)
2730 if err != nil {
2731- return InternalError(err, "unable to get package list: %v", err)
2732- }
2733-
2734- switch len(parts) {
2735- default:
2736- return InternalError(nil, "found %d active for %s", len(parts), pkgName)
2737- case 0:
2738- return NotFound
2739- case 1:
2740- // yay, or something
2741+ return InternalError(err, "unable to get load active package: %v", err)
2742 }
2743
2744 bs, err := ioutil.ReadAll(r.Body)
2745@@ -479,7 +439,7 @@
2746 return BadRequest(err, "reading config request body gave %v", err)
2747 }
2748
2749- config, err := parts[0].Config(bs)
2750+ config, err := part.Config(bs)
2751 if err != nil {
2752 return InternalError(err, "unable to retrieve config for %s: %v", pkgName, err)
2753 }
2754@@ -569,6 +529,14 @@
2755 return err
2756 }
2757
2758+func (inst *packageInstruction) activate() interface{} {
2759+ return snappy.SetActive(inst.pkg, true, inst.prog)
2760+}
2761+
2762+func (inst *packageInstruction) deactivate() interface{} {
2763+ return snappy.SetActive(inst.pkg, false, inst.prog)
2764+}
2765+
2766 func (inst *packageInstruction) dispatch() func() interface{} {
2767 switch inst.Action {
2768 case "install":
2769@@ -582,6 +550,10 @@
2770 return inst.purge
2771 case "rollback":
2772 return inst.rollback
2773+ case "activate":
2774+ return inst.activate
2775+ case "deactivate":
2776+ return inst.deactivate
2777 default:
2778 return nil
2779 }
2780@@ -729,3 +701,35 @@
2781
2782 return SyncResponse(logs)
2783 }
2784+
2785+func metaIconGet(c *Command, r *http.Request) Response {
2786+ vars := muxVars(r)
2787+ name := vars["icon"]
2788+
2789+ path := filepath.Join(dirs.SnapIconsDir, name)
2790+
2791+ return FileResponse(path)
2792+}
2793+
2794+func appIconGet(c *Command, r *http.Request) Response {
2795+ vars := muxVars(r)
2796+ name := vars["name"]
2797+ origin := vars["origin"]
2798+
2799+ bag := lightweight.PartBagByName(name, origin)
2800+ if bag == nil || len(bag.Versions) == 0 {
2801+ return NotFound
2802+ }
2803+
2804+ part := bag.LoadBest()
2805+ if part == nil {
2806+ return NotFound
2807+ }
2808+
2809+ path := filepath.Clean(part.Icon())
2810+ if !strings.HasPrefix(path, dirs.SnapAppsDir) && !strings.HasPrefix(path, dirs.SnapOemDir) {
2811+ return BadRequest
2812+ }
2813+
2814+ return FileResponse(path)
2815+}
2816
2817=== modified file 'daemon/api_test.go'
2818--- daemon/api_test.go 2015-09-15 12:55:09 +0000
2819+++ daemon/api_test.go 2015-10-12 09:23:01 +0000
2820@@ -32,11 +32,15 @@
2821 "net/http/httptest"
2822 "os"
2823 "path/filepath"
2824+ "strings"
2825 "testing"
2826 "time"
2827
2828 "gopkg.in/check.v1"
2829
2830+ "launchpad.net/snappy/dirs"
2831+ "launchpad.net/snappy/pkg"
2832+ "launchpad.net/snappy/pkg/lightweight"
2833 "launchpad.net/snappy/progress"
2834 "launchpad.net/snappy/release"
2835 "launchpad.net/snappy/snappy"
2836@@ -62,69 +66,123 @@
2837 return s.parts, s.err
2838 }
2839
2840+func (s *apiSuite) Updates() ([]snappy.Part, error) {
2841+ return s.parts, s.err
2842+}
2843+
2844 func (s *apiSuite) muxVars(*http.Request) map[string]string {
2845 return s.vars
2846 }
2847
2848 func (s *apiSuite) SetUpSuite(c *check.C) {
2849- newRepo = func() metarepo {
2850+ newRemoteRepo = func() metarepo {
2851 return s
2852 }
2853- newLocalRepo = newRepo
2854- newRemoteRepo = newRepo
2855+ newSystemRepo = newRemoteRepo
2856 muxVars = s.muxVars
2857 }
2858
2859 func (s *apiSuite) TearDownSuite(c *check.C) {
2860- newLocalRepo = nil
2861 newRemoteRepo = nil
2862- newRepo = nil
2863+ newSystemRepo = nil
2864 muxVars = nil
2865 }
2866
2867 func (s *apiSuite) SetUpTest(c *check.C) {
2868+ dirs.SetRootDir(c.MkDir())
2869+
2870 s.parts = nil
2871 s.err = nil
2872 s.vars = nil
2873 }
2874
2875+func (s *apiSuite) mkInstalled(c *check.C, name, origin, version string, active bool, extraYaml string) {
2876+ fullname := name + "." + origin
2877+ c.Assert(os.MkdirAll(filepath.Join(dirs.SnapDataDir, fullname, version), 0755), check.IsNil)
2878+
2879+ metadir := filepath.Join(dirs.SnapAppsDir, fullname, version, "meta")
2880+ c.Assert(os.MkdirAll(metadir, 0755), check.IsNil)
2881+
2882+ content := fmt.Sprintf(`
2883+name: %s
2884+version: %s
2885+vendor: a vendor
2886+%s`, name, version, extraYaml)
2887+ c.Check(ioutil.WriteFile(filepath.Join(metadir, "package.yaml"), []byte(content), 0644), check.IsNil)
2888+ c.Check(ioutil.WriteFile(filepath.Join(metadir, "hashes.yaml"), []byte(nil), 0644), check.IsNil)
2889+
2890+ if active {
2891+ c.Assert(os.Symlink(version, filepath.Join(dirs.SnapAppsDir, fullname, "current")), check.IsNil)
2892+ }
2893+}
2894+
2895+func (s *apiSuite) mkOem(c *check.C, store string) {
2896+ content := []byte(fmt.Sprintf(`name: test
2897+version: 1
2898+vendor: a vendor
2899+type: oem
2900+oem: {store: {id: %q}}
2901+`, store))
2902+
2903+ d := filepath.Join(dirs.SnapOemDir, "test")
2904+ m := filepath.Join(d, "1", "meta")
2905+ c.Assert(os.MkdirAll(m, 0755), check.IsNil)
2906+ c.Assert(os.Symlink("1", filepath.Join(d, "current")), check.IsNil)
2907+ c.Assert(ioutil.WriteFile(filepath.Join(m, "package.yaml"), content, 0644), check.IsNil)
2908+ c.Assert(ioutil.WriteFile(filepath.Join(m, "hashes.yaml"), []byte(nil), 0644), check.IsNil)
2909+}
2910+
2911 func (s *apiSuite) TestPackageInfoOneIntegration(c *check.C) {
2912 d := New()
2913 d.addRoutes()
2914
2915 s.vars = map[string]string{"name": "foo", "origin": "bar"}
2916
2917+ // the store tells us about v2
2918 s.parts = []snappy.Part{&tP{
2919- name: "foo",
2920- version: "v1",
2921- description: "description",
2922- origin: "bar",
2923- vendor: "a vendor",
2924- isInstalled: true,
2925- isActive: true,
2926- icon: iconPath + "icon.png",
2927- _type: "a type",
2928- installedSize: 42,
2929- downloadSize: 2,
2930+ name: "foo",
2931+ version: "v2",
2932+ description: "description",
2933+ origin: "bar",
2934+ vendor: "a vendor",
2935+ isInstalled: true,
2936+ isActive: true,
2937+ icon: dirs.SnapIconsDir + "icon.png",
2938+ _type: pkg.TypeApp,
2939+ downloadSize: 2,
2940 }}
2941+
2942+ // we have v0 installed
2943+ s.mkInstalled(c, "foo", "bar", "v0", false, "")
2944+ // and v1 is current
2945+ s.mkInstalled(c, "foo", "bar", "v1", true, "")
2946+
2947 rsp, ok := getPackageInfo(packageCmd, nil).(*resp)
2948 c.Assert(ok, check.Equals, true)
2949
2950+ // installed_size depends on vagaries of the filesystem, just check regexp
2951+ c.Assert(rsp, check.NotNil)
2952+ c.Assert(rsp.Result, check.FitsTypeOf, map[string]string{})
2953+ m := rsp.Result.(map[string]string)
2954+ c.Check(m["installed_size"], check.Matches, "[0-9]+")
2955+ delete(m, "installed_size")
2956+
2957 expected := &resp{
2958 Type: ResponseTypeSync,
2959 Status: http.StatusOK,
2960 Result: map[string]string{
2961- "name": "foo",
2962- "version": "v1",
2963- "description": "description",
2964- "origin": "bar",
2965- "vendor": "a vendor",
2966- "status": "active",
2967- "icon": iconPrefix + "icon.png",
2968- "type": "a type",
2969- "download_size": "2",
2970- "installed_size": "42",
2971- "resource": "/1.0/packages/foo.bar",
2972+ "name": "foo",
2973+ "version": "v1",
2974+ "description": "description",
2975+ "origin": "bar",
2976+ "vendor": "a vendor",
2977+ "status": "active",
2978+ "icon": "/1.0/icons/foo.bar/icon",
2979+ "type": string(pkg.TypeApp),
2980+ "download_size": "2",
2981+ "resource": "/1.0/packages/foo.bar",
2982+ "update_available": "v2",
2983+ "rollback_available": "v0",
2984 },
2985 }
2986
2987@@ -144,14 +202,14 @@
2988 c.Check(getPackageInfo(packageCmd, nil).Self(nil, nil).(*resp).Status, check.Equals, http.StatusNotFound)
2989 }
2990
2991-func (s *apiSuite) TestPackageInfoWeirdDetails(c *check.C) {
2992+func (s *apiSuite) TestPackageInfoIgnoresRemoteErrors(c *check.C) {
2993 s.vars = map[string]string{"name": "foo", "origin": "bar"}
2994 s.err = errors.New("weird")
2995
2996- rsp := getPackageInfo(packageCmd, nil).(*resp)
2997+ rsp := getPackageInfo(packageCmd, nil).Self(nil, nil).(*resp)
2998
2999 c.Check(rsp.Type, check.Equals, ResponseTypeError)
3000- c.Check(rsp.Status, check.Equals, http.StatusInternalServerError)
3001+ c.Check(rsp.Status, check.Equals, http.StatusNotFound)
3002 c.Check(rsp.Result, check.NotNil)
3003 }
3004
3005@@ -188,47 +246,6 @@
3006 c.Check(rsp.Result.(map[string]interface{})["msg"], check.Matches, `route can't build URL .*`)
3007 }
3008
3009-func (s *apiSuite) TestParts2Map(c *check.C) {
3010- parts := []snappy.Part{&tP{
3011- name: "foo",
3012- version: "v1",
3013- description: "description",
3014- origin: "bar",
3015- vendor: "a vendor",
3016- isInstalled: true,
3017- isActive: true,
3018- icon: "icon.png",
3019- _type: "a type",
3020- installedSize: 42,
3021- downloadSize: 2,
3022- }}
3023-
3024- resource := "/1.0/packages/foo.bar"
3025-
3026- expected := map[string]string{
3027- "name": "foo",
3028- "version": "v1",
3029- "description": "description",
3030- "origin": "bar",
3031- "vendor": "a vendor",
3032- "status": "active",
3033- "icon": "icon.png",
3034- "type": "a type",
3035- "download_size": "2",
3036- "installed_size": "42",
3037- "resource": resource,
3038- }
3039-
3040- c.Check(parts2map(parts, resource), check.DeepEquals, expected)
3041-}
3042-
3043-func (s *apiSuite) TestParts2MapState(c *check.C) {
3044- c.Check(parts2map([]snappy.Part{&tP{}}, "")["status"], check.Equals, "not installed")
3045- c.Check(parts2map([]snappy.Part{&tP{isInstalled: true}}, "")["status"], check.Equals, "installed")
3046- c.Check(parts2map([]snappy.Part{&tP{isInstalled: true, isActive: true}}, "")["status"], check.Equals, "active")
3047- // TODO: more statuses
3048-}
3049-
3050 func (s *apiSuite) TestListIncludesAll(c *check.C) {
3051 // Very basic check to help stop us from not adding all the
3052 // commands to the command list.
3053@@ -263,9 +280,8 @@
3054 "findServices",
3055 "maxReadBuflen",
3056 "muxVars",
3057- "newLocalRepo",
3058 "newRemoteRepo",
3059- "newRepo",
3060+ "newSystemRepo",
3061 "newSnap",
3062 "pkgActionDispatch",
3063 }
3064@@ -294,22 +310,26 @@
3065 c.Check(rsp.Result, check.DeepEquals, expected)
3066 }
3067
3068-func (s *apiSuite) TestV1(c *check.C) {
3069- // check it only does GET
3070- c.Check(v1Cmd.PUT, check.IsNil)
3071- c.Check(v1Cmd.POST, check.IsNil)
3072- c.Check(v1Cmd.DELETE, check.IsNil)
3073- c.Assert(v1Cmd.GET, check.NotNil)
3074-
3075- rec := httptest.NewRecorder()
3076- c.Check(v1Cmd.Path, check.Equals, "/1.0")
3077-
3078+func (s *apiSuite) mkrelease(c *check.C) {
3079 // set up release
3080 root := c.MkDir()
3081 d := filepath.Join(root, "etc", "system-image")
3082 c.Assert(os.MkdirAll(d, 0755), check.IsNil)
3083 c.Assert(ioutil.WriteFile(filepath.Join(d, "channel.ini"), []byte("[service]\nchannel: ubuntu-flavor/release/channel"), 0644), check.IsNil)
3084 c.Assert(release.Setup(root), check.IsNil)
3085+}
3086+
3087+func (s *apiSuite) TestV1(c *check.C) {
3088+ // check it only does GET
3089+ c.Check(v1Cmd.PUT, check.IsNil)
3090+ c.Check(v1Cmd.POST, check.IsNil)
3091+ c.Check(v1Cmd.DELETE, check.IsNil)
3092+ c.Assert(v1Cmd.GET, check.NotNil)
3093+
3094+ rec := httptest.NewRecorder()
3095+ c.Check(v1Cmd.Path, check.Equals, "/1.0")
3096+
3097+ s.mkrelease(c)
3098
3099 v1Cmd.GET(v1Cmd, nil).ServeHTTP(rec, nil)
3100 c.Check(rec.Code, check.Equals, 200)
3101@@ -328,6 +348,30 @@
3102 c.Check(rsp.Result, check.DeepEquals, expected)
3103 }
3104
3105+func (s *apiSuite) TestV1Store(c *check.C) {
3106+ rec := httptest.NewRecorder()
3107+ c.Check(v1Cmd.Path, check.Equals, "/1.0")
3108+
3109+ s.mkrelease(c)
3110+ s.mkOem(c, "some-store")
3111+
3112+ v1Cmd.GET(v1Cmd, nil).ServeHTTP(rec, nil)
3113+ c.Check(rec.Code, check.Equals, 200)
3114+
3115+ expected := map[string]interface{}{
3116+ "flavor": "flavor",
3117+ "release": "release",
3118+ "default_channel": "channel",
3119+ "api_compat": "0",
3120+ "store": "some-store",
3121+ }
3122+ var rsp resp
3123+ c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), check.IsNil)
3124+ c.Check(rsp.Status, check.Equals, 200)
3125+ c.Check(rsp.Type, check.Equals, ResponseTypeSync)
3126+ c.Check(rsp.Result, check.DeepEquals, expected)
3127+}
3128+
3129 func (s *apiSuite) TestPackagesInfoOnePerIntegration(c *check.C) {
3130 d := New()
3131 d.addRoutes()
3132@@ -335,12 +379,12 @@
3133 req, err := http.NewRequest("GET", "/1.0/packages", nil)
3134 c.Assert(err, check.IsNil)
3135
3136- s.parts = []snappy.Part{
3137- &tP{name: "foo", version: "v1", origin: "bar"},
3138- &tP{name: "bar", version: "v2", origin: "baz"},
3139- &tP{name: "baz", version: "v3", origin: "qux"},
3140- &tP{name: "qux", version: "v4", origin: "mip"},
3141+ ddirs := [][2]string{{"foo.bar", "v1"}, {"bar.baz", "v2"}, {"baz.qux", "v3"}, {"qux.mip", "v4"}}
3142+
3143+ for i := range ddirs {
3144+ c.Assert(os.MkdirAll(filepath.Join(dirs.SnapDataDir, ddirs[i][0], ddirs[i][1]), 0755), check.IsNil)
3145 }
3146+
3147 rsp, ok := getPackagesInfo(packagesCmd, req).(*resp)
3148 c.Assert(ok, check.Equals, true)
3149
3150@@ -351,21 +395,22 @@
3151 meta, ok := rsp.Result.(map[string]interface{})
3152 c.Assert(ok, check.Equals, true)
3153 c.Assert(meta, check.NotNil)
3154- c.Check(meta["paging"], check.DeepEquals, map[string]interface{}{"pages": 1, "page": 1, "count": len(s.parts)})
3155+ c.Check(meta["paging"], check.DeepEquals, map[string]interface{}{"pages": 1, "page": 1, "count": len(ddirs)})
3156
3157 packages, ok := meta["packages"].(map[string]map[string]string)
3158 c.Assert(ok, check.Equals, true)
3159 c.Check(packages, check.NotNil)
3160- c.Check(packages, check.HasLen, len(s.parts))
3161+ c.Check(packages, check.HasLen, len(ddirs))
3162
3163- for _, part := range s.parts {
3164- part := part.(*tP)
3165- qn := part.name + "." + part.origin
3166+ for i := range ddirs {
3167+ qn, version := ddirs[i][0], ddirs[i][1]
3168+ idx := strings.LastIndex(qn, ".")
3169+ name, origin := qn[:idx], qn[idx+1:]
3170 got := packages[qn]
3171 c.Assert(got, check.NotNil, check.Commentf(qn))
3172- c.Check(got["name"], check.Equals, part.name)
3173- c.Check(got["version"], check.Equals, part.version)
3174- c.Check(got["origin"], check.Equals, part.origin)
3175+ c.Check(got["name"], check.Equals, name)
3176+ c.Check(got["version"], check.Equals, version)
3177+ c.Check(got["origin"], check.Equals, origin)
3178 }
3179 }
3180
3181@@ -530,6 +575,14 @@
3182 }
3183 }
3184
3185+type cfgc struct{ cfg string }
3186+
3187+func (cfgc) IsInstalled(string) bool { return true }
3188+func (cfgc) ActiveIndex() int { return 0 }
3189+func (c cfgc) Load(string) (snappy.Part, error) {
3190+ return &tP{name: "foo", version: "v1", origin: "bar", isActive: true, config: c.cfg}, nil
3191+}
3192+
3193 func (s *apiSuite) TestPackageGetConfig(c *check.C) {
3194 d := New()
3195 d.addRoutes()
3196@@ -538,13 +591,16 @@
3197 c.Assert(err, check.IsNil)
3198
3199 configStr := "some: config"
3200+ oldConcrete := lightweight.NewConcrete
3201+ defer func() {
3202+ lightweight.NewConcrete = oldConcrete
3203+ }()
3204+ lightweight.NewConcrete = func(*lightweight.PartBag, string) lightweight.Concreter {
3205+ return &cfgc{configStr}
3206+ }
3207+
3208 s.vars = map[string]string{"name": "foo", "origin": "bar"}
3209- s.parts = []snappy.Part{
3210- &tP{name: "foo", version: "v1", origin: "bar", isActive: true, config: configStr},
3211- &tP{name: "bar", version: "v2", origin: "baz", isActive: true},
3212- &tP{name: "baz", version: "v3", origin: "qux", isActive: true},
3213- &tP{name: "qux", version: "v4", origin: "mip", isActive: true},
3214- }
3215+ s.mkInstalled(c, "foo", "bar", "v1", true, "")
3216
3217 rsp := packageConfig(packagesCmd, req).(*resp)
3218
3219@@ -564,15 +620,18 @@
3220 c.Assert(err, check.IsNil)
3221
3222 configStr := "some: config"
3223+ oldConcrete := lightweight.NewConcrete
3224+ defer func() {
3225+ lightweight.NewConcrete = oldConcrete
3226+ }()
3227+ lightweight.NewConcrete = func(*lightweight.PartBag, string) lightweight.Concreter {
3228+ return &cfgc{configStr}
3229+ }
3230+
3231 s.vars = map[string]string{"name": "foo", "origin": "bar"}
3232- s.parts = []snappy.Part{
3233- &tP{name: "foo", version: "v1", origin: "bar", isActive: true, config: configStr},
3234- &tP{name: "bar", version: "v2", origin: "baz", isActive: true},
3235- &tP{name: "baz", version: "v3", origin: "qux", isActive: true},
3236- &tP{name: "qux", version: "v4", origin: "mip", isActive: true},
3237- }
3238+ s.mkInstalled(c, "foo", "bar", "v1", true, "")
3239
3240- rsp := packageConfig(packagesCmd, req).(*resp)
3241+ rsp := packageConfig(packagesCmd, req).Self(nil, nil).(*resp)
3242
3243 c.Check(rsp, check.DeepEquals, &resp{
3244 Type: ResponseTypeSync,
3245@@ -592,11 +651,7 @@
3246 req, err := http.NewRequest("GET", "/1.0/packages/foo.bar/services", nil)
3247 c.Assert(err, check.IsNil)
3248
3249- s.parts = []snappy.Part{
3250- &tP{name: "foo", version: "v1", origin: "bar", isActive: true,
3251- svcYamls: []snappy.ServiceYaml{{Name: "svc"}},
3252- },
3253- }
3254+ s.mkInstalled(c, "foo", "bar", "v1", true, "services: [{name: svc}]")
3255 s.vars = map[string]string{"name": "foo", "origin": "bar"} // NB: no service specified
3256
3257 rsp := packageService(packageSvcsCmd, req).(*resp)
3258@@ -605,11 +660,10 @@
3259 c.Check(rsp.Status, check.Equals, http.StatusOK)
3260
3261 m := rsp.Result.(map[string]*svcDesc)
3262- c.Assert(m["svc"], check.DeepEquals, &svcDesc{
3263- Op: "status",
3264- Spec: &snappy.ServiceYaml{Name: "svc"},
3265- Status: &snappy.PackageServiceStatus{ServiceName: "svc"},
3266- })
3267+ c.Assert(m["svc"], check.FitsTypeOf, new(svcDesc))
3268+ c.Check(m["svc"].Op, check.Equals, "status")
3269+ c.Check(m["svc"].Spec, check.DeepEquals, &snappy.ServiceYaml{Name: "svc", StopTimeout: snappy.DefaultTimeout})
3270+ c.Check(m["svc"].Status, check.DeepEquals, &snappy.PackageServiceStatus{ServiceName: "svc"})
3271 }
3272
3273 func (s *apiSuite) TestPackageServicePut(c *check.C) {
3274@@ -624,11 +678,7 @@
3275 req, err := http.NewRequest("PUT", "/1.0/packages/foo.bar/services", buf)
3276 c.Assert(err, check.IsNil)
3277
3278- s.parts = []snappy.Part{
3279- &tP{name: "foo", version: "v1", origin: "bar", isActive: true,
3280- svcYamls: []snappy.ServiceYaml{{Name: "svc"}},
3281- },
3282- }
3283+ s.mkInstalled(c, "foo", "bar", "v1", true, "services: [{name: svc}]")
3284 s.vars = map[string]string{"name": "foo", "origin": "bar"} // NB: no service specified
3285
3286 rsp := packageService(packageSvcsCmd, req).(*resp)
3287
3288=== modified file 'daemon/daemon.go'
3289--- daemon/daemon.go 2015-09-15 12:55:09 +0000
3290+++ daemon/daemon.go 2015-10-12 09:23:01 +0000
3291@@ -128,11 +128,6 @@
3292 return nil
3293 }
3294
3295-const (
3296- iconPath = "/var/lib/snappy/icons/"
3297- iconPrefix = "/1.0/icons/"
3298-)
3299-
3300 func (d *Daemon) addRoutes() {
3301 d.router = mux.NewRouter()
3302
3303@@ -142,8 +137,6 @@
3304 d.router.Handle(c.Path, c).Name(c.Path)
3305 }
3306
3307- // hrmph
3308- d.router.PathPrefix(iconPrefix).Handler(http.StripPrefix(iconPrefix, http.FileServer(http.Dir(iconPath)))).Name(iconPrefix)
3309 // also maybe add a /favicon.ico handler...
3310
3311 d.router.NotFoundHandler = NotFound
3312
3313=== added file 'daemon/daemon_test.go'
3314--- daemon/daemon_test.go 1970-01-01 00:00:00 +0000
3315+++ daemon/daemon_test.go 2015-10-12 09:23:01 +0000
3316@@ -0,0 +1,95 @@
3317+// -*- Mode: Go; indent-tabs-mode: t -*-
3318+
3319+/*
3320+ * Copyright (C) 2014-2015 Canonical Ltd
3321+ *
3322+ * This program is free software: you can redistribute it and/or modify
3323+ * it under the terms of the GNU General Public License version 3 as
3324+ * published by the Free Software Foundation.
3325+ *
3326+ * This program is distributed in the hope that it will be useful,
3327+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3328+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3329+ * GNU General Public License for more details.
3330+ *
3331+ * You should have received a copy of the GNU General Public License
3332+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3333+ *
3334+ */
3335+
3336+package daemon
3337+
3338+import (
3339+ "fmt"
3340+ "net/http"
3341+ "net/http/httptest"
3342+
3343+ "github.com/gorilla/mux"
3344+ "gopkg.in/check.v1"
3345+)
3346+
3347+type daemonSuite struct{}
3348+
3349+var _ = check.Suite(&daemonSuite{})
3350+
3351+type mockHandler struct {
3352+ cmd *Command
3353+ lastMethod string
3354+}
3355+
3356+func (mck *mockHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3357+ mck.lastMethod = r.Method
3358+}
3359+func (mck *mockHandler) Self(*Command, *http.Request) Response {
3360+ return mck
3361+}
3362+
3363+func mkRF(c *check.C, cmd *Command, mck *mockHandler) ResponseFunc {
3364+ return func(innerCmd *Command, req *http.Request) Response {
3365+ c.Assert(cmd, check.Equals, innerCmd)
3366+ return mck
3367+ }
3368+}
3369+
3370+func (s *daemonSuite) TestCommandMethodDispatch(c *check.C) {
3371+ cmd := &Command{}
3372+ mck := &mockHandler{cmd: cmd}
3373+ rf := mkRF(c, cmd, mck)
3374+ cmd.GET = rf
3375+ cmd.PUT = rf
3376+ cmd.POST = rf
3377+ cmd.DELETE = rf
3378+
3379+ for _, method := range []string{"GET", "POST", "PUT", "DELETE"} {
3380+ req, err := http.NewRequest(method, "", nil)
3381+ c.Assert(err, check.IsNil)
3382+ cmd.ServeHTTP(nil, req)
3383+ c.Check(mck.lastMethod, check.Equals, method)
3384+ }
3385+
3386+ req, err := http.NewRequest("POTATO", "", nil)
3387+ c.Assert(err, check.IsNil)
3388+ rec := httptest.NewRecorder()
3389+ cmd.ServeHTTP(rec, req)
3390+ c.Check(rec.Code, check.Equals, http.StatusMethodNotAllowed)
3391+}
3392+
3393+func (s *daemonSuite) TestAddRoutes(c *check.C) {
3394+ d := New()
3395+ d.addRoutes()
3396+
3397+ expected := make([]string, len(api))
3398+ for i, v := range api {
3399+ expected[i] = v.Path
3400+ }
3401+
3402+ got := make([]string, 0, len(api))
3403+ c.Assert(d.router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
3404+ got = append(got, route.GetName())
3405+ return nil
3406+ }), check.IsNil)
3407+
3408+ c.Check(got, check.DeepEquals, expected) // this'll stop being true if routes are added that aren't commands (e.g. for the favicon)
3409+
3410+ c.Check(fmt.Sprintf("%p", d.router.NotFoundHandler), check.Equals, fmt.Sprintf("%p", NotFound))
3411+}
3412
3413=== modified file 'daemon/response.go'
3414--- daemon/response.go 2015-09-15 13:47:08 +0000
3415+++ daemon/response.go 2015-10-12 09:23:01 +0000
3416@@ -39,9 +39,8 @@
3417 ResponseTypeError ResponseType = "error"
3418 )
3419
3420-// Response knows how to render itself, how to handle itself, and how to find itself
3421+// Response knows how to serve itself, and how to find itself
3422 type Response interface {
3423- Render(w http.ResponseWriter) ([]byte, int)
3424 ServeHTTP(w http.ResponseWriter, r *http.Request)
3425 Self(*Command, *http.Request) Response // has the same arity as ResponseFunc for convenience
3426 }
3427@@ -61,19 +60,15 @@
3428 })
3429 }
3430
3431-func (r *resp) Render(w http.ResponseWriter) (buf []byte, status int) {
3432+func (r *resp) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
3433+ status := r.Status
3434 bs, err := r.MarshalJSON()
3435 if err != nil {
3436 logger.Noticef("unable to marshal %#v to JSON: %v", *r, err)
3437- return nil, http.StatusInternalServerError
3438+ bs = nil
3439+ status = http.StatusInternalServerError
3440 }
3441
3442- return bs, r.Status
3443-}
3444-
3445-func (r *resp) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
3446- bs, status := r.Render(w)
3447-
3448 hdr := w.Header()
3449 if r.Type == ResponseTypeAsync {
3450 if m, ok := r.Result.(map[string]interface{}); ok {
3451@@ -151,15 +146,21 @@
3452 return r.SetError
3453 }
3454
3455+// A FileResponse 's ServeHTTP method serves the file
3456+type FileResponse string
3457+
3458+// Self from the Response interface
3459+func (f FileResponse) Self(*Command, *http.Request) Response { return f }
3460+
3461+// ServeHTTP from the Response interface
3462+func (f FileResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3463+ http.ServeFile(w, r, string(f))
3464+}
3465+
3466 // ErrorResponseFunc is a callable error Response.
3467 // So you can return e.g. InternalError, or InternalError(err, "something broke"), etc.
3468 type ErrorResponseFunc func(error, string, ...interface{}) Response
3469
3470-// Render the response
3471-func (f ErrorResponseFunc) Render(w http.ResponseWriter) ([]byte, int) {
3472- return f(nil, "").Render(w)
3473-}
3474-
3475 func (f ErrorResponseFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3476 f(nil, "").ServeHTTP(w, r)
3477 }
3478
3479=== modified file 'daemon/snappy_part_iface_test.go'
3480--- daemon/snappy_part_iface_test.go 2015-09-14 14:35:06 +0000
3481+++ daemon/snappy_part_iface_test.go 2015-10-12 09:23:01 +0000
3482@@ -83,8 +83,8 @@
3483 }
3484 return p.config, p.configErr
3485 }
3486-func (p *tP) SetActive(progress.Meter) error { return p.setActiveErr }
3487-func (p *tP) Frameworks() ([]string, error) { return p.frameworks, p.frameworksErr }
3488+func (p *tP) SetActive(bool, progress.Meter) error { return p.setActiveErr }
3489+func (p *tP) Frameworks() ([]string, error) { return p.frameworks, p.frameworksErr }
3490
3491 // for ServiceYamler interface:
3492 func (p *tP) ServiceYamls() []snappy.ServiceYaml { return p.svcYamls }
3493
3494=== added file 'data/failure.txt'
3495--- data/failure.txt 1970-01-01 00:00:00 +0000
3496+++ data/failure.txt 2015-10-12 09:23:01 +0000
3497@@ -0,0 +1,8 @@
3498+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
3499+░░░█▀▀░█▀▄░█░█░█▀▀░█░█░▀█▀░█▀█░█▀▀░░░█▀▀░█▀█░▀█▀░█░░░█░█░█▀▄░█▀▀░░░
3500+░░░█░░░█▀▄░█░█░▀▀█░█▀█░░█░░█░█░█░█░░░█▀▀░█▀█░░█░░█░░░█░█░█▀▄░█▀▀░░░
3501+░░░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀░▀░▀▀▀░░░▀░░░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░░░
3502+░░░░░░░░░░░░░█▀█░█▀█░█▀▄░░░█▀▄░█▀▀░█▀▀░█▀█░█▀█░▀█▀░█▀▄░░░░░░░░░░░░░
3503+░░░░░░░░░░░░░█▀█░█░█░█░█░░░█░█░█▀▀░▀▀█░█▀▀░█▀█░░█░░█▀▄░░░░░░░░░░░░░
3504+░░░░░░░░░░░░░▀░▀░▀░▀░▀▀░░░░▀▀░░▀▀▀░▀▀▀░▀░░░▀░▀░▀▀▀░▀░▀░░░░░░░░░░░░░
3505+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
3506
3507=== added file 'data/success.txt'
3508--- data/success.txt 1970-01-01 00:00:00 +0000
3509+++ data/success.txt 2015-10-12 09:23:01 +0000
3510@@ -0,0 +1,20 @@
3511+ ▒██▒ ████ ████ ██
3512+ ▓██▓ ████ ████ ██
3513+ ████ ██ ██ ██
3514+ ████ ██ ██ ▒███▒██ ░████░ ░████░ ▒███░██
3515+ ▒█▓▓█▒ ██ ██ ░███████ ░██████░ ░██████░ ▒███████
3516+ ▓█▒▒█▓ ██ ██ ███ ███ ███ ███ ███ ███ ███ ███
3517+ ██ ██ ██ ██ ██░ ░██ ██░ ░██ ██░ ░██ ██░ ░██
3518+ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
3519+ ░██████░ ██ ██ ██░ ░██ ██░ ░██ ██░ ░██ ██░ ░██
3520+ ▒██ ██▒ ██▒ ██▒ ███ ███ ███ ███ ███ ███ ███ ███
3521+ ███ ███ █████ █████ ░███████ ░██████░ ░██████░ ▒███████
3522+ ██▒ ▒██ ░████ ░████ ▒███▒██ ░████░ ░████░ ▒███░██
3523+ █░ ▒██
3524+ ██████▓
3525+ ▒████▒
3526+
3527+ ▌ ▐ ▜ ▌ ▗ ▌ ▜
3528+ ▌ ▌▛▀▖▝▀▖▜▀ ▞▀▖▞▀▖▌ ▌▐ ▞▀▌ ▛▀▖▞▀▖▞▀▘▞▀▘▄ ▛▀▖▐ ▌ ▌ ▞▀▌▞▀▖ ▌ ▌▙▀▖▞▀▖▛▀▖▞▀▌
3529+ ▐▐▐ ▌ ▌▞▀▌▐ ▖ ▌ ▖▌ ▌▌ ▌▐ ▌ ▌ ▙▄▘▌ ▌▝▀▖▝▀▖▐ ▌ ▌▐ ▚▄▌ ▚▄▌▌ ▌ ▐▐▐ ▌ ▌ ▌▌ ▌▚▄▌
3530+ ▘▘ ▘ ▘▝▀▘ ▀ ▝▀ ▝▀ ▝▀▘ ▘▝▀▘ ▌ ▝▀ ▀▀ ▀▀ ▀▘▀▀ ▘▗▄▘ ▗▄▘▝▀ ▘▘ ▘ ▝▀ ▘ ▘▗▄▘
3531
3532=== modified file 'debian/control'
3533--- debian/control 2015-09-22 05:52:46 +0000
3534+++ debian/control 2015-10-12 09:23:01 +0000
3535@@ -24,7 +24,8 @@
3536 golang-go-systemd-dev,
3537 lsb-release,
3538 python3,
3539- python3-markdown
3540+ python3-markdown,
3541+ squashfs-tools
3542 Standards-Version: 3.9.6
3543 Homepage: https://launchpad.net/snappy
3544 Vcs-Browser: http://bazaar.launchpad.net/~snappy-dev/snappy/trunk/files
3545@@ -39,6 +40,7 @@
3546 Package: ubuntu-snappy
3547 Architecture: all
3548 Depends: debsig-verify,
3549+ squashfs-tools,
3550 system-image-cli (>= 3.0),
3551 ubuntu-snappy-cli (= ${binary:Version}),
3552 ubuntu-core-upgrader,
3553
3554=== modified file 'debian/ubuntu-snappy.snapd.socket'
3555--- debian/ubuntu-snappy.snapd.socket 2015-09-16 22:01:49 +0000
3556+++ debian/ubuntu-snappy.snapd.socket 2015-10-12 09:23:01 +0000
3557@@ -2,7 +2,11 @@
3558 Description=Socket activation for snappy daemon
3559
3560 [Socket]
3561-ListenStream=@snapd
3562+ListenStream=/run/snapd.socket
3563+SocketMode=0600
3564+# these are the defaults, but can't hurt to specify them anyway:
3565+SocketUser=root
3566+SocketGroup=root
3567
3568 [Install]
3569 WantedBy=sockets.target
3570
3571=== added directory 'dirs'
3572=== renamed file 'snappy/dirs.go' => 'dirs/dirs.go'
3573--- snappy/dirs.go 2015-06-30 12:36:00 +0000
3574+++ dirs/dirs.go 2015-10-12 09:23:01 +0000
3575@@ -17,58 +17,58 @@
3576 *
3577 */
3578
3579-package snappy
3580+package dirs
3581
3582 import "path/filepath"
3583
3584 // the various file paths
3585 var (
3586- globalRootDir string
3587-
3588- snapAppsDir string
3589- snapOemDir string
3590- snapDataDir string
3591- snapDataHomeGlob string
3592- snapAppArmorDir string
3593- snapSeccompDir string
3594- snapUdevRulesDir string
3595- localeDir string
3596- snapIconsDir string
3597- snapMetaDir string
3598-
3599- snapBinariesDir string
3600- snapServicesDir string
3601- snapBusPolicyDir string
3602-
3603- clickSystemHooksDir string
3604- cloudMetaDataFile string
3605+ GlobalRootDir string
3606+
3607+ SnapAppsDir string
3608+ SnapOemDir string
3609+ SnapDataDir string
3610+ SnapDataHomeGlob string
3611+ SnapAppArmorDir string
3612+ SnapSeccompDir string
3613+ SnapUdevRulesDir string
3614+ LocaleDir string
3615+ SnapIconsDir string
3616+ SnapMetaDir string
3617+
3618+ SnapBinariesDir string
3619+ SnapServicesDir string
3620+ SnapBusPolicyDir string
3621+
3622+ ClickSystemHooksDir string
3623+ CloudMetaDataFile string
3624+
3625+ SnappyDir = filepath.Join("var", "lib", "snappy")
3626 )
3627
3628-var snappyDir = filepath.Join("var", "lib", "snappy")
3629-
3630 // SetRootDir allows settings a new global root directory, this is useful
3631 // for e.g. chroot operations
3632 func SetRootDir(rootdir string) {
3633- globalRootDir = rootdir
3634-
3635- snapAppsDir = filepath.Join(rootdir, "/apps")
3636- snapOemDir = filepath.Join(rootdir, "/oem")
3637- snapDataDir = filepath.Join(rootdir, "/var/lib/apps")
3638- snapDataHomeGlob = filepath.Join(rootdir, "/home/*/apps/")
3639- snapAppArmorDir = filepath.Join(rootdir, "/var/lib/apparmor/clicks")
3640- snapSeccompDir = filepath.Join(rootdir, snappyDir, "seccomp", "profiles")
3641- snapIconsDir = filepath.Join(rootdir, snappyDir, "icons")
3642- snapMetaDir = filepath.Join(rootdir, snappyDir, "meta")
3643-
3644- snapBinariesDir = filepath.Join(snapAppsDir, "bin")
3645- snapServicesDir = filepath.Join(rootdir, "/etc/systemd/system")
3646- snapBusPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/system.d")
3647-
3648- clickSystemHooksDir = filepath.Join(rootdir, "/usr/share/click/hooks")
3649-
3650- cloudMetaDataFile = filepath.Join(rootdir, "/var/lib/cloud/seed/nocloud-net/meta-data")
3651-
3652- snapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d")
3653-
3654- localeDir = filepath.Join(rootdir, "/usr/share/locale")
3655+ GlobalRootDir = rootdir
3656+
3657+ SnapAppsDir = filepath.Join(rootdir, "/apps")
3658+ SnapOemDir = filepath.Join(rootdir, "/oem")
3659+ SnapDataDir = filepath.Join(rootdir, "/var/lib/apps")
3660+ SnapDataHomeGlob = filepath.Join(rootdir, "/home/*/apps/")
3661+ SnapAppArmorDir = filepath.Join(rootdir, "/var/lib/apparmor/clicks")
3662+ SnapSeccompDir = filepath.Join(rootdir, SnappyDir, "seccomp", "profiles")
3663+ SnapIconsDir = filepath.Join(rootdir, SnappyDir, "icons")
3664+ SnapMetaDir = filepath.Join(rootdir, SnappyDir, "meta")
3665+
3666+ SnapBinariesDir = filepath.Join(SnapAppsDir, "bin")
3667+ SnapServicesDir = filepath.Join(rootdir, "/etc/systemd/system")
3668+ SnapBusPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/system.d")
3669+
3670+ ClickSystemHooksDir = filepath.Join(rootdir, "/usr/share/click/hooks")
3671+
3672+ CloudMetaDataFile = filepath.Join(rootdir, "/var/lib/cloud/seed/nocloud-net/meta-data")
3673+
3674+ SnapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d")
3675+
3676+ LocaleDir = filepath.Join(rootdir, "/usr/share/locale")
3677 }
3678
3679=== modified file 'gen-coverage.sh'
3680--- gen-coverage.sh 2015-06-09 13:02:49 +0000
3681+++ gen-coverage.sh 2015-10-12 09:23:01 +0000
3682@@ -8,23 +8,9 @@
3683 # pass alternative output dir in $1
3684 OUTPUTDIR=${1:-$(pwd)}
3685
3686-(cd snappy &&
3687- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-snappy.html)
3688-(cd partition &&
3689- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-partition.html)
3690-(cd logger &&
3691- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-logger.html)
3692-(cd helpers &&
3693- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-helpers.html)
3694-(cd coreconfig &&
3695- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-coreconfig.html)
3696-(cd clickdeb &&
3697- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-clickdeb.html)
3698-(cd priv &&
3699- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-priv.html)
3700-(cd release &&
3701- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-release.html)
3702-(cd oauth &&
3703- $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-release.html)
3704+for d in pkg/snapfs pkg/clickdeb snappy partition logger helpers coreconfig priv release oauth; do
3705+ (cd $d &&
3706+ $GOPATH/bin/gocov test | $GOPATH/bin/gocov-html > $OUTPUTDIR/cov-$(echo $d|sed -r 's#/#_#').html)
3707+done
3708
3709 echo "Coverage html reports are available in $OUTPUTDIR"
3710
3711=== modified file 'helpers/helpers.go'
3712--- helpers/helpers.go 2015-09-18 09:53:19 +0000
3713+++ helpers/helpers.go 2015-10-12 09:23:01 +0000
3714@@ -49,15 +49,16 @@
3715 }
3716
3717 // ChDir runs runs "f" inside the given directory
3718-func ChDir(newDir string, f func()) (err error) {
3719+func ChDir(newDir string, f func() error) (err error) {
3720 cwd, err := os.Getwd()
3721- os.Chdir(newDir)
3722+ if err != nil {
3723+ return err
3724+ }
3725+ if err := os.Chdir(newDir); err != nil {
3726+ return err
3727+ }
3728 defer os.Chdir(cwd)
3729- if err != nil {
3730- return err
3731- }
3732- f()
3733- return err
3734+ return f()
3735 }
3736
3737 // ExitCode extract the exit code from the error of a failed cmd.Run() or the
3738
3739=== modified file 'helpers/helpers_test.go'
3740--- helpers/helpers_test.go 2015-09-18 09:53:19 +0000
3741+++ helpers/helpers_test.go 2015-10-12 09:23:01 +0000
3742@@ -117,11 +117,26 @@
3743 cwd, err := os.Getwd()
3744 c.Assert(err, IsNil)
3745 c.Assert(cwd, Not(Equals), tmpdir)
3746- ChDir(tmpdir, func() {
3747+ ChDir(tmpdir, func() error {
3748 cwd, err := os.Getwd()
3749 c.Assert(err, IsNil)
3750 c.Assert(cwd, Equals, tmpdir)
3751- })
3752+ return err
3753+ })
3754+}
3755+
3756+func (ts *HTestSuite) TestChdirErrorNoDir(c *C) {
3757+ err := ChDir("random-dir-that-does-not-exist", func() error {
3758+ return nil
3759+ })
3760+ c.Assert(err, ErrorMatches, "chdir .*: no such file or directory")
3761+}
3762+
3763+func (ts *HTestSuite) TestChdirErrorFromFunc(c *C) {
3764+ err := ChDir("/", func() error {
3765+ return fmt.Errorf("meep")
3766+ })
3767+ c.Assert(err, ErrorMatches, "meep")
3768 }
3769
3770 func (ts *HTestSuite) TestExitCode(c *C) {
3771@@ -494,10 +509,11 @@
3772 err := ioutil.WriteFile(filepath.Join(tmpdir, canaryName), []byte(nil), canaryPerms)
3773 c.Assert(err, IsNil)
3774
3775- ChDir(tmpdir, func() {
3776+ ChDir(tmpdir, func() error {
3777 cmd := exec.Command("tar", "cvf", tarArchive, ".")
3778 _, err = cmd.CombinedOutput()
3779 c.Assert(err, IsNil)
3780+ return err
3781 })
3782
3783 // set crazy umask
3784
3785=== renamed directory 'clickdeb' => 'pkg/clickdeb'
3786=== modified file 'pkg/clickdeb/deb.go'
3787--- clickdeb/deb.go 2015-09-16 11:02:59 +0000
3788+++ pkg/clickdeb/deb.go 2015-10-12 09:23:01 +0000
3789@@ -155,6 +155,11 @@
3790 return d.file.Close()
3791 }
3792
3793+// Verify checks that the clickdeb is signed
3794+func (d *ClickDeb) Verify(allowUnauthenticated bool) error {
3795+ return Verify(d.Name(), allowUnauthenticated)
3796+}
3797+
3798 // ControlMember returns the content of the given control member file
3799 // (e.g. the content of the "manifest" file in the control.tar.gz ar member)
3800 func (d *ClickDeb) ControlMember(controlMember string) (content []byte, err error) {
3801
3802=== added directory 'pkg/lightweight'
3803=== added file 'pkg/lightweight/example_test.go'
3804--- pkg/lightweight/example_test.go 1970-01-01 00:00:00 +0000
3805+++ pkg/lightweight/example_test.go 2015-10-12 09:23:01 +0000
3806@@ -0,0 +1,58 @@
3807+// -*- Mode: Go; indent-tabs-mode: t -*-
3808+
3809+/*
3810+ * Copyright (C) 2014-2015 Canonical Ltd
3811+ *
3812+ * This program is free software: you can redistribute it and/or modify
3813+ * it under the terms of the GNU General Public License version 3 as
3814+ * published by the Free Software Foundation.
3815+ *
3816+ * This program is distributed in the hope that it will be useful,
3817+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3818+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3819+ * GNU General Public License for more details.
3820+ *
3821+ * You should have received a copy of the GNU General Public License
3822+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3823+ *
3824+ */
3825+
3826+package lightweight_test
3827+
3828+import (
3829+ "fmt"
3830+ "io/ioutil"
3831+ "os"
3832+ "path/filepath"
3833+
3834+ "launchpad.net/snappy/dirs"
3835+ "launchpad.net/snappy/pkg/lightweight"
3836+)
3837+
3838+// we don't use example tests nearly as often as we should :-)
3839+// https://blog.golang.org/examples if you haven't seen them used before.
3840+// (also: look at it in the 'go doc' output for this package)
3841+
3842+func ExamplePartBag() {
3843+ d, _ := ioutil.TempDir("", "test-xyzzy-")
3844+ defer os.RemoveAll(d)
3845+ dirs.SetRootDir(d)
3846+ os.MkdirAll(filepath.Join(dirs.SnapDataDir, "foo.bar", "0.1"), 0755)
3847+ os.MkdirAll(filepath.Join(dirs.SnapDataDir, "foo.bar", "0.2"), 0755)
3848+ os.MkdirAll(filepath.Join(dirs.SnapDataDir, "foo.bar", "0.5"), 0755)
3849+ os.MkdirAll(filepath.Join(dirs.SnapDataDir, "baz", "0.4"), 0755)
3850+ os.MkdirAll(filepath.Join(dirs.SnapDataDir, "qux", "0.5"), 0755)
3851+ os.MkdirAll(filepath.Join(dirs.SnapOemDir, "qux", "0.5"), 0755)
3852+
3853+ bags := lightweight.AllPartBags()
3854+
3855+ for _, k := range []string{"foo.bar", "baz", "qux"} {
3856+ bag := bags[k]
3857+ fmt.Printf("Found %d versions for %s, type %q: %s\n",
3858+ len(bag.Versions), bag.QualifiedName(), bag.Type, bag.Versions)
3859+ }
3860+ // Output:
3861+ // Found 3 versions for foo.bar, type "app": [0.5 0.2 0.1]
3862+ // Found 1 versions for baz, type "framework": [0.4]
3863+ // Found 1 versions for qux, type "oem": [0.5]
3864+}
3865
3866=== added file 'pkg/lightweight/lightweight.go'
3867--- pkg/lightweight/lightweight.go 1970-01-01 00:00:00 +0000
3868+++ pkg/lightweight/lightweight.go 2015-10-12 09:23:01 +0000
3869@@ -0,0 +1,527 @@
3870+// -*- Mode: Go; indent-tabs-mode: t -*-
3871+
3872+/*
3873+ * Copyright (C) 2014-2015 Canonical Ltd
3874+ *
3875+ * This program is free software: you can redistribute it and/or modify
3876+ * it under the terms of the GNU General Public License version 3 as
3877+ * published by the Free Software Foundation.
3878+ *
3879+ * This program is distributed in the hope that it will be useful,
3880+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3881+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3882+ * GNU General Public License for more details.
3883+ *
3884+ * You should have received a copy of the GNU General Public License
3885+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3886+ *
3887+ */
3888+
3889+// Package lightweight provides a quick way of loading things that can become snaps.
3890+//
3891+// A lightweight.PartBag has a name and n versions; it might not even know its origin.
3892+package lightweight
3893+
3894+import (
3895+ "errors"
3896+ "fmt"
3897+ "os"
3898+ "path/filepath"
3899+ "sort"
3900+ "strconv"
3901+ "strings"
3902+
3903+ "launchpad.net/snappy/dirs"
3904+ "launchpad.net/snappy/helpers"
3905+ "launchpad.net/snappy/pkg"
3906+ "launchpad.net/snappy/pkg/removed"
3907+ "launchpad.net/snappy/snappy"
3908+)
3909+
3910+// split a path into the name and extension of the directory, and the file.
3911+// e.g. foo/bar.baz/quux -> bar, baz, quux
3912+//
3913+// panics if given path is lacking at least one separator (ie bar/quux
3914+// works (barely); quux panics). As it's supposed to be called with
3915+// the results of a glob on <pkgdir>/<pkg|*>/*, this is only a problem
3916+// if it's being used wrong.
3917+func split(path string) (name string, ext string, file string) {
3918+ const sep = string(os.PathSeparator)
3919+ idxFileSep := strings.LastIndex(path, sep)
3920+ if idxFileSep < 0 {
3921+ panic("bad path given to split: must have at least two separators")
3922+ }
3923+
3924+ file = path[idxFileSep+1:]
3925+ path = path[:idxFileSep]
3926+ name = path
3927+
3928+ idxDirSep := strings.LastIndex(path, sep)
3929+ if idxDirSep > -1 {
3930+ name = path[idxDirSep+1:]
3931+ }
3932+
3933+ idxOrig := strings.LastIndex(name, ".")
3934+ if idxOrig < 0 {
3935+ return name, "", file
3936+ }
3937+
3938+ return name[:idxOrig], name[idxOrig+1:], file
3939+}
3940+
3941+// extract the name, origin and list of versions from a list of paths that
3942+// end {name}[.{origin}]/{version}. If the name or origin changes, stop and
3943+// return the versions so far, and the remaining paths.
3944+//
3945+// Calls split() on each path in paths, so will panic if it does not have
3946+// the right number of path separators, as it's a programming error.
3947+func extract(paths []string) (string, string, []string, []string) {
3948+ name, origin, _ := split(paths[0])
3949+
3950+ var versions []string
3951+ for len(paths) > 0 {
3952+ n, o, v := split(paths[0])
3953+ if name != n || origin != o {
3954+ break
3955+ }
3956+ paths = paths[1:]
3957+
3958+ if v == "current" {
3959+ continue
3960+ }
3961+
3962+ versions = append(versions, v)
3963+ }
3964+
3965+ return name, origin, versions, paths
3966+}
3967+
3968+func versionSort(versions []string) {
3969+ sort.Sort(sort.Reverse(snappy.ByVersion(versions)))
3970+}
3971+
3972+// PartBagByName finds a PartBag with the given name.
3973+func PartBagByName(name string, origin string) *PartBag {
3974+ if strings.ContainsAny(name, ".*?/") || strings.ContainsAny(origin, ".*?/") {
3975+ panic("invalid name " + name + "." + origin)
3976+ }
3977+
3978+ for _, v := range find(name, origin) {
3979+ return v
3980+ }
3981+
3982+ return nil
3983+}
3984+
3985+// AllPartBags the PartBags in the system.
3986+func AllPartBags() map[string]*PartBag {
3987+ return find("*", "*")
3988+}
3989+
3990+type repo interface {
3991+ All() ([]snappy.Part, error)
3992+}
3993+
3994+func newCoreRepoImpl() repo {
3995+ return snappy.NewSystemImageRepository()
3996+}
3997+
3998+var newCoreRepo = newCoreRepoImpl
3999+
4000+func find(name string, origin string) map[string]*PartBag {
4001+ bags := make(map[string]*PartBag)
4002+
4003+ if (name == snappy.SystemImagePartName || name == "*") && (origin == snappy.SystemImagePartOrigin || origin == "*") {
4004+ // TODO: make this do less work
4005+ repo := newCoreRepo()
4006+ parts, err := repo.All()
4007+ if err != nil {
4008+ // can't really happen
4009+ panic(fmt.Sprintf("Bad SystemImageRepository: %v", err))
4010+ }
4011+
4012+ // parts can be empty during testing for example
4013+ if len(parts) > 0 {
4014+ versions := make([]string, len(parts))
4015+ for i, part := range parts {
4016+ versions[i] = part.Version()
4017+ }
4018+ versionSort(versions)
4019+
4020+ bag := &PartBag{
4021+ Name: snappy.SystemImagePartName,
4022+ Origin: snappy.SystemImagePartOrigin,
4023+ Type: pkg.TypeCore,
4024+ Versions: versions,
4025+ concrete: &concreteCore{},
4026+ }
4027+ bags[bag.QualifiedName()] = bag
4028+ }
4029+ }
4030+
4031+ type T struct {
4032+ inst string
4033+ qn string
4034+ typ pkg.Type
4035+ }
4036+
4037+ for _, s := range []T{
4038+ {dirs.SnapAppsDir, name + "." + origin, pkg.TypeApp},
4039+ {dirs.SnapAppsDir, name, pkg.TypeFramework}, // frameworks are installed under /apps also, for now
4040+ } {
4041+ // all snaps share the data dir, hence this bit of mess
4042+ paths, _ := filepath.Glob(filepath.Join(dirs.SnapDataDir, s.qn, "*"))
4043+ for len(paths) > 0 {
4044+ var name string
4045+ var origin string
4046+ var versions []string
4047+
4048+ name, origin, versions, paths = extract(paths)
4049+ if origin != "" && s.typ != pkg.TypeApp {
4050+ // this happens when called with name="*"
4051+ continue
4052+ }
4053+
4054+ versionSort(versions)
4055+
4056+ // if oems were removable, there'd be know way of
4057+ // telling the kind of a removed origin-less package
4058+ if s.typ == pkg.TypeFramework && helpers.FileExists(filepath.Join(dirs.SnapOemDir, name)) {
4059+ s.typ = pkg.TypeOem
4060+ s.inst = dirs.SnapOemDir
4061+ }
4062+
4063+ bag := &PartBag{
4064+ Name: name,
4065+ Origin: origin,
4066+ Type: s.typ,
4067+ Versions: versions,
4068+ }
4069+
4070+ bag.concrete = NewConcrete(bag, s.inst)
4071+
4072+ bags[bag.QualifiedName()] = bag
4073+ }
4074+ }
4075+
4076+ return bags
4077+}
4078+
4079+// A PartBag is a lightweight object that represents and knows how to
4080+// load a Part on demand.
4081+type PartBag struct {
4082+ Name string
4083+ Origin string
4084+ Type pkg.Type
4085+ Versions []string
4086+ concrete Concreter
4087+}
4088+
4089+// Concreter hides the part-specific details of PartBags
4090+type Concreter interface {
4091+ IsInstalled(string) bool
4092+ ActiveIndex() int
4093+ Load(string) (snappy.Part, error)
4094+}
4095+
4096+// NewConcrete is meant to be overridden in tests; is called when
4097+// needing a Concreter for app/fmk/oem snaps (ie not core).
4098+var NewConcrete = newConcreteImpl
4099+
4100+func newConcreteImpl(bag *PartBag, instdir string) Concreter {
4101+ return &concreteSnap{
4102+ self: bag,
4103+ instdir: instdir,
4104+ }
4105+}
4106+
4107+// QualifiedName of the PartBag.
4108+//
4109+// because PartBags read their origin from the filesystem, you don't need
4110+// to check the pacakge type.
4111+func (bag *PartBag) QualifiedName() string {
4112+ if bag.Origin == "" {
4113+ return bag.Name
4114+ }
4115+ return bag.FullName()
4116+}
4117+
4118+// FullName of the PartBag
4119+func (bag *PartBag) FullName() string {
4120+ return bag.Name + "." + bag.Origin
4121+}
4122+
4123+var (
4124+ // ErrBadVersionIndex is returned by Load when asked to load a
4125+ // non-existent version.
4126+ ErrBadVersionIndex = errors.New("Bad version index")
4127+ // ErrVersionGone is returned in the case where we find a
4128+ // version and it disappears before we get to load it.
4129+ ErrVersionGone = errors.New("Version gone")
4130+)
4131+
4132+type concreteCore struct{}
4133+
4134+func (*concreteCore) IsInstalled(string) bool { return true }
4135+func (*concreteCore) ActiveIndex() int { return 0 }
4136+func (*concreteCore) Load(version string) (snappy.Part, error) {
4137+ parts, err := newCoreRepo().All()
4138+ if err != nil {
4139+ // can't really happen
4140+ return nil, fmt.Errorf("Bad SystemImageRepository: %v", err)
4141+ }
4142+
4143+ for _, part := range parts {
4144+ if part.Version() == version {
4145+ return part, nil
4146+ }
4147+ }
4148+
4149+ return nil, ErrVersionGone
4150+}
4151+
4152+type concreteSnap struct {
4153+ self *PartBag
4154+ instdir string
4155+}
4156+
4157+func (c *concreteSnap) IsInstalled(version string) bool {
4158+ return helpers.FileExists(filepath.Join(c.instdir, c.self.QualifiedName(), version, "meta", "package.yaml"))
4159+}
4160+
4161+func (c *concreteSnap) ActiveIndex() int {
4162+ current, err := os.Readlink(filepath.Join(c.instdir, c.self.QualifiedName(), "current"))
4163+ if err != nil {
4164+ return -1
4165+ }
4166+
4167+ current = filepath.Base(current)
4168+
4169+ // Linear search is fine for now.
4170+ //
4171+ // If it ever becomes a problem, remember bag.Versions is sorted
4172+ // so you can use go's sort.Search and snappy.VersionCompare,
4173+ // but VersionCompare is not cheap, so that (on my machine, at
4174+ // the time of writing) linear of even 100k versions is only
4175+ // 2×-3× slower than binary; anything below about 50k versions
4176+ // is faster even in worst-case for linear (no match). And
4177+ // that's not even looking at memory impact.
4178+ //
4179+ // For example, on the ~90k lines in /usr/share/dict/words:
4180+ //
4181+ // BenchmarkBinaryPositive-4 10000 135041 ns/op 11534 B/op 316 allocs/op
4182+ // BenchmarkBinaryNegative-4 10000 109803 ns/op 10235 B/op 231 allocs/op
4183+ // BenchmarkLinearPositive-4 5000 272980 ns/op 0 B/op 0 allocs/op
4184+ // BenchmarkLinearNegative-4 5000 362244 ns/op 0 B/op 0 allocs/op
4185+ //
4186+ // on a corpus of 100k %016x-formatted rand.Int63()s:
4187+ //
4188+ // BenchmarkBinaryNegative-4 10000 152797 ns/op 10158 B/op 243 allocs/op
4189+ // BenchmarkLinearNegative-4 10000 163924 ns/op 0 B/op 0 allocs/op
4190+ //
4191+ // ... I think I might need help.
4192+ for i := range c.self.Versions {
4193+ if c.self.Versions[i] == current {
4194+ return i
4195+ }
4196+ }
4197+
4198+ return -1
4199+}
4200+
4201+func (c *concreteSnap) Load(version string) (snappy.Part, error) {
4202+ yamlPath := filepath.Join(c.instdir, c.self.QualifiedName(), version, "meta", "package.yaml")
4203+ if !helpers.FileExists(yamlPath) {
4204+ return removed.New(c.self.Name, c.self.Origin, version, c.self.Type), nil
4205+ }
4206+
4207+ part, err := snappy.NewInstalledSnapPart(yamlPath, c.self.Origin)
4208+ if err != nil {
4209+ return nil, err
4210+ }
4211+
4212+ return part, nil
4213+}
4214+
4215+// IsInstalled checks whether the given part is installed
4216+func (bag *PartBag) IsInstalled(idx int) bool {
4217+ if idx < 0 || idx >= len(bag.Versions) {
4218+ return false
4219+ }
4220+
4221+ return bag.concrete.IsInstalled(bag.Versions[idx])
4222+}
4223+
4224+// ActiveIndex returns the index of the active version, or -1
4225+func (bag *PartBag) ActiveIndex() int {
4226+ if bag == nil || len(bag.Versions) == 0 {
4227+ return -1
4228+ }
4229+
4230+ return bag.concrete.ActiveIndex()
4231+}
4232+
4233+// Load a Part from the PartBag
4234+func (bag *PartBag) Load(versionIdx int) (snappy.Part, error) {
4235+ if bag == nil {
4236+ return nil, nil
4237+ }
4238+
4239+ if versionIdx < 0 || versionIdx >= len(bag.Versions) {
4240+ return nil, ErrBadVersionIndex
4241+ }
4242+
4243+ version := bag.Versions[versionIdx]
4244+
4245+ return bag.concrete.Load(version)
4246+}
4247+
4248+// LoadActive gets the active index and loads it.
4249+// If none active, returns a nil Part and ErrBadVersionIndex.
4250+func (bag *PartBag) LoadActive() (snappy.Part, error) {
4251+ return bag.Load(bag.ActiveIndex())
4252+}
4253+
4254+// LoadBest looks for the best candidate Part and loads it.
4255+//
4256+// If there is an active part, load that. Otherwise, load the
4257+// highest-versioned installed part. Otherwise, load the first removed
4258+// part.
4259+//
4260+// If not even a removed part can be loaded, something is wrong. Nil
4261+// is returned, but you're in trouble (did the filesystem just
4262+// disappear under us?).
4263+func (bag *PartBag) LoadBest() snappy.Part {
4264+ if bag == nil {
4265+ return nil
4266+ }
4267+ if len(bag.Versions) == 0 {
4268+ return nil
4269+ }
4270+
4271+ activeIdx := bag.ActiveIndex()
4272+ if part, err := bag.Load(activeIdx); err == nil {
4273+ return part
4274+ }
4275+
4276+ for i := 0; i < len(bag.Versions); i++ {
4277+ if bag.IsInstalled(i) {
4278+ if part, err := bag.Load(i); err == nil {
4279+ return part
4280+ }
4281+ }
4282+ }
4283+
4284+ part, _ := bag.Load(0)
4285+
4286+ return part
4287+}
4288+
4289+// Map this PartBag into a map[string]string, augmenting it with the
4290+// given (purportedly remote) Part.
4291+//
4292+// It is a programming error (->panic) to call Map on a nil *PartBag with
4293+// a nil Part. PartBag or part may be nil, but not both.
4294+//
4295+// Also may panic if the remote part is nil and LoadBest can't load a
4296+// Part at all.
4297+func (bag *PartBag) Map(remotePart snappy.Part) map[string]string {
4298+ var version, update, rollback, icon, name, origin, _type, vendor, description string
4299+
4300+ if bag == nil && remotePart == nil {
4301+ panic("part bag & part both nil -- how did i even get here")
4302+ }
4303+
4304+ status := "not installed"
4305+ installedSize := "-1"
4306+ downloadSize := "-1"
4307+
4308+ part := bag.LoadBest()
4309+ if part != nil {
4310+ if part.IsActive() {
4311+ status = "active"
4312+ } else if part.IsInstalled() {
4313+ status = "installed"
4314+ } else {
4315+ status = "removed"
4316+ }
4317+ } else if remotePart == nil {
4318+ panic("unable to load a valid part")
4319+ }
4320+
4321+ if part != nil {
4322+ name = part.Name()
4323+ origin = part.Origin()
4324+ version = part.Version()
4325+ _type = string(part.Type())
4326+
4327+ icon = part.Icon()
4328+ vendor = part.Vendor()
4329+ description = part.Description()
4330+ installedSize = strconv.FormatInt(part.InstalledSize(), 10)
4331+
4332+ downloadSize = strconv.FormatInt(part.DownloadSize(), 10)
4333+ } else {
4334+ name = remotePart.Name()
4335+ origin = remotePart.Origin()
4336+ version = remotePart.Version()
4337+ _type = string(remotePart.Type())
4338+ }
4339+
4340+ if remotePart != nil {
4341+ if icon == "" {
4342+ icon = remotePart.Icon()
4343+ }
4344+ if description == "" {
4345+ description = remotePart.Description()
4346+ }
4347+ if vendor == "" {
4348+ vendor = remotePart.Vendor()
4349+ }
4350+
4351+ downloadSize = strconv.FormatInt(remotePart.DownloadSize(), 10)
4352+ }
4353+
4354+ if activeIdx := bag.ActiveIndex(); activeIdx >= 0 {
4355+ if remotePart != nil && version != remotePart.Version() {
4356+ // XXX: this does not handle the case where the
4357+ // one in the store is not the greatest version
4358+ // (e.g.: store has 1.1, locally available 1.1,
4359+ // 1.2, active 1.2)
4360+ update = remotePart.Version()
4361+ }
4362+
4363+ for i := activeIdx + 1; i < len(bag.Versions); i++ {
4364+ // XXX: it's also possible to "roll back" to a
4365+ // store version in the case mentioned above;
4366+ // also not covered by this code.
4367+ if bag.IsInstalled(i) {
4368+ rollback = bag.Versions[i]
4369+ break
4370+ }
4371+ }
4372+ }
4373+
4374+ result := map[string]string{
4375+ "icon": icon,
4376+ "name": name,
4377+ "origin": origin,
4378+ "status": status,
4379+ "type": _type,
4380+ "vendor": vendor,
4381+ "version": version,
4382+ "description": description,
4383+ "installed_size": installedSize,
4384+ "download_size": downloadSize,
4385+ }
4386+
4387+ if rollback != "" {
4388+ result["rollback_available"] = rollback
4389+ }
4390+
4391+ if update != "" {
4392+ result["update_available"] = update
4393+ }
4394+
4395+ return result
4396+}
4397
4398=== added file 'pkg/lightweight/lightweight_test.go'
4399--- pkg/lightweight/lightweight_test.go 1970-01-01 00:00:00 +0000
4400+++ pkg/lightweight/lightweight_test.go 2015-10-12 09:23:01 +0000
4401@@ -0,0 +1,417 @@
4402+// -*- Mode: Go; indent-tabs-mode: t -*-
4403+
4404+/*
4405+ * Copyright (C) 2014-2015 Canonical Ltd
4406+ *
4407+ * This program is free software: you can redistribute it and/or modify
4408+ * it under the terms of the GNU General Public License version 3 as
4409+ * published by the Free Software Foundation.
4410+ *
4411+ * This program is distributed in the hope that it will be useful,
4412+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4413+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4414+ * GNU General Public License for more details.
4415+ *
4416+ * You should have received a copy of the GNU General Public License
4417+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4418+ *
4419+ */
4420+
4421+package lightweight
4422+
4423+import (
4424+ "fmt"
4425+ "io/ioutil"
4426+ "os"
4427+ "path/filepath"
4428+ "testing"
4429+
4430+ "gopkg.in/check.v1"
4431+ "gopkg.in/yaml.v2"
4432+
4433+ "launchpad.net/snappy/dirs"
4434+ "launchpad.net/snappy/pkg"
4435+ "launchpad.net/snappy/pkg/remote"
4436+ "launchpad.net/snappy/pkg/removed"
4437+ "launchpad.net/snappy/snappy"
4438+)
4439+
4440+type lightweightSuite struct {
4441+ d string
4442+}
4443+
4444+func Test(t *testing.T) { check.TestingT(t) }
4445+
4446+var _ = check.Suite(&lightweightSuite{})
4447+
4448+func (s *lightweightSuite) SetUpTest(c *check.C) {
4449+ s.d = c.MkDir()
4450+ dirs.SetRootDir(s.d)
4451+
4452+ s.MkInstalled(c, pkg.TypeApp, dirs.SnapAppsDir, "foo", "bar", "1.0", true)
4453+ s.MkRemoved(c, "foo.bar", "0.9")
4454+ s.MkRemoved(c, "foo.baz", "0.8")
4455+
4456+ s.MkInstalled(c, pkg.TypeFramework, dirs.SnapAppsDir, "fmk", "", "123", false)
4457+ s.MkInstalled(c, pkg.TypeFramework, dirs.SnapAppsDir, "fmk", "", "120", true)
4458+ s.MkInstalled(c, pkg.TypeFramework, dirs.SnapAppsDir, "fmk", "", "119", false)
4459+ s.MkRemoved(c, "fmk", "12a1")
4460+
4461+ s.MkRemoved(c, "fmk2", "4.2.0ubuntu1")
4462+
4463+ s.MkInstalled(c, pkg.TypeOem, dirs.SnapOemDir, "oem", "", "3", false)
4464+
4465+ newCoreRepo = func() repo {
4466+ // you can't ever have a removed systemimagepart, but for testing it'll do
4467+ return mockrepo{removed.New(snappy.SystemImagePartName, snappy.SystemImagePartOrigin, "1", pkg.TypeCore)}
4468+ }
4469+}
4470+
4471+func (s *lightweightSuite) TearDownTest(c *check.C) {
4472+ newCoreRepo = newCoreRepoImpl
4473+}
4474+
4475+func (s *lightweightSuite) MkInstalled(c *check.C, _type pkg.Type, appdir, name, origin, version string, active bool) {
4476+ qn := name
4477+ if origin != "" {
4478+ qn += "." + origin
4479+ }
4480+
4481+ s.MkRemoved(c, qn, version)
4482+
4483+ apath := filepath.Join(appdir, qn, version, "meta")
4484+ yaml := fmt.Sprintf("name: %s\nversion: %s\nvendor: example.com\nicon: icon.png\ntype: %s\n", name, version, _type)
4485+ c.Check(os.MkdirAll(apath, 0755), check.IsNil)
4486+ c.Check(ioutil.WriteFile(filepath.Join(apath, "package.yaml"), []byte(yaml), 0644), check.IsNil)
4487+ c.Check(ioutil.WriteFile(filepath.Join(apath, "hashes.yaml"), nil, 0644), check.IsNil)
4488+
4489+ if active {
4490+ c.Check(os.Symlink(version, filepath.Join(appdir, qn, "current")), check.IsNil)
4491+ c.Check(os.Symlink(version, filepath.Join(dirs.SnapDataDir, qn, "current")), check.IsNil)
4492+ }
4493+}
4494+
4495+func (s *lightweightSuite) MkRemoved(c *check.C, qn, version string) {
4496+ dpath := filepath.Join(dirs.SnapDataDir, qn, version)
4497+ c.Check(os.MkdirAll(dpath, 0755), check.IsNil)
4498+ c.Check(ioutil.WriteFile(filepath.Join(dpath, "test.txt"), []byte("hello there\n"), 0644), check.IsNil)
4499+
4500+}
4501+
4502+func (s *lightweightSuite) TestLoadBadName(c *check.C) {
4503+ c.Check(func() { PartBagByName("*", "*") }, check.PanicMatches, "invalid name .*")
4504+}
4505+
4506+func (s *lightweightSuite) TestMapFmkNoPart(c *check.C) {
4507+ bag := PartBagByName("fmk", "sideload")
4508+ m := bag.Map(nil)
4509+ c.Check(m["installed_size"], check.Matches, "[0-9]+")
4510+ delete(m, "installed_size")
4511+ c.Check(m, check.DeepEquals, map[string]string{
4512+ "name": "fmk",
4513+ "origin": "sideload",
4514+ "status": "active",
4515+ "version": "120",
4516+ "icon": filepath.Join(s.d, "apps", "fmk", "120", "icon.png"),
4517+ "type": "framework",
4518+ "vendor": "example.com",
4519+ "download_size": "-1",
4520+ "description": "",
4521+ "rollback_available": "119",
4522+ })
4523+}
4524+
4525+func (s *lightweightSuite) TestMapRemovedFmkNoPart(c *check.C) {
4526+ bag := PartBagByName("fmk2", "sideload")
4527+ m := bag.Map(nil)
4528+ c.Check(m, check.DeepEquals, map[string]string{
4529+ "name": "fmk2",
4530+ "origin": "sideload",
4531+ "status": "removed",
4532+ "version": "4.2.0ubuntu1",
4533+ "icon": "",
4534+ "type": "framework",
4535+ "vendor": "",
4536+ "installed_size": "-1",
4537+ "download_size": "-1",
4538+ "description": "",
4539+ })
4540+}
4541+
4542+func (s *lightweightSuite) TestMapRemovedFmkNoPartButStoreMeta(c *check.C) {
4543+ snap := remote.Snap{
4544+ Name: "fmk2",
4545+ Origin: "fmk2origin",
4546+ Version: "4.2.0ubuntu1",
4547+ Type: pkg.TypeFramework,
4548+ IconURL: "http://example.com/icon",
4549+ DownloadSize: 42,
4550+ Publisher: "Example Inc.",
4551+ }
4552+ part := snappy.NewRemoteSnapPart(snap)
4553+
4554+ content, err := yaml.Marshal(snap)
4555+ c.Assert(err, check.IsNil)
4556+
4557+ p := snappy.RemoteManifestPath(part)
4558+ c.Assert(os.MkdirAll(filepath.Dir(p), 0755), check.IsNil)
4559+ c.Assert(ioutil.WriteFile(p, content, 0644), check.IsNil)
4560+
4561+ bag := PartBagByName("fmk2", "fmk2origin")
4562+ m := bag.Map(nil)
4563+ c.Check(m, check.DeepEquals, map[string]string{
4564+ "name": "fmk2",
4565+ "origin": "fmk2origin",
4566+ "status": "removed",
4567+ "version": "4.2.0ubuntu1",
4568+ "icon": "http://example.com/icon",
4569+ "type": "framework",
4570+ "vendor": "Example Inc.",
4571+ "installed_size": "-1",
4572+ "download_size": "42",
4573+ "description": "",
4574+ })
4575+}
4576+
4577+func (s *lightweightSuite) TestMapAppNoPart(c *check.C) {
4578+ bag := PartBagByName("foo", "bar")
4579+ m := bag.Map(nil)
4580+ c.Check(m["installed_size"], check.Matches, "[0-9]+")
4581+ delete(m, "installed_size")
4582+ c.Check(m, check.DeepEquals, map[string]string{
4583+ "name": "foo",
4584+ "origin": "bar",
4585+ "status": "active",
4586+ "version": "1.0",
4587+ "icon": filepath.Join(s.d, "apps", "foo.bar", "1.0", "icon.png"),
4588+ "type": "app",
4589+ "vendor": "example.com",
4590+ "download_size": "-1",
4591+ "description": "",
4592+ })
4593+}
4594+
4595+func (s *lightweightSuite) TestMapAppWithPart(c *check.C) {
4596+ snap := remote.Snap{
4597+ Name: "foo",
4598+ Origin: "bar",
4599+ Version: "2",
4600+ Type: pkg.TypeApp,
4601+ IconURL: "http://example.com/icon",
4602+ DownloadSize: 42,
4603+ }
4604+ part := snappy.NewRemoteSnapPart(snap)
4605+
4606+ bag := PartBagByName("foo", "bar")
4607+ m := bag.Map(part)
4608+ c.Check(m["installed_size"], check.Matches, "[0-9]+")
4609+ delete(m, "installed_size")
4610+ c.Check(m, check.DeepEquals, map[string]string{
4611+ "name": "foo",
4612+ "origin": "bar",
4613+ "status": "active",
4614+ "version": "1.0",
4615+ "icon": filepath.Join(s.d, "apps", "foo.bar", "1.0", "icon.png"),
4616+ "type": "app",
4617+ "vendor": "example.com",
4618+ "download_size": "42",
4619+ "description": "",
4620+ "update_available": "2",
4621+ })
4622+}
4623+
4624+func (s *lightweightSuite) TestMapAppNoPartBag(c *check.C) {
4625+ snap := remote.Snap{
4626+ Name: "foo",
4627+ Origin: "bar",
4628+ Version: "2",
4629+ Type: pkg.TypeApp,
4630+ IconURL: "http://example.com/icon",
4631+ Publisher: "example.com",
4632+ DownloadSize: 42,
4633+ }
4634+ part := snappy.NewRemoteSnapPart(snap)
4635+
4636+ m := (*PartBag)(nil).Map(part)
4637+ c.Check(m, check.DeepEquals, map[string]string{
4638+ "name": "foo",
4639+ "origin": "bar",
4640+ "status": "not installed",
4641+ "version": "2",
4642+ "icon": snap.IconURL,
4643+ "type": "app",
4644+ "vendor": "example.com",
4645+ "installed_size": "-1",
4646+ "download_size": "42",
4647+ "description": "",
4648+ })
4649+
4650+}
4651+
4652+func (s *lightweightSuite) TestMapRemovedAppNoPart(c *check.C) {
4653+ bag := PartBagByName("foo", "baz")
4654+ m := bag.Map(nil)
4655+ c.Check(m, check.DeepEquals, map[string]string{
4656+ "name": "foo",
4657+ "origin": "baz",
4658+ "status": "removed",
4659+ "version": "0.8",
4660+ "icon": "",
4661+ "type": "app",
4662+ "vendor": "",
4663+ "installed_size": "-1",
4664+ "download_size": "-1",
4665+ "description": "",
4666+ })
4667+}
4668+
4669+func (s *lightweightSuite) TestMapInactiveOemNoPart(c *check.C) {
4670+ bag := PartBagByName("oem", "canonical")
4671+ m := bag.Map(nil)
4672+ c.Check(m["installed_size"], check.Matches, "[0-9]+")
4673+ delete(m, "installed_size")
4674+ c.Check(m, check.DeepEquals, map[string]string{
4675+ "name": "oem",
4676+ "origin": "sideload", // best guess
4677+ "status": "installed",
4678+ "version": "3",
4679+ "icon": filepath.Join(s.d, "oem", "oem", "3", "icon.png"),
4680+ "type": "oem",
4681+ "vendor": "example.com",
4682+ "download_size": "-1",
4683+ "description": "",
4684+ })
4685+}
4686+
4687+func (s *lightweightSuite) TestLoadBadApp(c *check.C) {
4688+ s.MkRemoved(c, "quux.blah", "1")
4689+ // an unparsable package.yaml:
4690+ c.Check(os.MkdirAll(filepath.Join(dirs.SnapAppsDir, "quux.blah", "1", "meta", "package.yaml"), 0755), check.IsNil)
4691+
4692+ bag := PartBagByName("quux", "blah")
4693+ c.Assert(bag, check.NotNil)
4694+ c.Assert(bag.Versions, check.DeepEquals, []string{"1"})
4695+
4696+ p, err := bag.Load(0)
4697+ c.Check(err, check.NotNil)
4698+ c.Check(p, check.IsNil)
4699+ c.Check(p == nil, check.Equals, true) // NOTE this is stronger than the above
4700+}
4701+
4702+func (s *lightweightSuite) TestLoadFmk(c *check.C) {
4703+ bag := PartBagByName("fmk", "")
4704+ c.Assert(bag, check.NotNil)
4705+ c.Assert(bag.Versions, check.HasLen, 4)
4706+ // versions are sorted backwards by version -- index 0 is always newest version
4707+ c.Check(bag.Versions, check.DeepEquals, []string{"123", "120", "119", "12a1"})
4708+ // other things are as expected
4709+ c.Check(bag.Name, check.Equals, "fmk")
4710+ c.Check(bag.Type, check.Equals, pkg.TypeFramework)
4711+ c.Check(bag.ActiveIndex(), check.Equals, 1)
4712+
4713+ c.Check(bag.IsInstalled(0), check.Equals, true)
4714+ p, err := bag.Load(0)
4715+ c.Check(err, check.IsNil)
4716+ // load loaded the right implementation of Part
4717+ c.Check(p, check.FitsTypeOf, new(snappy.SnapPart))
4718+ c.Check(p.IsActive(), check.Equals, false)
4719+ c.Check(p.Version(), check.Equals, "123")
4720+
4721+ c.Check(bag.IsInstalled(1), check.Equals, true)
4722+ p, err = bag.Load(1)
4723+ c.Check(err, check.IsNil)
4724+ c.Check(p, check.FitsTypeOf, new(snappy.SnapPart))
4725+ c.Check(p.IsActive(), check.Equals, true)
4726+ c.Check(p.Version(), check.Equals, "120")
4727+
4728+ c.Check(bag.IsInstalled(2), check.Equals, true)
4729+ p, err = bag.Load(2)
4730+ c.Check(err, check.IsNil)
4731+ c.Check(p, check.FitsTypeOf, new(snappy.SnapPart))
4732+ c.Check(p.IsActive(), check.Equals, false)
4733+ c.Check(p.Version(), check.Equals, "119")
4734+
4735+ c.Check(bag.IsInstalled(3), check.Equals, false)
4736+ p, err = bag.Load(3)
4737+ c.Check(err, check.IsNil)
4738+ c.Check(p, check.FitsTypeOf, new(removed.Removed))
4739+ c.Check(p.Version(), check.Equals, "12a1")
4740+
4741+ _, err = bag.Load(42)
4742+ c.Check(err, check.Equals, ErrBadVersionIndex)
4743+
4744+}
4745+
4746+func (s *lightweightSuite) TestLoadApp(c *check.C) {
4747+ bag0 := PartBagByName("foo", "bar")
4748+ bag1 := PartBagByName("foo", "baz")
4749+
4750+ c.Check(bag0.QualifiedName(), check.Equals, "foo.bar")
4751+ c.Check(bag0.Versions, check.DeepEquals, []string{"1.0", "0.9"})
4752+ c.Check(bag0.Type, check.Equals, pkg.TypeApp)
4753+ c.Check(bag0.ActiveIndex(), check.Equals, 0)
4754+
4755+ c.Check(bag1.QualifiedName(), check.Equals, "foo.baz")
4756+ c.Check(bag1.Versions, check.DeepEquals, []string{"0.8"})
4757+ c.Check(bag1.Type, check.Equals, pkg.TypeApp)
4758+ c.Check(bag1.ActiveIndex(), check.Equals, -1)
4759+
4760+ c.Check(bag0.IsInstalled(0), check.Equals, true)
4761+ p, err := bag0.Load(0)
4762+ c.Check(err, check.IsNil)
4763+ c.Check(p, check.FitsTypeOf, new(snappy.SnapPart))
4764+ c.Check(p.IsActive(), check.Equals, true)
4765+ c.Check(p.Version(), check.Equals, "1.0")
4766+
4767+ c.Check(bag0.IsInstalled(1), check.Equals, false)
4768+ p, err = bag0.Load(1)
4769+ c.Check(err, check.IsNil)
4770+ c.Check(p, check.FitsTypeOf, new(removed.Removed))
4771+ c.Check(p.IsActive(), check.Equals, false)
4772+ c.Check(p.Version(), check.Equals, "0.9")
4773+
4774+ c.Check(bag1.IsInstalled(0), check.Equals, false)
4775+ p, err = bag1.Load(0)
4776+ c.Check(err, check.IsNil)
4777+ c.Check(p, check.FitsTypeOf, new(removed.Removed))
4778+ c.Check(p.IsActive(), check.Equals, false)
4779+ c.Check(p.Version(), check.Equals, "0.8")
4780+}
4781+
4782+func (s *lightweightSuite) TestLoadOem(c *check.C) {
4783+ oem := PartBagByName("oem", "whatever")
4784+ c.Assert(oem, check.NotNil)
4785+ c.Check(oem.Versions, check.DeepEquals, []string{"3"})
4786+ c.Check(oem.Type, check.Equals, pkg.TypeOem)
4787+
4788+ c.Check(oem.IsInstalled(0), check.Equals, true)
4789+ c.Check(oem.ActiveIndex(), check.Equals, -1)
4790+ p, err := oem.Load(0)
4791+ c.Check(err, check.IsNil)
4792+ c.Check(p, check.FitsTypeOf, new(snappy.SnapPart))
4793+ c.Check(p.Version(), check.Equals, "3")
4794+}
4795+
4796+type mockrepo struct{ p snappy.Part }
4797+
4798+func (r mockrepo) All() ([]snappy.Part, error) {
4799+ return []snappy.Part{r.p}, nil
4800+}
4801+
4802+func (s *lightweightSuite) TestLoadCore(c *check.C) {
4803+ core := PartBagByName(snappy.SystemImagePartName, snappy.SystemImagePartOrigin)
4804+ c.Assert(core, check.NotNil)
4805+ c.Check(core.Versions, check.DeepEquals, []string{"1"})
4806+
4807+ c.Check(core.IsInstalled(0), check.Equals, true)
4808+ c.Check(core.ActiveIndex(), check.Equals, 0)
4809+ p, err := core.Load(0)
4810+ c.Check(err, check.IsNil)
4811+ c.Check(p.Version(), check.Equals, "1")
4812+}
4813+
4814+func (s *lightweightSuite) TestAll(c *check.C) {
4815+ all := AllPartBags()
4816+
4817+ c.Check(all, check.HasLen, 6) // 2 fmk, 2 app, 1 oem, 1 core
4818+}
4819
4820=== added file 'pkg/lightweight/split_test.go'
4821--- pkg/lightweight/split_test.go 1970-01-01 00:00:00 +0000
4822+++ pkg/lightweight/split_test.go 2015-10-12 09:23:01 +0000
4823@@ -0,0 +1,70 @@
4824+// -*- Mode: Go; indent-tabs-mode: t -*-
4825+
4826+/*
4827+ * Copyright (C) 2014-2015 Canonical Ltd
4828+ *
4829+ * This program is free software: you can redistribute it and/or modify
4830+ * it under the terms of the GNU General Public License version 3 as
4831+ * published by the Free Software Foundation.
4832+ *
4833+ * This program is distributed in the hope that it will be useful,
4834+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4835+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4836+ * GNU General Public License for more details.
4837+ *
4838+ * You should have received a copy of the GNU General Public License
4839+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4840+ *
4841+ */
4842+
4843+package lightweight
4844+
4845+import (
4846+ "gopkg.in/check.v1"
4847+)
4848+
4849+type splitSuite struct{}
4850+
4851+var _ = check.Suite(&splitSuite{})
4852+
4853+func (s *splitSuite) TestSplitRegular(c *check.C) {
4854+ for _, s := range []string{"meh/foo.bar/baz", "foo.bar/baz"} {
4855+ n, e, f := split(s)
4856+ c.Check(n, check.Equals, "foo")
4857+ c.Check(e, check.Equals, "bar")
4858+ c.Check(f, check.Equals, "baz")
4859+ }
4860+}
4861+
4862+func (s *splitSuite) TestSplitExtless(c *check.C) {
4863+ for _, s := range []string{"meh/foo/baz", "foo/baz"} {
4864+ n, e, f := split(s)
4865+ c.Check(n, check.Equals, "foo")
4866+ c.Check(e, check.Equals, "")
4867+ c.Check(f, check.Equals, "baz")
4868+ }
4869+}
4870+
4871+func (s *splitSuite) TestSplitBad(c *check.C) {
4872+ c.Check(func() {
4873+ split("what")
4874+ }, check.PanicMatches, `bad path given.*`)
4875+}
4876+
4877+func (s *splitSuite) TestExtract(c *check.C) {
4878+ ps := []string{"meh/foo.bar/v1", "meh/foo.bar/v2", "meh/foo.baz/v3"}
4879+ n, o, vs, ps := extract(ps)
4880+ c.Check(n, check.Equals, "foo")
4881+ c.Check(o, check.Equals, "bar")
4882+ c.Check(vs, check.DeepEquals, []string{"v1", "v2"})
4883+ c.Check(ps, check.DeepEquals, []string{"meh/foo.baz/v3"})
4884+}
4885+
4886+func (s *splitSuite) TestExtractCurrent(c *check.C) {
4887+ ps := []string{"meh/foo.bar/current", "meh/foo.bar/v1", "meh/foo.bar/v2", "meh/foo.baz/v3"}
4888+ n, o, vs, ps := extract(ps)
4889+ c.Check(n, check.Equals, "foo")
4890+ c.Check(o, check.Equals, "bar")
4891+ c.Check(vs, check.DeepEquals, []string{"v1", "v2"})
4892+ c.Check(ps, check.DeepEquals, []string{"meh/foo.baz/v3"})
4893+}
4894
4895=== added directory 'pkg/remote'
4896=== added file 'pkg/remote/remote.go'
4897--- pkg/remote/remote.go 1970-01-01 00:00:00 +0000
4898+++ pkg/remote/remote.go 2015-10-12 09:23:01 +0000
4899@@ -0,0 +1,45 @@
4900+// -*- Mode: Go; indent-tabs-mode: t -*-
4901+
4902+/*
4903+ * Copyright (C) 2014-2015 Canonical Ltd
4904+ *
4905+ * This program is free software: you can redistribute it and/or modify
4906+ * it under the terms of the GNU General Public License version 3 as
4907+ * published by the Free Software Foundation.
4908+ *
4909+ * This program is distributed in the hope that it will be useful,
4910+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4911+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4912+ * GNU General Public License for more details.
4913+ *
4914+ * You should have received a copy of the GNU General Public License
4915+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4916+ *
4917+ */
4918+
4919+package remote
4920+
4921+import (
4922+ "launchpad.net/snappy/pkg"
4923+)
4924+
4925+// A Snap encapsulates the data sent to us from the store.
4926+type Snap struct {
4927+ Alias string `json:"alias,omitempty"`
4928+ AnonDownloadURL string `json:"anon_download_url,omitempty"`
4929+ DownloadSha512 string `json:"download_sha512,omitempty"`
4930+ Description string `json:"description,omitempty"`
4931+ DownloadSize int64 `json:"binary_filesize,omitempty"`
4932+ DownloadURL string `json:"download_url,omitempty"`
4933+ IconURL string `json:"icon_url"`
4934+ LastUpdated string `json:"last_updated,omitempty"`
4935+ Name string `json:"package_name"`
4936+ Origin string `json:"origin"`
4937+ Prices map[string]float64 `json:"prices,omitempty"`
4938+ Publisher string `json:"publisher,omitempty"`
4939+ RatingsAverage float64 `json:"ratings_average,omitempty"`
4940+ SupportURL string `json:"support_url"`
4941+ Title string `json:"title"`
4942+ Type pkg.Type `json:"content,omitempty"`
4943+ Version string `json:"version"`
4944+}
4945
4946=== added directory 'pkg/removed'
4947=== added file 'pkg/removed/removed.go'
4948--- pkg/removed/removed.go 1970-01-01 00:00:00 +0000
4949+++ pkg/removed/removed.go 2015-10-12 09:23:01 +0000
4950@@ -0,0 +1,158 @@
4951+// -*- Mode: Go; indent-tabs-mode: t -*-
4952+
4953+/*
4954+ * Copyright (C) 2014-2015 Canonical Ltd
4955+ *
4956+ * This program is free software: you can redistribute it and/or modify
4957+ * it under the terms of the GNU General Public License version 3 as
4958+ * published by the Free Software Foundation.
4959+ *
4960+ * This program is distributed in the hope that it will be useful,
4961+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4962+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4963+ * GNU General Public License for more details.
4964+ *
4965+ * You should have received a copy of the GNU General Public License
4966+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4967+ *
4968+ */
4969+
4970+// Package removed implements Removed packages, that are packages that
4971+// have been installed, removed, but not purged: there is no
4972+// application, but there might be data.
4973+package removed
4974+
4975+import (
4976+ "errors"
4977+ "io/ioutil"
4978+ "time"
4979+
4980+ "gopkg.in/yaml.v2"
4981+
4982+ "launchpad.net/snappy/pkg"
4983+ "launchpad.net/snappy/pkg/remote"
4984+ "launchpad.net/snappy/progress"
4985+ "launchpad.net/snappy/snappy"
4986+)
4987+
4988+// ErrRemoved is returned when you ask to operate on a removed package.
4989+var ErrRemoved = errors.New("package is removed")
4990+
4991+// Removed represents a removed package.
4992+type Removed struct {
4993+ name string
4994+ origin string
4995+ version string
4996+ pkgType pkg.Type
4997+ remote *remote.Snap
4998+}
4999+
5000+// New removed package.
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches