summaryrefslogtreecommitdiff
diff options
-rw-r--r--arch/arch.go43
-rw-r--r--arch/arch_test.go26
-rw-r--r--asserts/model.go6
-rw-r--r--boot/boot.go92
-rw-r--r--boot/boot_test.go95
-rw-r--r--cmd/Makefile.am4
-rw-r--r--cmd/libsnap-confine-private/snap-test.c9
-rw-r--r--cmd/libsnap-confine-private/test-utils-test.c24
-rw-r--r--cmd/libsnap-confine-private/test-utils.c35
-rw-r--r--cmd/libsnap-confine-private/test-utils.h6
-rw-r--r--cmd/snap-confine/cookie-support-test.c9
-rw-r--r--cmd/snap-confine/mount-support.c8
-rw-r--r--cmd/snap-confine/snap-confine-args-test.c54
-rw-r--r--cmd/snap-confine/snap-confine-args.c1
-rw-r--r--cmd/snap-confine/snap-confine-invocation-test.c162
-rw-r--r--cmd/snap-repair/runner.go2
-rw-r--r--cmd/snap-repair/runner_test.go6
-rw-r--r--cmd/snap-seccomp/export_test.go16
-rw-r--r--cmd/snap-seccomp/main.go22
-rw-r--r--cmd/snap-seccomp/main_test.go8
-rw-r--r--daemon/api_test.go2
-rw-r--r--daemon/response.go2
-rw-r--r--dirs/dirs.go14
-rw-r--r--dirs/dirs_test.go10
-rw-r--r--errtracker/errtracker.go2
-rw-r--r--errtracker/errtracker_test.go4
-rw-r--r--httputil/useragent.go2
-rw-r--r--image/export_test.go1
-rw-r--r--image/image.go166
-rw-r--r--image/image_test.go180
-rw-r--r--interfaces/seccomp/backend.go12
-rw-r--r--interfaces/seccomp/backend_test.go12
-rw-r--r--interfaces/seccomp/export_test.go8
-rw-r--r--overlord/snapstate/backend/mountunit_test.go1
-rw-r--r--overlord/snapstate/check_snap.go2
-rw-r--r--overlord/snapstate/check_snap_test.go2
-rw-r--r--overlord/snapstate/snapstate.go25
-rw-r--r--overlord/snapstate/snapstate_test.go12
-rw-r--r--packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch15
-rw-r--r--snap/channel/channel.go64
-rw-r--r--snap/channel/channel_test.go107
-rw-r--r--snap/snapenv/snapenv.go2
-rw-r--r--snap/snapenv/snapenv_test.go6
-rw-r--r--store/store.go2
-rw-r--r--store/store_test.go16
-rw-r--r--systemd/systemd.go1
-rw-r--r--systemd/systemd_test.go5
-rw-r--r--tests/main/interfaces-calendar-service/task.yaml3
-rw-r--r--tests/main/interfaces-contacts-service/task.yaml24
-rw-r--r--tests/main/interfaces-timeserver-control/task.yaml3
-rw-r--r--tests/unit/go/task.yaml3
51 files changed, 893 insertions, 443 deletions
diff --git a/arch/arch.go b/arch/arch.go
index d98139476e..9b8a578f36 100644
--- a/arch/arch.go
+++ b/arch/arch.go
@@ -33,33 +33,30 @@ type ArchitectureType string
// change the architecture. This is important to e.g. install
// armhf snaps onto a armhf image that is generated on an amd64
// machine
-var arch = ArchitectureType(ubuntuArchFromGoArch(runtime.GOARCH))
+var arch = ArchitectureType(dpkgArchFromGoArch(runtime.GOARCH))
// SetArchitecture allows overriding the auto detected Architecture
func SetArchitecture(newArch ArchitectureType) {
arch = newArch
}
-// FIXME: rename all Ubuntu*Architecture() to SnapdArchitecture()
-// (or DpkgArchitecture)
-
-// UbuntuArchitecture returns the debian equivalent architecture for the
+// DpkgArchitecture returns the debian equivalent architecture for the
// currently running architecture.
//
// If the architecture does not map any debian architecture, the
// GOARCH is returned.
-func UbuntuArchitecture() string {
+func DpkgArchitecture() string {
return string(arch)
}
-// ubuntuArchFromGoArch maps a go architecture string to the coresponding
-// Ubuntu architecture string.
+// dpkgArchFromGoArch maps a go architecture string to the coresponding
+// Debian equivalent architecture string.
//
// E.g. the go "386" architecture string maps to the ubuntu "i386"
// architecture.
-func ubuntuArchFromGoArch(goarch string) string {
+func dpkgArchFromGoArch(goarch string) string {
goArchMapping := map[string]string{
- // go ubuntu
+ // go dpkg
"386": "i386",
"amd64": "amd64",
"arm": "armhf",
@@ -82,27 +79,27 @@ func ubuntuArchFromGoArch(goarch string) string {
}
}
- ubuntuArch := goArchMapping[goarch]
- if ubuntuArch == "" {
+ dpkgArch := goArchMapping[goarch]
+ if dpkgArch == "" {
log.Panicf("unknown goarch %q", goarch)
}
- return ubuntuArch
+ return dpkgArch
}
-// UbuntuKernelArchitecture return the debian equivalent architecture
+// DpkgKernelArchitecture returns the debian equivalent architecture
// for the current running kernel. This is usually the same as the
-// UbuntuArchitecture - however there maybe cases that you run e.g.
+// DpkgArchitecture - however there maybe cases that you run e.g.
// a snapd:i386 on an amd64 kernel.
-func UbuntuKernelArchitecture() string {
- return ubuntuArchFromKernelArch(osutil.MachineName())
+func DpkgKernelArchitecture() string {
+ return dpkgArchFromKernelArch(osutil.MachineName())
}
-// ubuntuArchFromkernelArch maps the kernel architecture as reported
+// dpkgArchFromkernelArch maps the kernel architecture as reported
// via uname() to the dpkg architecture
-func ubuntuArchFromKernelArch(utsMachine string) string {
+func dpkgArchFromKernelArch(utsMachine string) string {
kernelArchMapping := map[string]string{
- // kernel ubuntu
+ // kernel dpkg
"i686": "i386",
"x86_64": "amd64",
"armv7l": "armhf",
@@ -115,12 +112,12 @@ func ubuntuArchFromKernelArch(utsMachine string) string {
"ppc64": "ppc64",
}
- ubuntuArch := kernelArchMapping[utsMachine]
- if ubuntuArch == "" {
+ dpkgArch := kernelArchMapping[utsMachine]
+ if dpkgArch == "" {
log.Panicf("unknown kernel arch %q", utsMachine)
}
- return ubuntuArch
+ return dpkgArch
}
// IsSupportedArchitecture returns true if the system architecture is in the
diff --git a/arch/arch_test.go b/arch/arch_test.go
index cc5c82c4a9..ef3e6401af 100644
--- a/arch/arch_test.go
+++ b/arch/arch_test.go
@@ -33,24 +33,24 @@ var _ = Suite(&ArchTestSuite{})
type ArchTestSuite struct {
}
-func (ts *ArchTestSuite) TestUbuntuArchitecture(c *C) {
- c.Check(ubuntuArchFromGoArch("386"), Equals, "i386")
- c.Check(ubuntuArchFromGoArch("amd64"), Equals, "amd64")
- c.Check(ubuntuArchFromGoArch("arm"), Equals, "armhf")
- c.Check(ubuntuArchFromGoArch("arm64"), Equals, "arm64")
- c.Check(ubuntuArchFromGoArch("ppc64le"), Equals, "ppc64el")
- c.Check(ubuntuArchFromGoArch("ppc64"), Equals, "ppc64")
- c.Check(ubuntuArchFromGoArch("s390x"), Equals, "s390x")
- c.Check(ubuntuArchFromGoArch("ppc"), Equals, "powerpc")
- c.Check(ubuntuArchFromGoArch("ppc64"), Equals, "ppc64")
+func (ts *ArchTestSuite) TestArchDpkgArchitecture(c *C) {
+ c.Check(dpkgArchFromGoArch("386"), Equals, "i386")
+ c.Check(dpkgArchFromGoArch("amd64"), Equals, "amd64")
+ c.Check(dpkgArchFromGoArch("arm"), Equals, "armhf")
+ c.Check(dpkgArchFromGoArch("arm64"), Equals, "arm64")
+ c.Check(dpkgArchFromGoArch("ppc64le"), Equals, "ppc64el")
+ c.Check(dpkgArchFromGoArch("ppc64"), Equals, "ppc64")
+ c.Check(dpkgArchFromGoArch("s390x"), Equals, "s390x")
+ c.Check(dpkgArchFromGoArch("ppc"), Equals, "powerpc")
+ c.Check(dpkgArchFromGoArch("ppc64"), Equals, "ppc64")
}
-func (ts *ArchTestSuite) TestSetArchitecture(c *C) {
+func (ts *ArchTestSuite) TestArchSetArchitecture(c *C) {
SetArchitecture("armhf")
- c.Assert(UbuntuArchitecture(), Equals, "armhf")
+ c.Assert(DpkgArchitecture(), Equals, "armhf")
}
-func (ts *ArchTestSuite) TestSupportedArchitectures(c *C) {
+func (ts *ArchTestSuite) TestArchSupportedArchitectures(c *C) {
arch = "armhf"
c.Check(IsSupportedArchitecture([]string{"all"}), Equals, true)
c.Check(IsSupportedArchitecture([]string{"amd64", "armhf", "powerpc"}), Equals, true)
diff --git a/asserts/model.go b/asserts/model.go
index e7b5150ed3..4cc8beeb6d 100644
--- a/asserts/model.go
+++ b/asserts/model.go
@@ -95,7 +95,7 @@ var (
defaultModes = []string{"run"}
)
-func checkExtendSnaps(extendedSnaps interface{}, base string) (*modelSnaps, error) {
+func checkExtendedSnaps(extendedSnaps interface{}, base string) (*modelSnaps, error) {
const wrongHeaderType = `"snaps" header must be a list of maps`
entries, ok := extendedSnaps.([]interface{})
@@ -234,7 +234,7 @@ func checkModelSnap(snap map[string]interface{}) (*ModelSnap, error) {
}
} else {
trackCh, err := channel.ParseVerbatim(track, "-")
- if err != nil || trackCh.Track == "" || trackCh.Risk != "" || trackCh.Branch != "" {
+ if err != nil || !trackCh.VerbatimTrackOnly() {
return nil, fmt.Errorf("invalid locked track for snap %q: %s", name, track)
}
}
@@ -604,7 +604,7 @@ func assembleModel(assert assertionBase) (Assertion, error) {
var modSnaps *modelSnaps
if extended {
// TODO: support and consider grade!
- modSnaps, err = checkExtendSnaps(extendedSnaps, base)
+ modSnaps, err = checkExtendedSnaps(extendedSnaps, base)
if err != nil {
return nil, err
}
diff --git a/boot/boot.go b/boot/boot.go
index 19b1b23a5f..217c50b7bf 100644
--- a/boot/boot.go
+++ b/boot/boot.go
@@ -22,10 +22,13 @@ package boot
import (
"errors"
"fmt"
+ "os"
"path/filepath"
"strings"
+ "github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/bootloader"
+ "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/snap"
)
@@ -273,3 +276,92 @@ func MarkBootSuccessful() error {
return bl.SetBootVars(m)
}
+
+// MakeBootable sets up the image filesystem with the given rootdir
+// such that it can be booted. bootWith is a map from the paths in the
+// image seed for kernel and boot base to their snap info. This
+// entails:
+// - installing the bootloader configuration from the gadget
+// - creating symlinks for boot snaps from seed to the runtime blob dir
+// - setting boot env vars pointing to the revisions of the boot snaps to use
+// - extracting kernel assets as needed by the bootloader
+func MakeBootable(model *asserts.Model, rootdir string, bootWith map[string]*snap.Info, unpackedGadgetDir string) error {
+ if len(bootWith) != 2 {
+ return fmt.Errorf("internal error: MakeBootable can only be called with exactly one kernel and exactly one core/base boot info: %v", bootWith)
+ }
+
+ // install the bootloader configuration from the gadget
+ if err := bootloader.InstallBootConfig(unpackedGadgetDir, rootdir); err != nil {
+ return err
+ }
+
+ // setup symlinks for kernel and boot base from the blob directory
+ // to the seed snaps
+
+ snapBlobDir := dirs.SnapBlobDirUnder(rootdir)
+ if err := os.MkdirAll(snapBlobDir, 0755); err != nil {
+ return err
+ }
+
+ for fn := range bootWith {
+ dst := filepath.Join(snapBlobDir, filepath.Base(fn))
+ // construct a relative symlink from the blob dir
+ // to the seed snap file
+ relSymlink, err := filepath.Rel(snapBlobDir, fn)
+ if err != nil {
+ return fmt.Errorf("cannot build symlink for boot snap: %v", err)
+ }
+ if err := os.Symlink(relSymlink, dst); err != nil {
+ return err
+ }
+ }
+
+ // Set bootvars for kernel/core snaps so the system boots and
+ // does the first-time initialization. There is also no
+ // mounted kernel/core/base snap, but just the blobs.
+ bl, err := bootloader.Find(rootdir, &bootloader.Options{
+ PrepareImageTime: true,
+ })
+ if err != nil {
+ return fmt.Errorf("cannot set kernel/core boot variables: %s", err)
+ }
+
+ m := map[string]string{
+ "snap_mode": "",
+ "snap_try_core": "",
+ "snap_try_kernel": "",
+ }
+ if model.DisplayName() != "" {
+ m["snap_menuentry"] = model.DisplayName()
+ }
+
+ for fn, info := range bootWith {
+ bootvar := ""
+
+ switch info.GetType() {
+ case snap.TypeOS, snap.TypeBase:
+ bootvar = "snap_core"
+ case snap.TypeKernel:
+ snapf, err := snap.Open(fn)
+ if err != nil {
+ return err
+ }
+
+ if err := bl.ExtractKernelAssets(info, snapf); err != nil {
+ return err
+ }
+ bootvar = "snap_kernel"
+ }
+
+ if bootvar != "" {
+ name := filepath.Base(fn)
+ m[bootvar] = name
+ }
+ }
+ if err := bl.SetBootVars(m); err != nil {
+ return err
+ }
+
+ return nil
+
+}
diff --git a/boot/boot_test.go b/boot/boot_test.go
index 0e02caaae4..d963d76cbf 100644
--- a/boot/boot_test.go
+++ b/boot/boot_test.go
@@ -21,17 +21,22 @@ package boot_test
import (
"errors"
+ "io/ioutil"
+ "os"
"path/filepath"
"testing"
. "gopkg.in/check.v1"
+ "github.com/snapcore/snapd/asserts"
+ "github.com/snapcore/snapd/asserts/assertstest"
"github.com/snapcore/snapd/boot"
"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/bootloader/bootloadertest"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/snap/snaptest"
"github.com/snapcore/snapd/testutil"
)
@@ -359,3 +364,93 @@ func (s *bootSetSuite) TestMarkBootSuccessfulKKernelUpdate(c *C) {
"snap_kernel": "k2",
})
}
+
+func (s *bootSetSuite) makeSnap(c *C, name, yaml string, revno snap.Revision) (fn string, info *snap.Info) {
+ si := &snap.SideInfo{
+ RealName: name,
+ Revision: revno,
+ }
+ fn = snaptest.MakeTestSnapWithFiles(c, yaml, nil)
+ snapf, err := snap.Open(fn)
+ c.Assert(err, IsNil)
+ info, err = snap.ReadInfoFromSnapFile(snapf, si)
+ c.Assert(err, IsNil)
+ return fn, info
+}
+
+func (s *bootSetSuite) TestMakeBootable(c *C) {
+ dirs.SetRootDir("")
+
+ headers := map[string]interface{}{
+ "type": "model",
+ "authority-id": "my-brand",
+ "series": "16",
+ "brand-id": "my-brand",
+ "model": "my-model",
+ "display-name": "My Model",
+ "architecture": "amd64",
+ "base": "core18",
+ "gadget": "pc=18",
+ "kernel": "pc-kernel=18",
+ "timestamp": "2018-01-01T08:00:00+00:00",
+ }
+ model := assertstest.FakeAssertion(headers).(*asserts.Model)
+
+ grubCfg := []byte("#grub cfg")
+ unpackedGadgetDir := c.MkDir()
+ err := ioutil.WriteFile(filepath.Join(unpackedGadgetDir, "grub.conf"), grubCfg, 0644)
+ c.Assert(err, IsNil)
+
+ rootdir := c.MkDir()
+
+ seedSnapsDirs := filepath.Join(rootdir, "/var/lib/snapd/seed", "snaps")
+ err = os.MkdirAll(seedSnapsDirs, 0755)
+ c.Assert(err, IsNil)
+
+ baseFn, baseInfo := s.makeSnap(c, "core18", `name: core18
+type: base
+version: 4.0
+`, snap.R(3))
+ baseInSeed := filepath.Join(seedSnapsDirs, filepath.Base(baseInfo.MountFile()))
+ err = os.Rename(baseFn, baseInSeed)
+ c.Assert(err, IsNil)
+ kernelFn, kernelInfo := s.makeSnap(c, "pc-kernel", `name: pc-kernel
+type: kernel
+version: 4.0
+`, snap.R(5))
+ kernelInSeed := filepath.Join(seedSnapsDirs, filepath.Base(kernelInfo.MountFile()))
+ err = os.Rename(kernelFn, kernelInSeed)
+ c.Assert(err, IsNil)
+
+ err = boot.MakeBootable(model, rootdir, map[string]*snap.Info{
+ kernelInSeed: kernelInfo,
+ baseInSeed: baseInfo,
+ }, unpackedGadgetDir)
+ c.Assert(err, IsNil)
+
+ // check the bootloader config
+ m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core", "snap_menuentry")
+ c.Assert(err, IsNil)
+ c.Check(m["snap_kernel"], Equals, "pc-kernel_5.snap")
+ c.Check(m["snap_core"], Equals, "core18_3.snap")
+ c.Check(m["snap_menuentry"], Equals, "My Model")
+
+ // kernel was extracted as needed
+ c.Check(s.bootloader.ExtractKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{kernelInfo})
+
+ // check symlinks from snap blob dir
+ kernelBlob := filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(kernelInfo.MountFile()))
+ dst, err := os.Readlink(filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(kernelInfo.MountFile())))
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/pc-kernel_5.snap")
+ c.Check(kernelBlob, testutil.FilePresent)
+
+ baseBlob := filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(baseInfo.MountFile()))
+ dst, err = os.Readlink(filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(baseInfo.MountFile())))
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/core18_3.snap")
+ c.Check(baseBlob, testutil.FilePresent)
+
+ // check that the bootloader (grub here) configuration was copied
+ c.Check(filepath.Join(rootdir, "boot", "grub/grub.cfg"), testutil.FileEquals, grubCfg)
+}
diff --git a/cmd/Makefile.am b/cmd/Makefile.am
index e10dee262d..804f9f78e6 100644
--- a/cmd/Makefile.am
+++ b/cmd/Makefile.am
@@ -69,6 +69,7 @@ new_format = \
snap-confine/seccomp-support-ext.h \
snap-confine/selinux-support.c \
snap-confine/selinux-support.h \
+ snap-confine/snap-confine-invocation-test.c \
snap-confine/snap-confine-invocation.c \
snap-confine/snap-confine-invocation.h \
snap-discard-ns/snap-discard-ns.c
@@ -336,8 +337,7 @@ snap_confine_unit_tests_SOURCES = \
snap-confine/mount-support-test.c \
snap-confine/ns-support-test.c \
snap-confine/snap-confine-args-test.c \
- snap-confine/snap-confine-invocation.c \
- snap-confine/snap-confine-invocation.h \
+ snap-confine/snap-confine-invocation-test.c \
snap-confine/snap-device-helper-test.c
snap_confine_unit_tests_CFLAGS = $(snap_confine_snap_confine_CFLAGS) $(GLIB_CFLAGS)
snap_confine_unit_tests_LDADD = $(snap_confine_snap_confine_LDADD) $(GLIB_LIBS)
diff --git a/cmd/libsnap-confine-private/snap-test.c b/cmd/libsnap-confine-private/snap-test.c
index 1141531319..9499703cdd 100644
--- a/cmd/libsnap-confine-private/snap-test.c
+++ b/cmd/libsnap-confine-private/snap-test.c
@@ -405,7 +405,9 @@ static void test_sc_snap_drop_instance_key_short_dest_max(void)
if (g_test_subprocess()) {
char dest[SNAP_NAME_LEN + 1] = { 0 };
/* 40 chars (max valid length), pretend dest is the same length, no space for terminator */
- sc_snap_drop_instance_key("01234567890123456789012345678901234567890", dest, sizeof dest - 1);
+ sc_snap_drop_instance_key
+ ("01234567890123456789012345678901234567890", dest,
+ sizeof dest - 1);
return;
}
g_test_trap_subprocess(NULL, 0, 0);
@@ -437,7 +439,8 @@ static void test_sc_snap_drop_instance_key_basic(void)
memset(name, 0xff, sizeof name);
/* 40 chars - snap name length */
- sc_snap_drop_instance_key("0123456789012345678901234567890123456789", name, sizeof name);
+ sc_snap_drop_instance_key("0123456789012345678901234567890123456789",
+ name, sizeof name);
g_assert_cmpstr(name, ==, "0123456789012345678901234567890123456789");
}
@@ -570,7 +573,7 @@ static void __attribute__((constructor)) init(void)
g_test_add_func("/snap/sc_snap_drop_instance_key/short_dest2",
test_sc_snap_drop_instance_key_short_dest2);
g_test_add_func("/snap/sc_snap_drop_instance_key/short_dest_max",
- test_sc_snap_drop_instance_key_short_dest_max);
+ test_sc_snap_drop_instance_key_short_dest_max);
g_test_add_func("/snap/sc_snap_split_instance_name/basic",
test_sc_snap_split_instance_name_basic);
diff --git a/cmd/libsnap-confine-private/test-utils-test.c b/cmd/libsnap-confine-private/test-utils-test.c
index cd760a1e8b..6d0a2d0cac 100644
--- a/cmd/libsnap-confine-private/test-utils-test.c
+++ b/cmd/libsnap-confine-private/test-utils-test.c
@@ -39,7 +39,31 @@ static void test_rm_rf_tmp(void)
g_test_trap_assert_failed();
}
+static void test_test_argc_argv(void)
+{
+ // Check that test_argc_argv() correctly stores data
+ int argc = 0;
+ char **argv = NULL;
+
+ test_argc_argv(&argc, &argv, NULL);
+ g_assert_cmpint(argc, ==, 0);
+ g_assert_nonnull(argv);
+ g_assert_null(argv[0]);
+
+ argc = 0;
+ argv = NULL;
+
+ test_argc_argv(&argc, &argv, "zero", "one", "two", NULL);
+ g_assert_cmpint(argc, ==, 3);
+ g_assert_nonnull(argv);
+ g_assert_cmpstr(argv[0], ==, "zero");
+ g_assert_cmpstr(argv[1], ==, "one");
+ g_assert_cmpstr(argv[2], ==, "two");
+ g_assert_null(argv[3]);
+}
+
static void __attribute__((constructor)) init(void)
{
g_test_add_func("/test-utils/rm_rf_tmp", test_rm_rf_tmp);
+ g_test_add_func("/test-utils/test_argc_argv", test_test_argc_argv);
}
diff --git a/cmd/libsnap-confine-private/test-utils.c b/cmd/libsnap-confine-private/test-utils.c
index ab13ce4b7c..c3e17a18d9 100644
--- a/cmd/libsnap-confine-private/test-utils.c
+++ b/cmd/libsnap-confine-private/test-utils.c
@@ -16,6 +16,7 @@
*/
#include "test-utils.h"
+#include "string-utils.h"
#include "error.h"
#include "utils.h"
@@ -71,3 +72,37 @@ void rm_rf_tmp(const char *dir)
g_free(argv[3]);
g_free(argv);
}
+
+void
+ __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...)
+{
+ int argc = 0;
+ char **argv = NULL;
+ va_list ap;
+
+ /* find out how many elements there are */
+ va_start(ap, argvp);
+ while (NULL != va_arg(ap, const char *)) {
+ argc += 1;
+ }
+ va_end(ap);
+
+ /* argc + terminating NULL entry */
+ argv = calloc(argc + 1, sizeof argv[0]);
+ g_assert_nonnull(argv);
+
+ va_start(ap, argvp);
+ for (int i = 0; i < argc; i++) {
+ const char *arg = va_arg(ap, const char *);
+ char *arg_copy = sc_strdup(arg);
+ g_test_queue_free(arg_copy);
+ argv[i] = arg_copy;
+ }
+ va_end(ap);
+
+ /* free argv last, so that entries do not leak */
+ g_test_queue_free(argv);
+
+ *argcp = argc;
+ *argvp = argv;
+}
diff --git a/cmd/libsnap-confine-private/test-utils.h b/cmd/libsnap-confine-private/test-utils.h
index e13ffcbfd7..2b41ed93a3 100644
--- a/cmd/libsnap-confine-private/test-utils.h
+++ b/cmd/libsnap-confine-private/test-utils.h
@@ -23,4 +23,10 @@
*/
void rm_rf_tmp(const char *dir);
+/**
+ * Create an argc + argv pair out of a NULL terminated argument list.
+ **/
+void
+ __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...);
+
#endif
diff --git a/cmd/snap-confine/cookie-support-test.c b/cmd/snap-confine/cookie-support-test.c
index 754ed7e092..f8ba544099 100644
--- a/cmd/snap-confine/cookie-support-test.c
+++ b/cmd/snap-confine/cookie-support-test.c
@@ -18,6 +18,7 @@
#include "cookie-support.h"
#include "cookie-support.c"
+#include "../libsnap-confine-private/cleanup-funcs.h"
#include "../libsnap-confine-private/test-utils.h"
#include <glib.h>
@@ -64,8 +65,8 @@ static void create_dumy_cookie_file(const char *snap_name,
static void test_cookie_get_from_snapd__successful(void)
{
- struct sc_error *err = NULL;
- char *cookie;
+ struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL;
+ char *cookie SC_CLEANUP(sc_cleanup_string) = NULL;
char *dummy = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijmnopqrst";
@@ -81,8 +82,8 @@ static void test_cookie_get_from_snapd__successful(void)
static void test_cookie_get_from_snapd__nofile(void)
{
- struct sc_error *err = NULL;
- char *cookie;
+ struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL;
+ char *cookie SC_CLEANUP(sc_cleanup_string) = NULL;
set_fake_cookie_dir();
diff --git a/cmd/snap-confine/mount-support.c b/cmd/snap-confine/mount-support.c
index 411fbd4598..4bdf6feee5 100644
--- a/cmd/snap-confine/mount-support.c
+++ b/cmd/snap-confine/mount-support.c
@@ -241,10 +241,10 @@ static void sc_bootstrap_mount_namespace(const struct sc_mount_config *config)
// replicate the state of the root filesystem into the scratch directory.
sc_do_mount(config->rootfs_dir, scratch_dir, NULL, MS_REC | MS_BIND,
NULL);
- // Make the scratch directory recursively private. Nothing done there will
- // be shared with any peer group, This effectively detaches us from the
- // original namespace and coupled with pivot_root below serves as the
- // foundation of the mount sandbox.
+ // Make the scratch directory recursively slave. Nothing done there will be
+ // shared with the initial mount namespace. This effectively detaches us,
+ // in one way, from the original namespace and coupled with pivot_root
+ // below serves as the foundation of the mount sandbox.
sc_do_mount("none", scratch_dir, NULL, MS_REC | MS_SLAVE, NULL);
// Bind mount certain directories from the host filesystem to the scratch
// directory. By default mount events will propagate in both into and out
diff --git a/cmd/snap-confine/snap-confine-args-test.c b/cmd/snap-confine/snap-confine-args-test.c
index ea57d97aca..34216a6b7f 100644
--- a/cmd/snap-confine/snap-confine-args-test.c
+++ b/cmd/snap-confine/snap-confine-args-test.c
@@ -23,57 +23,6 @@
#include <glib.h>
-/**
- * Create an argc + argv pair out of a NULL terminated argument list.
- **/
-static void
- __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...)
-{
- int argc = 0;
- char **argv = NULL;
- g_test_queue_free(argv);
-
- va_list ap;
- va_start(ap, argvp);
- const char *arg;
- do {
- arg = va_arg(ap, const char *);
- // XXX: yeah, wrong way but the worse that can happen is for test to fail
- argv = realloc(argv, sizeof(const char **) * (argc + 1));
- g_assert_nonnull(argv);
- if (arg != NULL) {
- char *arg_copy = sc_strdup(arg);
- g_test_queue_free(arg_copy);
- argv[argc] = arg_copy;
- argc += 1;
- } else {
- argv[argc] = NULL;
- }
- } while (arg != NULL);
- va_end(ap);
-
- *argcp = argc;
- *argvp = argv;
-}
-
-static void test_test_argc_argv(void)
-{
- // Check that test_argc_argv() correctly stores data
- int argc;
- char **argv;
-
- test_argc_argv(&argc, &argv, NULL);
- g_assert_cmpint(argc, ==, 0);
- g_assert_null(argv[0]);
-
- test_argc_argv(&argc, &argv, "zero", "one", "two", NULL);
- g_assert_cmpint(argc, ==, 3);
- g_assert_cmpstr(argv[0], ==, "zero");
- g_assert_cmpstr(argv[1], ==, "one");
- g_assert_cmpstr(argv[2], ==, "two");
- g_assert_null(argv[3]);
-}
-
static void test_sc_nonfatal_parse_args__typical(void)
{
// Test that typical invocation of snap-confine is parsed correctly.
@@ -239,6 +188,7 @@ static void test_sc_nonfatal_parse_args__evil_input(void)
g_assert_null(args);
g_assert_cmpstr(sc_error_msg(err), ==,
"cannot parse arguments, argcp or argvp is NULL");
+ sc_cleanup_error(&err);
int argc;
char **argv;
@@ -252,6 +202,7 @@ static void test_sc_nonfatal_parse_args__evil_input(void)
g_assert_null(args);
g_assert_cmpstr(sc_error_msg(err), ==,
"cannot parse arguments, argc is zero or argv is NULL");
+ sc_cleanup_error(&err);
// NULL argv[i] attack
test_argc_argv(&argc, &argv,
@@ -451,7 +402,6 @@ static void test_sc_nonfatal_parse_args__base_snap__twice(void)
static void __attribute__((constructor)) init(void)
{
- g_test_add_func("/args/test_argc_argv", test_test_argc_argv);
g_test_add_func("/args/sc_cleanup_args", test_sc_cleanup_args);
g_test_add_func("/args/sc_nonfatal_parse_args/typical",
test_sc_nonfatal_parse_args__typical);
diff --git a/cmd/snap-confine/snap-confine-args.c b/cmd/snap-confine/snap-confine-args.c
index f660a8aef9..a44c9ce89f 100644
--- a/cmd/snap-confine/snap-confine-args.c
+++ b/cmd/snap-confine/snap-confine-args.c
@@ -21,6 +21,7 @@
#include "../libsnap-confine-private/utils.h"
#include "../libsnap-confine-private/string-utils.h"
+#include "../libsnap-confine-private/test-utils.h"
struct sc_args {
// The security tag that the application is intended to run with
diff --git a/cmd/snap-confine/snap-confine-invocation-test.c b/cmd/snap-confine/snap-confine-invocation-test.c
new file mode 100644
index 0000000000..6f1369c2a4
--- /dev/null
+++ b/cmd/snap-confine/snap-confine-invocation-test.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "snap-confine-invocation.h"
+#include "snap-confine-args.h"
+#include "snap-confine-invocation.c"
+
+#include "../libsnap-confine-private/cleanup-funcs.h"
+#include "../libsnap-confine-private/test-utils.h"
+
+#include <stdarg.h>
+
+#include <glib.h>
+
+static struct sc_args *test_prepare_args(const char *base, const char *tag) {
+ struct sc_args *args = NULL;
+ sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL;
+ int argc;
+ char **argv;
+
+ test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine", "--base", (base != NULL) ? base : "core",
+ (tag != NULL) ? tag : "snap.foo.app", "/usr/lib/snapd/snap-exec", NULL);
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_assert_null(err);
+ g_assert_nonnull(args);
+
+ return args;
+}
+
+static void test_sc_invocation_basic(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", NULL);
+
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation);
+ ;
+ sc_init_invocation(&inv, args, "foo");
+
+ g_assert_cmpstr(inv.base_snap_name, ==, "core");
+ g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec");
+ g_assert_cmpstr(inv.orig_base_snap_name, ==, "core");
+ g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current");
+ g_assert_cmpstr(inv.security_tag, ==, "snap.foo.app");
+ g_assert_cmpstr(inv.snap_instance, ==, "foo");
+ g_assert_cmpstr(inv.snap_name, ==, "foo");
+ g_assert_false(inv.classic_confinement);
+ /* derived later */
+ g_assert_false(inv.is_normal_mode);
+}
+
+static void test_sc_invocation_instance_key(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", "snap.foo_bar.app");
+
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation);
+ ;
+ sc_init_invocation(&inv, args, "foo_bar");
+
+ // Check the error that we've got
+ g_assert_cmpstr(inv.snap_instance, ==, "foo_bar");
+ g_assert_cmpstr(inv.snap_name, ==, "foo");
+ g_assert_cmpstr(inv.orig_base_snap_name, ==, "core");
+ g_assert_cmpstr(inv.security_tag, ==, "snap.foo_bar.app");
+ g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec");
+ g_assert_false(inv.classic_confinement);
+ g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current");
+ g_assert_cmpstr(inv.base_snap_name, ==, "core");
+ /* derived later */
+ g_assert_false(inv.is_normal_mode);
+}
+
+static void test_sc_invocation_base_name(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("base-snap", NULL);
+
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation);
+ sc_init_invocation(&inv, args, "foo");
+
+ g_assert_cmpstr(inv.base_snap_name, ==, "base-snap");
+ g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec");
+ g_assert_cmpstr(inv.orig_base_snap_name, ==, "base-snap");
+ g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/base-snap/current");
+ g_assert_cmpstr(inv.security_tag, ==, "snap.foo.app");
+ g_assert_cmpstr(inv.snap_instance, ==, "foo");
+ g_assert_cmpstr(inv.snap_name, ==, "foo");
+ g_assert_false(inv.classic_confinement);
+ /* derived later */
+ g_assert_false(inv.is_normal_mode);
+}
+
+static void test_sc_invocation_bad_instance_name(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args(NULL, NULL);
+
+ if (g_test_subprocess()) {
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation) = {0};
+ sc_init_invocation(&inv, args, "foo_bar_bar_bar");
+ return;
+ }
+
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr("snap instance name can contain only one underscore\n");
+}
+
+static void test_sc_invocation_classic(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL;
+ sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL;
+ int argc;
+ char **argv = NULL;
+
+ test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine", "--classic", "--base", "core", "snap.foo-classic.app",
+ "/usr/lib/snapd/snap-exec", NULL);
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_assert_null(err);
+ g_assert_nonnull(args);
+
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation) = {0};
+ sc_init_invocation(&inv, args, "foo-classic");
+
+ g_assert_cmpstr(inv.base_snap_name, ==, "core");
+ g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec");
+ g_assert_cmpstr(inv.orig_base_snap_name, ==, "core");
+ g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current");
+ g_assert_cmpstr(inv.security_tag, ==, "snap.foo-classic.app");
+ g_assert_cmpstr(inv.snap_instance, ==, "foo-classic");
+ g_assert_cmpstr(inv.snap_name, ==, "foo-classic");
+ g_assert_true(inv.classic_confinement);
+}
+
+static void test_sc_invocation_tag_name_mismatch(void) {
+ struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", "snap.foo.app");
+
+ if (g_test_subprocess()) {
+ sc_invocation inv SC_CLEANUP(sc_cleanup_invocation);
+ ;
+ sc_init_invocation(&inv, args, "foo-not-foo");
+ return;
+ }
+
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr("security tag snap.foo.app not allowed\n");
+}
+
+static void __attribute__((constructor)) init(void) {
+ g_test_add_func("/invocation/bad_instance_name", test_sc_invocation_bad_instance_name);
+ g_test_add_func("/invocation/base_name", test_sc_invocation_base_name);
+ g_test_add_func("/invocation/basic", test_sc_invocation_basic);
+ g_test_add_func("/invocation/classic", test_sc_invocation_classic);
+ g_test_add_func("/invocation/instance_key", test_sc_invocation_instance_key);
+ g_test_add_func("/invocation/tag_name_mismatch", test_sc_invocation_tag_name_mismatch);
+}
diff --git a/cmd/snap-repair/runner.go b/cmd/snap-repair/runner.go
index 91f9fe9770..335f020dd3 100644
--- a/cmd/snap-repair/runner.go
+++ b/cmd/snap-repair/runner.go
@@ -776,7 +776,7 @@ func (run *Runner) Applicable(headers map[string]interface{}) bool {
if err != nil {
return false
}
- if len(archs) != 0 && !strutil.ListContains(archs, arch.UbuntuArchitecture()) {
+ if len(archs) != 0 && !strutil.ListContains(archs, arch.DpkgArchitecture()) {
return false
}
brandModel := fmt.Sprintf("%s/%s", run.state.Device.Brand, run.state.Device.Model)
diff --git a/cmd/snap-repair/runner_test.go b/cmd/snap-repair/runner_test.go
index 279854c6af..2afdf885af 100644
--- a/cmd/snap-repair/runner_test.go
+++ b/cmd/snap-repair/runner_test.go
@@ -782,10 +782,10 @@ func (s *runnerSuite) TestApplicable(c *C) {
{map[string]interface{}{"series": []interface{}{"18", "16"}}, true},
{map[string]interface{}{"series": "18"}, false},
{map[string]interface{}{"series": []interface{}{18}}, false},
- {map[string]interface{}{"architectures": []interface{}{arch.UbuntuArchitecture()}}, true},
+ {map[string]interface{}{"architectures": []interface{}{arch.DpkgArchitecture()}}, true},
{map[string]interface{}{"architectures": []interface{}{"other-arch"}}, false},
- {map[string]interface{}{"architectures": []interface{}{"other-arch", arch.UbuntuArchitecture()}}, true},
- {map[string]interface{}{"architectures": arch.UbuntuArchitecture()}, false},
+ {map[string]interface{}{"architectures": []interface{}{"other-arch", arch.DpkgArchitecture()}}, true},
+ {map[string]interface{}{"architectures": arch.DpkgArchitecture()}, false},
{map[string]interface{}{"models": []interface{}{"my-brand/my-model"}}, true},
{map[string]interface{}{"models": []interface{}{"other-brand/other-model"}}, false},
{map[string]interface{}{"models": []interface{}{"other-brand/other-model", "my-brand/my-model"}}, true},
diff --git a/cmd/snap-seccomp/export_test.go b/cmd/snap-seccomp/export_test.go
index c29030d9f7..48d23d846d 100644
--- a/cmd/snap-seccomp/export_test.go
+++ b/cmd/snap-seccomp/export_test.go
@@ -26,19 +26,19 @@ var (
GoSeccompFeatures = goSeccompFeatures
)
-func MockArchUbuntuArchitecture(f func() string) (restore func()) {
- realArchUbuntuArchitecture := archUbuntuArchitecture
- archUbuntuArchitecture = f
+func MockArchDpkgArchitecture(f func() string) (restore func()) {
+ realArchDpkgArchitecture := archDpkgArchitecture
+ archDpkgArchitecture = f
return func() {
- archUbuntuArchitecture = realArchUbuntuArchitecture
+ archDpkgArchitecture = realArchDpkgArchitecture
}
}
-func MockArchUbuntuKernelArchitecture(f func() string) (restore func()) {
- realArchUbuntuKernelArchitecture := archUbuntuKernelArchitecture
- archUbuntuKernelArchitecture = f
+func MockArchDpkgKernelArchitecture(f func() string) (restore func()) {
+ realArchDpkgKernelArchitecture := archDpkgKernelArchitecture
+ archDpkgKernelArchitecture = f
return func() {
- archUbuntuKernelArchitecture = realArchUbuntuKernelArchitecture
+ archDpkgKernelArchitecture = realArchDpkgKernelArchitecture
}
}
diff --git a/cmd/snap-seccomp/main.go b/cmd/snap-seccomp/main.go
index 8fcf6df74d..81670ce8b2 100644
--- a/cmd/snap-seccomp/main.go
+++ b/cmd/snap-seccomp/main.go
@@ -426,10 +426,10 @@ var seccompResolver = map[string]uint64{
"PTRACE_CONT": C.PTRACE_CONT,
}
-// UbuntuArchToScmpArch takes a dpkg architecture and converts it to
+// DpkgArchToScmpArch takes a dpkg architecture and converts it to
// the seccomp.ScmpArch as used in the libseccomp-golang library
-func UbuntuArchToScmpArch(ubuntuArch string) seccomp.ScmpArch {
- switch ubuntuArch {
+func DpkgArchToScmpArch(dpkgArch string) seccomp.ScmpArch {
+ switch dpkgArch {
case "amd64":
return seccomp.ArchAMD64
case "arm64":
@@ -447,7 +447,7 @@ func UbuntuArchToScmpArch(ubuntuArch string) seccomp.ScmpArch {
case "s390x":
return seccomp.ArchS390X
}
- panic(fmt.Sprintf("cannot map ubuntu arch %q to a seccomp arch", ubuntuArch))
+ panic(fmt.Sprintf("cannot map dpkg arch %q to a seccomp arch", dpkgArch))
}
// important for unit testing
@@ -631,13 +631,13 @@ func parseLine(line string, secFilter *seccomp.ScmpFilter) error {
// used to mock in tests
var (
- archUbuntuArchitecture = arch.UbuntuArchitecture
- archUbuntuKernelArchitecture = arch.UbuntuKernelArchitecture
+ archDpkgArchitecture = arch.DpkgArchitecture
+ archDpkgKernelArchitecture = arch.DpkgKernelArchitecture
)
var (
- ubuntuArchitecture = archUbuntuArchitecture()
- ubuntuKernelArchitecture = archUbuntuKernelArchitecture()
+ dpkgArchitecture = archDpkgArchitecture()
+ dpkgKernelArchitecture = archDpkgKernelArchitecture()
)
// For architectures that support a compat architecture, when the
@@ -653,8 +653,8 @@ func addSecondaryArches(secFilter *seccomp.ScmpFilter) error {
// add a compat architecture for some architectures that
// support it, e.g. on amd64 kernel and userland, we add
// compat i386 syscalls.
- if ubuntuArchitecture == ubuntuKernelArchitecture {
- switch archUbuntuArchitecture() {
+ if dpkgArchitecture == dpkgKernelArchitecture {
+ switch archDpkgArchitecture() {
case "amd64":
compatArch = seccomp.ArchX86
case "arm64":
@@ -672,7 +672,7 @@ func addSecondaryArches(secFilter *seccomp.ScmpFilter) error {
// snaps. While unusual from a traditional Linux distribution
// perspective, certain classes of embedded devices are known
// to use this configuration.
- compatArch = UbuntuArchToScmpArch(archUbuntuKernelArchitecture())
+ compatArch = DpkgArchToScmpArch(archDpkgKernelArchitecture())
}
if compatArch != seccomp.ArchInvalid {
diff --git a/cmd/snap-seccomp/main_test.go b/cmd/snap-seccomp/main_test.go
index 6385834e72..75262367fc 100644
--- a/cmd/snap-seccomp/main_test.go
+++ b/cmd/snap-seccomp/main_test.go
@@ -191,7 +191,7 @@ func (s *snapSeccompSuite) SetUpSuite(c *C) {
// Build 32bit runner on amd64 to test non-native syscall handling.
// Ideally we would build for ppc64el->powerpc and arm64->armhf but
// it seems tricky to find the right gcc-multilib for this.
- if arch.UbuntuArchitecture() == "amd64" && s.canCheckCompatArch {
+ if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch {
cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...)
cmd.Args = append(cmd.Args, "-m32")
for i, k := range cmd.Args {
@@ -274,7 +274,7 @@ restart_syscall
// compiler that can produce the required binaries. Currently
// we only test amd64 running i386 here.
if syscallArch != "native" {
- syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.UbuntuArchToScmpArch(syscallArch))
+ syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.DpkgArchToScmpArch(syscallArch))
c.Assert(err, IsNil)
switch syscallArch {
@@ -825,10 +825,10 @@ func (s *snapSeccompSuite) TestCompatArchWorks(c *C) {
// https://github.com/seccomp/libseccomp/issues/86
//
// This means we can not just
- // main.MockArchUbuntuArchitecture(t.arch)
+ // main.MockArchDpkgArchitecture(t.arch)
// here because on endian mismatch the arch will *not* be
// added
- if arch.UbuntuArchitecture() == t.arch {
+ if arch.DpkgArchitecture() == t.arch {
s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
}
}
diff --git a/daemon/api_test.go b/daemon/api_test.go
index 693f023e80..84073438b8 100644
--- a/daemon/api_test.go
+++ b/daemon/api_test.go
@@ -7125,7 +7125,7 @@ func (s *apiSuite) TestErrToResponseNoSnapsDoesNotPanic(c *check.C) {
func (s *apiSuite) TestErrToResponseForRevisionNotAvailable(c *check.C) {
si := &snapInstruction{Action: "frobble", Snaps: []string{"foo"}}
- thisArch := arch.UbuntuArchitecture()
+ thisArch := arch.DpkgArchitecture()
err := &store.RevisionNotAvailableError{
Action: "install",
diff --git a/daemon/response.go b/daemon/response.go
index b10937f522..c542cba82b 100644
--- a/daemon/response.go
+++ b/daemon/response.go
@@ -420,7 +420,7 @@ func SnapRevisionNotAvailable(snapName string, rnaErr *store.RevisionNotAvailabl
kind := errorKindSnapRevisionNotAvailable
msg := rnaErr.Error()
if len(rnaErr.Releases) != 0 && rnaErr.Channel != "" {
- thisArch := arch.UbuntuArchitecture()
+ thisArch := arch.DpkgArchitecture()
values := map[string]interface{}{
"snap-name": snapName,
"action": rnaErr.Action,
diff --git a/dirs/dirs.go b/dirs/dirs.go
index c022830653..a99e5687d9 100644
--- a/dirs/dirs.go
+++ b/dirs/dirs.go
@@ -206,6 +206,16 @@ func isInsideBaseSnap() (bool, error) {
return err == nil, err
}
+// SnapBlobDirUnder returns the path to the snap blob dir under rootdir.
+func SnapBlobDirUnder(rootdir string) string {
+ return filepath.Join(rootdir, snappyDir, "snaps")
+}
+
+// SnapSeedDirUnder returns the path to the snap seed dir under rootdir.
+func SnapSeedDirUnder(rootdir string) string {
+ return filepath.Join(rootdir, snappyDir, "seed")
+}
+
// SetRootDir allows settings a new global root directory, this is useful
// for e.g. chroot operations
func SetRootDir(rootdir string) {
@@ -231,7 +241,7 @@ func SetRootDir(rootdir string) {
SnapSeccompDir = filepath.Join(rootdir, snappyDir, "seccomp", "bpf")
SnapMountPolicyDir = filepath.Join(rootdir, snappyDir, "mount")
SnapMetaDir = filepath.Join(rootdir, snappyDir, "meta")
- SnapBlobDir = filepath.Join(rootdir, snappyDir, "snaps")
+ SnapBlobDir = SnapBlobDirUnder(rootdir)
SnapDesktopFilesDir = filepath.Join(rootdir, snappyDir, "desktop", "applications")
SnapRunDir = filepath.Join(rootdir, "/run/snapd")
SnapRunNsDir = filepath.Join(SnapRunDir, "/ns")
@@ -255,7 +265,7 @@ func SetRootDir(rootdir string) {
SnapCommandsDB = filepath.Join(SnapCacheDir, "commands.db")
SnapAuxStoreInfoDir = filepath.Join(SnapCacheDir, "aux")
- SnapSeedDir = filepath.Join(rootdir, snappyDir, "seed")
+ SnapSeedDir = SnapSeedDirUnder(rootdir)
SnapDeviceDir = filepath.Join(rootdir, snappyDir, "device")
SnapRepairDir = filepath.Join(rootdir, snappyDir, "repair")
diff --git a/dirs/dirs_test.go b/dirs/dirs_test.go
index 6c47dd7f12..e4b2084c62 100644
--- a/dirs/dirs_test.go
+++ b/dirs/dirs_test.go
@@ -156,3 +156,13 @@ func (s *DirsTestSuite) TestIsCompleteShSymlink(c *C) {
c.Check(dirs.IsCompleteShSymlink("/etc/passwd"), Equals, false)
c.Check(dirs.IsCompleteShSymlink("/does-not-exist"), Equals, false)
}
+
+func (s *DirsTestSuite) TestUnder(c *C) {
+ dirs.SetRootDir("/nowhere")
+ defer dirs.SetRootDir("")
+
+ rootdir := "/other-root"
+
+ c.Check(dirs.SnapBlobDirUnder(rootdir), Equals, "/other-root/var/lib/snapd/snaps")
+ c.Check(dirs.SnapSeedDirUnder(rootdir), Equals, "/other-root/var/lib/snapd/seed")
+}
diff --git a/errtracker/errtracker.go b/errtracker/errtracker.go
index d55643ec63..5172148183 100644
--- a/errtracker/errtracker.go
+++ b/errtracker/errtracker.go
@@ -429,7 +429,7 @@ func report(errMsg, dupSig string, extra map[string]string) (string, error) {
}
report := map[string]string{
- "Architecture": arch.UbuntuArchitecture(),
+ "Architecture": arch.DpkgArchitecture(),
"SnapdVersion": SnapdVersion,
"DistroRelease": distroRelease(),
"HostSnapdBuildID": hostBuildID,
diff --git a/errtracker/errtracker_test.go b/errtracker/errtracker_test.go
index 33613577c6..cf01819b47 100644
--- a/errtracker/errtracker_test.go
+++ b/errtracker/errtracker_test.go
@@ -167,7 +167,7 @@ func (s *ErrtrackerTestSuite) TestReport(c *C) {
"KernelVersion": osutil.KernelVersion(),
"ErrorMessage": "failed to do stuff",
"DuplicateSignature": "[failed to do stuff]",
- "Architecture": arch.UbuntuArchitecture(),
+ "Architecture": arch.DpkgArchitecture(),
"DidSnapdReExec": "yes",
"ProblemType": "Snap",
@@ -312,7 +312,7 @@ func (s *ErrtrackerTestSuite) TestReportRepair(c *C) {
"SnapdVersion": "some-snapd-version",
"Date": "Fri Feb 17 09:51:00 2017",
"KernelVersion": osutil.KernelVersion(),
- "Architecture": arch.UbuntuArchitecture(),
+ "Architecture": arch.DpkgArchitecture(),
"DidSnapdReExec": "yes",
"ProblemType": "Repair",
diff --git a/httputil/useragent.go b/httputil/useragent.go
index 20c86cb2c8..4c1dc15e5e 100644
--- a/httputil/useragent.go
+++ b/httputil/useragent.go
@@ -64,7 +64,7 @@ func SetUserAgentFromVersion(version string, extraProds ...string) (restore func
// assumption checks out in practice, q.v. https://github.com/zyga/os-release-zoo
userAgent = fmt.Sprintf("snapd/%v (%s)%s %s/%s (%s) linux/%s", version,
strings.Join(extras, "; "), extraProdStr, release.ReleaseInfo.ID,
- release.ReleaseInfo.VersionID, string(arch.UbuntuArchitecture()),
+ release.ReleaseInfo.VersionID, string(arch.DpkgArchitecture()),
sanitizeKernelVersion(osutil.KernelVersion()))
return func() {
userAgent = origUserAgent
diff --git a/image/export_test.go b/image/export_test.go
index e178dc4f59..ced1bada1d 100644
--- a/image/export_test.go
+++ b/image/export_test.go
@@ -31,7 +31,6 @@ func MockToolingStore(sto Store) *ToolingStore {
var (
LocalSnaps = localSnaps
DecodeModelAssertion = decodeModelAssertion
- DownloadUnpackGadget = downloadUnpackGadget
SetupSeed = setupSeed
InstallCloudConfig = installCloudConfig
SnapChannel = snapChannel
diff --git a/image/image.go b/image/image.go
index 4da7983689..cf5246dead 100644
--- a/image/image.go
+++ b/image/image.go
@@ -32,7 +32,7 @@ import (
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/snapasserts"
"github.com/snapcore/snapd/asserts/sysdb"
- "github.com/snapcore/snapd/bootloader"
+ "github.com/snapcore/snapd/boot"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/release"
@@ -229,9 +229,9 @@ func Prepare(opts *Options) error {
}
if !opts.Classic {
- // unpacking the gadget for core models
- if err := downloadUnpackGadget(tsto, model, opts, local); err != nil {
- return err
+ // create directory for later unpacking the gadget in
+ if err := os.MkdirAll(opts.GadgetUnpackDir, 0755); err != nil {
+ return fmt.Errorf("cannot create gadget unpack dir %q: %s", opts.GadgetUnpackDir, err)
}
}
@@ -275,6 +275,11 @@ func snapChannel(name string, model *asserts.Model, opts *Options, local *localI
// fallback to default channel
snapChannel = opts.Channel
}
+ if snapChannel != "" {
+ if _, err := channel.ParseVerbatim(snapChannel, "-"); err != nil {
+ return "", fmt.Errorf("cannot use option channel for snap %q: %v", name, err)
+ }
+ }
// consider snap types that can be pinned to a track by the model
var pinnedTrack string
var kind string
@@ -286,58 +291,21 @@ func snapChannel(name string, model *asserts.Model, opts *Options, local *localI
kind = "kernel"
pinnedTrack = model.KernelTrack()
}
- if pinnedTrack != "" {
- ch, err := makeChannelFromTrack(kind, pinnedTrack, snapChannel)
- if err != nil {
- return "", err
- }
- snapChannel = ch
-
+ ch, err := channel.ResolveLocked(pinnedTrack, snapChannel)
+ if err == channel.ErrLockedTrackSwitch {
+ return "", fmt.Errorf("channel %q for %s has a track incompatible with the track from model assertion: %s", snapChannel, kind, pinnedTrack)
}
- return snapChannel, nil
-}
-
-func makeChannelFromTrack(what, track, snapChannel string) (string, error) {
- mch, err := channel.Parse(track, "")
if err != nil {
- return "", fmt.Errorf("cannot use track %q for %s from model assertion: %v", track, what, err)
- }
- if snapChannel != "" {
- ch, err := channel.ParseVerbatim(snapChannel, "")
- if err != nil {
- return "", fmt.Errorf("cannot parse channel %q for %s", snapChannel, what)
- }
- if ch.Track != "" && ch.Track != mch.Track {
- return "", fmt.Errorf("channel %q for %s has a track incompatible with the track from model assertion: %s", snapChannel, what, track)
- }
- mch.Risk = ch.Risk
+ return "", err
}
- return mch.Clean().String(), nil
+ return ch, nil
}
-func downloadUnpackGadget(tsto *ToolingStore, model *asserts.Model, opts *Options, local *localInfos) error {
- if err := os.MkdirAll(opts.GadgetUnpackDir, 0755); err != nil {
- return fmt.Errorf("cannot create gadget unpack dir %q: %s", opts.GadgetUnpackDir, err)
- }
-
- gadgetName := model.Gadget()
- gadgetChannel, err := snapChannel(gadgetName, model, opts, local)
- if err != nil {
- return err
- }
-
- dlOpts := &DownloadOptions{
- TargetDir: opts.GadgetUnpackDir,
- Channel: gadgetChannel,
- }
- snapFn, _, err := acquireSnap(tsto, gadgetName, dlOpts, local)
- if err != nil {
- return err
- }
+func unpackGadget(gadgetFname, gadgetUnpackDir string) error {
// FIXME: jumping through layers here, we need to make
// unpack part of the container interface (again)
- snap := squashfs.New(snapFn)
- return snap.Unpack("*", opts.GadgetUnpackDir)
+ snap := squashfs.New(gadgetFname)
+ return snap.Unpack("*", gadgetUnpackDir)
}
func acquireSnap(tsto *ToolingStore, name string, dlOpts *DownloadOptions, local *localInfos) (downloadedSnap string, info *snap.Info, err error) {
@@ -586,14 +554,12 @@ func setupSeed(tsto *ToolingStore, model *asserts.Model, opts *Options, local *l
}
if !opts.Classic {
- // TODO: make this functionality part of the boot package?
-
- // now do the bootloader stuff
- if err := bootloader.InstallBootConfig(opts.GadgetUnpackDir, dirs.GlobalRootDir); err != nil {
+ // unpacking the gadget for core models
+ if err := unpackGadget(seed.gadgetFname, opts.GadgetUnpackDir); err != nil {
return err
}
- if err := setBootvars(seed.downloadedSnapsInfoForBootConfig, model); err != nil {
+ if err := boot.MakeBootable(model, opts.RootDir, seed.downloadedSnapsInfoForBootConfig, opts.GadgetUnpackDir); err != nil {
return err
}
@@ -639,6 +605,7 @@ type imageSeed struct {
seen map[string]bool
locals []string
downloadedSnapsInfoForBootConfig map[string]*snap.Info
+ gadgetFname string
needsCore []string
needsCore16 []string
@@ -722,19 +689,13 @@ func (s *imageSeed) add(snapName string) error {
// kernel/os/model.base are required for booting on core
if !opts.Classic && (typ == snap.TypeKernel || name == s.baseName) {
- dst := filepath.Join(dirs.SnapBlobDir, filepath.Base(fn))
- // construct a relative symlink from the blob dir
- // to the seed file
- relSymlink, err := filepath.Rel(dirs.SnapBlobDir, fn)
- if err != nil {
- return fmt.Errorf("cannot build symlink: %v", err)
- }
- if err := os.Symlink(relSymlink, dst); err != nil {
- return err
- }
// store the snap.Info for kernel/os/base so
- // that the bootload can DTRT
- s.downloadedSnapsInfoForBootConfig[dst] = info
+ // that boot.MakeBootable can DTRT
+ s.downloadedSnapsInfoForBootConfig[fn] = info
+ }
+ // remember the gadget for unpacking later
+ if !opts.Classic && typ == snap.TypeGadget {
+ s.gadgetFname = fn
}
s.entries = append(s.entries, seedEntry{
@@ -805,79 +766,6 @@ func (s *imageSeed) seedYaml() *seed.Seed16 {
return &seedYaml
}
-func setBootvars(downloadedSnapsInfoForBootConfig map[string]*snap.Info, model *asserts.Model) error {
- if len(downloadedSnapsInfoForBootConfig) != 2 {
- return fmt.Errorf("setBootvars can only be called with exactly one kernel and exactly one core/base boot info: %v", downloadedSnapsInfoForBootConfig)
- }
-
- // Set bootvars for kernel/core snaps so the system boots and
- // does the first-time initialization. There is also no
- // mounted kernel/core/base snap, but just the blobs.
- bloader, err := bootloader.Find("", &bootloader.Options{
- PrepareImageTime: true,
- })
- if err != nil {
- return fmt.Errorf("cannot set kernel/core boot variables: %s", err)
- }
-
- snaps, err := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*.snap"))
- if len(snaps) == 0 || err != nil {
- return fmt.Errorf("internal error: cannot find core/kernel snap")
- }
-
- m := map[string]string{
- "snap_mode": "",
- "snap_try_core": "",
- "snap_try_kernel": "",
- }
- if model.DisplayName() != "" {
- m["snap_menuentry"] = model.DisplayName()
- }
-
- for _, fn := range snaps {
- bootvar := ""
-
- info := downloadedSnapsInfoForBootConfig[fn]
- if info == nil {
- // this should never happen, if it does print some
- // debug info
- keys := make([]string, 0, len(downloadedSnapsInfoForBootConfig))
- for k := range downloadedSnapsInfoForBootConfig {
- keys = append(keys, k)
- }
- return fmt.Errorf("cannot get download info for snap %s, available infos: %v", fn, keys)
- }
- switch info.GetType() {
- case snap.TypeOS, snap.TypeBase:
- bootvar = "snap_core"
- case snap.TypeKernel:
- bootvar = "snap_kernel"
- if err := extractKernelAssets(bloader, fn, info, model); err != nil {
- return err
- }
- }
-
- if bootvar != "" {
- name := filepath.Base(fn)
- m[bootvar] = name
- }
- }
- if err := bloader.SetBootVars(m); err != nil {
- return err
- }
-
- return nil
-}
-
-func extractKernelAssets(bootloader bootloader.Bootloader, snapPath string, info *snap.Info, model *asserts.Model) error {
- snapf, err := snap.Open(snapPath)
- if err != nil {
- return err
- }
-
- return bootloader.ExtractKernelAssets(info, snapf)
-}
-
func copyLocalSnapFile(snapPath, targetDir string, info *snap.Info) (dstPath string, err error) {
dst := filepath.Join(targetDir, filepath.Base(info.MountFile()))
return dst, osutil.CopyFile(snapPath, dst, 0)
diff --git a/image/image_test.go b/image/image_test.go
index a421c9dae8..d10d0e7d1d 100644
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -463,79 +463,12 @@ func (s *imageSuite) TestHappyDecodeModelAssertion(c *C) {
c.Check(a.Type(), Equals, asserts.ModelType)
}
-func (s *imageSuite) TestMissingGadgetUnpackDir(c *C) {
- err := image.DownloadUnpackGadget(s.tsto, s.model, &image.Options{}, nil)
- c.Assert(err, ErrorMatches, `cannot create gadget unpack dir "": mkdir : no such file or directory`)
-}
-
-func (s *imageSuite) TestDownloadUnpackGadget(c *C) {
- files := [][]string{
- {"subdir/canary.txt", "I'm a canary"},
- }
- s.MakeAssertedSnap(c, packageGadget, files, snap.R(99), "canonical")
-
- gadgetUnpackDir := filepath.Join(c.MkDir(), "gadget-unpack-dir")
- opts := &image.Options{
- GadgetUnpackDir: gadgetUnpackDir,
- }
- local, err := image.LocalSnaps(s.tsto, opts)
- c.Assert(err, IsNil)
-
- err = image.DownloadUnpackGadget(s.tsto, s.model, opts, local)
- c.Assert(err, IsNil)
-
- // verify the right data got unpacked
- for _, t := range []struct{ file, content string }{
- {"meta/snap.yaml", packageGadget},
- {files[0][0], files[0][1]},
- } {
- fn := filepath.Join(gadgetUnpackDir, t.file)
- c.Check(fn, testutil.FileEquals, t.content)
- }
-}
-
-func (s *imageSuite) TestDownloadUnpackGadgetFromTrack(c *C) {
- s.MakeAssertedSnap(c, packageGadget, nil, snap.R(1818), "canonical")
-
- model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
-
- "architecture": "amd64",
- "gadget": "pc=18",
- "kernel": "pc-kernel=18",
- })
-
- gadgetUnpackDir := filepath.Join(c.MkDir(), "gadget-unpack-dir")
- opts := &image.Options{
- GadgetUnpackDir: gadgetUnpackDir,
- }
- local, err := image.LocalSnaps(s.tsto, opts)
- c.Assert(err, IsNil)
-
- err = image.DownloadUnpackGadget(s.tsto, model, opts, local)
- c.Assert(err, IsNil)
-
- c.Check(s.storeActions, HasLen, 1)
- c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
- Action: "download",
- Channel: "18/stable",
- InstanceName: "pc",
- })
-
-}
-
func (s *imageSuite) setupSnaps(c *C, gadgetUnpackDir string, publishers map[string]string) {
- if gadgetUnpackDir != "" {
- err := os.MkdirAll(gadgetUnpackDir, 0755)
- c.Assert(err, IsNil)
- err = ioutil.WriteFile(filepath.Join(gadgetUnpackDir, "grub.conf"), nil, 0644)
- c.Assert(err, IsNil)
- }
-
if _, ok := publishers["pc"]; ok {
- s.MakeAssertedSnap(c, packageGadget, [][]string{{"grub.cfg", "I'm a grub.cfg"}}, snap.R(1), publishers["pc"])
+ s.MakeAssertedSnap(c, packageGadget, [][]string{{"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}}, snap.R(1), publishers["pc"])
}
if _, ok := publishers["pc18"]; ok {
- s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{{"grub.cfg", "I'm a grub.cfg"}}, snap.R(4), publishers["pc18"])
+ s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{{"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}}, snap.R(4), publishers["pc18"])
}
if _, ok := publishers["classic-gadget"]; ok {
@@ -577,6 +510,7 @@ func (s *imageSuite) TestSetupSeed(c *C) {
defer restore()
rootdir := filepath.Join(c.MkDir(), "imageroot")
+ blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps")
seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
seedsnapsdir := filepath.Join(seeddir, "snaps")
seedassertsdir := filepath.Join(seeddir, "assertions")
@@ -653,7 +587,37 @@ func (s *imageSuite) TestSetupSeed(c *C) {
c.Check(m["snap_core"], Equals, "core_3.snap")
c.Check(m["snap_menuentry"], Equals, "my display name")
+ // check symlinks from snap blob dir
+ kernelInfo := s.AssertedSnapInfo("pc-kernel")
+ coreInfo := s.AssertedSnapInfo("core")
+ kernelBlob := filepath.Join(blobdir, filepath.Base(kernelInfo.MountFile()))
+ dst, err := os.Readlink(kernelBlob)
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap")
+ c.Check(kernelBlob, testutil.FilePresent)
+
+ coreBlob := filepath.Join(blobdir, filepath.Base(coreInfo.MountFile()))
+ dst, err = os.Readlink(coreBlob)
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/core_3.snap")
+ c.Check(coreBlob, testutil.FilePresent)
+
c.Check(s.stderr.String(), Equals, "")
+
+ // check the downloads
+ c.Check(s.storeActions, HasLen, 4)
+ c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "core",
+ })
+ c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc-kernel",
+ })
+ c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc",
+ })
}
func (s *imageSuite) TestSetupSeedLocalCoreBrandKernel(c *C) {
@@ -890,6 +854,7 @@ func (s *imageSuite) TestSetupSeedWithBase(c *C) {
})
rootdir := filepath.Join(c.MkDir(), "imageroot")
+ blobdir := filepath.Join(rootdir, "/var/lib/snapd/snaps")
gadgetUnpackDir := c.MkDir()
s.setupSnaps(c, gadgetUnpackDir, map[string]string{
"core18": "canonical",
@@ -955,7 +920,41 @@ func (s *imageSuite) TestSetupSeedWithBase(c *C) {
c.Assert(err, IsNil)
c.Check(m["snap_core"], Equals, "core18_18.snap")
+ // check symlinks from snap blob dir
+ kernelInfo := s.AssertedSnapInfo("pc-kernel")
+ baseInfo := s.AssertedSnapInfo("core18")
+ kernelBlob := filepath.Join(blobdir, filepath.Base(kernelInfo.MountFile()))
+ dst, err := os.Readlink(kernelBlob)
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap")
+ c.Check(kernelBlob, testutil.FilePresent)
+
+ baseBlob := filepath.Join(blobdir, filepath.Base(baseInfo.MountFile()))
+ dst, err = os.Readlink(baseBlob)
+ c.Assert(err, IsNil)
+ c.Check(dst, Equals, "../seed/snaps/core18_18.snap")
+ c.Check(baseBlob, testutil.FilePresent)
+
c.Check(s.stderr.String(), Equals, "")
+
+ // check the downloads
+ c.Check(s.storeActions, HasLen, 5)
+ c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "snapd",
+ })
+ c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "core18",
+ })
+ c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc-kernel",
+ })
+ c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc18",
+ })
}
func (s *imageSuite) TestSetupSeedWithBaseLegacySnap(c *C) {
@@ -1313,6 +1312,18 @@ func (s *imageSuite) TestSetupSeedLocalSnapsWithChannels(c *C) {
c.Check(l, HasLen, 4)
}
+func (s *imageSuite) TestMissingGadgetUnpackDir(c *C) {
+ fn := filepath.Join(c.MkDir(), "model.assertion")
+ err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
+ c.Assert(err, IsNil)
+
+ err = image.Prepare(&image.Options{
+ ModelFile: fn,
+ Channel: "stable",
+ })
+ c.Assert(err, ErrorMatches, `cannot create gadget unpack dir "": mkdir : no such file or directory`)
+}
+
func (s *imageSuite) TestNoLocalParallelSnapInstances(c *C) {
fn := filepath.Join(c.MkDir(), "model.assertion")
err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
@@ -1442,6 +1453,7 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) {
opts := &image.Options{
RootDir: rootdir,
GadgetUnpackDir: gadgetUnpackDir,
+ Channel: "stable",
}
local, err := image.LocalSnaps(s.tsto, opts)
c.Assert(err, IsNil)
@@ -1455,9 +1467,10 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) {
c.Check(seedYaml.Snaps, HasLen, 3)
c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
- Name: "core",
- SnapID: s.AssertedSnapID("core"),
- File: "core_3.snap",
+ Name: "core",
+ SnapID: s.AssertedSnapID("core"),
+ File: "core_3.snap",
+ Channel: "stable",
})
c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
@@ -1471,6 +1484,24 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) {
File: "pc_1.snap",
Channel: "18/stable",
})
+
+ // check the downloads
+ c.Check(s.storeActions, HasLen, 3)
+ c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "core",
+ Channel: "stable",
+ })
+ c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc-kernel",
+ Channel: "18/stable",
+ })
+ c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{
+ Action: "download",
+ InstanceName: "pc",
+ Channel: "18/stable",
+ })
}
func (s *imageSuite) TestSetupSeedWithKernelTrackWithDefaultChannel(c *C) {
@@ -2293,6 +2324,11 @@ func (s *imageSuite) TestSnapChannel(c *C) {
opts.SnapChannels["pc-kernel"] = "lts/candidate"
_, err = image.SnapChannel("pc-kernel", model, opts, local)
c.Assert(err, ErrorMatches, `channel "lts/candidate" for kernel has a track incompatible with the track from model assertion: 18`)
+
+ opts.SnapChannels["pc-kernel"] = "track/foo"
+ _, err = image.SnapChannel("pc-kernel", model, opts, local)
+ c.Assert(err, ErrorMatches, `cannot use option channel for snap "pc-kernel": invalid risk in channel name: track/foo`)
+
}
func (s *imageSuite) TestSetupSeedLocalSnapd(c *C) {
diff --git a/interfaces/seccomp/backend.go b/interfaces/seccomp/backend.go
index e0c23e9faa..6108b951bb 100644
--- a/interfaces/seccomp/backend.go
+++ b/interfaces/seccomp/backend.go
@@ -53,11 +53,11 @@ import (
)
var (
- kernelFeatures = release.SecCompActions
- ubuntuKernelArchitecture = arch.UbuntuKernelArchitecture
- releaseInfoId = release.ReleaseInfo.ID
- releaseInfoVersionId = release.ReleaseInfo.VersionID
- requiresSocketcall = requiresSocketcallImpl
+ kernelFeatures = release.SecCompActions
+ dpkgKernelArchitecture = arch.DpkgKernelArchitecture
+ releaseInfoId = release.ReleaseInfo.ID
+ releaseInfoVersionId = release.ReleaseInfo.VersionID
+ requiresSocketcall = requiresSocketcallImpl
snapSeccompVersionInfo = snapSeccompVersionInfoImpl
seccompCompilerLookup = cmd.InternalToolPath
@@ -365,7 +365,7 @@ func (b *Backend) SandboxFeatures() []string {
// - if the kernel architecture is not any of the above, force the use of
// socketcall()
func requiresSocketcallImpl(baseSnap string) bool {
- switch ubuntuKernelArchitecture() {
+ switch dpkgKernelArchitecture() {
case "i386", "s390x":
// glibc sysdeps/unix/sysv/linux/i386/kernel-features.h and
// sysdeps/unix/sysv/linux/s390/kernel-features.h added the
diff --git a/interfaces/seccomp/backend_test.go b/interfaces/seccomp/backend_test.go
index c1b9fe34c1..89e94d6ea3 100644
--- a/interfaces/seccomp/backend_test.go
+++ b/interfaces/seccomp/backend_test.go
@@ -491,7 +491,7 @@ fi`)
func (s *backendSuite) TestRequiresSocketcallByNotNeededArch(c *C) {
testArchs := []string{"amd64", "armhf", "arm64", "powerpc", "ppc64el", "unknownDefault"}
for _, arch := range testArchs {
- restore := seccomp.MockUbuntuKernelArchitecture(func() string { return arch })
+ restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch })
defer restore()
c.Assert(seccomp.RequiresSocketcall(""), Equals, false)
}
@@ -500,7 +500,7 @@ func (s *backendSuite) TestRequiresSocketcallByNotNeededArch(c *C) {
func (s *backendSuite) TestRequiresSocketcallForceByArch(c *C) {
testArchs := []string{"sparc", "sparc64"}
for _, arch := range testArchs {
- restore := seccomp.MockUbuntuKernelArchitecture(func() string { return arch })
+ restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch })
defer restore()
c.Assert(seccomp.RequiresSocketcall(""), Equals, true)
}
@@ -542,7 +542,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaUbuntuRelease(c *C) {
for _, t := range tests {
restore = seccomp.MockReleaseInfoId(t.distro)
defer restore()
- restore = seccomp.MockUbuntuKernelArchitecture(func() string { return t.arch })
+ restore = seccomp.MockDpkgKernelArchitecture(func() string { return t.arch })
defer restore()
restore = seccomp.MockReleaseInfoVersionId(t.release)
defer restore()
@@ -581,7 +581,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaKernelVersion(c *C) {
}
for _, t := range tests {
- restore := seccomp.MockUbuntuKernelArchitecture(func() string { return t.arch })
+ restore := seccomp.MockDpkgKernelArchitecture(func() string { return t.arch })
defer restore()
restore = osutil.MockKernelVersion(t.version)
defer restore()
@@ -597,7 +597,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaBaseSnap(c *C) {
// check is reached
restore := seccomp.MockReleaseInfoId("other")
defer restore()
- restore = seccomp.MockUbuntuKernelArchitecture(func() string { return "i386" })
+ restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" })
defer restore()
restore = osutil.MockKernelVersion("4.3")
defer restore()
@@ -613,7 +613,7 @@ func (s *backendSuite) TestRequiresSocketcallNotForcedViaBaseSnap(c *C) {
// check is reached
restore := seccomp.MockReleaseInfoId("other")
defer restore()
- restore = seccomp.MockUbuntuKernelArchitecture(func() string { return "i386" })
+ restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" })
defer restore()
restore = osutil.MockKernelVersion("4.3")
defer restore()
diff --git a/interfaces/seccomp/export_test.go b/interfaces/seccomp/export_test.go
index f5c22d4a2e..36b9ec0e6c 100644
--- a/interfaces/seccomp/export_test.go
+++ b/interfaces/seccomp/export_test.go
@@ -54,11 +54,11 @@ func MockRequiresSocketcall(f func(string) bool) (restore func()) {
}
}
-func MockUbuntuKernelArchitecture(f func() string) (restore func()) {
- old := ubuntuKernelArchitecture
- ubuntuKernelArchitecture = f
+func MockDpkgKernelArchitecture(f func() string) (restore func()) {
+ old := dpkgKernelArchitecture
+ dpkgKernelArchitecture = f
return func() {
- ubuntuKernelArchitecture = old
+ dpkgKernelArchitecture = old
}
}
diff --git a/overlord/snapstate/backend/mountunit_test.go b/overlord/snapstate/backend/mountunit_test.go
index 7c4821ba6a..5ac596d46f 100644
--- a/overlord/snapstate/backend/mountunit_test.go
+++ b/overlord/snapstate/backend/mountunit_test.go
@@ -89,6 +89,7 @@ What=/var/lib/snapd/snaps/foo_13.snap
Where=%s/foo/13
Type=squashfs
Options=nodev,ro,x-gdu.hide
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
diff --git a/overlord/snapstate/check_snap.go b/overlord/snapstate/check_snap.go
index cf3ff7f04b..d8ba5f0583 100644
--- a/overlord/snapstate/check_snap.go
+++ b/overlord/snapstate/check_snap.go
@@ -320,7 +320,7 @@ func validateInfoAndFlags(info *snap.Info, snapst *SnapState, flags Flags) error
// verify we have a valid architecture
if !arch.IsSupportedArchitecture(info.Architectures) {
- return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.UbuntuArchitecture())
+ return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.DpkgArchitecture())
}
// check assumes
diff --git a/overlord/snapstate/check_snap_test.go b/overlord/snapstate/check_snap_test.go
index 9bbb7e0c9c..cb263b0f11 100644
--- a/overlord/snapstate/check_snap_test.go
+++ b/overlord/snapstate/check_snap_test.go
@@ -88,7 +88,7 @@ architectures:
err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil)
- errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.UbuntuArchitecture())
+ errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.DpkgArchitecture())
c.Assert(err.Error(), Equals, errorMsg)
}
diff --git a/overlord/snapstate/snapstate.go b/overlord/snapstate/snapstate.go
index 5592fcdd06..b52df2c7d5 100644
--- a/overlord/snapstate/snapstate.go
+++ b/overlord/snapstate/snapstate.go
@@ -1206,24 +1206,19 @@ func resolveChannel(st *state.State, snapName, newChannel string, deviceCtx Devi
return newChannel, nil
}
- nch, err := channel.ParseVerbatim(newChannel, "")
- if err != nil {
- return "", err
- }
-
- if nch.Track == "" {
- // channel name is valid and consist of risk level or
- // risk/branch only, do the right thing and default to risk (or
- // risk/branch) within the pinned track
- return pinnedTrack + "/" + newChannel, nil
- }
- if nch.Track != "" && nch.Track != pinnedTrack {
+ // channel name is valid and consist of risk level or
+ // risk/branch only, do the right thing and default to risk (or
+ // risk/branch) within the pinned track
+ resChannel, err := channel.ResolveLocked(pinnedTrack, newChannel)
+ if err == channel.ErrLockedTrackSwitch {
// switching to a different track is not allowed
- return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, nch.Clean().String())
+ return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, newChannel)
}
-
- return newChannel, nil
+ if err != nil {
+ return "", err
+ }
+ return resChannel, nil
}
var errRevisionSwitch = errors.New("cannot switch revision")
diff --git a/overlord/snapstate/snapstate_test.go b/overlord/snapstate/snapstate_test.go
index 3b4d46c487..4dc18072ca 100644
--- a/overlord/snapstate/snapstate_test.go
+++ b/overlord/snapstate/snapstate_test.go
@@ -1673,7 +1673,7 @@ func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) {
})
_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"})
- c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
}
func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) {
@@ -1730,7 +1730,7 @@ func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) {
})
_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"})
- c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
}
func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) {
@@ -6597,7 +6597,7 @@ func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) {
// switching tracks is not ok
_, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
- c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
// no change to the channel is ok
_, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{})
@@ -6633,7 +6633,7 @@ func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) {
// switching tracks is not ok
_, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
- c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
// no change to the channel is ok
_, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{})
@@ -13899,7 +13899,7 @@ version: 1.0`)
Channel: "some-channel",
}
_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
- c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`)
}
func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) {
@@ -13930,7 +13930,7 @@ version: 1.0`)
Channel: "some-channel",
}
_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
- c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel/stable"`)
+ c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`)
}
func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) {
diff --git a/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch b/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch
index 0db9d1e8a4..f6e032a0fd 100644
--- a/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch
+++ b/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch
@@ -24,14 +24,14 @@ Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>
cmd/snap-seccomp/main_test.go | 8 ++++++++
1 file changed, 8 insertions(+)
-diff --git a/cmd/snap-seccomp/main_test.go b/cmd/snap-seccomp/main_test.go
-index d4ca193b2..8977385c2 100644
---- a/cmd/snap-seccomp/main_test.go
-+++ b/cmd/snap-seccomp/main_test.go
-@@ -185,6 +185,14 @@ func (s *snapSeccompSuite) SetUpSuite(c *C) {
+Index: snapd/cmd/snap-seccomp/main_test.go
+===================================================================
+--- snapd.orig/cmd/snap-seccomp/main_test.go
++++ snapd/cmd/snap-seccomp/main_test.go
+@@ -192,6 +192,14 @@ func (s *snapSeccompSuite) SetUpSuite(c
// Ideally we would build for ppc64el->powerpc and arm64->armhf but
// it seems tricky to find the right gcc-multilib for this.
- if arch.UbuntuArchitecture() == "amd64" && s.canCheckCompatArch {
+ if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch {
+ // This test fails on Debian amd64
+ // cannot build multi-lib syscall runner: exit status 1
+ // In file included from /usr/include/errno.h:25,
@@ -43,6 +43,3 @@ index d4ca193b2..8977385c2 100644
cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...)
cmd.Args = append(cmd.Args, "-m32")
for i, k := range cmd.Args {
---
-2.17.1
-
diff --git a/snap/channel/channel.go b/snap/channel/channel.go
index f62b60fa89..30d652531e 100644
--- a/snap/channel/channel.go
+++ b/snap/channel/channel.go
@@ -20,6 +20,7 @@
package channel
import (
+ "errors"
"fmt"
"strings"
@@ -68,7 +69,7 @@ func ParseVerbatim(s string, architecture string) (Channel, error) {
}
if architecture == "" {
- architecture = arch.UbuntuArchitecture()
+ architecture = arch.DpkgArchitecture()
}
ch := Channel{
@@ -150,6 +151,16 @@ func (c *Channel) Full() string {
return c.String()
}
+// VerbatimTrackOnly returns whether the channel represents a track only.
+func (c *Channel) VerbatimTrackOnly() bool {
+ return c.Track != "" && c.Risk == "" && c.Branch == ""
+}
+
+// VerbatimRiskOnly returns whether the channel represents a risk only.
+func (c *Channel) VerbatimRiskOnly() bool {
+ return c.Track == "" && c.Risk != "" && c.Branch == ""
+}
+
func riskLevel(risk string) int {
for i, r := range channelRisks {
if r == risk {
@@ -200,3 +211,54 @@ func (c *Channel) Match(c1 *Channel) ChannelMatch {
Risk: requestedRiskLevel >= rl1,
}
}
+
+// Resolve resolves newChannel wrt channel, this means if newChannel
+// is risk/branch only it will preserve the track of channel. It
+// assumes that if both are not empty, channel is parseable.
+func Resolve(channel, newChannel string) (string, error) {
+ if newChannel == "" {
+ return channel, nil
+ }
+ if channel == "" {
+ return newChannel, nil
+ }
+ ch, err := ParseVerbatim(channel, "-")
+ if err != nil {
+ return "", err
+ }
+ p := strings.Split(newChannel, "/")
+ if strutil.ListContains(channelRisks, p[0]) && ch.Track != "" {
+ // risk/branch inherits the track if any
+ return ch.Track + "/" + newChannel, nil
+ }
+ return newChannel, nil
+}
+
+var ErrLockedTrackSwitch = errors.New("cannot switch locked track")
+
+// ResolveLocked resolves newChannel wrt a locked track, newChannel
+// can only be risk/branch-only or have the same track, otherwise
+// ErrLockedTrackSwitch is returned.
+func ResolveLocked(track, newChannel string) (string, error) {
+ if track == "" {
+ return newChannel, nil
+ }
+ ch, err := ParseVerbatim(track, "-")
+ if err != nil || !ch.VerbatimTrackOnly() {
+ return "", fmt.Errorf("invalid locked track: %s", track)
+ }
+ if newChannel == "" {
+ return track, nil
+ }
+ trackPrefix := ch.Track + "/"
+ p := strings.Split(newChannel, "/")
+ if strutil.ListContains(channelRisks, p[0]) && ch.Track != "" {
+ // risk/branch inherits the track if any
+ return trackPrefix + newChannel, nil
+ }
+ if newChannel != track && !strings.HasPrefix(newChannel, trackPrefix) {
+ // the track is locked/pinned
+ return "", ErrLockedTrackSwitch
+ }
+ return newChannel, nil
+}
diff --git a/snap/channel/channel_test.go b/snap/channel/channel_test.go
index 907568e33d..973aac7678 100644
--- a/snap/channel/channel_test.go
+++ b/snap/channel/channel_test.go
@@ -38,7 +38,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err := channel.Parse("stable", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "stable",
Track: "",
Risk: "stable",
@@ -48,7 +48,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err = channel.Parse("latest/stable", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "stable",
Track: "",
Risk: "stable",
@@ -58,7 +58,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err = channel.Parse("1.0/edge", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "1.0/edge",
Track: "1.0",
Risk: "edge",
@@ -68,7 +68,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err = channel.Parse("1.0", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "1.0/stable",
Track: "1.0",
Risk: "stable",
@@ -78,7 +78,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err = channel.Parse("1.0/beta/foo", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "1.0/beta/foo",
Track: "1.0",
Risk: "beta",
@@ -88,7 +88,7 @@ func (s storeChannelSuite) TestParse(c *C) {
ch, err = channel.Parse("candidate/foo", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Name: "candidate/foo",
Track: "",
Risk: "candidate",
@@ -116,36 +116,54 @@ func (s storeChannelSuite) TestParseVerbatim(c *C) {
ch, err := channel.ParseVerbatim("sometrack", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Track: "sometrack",
})
+ c.Check(ch.VerbatimTrackOnly(), Equals, true)
+ c.Check(ch.VerbatimRiskOnly(), Equals, false)
c.Check(mustParse(c, "sometrack"), DeepEquals, ch.Clean())
ch, err = channel.ParseVerbatim("latest", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Track: "latest",
})
+ c.Check(ch.VerbatimTrackOnly(), Equals, true)
+ c.Check(ch.VerbatimRiskOnly(), Equals, false)
c.Check(mustParse(c, "latest"), DeepEquals, ch.Clean())
+ ch, err = channel.ParseVerbatim("edge", "")
+ c.Assert(err, IsNil)
+ c.Check(ch, DeepEquals, channel.Channel{
+ Architecture: arch.DpkgArchitecture(),
+ Risk: "edge",
+ })
+ c.Check(ch.VerbatimTrackOnly(), Equals, false)
+ c.Check(ch.VerbatimRiskOnly(), Equals, true)
+ c.Check(mustParse(c, "edge"), DeepEquals, ch.Clean())
+
ch, err = channel.ParseVerbatim("latest/stable", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Track: "latest",
Risk: "stable",
})
+ c.Check(ch.VerbatimTrackOnly(), Equals, false)
+ c.Check(ch.VerbatimRiskOnly(), Equals, false)
c.Check(mustParse(c, "latest/stable"), DeepEquals, ch.Clean())
ch, err = channel.ParseVerbatim("latest/stable/foo", "")
c.Assert(err, IsNil)
c.Check(ch, DeepEquals, channel.Channel{
- Architecture: arch.UbuntuArchitecture(),
+ Architecture: arch.DpkgArchitecture(),
Track: "latest",
Risk: "stable",
Branch: "foo",
})
+ c.Check(ch.VerbatimTrackOnly(), Equals, false)
+ c.Check(ch.VerbatimRiskOnly(), Equals, false)
c.Check(mustParse(c, "latest/stable/foo"), DeepEquals, ch.Clean())
}
@@ -266,3 +284,72 @@ func (s *storeChannelSuite) TestMatch(c *C) {
c.Check(req.Match(&c1).String(), Equals, t.res)
}
}
+
+func (s *storeChannelSuite) TestResolve(c *C) {
+ tests := []struct {
+ channel string
+ new string
+ result string
+ expErr string
+ }{
+ {"", "", "", ""},
+ {"", "edge", "edge", ""},
+ {"track/foo", "", "track/foo", ""},
+ {"stable", "", "stable", ""},
+ {"stable", "edge", "edge", ""},
+ {"stable/branch1", "edge/branch2", "edge/branch2", ""},
+ {"track", "track", "track", ""},
+ {"track", "beta", "track/beta", ""},
+ {"track/stable", "beta", "track/beta", ""},
+ {"track/stable", "stable/branch", "track/stable/branch", ""},
+ {"track/stable", "track/edge/branch", "track/edge/branch", ""},
+ {"track/stable", "track/candidate", "track/candidate", ""},
+ {"track/stable", "track/stable/branch", "track/stable/branch", ""},
+ {"track1/stable", "track2/stable", "track2/stable", ""},
+ {"track1/stable", "track2/stable/branch", "track2/stable/branch", ""},
+ {"track/foo", "track/stable/branch", "", "invalid risk in channel name: track/foo"},
+ }
+
+ for _, t := range tests {
+ r, err := channel.Resolve(t.channel, t.new)
+ tcomm := Commentf("%#v", t)
+ if t.expErr == "" {
+ c.Assert(err, IsNil, tcomm)
+ c.Check(r, Equals, t.result, tcomm)
+ } else {
+ c.Assert(err, ErrorMatches, t.expErr, tcomm)
+ }
+ }
+}
+
+func (s *storeChannelSuite) TestResolveLocked(c *C) {
+ tests := []struct {
+ track string
+ new string
+ result string
+ expErr string
+ }{
+ {"", "", "", ""},
+ {"", "anytrack/stable", "anytrack/stable", ""},
+ {"track/foo", "", "", "invalid locked track: track/foo"},
+ {"track", "", "track", ""},
+ {"track", "track", "track", ""},
+ {"track", "beta", "track/beta", ""},
+ {"track", "stable/branch", "track/stable/branch", ""},
+ {"track", "track/edge/branch", "track/edge/branch", ""},
+ {"track", "track/candidate", "track/candidate", ""},
+ {"track", "track/stable/branch", "track/stable/branch", ""},
+ {"track1", "track2/stable", "track2/stable", "cannot switch locked track"},
+ {"track1", "track2/stable/branch", "track2/stable/branch", "cannot switch locked track"},
+ }
+ for _, t := range tests {
+ r, err := channel.ResolveLocked(t.track, t.new)
+ tcomm := Commentf("%#v", t)
+ if t.expErr == "" {
+ c.Assert(err, IsNil, tcomm)
+ c.Check(r, Equals, t.result, tcomm)
+ } else {
+ c.Assert(err, ErrorMatches, t.expErr, tcomm)
+ }
+ }
+}
diff --git a/snap/snapenv/snapenv.go b/snap/snapenv/snapenv.go
index 58e47980b2..03ae4490bc 100644
--- a/snap/snapenv/snapenv.go
+++ b/snap/snapenv/snapenv.go
@@ -112,7 +112,7 @@ func basicEnv(info *snap.Info) map[string]string {
"SNAP_INSTANCE_KEY": info.InstanceKey,
"SNAP_VERSION": info.Version,
"SNAP_REVISION": info.Revision.String(),
- "SNAP_ARCH": arch.UbuntuArchitecture(),
+ "SNAP_ARCH": arch.DpkgArchitecture(),
// see https://github.com/snapcore/snapd/pull/2732#pullrequestreview-18827193
"SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void",
"SNAP_REEXEC": os.Getenv("SNAP_REEXEC"),
diff --git a/snap/snapenv/snapenv_test.go b/snap/snapenv/snapenv_test.go
index afa78fc5c0..030f35ede6 100644
--- a/snap/snapenv/snapenv_test.go
+++ b/snap/snapenv/snapenv_test.go
@@ -82,7 +82,7 @@ func (ts *HTestSuite) TestBasic(c *C) {
c.Assert(env, DeepEquals, map[string]string{
"SNAP": fmt.Sprintf("%s/foo/17", dirs.CoreSnapMountDir),
- "SNAP_ARCH": arch.UbuntuArchitecture(),
+ "SNAP_ARCH": arch.DpkgArchitecture(),
"SNAP_COMMON": "/var/snap/foo/common",
"SNAP_DATA": "/var/snap/foo/17",
"SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void",
@@ -136,7 +136,7 @@ func (s *HTestSuite) TestSnapRunSnapExecEnv(c *C) {
env := snapEnv(info)
c.Check(env, DeepEquals, map[string]string{
- "SNAP_ARCH": arch.UbuntuArchitecture(),
+ "SNAP_ARCH": arch.DpkgArchitecture(),
"SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void",
"SNAP_NAME": "snapname",
"SNAP_INSTANCE_NAME": "snapname",
@@ -178,7 +178,7 @@ func (s *HTestSuite) TestParallelInstallSnapRunSnapExecEnv(c *C) {
env := snapEnv(info)
c.Check(env, DeepEquals, map[string]string{
- "SNAP_ARCH": arch.UbuntuArchitecture(),
+ "SNAP_ARCH": arch.DpkgArchitecture(),
"SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void",
"SNAP_NAME": "snapname",
"SNAP_INSTANCE_NAME": "snapname_foo",
diff --git a/store/store.go b/store/store.go
index 49ba063acc..ec359cb883 100644
--- a/store/store.go
+++ b/store/store.go
@@ -348,7 +348,7 @@ func New(cfg *Config, dauthCtx DeviceAndAuthContext) *Store {
architecture := cfg.Architecture
if cfg.Architecture == "" {
- architecture = arch.UbuntuArchitecture()
+ architecture = arch.DpkgArchitecture()
}
series := cfg.Series
diff --git a/store/store_test.go b/store/store_test.go
index e963b30e23..ad482dbf4c 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -1997,7 +1997,7 @@ func (s *storeTestSuite) TestInfo(c *C) {
query := r.URL.Query()
c.Check(query.Get("fields"), Equals, "abc,def")
- c.Check(query.Get("architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(query.Get("architecture"), Equals, arch.DpkgArchitecture())
w.Header().Set("X-Suggested-Currency", "GBP")
w.WriteHeader(200)
@@ -2961,7 +2961,7 @@ func (s *storeTestSuite) TestFind(c *C) {
c.Check(r.URL.Query().Get("fields"), Equals, "abc,def")
c.Check(r.Header.Get("X-Ubuntu-Series"), Equals, release.Series)
- c.Check(r.Header.Get("X-Ubuntu-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("X-Ubuntu-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("X-Ubuntu-Classic"), Equals, "false")
c.Check(r.Header.Get("X-Ubuntu-Confinement"), Equals, "")
@@ -4452,7 +4452,7 @@ func (s *storeTestSuite) TestSnapAction(c *C) {
c.Check(storeID, Equals, "")
c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series)
- c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("Snap-Classic"), Equals, "false")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -4562,7 +4562,7 @@ func (s *storeTestSuite) TestSnapActionNonZeroEpochAndEpochBump(c *C) {
c.Check(storeID, Equals, "")
c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series)
- c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("Snap-Classic"), Equals, "false")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -5567,7 +5567,7 @@ func (s *storeTestSuite) testSnapActionGet(action, cohort string, c *C) {
c.Check(storeID, Equals, "")
c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series)
- c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("Snap-Classic"), Equals, "false")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -5664,7 +5664,7 @@ func (s *storeTestSuite) TestSnapActionInstallAmend(c *C) {
c.Check(storeID, Equals, "")
c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series)
- c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("Snap-Classic"), Equals, "false")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -5834,7 +5834,7 @@ func (s *storeTestSuite) testSnapActionGetWithRevision(action string, c *C) {
c.Check(storeID, Equals, "")
c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series)
- c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture())
+ c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture())
c.Check(r.Header.Get("Snap-Classic"), Equals, "false")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -6564,7 +6564,7 @@ func (s *storeTestSuite) TestConnectivityCheckHappy(c *C) {
switch r.URL.Path {
case "/v2/snaps/info/core":
c.Check(r.Method, Equals, "GET")
- c.Check(r.URL.Query(), DeepEquals, url.Values{"fields": {"download"}, "architecture": {arch.UbuntuArchitecture()}})
+ c.Check(r.URL.Query(), DeepEquals, url.Values{"fields": {"download"}, "architecture": {arch.DpkgArchitecture()}})
u, err := url.Parse("/download/core")
c.Assert(err, IsNil)
io.WriteString(w,
diff --git a/systemd/systemd.go b/systemd/systemd.go
index fdc7e0ed54..42f6466436 100644
--- a/systemd/systemd.go
+++ b/systemd/systemd.go
@@ -674,6 +674,7 @@ What=%s
Where=%s
Type=%s
Options=%s
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
diff --git a/systemd/systemd_test.go b/systemd/systemd_test.go
index cfdea50e8c..499aea317a 100644
--- a/systemd/systemd_test.go
+++ b/systemd/systemd_test.go
@@ -525,6 +525,7 @@ What=%s
Where=/snap/snapname/123
Type=squashfs
Options=nodev,ro,x-gdu.hide
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
@@ -557,6 +558,7 @@ What=%s
Where=/snap/snapname/x1
Type=none
Options=nodev,ro,x-gdu.hide,bind
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
@@ -595,6 +597,7 @@ What=%s
Where=/snap/snapname/123
Type=squashfs
Options=nodev,ro,x-gdu.hide,context=system_u:object_r:snappy_snap_t:s0
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
@@ -637,6 +640,7 @@ What=%s
Where=/snap/snapname/123
Type=fuse.squashfuse
Options=nodev,ro,x-gdu.hide,allow_other
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
@@ -675,6 +679,7 @@ What=%s
Where=/snap/snapname/123
Type=squashfs
Options=nodev,ro,x-gdu.hide
+LazyUnmount=yes
[Install]
WantedBy=multi-user.target
diff --git a/tests/main/interfaces-calendar-service/task.yaml b/tests/main/interfaces-calendar-service/task.yaml
index e79bf8a956..a445990001 100644
--- a/tests/main/interfaces-calendar-service/task.yaml
+++ b/tests/main/interfaces-calendar-service/task.yaml
@@ -5,8 +5,7 @@ summary: Ensure that the calendar-service interface works
#
# FIXME: disable opensuse-tumbleweed until
# https://github.com/snapcore/snapd/pull/7230 is landed
-# ubuntu-19.10-64: pending fix for this test coming on a following change
-systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -opensuse-tumbleweed-*, -ubuntu-19.10-64]
+systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -opensuse-tumbleweed-*]
# fails in the autopkgtest env with:
# [Wed Aug 15 16:34:12 2018] audit: type=1400
diff --git a/tests/main/interfaces-contacts-service/task.yaml b/tests/main/interfaces-contacts-service/task.yaml
index 11e3af457c..2506da7a32 100644
--- a/tests/main/interfaces-contacts-service/task.yaml
+++ b/tests/main/interfaces-contacts-service/task.yaml
@@ -3,8 +3,7 @@ summary: Ensure that the contacts-service interface works
# Only test on classic systems. Don't test on Ubuntu 14.04, which
# does not ship a new enough evolution-data-server.
# amazon: no need to run this on amazon
-# ubuntu-19.10-64: pending fix for this test coming on a following change
-systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -ubuntu-19.10-64]
+systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*]
# fails in autopkgtest environment with:
# [Wed Aug 15 16:08:23 2018] audit: type=1400
@@ -24,19 +23,15 @@ environment:
debug: |
echo "Output process to see what might write to $XDG"
ps uafx
+ echo "Output dbus-session"
+ systemctl status dbus-session || true
echo "Show what is in $XDG"
ls -alR "$XDG"
restore: |
- if [ -e dbus-launch.pid ]; then
- kill "$(cat dbus-launch.pid)"
- fi
-
- # In case the process gvfsd-metadata does not finish by itself, it is manually stopped
- # The reason is that gvfsd-metadata locks the xdg/share/gvfs-metadata directory content
- # producing an error when the xdg directory is removed.
- if pid="$(pidof gvfsd-metadata)"; then
- kill -9 "$pid" || true
+ echo "Stop dbus session bus and all its children"
+ if systemctl is-active dbus-session; then
+ systemctl stop dbus-session
fi
rm -rf "$XDG"
@@ -51,9 +46,10 @@ execute: |
fi
mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME"
- echo "Setting up D-Bus session bus"
- eval "$(dbus-launch --sh-syntax)"
- echo "$DBUS_SESSION_BUS_PID" > dbus-launch.pid
+ echo "Setting up D-Bus session bus in a systemd unit"
+ systemd-run --unit=dbus-session --property=Type=forking -r /bin/sh -c "XDG_CONFIG_HOME=$XDG_CONFIG_HOME XDG_DATA_HOME=$XDG_DATA_HOME XDG_CACHE_HOME=$XDG_CACHE_HOME dbus-launch --sh-syntax > /tmp/dbus-sh"
+ retry-tool -n 20 test -e /tmp/dbus-sh
+ eval "$(cat /tmp/dbus-sh)"
echo "The interface is initially disconnected"
snap interfaces -i contacts-service | MATCH -- '- +test-snapd-eds:contacts-service'
diff --git a/tests/main/interfaces-timeserver-control/task.yaml b/tests/main/interfaces-timeserver-control/task.yaml
index 2d0a84be31..23828fdca3 100644
--- a/tests/main/interfaces-timeserver-control/task.yaml
+++ b/tests/main/interfaces-timeserver-control/task.yaml
@@ -7,8 +7,7 @@ details: |
# Debian sid is skipped because "timedatectl set-ntp" fails with the error:
# "Failed to set ntp: Message recipient disconnected from message bus without replying"
# A workaround is to make "systemctl enable --now systemd-timesyncd.service" instead
-# FIXME: re-enable ubuntu-19.10 after stabelizing it
-systems: [-debian-sid-*, -ubuntu-19.10-*]
+systems: [-debian-sid-*]
prepare: |
# shellcheck source=tests/lib/snaps.sh
diff --git a/tests/unit/go/task.yaml b/tests/unit/go/task.yaml
index 3ea8808ff1..95f1fb999f 100644
--- a/tests/unit/go/task.yaml
+++ b/tests/unit/go/task.yaml
@@ -1,8 +1,7 @@
summary: Run project static and unit tests
# debian-sid complains about format-wrong most likely because of newer go
-# ubuntu-19.10-64: pending fix for this test coming on a following change
-systems: [-debian-sid-*, -ubuntu-19.10-64]
+systems: [-debian-sid-*]
# Start before anything else as it takes a long time.
priority: 1000