summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2018-03-20 12:21:39 +0100
committerMichael Vogt <mvo@ubuntu.com>2018-03-20 12:21:39 +0100
commit609a29f02683e1aa92bc5cf365b44585d6eb15ec (patch)
treebcb8b1d9f28b250f4793c29de8c316cbf9597e53
parent97f2683d2b0c0356df24db5275ef7c29500e4437 (diff)
parent482f8cb4761b92660a63a8b17a0ab6d10cf33ff6 (diff)
Merge remote-tracking branch 'upstream/master' into bionic-gnupg-recommendsbionic-gnupg-recommends
-rw-r--r--cmd/snap-mgmt/snap-mgmt.sh.in6
-rw-r--r--cmd/snap-update-ns/change.go11
-rw-r--r--cmd/snap-update-ns/change_test.go62
-rw-r--r--cmd/snap-update-ns/export_test.go5
-rw-r--r--cmd/snap-update-ns/utils.go23
-rw-r--r--cmd/snap-update-ns/utils_test.go53
-rw-r--r--cmd/snap/cmd_pack.go29
-rw-r--r--cmd/snap/cmd_pack_test.go80
-rw-r--r--daemon/api.go11
-rw-r--r--daemon/api_test.go84
-rw-r--r--overlord/devicestate/devicemgr.go36
-rw-r--r--overlord/devicestate/devicestate_test.go65
-rw-r--r--overlord/devicestate/handlers.go27
-rw-r--r--packaging/ubuntu-14.04/snapd.postrm4
-rw-r--r--packaging/ubuntu-16.04/snapd.postinst27
-rw-r--r--packaging/ubuntu-16.04/snapd.postrm4
-rw-r--r--packaging/ubuntu-16.04/tests/integrationtests4
-rwxr-xr-xrun-checks10
-rw-r--r--snap/pack/pack.go26
-rw-r--r--snap/pack/pack_test.go12
-rw-r--r--snap/squashfs/squashfs_test.go2
-rw-r--r--spread.yaml10
-rw-r--r--store/store.go38
-rw-r--r--store/store_test.go43
-rw-r--r--tests/lib/names.sh1
-rwxr-xr-xtests/lib/pkgdb.sh7
-rwxr-xr-xtests/lib/prepare.sh2
-rwxr-xr-xtests/lib/reset.sh12
-rwxr-xr-xtests/lib/snaps/test-snapd-service/bin/reload2
-rw-r--r--tests/lib/store.sh3
-rw-r--r--tests/main/classic-ubuntu-core-transition-auth/task.yaml4
-rw-r--r--tests/main/classic-ubuntu-core-transition-two-cores/task.yaml4
-rw-r--r--tests/main/classic-ubuntu-core-transition/task.yaml4
-rw-r--r--tests/main/econnreset/task.yaml4
-rw-r--r--tests/main/interfaces-kernel-module-control/task.yaml7
-rw-r--r--tests/main/interfaces-snapd-control-with-manage/task.yaml5
-rw-r--r--tests/main/layout/task.yaml72
-rw-r--r--tests/main/listing/task.yaml5
-rw-r--r--tests/main/lxd/task.yaml4
-rw-r--r--tests/main/prepare-image-uboot/task.yaml4
-rw-r--r--tests/main/searching/task.yaml4
-rw-r--r--tests/main/system-core-alias/task.yaml19
-rw-r--r--tests/upgrade/basic/task.yaml5
-rw-r--r--testutil/lowlevel.go4
44 files changed, 698 insertions, 146 deletions
diff --git a/cmd/snap-mgmt/snap-mgmt.sh.in b/cmd/snap-mgmt/snap-mgmt.sh.in
index 907d64c1ca..360fb4bd6e 100644
--- a/cmd/snap-mgmt/snap-mgmt.sh.in
+++ b/cmd/snap-mgmt/snap-mgmt.sh.in
@@ -33,7 +33,7 @@ systemctl_stop() {
}
purge() {
- # debian-9, fedora-27, ubuntu-16.04...
+ # shellcheck disable=SC1091
distribution=$(. /etc/os-release; echo "${ID}-${VERSION_ID}")
if [ "$distribution" = "ubuntu-14.04" ]; then
@@ -108,13 +108,13 @@ purge() {
echo "Discarding preserved snap namespaces"
# opportunistic as those might not be actually mounted
if [ -d /run/snapd/ns ]; then
- if [ $(ls /run/snapd/ns/*.mnt | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.mnt" | wc -l)" -gt 0 ]; then
for mnt in /run/snapd/ns/*.mnt; do
umount -l "$mnt" || true
rm -f "$mnt"
done
fi
- if [ $(ls /run/snapd/ns/*.fstab | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.fstab" | wc -l)" -gt 0 ]; then
for fstab in /run/snapd/ns/*.fstab; do
rm -f "$fstab"
done
diff --git a/cmd/snap-update-ns/change.go b/cmd/snap-update-ns/change.go
index c1450dcc8c..075aed7aac 100644
--- a/cmd/snap-update-ns/change.go
+++ b/cmd/snap-update-ns/change.go
@@ -94,7 +94,7 @@ func (c *Change) createPath(path string, pokeHoles bool) ([]*Change, error) {
if err2, ok := err.(*ReadOnlyFsError); ok && pokeHoles {
// If the writing failed because the underlying file-system is read-only
// we can construct a writable mimic to fix that.
- changes, err = createWritableMimic(err2.Path)
+ changes, err = createWritableMimic(err2.Path, path)
if err != nil {
err = fmt.Errorf("cannot create writable mimic over %q: %s", err2.Path, err)
} else {
@@ -135,9 +135,12 @@ func (c *Change) ensureTarget() ([]*Change, error) {
err = fmt.Errorf("cannot use %q as mount point: not a regular file", path)
}
case "symlink":
- // When we want to create a symlink we just need the empty
- // space so anything that is in the way is a problem.
- err = fmt.Errorf("cannot create symlink in %q: existing file in the way", path)
+ if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
+ // Create path verifies the symlink or fails if it is not what we wanted.
+ _, err = c.createPath(path, false)
+ } else {
+ err = fmt.Errorf("cannot create symlink in %q: existing file in the way", path)
+ }
}
} else if os.IsNotExist(err) {
changes, err = c.createPath(path, true)
diff --git a/cmd/snap-update-ns/change_test.go b/cmd/snap-update-ns/change_test.go
index 17fd54d146..25acdaa2c8 100644
--- a/cmd/snap-update-ns/change_test.go
+++ b/cmd/snap-update-ns/change_test.go
@@ -390,7 +390,10 @@ func (s *changeSuite) TestPerformFilesystemMountWithoutMountPointAndReadOnlyBase
synth, err := chg.Perform()
c.Assert(err, IsNil)
c.Assert(synth, DeepEquals, []*update.Change{
- {Action: update.Mount, Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/rofs", Type: "tmpfs"}},
+ {Action: update.Mount, Entry: osutil.MountEntry{
+ Name: "tmpfs", Dir: "/rofs", Type: "tmpfs",
+ Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/rofs/target"}},
+ },
})
c.Assert(s.sys.Calls(), DeepEquals, []string{
// sniff mount target
@@ -746,7 +749,10 @@ func (s *changeSuite) TestPerformDirectoryBindMountWithoutMountPointAndReadOnlyB
synth, err := chg.Perform()
c.Assert(err, IsNil)
c.Assert(synth, DeepEquals, []*update.Change{
- {Action: update.Mount, Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/rofs", Type: "tmpfs"}},
+ {Action: update.Mount, Entry: osutil.MountEntry{
+ Name: "tmpfs", Dir: "/rofs", Type: "tmpfs",
+ Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/rofs/target"}},
+ },
})
c.Assert(s.sys.Calls(), DeepEquals, []string{
// sniff mount target
@@ -1021,7 +1027,10 @@ func (s *changeSuite) TestPerformFileBindMountWithoutMountPointAndReadOnlyBase(c
synth, err := chg.Perform()
c.Assert(err, IsNil)
c.Assert(synth, DeepEquals, []*update.Change{
- {Action: update.Mount, Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/rofs", Type: "tmpfs"}},
+ {Action: update.Mount, Entry: osutil.MountEntry{
+ Name: "tmpfs", Dir: "/rofs", Type: "tmpfs",
+ Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/rofs/target"}},
+ },
})
c.Assert(s.sys.Calls(), DeepEquals, []string{
// sniff mount target
@@ -1253,7 +1262,10 @@ func (s *changeSuite) TestPerformCreateSymlinkWithoutBaseDirAndReadOnlyBase(c *C
synth, err := chg.Perform()
c.Assert(err, IsNil)
c.Assert(synth, DeepEquals, []*update.Change{
- {Action: update.Mount, Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/rofs", Type: "tmpfs"}},
+ {Action: update.Mount, Entry: osutil.MountEntry{
+ Name: "tmpfs", Dir: "/rofs", Type: "tmpfs",
+ Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/rofs/name"}},
+ },
})
c.Assert(s.sys.Calls(), DeepEquals, []string{
// sniff symlink name
@@ -1314,6 +1326,48 @@ func (s *changeSuite) TestPerformCreateSymlinkWithFileInTheWay(c *C) {
})
}
+// Change.Perform wants to create a symlink but a correct symlink is already present.
+func (s *changeSuite) TestPerformCreateSymlinkWithGoodSymlinkPresent(c *C) {
+ s.sys.InsertLstatResult(`lstat "/name"`, testutil.FileInfoSymlink)
+ s.sys.InsertFault(`symlinkat "/oldname" 3 "name"`, syscall.EEXIST)
+ s.sys.InsertFstatResult(`fstat 4 <ptr>`, syscall.Stat_t{Mode: syscall.S_IFLNK})
+ s.sys.InsertReadlinkatResult(`readlinkat 4 "" <ptr>`, "/oldname")
+ chg := &update.Change{Action: update.Mount, Entry: osutil.MountEntry{Name: "unused", Dir: "/name", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=/oldname"}}}
+ synth, err := chg.Perform()
+ c.Assert(err, IsNil)
+ c.Assert(synth, HasLen, 0)
+ c.Assert(s.sys.Calls(), DeepEquals, []string{
+ `lstat "/name"`,
+ `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY 0`, // -> 3
+ `symlinkat "/oldname" 3 "name"`, // -> EEXIST
+ `openat 3 "name" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, // -> 4
+ `fstat 4 <ptr>`,
+ `readlinkat 4 "" <ptr>`,
+ `close 3`,
+ })
+}
+
+// Change.Perform wants to create a symlink but a incorrect symlink is already present.
+func (s *changeSuite) TestPerformCreateSymlinkWithBadSymlinkPresent(c *C) {
+ s.sys.InsertLstatResult(`lstat "/name"`, testutil.FileInfoSymlink)
+ s.sys.InsertFault(`symlinkat "/oldname" 3 "name"`, syscall.EEXIST)
+ s.sys.InsertFstatResult(`fstat 4 <ptr>`, syscall.Stat_t{Mode: syscall.S_IFLNK})
+ s.sys.InsertReadlinkatResult(`readlinkat 4 "" <ptr>`, "/evil")
+ chg := &update.Change{Action: update.Mount, Entry: osutil.MountEntry{Name: "unused", Dir: "/name", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=/oldname"}}}
+ synth, err := chg.Perform()
+ c.Assert(err, ErrorMatches, `cannot create path "/name": cannot create symbolic link "name": existing symbolic link in the way`)
+ c.Assert(synth, HasLen, 0)
+ c.Assert(s.sys.Calls(), DeepEquals, []string{
+ `lstat "/name"`,
+ `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY 0`, // -> 3
+ `symlinkat "/oldname" 3 "name"`, // -> EEXIST
+ `openat 3 "name" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, // -> 4
+ `fstat 4 <ptr>`,
+ `readlinkat 4 "" <ptr>`,
+ `close 3`,
+ })
+}
+
func (s *changeSuite) TestPerformRemoveSymlink(c *C) {
chg := &update.Change{Action: update.Unmount, Entry: osutil.MountEntry{Name: "unused", Dir: "/name", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=/oldname"}}}
synth, err := chg.Perform()
diff --git a/cmd/snap-update-ns/export_test.go b/cmd/snap-update-ns/export_test.go
index 36c723755f..591155a471 100644
--- a/cmd/snap-update-ns/export_test.go
+++ b/cmd/snap-update-ns/export_test.go
@@ -21,6 +21,7 @@ package main
import (
"os"
+ "syscall"
. "gopkg.in/check.v1"
@@ -61,6 +62,7 @@ type SystemCalls interface {
Open(path string, flags int, mode uint32) (fd int, err error)
Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
Unmount(target string, flags int) error
+ Fstat(fd int, buf *syscall.Stat_t) error
}
// MockSystemCalls replaces real system calls with those of the argument.
@@ -79,6 +81,7 @@ func MockSystemCalls(sc SystemCalls) (restore func()) {
oldSysUnmount := sysUnmount
oldSysSymlinkat := sysSymlinkat
oldReadlinkat := sysReadlinkat
+ oldFstat := sysFstat
// override
osLstat = sc.Lstat
@@ -94,6 +97,7 @@ func MockSystemCalls(sc SystemCalls) (restore func()) {
sysUnmount = sc.Unmount
sysSymlinkat = sc.Symlinkat
sysReadlinkat = sc.Readlinkat
+ sysFstat = sc.Fstat
return func() {
// restore
@@ -110,6 +114,7 @@ func MockSystemCalls(sc SystemCalls) (restore func()) {
sysUnmount = oldSysUnmount
sysSymlinkat = oldSysSymlinkat
sysReadlinkat = oldReadlinkat
+ sysFstat = oldFstat
}
}
diff --git a/cmd/snap-update-ns/utils.go b/cmd/snap-update-ns/utils.go
index cbbfefdd36..a6825878e1 100644
--- a/cmd/snap-update-ns/utils.go
+++ b/cmd/snap-update-ns/utils.go
@@ -222,10 +222,9 @@ func secureMkSymlink(fd int, segments []string, i int, oldname string) error {
switch err {
case syscall.EEXIST:
var objFd int
- const O_PATH = 010000000
// If the file exists then just open it for examination.
// Maybe it's the symlink we were hoping to create.
- objFd, err = sysOpenat(fd, segment, syscall.O_CLOEXEC|O_PATH|syscall.O_NOFOLLOW, 0)
+ objFd, err = sysOpenat(fd, segment, syscall.O_CLOEXEC|sys.O_PATH|syscall.O_NOFOLLOW, 0)
if err != nil {
return fmt.Errorf("cannot open existing file %q: %v", segment, err)
}
@@ -397,7 +396,7 @@ func secureMksymlinkAll(name string, perm os.FileMode, uid sys.UserID, gid sys.G
// be used with. Since the original directory is hidden the algorithm relies on
// a temporary directory where the original is bind-mounted during the
// progression of the algorithm.
-func planWritableMimic(dir string) ([]*Change, error) {
+func planWritableMimic(dir, neededBy string) ([]*Change, error) {
// We need a place for "safe keeping" of what is present in the original
// directory as we are about to attach a tmpfs there, which will hide
// everything inside.
@@ -427,7 +426,10 @@ func planWritableMimic(dir string) ([]*Change, error) {
})
// Mount tmpfs over the original directory, hiding its contents.
changes = append(changes, &Change{
- Action: Mount, Entry: osutil.MountEntry{Name: "tmpfs", Dir: dir, Type: "tmpfs"},
+ Action: Mount, Entry: osutil.MountEntry{
+ Name: "tmpfs", Dir: dir, Type: "tmpfs",
+ Options: []string{"x-snapd.synthetic", fmt.Sprintf("x-snapd.needed-by=%s", neededBy)},
+ },
})
// Iterate over the items in the original directory (nothing is mounted _yet_).
entries, err := ioutilReadDir(dir)
@@ -446,18 +448,21 @@ func planWritableMimic(dir string) ([]*Change, error) {
switch {
case m.IsDir():
ch.Entry.Options = []string{"rbind"}
- changes = append(changes, ch)
case m.IsRegular():
ch.Entry.Options = []string{"bind", "x-snapd.kind=file"}
- changes = append(changes, ch)
case m&os.ModeSymlink != 0:
if target, err := osReadlink(filepath.Join(dir, fi.Name())); err == nil {
ch.Entry.Options = []string{"x-snapd.kind=symlink", fmt.Sprintf("x-snapd.symlink=%s", target)}
- changes = append(changes, ch)
+ } else {
+ continue
}
default:
logger.Noticef("skipping unsupported file %s", fi)
+ continue
}
+ ch.Entry.Options = append(ch.Entry.Options, "x-snapd.synthetic")
+ ch.Entry.Options = append(ch.Entry.Options, fmt.Sprintf("x-snapd.needed-by=%s", neededBy))
+ changes = append(changes, ch)
}
// Finally unbind the safe-keeping directory as we don't need it anymore.
changes = append(changes, &Change{
@@ -568,8 +573,8 @@ func execWritableMimic(plan []*Change) ([]*Change, error) {
return undoChanges, nil
}
-func createWritableMimic(dir string) ([]*Change, error) {
- plan, err := planWritableMimic(dir)
+func createWritableMimic(dir, neededBy string) ([]*Change, error) {
+ plan, err := planWritableMimic(dir, neededBy)
if err != nil {
return nil, err
}
diff --git a/cmd/snap-update-ns/utils_test.go b/cmd/snap-update-ns/utils_test.go
index c94040114c..c64347ee7f 100644
--- a/cmd/snap-update-ns/utils_test.go
+++ b/cmd/snap-update-ns/utils_test.go
@@ -252,20 +252,21 @@ func (s *utilsSuite) TestPlanWritableMimic(c *C) {
})
defer restore()
- changes, err := update.PlanWritableMimic("/foo")
+ changes, err := update.PlanWritableMimic("/foo", "/foo/bar")
c.Assert(err, IsNil)
+
c.Assert(changes, DeepEquals, []*update.Change{
// Store /foo in /tmp/.snap/foo while we set things up
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind"}}, Action: update.Mount},
// Put a tmpfs over /foo
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
// Bind mount files and directories over. Note that files are identified by x-snapd.kind=file option.
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
// Create symlinks.
// Bad symlinks and all other file types are skipped and not
// recorded in mount changes.
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
// Unmount the safe-keeping directory
{Entry: osutil.MountEntry{Name: "none", Dir: "/tmp/.snap/foo", Options: []string{"x-snapd.detach"}}, Action: update.Unmount},
})
@@ -282,7 +283,7 @@ func (s *utilsSuite) TestPlanWritableMimicErrors(c *C) {
})
defer restore()
- changes, err := update.PlanWritableMimic("/foo")
+ changes, err := update.PlanWritableMimic("/foo", "/foo/bar")
c.Assert(err, ErrorMatches, "testing")
c.Assert(changes, HasLen, 0)
}
@@ -291,10 +292,10 @@ func (s *utilsSuite) TestExecWirableMimicSuccess(c *C) {
// This plan is the same as in the test above. This is what comes out of planWritableMimic.
plan := []*update.Change{
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
{Entry: osutil.MountEntry{Name: "none", Dir: "/tmp/.snap/foo", Options: []string{"x-snapd.detach"}}, Action: update.Unmount},
}
@@ -309,9 +310,9 @@ func (s *utilsSuite) TestExecWirableMimicSuccess(c *C) {
undoPlan, err := update.ExecWritableMimic(plan)
c.Assert(err, IsNil)
c.Assert(undoPlan, DeepEquals, []*update.Change{
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.detach"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar", "x-snapd.detach"}}, Action: update.Mount},
})
}
@@ -319,9 +320,9 @@ func (s *utilsSuite) TestExecWirableMimicErrorWithRecovery(c *C) {
// This plan is the same as in the test above. This is what comes out of planWritableMimic.
plan := []*update.Change{
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
// NOTE: the next perform will fail. Notably the symlink did not fail.
{Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind"}}, Action: update.Mount},
{Entry: osutil.MountEntry{Name: "none", Dir: "/tmp/.snap/foo", Options: []string{"x-snapd.detach"}}, Action: update.Unmount},
@@ -355,8 +356,8 @@ func (s *utilsSuite) TestExecWirableMimicErrorWithRecovery(c *C) {
// The changes we managed to perform were undone correctly.
c.Assert(recoveryPlan, DeepEquals, []*update.Change{
// NOTE: there is no symlink undo entry as it is implicitly undone by unmounting the tmpfs.
- {Entry: osutil.MountEntry{Name: "/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Unmount},
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Unmount},
+ {Entry: osutil.MountEntry{Name: "/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Unmount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Unmount},
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind", "x-snapd.detach"}}, Action: update.Unmount},
})
}
@@ -365,10 +366,10 @@ func (s *utilsSuite) TestExecWirableMimicErrorNothingDone(c *C) {
// This plan is the same as in the test above. This is what comes out of planWritableMimic.
plan := []*update.Change{
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
{Entry: osutil.MountEntry{Name: "none", Dir: "/tmp/.snap/foo", Options: []string{"x-snapd.detach"}}, Action: update.Unmount},
}
@@ -388,10 +389,10 @@ func (s *utilsSuite) TestExecWirableMimicErrorCannotUndo(c *C) {
// This plan is the same as in the test above. This is what comes out of planWritableMimic.
plan := []*update.Change{
{Entry: osutil.MountEntry{Name: "/foo", Dir: "/tmp/.snap/foo", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs"}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind"}}, Action: update.Mount},
- {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/foo", Type: "tmpfs", Options: []string{"x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/file", Dir: "/foo/file", Options: []string{"bind", "x-snapd.kind=file", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/dir", Dir: "/foo/dir", Options: []string{"rbind", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
+ {Entry: osutil.MountEntry{Name: "/tmp/.snap/foo/symlink", Dir: "/foo/symlink", Options: []string{"x-snapd.kind=symlink", "x-snapd.symlink=target", "x-snapd.synthetic", "x-snapd.needed-by=/foo/bar"}}, Action: update.Mount},
{Entry: osutil.MountEntry{Name: "none", Dir: "/tmp/.snap/foo", Options: []string{"x-snapd.detach"}}, Action: update.Unmount},
}
diff --git a/cmd/snap/cmd_pack.go b/cmd/snap/cmd_pack.go
index 2d8a3c21d7..68c91bbe2d 100644
--- a/cmd/snap/cmd_pack.go
+++ b/cmd/snap/cmd_pack.go
@@ -25,19 +25,30 @@ import (
"github.com/jessevdk/go-flags"
"github.com/snapcore/snapd/i18n"
+ "github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/pack"
)
type packCmd struct {
- Positional struct {
+ CheckSkeleton bool `long:"check-skeleton"`
+ Positional struct {
SnapDir string `positional-arg-name:"<snap-dir>"`
TargetDir string `positional-arg-name:"<target-dir>"`
} `positional-args:"yes"`
}
-var shortPackHelp = i18n.G("Pack the given target dir as a snap")
+var shortPackHelp = i18n.G("Pack the given directory as a snap")
var longPackHelp = i18n.G(`
-The pack command packs the given snap-dir as a snap.
+The pack command packs the given snap-dir as a snap and writes the result to
+target-dir. If target-dir is omitted, the result is written to current
+directory. If both source-dir and target-dir are omitted, the pack command packs
+the current directory.
+
+When used with --check-skeleton, pack only checks whether snap-dir contains
+valid snap metadata and raises an error otherwise. Application commands listed
+in snap metadata file, but appearing with incorrect permission bits result in an
+error. Commands that are missing from snap-dir are listed in diagnostic
+messages.
`)
func init() {
@@ -46,7 +57,9 @@ func init() {
longPackHelp,
func() flags.Commander {
return &packCmd{}
- }, nil, nil)
+ }, map[string]string{
+ "check-skeleton": i18n.G("Validate snap-dir metadata only"),
+ }, nil)
}
func (x *packCmd) Execute([]string) error {
@@ -57,6 +70,14 @@ func (x *packCmd) Execute([]string) error {
x.Positional.TargetDir = "."
}
+ if x.CheckSkeleton {
+ err := pack.CheckSkeleton(x.Positional.SnapDir)
+ if err == snap.ErrMissingPaths {
+ return nil
+ }
+ return err
+ }
+
snapPath, err := pack.Snap(x.Positional.SnapDir, x.Positional.TargetDir)
if err != nil {
return fmt.Errorf("cannot pack %q: %v", x.Positional.SnapDir, err)
diff --git a/cmd/snap/cmd_pack_test.go b/cmd/snap/cmd_pack_test.go
new file mode 100644
index 0000000000..2cbf568961
--- /dev/null
+++ b/cmd/snap/cmd_pack_test.go
@@ -0,0 +1,80 @@
+package main_test
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "gopkg.in/check.v1"
+
+ snaprun "github.com/snapcore/snapd/cmd/snap"
+)
+
+const packSnapYaml = `name: hello
+version: 1.0.1
+apps:
+ app:
+ command: bin/hello
+`
+
+func makeSnapDirForPack(c *check.C, snapYaml string) string {
+ tempdir := c.MkDir()
+ c.Assert(os.Chmod(tempdir, 0755), check.IsNil)
+
+ // use meta/snap.yaml
+ metaDir := filepath.Join(tempdir, "meta")
+ err := os.Mkdir(metaDir, 0755)
+ c.Assert(err, check.IsNil)
+ err = ioutil.WriteFile(filepath.Join(metaDir, "snap.yaml"), []byte(snapYaml), 0644)
+ c.Assert(err, check.IsNil)
+
+ return tempdir
+}
+
+func (s *SnapSuite) TestPackCheckSkeletonNoAppFiles(c *check.C) {
+ snapDir := makeSnapDirForPack(c, packSnapYaml)
+
+ // check-skeleton does not fail due to missing files
+ _, err := snaprun.Parser().ParseArgs([]string{"pack", "--check-skeleton", snapDir})
+ c.Assert(err, check.IsNil)
+}
+
+func (s *SnapSuite) TestPackCheckSkeletonBadMeta(c *check.C) {
+ // no snap name
+ snapYaml := `
+version: foobar
+apps:
+`
+ snapDir := makeSnapDirForPack(c, snapYaml)
+
+ _, err := snaprun.Parser().ParseArgs([]string{"pack", "--check-skeleton", snapDir})
+ c.Assert(err, check.ErrorMatches, "snap name cannot be empty")
+}
+
+func (s *SnapSuite) TestPackPacksFailsForMissingPaths(c *check.C) {
+ snapDir := makeSnapDirForPack(c, packSnapYaml)
+
+ _, err := snaprun.Parser().ParseArgs([]string{"pack", snapDir, snapDir})
+ c.Assert(err, check.ErrorMatches, ".* snap is unusable due to missing files")
+}
+
+func (s *SnapSuite) TestPackPacksASnap(c *check.C) {
+ snapDir := makeSnapDirForPack(c, packSnapYaml)
+
+ const helloBinContent = `#!/bin/sh
+printf "hello world"
+`
+ // an example binary
+ binDir := filepath.Join(snapDir, "bin")
+ err := os.Mkdir(binDir, 0755)
+ c.Assert(err, check.IsNil)
+ err = ioutil.WriteFile(filepath.Join(binDir, "hello"), []byte(helloBinContent), 0755)
+ c.Assert(err, check.IsNil)
+
+ _, err = snaprun.Parser().ParseArgs([]string{"pack", snapDir, snapDir})
+ c.Assert(err, check.IsNil)
+
+ matches, err := filepath.Glob(snapDir + "/hello*.snap")
+ c.Assert(err, check.IsNil)
+ c.Assert(matches, check.HasLen, 1)
+}
diff --git a/daemon/api.go b/daemon/api.go
index 5814bceb6c..242f3f9c03 100644
--- a/daemon/api.go
+++ b/daemon/api.go
@@ -1593,7 +1593,7 @@ func splitQS(qs string) []string {
func getSnapConf(c *Command, r *http.Request, user *auth.UserState) Response {
vars := muxVars(r)
- snapName := vars["name"]
+ snapName := systemCoreSnapUnalias(vars["name"])
keys := splitQS(r.URL.Query().Get("keys"))
@@ -1636,7 +1636,7 @@ func getSnapConf(c *Command, r *http.Request, user *auth.UserState) Response {
func setSnapConf(c *Command, r *http.Request, user *auth.UserState) Response {
vars := muxVars(r)
- snapName := vars["name"]
+ snapName := systemCoreSnapUnalias(vars["name"])
var patchValues map[string]interface{}
if err := jsonutil.DecodeWithNumber(r.Body, &patchValues); err != nil {
@@ -2799,3 +2799,10 @@ func postApps(c *Command, r *http.Request, user *auth.UserState) Response {
st.EnsureBefore(0)
return AsyncResponse(nil, &Meta{Change: chg.ID()})
}
+
+func systemCoreSnapUnalias(name string) string {
+ if name == "system" {
+ return "core"
+ }
+ return name
+}
diff --git a/daemon/api_test.go b/daemon/api_test.go
index 5da5016ad9..f4247a4668 100644
--- a/daemon/api_test.go
+++ b/daemon/api_test.go
@@ -2543,9 +2543,9 @@ func (s *apiSuite) sideloadCheck(c *check.C, content string, head map[string]str
return chg.Summary()
}
-func (s *apiSuite) runGetConf(c *check.C, keys []string, statusCode int) map[string]interface{} {
- s.vars = map[string]string{"name": "test-snap"}
- req, err := http.NewRequest("GET", "/v2/snaps/test-snap/conf?keys="+strings.Join(keys, ","), nil)
+func (s *apiSuite) runGetConf(c *check.C, snapName string, keys []string, statusCode int) map[string]interface{} {
+ s.vars = map[string]string{"name": snapName}
+ req, err := http.NewRequest("GET", "/v2/snaps/"+snapName+"/conf?keys="+strings.Join(keys, ","), nil)
c.Check(err, check.IsNil)
rec := httptest.NewRecorder()
snapConfCmd.GET(snapConfCmd, req, nil).ServeHTTP(rec, req)
@@ -2568,15 +2568,32 @@ func (s *apiSuite) TestGetConfSingleKey(c *check.C) {
tr.Commit()
d.overlord.State().Unlock()
- result := s.runGetConf(c, []string{"test-key1"}, 200)
+ result := s.runGetConf(c, "test-snap", []string{"test-key1"}, 200)
c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"})
- result = s.runGetConf(c, []string{"test-key1", "test-key2"}, 200)
+ result = s.runGetConf(c, "test-snap", []string{"test-key1", "test-key2"}, 200)
c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1", "test-key2": "test-value2"})
}
+func (s *apiSuite) TestGetConfCoreSystemAlias(c *check.C) {
+ d := s.daemon(c)
+
+ // Set a config that we'll get in a moment
+ d.overlord.State().Lock()
+ tr := config.NewTransaction(d.overlord.State())
+ tr.Set("core", "test-key1", "test-value1")
+ tr.Commit()
+ d.overlord.State().Unlock()
+
+ result := s.runGetConf(c, "core", []string{"test-key1"}, 200)
+ c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"})
+
+ result = s.runGetConf(c, "system", []string{"test-key1"}, 200)
+ c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"})
+}
+
func (s *apiSuite) TestGetConfMissingKey(c *check.C) {
- result := s.runGetConf(c, []string{"test-key2"}, 400)
+ result := s.runGetConf(c, "test-snap", []string{"test-key2"}, 400)
c.Check(result, check.DeepEquals, map[string]interface{}{"message": `snap "test-snap" has no "test-key2" configuration option`})
}
@@ -2589,13 +2606,13 @@ func (s *apiSuite) TestGetRootDocument(c *check.C) {
tr.Commit()
d.overlord.State().Unlock()
- result := s.runGetConf(c, nil, 200)
+ result := s.runGetConf(c, "test-snap", nil, 200)
c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1", "test-key2": "test-value2"})
}
func (s *apiSuite) TestGetConfBadKey(c *check.C) {
// TODO: this one in particular should really be a 400 also
- result := s.runGetConf(c, []string{"."}, 500)
+ result := s.runGetConf(c, "test-snap", []string{"."}, 500)
c.Check(result, check.DeepEquals, map[string]interface{}{"message": `invalid option name: ""`})
}
@@ -2647,6 +2664,57 @@ func (s *apiSuite) TestSetConf(c *check.C) {
}})
}
+func (s *apiSuite) TestSetConfCoreSystemAlias(c *check.C) {
+ d := s.daemon(c)
+ s.mockSnap(c, `
+name: core
+version: 1
+`)
+ // Mock the hook runner
+ hookRunner := testutil.MockCommand(c, "snap", "")
+ defer hookRunner.Restore()
+
+ d.overlord.Loop()
+ defer d.overlord.Stop()
+
+ text, err := json.Marshal(map[string]interface{}{"key": "value"})
+ c.Assert(err, check.IsNil)
+
+ buffer := bytes.NewBuffer(text)
+ req, err := http.NewRequest("PUT", "/v2/snaps/system/conf", buffer)
+ c.Assert(err, check.IsNil)
+
+ s.vars = map[string]string{"name": "system"}
+
+ rec := httptest.NewRecorder()
+ snapConfCmd.PUT(snapConfCmd, req, nil).ServeHTTP(rec, req)
+ c.Check(rec.Code, check.Equals, 202)
+
+ var body map[string]interface{}
+ err = json.Unmarshal(rec.Body.Bytes(), &body)
+ c.Assert(err, check.IsNil)
+ id := body["change"].(string)
+
+ st := d.overlord.State()
+ st.Lock()
+ chg := st.Change(id)
+ st.Unlock()
+ c.Assert(chg, check.NotNil)
+
+ <-chg.Ready()
+
+ st.Lock()
+ err = chg.Err()
+ tr := config.NewTransaction(st)
+ st.Unlock()
+ c.Assert(err, check.IsNil)
+
+ var value string
+ tr.Get("core", "key", &value)
+ c.Assert(value, check.Equals, "value")
+
+}
+
func (s *apiSuite) TestSetConfNumber(c *check.C) {
d := s.daemon(c)
s.mockSnap(c, configYaml)
diff --git a/overlord/devicestate/devicemgr.go b/overlord/devicestate/devicemgr.go
index 504c5c90cd..da7e206897 100644
--- a/overlord/devicestate/devicemgr.go
+++ b/overlord/devicestate/devicemgr.go
@@ -51,6 +51,8 @@ type DeviceManager struct {
lastBecomeOperationalAttempt time.Time
becomeOperationalBackoff time.Duration
+ registered bool
+ reg chan struct{}
}
// Manager returns a new device manager.
@@ -65,7 +67,11 @@ func Manager(s *state.State, hookManager *hookstate.HookManager) (*DeviceManager
}
- m := &DeviceManager{state: s, keypairMgr: keypairMgr, runner: runner}
+ m := &DeviceManager{state: s, keypairMgr: keypairMgr, runner: runner, reg: make(chan struct{})}
+
+ if err := m.confirmRegistered(); err != nil {
+ return nil, err
+ }
hookManager.Register(regexp.MustCompile("^prepare-device$"), newPrepareDeviceHandler)
@@ -76,6 +82,29 @@ func Manager(s *state.State, hookManager *hookstate.HookManager) (*DeviceManager
return m, nil
}
+func (m *DeviceManager) confirmRegistered() error {
+ m.state.Lock()
+ defer m.state.Unlock()
+
+ device, err := auth.Device(m.state)
+ if err != nil {
+ return err
+ }
+
+ if device.Serial != "" {
+ m.markRegistered()
+ }
+ return nil
+}
+
+func (m *DeviceManager) markRegistered() {
+ if m.registered {
+ return
+ }
+ m.registered = true
+ close(m.reg)
+}
+
type prepareDeviceHandler struct{}
func newPrepareDeviceHandler(context *hookstate.Context) hookstate.Handler {
@@ -432,6 +461,11 @@ func (m *DeviceManager) Serial() (*asserts.Serial, error) {
return Serial(m.state)
}
+// Registered returns a channel that is closed when the device is known to have been registered.
+func (m *DeviceManager) Registered() <-chan struct{} {
+ return m.reg
+}
+
// DeviceSessionRequestParams produces a device-session-request with the given nonce, together with other required parameters, the device serial and model assertions.
func (m *DeviceManager) DeviceSessionRequestParams(nonce string) (*auth.DeviceSessionRequestParams, error) {
m.state.Lock()
diff --git a/overlord/devicestate/devicestate_test.go b/overlord/devicestate/devicestate_test.go
index 14dde7c2ae..87506ded5c 100644
--- a/overlord/devicestate/devicestate_test.go
+++ b/overlord/devicestate/devicestate_test.go
@@ -392,6 +392,15 @@ version: gadget
c.Check(device.Model, Equals, "pc")
c.Check(device.Serial, Equals, "9999")
+ ok := false
+ select {
+ case <-s.mgr.Registered():
+ ok = true
+ case <-time.After(5 * time.Second):
+ c.Fatal("should have been marked registered")
+ }
+ c.Check(ok, Equals, true)
+
a, err := s.db.Find(asserts.SerialType, map[string]string{
"brand-id": "canonical",
"model": "pc",
@@ -689,6 +698,14 @@ version: gadget
})
c.Assert(err, IsNil)
+ ok := false
+ select {
+ case <-s.mgr.Registered():
+ default:
+ ok = true
+ }
+ c.Check(ok, Equals, true)
+
s.state.Unlock()
s.mgr.Ensure()
s.mgr.Wait()
@@ -699,6 +716,15 @@ version: gadget
device, err = auth.Device(s.state)
c.Check(err, IsNil)
c.Check(device.Serial, Equals, "9999")
+
+ ok = false
+ select {
+ case <-s.mgr.Registered():
+ ok = true
+ case <-time.After(5 * time.Second):
+ c.Fatal("should have been marked registered")
+ }
+ c.Check(ok, Equals, true)
}
func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterGotSerial(c *C) {
@@ -2150,3 +2176,42 @@ func (s *deviceMgrSuite) TestCanManageRefreshesNoRefreshScheduleManaged(c *C) {
c.Check(devicestate.CanManageRefreshes(st), Equals, false)
}
+
+func (s *deviceMgrSuite) TestReloadRegistered(c *C) {
+ st := state.New(nil)
+
+ hookMgr1, err := hookstate.Manager(st)
+ c.Assert(err, IsNil)
+ mgr1, err := devicestate.Manager(st, hookMgr1)
+ c.Assert(err, IsNil)
+
+ ok := false
+ select {
+ case <-mgr1.Registered():
+ default:
+ ok = true
+ }
+ c.Check(ok, Equals, true)
+
+ st.Lock()
+ auth.SetDevice(st, &auth.DeviceState{
+ Brand: "canonical",
+ Model: "pc",
+ Serial: "serial",
+ })
+ st.Unlock()
+
+ hookMgr2, err := hookstate.Manager(st)
+ c.Assert(err, IsNil)
+ mgr2, err := devicestate.Manager(st, hookMgr2)
+ c.Assert(err, IsNil)
+
+ ok = false
+ select {
+ case <-mgr2.Registered():
+ ok = true
+ case <-time.After(5 * time.Second):
+ c.Fatal("should have been marked registered")
+ }
+ c.Check(ok, Equals, true)
+}
diff --git a/overlord/devicestate/handlers.go b/overlord/devicestate/handlers.go
index 5639483481..49ace1c860 100644
--- a/overlord/devicestate/handlers.go
+++ b/overlord/devicestate/handlers.go
@@ -420,6 +420,17 @@ func getSerialRequestConfig(t *state.Task) (*serialRequestConfig, error) {
return &cfg, nil
}
+func (m *DeviceManager) finishRegistration(t *state.Task, device *auth.DeviceState, serial *asserts.Serial) error {
+ device.Serial = serial.Serial()
+ err := auth.SetDevice(t.State(), device)
+ if err != nil {
+ return err
+ }
+ m.markRegistered()
+ t.SetStatus(state.DoneStatus)
+ return nil
+}
+
func (m *DeviceManager) doRequestSerial(t *state.Task, _ *tomb.Tomb) error {
st := t.State()
st.Lock()
@@ -456,13 +467,7 @@ func (m *DeviceManager) doRequestSerial(t *state.Task, _ *tomb.Tomb) error {
if len(serials) == 1 {
// means we saved the assertion but didn't get to the end of the task
- device.Serial = serials[0].(*asserts.Serial).Serial()
- err := auth.SetDevice(st, device)
- if err != nil {
- return err
- }
- t.SetStatus(state.DoneStatus)
- return nil
+ return m.finishRegistration(t, device, serials[0].(*asserts.Serial))
}
if len(serials) > 1 {
return fmt.Errorf("internal error: multiple serial assertions for the same device key")
@@ -499,13 +504,7 @@ func (m *DeviceManager) doRequestSerial(t *state.Task, _ *tomb.Tomb) error {
return &state.Retry{}
}
- device.Serial = serial.Serial()
- err = auth.SetDevice(st, device)
- if err != nil {
- return err
- }
- t.SetStatus(state.DoneStatus)
- return nil
+ return m.finishRegistration(t, device, serial)
}
var repeatRequestSerial string // for tests
diff --git a/packaging/ubuntu-14.04/snapd.postrm b/packaging/ubuntu-14.04/snapd.postrm
index c58e52fd86..ff324c4fb3 100644
--- a/packaging/ubuntu-14.04/snapd.postrm
+++ b/packaging/ubuntu-14.04/snapd.postrm
@@ -87,13 +87,13 @@ if [ "$1" = "purge" ]; then
echo "Discarding preserved snap namespaces"
# opportunistic as those might not be actually mounted
if [ -d /run/snapd/ns ]; then
- if [ $(ls /run/snapd/ns/*.mnt | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.mnt" | wc -l)" -gt 0 ]; then
for mnt in /run/snapd/ns/*.mnt; do
umount -l "$mnt" || true
rm -f "$mnt"
done
fi
- if [ $(ls /run/snapd/ns/*.fstab | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.fstab" | wc -l)" -gt 0 ]; then
for fstab in /run/snapd/ns/*.fstab; do
rm -f "$fstab"
done
diff --git a/packaging/ubuntu-16.04/snapd.postinst b/packaging/ubuntu-16.04/snapd.postinst
index 9aefd971a3..67a5b1334b 100644
--- a/packaging/ubuntu-16.04/snapd.postinst
+++ b/packaging/ubuntu-16.04/snapd.postinst
@@ -11,4 +11,29 @@ case "$1" in
if dpkg --compare-versions "$2" lt-nl "2.0.7"; then
ldconfig
fi
-esac
+
+ # Ensure that we undo the damage done by the snap.mount unit that was present
+ # in snapd 2.31.
+ #
+ # We found that update scripts make systemd stop inactive mount units and this
+ # in turn stops all the units that depend on it so when the snap.mount unit is
+ # stopped all the per-snap mount units gets stopped along with them. The 2.31
+ # release was only out briefly in xenial-proposed and bionic but to keep the
+ # affected users safe let's start all the per-snap mount units so that snaps no
+ # longer appear as broken after update.
+ if dpkg --compare-versions "$2" ge-nl "2.31" && \
+ dpkg --compare-versions "$2" lt-nl "2.31.2"; then
+ units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true)
+ mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true)
+ for unit in $mounts; do
+ # ensure its really a snap mount unit or systemd unit
+ if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then
+ echo "Skipping non-snapd systemd unit $unit"
+ continue
+ fi
+
+ echo "Starting $unit"
+ systemctl start "$unit" || true
+ done
+ fi
+esac \ No newline at end of file
diff --git a/packaging/ubuntu-16.04/snapd.postrm b/packaging/ubuntu-16.04/snapd.postrm
index efe936f69e..429ed13e5e 100644
--- a/packaging/ubuntu-16.04/snapd.postrm
+++ b/packaging/ubuntu-16.04/snapd.postrm
@@ -90,13 +90,13 @@ if [ "$1" = "purge" ]; then
echo "Discarding preserved snap namespaces"
# opportunistic as those might not be actually mounted
if [ -d /run/snapd/ns ]; then
- if [ $(ls /run/snapd/ns/*.mnt | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.mnt" | wc -l)" -gt 0 ]; then
for mnt in /run/snapd/ns/*.mnt; do
umount -l "$mnt" || true
rm -f "$mnt"
done
fi
- if [ $(ls /run/snapd/ns/*.fstab | wc -l) -gt 0 ]; then
+ if [ "$(find /run/snapd/ns/ -name "*.fstab" | wc -l)" -gt 0 ]; then
for fstab in /run/snapd/ns/*.fstab; do
rm -f "$fstab"
done
diff --git a/packaging/ubuntu-16.04/tests/integrationtests b/packaging/ubuntu-16.04/tests/integrationtests
index aec488e290..191ab592a3 100644
--- a/packaging/ubuntu-16.04/tests/integrationtests
+++ b/packaging/ubuntu-16.04/tests/integrationtests
@@ -18,7 +18,7 @@ fi
systemctl daemon-reload
# ensure we are not get killed too easily
-echo -950 > /proc/$$/oom_score_adj
+printf "%s\n" "-950" > /proc/$$/oom_score_adj
# see what mem we have (for debugging)
cat /proc/meminfo
@@ -45,4 +45,4 @@ go get -u github.com/snapcore/spread/cmd/spread
# store journal info for inspectsion
journalctl --sync
-journalctl -ab > $ADT_ARTIFACTS/journal.txt
+journalctl -ab > "$ADT_ARTIFACTS"/journal.txt
diff --git a/run-checks b/run-checks
index 925dcc1696..472f71478d 100755
--- a/run-checks
+++ b/run-checks
@@ -16,6 +16,16 @@ else
goctest="go test"
fi
COVERMODE=${COVERMODE:-atomic}
+
+# add workaround for https://github.com/golang/go/issues/24449
+if [ "$(uname -m)" = "s390x" ]; then
+ if go version | grep -q go1.10; then
+ echo "covermode 'atomic' crashes on s390x with go1.10, reseting "
+ echo "to 'set'. see https://github.com/golang/go/issues/24449"
+ COVERMODE="set"
+ fi
+fi
+
export GOPATH="${GOPATH:-$(realpath "$(dirname "$0")"/../../../../)}"
export PATH="$PATH:${GOPATH%%:*}/bin"
diff --git a/snap/pack/pack.go b/snap/pack/pack.go
index 9092159abd..5bf9e6e6a7 100644
--- a/snap/pack/pack.go
+++ b/snap/pack/pack.go
@@ -211,24 +211,36 @@ func copyToBuildDir(sourceDir, buildDir string) error {
})
}
-func prepare(sourceDir, targetDir, buildDir string) (snapName string, err error) {
+// CheckSkeleton attempts to validate snap data in source directory
+func CheckSkeleton(sourceDir string) error {
+ _, err := loadAndValidate(sourceDir)
+ return err
+}
+
+func loadAndValidate(sourceDir string) (*snap.Info, error) {
// ensure we have valid content
yaml, err := ioutil.ReadFile(filepath.Join(sourceDir, "meta", "snap.yaml"))
if err != nil {
- return "", err
+ return nil, err
}
info, err := snap.InfoFromSnapYaml(yaml)
if err != nil {
- return "", err
+ return nil, err
}
- err = snap.Validate(info)
- if err != nil {
- return "", err
+ if err := snap.Validate(info); err != nil {
+ return nil, err
}
- err = snap.ValidateContainer(snapdir.New(sourceDir), info, logger.Noticef)
+ if err := snap.ValidateContainer(snapdir.New(sourceDir), info, logger.Noticef); err != nil {
+ return nil, err
+ }
+ return info, nil
+}
+
+func prepare(sourceDir, targetDir, buildDir string) (snapName string, err error) {
+ info, err := loadAndValidate(sourceDir)
if err != nil {
return "", err
}
diff --git a/snap/pack/pack_test.go b/snap/pack/pack_test.go
index b442e0c560..245d7ed3ab 100644
--- a/snap/pack/pack_test.go
+++ b/snap/pack/pack_test.go
@@ -123,6 +123,18 @@ apps:
c.Assert(err, Equals, snap.ErrMissingPaths)
}
+func (s *packSuite) TestValidateMissingAppFailsWithErrMissingPaths(c *C) {
+ sourceDir := makeExampleSnapSourceDir(c, `name: hello
+version: 0
+apps:
+ foo:
+ command: bin/hello-world
+`)
+ c.Assert(os.Remove(filepath.Join(sourceDir, "bin", "hello-world")), IsNil)
+ err := pack.CheckSkeleton(sourceDir)
+ c.Assert(err, Equals, snap.ErrMissingPaths)
+}
+
func (s *packSuite) TestCopyCopies(c *C) {
sourceDir := makeExampleSnapSourceDir(c, "{name: hello, version: 0}")
// actually this'll be on /tmp so it'll be a link
diff --git a/snap/squashfs/squashfs_test.go b/snap/squashfs/squashfs_test.go
index ad121c924e..f1c1e702e9 100644
--- a/snap/squashfs/squashfs_test.go
+++ b/snap/squashfs/squashfs_test.go
@@ -423,5 +423,5 @@ func (s *SquashfsTestSuite) TestBuildDate(c *C) {
c.Assert(snap.Build(d), IsNil)
// and see it's BuildDate is _now_, not _then_.
c.Check(BuildDate(filename), Equals, snap.BuildDate())
- c.Check(math.Abs(now.Sub(snap.BuildDate()).Seconds()) <= 61, Equals, true)
+ c.Check(math.Abs(now.Sub(snap.BuildDate()).Seconds()) <= 61, Equals, true, Commentf("Unexpected build date %s", snap.BuildDate()))
}
diff --git a/spread.yaml b/spread.yaml
index 0f67e25470..7a55e94c3b 100644
--- a/spread.yaml
+++ b/spread.yaml
@@ -49,10 +49,14 @@ backends:
key: "$(HOST: echo $SPREAD_GOOGLE_KEY)"
location: computeengine/us-east1-b
systems:
- - ubuntu-16.04-64:
- workers: 8
- ubuntu-14.04-64:
workers: 6
+ - ubuntu-16.04-64:
+ workers: 8
+ # disabled because of: cannot connect: cannot connect to google:ubuntu-18.04-64 (mar200647-634341): ssh: handshake failed: EOF
+ #- ubuntu-18.04-64:
+ # image: ubuntu-os-cloud-devel/ubuntu-1804-lts
+ # workers: 6
- ubuntu-core-16-64:
image: ubuntu-16.04-64
@@ -439,6 +443,8 @@ suites:
# we keep them disabled. A later PR will enable most tests and
# drop this blacklist.
systems: [-ubuntu-core-16-*, -fedora-*, -opensuse-*]
+ # unittests are run as part of the autopkgtest build already
+ backends: [-autopkgtest]
environment:
# env vars required for coverage reporting from a spread task
TRAVIS_BUILD_NUMBER: "$(HOST: echo $TRAVIS_BUILD_NUMBER)"
diff --git a/store/store.go b/store/store.go
index 238595e79c..826f67c862 100644
--- a/store/store.go
+++ b/store/store.go
@@ -585,7 +585,7 @@ func authenticateDevice(r *http.Request, device *auth.DeviceState) {
}
}
-func (s *Store) setStoreID(r *http.Request) {
+func (s *Store) setStoreID(r *http.Request) (customStore bool) {
storeID := s.fallbackStoreID
if s.authContext != nil {
cand, err := s.authContext.StoreID(storeID)
@@ -597,9 +597,18 @@ func (s *Store) setStoreID(r *http.Request) {
}
if storeID != "" {
r.Header.Set("X-Ubuntu-Store", storeID)
+ return true
}
+ return false
}
+type deviceAuthNeed int
+
+const (
+ deviceAuthPreferred deviceAuthNeed = iota
+ deviceAuthCustomStoreOnly
+)
+
// requestOptions specifies parameters for store requests.
type requestOptions struct {
Method string
@@ -608,6 +617,13 @@ type requestOptions struct {
ContentType string
ExtraHeaders map[string]string
Data []byte
+
+ // DeviceAuthNeed indicates the level of need to supply device
+ // authorization for this request, can be:
+ // - deviceAuthPreferred: should be provided if available
+ // - deviceAuthCustomStoreOnly: should be provided only in case
+ // of a custom store
+ DeviceAuthNeed deviceAuthNeed
}
func (r *requestOptions) addHeader(k, v string) {
@@ -814,7 +830,9 @@ func (s *Store) newRequest(reqOptions *requestOptions, user *auth.UserState) (*h
return nil, err
}
- if s.authContext != nil {
+ customStore := s.setStoreID(req)
+
+ if s.authContext != nil && (customStore || reqOptions.DeviceAuthNeed != deviceAuthCustomStoreOnly) {
device, err := s.authContext.Device()
if err != nil {
return nil, err
@@ -861,8 +879,6 @@ func (s *Store) newRequest(reqOptions *requestOptions, user *auth.UserState) (*h
req.Header.Set(header, value)
}
- s.setStoreID(req)
-
return req, nil
}
@@ -1175,9 +1191,10 @@ func (s *Store) Find(search *Search, user *auth.UserState) ([]*snap.Info, error)
// Sections retrieves the list of available store sections.
func (s *Store) Sections(ctx context.Context, user *auth.UserState) ([]string, error) {
reqOptions := &requestOptions{
- Method: "GET",
- URL: s.endpointURL(sectionsEndpPath, nil),
- Accept: halJsonContentType,
+ Method: "GET",
+ URL: s.endpointURL(sectionsEndpPath, nil),
+ Accept: halJsonContentType,
+ DeviceAuthNeed: deviceAuthCustomStoreOnly,
}
var sectionData sectionResults
@@ -1216,9 +1233,10 @@ func (s *Store) WriteCatalogs(ctx context.Context, names io.Writer, adder SnapAd
u.RawQuery = q.Encode()
reqOptions := &requestOptions{
- Method: "GET",
- URL: &u,
- Accept: halJsonContentType,
+ Method: "GET",
+ URL: &u,
+ Accept: halJsonContentType,
+ DeviceAuthNeed: deviceAuthCustomStoreOnly,
}
// do not log body for catalog updates (its huge)
diff --git a/store/store_test.go b/store/store_test.go
index 9c9a471ee2..7b914704da 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -2777,6 +2777,8 @@ func (s *storeTestSuite) TestSectionsQuery(c *C) {
n := 0
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assertRequest(c, r, "GET", sectionsPath)
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, "")
+
switch n {
case 0:
// All good.
@@ -2796,7 +2798,41 @@ func (s *storeTestSuite) TestSectionsQuery(c *C) {
cfg := Config{
StoreBaseURL: serverURL,
}
- sto := New(&cfg, nil)
+ authContext := &testAuthContext{c: c, device: s.device}
+ sto := New(&cfg, authContext)
+
+ sections, err := sto.Sections(context.TODO(), s.user)
+ c.Check(err, IsNil)
+ c.Check(sections, DeepEquals, []string{"featured", "database"})
+}
+
+func (s *storeTestSuite) TestSectionsQueryCustomStore(c *C) {
+ n := 0
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ assertRequest(c, r, "GET", sectionsPath)
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ switch n {
+ case 0:
+ // All good.
+ default:
+ c.Fatalf("what? %d", n)
+ }
+
+ w.Header().Set("Content-Type", "application/hal+json")
+ w.WriteHeader(200)
+ io.WriteString(w, MockSectionsJSON)
+ n++
+ }))
+ c.Assert(mockServer, NotNil)
+ defer mockServer.Close()
+
+ serverURL, _ := url.Parse(mockServer.URL)
+ cfg := Config{
+ StoreBaseURL: serverURL,
+ }
+ authContext := &testAuthContext{c: c, device: s.device, storeID: "my-brand-store"}
+ sto := New(&cfg, authContext)
sections, err := sto.Sections(context.TODO(), s.user)
c.Check(err, IsNil)
@@ -2846,6 +2882,8 @@ func (s *storeTestSuite) testSnapCommands(c *C, onClassic bool) {
n := 0
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, "")
+
switch n {
case 0:
query := r.URL.Query()
@@ -2870,7 +2908,8 @@ func (s *storeTestSuite) testSnapCommands(c *C, onClassic bool) {
defer mockServer.Close()
serverURL, _ := url.Parse(mockServer.URL)
- sto := New(&Config{StoreBaseURL: serverURL}, nil)
+ authContext := &testAuthContext{c: c, device: s.device}
+ sto := New(&Config{StoreBaseURL: serverURL}, authContext)
db, err := advisor.Create()
c.Assert(err, IsNil)
diff --git a/tests/lib/names.sh b/tests/lib/names.sh
index 9a0a6e5d70..6b898f5b61 100644
--- a/tests/lib/names.sh
+++ b/tests/lib/names.sh
@@ -1,4 +1,5 @@
#!/bin/bash
+# shellcheck disable=SC2034
gadget_name=$(snap list | grep 'gadget$' | awk '{ print $1 }')
kernel_name=$(snap list | grep 'kernel$' | awk '{ print $1 }')
diff --git a/tests/lib/pkgdb.sh b/tests/lib/pkgdb.sh
index 2b77f43115..ec31ec9a03 100755
--- a/tests/lib/pkgdb.sh
+++ b/tests/lib/pkgdb.sh
@@ -205,6 +205,7 @@ distro_install_package() {
}
distro_purge_package() {
+ # shellcheck disable=SC2046
set -- $(
for pkg in "$@" ; do
package_name=$(distro_name_package "$pkg")
@@ -428,6 +429,12 @@ pkg_dependencies_ubuntu_classic(){
linux-image-extra-4.13.0-16-generic
"
;;
+ ubuntu-18.04-64)
+ echo "
+ linux-image-extra-$(uname -r)
+ squashfs-tools
+ "
+ ;;
ubuntu-*)
echo "
linux-image-extra-$(uname -r)
diff --git a/tests/lib/prepare.sh b/tests/lib/prepare.sh
index 44c7d8042e..6efeb3f4ca 100755
--- a/tests/lib/prepare.sh
+++ b/tests/lib/prepare.sh
@@ -210,7 +210,7 @@ prepare_classic() {
done
# Copy all of the snaps back to the spool directory. From there we
# will reuse them during subsequent `snap install` operations.
- cp *.snap /var/lib/snapd/snaps/
+ cp -- *.snap /var/lib/snapd/snaps/
set +x
)
diff --git a/tests/lib/reset.sh b/tests/lib/reset.sh
index db64729c2a..a690042fe8 100755
--- a/tests/lib/reset.sh
+++ b/tests/lib/reset.sh
@@ -13,6 +13,18 @@ reset_classic() {
# have changed on the disk.
systemctl daemon-reload
+ echo "Ensure the service is active before stopping it"
+ retries=20
+ systemctl status snapd.service snapd.socket || true
+ while systemctl status snapd.service snapd.socket | grep "Active: activating"; do
+ if [ $retries -eq 0 ]; then
+ echo "snapd service or socket not active"
+ exit 1
+ fi
+ retries=$(( retries - 1 ))
+ sleep 1
+ done
+
systemd_stop_units snapd.service snapd.socket
case "$SPREAD_SYSTEM" in
diff --git a/tests/lib/snaps/test-snapd-service/bin/reload b/tests/lib/snaps/test-snapd-service/bin/reload
index e04f606e14..4e14f5aa90 100755
--- a/tests/lib/snaps/test-snapd-service/bin/reload
+++ b/tests/lib/snaps/test-snapd-service/bin/reload
@@ -5,4 +5,4 @@ if [ -z "$MAINPID" ]; then
exit 1
fi
echo "reloading main PID: $MAINPID"
-kill -HUP $MAINPID
+kill -HUP "$MAINPID"
diff --git a/tests/lib/store.sh b/tests/lib/store.sh
index 3915b3e9f9..84db342d44 100644
--- a/tests/lib/store.sh
+++ b/tests/lib/store.sh
@@ -48,6 +48,9 @@ make_snap_installable(){
}
setup_fake_store(){
+ # before switching make sure we have a session macaroon
+ snap find test-snapd-tools
+
local top_dir=$1
mkdir -p "$top_dir/asserts"
diff --git a/tests/main/classic-ubuntu-core-transition-auth/task.yaml b/tests/main/classic-ubuntu-core-transition-auth/task.yaml
index ffeef91408..34fffc7976 100644
--- a/tests/main/classic-ubuntu-core-transition-auth/task.yaml
+++ b/tests/main/classic-ubuntu-core-transition-auth/task.yaml
@@ -6,6 +6,10 @@ summary: Ensure that the ubuntu-core -> core transition works with auth.json
# fishy going on and the snapd service gets terminated during the process.
systems: [-ubuntu-core-16-*, -ubuntu-*-ppc64el, -fedora-*, -opensuse-*]
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
warn-timeout: 1m
kill-timeout: 5m
debug: |
diff --git a/tests/main/classic-ubuntu-core-transition-two-cores/task.yaml b/tests/main/classic-ubuntu-core-transition-two-cores/task.yaml
index 93a46d37a9..4bdd118a03 100644
--- a/tests/main/classic-ubuntu-core-transition-two-cores/task.yaml
+++ b/tests/main/classic-ubuntu-core-transition-two-cores/task.yaml
@@ -4,6 +4,10 @@ summary: Ensure that the ubuntu-core -> core transition works with two cores
# we disable on ppc64el because the downloads are very slow there
systems: [-ubuntu-core-16-*, -ubuntu-*-ppc64el]
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
warn-timeout: 1m
kill-timeout: 5m
debug: |
diff --git a/tests/main/classic-ubuntu-core-transition/task.yaml b/tests/main/classic-ubuntu-core-transition/task.yaml
index 7a6204a0c1..c58c302004 100644
--- a/tests/main/classic-ubuntu-core-transition/task.yaml
+++ b/tests/main/classic-ubuntu-core-transition/task.yaml
@@ -6,6 +6,10 @@ summary: Ensure that the ubuntu-core -> core transition works
# fishy going on and the snapd service gets terminated during the process.
systems: [-ubuntu-core-16-*, -ubuntu-*-ppc64el, -fedora-*, -opensuse-*, -ubuntu-*-i386]
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
warn-timeout: 1m
kill-timeout: 5m
diff --git a/tests/main/econnreset/task.yaml b/tests/main/econnreset/task.yaml
index a05216395d..f9b5b409c4 100644
--- a/tests/main/econnreset/task.yaml
+++ b/tests/main/econnreset/task.yaml
@@ -5,6 +5,10 @@ restore: |
rm -f test-snapd-huge_*
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
execute: |
echo "Downloading a large snap in the background"
su -c "/usr/bin/env SNAPD_DEBUG=1 snap download --edge test-snapd-huge 2>snap-download.log" test &
diff --git a/tests/main/interfaces-kernel-module-control/task.yaml b/tests/main/interfaces-kernel-module-control/task.yaml
index f8dcdeb828..905be7a9f4 100644
--- a/tests/main/interfaces-kernel-module-control/task.yaml
+++ b/tests/main/interfaces-kernel-module-control/task.yaml
@@ -40,6 +40,11 @@ execute: |
CONNECTED_PATTERN=":kernel-module-control +.*test-snapd-kernel-module-consumer"
DISCONNECTED_PATTERN="\- +test-snapd-kernel-module-consumer:kernel-module-control"
+ if ! [ -f /lib/modules/$(uname -r)/kernel/fs/$MODULE/$MODULE.ko ]; then
+ echo "$MODULE module not available in the system"
+ exit 0
+ fi
+
echo "The plug is disconnected by default"
snap interfaces | MATCH "$DISCONNECTED_PATTERN"
@@ -57,7 +62,7 @@ execute: |
echo "And the snap is able to insert a module"
if lsmod | MATCH $MODULE; then
touch module_present
- rmmod minix
+ rmmod $MODULE
fi
lsmod | MATCH -v $MODULE
test-snapd-kernel-module-consumer.insmod $MODULE_PATH
diff --git a/tests/main/interfaces-snapd-control-with-manage/task.yaml b/tests/main/interfaces-snapd-control-with-manage/task.yaml
index 46ab4303db..d3e8f53cdf 100644
--- a/tests/main/interfaces-snapd-control-with-manage/task.yaml
+++ b/tests/main/interfaces-snapd-control-with-manage/task.yaml
@@ -49,7 +49,10 @@ restore: |
fi
. $TESTSLIB/store.sh
teardown_fake_store $BLOB_DIR
-
+
+debug: |
+ jq .data.auth.device /var/lib/snapd/state.json
+
execute: |
if [ "$TRUST_TEST_KEYS" = "false" ]; then
echo "This test needs test keys to be trusted"
diff --git a/tests/main/layout/task.yaml b/tests/main/layout/task.yaml
index 15ab8516ea..49cecba482 100644
--- a/tests/main/layout/task.yaml
+++ b/tests/main/layout/task.yaml
@@ -9,49 +9,49 @@ prepare: |
snap set core experimental.layouts=true
. $TESTSLIB/snaps.sh
install_local test-snapd-layout
-debug: |
- # We saw some failures where it looked like we were confined by the most
- # wrong thing imaginable. To get an idea what is going on let's show
- # the profile we were using when the test fails.
- # Before per-snap update-ns profiles are here.
- cat /etc/apparmor.d/*snap.core.*.usr.lib.snapd.snap-confine* || :
- # Once per-snap update-ns profiles are here.
- cat /var/lib/snapd/apparmor/profiles/snap-update-ns.test-snapd-layout || :
execute: |
- echo "snap declaring layouts doesn't explode on startup"
- test-snapd-layout.sh -c "true"
-
- echo "layout declarations are honored"
+ . $TESTSLIB/snaps.sh
+ for i in $(seq 2); do
+ if [ "$i" -eq 2 ]; then
+ echo "The snap works across refreshes"
+ install_local test-snapd-layout
+ fi
+
+ echo "snap declaring layouts doesn't explode on startup"
+ test-snapd-layout.sh -c "true"
+
+ echo "layout declarations are honored"
- test-snapd-layout.sh -c "test -d /etc/demo"
- test-snapd-layout.sh -c "test -f /etc/demo.conf"
- test-snapd-layout.sh -c "test -h /etc/demo.cfg"
- test "$(test-snapd-layout.sh -c "readlink /etc/demo.cfg")" = "$(test-snapd-layout.sh -c 'echo $SNAP_COMMON/etc/demo.conf')"
- test-snapd-layout.sh -c "test -d /usr/share/demo"
- test-snapd-layout.sh -c "test -d /var/lib/demo"
- test-snapd-layout.sh -c "test -d /var/cache/demo"
- test-snapd-layout.sh -c "test -d /opt/demo"
+ test-snapd-layout.sh -c "test -d /etc/demo"
+ test-snapd-layout.sh -c "test -f /etc/demo.conf"
+ test-snapd-layout.sh -c "test -h /etc/demo.cfg"
+ test "$(test-snapd-layout.sh -c "readlink /etc/demo.cfg")" = "$(test-snapd-layout.sh -c 'echo $SNAP_COMMON/etc/demo.conf')"
+ test-snapd-layout.sh -c "test -d /usr/share/demo"
+ test-snapd-layout.sh -c "test -d /var/lib/demo"
+ test-snapd-layout.sh -c "test -d /var/cache/demo"
+ test-snapd-layout.sh -c "test -d /opt/demo"
- echo "layout locations pointing to SNAP_DATA and SNAP_COMMON are writable"
- echo "and the writes go to the right place in the backing store"
+ echo "layout locations pointing to SNAP_DATA and SNAP_COMMON are writable"
+ echo "and the writes go to the right place in the backing store"
- test-snapd-layout.sh -c "echo foo-1 > /etc/demo/writable"
- test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo/writable')" = "foo-1"
+ test-snapd-layout.sh -c "echo foo-1 > /etc/demo/writable"
+ test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo/writable')" = "foo-1"
- test-snapd-layout.sh -c "echo foo-2 > /etc/demo.conf"
- test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo.conf')" = "foo-2"
+ test-snapd-layout.sh -c "echo foo-2 > /etc/demo.conf"
+ test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo.conf')" = "foo-2"
- # NOTE: this is a symlink to demo.conf, effectively
- test-snapd-layout.sh -c "echo foo-3 > /etc/demo.cfg"
- test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo.conf')" = "foo-3"
+ # NOTE: this is a symlink to demo.conf, effectively
+ test-snapd-layout.sh -c "echo foo-3 > /etc/demo.cfg"
+ test "$(test-snapd-layout.sh -c 'cat $SNAP_COMMON/etc/demo.conf')" = "foo-3"
- test-snapd-layout.sh -c "echo foo-4 > /var/lib/demo/writable"
- test "$(test-snapd-layout.sh -c 'cat $SNAP_DATA/var/lib/demo/writable')" = "foo-4"
+ test-snapd-layout.sh -c "echo foo-4 > /var/lib/demo/writable"
+ test "$(test-snapd-layout.sh -c 'cat $SNAP_DATA/var/lib/demo/writable')" = "foo-4"
- test-snapd-layout.sh -c "echo foo-5 > /var/cache/demo/writable"
- test "$(test-snapd-layout.sh -c 'cat $SNAP_DATA/var/cache/demo/writable')" = "foo-5"
+ test-snapd-layout.sh -c "echo foo-5 > /var/cache/demo/writable"
+ test "$(test-snapd-layout.sh -c 'cat $SNAP_DATA/var/cache/demo/writable')" = "foo-5"
- echo "layout locations pointing to SNAP are readable"
+ echo "layout locations pointing to SNAP are readable"
- test-snapd-layout.sh -c "test -r /usr/share/demo/file"
- test-snapd-layout.sh -c "test -r /opt/demo/file"
+ test-snapd-layout.sh -c "test -r /usr/share/demo/file"
+ test-snapd-layout.sh -c "test -r /opt/demo/file"
+ done
diff --git a/tests/main/listing/task.yaml b/tests/main/listing/task.yaml
index a7567ee187..067a4f9dfe 100644
--- a/tests/main/listing/task.yaml
+++ b/tests/main/listing/task.yaml
@@ -4,8 +4,11 @@ prepare: |
. $TESTSLIB/snaps.sh
install_local test-snapd-tools
-execute: |
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+execute: |
echo "List prints core snap version"
# most core versions should be like "16-2", so [0-9]{2}-[0-9.]+
# but edge will have a timestamp in there, "16.2+201701010932", so add an optional \+[0-9]+ to the end
diff --git a/tests/main/lxd/task.yaml b/tests/main/lxd/task.yaml
index 7cc1d1fb4a..e5c61e3167 100644
--- a/tests/main/lxd/task.yaml
+++ b/tests/main/lxd/task.yaml
@@ -7,6 +7,10 @@ summary: Ensure that lxd works
# FIXME LXD has some issue on Google images.
systems: [ubuntu-16.04-32, ubuntu-core-*]
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
# lxd downloads can be quite slow
kill-timeout: 25m
diff --git a/tests/main/prepare-image-uboot/task.yaml b/tests/main/prepare-image-uboot/task.yaml
index a233f7a29f..5285c77fe8 100644
--- a/tests/main/prepare-image-uboot/task.yaml
+++ b/tests/main/prepare-image-uboot/task.yaml
@@ -1,5 +1,9 @@
summary: Check that prepare-image works for uboot-systems
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
environment:
ROOT: /home/test/tmp/
IMAGE: /home/test/tmp/image
diff --git a/tests/main/searching/task.yaml b/tests/main/searching/task.yaml
index 67629ebe53..337236da86 100644
--- a/tests/main/searching/task.yaml
+++ b/tests/main/searching/task.yaml
@@ -3,6 +3,10 @@ summary: Check snap search
# s390x,ppc64el have nothing featured
systems: [-ubuntu-*-ppc64el, -ubuntu-*-s390x]
+# autopkgtest run only a subset of tests that deals with the integration
+# with the distro
+backends: [-autopkgtest]
+
restore: |
rm -f featured.txt
diff --git a/tests/main/system-core-alias/task.yaml b/tests/main/system-core-alias/task.yaml
new file mode 100644
index 0000000000..a7c0c8c8a0
--- /dev/null
+++ b/tests/main/system-core-alias/task.yaml
@@ -0,0 +1,19 @@
+summary: Verify that 'system' can be used as an alias for 'core'
+
+execute: |
+ echo "When a configuration is set for the core snap"
+ snap set core foo.bar=true
+ snap get core foo.bar | MATCH "true"
+
+ echo "It can be retrieved using system alias"
+ snap get system foo.bar | MATCH "true"
+
+ echo "When a configuration is set using the system alias"
+ snap set system foo.bar=baz
+ snap get system foo.bar | MATCH "baz"
+
+ echo "It is also visible through the core snap"
+ snap get core foo.bar | MATCH "baz"
+debug: |
+ snap get core -d || true
+ snap get system -d || true
diff --git a/tests/upgrade/basic/task.yaml b/tests/upgrade/basic/task.yaml
index b4903ce9c1..b907dbdf8e 100644
--- a/tests/upgrade/basic/task.yaml
+++ b/tests/upgrade/basic/task.yaml
@@ -18,7 +18,10 @@ execute: |
. "$TESTSLIB/snaps.sh"
echo "Remove snapd and snap-confine"
- distro_purge_package snapd snapd-selinux snap-confine || true
+ distro_purge_package snapd snap-confine || true
+ if [[ "$SPREAD_SYSTEM" = fedora-* ]] ; then
+ distro_purge_package snapd-selinux || true
+ fi
echo "Install previous snapd version from the store"
distro_install_package snap-confine snapd
diff --git a/testutil/lowlevel.go b/testutil/lowlevel.go
index 035ab085ef..18d431736e 100644
--- a/testutil/lowlevel.go
+++ b/testutil/lowlevel.go
@@ -90,6 +90,10 @@ func formatOpenFlags(flags int) string {
flags ^= syscall.O_EXCL
fl = append(fl, "O_EXCL")
}
+ if flags&sys.O_PATH != 0 {
+ flags ^= sys.O_PATH
+ fl = append(fl, "O_PATH")
+ }
if flags != 0 {
panic(fmt.Errorf("unrecognized open flags %d", flags))
}