summaryrefslogtreecommitdiff
diff options
authorPawel Stolowski <stolowski@gmail.com>2019-01-29 13:07:25 +0100
committerPawel Stolowski <stolowski@gmail.com>2019-01-29 13:07:25 +0100
commitef9e72251a38aacdfceaf3f282767668c4155896 (patch)
treedba34fb7f71b8c74b471eb8fba94a94cf829e497
parent4d376a19aed0294393dc89ee4dfd3302b217ed1a (diff)
parent740eec88724e87c2eb4b96c2e9ef65b086acc773 (diff)
Merge branch 'master' into hotplug-update-slothotplug-update-slot
-rw-r--r--.travis.yml7
-rw-r--r--cmd/snap-update-ns/bootstrap.c3
-rw-r--r--cmd/snap/cmd_info.go193
-rw-r--r--cmd/snap/cmd_info_test.go53
-rw-r--r--cmd/snap/cmd_run_test.go120
-rw-r--r--cmd/snap/error.go4
-rw-r--r--cmd/snap/export_test.go9
-rw-r--r--daemon/api.go26
-rw-r--r--daemon/api_snapshots.go3
-rw-r--r--daemon/api_snapshots_test.go2
-rw-r--r--daemon/api_test.go4
-rw-r--r--daemon/export_snapshots_test.go2
-rw-r--r--daemon/snap.go7
-rw-r--r--image/helpers.go2
-rw-r--r--image/image_test.go2
-rw-r--r--interfaces/apparmor/backend_test.go28
-rw-r--r--interfaces/apparmor/template.go6
-rw-r--r--interfaces/builtin/block_devices.go98
-rw-r--r--interfaces/builtin/block_devices_test.go118
-rw-r--r--interfaces/builtin/camera.go1
-rw-r--r--interfaces/builtin/common.go5
-rw-r--r--interfaces/builtin/common_test.go10
-rw-r--r--interfaces/builtin/dbus.go12
-rw-r--r--interfaces/builtin/display_control.go137
-rw-r--r--interfaces/builtin/display_control_test.go125
-rw-r--r--interfaces/builtin/export_test.go10
-rw-r--r--interfaces/builtin/hardware_observe.go1
-rw-r--r--interfaces/builtin/home.go1
-rw-r--r--interfaces/builtin/kvm.go68
-rw-r--r--interfaces/builtin/kvm_test.go92
-rw-r--r--interfaces/builtin/pulseaudio.go2
-rw-r--r--interfaces/builtin/system_observe.go1
-rw-r--r--interfaces/builtin/u2f_devices.go149
-rw-r--r--interfaces/builtin/u2f_devices_test.go116
-rw-r--r--interfaces/core.go24
-rw-r--r--interfaces/policy/basedeclaration_test.go20
-rw-r--r--interfaces/seccomp/template.go2
-rw-r--r--interfaces/sorting.go11
-rw-r--r--interfaces/sorting_test.go62
-rw-r--r--osutil/context.go3
-rw-r--r--osutil/context_test.go20
-rw-r--r--overlord/auth/auth.go2
-rw-r--r--overlord/auth/auth_test.go3
-rw-r--r--overlord/hookstate/ctlcmd/services_test.go3
-rw-r--r--overlord/ifacestate/ifacestate_test.go68
-rw-r--r--overlord/managers_test.go3
-rw-r--r--overlord/snapshotstate/backend/backend.go3
-rw-r--r--overlord/snapshotstate/backend/backend_test.go2
-rw-r--r--overlord/snapshotstate/backend/reader.go3
-rw-r--r--overlord/snapshotstate/export_test.go3
-rw-r--r--overlord/snapshotstate/snapshotmgr_test.go2
-rw-r--r--overlord/snapshotstate/snapshotstate.go3
-rw-r--r--overlord/snapshotstate/snapshotstate_test.go2
-rw-r--r--overlord/snapstate/autorefresh_test.go3
-rw-r--r--overlord/snapstate/backend.go3
-rw-r--r--overlord/snapstate/backend/export_test.go12
-rw-r--r--overlord/snapstate/backend/fontconfig.go3
-rw-r--r--overlord/snapstate/backend/link.go28
-rw-r--r--overlord/snapstate/backend/link_test.go54
-rw-r--r--overlord/snapstate/backend_test.go3
-rw-r--r--overlord/snapstate/catalogrefresh_test.go3
-rw-r--r--overlord/snapstate/refreshhints_test.go3
-rw-r--r--overlord/snapstate/snapstate.go3
-rw-r--r--overlord/snapstate/snapstate_test.go31
-rw-r--r--overlord/snapstate/storehelpers.go12
-rw-r--r--packaging/fedora/snapd.spec10
-rw-r--r--packaging/opensuse/snapd.spec5
-rw-r--r--packaging/ubuntu-14.04/control2
-rwxr-xr-xpackaging/ubuntu-14.04/rules5
-rw-r--r--packaging/ubuntu-14.04/snapd.dirs1
-rw-r--r--packaging/ubuntu-16.04/control2
-rwxr-xr-xpackaging/ubuntu-16.04/rules2
-rw-r--r--packaging/ubuntu-16.04/snapd.dirs1
-rw-r--r--parts/plugins/x_builddeb.py3
-rwxr-xr-xrun-checks3
-rw-r--r--spread.yaml11
-rw-r--r--store/download_test.go2
-rw-r--r--store/export_test.go2
-rw-r--r--store/store.go11
-rw-r--r--store/store_test.go2
-rw-r--r--store/storetest/storetest.go3
-rwxr-xr-xtests/lib/best_golang.py12
-rwxr-xr-xtests/lib/prepare-restore.sh16
-rwxr-xr-xtests/lib/snaps/test-snapd-dbus-consumer/consumer.py10
-rw-r--r--tests/lib/snaps/test-snapd-dbus-consumer/snapcraft.yaml9
-rwxr-xr-xtests/lib/snaps/test-snapd-dbus-provider/consumer.py14
-rw-r--r--tests/lib/snaps/test-snapd-dbus-provider/provider.py13
-rw-r--r--tests/lib/snaps/test-snapd-dbus-provider/snapcraft.yaml28
-rwxr-xr-xtests/lib/snaps/test-snapd-dbus-provider/wrapper9
-rw-r--r--tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml9
-rw-r--r--tests/main/auth-errors/task.yaml4
-rw-r--r--tests/main/install-snaps/task.yaml4
-rw-r--r--tests/main/interfaces-autopilot-introspection/task.yaml30
-rw-r--r--tests/main/interfaces-avahi-observe/task.yaml23
-rw-r--r--tests/main/interfaces-bluetooth-control/task.yaml51
-rw-r--r--tests/main/interfaces-broadcom-asic-control/task.yaml2
-rw-r--r--tests/main/interfaces-browser-support/task.yaml93
-rw-r--r--tests/main/interfaces-contacts-service/task.yaml17
-rw-r--r--tests/main/interfaces-dbus/task.yaml21
-rw-r--r--tests/main/interfaces-device-buttons/task.yaml2
-rw-r--r--tests/main/interfaces-dvb/task.yaml6
-rw-r--r--tests/main/interfaces-fuse_support/task.yaml7
-rw-r--r--tests/main/interfaces-hostname-control/task.yaml10
-rw-r--r--tests/main/interfaces-joystick/task.yaml2
-rw-r--r--tests/main/interfaces-juju-client-observe/task.yaml6
-rw-r--r--tests/main/interfaces-kernel-module-control/task.yaml8
-rw-r--r--tests/main/interfaces-libvirt/task.yaml2
-rw-r--r--tests/main/interfaces-locale-control/task.yaml22
-rw-r--r--tests/main/interfaces-location-control/task.yaml2
-rw-r--r--tests/main/interfaces-mount-observe/task.yaml24
-rw-r--r--tests/main/interfaces-network-control-tuntap/task.yaml12
-rw-r--r--tests/main/interfaces-network-manager/task.yaml2
-rw-r--r--tests/main/interfaces-network-observe/task.yaml21
-rw-r--r--tests/main/interfaces-network-status/task.yaml2
-rw-r--r--tests/main/interfaces-openvswitch-support/task.yaml2
-rw-r--r--tests/main/interfaces-openvswitch/task.yaml8
-rw-r--r--tests/main/interfaces-physical-memory-observe/task.yaml2
-rw-r--r--tests/main/interfaces-process-control/task.yaml2
-rw-r--r--tests/main/interfaces-raw-usb/task.yaml2
-rw-r--r--tests/main/interfaces-removable-media/task.yaml2
-rw-r--r--tests/main/interfaces-shutdown-introspection/task.yaml6
-rw-r--r--tests/main/interfaces-snapd-control/task.yaml29
-rw-r--r--tests/main/interfaces-ssh-keys/task.yaml2
-rw-r--r--tests/main/interfaces-ssh-public-keys/task.yaml4
-rw-r--r--tests/main/interfaces-system-dbus/task.yaml50
-rw-r--r--tests/main/interfaces-system-observe/task.yaml37
-rw-r--r--tests/main/interfaces-time-control/task.yaml18
-rw-r--r--tests/main/interfaces-timeserver-control/task.yaml4
-rw-r--r--tests/main/interfaces-timezone-control/task.yaml6
-rw-r--r--tests/main/interfaces-uhid/task.yaml2
-rw-r--r--tests/main/interfaces-upower-observe/task.yaml22
-rw-r--r--tests/main/searching/task.yaml7
-rw-r--r--tests/main/security-dev-input-event-denied/task.yaml8
-rw-r--r--tests/main/security-udev-input-subsystem/task.yaml8
-rw-r--r--vendor/vendor.json6
135 files changed, 2048 insertions, 551 deletions
diff --git a/.travis.yml b/.travis.yml
index 0c02f33f3b..52404c8e09 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,9 @@ git:
jobs:
include:
- stage: quick
- name: go 1.6/xenial static and unit test suites
+ name: go 1.9/xenial static and unit test suites
dist: xenial
- go: 1.6
+ go: 1.9
before_install:
- sudo apt --quiet -o Dpkg::Progress-Fancy=false update
install:
@@ -36,6 +36,7 @@ jobs:
- /tmp/snp pack tests/lib/snaps/test-snapd-tools/ /tmp
- stage: quick
name: CLA check
+ dist: xenial
if: type = pull_request
language: bash
addons:
@@ -46,7 +47,7 @@ jobs:
- ./tests/lib/cla_check.py
- stage: integration
name: spread
- os: linux
+ dist: xenial
addons:
apt:
packages:
diff --git a/cmd/snap-update-ns/bootstrap.c b/cmd/snap-update-ns/bootstrap.c
index 1d774ca679..8fd3d82d27 100644
--- a/cmd/snap-update-ns/bootstrap.c
+++ b/cmd/snap-update-ns/bootstrap.c
@@ -344,6 +344,7 @@ static int parse_arg_u(int argc, char * const *argv, int *optind, unsigned long
errno = 0;
char *uid_text_end = NULL;
unsigned long parsed_uid = strtoul(uid_text, &uid_text_end, 10);
+ int saved_errno = errno;
char c = *uid_text;
if (
/* Reject overflow in parsed representation */
@@ -359,7 +360,7 @@ static int parse_arg_u(int argc, char * const *argv, int *optind, unsigned long
|| (*uid_text != '\0' && uid_text_end != NULL
&& *uid_text_end != '\0')) {
bootstrap_msg = "cannot parse user id";
- bootstrap_errno = errno;
+ bootstrap_errno = saved_errno;
return -1;
}
if ((long)parsed_uid < 0) {
diff --git a/cmd/snap/cmd_info.go b/cmd/snap/cmd_info.go
index 74d7eca985..760d15bee0 100644
--- a/cmd/snap/cmd_info.go
+++ b/cmd/snap/cmd_info.go
@@ -23,6 +23,7 @@ import (
"fmt"
"io"
"path/filepath"
+ "strconv"
"strings"
"text/tabwriter"
"time"
@@ -118,7 +119,7 @@ func maybePrintBase(w io.Writer, base string, verbose bool) {
}
}
-func tryDirect(w io.Writer, path string, verbose bool) bool {
+func tryDirect(w io.Writer, path string, verbose bool, termWidth int) bool {
path = norm(path)
snapf, err := snap.Open(path)
@@ -141,7 +142,7 @@ func tryDirect(w io.Writer, path string, verbose bool) bool {
}
fmt.Fprintf(w, "path:\t%q\n", path)
fmt.Fprintf(w, "name:\t%s\n", info.InstanceName())
- fmt.Fprintf(w, "summary:\t%s\n", formatSummary(info.Summary()))
+ printSummary(w, info.Summary(), termWidth)
var notes *Notes
if verbose {
@@ -195,9 +196,31 @@ func runesLastIndexSpace(text []rune) int {
return -1
}
-// wrapLine wraps a line to fit into width, preserving the line's indent, and
+// wrapLine wraps a line, assumed to be part of a block-style yaml
+// string, to fit into termWidth, preserving the line's indent, and
// writes it out prepending padding to each line.
-func wrapLine(out io.Writer, text []rune, pad string, width int) error {
+func wrapLine(out io.Writer, text []rune, pad string, termWidth int) error {
+ // discard any trailing whitespace
+ text = runesTrimRightSpace(text)
+ // establish the indent of the whole block
+ idx := 0
+ for idx < len(text) && unicode.IsSpace(text[idx]) {
+ idx++
+ }
+ indent := pad + string(text[:idx])
+ text = text[idx:]
+ return wrapGeneric(out, text, indent, indent, termWidth)
+}
+
+// wrapFlow wraps the text using yaml's flow style, allowing indent
+// characters for the first line.
+func wrapFlow(out io.Writer, text []rune, indent string, termWidth int) error {
+ return wrapGeneric(out, text, indent, " ", termWidth)
+}
+
+// wrapGeneric wraps the given text to the given width, prefixing the
+// first line with indent and the remaining lines with indent2
+func wrapGeneric(out io.Writer, text []rune, indent, indent2 string, termWidth int) error {
// Note: this is _wrong_ for much of unicode (because the width of a rune on
// the terminal is anything between 0 and 2, not always 1 as this code
// assumes) but fixing that is Hard. Long story short, you can get close
@@ -210,16 +233,12 @@ func wrapLine(out io.Writer, text []rune, pad string, width int) error {
// This (and possibly printDescr below) should move to strutil once
// we're happy with it getting wider (heh heh) use.
- // discard any trailing whitespace
- text = runesTrimRightSpace(text)
+ indentWidth := utf8.RuneCountInString(indent)
+ delta := indentWidth - utf8.RuneCountInString(indent2)
+ width := termWidth - indentWidth
+
// establish the indent of the whole block
idx := 0
- for idx < len(text) && unicode.IsSpace(text[idx]) {
- idx++
- }
- indent := pad + string(text[:idx])
- text = text[idx:]
- width -= idx + utf8.RuneCountInString(pad)
var err error
for len(text) > width && err == nil {
// find a good place to chop the text
@@ -234,6 +253,9 @@ func wrapLine(out io.Writer, text []rune, pad string, width int) error {
idx++
}
text = text[idx:]
+ width += delta
+ indent = indent2
+ delta = 0
}
if err != nil {
return err
@@ -242,6 +264,21 @@ func wrapLine(out io.Writer, text []rune, pad string, width int) error {
return err
}
+func printSummary(w io.Writer, raw string, termWidth int) error {
+ // simplest way of checking to see if it needs quoting is to try
+ raw = strings.TrimSpace(raw)
+ type T struct {
+ S string
+ }
+ if len(raw) == 0 {
+ raw = `""`
+ } else if err := yaml.UnmarshalStrict([]byte("s: "+raw), &T{}); err != nil {
+ raw = strconv.Quote(raw)
+ }
+
+ return wrapFlow(w, []rune(raw), "summary:\t", termWidth)
+}
+
// printDescr formats a given string (typically a snap description)
// in a user friendly way.
//
@@ -249,11 +286,11 @@ func wrapLine(out io.Writer, text []rune, pad string, width int) error {
// - trim trailing whitespace
// - word wrap at "max" chars preserving line indent
// - keep \n intact and break there
-func printDescr(w io.Writer, descr string, max int) error {
+func printDescr(w io.Writer, descr string, termWidth int) error {
var err error
descr = strings.TrimRightFunc(descr, unicode.IsSpace)
for _, line := range strings.Split(descr, "\n") {
- err = wrapLine(w, []rune(line), " ", max)
+ err = wrapLine(w, []rune(line), " ", termWidth)
if err != nil {
break
}
@@ -321,21 +358,59 @@ func maybePrintServices(w io.Writer, snapName string, allApps []client.AppInfo,
var channelRisks = []string{"stable", "candidate", "beta", "edge"}
-// displayChannels displays channels and tracks in the right order
-func (x *infoCmd) displayChannels(w io.Writer, chantpl string, esc *escapes, remote *client.Snap, revLen, sizeLen int) (maxRevLen, maxSizeLen int) {
- fmt.Fprintln(w, "channels:")
+type channelInfo struct {
+ indent, name, version, released, revision, size, notes string
+}
+
+type channelInfos struct {
+ channels []*channelInfo
+ maxRevLen, maxSizeLen int
+ releasedfmt, chantpl string
+ needsHeader bool
+ esc *escapes
+}
- releasedfmt := "2006-01-02"
- if x.AbsTime {
- releasedfmt = time.RFC3339
+func (chInfos *channelInfos) add(indent, name, version string, revision snap.Revision, released time.Time, size int64, notes *Notes) {
+ chInfo := &channelInfo{
+ indent: indent,
+ name: name,
+ version: version,
+ revision: fmt.Sprintf("(%s)", revision),
+ size: strutil.SizeToStr(size),
+ notes: notes.String(),
+ }
+ if !released.IsZero() {
+ chInfo.released = released.Format(chInfos.releasedfmt)
+ }
+ if len(chInfo.revision) > chInfos.maxRevLen {
+ chInfos.maxRevLen = len(chInfo.revision)
}
+ if len(chInfo.size) > chInfos.maxSizeLen {
+ chInfos.maxSizeLen = len(chInfo.size)
+ }
+ chInfos.channels = append(chInfos.channels, chInfo)
+}
+
+func (chInfos *channelInfos) addFromLocal(local *client.Snap) {
+ chInfos.add("", "installed", local.Version, local.Revision, time.Time{}, local.InstalledSize, NotesFromLocal(local))
+}
+
+func (chInfos *channelInfos) addOpenChannel(name, version string, revision snap.Revision, released time.Time, size int64, notes *Notes) {
+ chInfos.add(" ", name, version, revision, released, size, notes)
+}
- type chInfoT struct {
- name, version, released, revision, size, notes string
+func (chInfos *channelInfos) addClosedChannel(name string, trackHasOpenChannel bool) {
+ chInfo := &channelInfo{indent: " ", name: name}
+ if trackHasOpenChannel {
+ chInfo.version = chInfos.esc.uparrow
+ } else {
+ chInfo.version = chInfos.esc.dash
}
- var chInfos []*chInfoT
- maxRevLen, maxSizeLen = revLen, sizeLen
+ chInfos.channels = append(chInfos.channels, chInfo)
+}
+
+func (chInfos *channelInfos) addFromRemote(remote *client.Snap) {
// order by tracks
for _, tr := range remote.Tracks {
trackHasOpenChannel := false
@@ -345,44 +420,24 @@ func (x *infoCmd) displayChannels(w io.Writer, chantpl string, esc *escapes, rem
if tr == "latest" {
chName = risk
}
- chInfo := chInfoT{name: chName}
if ok {
- chInfo.version = ch.Version
- chInfo.revision = fmt.Sprintf("(%s)", ch.Revision)
- if len(chInfo.revision) > maxRevLen {
- maxRevLen = len(chInfo.revision)
- }
- chInfo.released = ch.ReleasedAt.Format(releasedfmt)
- chInfo.size = strutil.SizeToStr(ch.Size)
- if len(chInfo.size) > maxSizeLen {
- maxSizeLen = len(chInfo.size)
- }
- chInfo.notes = NotesFromChannelSnapInfo(ch).String()
+ chInfos.addOpenChannel(chName, ch.Version, ch.Revision, ch.ReleasedAt, ch.Size, NotesFromChannelSnapInfo(ch))
trackHasOpenChannel = true
} else {
- if trackHasOpenChannel {
- chInfo.version = esc.uparrow
- } else {
- chInfo.version = esc.dash
- }
+ chInfos.addClosedChannel(chName, trackHasOpenChannel)
}
- chInfos = append(chInfos, &chInfo)
}
}
-
- for _, chInfo := range chInfos {
- fmt.Fprintf(w, " "+chantpl, chInfo.name, chInfo.version, chInfo.released, maxRevLen, chInfo.revision, maxSizeLen, chInfo.size, chInfo.notes)
- }
-
- return maxRevLen, maxSizeLen
+ chInfos.needsHeader = len(chInfos.channels) > 0
}
-func formatSummary(raw string) string {
- s, err := yaml.Marshal(raw)
- if err != nil {
- return fmt.Sprintf("cannot marshal summary: %s", err)
+func (chInfos *channelInfos) dump(w io.Writer) {
+ if chInfos.needsHeader {
+ fmt.Fprintln(w, "channels:")
+ }
+ for _, c := range chInfos.channels {
+ fmt.Fprintf(w, chInfos.chantpl, c.indent, c.name, c.version, c.released, chInfos.maxRevLen, c.revision, chInfos.maxSizeLen, c.size, c.notes)
}
- return strings.TrimSpace(string(s))
}
func (x *infoCmd) Execute([]string) error {
@@ -407,7 +462,7 @@ func (x *infoCmd) Execute([]string) error {
continue
}
- if tryDirect(w, snapName, x.Verbose) {
+ if tryDirect(w, snapName, x.Verbose, termWidth) {
noneOK = false
continue
}
@@ -427,7 +482,7 @@ func (x *infoCmd) Execute([]string) error {
noneOK = false
fmt.Fprintf(w, "name:\t%s\n", both.Name)
- fmt.Fprintf(w, "summary:\t%s\n", formatSummary(both.Summary))
+ printSummary(w, both.Summary, termWidth)
fmt.Fprintf(w, "publisher:\t%s\n", longPublisher(esc, both.Publisher))
if both.Contact != "" {
fmt.Fprintf(w, "contact:\t%s\n", strings.TrimPrefix(both.Contact, "mailto:"))
@@ -449,7 +504,6 @@ func (x *infoCmd) Execute([]string) error {
fmt.Fprintf(w, " confinement:\t%s\n", both.Confinement)
}
- var notes *Notes
if local != nil {
if x.Verbose {
jailMode := local.Confinement == client.DevModeConfinement && !local.DevMode
@@ -464,8 +518,6 @@ func (x *infoCmd) Execute([]string) error {
}
fmt.Fprintf(w, " ignore-validation:\t%t\n", local.IgnoreValidation)
- } else {
- notes = NotesFromLocal(local)
}
}
// stops the notes etc trying to be aligned with channels
@@ -473,8 +525,6 @@ func (x *infoCmd) Execute([]string) error {
maybePrintType(w, both.Type)
maybePrintBase(w, both.Base, x.Verbose)
maybePrintID(w, both)
- var localRev, localSize string
- var revLen, sizeLen int
if local != nil {
if local.TrackingChannel != "" {
fmt.Fprintf(w, "tracking:\t%s\n", local.TrackingChannel)
@@ -482,24 +532,25 @@ func (x *infoCmd) Execute([]string) error {
if !local.InstallDate.IsZero() {
fmt.Fprintf(w, "refresh-date:\t%s\n", x.fmtTime(local.InstallDate))
}
- localRev = fmt.Sprintf("(%s)", local.Revision)
- revLen = len(localRev)
- localSize = strutil.SizeToStr(local.InstalledSize)
- sizeLen = len(localSize)
}
- chantpl := "%s:\t%s %s%*s %*s %s\n"
+ chInfos := channelInfos{
+ chantpl: "%s%s:\t%s %s%*s %*s %s\n",
+ releasedfmt: "2006-01-02",
+ esc: esc,
+ }
+ if x.AbsTime {
+ chInfos.releasedfmt = time.RFC3339
+ }
if remote != nil && remote.Channels != nil && remote.Tracks != nil {
- chantpl = "%s:\t%s\t%s\t%*s\t%*s\t%s\n"
-
w.Flush()
- revLen, sizeLen = x.displayChannels(w, chantpl, esc, remote, revLen, sizeLen)
+ chInfos.chantpl = "%s%s:\t%s\t%s\t%*s\t%*s\t%s\n"
+ chInfos.addFromRemote(remote)
}
if local != nil {
- fmt.Fprintf(w, chantpl,
- "installed", local.Version, "", revLen, localRev, sizeLen, localSize, notes)
+ chInfos.addFromLocal(local)
}
-
+ chInfos.dump(w)
}
w.Flush()
diff --git a/cmd/snap/cmd_info_test.go b/cmd/snap/cmd_info_test.go
index 185fd7df92..4fac59bb7f 100644
--- a/cmd/snap/cmd_info_test.go
+++ b/cmd/snap/cmd_info_test.go
@@ -97,6 +97,45 @@ func (s *infoSuite) TestMaybePrintCommandsNoCommands(c *check.C) {
}
}
+func (s *infoSuite) TestInfoPricedNarrowTerminal(c *check.C) {
+ defer snap.MockTermSize(func() (int, int) { return 44, 25 })()
+
+ n := 0
+ s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
+ switch n {
+ case 0:
+ c.Check(r.Method, check.Equals, "GET")
+ c.Check(r.URL.Path, check.Equals, "/v2/find")
+ fmt.Fprintln(w, findPricedJSON)
+ case 1:
+ c.Check(r.Method, check.Equals, "GET")
+ c.Check(r.URL.Path, check.Equals, "/v2/snaps/hello")
+ fmt.Fprintln(w, "{}")
+ default:
+ c.Fatalf("expected to get 1 requests, now on %d (%v)", n+1, r)
+ }
+
+ n++
+ })
+ rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"info", "hello"})
+ c.Assert(err, check.IsNil)
+ c.Assert(rest, check.DeepEquals, []string{})
+ c.Check(s.Stdout(), check.Equals, `
+name: hello
+summary: GNU Hello, the "hello world"
+ snap
+publisher: Canonical*
+license: Proprietary
+price: 1.99GBP
+description: |
+ GNU hello prints a friendly greeting.
+ This is part of the snapcraft tour at
+ https://snapcraft.io/
+snap-id: mVyGrEwiqSi5PugCwyH7WgpoQLemtTd6
+`[1:])
+ c.Check(s.Stderr(), check.Equals, "")
+}
+
func (s *infoSuite) TestInfoPriced(c *check.C) {
n := 0
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
@@ -550,3 +589,17 @@ func (infoSuite) TestDescr(c *check.C) {
c.Check(buf.String(), check.Equals, v, check.Commentf("%q", k))
}
}
+
+func (infoSuite) TestWrapCornerCase(c *check.C) {
+ // this particular corner case isn't currently reachable from
+ // printDescr nor printSummary, but best to have it covered
+ var buf bytes.Buffer
+ const s = "This is a paragraph indented with leading spaces that are encoded as multiple bytes. All hail EN SPACE."
+ snap.WrapFlow(&buf, []rune(s), "\u2002\u2002", 30)
+ c.Check(buf.String(), check.Equals, `
+  This is a paragraph indented
+ with leading spaces that are
+ encoded as multiple bytes.
+ All hail EN SPACE.
+`[1:])
+}
diff --git a/cmd/snap/cmd_run_test.go b/cmd/snap/cmd_run_test.go
index beb7b2e236..b0779f4e39 100644
--- a/cmd/snap/cmd_run_test.go
+++ b/cmd/snap/cmd_run_test.go
@@ -50,7 +50,25 @@ hooks:
configure:
`)
-func (s *SnapSuite) TestInvalidParameters(c *check.C) {
+type RunSuite struct {
+ fakeHome string
+ SnapSuite
+}
+
+var _ = check.Suite(&RunSuite{})
+
+func (s *RunSuite) SetUpTest(c *check.C) {
+ s.SnapSuite.SetUpTest(c)
+ s.fakeHome = c.MkDir()
+
+ u, err := user.Current()
+ c.Assert(err, check.IsNil)
+ s.AddCleanup(snaprun.MockUserCurrent(func() (*user.User, error) {
+ return &user.User{Uid: u.Uid, HomeDir: s.fakeHome}, nil
+ }))
+}
+
+func (s *RunSuite) TestInvalidParameters(c *check.C) {
invalidParameters := []string{"run", "--hook=configure", "--command=command-name", "--", "snap-name"}
_, err := snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters)
c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*")
@@ -76,7 +94,7 @@ func (s *SnapSuite) TestInvalidParameters(c *check.C) {
c.Check(err, check.ErrorMatches, ".*too many arguments for hook \"configure\": bar.*")
}
-func (s *SnapSuite) TestSnapRunWhenMissingConfine(c *check.C) {
+func (s *RunSuite) TestSnapRunWhenMissingConfine(c *check.C) {
_, r := logger.MockLogger()
defer r()
@@ -105,7 +123,7 @@ func (s *SnapSuite) TestSnapRunWhenMissingConfine(c *check.C) {
c.Check(execs, check.IsNil)
}
-func (s *SnapSuite) TestSnapRunAppIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunAppIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -138,7 +156,7 @@ func (s *SnapSuite) TestSnapRunAppIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
}
-func (s *SnapSuite) TestSnapRunClassicAppIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunClassicAppIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -172,7 +190,7 @@ func (s *SnapSuite) TestSnapRunClassicAppIntegration(c *check.C) {
}
-func (s *SnapSuite) TestSnapRunClassicAppIntegrationReexeced(c *check.C) {
+func (s *RunSuite) TestSnapRunClassicAppIntegrationReexeced(c *check.C) {
mountedCorePath := filepath.Join(dirs.SnapMountDir, "core/current")
mountedCoreLibExecPath := filepath.Join(mountedCorePath, dirs.CoreLibExecDir)
@@ -205,7 +223,7 @@ func (s *SnapSuite) TestSnapRunClassicAppIntegrationReexeced(c *check.C) {
"snapname.app", "--arg1", "arg2"})
}
-func (s *SnapSuite) TestSnapRunAppWithCommandIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunAppWithCommandIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -237,48 +255,36 @@ func (s *SnapSuite) TestSnapRunAppWithCommandIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
}
-func (s *SnapSuite) TestSnapRunCreateDataDirs(c *check.C) {
+func (s *RunSuite) TestSnapRunCreateDataDirs(c *check.C) {
info, err := snap.InfoFromSnapYaml(mockYaml)
c.Assert(err, check.IsNil)
info.SideInfo.Revision = snap.R(42)
- fakeHome := c.MkDir()
- restorer := snaprun.MockUserCurrent(func() (*user.User, error) {
- return &user.User{HomeDir: fakeHome}, nil
- })
- defer restorer()
-
err = snaprun.CreateUserDataDirs(info)
c.Assert(err, check.IsNil)
- c.Check(osutil.FileExists(filepath.Join(fakeHome, "/snap/snapname/42")), check.Equals, true)
- c.Check(osutil.FileExists(filepath.Join(fakeHome, "/snap/snapname/common")), check.Equals, true)
+ c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/42")), check.Equals, true)
+ c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/common")), check.Equals, true)
}
-func (s *SnapSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) {
+func (s *RunSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) {
info, err := snap.InfoFromSnapYaml(mockYaml)
c.Assert(err, check.IsNil)
info.SideInfo.Revision = snap.R(42)
info.InstanceKey = "foo"
- fakeHome := c.MkDir()
- restorer := snaprun.MockUserCurrent(func() (*user.User, error) {
- return &user.User{HomeDir: fakeHome}, nil
- })
- defer restorer()
-
err = snaprun.CreateUserDataDirs(info)
c.Assert(err, check.IsNil)
- c.Check(osutil.FileExists(filepath.Join(fakeHome, "/snap/snapname_foo/42")), check.Equals, true)
- c.Check(osutil.FileExists(filepath.Join(fakeHome, "/snap/snapname_foo/common")), check.Equals, true)
+ c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/42")), check.Equals, true)
+ c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/common")), check.Equals, true)
// mount point for snap instance mapping has been created
- c.Check(osutil.FileExists(filepath.Join(fakeHome, "/snap/snapname")), check.Equals, true)
+ c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname")), check.Equals, true)
// and it's empty inside
- m, err := filepath.Glob(filepath.Join(fakeHome, "/snap/snapname/*"))
+ m, err := filepath.Glob(filepath.Join(s.fakeHome, "/snap/snapname/*"))
c.Assert(err, check.IsNil)
c.Assert(m, check.HasLen, 0)
}
-func (s *SnapSuite) TestSnapRunHookIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -310,7 +316,7 @@ func (s *SnapSuite) TestSnapRunHookIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
}
-func (s *SnapSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -342,7 +348,7 @@ func (s *SnapSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42")
}
-func (s *SnapSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -378,7 +384,7 @@ func (s *SnapSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=41")
}
-func (s *SnapSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) {
// Only create revision 42
snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
Revision: snap.R(42),
@@ -396,13 +402,13 @@ func (s *SnapSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) {
c.Check(err, check.ErrorMatches, "cannot find .*")
}
-func (s *SnapSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) {
_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=invalid", "--", "snapname"})
c.Assert(err, check.NotNil)
c.Check(err, check.ErrorMatches, "invalid snap revision: \"invalid\"")
}
-func (s *SnapSuite) TestSnapRunHookMissingHookIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunHookMissingHookIntegration(c *check.C) {
// Only create revision 42
snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{
Revision: snap.R(42),
@@ -421,22 +427,22 @@ func (s *SnapSuite) TestSnapRunHookMissingHookIntegration(c *check.C) {
c.Check(called, check.Equals, false)
}
-func (s *SnapSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) {
+func (s *RunSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) {
_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--unknown", "--", "snapname.app", "--arg1", "arg2"})
c.Assert(err, check.ErrorMatches, "unknown flag `unknown'")
}
-func (s *SnapSuite) TestSnapRunErorsForMissingApp(c *check.C) {
+func (s *RunSuite) TestSnapRunErorsForMissingApp(c *check.C) {
_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=shell"})
c.Assert(err, check.ErrorMatches, "need the application to run as argument")
}
-func (s *SnapSuite) TestSnapRunErorrForUnavailableApp(c *check.C) {
+func (s *RunSuite) TestSnapRunErorrForUnavailableApp(c *check.C) {
_, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "not-there"})
c.Assert(err, check.ErrorMatches, fmt.Sprintf("cannot find current revision for snap not-there: readlink %s/not-there/current: no such file or directory", dirs.SnapMountDir))
}
-func (s *SnapSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) {
+func (s *RunSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -471,7 +477,7 @@ func (s *SnapSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_THE_WORLD=YES")
}
-func (s *SnapSuite) TestSnapRunIsReexeced(c *check.C) {
+func (s *RunSuite) TestSnapRunIsReexeced(c *check.C) {
var osReadlinkResult string
restore := snaprun.MockOsReadlink(func(name string) (string, error) {
return osReadlinkResult, nil
@@ -490,7 +496,7 @@ func (s *SnapSuite) TestSnapRunIsReexeced(c *check.C) {
}
}
-func (s *SnapSuite) TestSnapRunAppIntegrationFromCore(c *check.C) {
+func (s *RunSuite) TestSnapRunAppIntegrationFromCore(c *check.C) {
defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))()
// mock installed snap
@@ -529,7 +535,7 @@ func (s *SnapSuite) TestSnapRunAppIntegrationFromCore(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
}
-func (s *SnapSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) {
+func (s *RunSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) {
defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "snapd", "222", dirs.CoreLibExecDir))()
// mock installed snap
@@ -568,7 +574,7 @@ func (s *SnapSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
}
-func (s *SnapSuite) TestSnapRunXauthorityMigration(c *check.C) {
+func (s *RunSuite) TestSnapRunXauthorityMigration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
u, err := user.Current()
@@ -644,7 +650,7 @@ func mkCompArgs(compPoint string, argv ...string) []string {
return out
}
-func (s *SnapSuite) TestAntialiasHappy(c *check.C) {
+func (s *RunSuite) TestAntialiasHappy(c *check.C) {
c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
inArgs := mkCompArgs("10", "alias", "alias", "bo-alias")
@@ -672,7 +678,7 @@ func (s *SnapSuite) TestAntialiasHappy(c *check.C) {
})
}
-func (s *SnapSuite) TestAntialiasBailsIfUnhappy(c *check.C) {
+func (s *RunSuite) TestAntialiasBailsIfUnhappy(c *check.C) {
// alias exists but args are somehow wonky
c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil)
c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil)
@@ -701,7 +707,7 @@ func (s *SnapSuite) TestAntialiasBailsIfUnhappy(c *check.C) {
}
}
-func (s *SnapSuite) TestSnapRunAppWithStraceIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunAppWithStraceIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -781,7 +787,7 @@ and more
c.Check(s.Stderr(), check.Equals, fmt.Sprintf(expectedFullFmt, dirs.SnapMountDir))
}
-func (s *SnapSuite) TestSnapRunAppWithStraceOptions(c *check.C) {
+func (s *RunSuite) TestSnapRunAppWithStraceOptions(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -822,7 +828,7 @@ func (s *SnapSuite) TestSnapRunAppWithStraceOptions(c *check.C) {
})
}
-func (s *SnapSuite) TestSnapRunShellIntegration(c *check.C) {
+func (s *RunSuite) TestSnapRunShellIntegration(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -855,7 +861,7 @@ func (s *SnapSuite) TestSnapRunShellIntegration(c *check.C) {
c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2")
}
-func (s *SnapSuite) TestSnapRunAppTimer(c *check.C) {
+func (s *RunSuite) TestSnapRunAppTimer(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -910,7 +916,7 @@ func (s *SnapSuite) TestSnapRunAppTimer(c *check.C) {
"snapname.app", "--arg1", "arg2"})
}
-func (s *SnapSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) {
+func (s *RunSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) {
defer mockSnapConfine(dirs.DistroLibExecDir)()
// mock installed snap
@@ -933,7 +939,7 @@ func (s *SnapSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) {
c.Check(s.Stderr(), check.Equals, "")
}
-func (s *SnapSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
+func (s *RunSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
logbuf, restorer := logger.MockLogger()
defer restorer()
@@ -944,12 +950,6 @@ func (s *SnapSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
Revision: snap.R("x2"),
})
- fakeHome := c.MkDir()
- restorer = snaprun.MockUserCurrent(func() (*user.User, error) {
- return &user.User{HomeDir: fakeHome}, nil
- })
- defer restorer()
-
// redirect exec
execCalled := 0
restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
@@ -964,7 +964,7 @@ func (s *SnapSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
enabled := false
verify := true
- snapUserDir := filepath.Join(fakeHome, dirs.UserHomeSnapDir)
+ snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
c.Check(what, check.Equals, snapUserDir)
@@ -1020,7 +1020,7 @@ func (s *SnapSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) {
c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("restoring default SELinux context of %s", snapUserDir))
}
-func (s *SnapSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
+func (s *RunSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
logbuf, restorer := logger.MockLogger()
defer restorer()
@@ -1031,12 +1031,6 @@ func (s *SnapSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
Revision: snap.R("x2"),
})
- fakeHome := c.MkDir()
- restorer = snaprun.MockUserCurrent(func() (*user.User, error) {
- return &user.User{HomeDir: fakeHome}, nil
- })
- defer restorer()
-
// redirect exec
execCalled := 0
restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error {
@@ -1052,7 +1046,7 @@ func (s *SnapSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) {
verifyErr := errors.New("verify failed")
restoreErr := errors.New("restore failed")
- snapUserDir := filepath.Join(fakeHome, dirs.UserHomeSnapDir)
+ snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)
restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) {
c.Check(what, check.Equals, snapUserDir)
diff --git a/cmd/snap/error.go b/cmd/snap/error.go
index 841e331cc1..5174ad6a01 100644
--- a/cmd/snap/error.go
+++ b/cmd/snap/error.go
@@ -41,7 +41,9 @@ import (
var errorPrefix = i18n.G("error: %v\n")
-func termSize() (width, height int) {
+var termSize = termSizeImpl
+
+func termSizeImpl() (width, height int) {
if f, ok := Stdout.(*os.File); ok {
width, height, _ = terminal.GetSize(int(f.Fd()))
}
diff --git a/cmd/snap/export_test.go b/cmd/snap/export_test.go
index 0dc4b1cef7..92b0afbac8 100644
--- a/cmd/snap/export_test.go
+++ b/cmd/snap/export_test.go
@@ -48,6 +48,7 @@ var (
Antialias = antialias
FormatChannel = fmtChannel
PrintDescr = printDescr
+ WrapFlow = wrapFlow
TrueishJSON = trueishJSON
CanUnicode = canUnicode
@@ -241,3 +242,11 @@ func MockSELinuxRestoreContext(restorecon func(string, selinux.RestoreMode) erro
selinuxRestoreContext = old
}
}
+
+func MockTermSize(newTermSize func() (int, int)) (restore func()) {
+ old := termSize
+ termSize = newTermSize
+ return func() {
+ termSize = old
+ }
+}
diff --git a/daemon/api.go b/daemon/api.go
index b871f0ee33..86563ac2a4 100644
--- a/daemon/api.go
+++ b/daemon/api.go
@@ -20,6 +20,7 @@
package daemon
import (
+ "context"
"encoding/json"
"errors"
"fmt"
@@ -41,7 +42,6 @@ import (
"github.com/gorilla/mux"
"github.com/jessevdk/go-flags"
- "golang.org/x/net/context"
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/snapasserts"
@@ -1618,21 +1618,29 @@ func unsafeReadSnapInfoImpl(snapPath string) (*snap.Info, error) {
var unsafeReadSnapInfo = unsafeReadSnapInfoImpl
func iconGet(st *state.State, name string) Response {
- about, err := localSnapInfo(st, name)
+ st.Lock()
+ defer st.Unlock()
+
+ var snapst snapstate.SnapState
+ err := snapstate.Get(st, name, &snapst)
if err != nil {
- if err == errNoSnap {
+ if err == state.ErrNoState {
return SnapNotFound(name, err)
}
- return InternalError("%v", err)
+ return InternalError("cannot consult state: %v", err)
+ }
+ sideInfo := snapst.CurrentSideInfo()
+ if sideInfo == nil {
+ return NotFound("snap has no current revision")
}
- path := filepath.Clean(snapIcon(about.info))
- if !strings.HasPrefix(path, dirs.SnapMountDir) {
- // XXX: how could this happen?
- return BadRequest("requested icon is not in snap path")
+ icon := snapIcon(snap.MinimalPlaceInfo(name, sideInfo.Revision))
+
+ if icon == "" {
+ return NotFound("local snap has no icon")
}
- return FileResponse(path)
+ return FileResponse(icon)
}
func appIconGet(c *Command, r *http.Request, user *auth.UserState) Response {
diff --git a/daemon/api_snapshots.go b/daemon/api_snapshots.go
index f52eed9305..bdc23aaf3c 100644
--- a/daemon/api_snapshots.go
+++ b/daemon/api_snapshots.go
@@ -20,14 +20,13 @@
package daemon
import (
+ "context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/overlord/auth"
"github.com/snapcore/snapd/overlord/state"
diff --git a/daemon/api_snapshots_test.go b/daemon/api_snapshots_test.go
index e869437725..1a7fe8e5b1 100644
--- a/daemon/api_snapshots_test.go
+++ b/daemon/api_snapshots_test.go
@@ -20,12 +20,12 @@
package daemon_test
import (
+ "context"
"errors"
"fmt"
"net/http"
"strings"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"github.com/snapcore/snapd/client"
diff --git a/daemon/api_test.go b/daemon/api_test.go
index dac4607526..f8f1e1c3a6 100644
--- a/daemon/api_test.go
+++ b/daemon/api_test.go
@@ -21,6 +21,7 @@ package daemon
import (
"bytes"
+ "context"
"crypto"
"encoding/json"
"errors"
@@ -42,7 +43,6 @@ import (
"time"
"golang.org/x/crypto/sha3"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"gopkg.in/tomb.v2"
@@ -3236,7 +3236,7 @@ func (s *apiSuite) TestAppIconGetNoApp(c *check.C) {
func (s *apiSuite) TestNotInstalledSnapIcon(c *check.C) {
info := &snap.Info{SuggestedName: "notInstalledSnap", Media: []snap.MediaInfo{{Type: "icon", URL: "icon.svg"}}}
iconfile := snapIcon(info)
- c.Check(iconfile, testutil.Contains, "icon.svg")
+ c.Check(iconfile, check.Equals, "")
}
func (s *apiSuite) TestInstallOnNonDevModeDistro(c *check.C) {
diff --git a/daemon/export_snapshots_test.go b/daemon/export_snapshots_test.go
index 5468518a95..dec50774d3 100644
--- a/daemon/export_snapshots_test.go
+++ b/daemon/export_snapshots_test.go
@@ -20,10 +20,10 @@
package daemon
import (
+ "context"
"encoding/json"
"net/http"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"github.com/snapcore/snapd/client"
diff --git a/daemon/snap.go b/daemon/snap.go
index e9c1c86858..f7017761a7 100644
--- a/daemon/snap.go
+++ b/daemon/snap.go
@@ -39,11 +39,10 @@ import (
var errNoSnap = errors.New("snap not installed")
// snapIcon tries to find the icon inside the snap
-func snapIcon(info *snap.Info) string {
- // XXX: copy of snap.Snap.Icon which will go away
+func snapIcon(info snap.PlaceInfo) string {
found, _ := filepath.Glob(filepath.Join(info.MountDir(), "meta", "gui", "icon.*"))
if len(found) == 0 {
- return info.Media.IconURL()
+ return ""
}
return found[0]
@@ -358,7 +357,7 @@ func mapRemote(remoteSnap *snap.Info) *client.Snap {
Developer: remoteSnap.Publisher.Username,
Publisher: &publisher,
DownloadSize: remoteSnap.Size,
- Icon: snapIcon(remoteSnap),
+ Icon: remoteSnap.Media.IconURL(),
ID: remoteSnap.SnapID,
Name: remoteSnap.InstanceName(),
Revision: remoteSnap.Revision,
diff --git a/image/helpers.go b/image/helpers.go
index 921db3c58f..35f586ab21 100644
--- a/image/helpers.go
+++ b/image/helpers.go
@@ -23,6 +23,7 @@ package image
import (
"bytes"
+ "context"
"crypto"
"encoding/json"
"fmt"
@@ -34,7 +35,6 @@ import (
"syscall"
"github.com/mvo5/goconfigparser"
- "golang.org/x/net/context"
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/snapasserts"
diff --git a/image/image_test.go b/image/image_test.go
index 002aafc12a..627a341111 100644
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -21,6 +21,7 @@ package image_test
import (
"bytes"
+ "context"
"fmt"
"io/ioutil"
"net/url"
@@ -30,7 +31,6 @@ import (
"testing"
"time"
- "golang.org/x/net/context"
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/asserts"
diff --git a/interfaces/apparmor/backend_test.go b/interfaces/apparmor/backend_test.go
index 7cd9555e70..f1eac265c7 100644
--- a/interfaces/apparmor/backend_test.go
+++ b/interfaces/apparmor/backend_test.go
@@ -939,6 +939,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyNoNFS(c *C) {
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return false, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
@@ -974,6 +978,10 @@ func (s *backendSuite) testSetupSnapConfineGeneratedPolicyWithNFS(c *C, profileF
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return true, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
@@ -1031,6 +1039,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyWithNFSAndReExec(c *C)
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return true, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
@@ -1072,6 +1084,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyError1(c *C) {
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return false, fmt.Errorf("broken") })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
@@ -1108,6 +1124,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyError2(c *C) {
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return true, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
@@ -1137,6 +1157,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyError3(c *C) {
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return true, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser and make it fail.
cmd := testutil.MockCommand(c, "apparmor_parser", "echo testing; exit 1")
defer cmd.Restore()
@@ -1193,6 +1217,10 @@ func (s *backendSuite) TestSetupSnapConfineGeneratedPolicyError5(c *C) {
restore := apparmor.MockIsHomeUsingNFS(func() (bool, error) { return false, nil })
defer restore()
+ // Make it appear as if overlay was not used.
+ restore = apparmor.MockIsRootWritableOverlay(func() (string, error) { return "", nil })
+ defer restore()
+
// Intercept interaction with apparmor_parser and make it fail.
cmd := testutil.MockCommand(c, "apparmor_parser", "")
defer cmd.Restore()
diff --git a/interfaces/apparmor/template.go b/interfaces/apparmor/template.go
index ea917e8730..849c9cf965 100644
--- a/interfaces/apparmor/template.go
+++ b/interfaces/apparmor/template.go
@@ -375,6 +375,9 @@ var defaultTemplate = `
@{PROC}/net/dev r,
@{PROC}/@{pid}/net/dev r,
+ # Read-only of this snap
+ /var/lib/snapd/snaps/@{SNAP_NAME}_*.snap r,
+
# Read-only for the install directory
# bind mount used here (see 'parallel installs', above)
@{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/ r,
@@ -635,6 +638,9 @@ profile snap-update-ns.###SNAP_INSTANCE_NAME### (attach_disconnected) {
# Allow reading /proc/version. For release.go WSL detection.
@{PROC}/version r,
+ # Allow reading somaxconn, required in newer distro releases
+ @{PROC}/sys/net/core/somaxconn r,
+
# Allow reading the os-release file (possibly a symlink to /usr/lib).
/{etc/,usr/lib/}os-release r,
diff --git a/interfaces/builtin/block_devices.go b/interfaces/builtin/block_devices.go
new file mode 100644
index 0000000000..47711b33a4
--- /dev/null
+++ b/interfaces/builtin/block_devices.go
@@ -0,0 +1,98 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin
+
+// Only allow raw disk devices; not loop, ram, CDROM, generic SCSI, network,
+// tape, raid, etc devices or disk partitions
+const blockDevicesSummary = `allows access to disk block devices`
+
+const blockDevicesBaseDeclarationPlugs = `
+ block-devices:
+ allow-installation: false
+ deny-auto-connection: true
+`
+
+const blockDevicesBaseDeclarationSlots = `
+ block-devices:
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
+`
+
+// https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
+// For now, only list common devices and skip the following:
+// /dev/mfm{a,b} rw, # Acorn MFM
+// /dev/ad[a-p] rw, # ACSI
+// /dev/pd[a-d] rw, # Parallel port IDE
+// /dev/pf[0-3] rw, # Parallel port ATAPI
+// /dev/ub[a-z] rw, # USB block device
+const blockDevicesConnectedPlugAppArmor = `
+# Description: Allow write access to raw disk block devices.
+
+@{PROC}/devices r,
+/run/udev/data/b[0-9]*:[0-9]* r,
+/sys/block/ r,
+/sys/devices/**/block/** r,
+
+# Access to raw devices, not individual partitions
+/dev/hd[a-t] rw, # IDE, MFM, RLL
+/dev/sd{,[a-h]}[a-z] rw, # SCSI
+/dev/sdi[a-v] rw, # SCSI continued
+/dev/i2o/hd{,[a-c]}[a-z] rw, # I2O hard disk
+/dev/i2o/hdd[a-x] rw, # I2O hard disk continued
+/dev/mmcblk[0-9]{,[0-9],[0-9][0-9]} rw, # MMC (up to 1000 devices)
+/dev/nvme[0-9]{,[0-9]} rw, # NVMe (up to 100 devices)
+/dev/vd[a-z] rw, # virtio
+
+# SCSI device commands, et al
+capability sys_rawio,
+
+# Perform various privileged block-device ioctl operations
+capability sys_admin,
+
+# Devices for various controllers used with ioctl()
+/dev/mpt2ctl{,_wd} rw,
+/dev/megaraid_sas_ioctl_node rw,
+`
+
+var blockDevicesConnectedPlugUDev = []string{
+ `SUBSYSTEM=="block"`,
+ `KERNEL=="mpt2ctl*"`,
+ `KERNEL=="megaraid_sas_ioctl_node"`,
+}
+
+type blockDevicesInterface struct {
+ commonInterface
+}
+
+func init() {
+ registerIface(&blockDevicesInterface{commonInterface{
+ name: "block-devices",
+ summary: blockDevicesSummary,
+ implicitOnCore: true,
+ implicitOnClassic: true,
+ baseDeclarationPlugs: blockDevicesBaseDeclarationPlugs,
+ baseDeclarationSlots: blockDevicesBaseDeclarationSlots,
+ connectedPlugAppArmor: blockDevicesConnectedPlugAppArmor,
+ connectedPlugUDev: blockDevicesConnectedPlugUDev,
+ reservedForOS: true,
+ }})
+}
diff --git a/interfaces/builtin/block_devices_test.go b/interfaces/builtin/block_devices_test.go
new file mode 100644
index 0000000000..e8a9bdf74a
--- /dev/null
+++ b/interfaces/builtin/block_devices_test.go
@@ -0,0 +1,118 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin_test
+
+import (
+ . "gopkg.in/check.v1"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+ "github.com/snapcore/snapd/interfaces/builtin"
+ "github.com/snapcore/snapd/interfaces/udev"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/testutil"
+)
+
+type blockDevicesInterfaceSuite struct {
+ testutil.BaseTest
+
+ iface interfaces.Interface
+ slotInfo *snap.SlotInfo
+ slot *interfaces.ConnectedSlot
+ plugInfo *snap.PlugInfo
+ plug *interfaces.ConnectedPlug
+}
+
+var _ = Suite(&blockDevicesInterfaceSuite{
+ iface: builtin.MustInterface("block-devices"),
+})
+
+const blockDevicesConsumerYaml = `name: consumer
+version: 0
+apps:
+ app:
+ plugs: [block-devices]
+`
+
+const blockDevicesCoreYaml = `name: core
+version: 0
+type: os
+slots:
+ block-devices:
+`
+
+func (s *blockDevicesInterfaceSuite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+
+ s.plug, s.plugInfo = MockConnectedPlug(c, blockDevicesConsumerYaml, nil, "block-devices")
+ s.slot, s.slotInfo = MockConnectedSlot(c, blockDevicesCoreYaml, nil, "block-devices")
+}
+
+func (s *blockDevicesInterfaceSuite) TestName(c *C) {
+ c.Assert(s.iface.Name(), Equals, "block-devices")
+}
+
+func (s *blockDevicesInterfaceSuite) TestSanitizeSlot(c *C) {
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, s.slotInfo), IsNil)
+ slot := &snap.SlotInfo{
+ Snap: &snap.Info{SuggestedName: "some-snap"},
+ Name: "block-devices",
+ Interface: "block-devices",
+ }
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches,
+ "block-devices slots are reserved for the core snap")
+}
+
+func (s *blockDevicesInterfaceSuite) TestSanitizePlug(c *C) {
+ c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
+}
+
+func (s *blockDevicesInterfaceSuite) TestAppArmorSpec(c *C) {
+ spec := &apparmor.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `# Description: Allow write access to raw disk block devices.`)
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `/dev/sd{,[a-h]}[a-z] rw,`)
+}
+
+func (s *blockDevicesInterfaceSuite) TestUDevSpec(c *C) {
+ spec := &udev.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Snippets(), HasLen, 4)
+ c.Assert(spec.Snippets()[0], Equals, `# block-devices
+KERNEL=="megaraid_sas_ioctl_node", TAG+="snap_consumer_app"`)
+ c.Assert(spec.Snippets(), testutil.Contains, `TAG=="snap_consumer_app", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_consumer_app $devpath $major:$minor"`)
+}
+
+func (s *blockDevicesInterfaceSuite) TestStaticInfo(c *C) {
+ si := interfaces.StaticInfoOf(s.iface)
+ c.Assert(si.ImplicitOnCore, Equals, true)
+ c.Assert(si.ImplicitOnClassic, Equals, true)
+ c.Assert(si.Summary, Equals, `allows access to disk block devices`)
+ c.Assert(si.BaseDeclarationSlots, testutil.Contains, "block-devices")
+}
+
+func (s *blockDevicesInterfaceSuite) TestAutoConnect(c *C) {
+ c.Assert(s.iface.AutoConnect(s.plugInfo, s.slotInfo), Equals, true)
+}
+
+func (s *blockDevicesInterfaceSuite) TestInterfaces(c *C) {
+ c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
+}
diff --git a/interfaces/builtin/camera.go b/interfaces/builtin/camera.go
index 639bb96a30..5c44a62b30 100644
--- a/interfaces/builtin/camera.go
+++ b/interfaces/builtin/camera.go
@@ -43,6 +43,7 @@ const cameraConnectedPlugAppArmor = `
/sys/devices/pci**/usb*/**/modalias r,
/sys/devices/pci**/usb*/**/speed r,
/run/udev/data/c81:[0-9]* r, # video4linux (/dev/video*, etc)
+/run/udev/data/+usb:* r,
/sys/class/video4linux/ r,
/sys/devices/pci**/usb*/**/video4linux/** r,
`
diff --git a/interfaces/builtin/common.go b/interfaces/builtin/common.go
index aa5fe280c6..7809cff320 100644
--- a/interfaces/builtin/common.go
+++ b/interfaces/builtin/common.go
@@ -20,6 +20,7 @@
package builtin
import (
+ "io/ioutil"
"path/filepath"
"github.com/snapcore/snapd/interfaces"
@@ -34,6 +35,10 @@ import (
// applicable for testing.
var evalSymlinks = filepath.EvalSymlinks
+// readDir is either ioutil.ReadDir or a mocked function for applicable for
+// testing.
+var readDir = ioutil.ReadDir
+
type commonInterface struct {
name string
summary string
diff --git a/interfaces/builtin/common_test.go b/interfaces/builtin/common_test.go
index 0d37dcbc3b..acdbfe6ab6 100644
--- a/interfaces/builtin/common_test.go
+++ b/interfaces/builtin/common_test.go
@@ -21,6 +21,7 @@ package builtin
import (
. "gopkg.in/check.v1"
+ "os"
"github.com/snapcore/snapd/interfaces/apparmor"
"github.com/snapcore/snapd/interfaces/udev"
@@ -84,6 +85,15 @@ func MockEvalSymlinks(test *testutil.BaseTest, fn func(string) (string, error))
})
}
+// MockReadDir replaces the io/ioutil.ReadDir function used inside the caps package.
+func MockReadDir(test *testutil.BaseTest, fn func(string) ([]os.FileInfo, error)) {
+ orig := readDir
+ readDir = fn
+ test.AddCleanup(func() {
+ readDir = orig
+ })
+}
+
func (s *commonIfaceSuite) TestSuppressPtraceTrace(c *C) {
plug, _ := MockConnectedPlug(c, `
name: consumer
diff --git a/interfaces/builtin/dbus.go b/interfaces/builtin/dbus.go
index 3413840547..c3f25f4eb4 100644
--- a/interfaces/builtin/dbus.go
+++ b/interfaces/builtin/dbus.go
@@ -78,12 +78,16 @@ dbus (bind)
bus=###DBUS_BUS###
name=###DBUS_NAME###,
-# For KDE applications, also support alternation since they use org.kde.foo-PID
-# as their 'well-known' name. snapd does not allow declaring a 'well-known'
-# name that ends with '-[0-9]+', so this is ok.
+# For KDE applications and some other cases, also support alternation for:
+# - using org.kde.foo-PID as the 'well-known' name
+# - using org.foo.cmd_<num>_<num> as the 'well-known' name
+# Note, snapd does not allow declaring a 'well-known' name that ends with
+# '-[0-9]+' or that contains '_'. Parallel installs of DBus services aren't
+# supported at this time, but if they were, this could allow a parallel
+# install'swell-known name to overlap with the normal install.
dbus (bind)
bus=###DBUS_BUS###
- name=###DBUS_NAME###-[1-9]{,[0-9]}{,[0-9]}{,[0-9]}{,[0-9]}{,[0-9]},
+ name=###DBUS_NAME###{_,-}[1-9]{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9]}{,_[1-9]{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9_]}{,[0-9]}},
# Allow us to talk to dbus-daemon
dbus (receive)
diff --git a/interfaces/builtin/display_control.go b/interfaces/builtin/display_control.go
new file mode 100644
index 0000000000..0ab36eb42b
--- /dev/null
+++ b/interfaces/builtin/display_control.go
@@ -0,0 +1,137 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin
+
+import (
+ "bytes"
+ "fmt"
+ "path"
+ "path/filepath"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+)
+
+const displayControlSummary = `allows configuring display parameters`
+
+const displayControlBaseDeclarationSlots = `
+ display-control:
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
+`
+
+// gnome-settings-daemon also provides an API via setting the Brightness
+// property via a Set() method on the org.gnome.SettingsDaemon.Power.Screen
+// interface, but we can't mediate member data. This could instead be supported
+// via userd...
+const displayControlConnectedPlugAppArmor = `
+# Description: This interface allows getting information about a connected
+# display and setting parameters like backlight brightness.
+
+# keyboard backlight key
+/sys/class/leds/ r,
+/sys/devices/**/leds/**kbd_backlight/{,**} r,
+/sys/devices/**/leds/**kbd_backlight/brightness w,
+
+# upower
+#include <abstractions/dbus-strict>
+dbus (send)
+ bus=system
+ path=/org/freedesktop/UPower/KbdBacklight
+ interface=org.freedesktop.DBus.Introspectable
+ member=Introspect
+ peer=(label=unconfined),
+dbus (send)
+ bus=system
+ path=/org/freedesktop/UPower/KbdBacklight
+ interface=org.freedesktop.UPower.KbdBacklight
+ member={GetBrightness,GetMaxBrightness,SetBrightness}
+ peer=(label=unconfined),
+
+# gnome-settings-daemon
+#include <abstractions/dbus-session-strict>
+dbus (send)
+ bus=session
+ path=/org/gnome/SettingsDaemon/Power
+ interface=org.freedesktop.DBus.Introspectable
+ member=Introspect
+ peer=(label=unconfined),
+dbus (send)
+ bus=session
+ path=/org/gnome/SettingsDaemon/Power
+ interface=org.gnome.SettingsDaemon.Power.Screen
+ member=Step{Down,Up}
+ peer=(label=unconfined),
+
+/sys/class/backlight/ r,
+`
+
+type displayControlInterface struct {
+ commonInterface
+}
+
+func (iface *displayControlInterface) dereferencedBacklightPaths() []string {
+ var paths []string
+ sysClass := "/sys/class/backlight"
+ dirs, err := readDir(sysClass)
+ if err != nil {
+ return paths
+ }
+
+ for _, s := range dirs {
+ p, err := evalSymlinks(filepath.Join(sysClass, s.Name()))
+ if err != nil {
+ continue
+ }
+ paths = append(paths, filepath.Clean(p))
+ }
+ return paths
+}
+
+func (iface *displayControlInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
+ // add the static rules
+ spec.AddSnippet(displayControlConnectedPlugAppArmor)
+
+ // add the detected rules
+ for _, p := range iface.dereferencedBacklightPaths() {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "# autodetected backlight: %s\n", path.Base(p))
+ fmt.Fprintf(&buf, "%s/{,**} r,\n", p)
+ fmt.Fprintf(&buf, "%s/bl_power w,\n", p)
+ fmt.Fprintf(&buf, "%s/brightness w,\n", p)
+ spec.AddSnippet(buf.String())
+ }
+
+ return nil
+}
+
+func init() {
+ registerIface(&displayControlInterface{commonInterface{
+ name: "display-control",
+ summary: displayControlSummary,
+ implicitOnCore: true,
+ implicitOnClassic: true,
+ baseDeclarationSlots: displayControlBaseDeclarationSlots,
+ connectedPlugAppArmor: displayControlConnectedPlugAppArmor,
+ reservedForOS: true,
+ }})
+}
diff --git a/interfaces/builtin/display_control_test.go b/interfaces/builtin/display_control_test.go
new file mode 100644
index 0000000000..0178f832ce
--- /dev/null
+++ b/interfaces/builtin/display_control_test.go
@@ -0,0 +1,125 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin_test
+
+import (
+ . "gopkg.in/check.v1"
+
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+ "github.com/snapcore/snapd/interfaces/builtin"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/testutil"
+)
+
+type displayControlInterfaceSuite struct {
+ testutil.BaseTest
+
+ iface interfaces.Interface
+ slotInfo *snap.SlotInfo
+ slot *interfaces.ConnectedSlot
+ plugInfo *snap.PlugInfo
+ plug *interfaces.ConnectedPlug
+
+ tmpdir string
+}
+
+var _ = Suite(&displayControlInterfaceSuite{
+ iface: builtin.MustInterface("display-control"),
+})
+
+const displayControlConsumerYaml = `name: consumer
+version: 0
+apps:
+ app:
+ plugs: [display-control]
+`
+
+const displayControlCoreYaml = `name: core
+version: 0
+type: os
+slots:
+ display-control:
+`
+
+func (s *displayControlInterfaceSuite) SetUpTest(c *C) {
+ s.plug, s.plugInfo = MockConnectedPlug(c, displayControlConsumerYaml, nil, "display-control")
+ s.slot, s.slotInfo = MockConnectedSlot(c, displayControlCoreYaml, nil, "display-control")
+
+ s.tmpdir = c.MkDir()
+}
+
+func (s *displayControlInterfaceSuite) TestName(c *C) {
+ c.Assert(s.iface.Name(), Equals, "display-control")
+}
+
+func (s *displayControlInterfaceSuite) TestSanitizeSlot(c *C) {
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, s.slotInfo), IsNil)
+ slot := &snap.SlotInfo{
+ Snap: &snap.Info{SuggestedName: "some-snap"},
+ Name: "display-control",
+ Interface: "display-control",
+ }
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches,
+ "display-control slots are reserved for the core snap")
+}
+
+func (s *displayControlInterfaceSuite) TestSanitizePlug(c *C) {
+ c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
+}
+
+func (s *displayControlInterfaceSuite) TestAppArmorSpec(c *C) {
+ c.Assert(os.MkdirAll(filepath.Join(s.tmpdir, "foo_backlight"), 0755), IsNil)
+ c.Assert(os.MkdirAll(filepath.Join(s.tmpdir, "bar_backlight"), 0755), IsNil)
+ builtin.MockReadDir(&s.BaseTest, func(path string) ([]os.FileInfo, error) {
+ return ioutil.ReadDir(s.tmpdir)
+ })
+ builtin.MockEvalSymlinks(&s.BaseTest, func(path string) (string, error) {
+ return "(dereferenced)" + path, nil
+ })
+ spec := &apparmor.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/sys/class/backlight/ r,\n")
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "autodetected backlight: bar_backlight\n")
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "(dereferenced)/sys/class/backlight/bar_backlight/{,**} r,\n")
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "autodetected backlight: foo_backlight\n")
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "(dereferenced)/sys/class/backlight/foo_backlight/{,**} r,\n")
+}
+
+func (s *displayControlInterfaceSuite) TestStaticInfo(c *C) {
+ si := interfaces.StaticInfoOf(s.iface)
+ c.Assert(si.ImplicitOnCore, Equals, true)
+ c.Assert(si.ImplicitOnClassic, Equals, true)
+ c.Assert(si.Summary, Equals, `allows configuring display parameters`)
+ c.Assert(si.BaseDeclarationSlots, testutil.Contains, "display-control")
+}
+
+func (s *displayControlInterfaceSuite) TestAutoConnect(c *C) {
+ c.Assert(s.iface.AutoConnect(s.plugInfo, s.slotInfo), Equals, true)
+}
+
+func (s *displayControlInterfaceSuite) TestInterfaces(c *C) {
+ c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
+}
diff --git a/interfaces/builtin/export_test.go b/interfaces/builtin/export_test.go
index fbe7410e9a..2315e924be 100644
--- a/interfaces/builtin/export_test.go
+++ b/interfaces/builtin/export_test.go
@@ -102,3 +102,13 @@ func MockOsGetenv(mock func(string) string) (restore func()) {
return restore
}
+
+func MockProcCpuinfo(filename string) (restore func()) {
+ old := procCpuinfo
+ restore = func() {
+ procCpuinfo = old
+ }
+ procCpuinfo = filename
+
+ return restore
+}
diff --git a/interfaces/builtin/hardware_observe.go b/interfaces/builtin/hardware_observe.go
index 12a376263f..6b281306b7 100644
--- a/interfaces/builtin/hardware_observe.go
+++ b/interfaces/builtin/hardware_observe.go
@@ -57,6 +57,7 @@ capability sys_admin,
# power information
/sys/power/{,**} r,
+/run/udev/data/+power_supply:* r,
# interrupts
@{PROC}/interrupts r,
diff --git a/interfaces/builtin/home.go b/interfaces/builtin/home.go
index a9cfd1f74f..d95493ae75 100644
--- a/interfaces/builtin/home.go
+++ b/interfaces/builtin/home.go
@@ -82,6 +82,7 @@ audit deny @{HOME}/bin/{,**} wl,
const homeConnectedPlugAppArmorWithAllRead = `
# Allow non-owner read to non-hidden and non-snap files and directories
+capability dac_read_search,
@{HOME}/ r,
@{HOME}/[^s.]** r,
@{HOME}/s[^n]** r,
diff --git a/interfaces/builtin/kvm.go b/interfaces/builtin/kvm.go
index cb254c5c7c..157d033ea6 100644
--- a/interfaces/builtin/kvm.go
+++ b/interfaces/builtin/kvm.go
@@ -1,7 +1,7 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
- * Copyright (C) 2017 Canonical Ltd
+ * Copyright (C) 2017-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
@@ -19,6 +19,18 @@
package builtin
+import (
+ "fmt"
+ "io/ioutil"
+ "regexp"
+ "strings"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/kmod"
+ "github.com/snapcore/snapd/logger"
+ "github.com/snapcore/snapd/strutil"
+)
+
const kvmSummary = `allows access to the kvm device`
const kvmBaseDeclarationSlots = `
@@ -38,8 +50,58 @@ const kvmConnectedPlugAppArmor = `
var kvmConnectedPlugUDev = []string{`KERNEL=="kvm"`}
+type kvmInterface struct {
+ commonInterface
+}
+
+var procCpuinfo = "/proc/cpuinfo"
+var flagsMatcher = regexp.MustCompile(`(?m)^flags\s+:\s+(.*)$`).FindSubmatch
+
+func getCpuFlags() (flags []string, err error) {
+ buf, err := ioutil.ReadFile(procCpuinfo)
+ if err != nil {
+ // if we can't read cpuinfo, we want to know _why_
+ return nil, fmt.Errorf("unable to read %v: %v", procCpuinfo, err)
+ }
+
+ // want to capture the text after 'flags:' entry
+ match := flagsMatcher(buf)
+ if len(match) == 0 {
+ return nil, fmt.Errorf("%v does not contain a 'flags:' entry", procCpuinfo)
+ }
+
+ // match[0] has whole matching line, match[1] must exist as it has the captured text after 'flags:'
+ cpu_flags := strings.Fields(string(match[1]))
+ return cpu_flags, nil
+}
+
+func (iface *kvmInterface) KModConnectedPlug(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
+ // Check CPU capabilities to load suitable module
+ // NOTE: this only considers i386, x86_64 and amd64 CPUs, but some ARM, PPC and S390 CPUs also support KVM
+ m := "kvm"
+ cpu_flags, err := getCpuFlags()
+ if err != nil {
+ logger.Debugf("kvm: fetching cpu info failed: %v", err)
+ }
+
+ if strutil.ListContains(cpu_flags, "vmx") {
+ m = "kvm_intel"
+ } else if strutil.ListContains(cpu_flags, "svm") {
+ m = "kvm_amd"
+ } else {
+ // CPU appears not to support KVM extensions, fall back to bare kvm module as it appears
+ // sufficient for some architectures
+ logger.Noticef("kvm: failed to detect CPU specific KVM support, will attempt to modprobe generic KVM support")
+ }
+
+ if err := spec.AddModule(m); err != nil {
+ return nil
+ }
+ return nil
+}
+
func init() {
- registerIface(&commonInterface{
+ registerIface(&kvmInterface{commonInterface{
name: "kvm",
summary: kvmSummary,
implicitOnCore: true,
@@ -48,5 +110,5 @@ func init() {
connectedPlugAppArmor: kvmConnectedPlugAppArmor,
connectedPlugUDev: kvmConnectedPlugUDev,
reservedForOS: true,
- })
+ }})
}
diff --git a/interfaces/builtin/kvm_test.go b/interfaces/builtin/kvm_test.go
index ceeb171159..46410976c9 100644
--- a/interfaces/builtin/kvm_test.go
+++ b/interfaces/builtin/kvm_test.go
@@ -1,7 +1,7 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
- * Copyright (C) 2017 Canonical Ltd
+ * Copyright (C) 2017-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
@@ -20,22 +20,31 @@
package builtin_test
import (
+ "io/ioutil"
+ "path/filepath"
+
. "gopkg.in/check.v1"
+ "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/interfaces/apparmor"
"github.com/snapcore/snapd/interfaces/builtin"
+ "github.com/snapcore/snapd/interfaces/kmod"
"github.com/snapcore/snapd/interfaces/udev"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/testutil"
)
type kvmInterfaceSuite struct {
+ testutil.BaseTest
+
iface interfaces.Interface
slotInfo *snap.SlotInfo
slot *interfaces.ConnectedSlot
plugInfo *snap.PlugInfo
plug *interfaces.ConnectedPlug
+
+ tmpdir string
}
var _ = Suite(&kvmInterfaceSuite{
@@ -57,8 +66,25 @@ slots:
`
func (s *kvmInterfaceSuite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+
s.plug, s.plugInfo = MockConnectedPlug(c, kvmConsumerYaml, nil, "kvm")
s.slot, s.slotInfo = MockConnectedSlot(c, kvmCoreYaml, nil, "kvm")
+
+ // Need to Mock output of /proc/cpuinfo
+ s.tmpdir = c.MkDir()
+ dirs.SetRootDir(s.tmpdir)
+ s.AddCleanup(func() { dirs.SetRootDir("/") })
+
+ mockCpuinfo := filepath.Join(s.tmpdir, "cpuinfo")
+ c.Assert(ioutil.WriteFile(mockCpuinfo, []byte(`
+processor : 0
+flags : cpuflags without kvm support
+
+processor : 42
+flags : another cpu also without kvm support
+`[1:]), 0644), IsNil)
+ s.AddCleanup(builtin.MockProcCpuinfo(mockCpuinfo))
}
func (s *kvmInterfaceSuite) TestName(c *C) {
@@ -116,3 +142,67 @@ func (s *kvmInterfaceSuite) TestAutoConnect(c *C) {
func (s *kvmInterfaceSuite) TestInterfaces(c *C) {
c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
}
+
+func (s *kvmInterfaceSuite) TestKModSpecWithUnknownCpu(c *C) {
+ spec := &kmod.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Modules(), DeepEquals, map[string]bool{
+ "kvm": true,
+ })
+}
+
+func (s *kvmInterfaceSuite) TestKModSpecWithIntel(c *C) {
+ mockCpuinfo := filepath.Join(s.tmpdir, "cpuinfo")
+ c.Assert(ioutil.WriteFile(mockCpuinfo, []byte(`
+processor : 0
+flags : stuff vmx other
+`[1:]), 0644), IsNil)
+
+ spec := &kmod.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Modules(), DeepEquals, map[string]bool{
+ "kvm_intel": true,
+ })
+}
+
+func (s *kvmInterfaceSuite) TestKModSpecWithAMD(c *C) {
+ mockCpuinfo := filepath.Join(s.tmpdir, "cpuinfo")
+ c.Assert(ioutil.WriteFile(mockCpuinfo, []byte(`
+processor : 0
+flags : stuff svm other
+`[1:]), 0644), IsNil)
+
+ s.AddCleanup(builtin.MockProcCpuinfo(mockCpuinfo))
+
+ spec := &kmod.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Modules(), DeepEquals, map[string]bool{
+ "kvm_amd": true,
+ })
+}
+
+func (s *kvmInterfaceSuite) TestKModSpecWithEmptyCpuinfo(c *C) {
+ mockCpuinfo := filepath.Join(s.tmpdir, "cpuinfo")
+ c.Assert(ioutil.WriteFile(mockCpuinfo, []byte(`
+`[1:]), 0644), IsNil)
+
+ s.AddCleanup(builtin.MockProcCpuinfo(mockCpuinfo))
+
+ spec := &kmod.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Modules(), DeepEquals, map[string]bool{
+ "kvm": true,
+ })
+}
+
+func (s *kvmInterfaceSuite) TestKModSpecWithMissingCpuinfo(c *C) {
+ mockCpuinfo := filepath.Join(s.tmpdir, "non-existent-cpuinfo")
+
+ s.AddCleanup(builtin.MockProcCpuinfo(mockCpuinfo))
+
+ spec := &kmod.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Modules(), DeepEquals, map[string]bool{
+ "kvm": true,
+ })
+}
diff --git a/interfaces/builtin/pulseaudio.go b/interfaces/builtin/pulseaudio.go
index b4dc10175d..ab14aada92 100644
--- a/interfaces/builtin/pulseaudio.go
+++ b/interfaces/builtin/pulseaudio.go
@@ -59,7 +59,7 @@ const pulseaudioConnectedPlugAppArmorDesktop = `
# to read available client side configuration settings. On an Ubuntu Core
# device those things will be stored inside the snap directory.
/etc/pulse/ r,
-/etc/pulse/* r,
+/etc/pulse/** r,
owner @{HOME}/.pulse-cookie rk,
owner @{HOME}/.config/pulse/cookie rk,
owner /{,var/}run/user/*/pulse/ rwk,
diff --git a/interfaces/builtin/system_observe.go b/interfaces/builtin/system_observe.go
index e60850b273..4d97b74aa6 100644
--- a/interfaces/builtin/system_observe.go
+++ b/interfaces/builtin/system_observe.go
@@ -42,6 +42,7 @@ const systemObserveConnectedPlugAppArmor = `
ptrace (read),
# Other miscellaneous accesses for observing the system
+@{PROC}/locks r,
@{PROC}/modules r,
@{PROC}/stat r,
@{PROC}/vmstat r,
diff --git a/interfaces/builtin/u2f_devices.go b/interfaces/builtin/u2f_devices.go
new file mode 100644
index 0000000000..681d32f8ec
--- /dev/null
+++ b/interfaces/builtin/u2f_devices.go
@@ -0,0 +1,149 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin
+
+import (
+ "fmt"
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/udev"
+)
+
+const u2fDevicesSummary = `allows access to u2f devices`
+
+const u2fDevicesBaseDeclarationSlots = `
+ u2f-devices:
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
+`
+
+type u2fDevice struct {
+ Name, VendorIDPattern, ProductIDPattern string
+}
+
+// https://github.com/Yubico/libu2f-host/blob/master/70-u2f.rules
+var u2fDevices = []u2fDevice{
+ {
+ Name: "Yubico YubiKey",
+ VendorIDPattern: "1050",
+ ProductIDPattern: "0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410",
+ },
+ {
+ Name: "Happlink (formerly Plug-Up) Security KEY",
+ VendorIDPattern: "2581",
+ ProductIDPattern: "f1d0",
+ },
+ {
+ Name: "Neowave Keydo and Keydo AES",
+ VendorIDPattern: "1e0d",
+ ProductIDPattern: "f1d0|f1ae",
+ },
+ {
+ Name: "HyperSecu HyperFIDO",
+ VendorIDPattern: "096e|2ccf",
+ ProductIDPattern: "0880",
+ },
+ {
+ Name: "Feitian ePass FIDO, BioPass FIDO2",
+ VendorIDPattern: "096e",
+ ProductIDPattern: "0850|0852|0853|0854|0856|0858|085a|085b|085d",
+ },
+ {
+ Name: "JaCarta U2F",
+ VendorIDPattern: "24dc",
+ ProductIDPattern: "0101",
+ },
+ {
+ Name: "U2F Zero",
+ VendorIDPattern: "10c4",
+ ProductIDPattern: "8acf",
+ },
+ {
+ Name: "VASCO SeccureClick",
+ VendorIDPattern: "1a44",
+ ProductIDPattern: "00bb",
+ },
+ {
+ Name: "Bluink Key",
+ VendorIDPattern: "2abe",
+ ProductIDPattern: "1002",
+ },
+ {
+ Name: "Thetis Key",
+ VendorIDPattern: "1ea8",
+ ProductIDPattern: "f025",
+ },
+ {
+ Name: "Nitrokey FIDO U2F",
+ VendorIDPattern: "20a0",
+ ProductIDPattern: "4287",
+ },
+ {
+ Name: "Google Titan U2F",
+ VendorIDPattern: "18d1",
+ ProductIDPattern: "5026",
+ },
+ {
+ Name: "Tomu board + chopstx U2F",
+ VendorIDPattern: "0483",
+ ProductIDPattern: "cdab",
+ },
+}
+
+const u2fDevicesConnectedPlugAppArmor = `
+# Description: Allow write access to u2f hidraw devices.
+
+# Use a glob rule and rely on device cgroup for mediation.
+/dev/hidraw* rw,
+
+# char 234-254 are used for dynamic assignment, which u2f devices are
+/run/udev/data/c23[4-9]:* r,
+/run/udev/data/c24[0-9]:* r,
+/run/udev/data/c25[0-4]:* r,
+
+# misc required accesses
+/run/udev/data/+power_supply:hid* r,
+/run/udev/data/c14:[0-9]* r,
+/sys/devices/**/usb*/**/report_descriptor r,
+`
+
+type u2fDevicesInterface struct {
+ commonInterface
+}
+
+func (iface *u2fDevicesInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
+ for _, d := range u2fDevices {
+ spec.TagDevice(fmt.Sprintf("# %s\nSUBSYSTEM==\"hidraw\", KERNEL==\"hidraw*\", ATTRS{idVendor}==\"%s\", ATTRS{idProduct}==\"%s\"", d.Name, d.VendorIDPattern, d.ProductIDPattern))
+ }
+ return nil
+}
+
+func init() {
+ registerIface(&u2fDevicesInterface{commonInterface{
+ name: "u2f-devices",
+ summary: u2fDevicesSummary,
+ implicitOnCore: true,
+ implicitOnClassic: true,
+ baseDeclarationSlots: u2fDevicesBaseDeclarationSlots,
+ connectedPlugAppArmor: u2fDevicesConnectedPlugAppArmor,
+ reservedForOS: true,
+ }})
+}
diff --git a/interfaces/builtin/u2f_devices_test.go b/interfaces/builtin/u2f_devices_test.go
new file mode 100644
index 0000000000..22fe06bec6
--- /dev/null
+++ b/interfaces/builtin/u2f_devices_test.go
@@ -0,0 +1,116 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * 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/>.
+ *
+ */
+
+package builtin_test
+
+import (
+ . "gopkg.in/check.v1"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+ "github.com/snapcore/snapd/interfaces/builtin"
+ "github.com/snapcore/snapd/interfaces/udev"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/testutil"
+)
+
+type u2fDevicesInterfaceSuite struct {
+ testutil.BaseTest
+
+ iface interfaces.Interface
+ slotInfo *snap.SlotInfo
+ slot *interfaces.ConnectedSlot
+ plugInfo *snap.PlugInfo
+ plug *interfaces.ConnectedPlug
+}
+
+var _ = Suite(&u2fDevicesInterfaceSuite{
+ iface: builtin.MustInterface("u2f-devices"),
+})
+
+const u2fDevicesConsumerYaml = `name: consumer
+version: 0
+apps:
+ app:
+ plugs: [u2f-devices]
+`
+
+const u2fDevicesCoreYaml = `name: core
+version: 0
+type: os
+slots:
+ u2f-devices:
+`
+
+func (s *u2fDevicesInterfaceSuite) SetUpTest(c *C) {
+ s.plug, s.plugInfo = MockConnectedPlug(c, u2fDevicesConsumerYaml, nil, "u2f-devices")
+ s.slot, s.slotInfo = MockConnectedSlot(c, u2fDevicesCoreYaml, nil, "u2f-devices")
+}
+
+func (s *u2fDevicesInterfaceSuite) TestName(c *C) {
+ c.Assert(s.iface.Name(), Equals, "u2f-devices")
+}
+
+func (s *u2fDevicesInterfaceSuite) TestSanitizeSlot(c *C) {
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, s.slotInfo), IsNil)
+ slot := &snap.SlotInfo{
+ Snap: &snap.Info{SuggestedName: "some-snap"},
+ Name: "u2f-devices",
+ Interface: "u2f-devices",
+ }
+ c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches, "u2f-devices slots are reserved for the core snap")
+}
+
+func (s *u2fDevicesInterfaceSuite) TestSanitizePlug(c *C) {
+ c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
+}
+
+func (s *u2fDevicesInterfaceSuite) TestAppArmorSpec(c *C) {
+ spec := &apparmor.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `# Description: Allow write access to u2f hidraw devices.`)
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `/dev/hidraw* rw,`)
+}
+
+func (s *u2fDevicesInterfaceSuite) TestUDevSpec(c *C) {
+ spec := &udev.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
+ c.Assert(spec.Snippets(), HasLen, 14)
+ c.Assert(spec.Snippets(), testutil.Contains, `# u2f-devices
+# Yubico YubiKey
+SUBSYSTEM=="hidraw", KERNEL=="hidraw*", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410", TAG+="snap_consumer_app"`)
+ c.Assert(spec.Snippets(), testutil.Contains, `TAG=="snap_consumer_app", RUN+="/usr/lib/snapd/snap-device-helper $env{ACTION} snap_consumer_app $devpath $major:$minor"`)
+}
+
+func (s *u2fDevicesInterfaceSuite) TestStaticInfo(c *C) {
+ si := interfaces.StaticInfoOf(s.iface)
+ c.Assert(si.ImplicitOnCore, Equals, true)
+ c.Assert(si.ImplicitOnClassic, Equals, true)
+ c.Assert(si.Summary, Equals, `allows access to u2f devices`)
+ c.Assert(si.BaseDeclarationSlots, testutil.Contains, "u2f-devices")
+}
+
+func (s *u2fDevicesInterfaceSuite) TestAutoConnect(c *C) {
+ c.Assert(s.iface.AutoConnect(s.plugInfo, s.slotInfo), Equals, true)
+}
+
+func (s *u2fDevicesInterfaceSuite) TestInterfaces(c *C) {
+ c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
+}
diff --git a/interfaces/core.go b/interfaces/core.go
index fe64b814d6..7de4616e32 100644
--- a/interfaces/core.go
+++ b/interfaces/core.go
@@ -51,6 +51,14 @@ func (ref PlugRef) String() string {
return fmt.Sprintf("%s:%s", ref.Snap, ref.Name)
}
+// SortsBefore returns true when plug should be sorted before the other
+func (ref PlugRef) SortsBefore(other PlugRef) bool {
+ if ref.Snap != other.Snap {
+ return ref.Snap < other.Snap
+ }
+ return ref.Name < other.Name
+}
+
// Sanitize slot with a given snapd interface.
func BeforePrepareSlot(iface Interface, slotInfo *snap.SlotInfo) error {
if iface.Name() != slotInfo.Interface {
@@ -75,6 +83,14 @@ func (ref SlotRef) String() string {
return fmt.Sprintf("%s:%s", ref.Snap, ref.Name)
}
+// SortsBefore returns true when slot should be sorted before the other
+func (ref SlotRef) SortsBefore(other SlotRef) bool {
+ if ref.Snap != other.Snap {
+ return ref.Snap < other.Snap
+ }
+ return ref.Name < other.Name
+}
+
// Interfaces holds information about a list of plugs, slots and their connections.
type Interfaces struct {
Plugs []*snap.PlugInfo
@@ -110,6 +126,14 @@ func (conn *ConnRef) ID() string {
return fmt.Sprintf("%s:%s %s:%s", conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name)
}
+// SortsBefore returns true when connection should be sorted before the other
+func (conn *ConnRef) SortsBefore(other *ConnRef) bool {
+ if conn.PlugRef != other.PlugRef {
+ return conn.PlugRef.SortsBefore(other.PlugRef)
+ }
+ return conn.SlotRef.SortsBefore(other.SlotRef)
+}
+
// ParseConnRef parses an ID string
func ParseConnRef(id string) (*ConnRef, error) {
var conn ConnRef
diff --git a/interfaces/policy/basedeclaration_test.go b/interfaces/policy/basedeclaration_test.go
index d49a7f1e0b..f17be4dd1a 100644
--- a/interfaces/policy/basedeclaration_test.go
+++ b/interfaces/policy/basedeclaration_test.go
@@ -471,6 +471,24 @@ plugs:
c.Check(err, IsNil)
}
+func (s *baseDeclSuite) TestAutoConnectionBlockDevicesOverride(c *C) {
+ cand := s.connectCand(c, "block-devices", "", "")
+ err := cand.CheckAutoConnect()
+ c.Check(err, NotNil)
+ c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"block-devices\"")
+
+ plugsSlots := `
+plugs:
+ block-devices:
+ allow-auto-connection: true
+`
+
+ snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
+ cand.PlugSnapDeclaration = snapDecl
+ err = cand.CheckAutoConnect()
+ c.Check(err, IsNil)
+}
+
func (s *baseDeclSuite) TestAutoConnectionOverrideMultiple(c *C) {
plugsSlots := `
plugs:
@@ -640,6 +658,7 @@ func (s *baseDeclSuite) TestPlugInstallation(c *C) {
all := builtin.Interfaces()
restricted := map[string]bool{
+ "block-devices": true,
"classic-support": true,
"docker-support": true,
"greengrass-support": true,
@@ -766,6 +785,7 @@ func (s *baseDeclSuite) TestSanity(c *C) {
// given how the rules work this can be delicate,
// listed here to make sure that was a conscious decision
bothSides := map[string]bool{
+ "block-devices": true,
"classic-support": true,
"core-support": true,
"docker-support": true,
diff --git a/interfaces/seccomp/template.go b/interfaces/seccomp/template.go
index 7f4b470f64..cce88f0e12 100644
--- a/interfaces/seccomp/template.go
+++ b/interfaces/seccomp/template.go
@@ -337,6 +337,8 @@ _newselect
pselect
pselect6
+# Allow use of SysV semaphores. Note that allocated resources are not freed by
+# OOM which can lead to global kernel resource leakage.
semctl
semget
semop
diff --git a/interfaces/sorting.go b/interfaces/sorting.go
index f5bb3a345d..b836117ee8 100644
--- a/interfaces/sorting.go
+++ b/interfaces/sorting.go
@@ -30,16 +30,7 @@ type byConnRef []*ConnRef
func (c byConnRef) Len() int { return len(c) }
func (c byConnRef) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c byConnRef) Less(i, j int) bool {
- if c[i].PlugRef.Snap != c[j].PlugRef.Snap {
- return c[i].PlugRef.Snap < c[j].PlugRef.Snap
- }
- if c[i].PlugRef.Name != c[j].PlugRef.Name {
- return c[i].PlugRef.Name < c[j].PlugRef.Name
- }
- if c[i].SlotRef.Snap != c[j].SlotRef.Snap {
- return c[i].SlotRef.Snap < c[j].SlotRef.Snap
- }
- return c[i].SlotRef.Name < c[j].SlotRef.Name
+ return c[i].SortsBefore(c[j])
}
type byPlugSnapAndName []*snap.PlugInfo
diff --git a/interfaces/sorting_test.go b/interfaces/sorting_test.go
index f396fa3f78..afd7d682e4 100644
--- a/interfaces/sorting_test.go
+++ b/interfaces/sorting_test.go
@@ -70,3 +70,65 @@ func (s *SortingSuite) TestByConnRef(c *C) {
newConnRef("name-1_instance", "plug-1", "name-2", "slot-1"),
})
}
+
+func newSlotRef(snap, name string) *interfaces.SlotRef {
+ return &interfaces.SlotRef{Snap: snap, Name: name}
+}
+
+type bySlotRef []*interfaces.SlotRef
+
+func (b bySlotRef) Len() int { return len(b) }
+func (b bySlotRef) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b bySlotRef) Less(i, j int) bool {
+ return b[i].SortsBefore(*b[j])
+}
+
+func (s *SortingSuite) TestSortSlotRef(c *C) {
+ list := []*interfaces.SlotRef{
+ newSlotRef("name-2", "slot-3"),
+ newSlotRef("name-2_instance", "slot-1"),
+ newSlotRef("name-2", "slot-2"),
+ newSlotRef("name-2", "slot-4"),
+ newSlotRef("name-2", "slot-1"),
+ }
+ sort.Sort(bySlotRef(list))
+
+ c.Assert(list, DeepEquals, []*interfaces.SlotRef{
+ newSlotRef("name-2", "slot-1"),
+ newSlotRef("name-2", "slot-2"),
+ newSlotRef("name-2", "slot-3"),
+ newSlotRef("name-2", "slot-4"),
+ newSlotRef("name-2_instance", "slot-1"),
+ })
+}
+
+type byPlugRef []*interfaces.PlugRef
+
+func (b byPlugRef) Len() int { return len(b) }
+func (b byPlugRef) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byPlugRef) Less(i, j int) bool {
+ return b[i].SortsBefore(*b[j])
+}
+
+func newPlugRef(snap, name string) *interfaces.PlugRef {
+ return &interfaces.PlugRef{Snap: snap, Name: name}
+}
+
+func (s *SortingSuite) TestSortPlugRef(c *C) {
+ list := []*interfaces.PlugRef{
+ newPlugRef("name-2", "plug-3"),
+ newPlugRef("name-2_instance", "plug-1"),
+ newPlugRef("name-2", "plug-4"),
+ newPlugRef("name-2", "plug-2"),
+ newPlugRef("name-2", "plug-1"),
+ }
+ sort.Sort(byPlugRef(list))
+
+ c.Assert(list, DeepEquals, []*interfaces.PlugRef{
+ newPlugRef("name-2", "plug-1"),
+ newPlugRef("name-2", "plug-2"),
+ newPlugRef("name-2", "plug-3"),
+ newPlugRef("name-2", "plug-4"),
+ newPlugRef("name-2_instance", "plug-1"),
+ })
+}
diff --git a/osutil/context.go b/osutil/context.go
index 0d59cf3152..0c5fb26476 100644
--- a/osutil/context.go
+++ b/osutil/context.go
@@ -20,12 +20,11 @@
package osutil
import (
+ "context"
"io"
"os/exec"
"sync/atomic"
"syscall"
-
- "golang.org/x/net/context"
)
// ContextWriter returns a discarding io.Writer which Write method
diff --git a/osutil/context_test.go b/osutil/context_test.go
index 52ecbcd42b..92ee31d6a5 100644
--- a/osutil/context_test.go
+++ b/osutil/context_test.go
@@ -20,13 +20,13 @@
package osutil_test
import (
+ "context"
"io"
"os/exec"
"strings"
"testing"
"time"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"github.com/snapcore/snapd/osutil"
@@ -43,7 +43,8 @@ func (dumbReader) Read([]byte) (int, error) {
var _ = check.Suite(&ctxSuite{})
func (ctxSuite) TestWriter(c *check.C) {
- ctx, _ := context.WithTimeout(context.Background(), time.Second/100)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second/100)
+ defer cancel()
n, err := io.Copy(osutil.ContextWriter(ctx), dumbReader{})
c.Assert(err, check.Equals, context.DeadlineExceeded)
// but we copied things until the deadline hit
@@ -60,7 +61,8 @@ func (ctxSuite) TestWriterDone(c *check.C) {
}
func (ctxSuite) TestWriterSuccess(c *check.C) {
- ctx, _ := context.WithTimeout(context.Background(), time.Second/100)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second/100)
+ defer cancel()
// check we can copy if we're quick
n, err := io.Copy(osutil.ContextWriter(ctx), strings.NewReader("hello"))
c.Check(err, check.IsNil)
@@ -68,7 +70,8 @@ func (ctxSuite) TestWriterSuccess(c *check.C) {
}
func (ctxSuite) TestRun(c *check.C) {
- ctx, _ := context.WithTimeout(context.Background(), time.Second/100)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second/100)
+ defer cancel()
cmd := exec.Command("/bin/sleep", "1")
err := osutil.RunWithContext(ctx, cmd)
c.Check(err, check.Equals, context.DeadlineExceeded)
@@ -94,8 +97,9 @@ func (ctxSuite) TestRunRace(c *check.C) {
nfailed := 0
for nfailed == 0 || nkilled == 0 {
cmd := exec.Command("/bin/false")
- ctx, _ := context.WithTimeout(context.Background(), dt)
+ ctx, cancel := context.WithTimeout(context.Background(), dt)
err := osutil.RunWithContext(ctx, cmd)
+ cancel()
switch err.Error() {
case killedstr:
nkilled++
@@ -118,14 +122,16 @@ func (ctxSuite) TestRunDone(c *check.C) {
}
func (ctxSuite) TestRunSuccess(c *check.C) {
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
cmd := exec.Command("/bin/sleep", "0.01")
err := osutil.RunWithContext(ctx, cmd)
c.Check(err, check.IsNil)
}
func (ctxSuite) TestRunSuccessfulFailure(c *check.C) {
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
cmd := exec.Command("not/something/you/can/run")
err := osutil.RunWithContext(ctx, cmd)
c.Check(err, check.ErrorMatches, `fork/exec \S+: no such file or directory`)
diff --git a/overlord/auth/auth.go b/overlord/auth/auth.go
index e33b6c9e29..b747b92596 100644
--- a/overlord/auth/auth.go
+++ b/overlord/auth/auth.go
@@ -20,6 +20,7 @@
package auth
import (
+ "context"
"crypto/rand"
"encoding/base64"
"errors"
@@ -29,7 +30,6 @@ import (
"sort"
"strconv"
- "golang.org/x/net/context"
"gopkg.in/macaroon.v1"
"github.com/snapcore/snapd/asserts"
diff --git a/overlord/auth/auth_test.go b/overlord/auth/auth_test.go
index 4357f73c80..3248f61858 100644
--- a/overlord/auth/auth_test.go
+++ b/overlord/auth/auth_test.go
@@ -20,14 +20,13 @@
package auth_test
import (
+ "context"
"net/url"
"os"
"strings"
"testing"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"gopkg.in/macaroon.v1"
diff --git a/overlord/hookstate/ctlcmd/services_test.go b/overlord/hookstate/ctlcmd/services_test.go
index 88c17e49f9..5da02f6de7 100644
--- a/overlord/hookstate/ctlcmd/services_test.go
+++ b/overlord/hookstate/ctlcmd/services_test.go
@@ -20,11 +20,10 @@
package ctlcmd_test
import (
+ "context"
"fmt"
"sort"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/asserts"
diff --git a/overlord/ifacestate/ifacestate_test.go b/overlord/ifacestate/ifacestate_test.go
index 5bcd86a1d4..89bb7d4516 100644
--- a/overlord/ifacestate/ifacestate_test.go
+++ b/overlord/ifacestate/ifacestate_test.go
@@ -5230,6 +5230,74 @@ func (s *interfaceManagerSuite) TestHotplugAutoconnectConflictRetry(c *C) {
c.Check(hotplugConnectTask.Log()[0], Matches, `.*hotplug connect will be retried: conflicting snap core with task "link-snap"`)
}
+// mockConsumer mocks a consumer snap and its single plug in the repository
+func mockConsumer(c *C, st *state.State, repo *interfaces.Repository, snapYaml, consumerSnapName, plugName string) {
+ si := &snap.SideInfo{RealName: consumerSnapName, Revision: snap.R(1)}
+ consumer := snaptest.MockSnapInstance(c, "", snapYaml, si)
+ c.Assert(consumer.Plugs[plugName], NotNil)
+ c.Assert(repo.AddPlug(consumer.Plugs[plugName]), IsNil)
+ snapstate.Set(st, consumerSnapName, &snapstate.SnapState{
+ Active: true,
+ Sequence: []*snap.SideInfo{si},
+ Current: snap.R(1),
+ SnapType: "app",
+ })
+}
+
+func (s *interfaceManagerSuite) TestHotplugConnectAndAutoconnect(c *C) {
+ s.MockModel(c, nil)
+
+ coreInfo := s.mockSnap(c, coreSnapYaml)
+ repo := s.manager(c).Repository()
+ c.Assert(repo.AddInterface(&ifacetest.TestInterface{InterfaceName: "test"}), IsNil)
+
+ // mock hotplug slot in the repo and state
+ c.Assert(repo.AddSlot(&snap.SlotInfo{Snap: coreInfo, Name: "hotplugslot", Interface: "test", HotplugKey: "1234"}), IsNil)
+
+ s.state.Lock()
+ s.state.Set("hotplug-slots", map[string]interface{}{
+ "hotplugslot": map[string]interface{}{"name": "hotplugslot", "interface": "test", "hotplug-key": "1234"},
+ })
+
+ mockConsumer(c, s.state, repo, consumerYaml, "consumer", "plug")
+ mockConsumer(c, s.state, repo, consumer2Yaml, "consumer2", "plug")
+
+ chg := s.state.NewChange("hotplug change", "")
+ t := s.state.NewTask("hotplug-connect", "")
+ ifacestate.SetHotplugAttrs(t, "test", "1234")
+ chg.AddTask(t)
+
+ // simulate a device that was known and connected before to only one consumer, this connection will be restored
+ s.state.Set("conns", map[string]interface{}{
+ "consumer:plug core:hotplugslot": map[string]interface{}{
+ "interface": "test",
+ "hotplug-key": "1234",
+ "hotplug-gone": true,
+ }})
+
+ s.state.Unlock()
+ s.settle(c)
+ s.state.Lock()
+
+ c.Assert(chg.Err(), IsNil)
+
+ // two connections now present (restored one for consumer, and new one for consumer2)
+ var conns map[string]interface{}
+ c.Assert(s.state.Get("conns", &conns), IsNil)
+ c.Assert(conns, DeepEquals, map[string]interface{}{
+ "consumer:plug core:hotplugslot": map[string]interface{}{
+ "interface": "test",
+ "hotplug-key": "1234",
+ "plug-static": map[string]interface{}{"attr1": "value1"},
+ },
+ "consumer2:plug core:hotplugslot": map[string]interface{}{
+ "interface": "test",
+ "hotplug-key": "1234",
+ "auto": true,
+ "plug-static": map[string]interface{}{"attr1": "value1"},
+ }})
+}
+
func (s *interfaceManagerSuite) TestHotplugDisconnect(c *C) {
coreInfo := s.mockSnap(c, coreSnapYaml)
repo := s.manager(c).Repository()
diff --git a/overlord/managers_test.go b/overlord/managers_test.go
index 21f9d04156..5ebb861c50 100644
--- a/overlord/managers_test.go
+++ b/overlord/managers_test.go
@@ -22,6 +22,7 @@ package overlord_test
// test the various managers and their operation together through overlord
import (
+ "context"
"encoding/json"
"fmt"
"io"
@@ -34,8 +35,6 @@ import (
"strings"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/asserts"
diff --git a/overlord/snapshotstate/backend/backend.go b/overlord/snapshotstate/backend/backend.go
index 590f291618..30b46a7dd5 100644
--- a/overlord/snapshotstate/backend/backend.go
+++ b/overlord/snapshotstate/backend/backend.go
@@ -21,6 +21,7 @@ package backend
import (
"archive/zip"
+ "context"
"crypto"
"encoding/json"
"errors"
@@ -31,8 +32,6 @@ import (
"sort"
"time"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/logger"
diff --git a/overlord/snapshotstate/backend/backend_test.go b/overlord/snapshotstate/backend/backend_test.go
index ff363bbe8a..f20d91152f 100644
--- a/overlord/snapshotstate/backend/backend_test.go
+++ b/overlord/snapshotstate/backend/backend_test.go
@@ -22,6 +22,7 @@ package backend_test
import (
"archive/zip"
"bytes"
+ "context"
"errors"
"fmt"
"io"
@@ -34,7 +35,6 @@ import (
"strings"
"testing"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"github.com/snapcore/snapd/client"
diff --git a/overlord/snapshotstate/backend/reader.go b/overlord/snapshotstate/backend/reader.go
index 61c273c5c3..94ac8cf82f 100644
--- a/overlord/snapshotstate/backend/reader.go
+++ b/overlord/snapshotstate/backend/reader.go
@@ -21,6 +21,7 @@ package backend
import (
"bytes"
+ "context"
"crypto"
"errors"
"fmt"
@@ -32,8 +33,6 @@ import (
"sort"
"syscall"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/jsonutil"
"github.com/snapcore/snapd/logger"
diff --git a/overlord/snapshotstate/export_test.go b/overlord/snapshotstate/export_test.go
index 99bf47e419..c5fa581a0d 100644
--- a/overlord/snapshotstate/export_test.go
+++ b/overlord/snapshotstate/export_test.go
@@ -20,10 +20,9 @@
package snapshotstate
import (
+ "context"
"encoding/json"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/overlord/snapshotstate/backend"
"github.com/snapcore/snapd/overlord/snapstate"
diff --git a/overlord/snapshotstate/snapshotmgr_test.go b/overlord/snapshotstate/snapshotmgr_test.go
index c13aee7725..f1ee73eb86 100644
--- a/overlord/snapshotstate/snapshotmgr_test.go
+++ b/overlord/snapshotstate/snapshotmgr_test.go
@@ -20,12 +20,12 @@
package snapshotstate_test
import (
+ "context"
"encoding/json"
"errors"
"path/filepath"
"sort"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"gopkg.in/tomb.v2"
diff --git a/overlord/snapshotstate/snapshotstate.go b/overlord/snapshotstate/snapshotstate.go
index 7860863b49..0621610669 100644
--- a/overlord/snapshotstate/snapshotstate.go
+++ b/overlord/snapshotstate/snapshotstate.go
@@ -20,11 +20,10 @@
package snapshotstate
import (
+ "context"
"fmt"
"sort"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/overlord/snapshotstate/backend"
"github.com/snapcore/snapd/overlord/snapstate"
diff --git a/overlord/snapshotstate/snapshotstate_test.go b/overlord/snapshotstate/snapshotstate_test.go
index ed17956936..b023d9928a 100644
--- a/overlord/snapshotstate/snapshotstate_test.go
+++ b/overlord/snapshotstate/snapshotstate_test.go
@@ -20,6 +20,7 @@
package snapshotstate_test
import (
+ "context"
"errors"
"fmt"
"os"
@@ -31,7 +32,6 @@ import (
"testing"
"time"
- "golang.org/x/net/context"
"gopkg.in/check.v1"
"github.com/snapcore/snapd/client"
diff --git a/overlord/snapstate/autorefresh_test.go b/overlord/snapstate/autorefresh_test.go
index bf44ca9514..3fb32ee748 100644
--- a/overlord/snapstate/autorefresh_test.go
+++ b/overlord/snapstate/autorefresh_test.go
@@ -20,13 +20,12 @@
package snapstate_test
import (
+ "context"
"fmt"
"os"
"path/filepath"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/dirs"
diff --git a/overlord/snapstate/backend.go b/overlord/snapstate/backend.go
index ab04b9fe31..5cfc9d39f2 100644
--- a/overlord/snapstate/backend.go
+++ b/overlord/snapstate/backend.go
@@ -20,10 +20,9 @@
package snapstate
import (
+ "context"
"io"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/overlord/auth"
diff --git a/overlord/snapstate/backend/export_test.go b/overlord/snapstate/backend/export_test.go
index 2ff5ecf906..648e3f16fe 100644
--- a/overlord/snapstate/backend/export_test.go
+++ b/overlord/snapstate/backend/export_test.go
@@ -19,6 +19,10 @@
package backend
+import (
+ "os/exec"
+)
+
var (
AddMountUnit = addMountUnit
RemoveMountUnit = removeMountUnit
@@ -31,3 +35,11 @@ func MockUpdateFontconfigCaches(f func() error) (restore func()) {
updateFontconfigCaches = oldUpdateFontconfigCaches
}
}
+
+func MockCommandFromCore(f func(string, string, ...string) (*exec.Cmd, error)) (restore func()) {
+ old := commandFromCore
+ commandFromCore = f
+ return func() {
+ commandFromCore = old
+ }
+}
diff --git a/overlord/snapstate/backend/fontconfig.go b/overlord/snapstate/backend/fontconfig.go
index 578b84713d..57e8cbf845 100644
--- a/overlord/snapstate/backend/fontconfig.go
+++ b/overlord/snapstate/backend/fontconfig.go
@@ -27,12 +27,13 @@ import (
)
var updateFontconfigCaches = updateFontconfigCachesImpl
+var commandFromCore = osutil.CommandFromCore
// updateFontconfigCaches always update the fontconfig caches
func updateFontconfigCachesImpl() error {
for _, fc := range []string{"fc-cache-v6", "fc-cache-v7"} {
// FIXME: also use the snapd snap if available
- cmd, err := osutil.CommandFromCore(dirs.SnapMountDir, "/bin/"+fc)
+ cmd, err := commandFromCore(dirs.SnapMountDir, "/bin/"+fc)
if err != nil {
return fmt.Errorf("cannot get %s from core: %v", fc, err)
}
diff --git a/overlord/snapstate/backend/link.go b/overlord/snapstate/backend/link.go
index 8543bd557f..6e24c7bbcc 100644
--- a/overlord/snapstate/backend/link.go
+++ b/overlord/snapstate/backend/link.go
@@ -58,6 +58,13 @@ func updateCurrentSymlinks(info *snap.Info) error {
return os.Symlink(filepath.Base(mountDir), currentActiveSymlink)
}
+func hasFontConfigCache(info *snap.Info) bool {
+ if info.InstanceName() == "core" || info.InstanceName() == "snapd" {
+ return true
+ }
+ return false
+}
+
// LinkSnap makes the snap available by generating wrappers and setting the current symlinks.
func (b Backend) LinkSnap(info *snap.Info, model *asserts.Model) error {
if info.Revision.Unset() {
@@ -68,9 +75,11 @@ func (b Backend) LinkSnap(info *snap.Info, model *asserts.Model) error {
return err
}
- // fontconfig is only relevant on classic
- // TODO: consider moving this to a less hidden place
- if release.OnClassic {
+ // fontconfig is only relevant on classic and is carried by 'core' or
+ // 'snapd' snaps
+ // for non-core snaps, fontconfig cache needs to be updated before the
+ // snap applications are runnable
+ if release.OnClassic && !hasFontConfigCache(info) {
if err := updateFontconfigCaches(); err != nil {
logger.Noticef("cannot update fontconfig cache: %v", err)
}
@@ -90,7 +99,18 @@ func (b Backend) LinkSnap(info *snap.Info, model *asserts.Model) error {
}
}
- return updateCurrentSymlinks(info)
+ if err := updateCurrentSymlinks(info); err != nil {
+ return err
+ }
+
+ // for core snap, fontconfig cache can be updated after the snap has
+ // been made available
+ if release.OnClassic && hasFontConfigCache(info) {
+ if err := updateFontconfigCaches(); err != nil {
+ logger.Noticef("cannot update fontconfig cache: %v", err)
+ }
+ }
+ return nil
}
func (b Backend) StartServices(apps []*snap.AppInfo, meter progress.Meter) error {
diff --git a/overlord/snapstate/backend/link_test.go b/overlord/snapstate/backend/link_test.go
index 17fe766d65..b5ff515f3b 100644
--- a/overlord/snapstate/backend/link_test.go
+++ b/overlord/snapstate/backend/link_test.go
@@ -23,6 +23,7 @@ import (
"errors"
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
. "gopkg.in/check.v1"
@@ -34,6 +35,7 @@ import (
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
"github.com/snapcore/snapd/systemd"
+ "github.com/snapcore/snapd/testutil"
"github.com/snapcore/snapd/overlord/snapstate/backend"
)
@@ -315,13 +317,15 @@ func (s *linkCleanupSuite) TestLinkCleanupOnSystemctlFail(c *C) {
}
func (s *linkCleanupSuite) TestLinkRunsUpdateFontconfigCachesClassic(c *C) {
- for _, onClassic := range []bool{false, true} {
+ current := filepath.Join(s.info.MountDir(), "..", "current")
+ for _, onClassic := range []bool{false, true} {
restore := release.MockOnClassic(onClassic)
defer restore()
var updateFontconfigCaches int
restore = backend.MockUpdateFontconfigCaches(func() error {
+ c.Assert(osutil.FileExists(current), Equals, false)
updateFontconfigCaches += 1
return nil
})
@@ -334,5 +338,53 @@ func (s *linkCleanupSuite) TestLinkRunsUpdateFontconfigCachesClassic(c *C) {
} else {
c.Assert(updateFontconfigCaches, Equals, 0)
}
+ c.Assert(os.Remove(current), IsNil)
}
}
+
+func (s *linkCleanupSuite) TestLinkRunsUpdateFontconfigCachesCallsFromNewCurrent(c *C) {
+ restore := release.MockOnClassic(true)
+ defer restore()
+
+ const yaml = `name: core
+version: 1.0
+type: os
+`
+ // old version is 'current'
+ infoOld := snaptest.MockSnap(c, yaml, &snap.SideInfo{Revision: snap.R(11)})
+ mountDirOld := infoOld.MountDir()
+ err := os.Symlink(filepath.Base(mountDirOld), filepath.Join(mountDirOld, "..", "current"))
+ c.Assert(err, IsNil)
+
+ err = os.MkdirAll(filepath.Join(mountDirOld, "bin"), 0755)
+ c.Assert(err, IsNil)
+
+ oldCmdV6 := testutil.MockCommand(c, filepath.Join(mountDirOld, "bin", "fc-cache-v6"), "")
+ oldCmdV7 := testutil.MockCommand(c, filepath.Join(mountDirOld, "bin", "fc-cache-v7"), "")
+
+ infoNew := snaptest.MockSnap(c, yaml, &snap.SideInfo{Revision: snap.R(12)})
+ mountDirNew := infoNew.MountDir()
+
+ err = os.MkdirAll(filepath.Join(mountDirNew, "bin"), 0755)
+ c.Assert(err, IsNil)
+
+ newCmdV6 := testutil.MockCommand(c, filepath.Join(mountDirNew, "bin", "fc-cache-v6"), "")
+ newCmdV7 := testutil.MockCommand(c, filepath.Join(mountDirNew, "bin", "fc-cache-v7"), "")
+
+ // provide our own mock, osutil.CommandFromCore expects an ELF binary
+ restore = backend.MockCommandFromCore(func(mountDir, name string, args ...string) (*exec.Cmd, error) {
+ cmd := filepath.Join(mountDir, "core", "current", name)
+ c.Logf("command from core: %v", cmd)
+ return exec.Command(cmd, args...), nil
+ })
+ defer restore()
+
+ err = s.be.LinkSnap(infoNew, nil)
+ c.Assert(err, IsNil)
+
+ c.Check(oldCmdV6.Calls(), HasLen, 0)
+ c.Check(oldCmdV7.Calls(), HasLen, 0)
+
+ c.Check(newCmdV6.Calls(), HasLen, 1)
+ c.Check(newCmdV7.Calls(), HasLen, 1)
+}
diff --git a/overlord/snapstate/backend_test.go b/overlord/snapstate/backend_test.go
index 2fc378c8d0..fbeb37f0a5 100644
--- a/overlord/snapstate/backend_test.go
+++ b/overlord/snapstate/backend_test.go
@@ -20,6 +20,7 @@
package snapstate_test
import (
+ "context"
"errors"
"fmt"
"io"
@@ -28,8 +29,6 @@ import (
"strings"
"sync"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/overlord/auth"
diff --git a/overlord/snapstate/catalogrefresh_test.go b/overlord/snapstate/catalogrefresh_test.go
index 541d5b0e01..59d8fca731 100644
--- a/overlord/snapstate/catalogrefresh_test.go
+++ b/overlord/snapstate/catalogrefresh_test.go
@@ -20,14 +20,13 @@
package snapstate_test
import (
+ "context"
"io"
"io/ioutil"
"os"
"path/filepath"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/advisor"
diff --git a/overlord/snapstate/refreshhints_test.go b/overlord/snapstate/refreshhints_test.go
index 916c0659d8..c8a33f8d00 100644
--- a/overlord/snapstate/refreshhints_test.go
+++ b/overlord/snapstate/refreshhints_test.go
@@ -20,10 +20,9 @@
package snapstate_test
import (
+ "context"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/overlord/auth"
diff --git a/overlord/snapstate/snapstate.go b/overlord/snapstate/snapstate.go
index 8209aa9fde..d21e65fd98 100644
--- a/overlord/snapstate/snapstate.go
+++ b/overlord/snapstate/snapstate.go
@@ -21,6 +21,7 @@
package snapstate
import (
+ "context"
"encoding/json"
"fmt"
"os"
@@ -28,8 +29,6 @@ import (
"strings"
"time"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/boot"
"github.com/snapcore/snapd/dirs"
diff --git a/overlord/snapstate/snapstate_test.go b/overlord/snapstate/snapstate_test.go
index 228adc7831..c910d80194 100644
--- a/overlord/snapstate/snapstate_test.go
+++ b/overlord/snapstate/snapstate_test.go
@@ -21,6 +21,7 @@ package snapstate_test
import (
"bytes"
+ "context"
"encoding/json"
"errors"
"fmt"
@@ -32,8 +33,6 @@ import (
"testing"
"time"
- "golang.org/x/net/context"
-
. "gopkg.in/check.v1"
"gopkg.in/tomb.v2"
@@ -4346,7 +4345,33 @@ func (s *snapmgrTestSuite) TestUpdateNoStoreResults(c *C) {
})
_, err := snapstate.Update(s.state, "some-snap", "channel-for-7", snap.R(0), s.user.ID, snapstate.Flags{})
- c.Assert(err, Equals, store.ErrNoUpdateAvailable)
+ c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
+}
+
+func (s *snapmgrTestSuite) TestUpdateNoStoreResultsWithChannelChange(c *C) {
+ s.state.Lock()
+ defer s.state.Unlock()
+
+ snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
+
+ // this is an atypical case in which the store didn't return
+ // an error nor a result, we are defensive and return
+ // a reasonable error
+ si := snap.SideInfo{
+ RealName: "some-snap",
+ SnapID: "some-snap-id",
+ Revision: snap.R(7),
+ }
+
+ snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
+ Active: true,
+ Sequence: []*snap.SideInfo{&si},
+ Channel: "channel-for-9",
+ Current: si.Revision,
+ })
+
+ _, err := snapstate.Update(s.state, "some-snap", "channel-for-7", snap.R(0), s.user.ID, snapstate.Flags{})
+ c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
}
func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannel(c *C) {
diff --git a/overlord/snapstate/storehelpers.go b/overlord/snapstate/storehelpers.go
index 3458c77862..f4eaf46fcc 100644
--- a/overlord/snapstate/storehelpers.go
+++ b/overlord/snapstate/storehelpers.go
@@ -20,11 +20,10 @@
package snapstate
import (
+ "context"
"fmt"
"sort"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/overlord/auth"
"github.com/snapcore/snapd/overlord/state"
@@ -198,6 +197,8 @@ func preUpdateInfo(st *state.State, snapst *SnapState, amend bool, userID int) (
return curInfo, user, nil
}
+var ErrMissingExpectedResult = fmt.Errorf("unexpectedly empty response from the server (try again later)")
+
func singleActionResult(name, action string, results []*snap.Info, e error) (info *snap.Info, err error) {
if len(results) > 1 {
return nil, fmt.Errorf("internal error: multiple store results for a single snap op")
@@ -225,12 +226,7 @@ func singleActionResult(name, action string, results []*snap.Info, e error) (inf
// no result, atypical case
if saErr.NoResults {
- switch action {
- case "refresh":
- return nil, store.ErrNoUpdateAvailable
- case "install":
- return nil, store.ErrSnapNotFound
- }
+ return nil, ErrMissingExpectedResult
}
}
diff --git a/packaging/fedora/snapd.spec b/packaging/fedora/snapd.spec
index 671a2c9ceb..a8e64fa729 100644
--- a/packaging/fedora/snapd.spec
+++ b/packaging/fedora/snapd.spec
@@ -112,7 +112,7 @@ ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x
%endif
# If go_compiler is not set to 1, there is no virtual provide. Use golang instead.
-BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang}
+BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} >= 1.9
BuildRequires: systemd
%{?systemd_requires}
@@ -164,8 +164,6 @@ BuildRequires: golang(golang.org/x/crypto/openpgp/armor)
BuildRequires: golang(golang.org/x/crypto/openpgp/packet)
BuildRequires: golang(golang.org/x/crypto/sha3)
BuildRequires: golang(golang.org/x/crypto/ssh/terminal)
-BuildRequires: golang(golang.org/x/net/context)
-BuildRequires: golang(golang.org/x/net/context/ctxhttp)
BuildRequires: golang(gopkg.in/check.v1)
BuildRequires: golang(gopkg.in/macaroon.v1)
BuildRequires: golang(gopkg.in/mgo.v2/bson)
@@ -262,8 +260,6 @@ Requires: golang(golang.org/x/crypto/openpgp/armor)
Requires: golang(golang.org/x/crypto/openpgp/packet)
Requires: golang(golang.org/x/crypto/sha3)
Requires: golang(golang.org/x/crypto/ssh/terminal)
-Requires: golang(golang.org/x/net/context)
-Requires: golang(golang.org/x/net/context/ctxhttp)
Requires: golang(gopkg.in/check.v1)
Requires: golang(gopkg.in/macaroon.v1)
Requires: golang(gopkg.in/mgo.v2/bson)
@@ -291,8 +287,6 @@ Provides: bundled(golang(golang.org/x/crypto/openpgp/armor))
Provides: bundled(golang(golang.org/x/crypto/openpgp/packet))
Provides: bundled(golang(golang.org/x/crypto/sha3))
Provides: bundled(golang(golang.org/x/crypto/ssh/terminal))
-Provides: bundled(golang(golang.org/x/net/context))
-Provides: bundled(golang(golang.org/x/net/context/ctxhttp))
Provides: bundled(golang(gopkg.in/check.v1))
Provides: bundled(golang(gopkg.in/macaroon.v1))
Provides: bundled(golang(gopkg.in/mgo.v2/bson))
@@ -535,6 +529,7 @@ install -d -p %{buildroot}%{_sharedstatedir}/snapd/device
install -d -p %{buildroot}%{_sharedstatedir}/snapd/hostfs
install -d -p %{buildroot}%{_sharedstatedir}/snapd/lib/gl
install -d -p %{buildroot}%{_sharedstatedir}/snapd/lib/gl32
+install -d -p %{buildroot}%{_sharedstatedir}/snapd/lib/glvnd
install -d -p %{buildroot}%{_sharedstatedir}/snapd/lib/vulkan
install -d -p %{buildroot}%{_sharedstatedir}/snapd/mount
install -d -p %{buildroot}%{_sharedstatedir}/snapd/seccomp/bpf
@@ -723,6 +718,7 @@ popd
%dir %{_sharedstatedir}/snapd/lib
%dir %{_sharedstatedir}/snapd/lib/gl
%dir %{_sharedstatedir}/snapd/lib/gl32
+%dir %{_sharedstatedir}/snapd/lib/glvnd
%dir %{_sharedstatedir}/snapd/lib/vulkan
%dir %{_sharedstatedir}/snapd/mount
%dir %{_sharedstatedir}/snapd/seccomp
diff --git a/packaging/opensuse/snapd.spec b/packaging/opensuse/snapd.spec
index 7cf0f165aa..713922ed12 100644
--- a/packaging/opensuse/snapd.spec
+++ b/packaging/opensuse/snapd.spec
@@ -91,7 +91,7 @@ BuildRequires: autoconf
BuildRequires: automake
BuildRequires: glib2-devel
BuildRequires: glibc-devel-static
-BuildRequires: go
+BuildRequires: go >= 1.9
BuildRequires: gpg2
BuildRequires: indent
BuildRequires: libapparmor-devel
@@ -274,7 +274,7 @@ rm -f %{buildroot}%{_libexecdir}/snapd/system-shutdown
# Install the directories that snapd creates by itself so that they can be a part of the package
install -d %{buildroot}%{_sharedstatedir}/snapd/{assertions,cookie,desktop/applications,device,hostfs,mount,apparmor/profiles,seccomp/bpf,snaps}
-install -d %{buildroot}%{_sharedstatedir}/snapd/{lib/gl,lib/gl32,lib/vulkan}
+install -d %{buildroot}%{_sharedstatedir}/snapd/{lib/gl,lib/gl32,lib/glvnd,lib/vulkan}
install -d %{buildroot}%{_localstatedir}/cache/snapd
install -d %{buildroot}%{_datadir}/polkit-1/actions
install -d %{buildroot}%{snap_mount_dir}/bin
@@ -372,6 +372,7 @@ fi
%dir %{_sharedstatedir}/snapd/lib
%dir %{_sharedstatedir}/snapd/lib/gl
%dir %{_sharedstatedir}/snapd/lib/gl32
+%dir %{_sharedstatedir}/snapd/lib/glvnd
%dir %{_sharedstatedir}/snapd/lib/vulkan
%dir %{_localstatedir}/cache/snapd
%dir %{_environmentdir}
diff --git a/packaging/ubuntu-14.04/control b/packaging/ubuntu-14.04/control
index 46f0bfd614..f5df3a8fc7 100644
--- a/packaging/ubuntu-14.04/control
+++ b/packaging/ubuntu-14.04/control
@@ -17,7 +17,7 @@ Build-Depends: autoconf,
gettext,
grub-common,
gnupg2,
- golang-any (>=2:1.6) | golang-1.6,
+ golang-any (>=2:1.10) | golang-1.10,
indent,
init-system-helpers,
libcap-dev,
diff --git a/packaging/ubuntu-14.04/rules b/packaging/ubuntu-14.04/rules
index 2cac5f7a1b..3b3e59ca4b 100755
--- a/packaging/ubuntu-14.04/rules
+++ b/packaging/ubuntu-14.04/rules
@@ -20,7 +20,7 @@ export DH_GOLANG_GO_GENERATE=1
export PATH:=${PATH}:${CURDIR}
# make sure that correct go version is found on trusty
-export PATH:=/usr/lib/go-1.6/bin:${PATH}
+export PATH:=/usr/lib/go-1.10/bin:${PATH}
include /etc/os-release
@@ -31,9 +31,6 @@ include /etc/os-release
# from /lib/systemd/upstart.
SYSTEMD_UNITS_DESTDIR="lib/systemd/upstart/"
-# make sure that trusty's golang-1.6 is picked up correctly.
-export PATH:=/usr/lib/go-1.6/bin:${PATH}
-
# The go tool does not fully support vendoring with gccgo, but we can
# work around that by constructing the appropriate -I flag by hand.
GCCGO := $(shell go tool dist env > /dev/null 2>&1 && echo no || echo yes)
diff --git a/packaging/ubuntu-14.04/snapd.dirs b/packaging/ubuntu-14.04/snapd.dirs
index 3add9c7a56..c66ae5cc02 100644
--- a/packaging/ubuntu-14.04/snapd.dirs
+++ b/packaging/ubuntu-14.04/snapd.dirs
@@ -8,6 +8,7 @@ var/lib/snapd/environment
var/lib/snapd/firstboot
var/lib/snapd/lib/gl
var/lib/snapd/lib/gl32
+var/lib/snapd/lib/glvnd
var/lib/snapd/lib/vulkan
var/lib/snapd/snaps/partial
var/lib/snapd/void
diff --git a/packaging/ubuntu-16.04/control b/packaging/ubuntu-16.04/control
index 25eb1e6273..fe61e7d594 100644
--- a/packaging/ubuntu-16.04/control
+++ b/packaging/ubuntu-16.04/control
@@ -17,7 +17,7 @@ Build-Depends: autoconf,
gettext,
grub-common,
gnupg2,
- golang-any (>=2:1.6) | golang-1.6,
+ golang-any (>=2:1.10) | golang-1.10,
indent,
init-system-helpers,
libapparmor-dev,
diff --git a/packaging/ubuntu-16.04/rules b/packaging/ubuntu-16.04/rules
index ecc7f6bbb4..852a919398 100755
--- a/packaging/ubuntu-16.04/rules
+++ b/packaging/ubuntu-16.04/rules
@@ -18,7 +18,7 @@ export DH_GOLANG_GO_GENERATE=1
export PATH:=${PATH}:${CURDIR}
# make sure that correct go version is found on trusty
-export PATH:=/usr/lib/go-1.6/bin:${PATH}
+export PATH:=/usr/lib/go-1.10/bin:${PATH}
include /etc/os-release
diff --git a/packaging/ubuntu-16.04/snapd.dirs b/packaging/ubuntu-16.04/snapd.dirs
index 3add9c7a56..c66ae5cc02 100644
--- a/packaging/ubuntu-16.04/snapd.dirs
+++ b/packaging/ubuntu-16.04/snapd.dirs
@@ -8,6 +8,7 @@ var/lib/snapd/environment
var/lib/snapd/firstboot
var/lib/snapd/lib/gl
var/lib/snapd/lib/gl32
+var/lib/snapd/lib/glvnd
var/lib/snapd/lib/vulkan
var/lib/snapd/snaps/partial
var/lib/snapd/void
diff --git a/parts/plugins/x_builddeb.py b/parts/plugins/x_builddeb.py
index f2604fbab6..d12fe699ee 100644
--- a/parts/plugins/x_builddeb.py
+++ b/parts/plugins/x_builddeb.py
@@ -45,6 +45,9 @@ class XBuildDeb(snapcraft.BasePlugin):
# XXX: get this from "debian/gbp.conf:postexport"
self.run(["./get-deps.sh", "--skip-unused-check"])
env=os.environ.copy()
+ # ensure build with go-1.10 if available
+ if os.path.exists("/usr/lib/go-1.10/bin"):
+ env["PATH"] = "/usr/lib/go-1.10/bin:{}".format(env["PATH"])
if os.getuid() == 0:
# disable running the tests during the build when run as root
# because quite a few of them will break
diff --git a/run-checks b/run-checks
index 11cc19500c..dc3deb5d49 100755
--- a/run-checks
+++ b/run-checks
@@ -16,6 +16,9 @@ else
fi
COVERMODE=${COVERMODE:-atomic}
+# ensure we use go-1.10 by default
+export PATH=/usr/lib/go-1.10/bin:${PATH}
+
# add workaround for https://github.com/golang/go/issues/24449
if [ "$(uname -m)" = "s390x" ]; then
if go version | grep -q go1.10; then
diff --git a/spread.yaml b/spread.yaml
index 14046cee59..f7c4f79619 100644
--- a/spread.yaml
+++ b/spread.yaml
@@ -80,7 +80,7 @@ backends:
workers: 4
manual: true
- fedora-29-64:
- workers: 4
+ workers: 6
- opensuse-42.3-64:
workers: 4
# golang stack cannot compile anything, needs investigation
@@ -117,8 +117,6 @@ backends:
workers: 1
- fedora-29-64:
workers: 1
- # https://twitter.com/zygoon/status/1073629342884864000
- manual: true
- arch-linux-64:
workers: 1
- amazon-linux-2-64:
@@ -138,10 +136,11 @@ backends:
workers: 4
- ubuntu-16.04-64:
workers: 4
- - ubuntu-17.10-64:
- workers: 4
- ubuntu-18.04-64:
workers: 4
+ - ubuntu-18.10-64:
+ image: ubuntu-1810
+ workers: 4
google-nested:
type: google
@@ -474,7 +473,7 @@ repack: |
tar c current.delta >&4
fi
-kill-timeout: 20m
+kill-timeout: 30m
prepare: |
# NOTE: This part of the code needs to be in spread.yaml as it runs before
diff --git a/store/download_test.go b/store/download_test.go
index d97e633371..13d21a0280 100644
--- a/store/download_test.go
+++ b/store/download_test.go
@@ -21,6 +21,7 @@ package store_test
import (
"bytes"
+ "context"
"crypto"
"errors"
"fmt"
@@ -33,7 +34,6 @@ import (
"time"
"github.com/juju/ratelimit"
- "golang.org/x/net/context"
. "gopkg.in/check.v1"
"gopkg.in/retry.v1"
diff --git a/store/export_test.go b/store/export_test.go
index 2537b45e5a..e0e326b67e 100644
--- a/store/export_test.go
+++ b/store/export_test.go
@@ -22,11 +22,11 @@ package store
import (
"io"
+ "context"
"net/http"
"net/url"
"github.com/juju/ratelimit"
- "golang.org/x/net/context"
"gopkg.in/retry.v1"
"github.com/snapcore/snapd/overlord/auth"
diff --git a/store/store.go b/store/store.go
index a27151a9e4..b4936ea711 100644
--- a/store/store.go
+++ b/store/store.go
@@ -22,6 +22,7 @@ package store
import (
"bytes"
+ "context"
"crypto"
"encoding/base64"
"encoding/json"
@@ -40,8 +41,6 @@ import (
"time"
"github.com/juju/ratelimit"
- "golang.org/x/net/context"
- "golang.org/x/net/context/ctxhttp"
"gopkg.in/retry.v1"
"github.com/snapcore/snapd/arch"
@@ -763,13 +762,11 @@ func (s *Store) doRequest(ctx context.Context, client *http.Client, reqOptions *
if err != nil {
return nil, err
}
-
- var resp *http.Response
if ctx != nil {
- resp, err = ctxhttp.Do(ctx, client, req)
- } else {
- resp, err = client.Do(req)
+ req = req.WithContext(ctx)
}
+
+ resp, err := client.Do(req)
if err != nil {
return nil, err
}
diff --git a/store/store_test.go b/store/store_test.go
index b1a79ee649..70d51e96f7 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -21,6 +21,7 @@ package store_test
import (
"bytes"
+ "context"
"crypto"
"encoding/json"
"fmt"
@@ -37,7 +38,6 @@ import (
"time"
"golang.org/x/crypto/sha3"
- "golang.org/x/net/context"
. "gopkg.in/check.v1"
"gopkg.in/macaroon.v1"
"gopkg.in/retry.v1"
diff --git a/store/storetest/storetest.go b/store/storetest/storetest.go
index 82556655b8..fde4c14cf3 100644
--- a/store/storetest/storetest.go
+++ b/store/storetest/storetest.go
@@ -20,10 +20,9 @@
package storetest
import (
+ "context"
"io"
- "golang.org/x/net/context"
-
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/client"
"github.com/snapcore/snapd/overlord/auth"
diff --git a/tests/lib/best_golang.py b/tests/lib/best_golang.py
new file mode 100755
index 0000000000..862a12b1cf
--- /dev/null
+++ b/tests/lib/best_golang.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import apt
+import re
+
+best_golang=None
+for p in apt.Cache():
+ if re.match(r"golang-([0-9.]+)$", p.name):
+ if best_golang is None or apt.apt_pkg.version_compare(best_golang.candidate.version, p.candidate.version) < 0:
+ best_golang = p
+
+print(best_golang.name)
diff --git a/tests/lib/prepare-restore.sh b/tests/lib/prepare-restore.sh
index d516d8f357..162cccd695 100755
--- a/tests/lib/prepare-restore.sh
+++ b/tests/lib/prepare-restore.sh
@@ -4,9 +4,6 @@ set -x
# statements we execute stops the build. The code is not (yet) written to
# handle errors in general.
set -e
-# Set pipefail option so that "foo | bar" behaves with fewer surprises by
-# failing if foo fails, not just if bar fails.
-set -o pipefail
# shellcheck source=tests/lib/quiet.sh
. "$TESTSLIB/quiet.sh"
@@ -332,7 +329,20 @@ prepare_project() {
case "$SPREAD_SYSTEM" in
debian-*|ubuntu-*)
# in 16.04: apt build-dep -y ./
+ if [[ "$SPREAD_SYSTEM" == debian-9-* ]]; then
+ best_golang="$(python3 ./tests/lib/best_golang.py)"
+ test -n "$best_golang"
+ sed -i -e "s/golang-1.10/$best_golang/" ./debian/control
+ else
+ best_golang=golang-1.10
+ fi
gdebi --quiet --apt-line ./debian/control | quiet xargs -r apt-get install -y
+ # The go 1.10 backport is not using alternatives or anything else so
+ # we need to get it on path somehow. This is not perfect but simple.
+ if [ -z "$(command -v go)" ]; then
+ # the path filesystem path is: /usr/lib/go-1.10/bin
+ ln -s "/usr/lib/${best_golang/lang/}/bin/go" /usr/bin/go
+ fi
;;
esac
diff --git a/tests/lib/snaps/test-snapd-dbus-consumer/consumer.py b/tests/lib/snaps/test-snapd-dbus-consumer/consumer.py
index 8cd3d2a992..a5139d06c1 100755
--- a/tests/lib/snaps/test-snapd-dbus-consumer/consumer.py
+++ b/tests/lib/snaps/test-snapd-dbus-consumer/consumer.py
@@ -2,9 +2,13 @@
import dbus
import sys
-def run():
- obj = dbus.SessionBus().get_object("com.dbustest.HelloWorld", "/com/dbustest/HelloWorld")
+def run(bus):
+ obj = bus.get_object("com.dbustest.HelloWorld", "/com/dbustest/HelloWorld")
print(obj.SayHello(dbus_interface="com.dbustest.HelloWorld"))
if __name__ == "__main__":
- sys.exit(run())
+ if sys.argv[1] == "system":
+ bus = dbus.SystemBus()
+ else:
+ bus = dbus.SessionBus()
+ run(bus)
diff --git a/tests/lib/snaps/test-snapd-dbus-consumer/snapcraft.yaml b/tests/lib/snaps/test-snapd-dbus-consumer/snapcraft.yaml
index fe1699f5ba..50595078c0 100644
--- a/tests/lib/snaps/test-snapd-dbus-consumer/snapcraft.yaml
+++ b/tests/lib/snaps/test-snapd-dbus-consumer/snapcraft.yaml
@@ -6,13 +6,20 @@ description: A basic snap declaring a plug on dbus
apps:
dbus-consumer:
plugs: [dbus-test]
- command: bin/consumer
+ command: bin/consumer session
+ dbus-system-consumer:
+ plugs: [dbus-system-test]
+ command: bin/consumer system
plugs:
dbus-test:
interface: dbus
bus: session
name: com.dbustest.HelloWorld
+ dbus-system-test:
+ interface: dbus
+ bus: system
+ name: com.dbustest.HelloWorld
parts:
consumer:
diff --git a/tests/lib/snaps/test-snapd-dbus-provider/consumer.py b/tests/lib/snaps/test-snapd-dbus-provider/consumer.py
new file mode 100755
index 0000000000..a5139d06c1
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-dbus-provider/consumer.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+import dbus
+import sys
+
+def run(bus):
+ obj = bus.get_object("com.dbustest.HelloWorld", "/com/dbustest/HelloWorld")
+ print(obj.SayHello(dbus_interface="com.dbustest.HelloWorld"))
+
+if __name__ == "__main__":
+ if sys.argv[1] == "system":
+ bus = dbus.SystemBus()
+ else:
+ bus = dbus.SessionBus()
+ run(bus)
diff --git a/tests/lib/snaps/test-snapd-dbus-provider/provider.py b/tests/lib/snaps/test-snapd-dbus-provider/provider.py
index 867939041c..21f32274df 100644
--- a/tests/lib/snaps/test-snapd-dbus-provider/provider.py
+++ b/tests/lib/snaps/test-snapd-dbus-provider/provider.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+import sys
from gi.repository import GLib
import dbus
import dbus.service
@@ -8,8 +9,7 @@ from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
class DBusProvider(dbus.service.Object):
- def __init__(self):
- bus = dbus.SessionBus()
+ def __init__(self, bus):
bus_name = dbus.service.BusName("com.dbustest.HelloWorld", bus=bus)
dbus.service.Object.__init__(self, bus_name, "/com/dbustest/HelloWorld")
@@ -19,6 +19,13 @@ class DBusProvider(dbus.service.Object):
return "hello world"
if __name__ == "__main__":
- DBusProvider()
+ if sys.argv[1] == "system":
+ bus = dbus.SystemBus()
+ elif sys.argv[1] == "session":
+ bus = dbus.SessionBus()
+ else:
+ print("unknown bus: %s", sys.argv[1:])
+ sys.exit(1)
+ DBusProvider(bus)
loop = GLib.MainLoop()
loop.run()
diff --git a/tests/lib/snaps/test-snapd-dbus-provider/snapcraft.yaml b/tests/lib/snaps/test-snapd-dbus-provider/snapcraft.yaml
index ec3db0f6cf..0f5ca5d212 100644
--- a/tests/lib/snaps/test-snapd-dbus-provider/snapcraft.yaml
+++ b/tests/lib/snaps/test-snapd-dbus-provider/snapcraft.yaml
@@ -5,14 +5,40 @@ description: A basic snap declaring a dbus slot
apps:
provider:
- command: wrapper
+ command: wrapper session
slots: [dbus-test]
+ system-provider:
+ command: wrapper system
+ daemon: simple
+ slots: [dbus-system-test]
+ # provide an in-snap consumer as well for testing
+ consumer:
+ command: consumer.py session
+ plugs: [dbus-test-plug]
+ system-consumer:
+ command: consumer.py system
+ plugs: [dbus-system-test-plug]
+
+
+plugs:
+ dbus-test-plug:
+ interface: dbus
+ bus: session
+ name: com.dbustest.HelloWorld
+ dbus-system-test-plug:
+ interface: dbus
+ bus: system
+ name: com.dbustest.HelloWorld
slots:
dbus-test:
interface: dbus
bus: session
name: com.dbustest.HelloWorld
+ dbus-system-test:
+ interface: dbus
+ bus: system
+ name: com.dbustest.HelloWorld
parts:
provider:
diff --git a/tests/lib/snaps/test-snapd-dbus-provider/wrapper b/tests/lib/snaps/test-snapd-dbus-provider/wrapper
index 5398d19960..7423e88f82 100755
--- a/tests/lib/snaps/test-snapd-dbus-provider/wrapper
+++ b/tests/lib/snaps/test-snapd-dbus-provider/wrapper
@@ -1,3 +1,8 @@
-export GI_TYPELIB_PATH=$SNAP/usr/lib/girepository-1.0:$(ls -d $SNAP/usr/lib/*/girepository-1.0)
+#!/bin/sh
-$SNAP/usr/bin/python3 $SNAP/provider.py
+set -e
+
+GI_TYPELIB_PATH="$SNAP"/usr/lib/girepository-1.0:"$(ls -d "$SNAP"/usr/lib/*/girepository-1.0)"
+export GI_TYPELIB_PATH
+
+"$SNAP"/usr/bin/python3 "$SNAP"/provider.py "$@"
diff --git a/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml b/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml
index aee7bb07ca..e4ab7d3a9c 100644
--- a/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml
+++ b/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml
@@ -23,6 +23,9 @@ apps:
avahi-observe:
command: bin/run
plugs: [ avahi-observe ]
+ block-devices:
+ command: bin/run
+ plugs: [ block-devices ]
bluetooth-control:
command: bin/run
plugs: [ bluetooth-control ]
@@ -83,6 +86,9 @@ apps:
device-buttons:
command: bin/run
plugs: [ device-buttons ]
+ display-control:
+ command: bin/run
+ plugs: [ display-control ]
docker:
command: bin/run
plugs: [ docker ]
@@ -326,6 +332,9 @@ apps:
tpm:
command: bin/run
plugs: [ tpm ]
+ u2f-devices:
+ command: bin/run
+ plugs: [ u2f-devices ]
ubuntu-download-manager:
command: bin/run
plugs: [ ubuntu-download-manager ]
diff --git a/tests/main/auth-errors/task.yaml b/tests/main/auth-errors/task.yaml
index ceefb4089c..d808146980 100644
--- a/tests/main/auth-errors/task.yaml
+++ b/tests/main/auth-errors/task.yaml
@@ -12,7 +12,7 @@ restore: |
execute: |
echo "An unauthenticated user cannot install snaps"
- if su - -c "snap install test-snapd-tools" test 2>"${PWD}/install.output" ; then
+ if su - -c "snap install test-snapd-tools" test 2> install.output; then
echo "Expected error installing snap from unauthenticated account"
exit 1
fi
@@ -20,7 +20,7 @@ execute: |
[ "$(cat install.output)" = "$expected" ]
echo "An unauthenticated user cannot connect plugs to slots"
- if su - -c "snap connect foo:bar baz:fromp" test 2>"${PWD}/connect.output" ; then
+ if su - -c "snap connect foo:bar baz:fromp" test 2> connect.output; then
echo "Expected error connecting plugs to slots from unauthenticated account"
exit 1
fi
diff --git a/tests/main/install-snaps/task.yaml b/tests/main/install-snaps/task.yaml
index a387010649..36962c98ef 100644
--- a/tests/main/install-snaps/task.yaml
+++ b/tests/main/install-snaps/task.yaml
@@ -97,11 +97,11 @@ execute: |
CHANNELS="stable candidate beta edge"
for CHANNEL in $CHANNELS; do
# shellcheck disable=SC2153
- if ! CHANNEL_INFO="$(snap info "$SNAP" | grep " $CHANNEL: ")"; then
+ if ! CHANNEL_INFO="$(snap info --unicode=never "$SNAP" | grep " $CHANNEL: ")"; then
echo "Snap $SNAP not found"
exit
fi
- if echo "$CHANNEL_INFO" | MATCH "$CHANNEL:.*–"; then
+ if echo "$CHANNEL_INFO" | MATCH "$CHANNEL:.*--"; then
continue
fi
diff --git a/tests/main/interfaces-autopilot-introspection/task.yaml b/tests/main/interfaces-autopilot-introspection/task.yaml
index 6d02073f8d..8a4f5c3baf 100644
--- a/tests/main/interfaces-autopilot-introspection/task.yaml
+++ b/tests/main/interfaces-autopilot-introspection/task.yaml
@@ -58,25 +58,23 @@ execute: |
echo "And the snap app state can be intrsopected"
$SNAP_MOUNT_DIR/bin/test-snapd-autopilot-consumer.consumer GetState | MATCH "my-ap-state"
- if [ "$(snap debug confinement)" = none ]; then
+ if [ "$(snap debug confinement)" = partial ] ; then
exit 0
fi
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect test-snapd-autopilot-consumer:autopilot-introspection
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-autopilot-consumer:autopilot-introspection
- echo "Then the snap version is not introspectable"
- if $SNAP_MOUNT_DIR/bin/test-snapd-autopilot-consumer.consumer GetVersion 2>"${PWD}"/getversion.error ; then
- echo "Expected permission error trying to introspect version with disconnected plug"
- exit 1
- fi
- MATCH "Permission denied" < getversion.error
+ echo "Then the snap version is not introspectable"
+ if $SNAP_MOUNT_DIR/bin/test-snapd-autopilot-consumer.consumer GetVersion 2> getversion.error ; then
+ echo "Expected permission error trying to introspect version with disconnected plug"
+ exit 1
+ fi
+ MATCH "Permission denied" < getversion.error
- echo "And the snap state is not introspectable"
- if $SNAP_MOUNT_DIR/bin/test-snapd-autopilot-consumer.consumer GetState 2>"${PWD}"/getstate.error; then
- echo "Expected permission error trying to introspect state with disconnected plug"
- exit 1
- fi
- MATCH "Permission denied" < getstate.error
+ echo "And the snap state is not introspectable"
+ if $SNAP_MOUNT_DIR/bin/test-snapd-autopilot-consumer.consumer GetState 2> getstate.error; then
+ echo "Expected permission error trying to introspect state with disconnected plug"
+ exit 1
fi
+ MATCH "Permission denied" < getstate.error
diff --git a/tests/main/interfaces-avahi-observe/task.yaml b/tests/main/interfaces-avahi-observe/task.yaml
index da475fa6ca..7fea3c723a 100644
--- a/tests/main/interfaces-avahi-observe/task.yaml
+++ b/tests/main/interfaces-avahi-observe/task.yaml
@@ -26,18 +26,23 @@ execute: |
echo "Then the plug is disconnected by default"
snap interfaces -i avahi-observe | MATCH '^\- +generic-consumer:avahi-observe'
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "And the snap is not able to access avahi provided info"
- if avahi_dbus_call 2>avahi.error; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "org.freedesktop.DBus.Error.AccessDenied" < avahi.error
- fi
-
echo "When the plug is connected"
snap connect generic-consumer:avahi-observe
echo "Then the snap is able to access avahi provided info"
hostname=$(hostname)
avahi_dbus_call | MATCH "$hostname"
+
+ if [ "$(snap debug confinement)" = partial ]; then
+ exit 0
+ fi
+
+ echo "When the plug is disconnected"
+ snap disconnect generic-consumer:avahi-observe
+
+ echo "And the snap is not able to access avahi provided info"
+ if avahi_dbus_call 2> avahi.error; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "org.freedesktop.DBus.Error.AccessDenied" < avahi.error
diff --git a/tests/main/interfaces-bluetooth-control/task.yaml b/tests/main/interfaces-bluetooth-control/task.yaml
index 52ac631c5c..d83eeb5f66 100644
--- a/tests/main/interfaces-bluetooth-control/task.yaml
+++ b/tests/main/interfaces-bluetooth-control/task.yaml
@@ -16,29 +16,6 @@ execute: |
#shellcheck disable=SC1117
snap interfaces -i bluetooth-control | MATCH "^\- +generic-consumer:bluetooth-control"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "And the snap is not able to read usb"
- if su -l -c "/snap/bin/generic-consumer.cmd cat /sys/bus/usb/drivers/btusb/module/version" test 2>"${PWD}/btusb.error"; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < btusb.error
-
- echo "And the snap is not able to read class"
- if su -l -c "/snap/bin/generic-consumer.cmd cat /sys/class/bluetooth/*/name" test 2>"${PWD}/btclass.error"; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < btclass.error
-
- echo "And the snap is not able to read dev"
- if su -l -c "/snap/bin/generic-consumer.cmd cat $BTDEV/*/power/control" test 2>"${PWD}/btdev-read.error"; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < btdev-read.error
- fi
-
echo "When the plug is connected"
snap connect generic-consumer:bluetooth-control
@@ -55,3 +32,31 @@ execute: |
echo "And the snap is able to read dev"
cat "$BTDEV"/*/power/control | tee control
[ "$(su -l -c '/snap/bin/generic-consumer.cmd cat '"$BTDEV"'/*/power/control' test)" = "$(cat control)" ]
+
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "When the plug is disconnected"
+ snap disconnect generic-consumer:bluetooth-control
+
+ echo "And the snap is not able to read usb"
+ if su -l -c "/snap/bin/generic-consumer.cmd cat /sys/bus/usb/drivers/btusb/module/version" test 2> btusb.error; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < btusb.error
+
+ echo "And the snap is not able to read class"
+ if su -l -c "/snap/bin/generic-consumer.cmd cat /sys/class/bluetooth/*/name" test 2> btclass.error; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < btclass.error
+
+ echo "And the snap is not able to read dev"
+ if su -l -c "/snap/bin/generic-consumer.cmd cat $BTDEV/*/power/control" test 2> btdev-read.error; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < btdev-read.error
diff --git a/tests/main/interfaces-broadcom-asic-control/task.yaml b/tests/main/interfaces-broadcom-asic-control/task.yaml
index 766f48bcd4..7155b704c5 100644
--- a/tests/main/interfaces-broadcom-asic-control/task.yaml
+++ b/tests/main/interfaces-broadcom-asic-control/task.yaml
@@ -77,7 +77,7 @@ execute: |
snap disconnect test-snapd-sh:broadcom-asic-control
echo "Then the snap is not able to read the device"
- if test-snapd-sh.with-broadcom-asic-control-plug -c "cat /dev/linux-bcm-knet" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-broadcom-asic-control-plug -c "cat /dev/linux-bcm-knet" 2> call.error; then
echo "Expected permission error accessing to device"
exit 1
fi
diff --git a/tests/main/interfaces-browser-support/task.yaml b/tests/main/interfaces-browser-support/task.yaml
index 3ccaeeb39c..670ea73f34 100644
--- a/tests/main/interfaces-browser-support/task.yaml
+++ b/tests/main/interfaces-browser-support/task.yaml
@@ -106,62 +106,65 @@ execute: |
done
fi
- if [ "$(snap debug confinement)" = strict ] ; then
- if [ "$ALLOW_SANDBOX" = "false" ]; then
- echo "And the policy has the ptrace suppression rule without sandbox"
- MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.browser-support-consumer.cmd
- else
- echo "And the policy has the ptrace suppression rule with sandbox"
- MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.browser-support-consumer.cmd && echo "Found ptrace rule, but shouldn't have" && exit 1
- fi
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
- echo "And the resources available with sandbox are not reachable without it"
- if [ "$ALLOW_SANDBOX" = "false" ]; then
- for readable_file in $READABLE_WITH_SANDBOX_FILES; do
- if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2>"${PWD}/readable-without-sandbox-read.err"; then
- echo "Expected error without sandbox didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < readable-without-sandbox-read.err
- done
- fi
+ if [ "$ALLOW_SANDBOX" = "false" ]; then
+ echo "And the policy has the ptrace suppression rule without sandbox"
+ MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.browser-support-consumer.cmd
+ else
+ echo "And the policy has the ptrace suppression rule with sandbox"
+ MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.browser-support-consumer.cmd && echo "Found ptrace rule, but shouldn't have" && exit 1
+ fi
- echo "When the plug is disconnected"
- snap disconnect browser-support-consumer:browser-support
+ echo "And the resources available with sandbox are not reachable without it"
+ if [ "$ALLOW_SANDBOX" = "false" ]; then
+ for readable_file in $READABLE_WITH_SANDBOX_FILES; do
+ if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2> readable-without-sandbox-read.err; then
+ echo "Expected error without sandbox didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < readable-without-sandbox-read.err
+ done
+ fi
- echo "Then the snap is not able to access tmp"
- if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd ls /var/tmp/" test 2>"${PWD}/tmpdir-access.err"; then
+ echo "When the plug is disconnected"
+ snap disconnect browser-support-consumer:browser-support
+
+ echo "Then the snap is not able to access tmp"
+ if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd ls /var/tmp/" test 2> tmpdir-access.err; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < tmpdir-access.err
+ if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat /var/tmp/etilqs_test" test 2> tmpfile-read.err; then
+ echo "Expected error with disconnected plug didn't happen"
+ exit 1
+ fi
+ MATCH "Permission denied" < tmpfile-read.err
+
+ for owned_file in $OWNED_FILES; do
+ if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $owned_file" test 2> owned-read.err; then
echo "Expected error with disconnected plug didn't happen"
exit 1
fi
- MATCH "Permission denied" < tmpdir-access.err
- if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat /var/tmp/etilqs_test" test 2>"${PWD}/tmpfile-read.err"; then
+ MATCH "Permission denied" < owned-read.err
+ done
+ for readable_file in $READABLE_FILES; do
+ if [ -f "$readable_file" ] && su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2> readable-read.err; then
echo "Expected error with disconnected plug didn't happen"
exit 1
fi
- MATCH "Permission denied" < tmpfile-read.err
-
- for owned_file in $OWNED_FILES; do
- if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $owned_file" test 2>"${PWD}/owned-read.err"; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < owned-read.err
- done
- for readable_file in $READABLE_FILES; do
- if [ -f "$readable_file" ] && su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2>"${PWD}/readable-read.err"; then
+ MATCH "Permission denied" < readable-read.err
+ done
+ if [ "$ALLOW_SANDBOX" = "true" ]; then
+ for readable_file in $READABLE_WITH_SANDBOX_FILES; do
+ if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2> readable-with-sandbox-read.err; then
echo "Expected error with disconnected plug didn't happen"
exit 1
fi
- MATCH "Permission denied" < readable-read.err
+ MATCH "Permission denied" < readable-with-sandbox-read.err
done
- if [ "$ALLOW_SANDBOX" = "true" ]; then
- for readable_file in $READABLE_WITH_SANDBOX_FILES; do
- if su -l -c "$SNAP_MOUNT_DIR/bin/browser-support-consumer.cmd cat $readable_file" test 2>"${PWD}/readable-with-sandbox-read.err"; then
- echo "Expected error with disconnected plug didn't happen"
- exit 1
- fi
- MATCH "Permission denied" < readable-with-sandbox-read.err
- done
- fi
fi
+
diff --git a/tests/main/interfaces-contacts-service/task.yaml b/tests/main/interfaces-contacts-service/task.yaml
index 3109e931fa..4e2036e49e 100644
--- a/tests/main/interfaces-contacts-service/task.yaml
+++ b/tests/main/interfaces-contacts-service/task.yaml
@@ -65,29 +65,12 @@ execute: |
END:VCARD
EOF
- test-snapd-eds.contacts load test-address-book << EOF
- BEGIN:VCARD
- VERSION:3.0
- FN:John Doe
- N:Doe;John;;;
- EMAIL;type=WORK:john@example.com
- END:VCARD
- EOF
-
echo "We can also retrieve those contacts"
# Filter out ID and revision, which are unpredictable
test-snapd-eds.contacts list test-address-book | sed -E 's/^(UID|REV):.*/\1:.../' > /tmp/contacts.vcf
diff -uw - /tmp/contacts.vcf << EOF
BEGIN:VCARD
VERSION:3.0
- FN:John Doe
- N:Doe;John;;;
- EMAIL;type=WORK:john@example.com
- UID:...
- REV:...
- END:VCARD
- BEGIN:VCARD
- VERSION:3.0
FN:Fred Smith
N:Smith;Fred;;;
EMAIL;type=HOME:fred@example.org
diff --git a/tests/main/interfaces-dbus/task.yaml b/tests/main/interfaces-dbus/task.yaml
index b7723b176e..53b4f2c7b1 100644
--- a/tests/main/interfaces-dbus/task.yaml
+++ b/tests/main/interfaces-dbus/task.yaml
@@ -18,10 +18,10 @@ prepare: |
. "$TESTSLIB/dirs.sh"
echo "Give a snap declaring a dbus slot in installed"
- snap install --edge test-snapd-dbus-provider
+ snap install --beta test-snapd-dbus-provider
echo "And a snap declaring a matching dbus plug is installed"
- snap install --edge test-snapd-dbus-consumer
+ snap install --beta test-snapd-dbus-consumer
echo "And the provider dbus loop is started"
#shellcheck source=tests/lib/dbus.sh
@@ -45,19 +45,22 @@ execute: |
echo "And plug is disconnected by default"
snap interfaces -i dbus | MATCH '^- +test-snapd-dbus-consumer:dbus-test'
+ echo "When the plug is connected"
+ snap connect test-snapd-dbus-consumer:dbus-test test-snapd-dbus-provider:dbus-test
+
+ echo "Then the consumer is able to call the provided method"
+ test-snapd-dbus-consumer.dbus-consumer | MATCH "hello world"
+
if [ "$(snap debug confinement)" = partial ]; then
exit 0
fi
- echo "And the consumer is not able to access the provided method"
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-dbus-consumer:dbus-test test-snapd-dbus-provider:dbus-test
+
+ echo "The consumer is not able to access the provided method"
if test-snapd-dbus-consumer.dbus-consumer 2> call.error; then
echo "Expected permission error calling dbus method with disconnected plug"
exit 1
fi
MATCH "Permission denied" < call.error
-
- echo "When the plug is connected"
- snap connect test-snapd-dbus-consumer:dbus-test test-snapd-dbus-provider:dbus-test
-
- echo "Then the consumer is able to call the provided method"
- test-snapd-dbus-consumer.dbus-consumer | MATCH "hello world"
diff --git a/tests/main/interfaces-device-buttons/task.yaml b/tests/main/interfaces-device-buttons/task.yaml
index 5a4dbf422d..d7261a8cc0 100644
--- a/tests/main/interfaces-device-buttons/task.yaml
+++ b/tests/main/interfaces-device-buttons/task.yaml
@@ -49,7 +49,7 @@ execute: |
snap disconnect test-snapd-sh:device-buttons
echo "Then the snap is not able to read the input device"
- if test-snapd-sh.with-device-buttons-plug -c "cat /dev/input/event1" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-device-buttons-plug -c "cat /dev/input/event1" 2> call.error; then
echo "Expected permission error accessing to input device"
exit 1
fi
diff --git a/tests/main/interfaces-dvb/task.yaml b/tests/main/interfaces-dvb/task.yaml
index d25dadf27e..83987c3b82 100644
--- a/tests/main/interfaces-dvb/task.yaml
+++ b/tests/main/interfaces-dvb/task.yaml
@@ -35,13 +35,13 @@ execute: |
test-snapd-sh.with-dvb-plug -c "echo test >> /dev/dvb/adapter9/video9"
test-snapd-sh.with-dvb-plug -c "cat /run/udev/data/c212:9"
- echo "When the plug is disconnected"
- snap disconnect test-snapd-sh:dvb
-
if [ "$(snap debug confinement)" = partial ]; then
exit 0
fi
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-sh:dvb
+
echo "Then the snap is not able to read the input device"
if test-snapd-sh.with-dvb-plug -c 'cat /dev/dvb/adapter9/video9' 2>call.error; then
echo "Expected permission error accessing to input device"
diff --git a/tests/main/interfaces-fuse_support/task.yaml b/tests/main/interfaces-fuse_support/task.yaml
index 8399d984a1..11b07062f3 100644
--- a/tests/main/interfaces-fuse_support/task.yaml
+++ b/tests/main/interfaces-fuse_support/task.yaml
@@ -19,7 +19,7 @@ environment:
MOUNT_POINT_OUTSIDE/regular: /var/snap/test-snapd-fuse-consumer/current/mount_point
NAME/regular: test-snapd-fuse-consumer
# snap with instance key 'foo'
- MOUNT_POINT/parallel: /var/snap/test-snapd-fuse-consumer/current/mount_point
+ MOUNT_POINT/parallel: /var/snap/test-snapd-fuse-consumer_foo/current/mount_point
MOUNT_POINT_OUTSIDE/parallel: /var/snap/test-snapd-fuse-consumer_foo/current/mount_point
NAME/parallel: test-snapd-fuse-consumer_foo
@@ -50,8 +50,8 @@ execute: |
echo "The interface is disconnected by default"
snap interfaces -i fuse-support | MATCH "^- +$NAME:fuse-support"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "Then the snap is not able to create a fuse file system"
+ if [ "$(snap debug confinement)" = strict ]; then
+ echo "The snap is not able to create a fuse file system with the plug disconnected"
if "$NAME.create" -f "$MOUNT_POINT" 2> fuse.error; then
echo "Expected permission error creating fuse filesystem with disconnected plug"
exit 1
@@ -95,4 +95,3 @@ execute: |
mountpath=$(nsenter "--mount=/run/snapd/ns/$NAME.mnt" cat /proc/mounts | \
grep "$(basename "$MOUNT_POINT") fuse" | cut -f2 -d' ')
test -n "$mountpath"
-
diff --git a/tests/main/interfaces-hostname-control/task.yaml b/tests/main/interfaces-hostname-control/task.yaml
index 0982a41d8d..11b9cb5b39 100644
--- a/tests/main/interfaces-hostname-control/task.yaml
+++ b/tests/main/interfaces-hostname-control/task.yaml
@@ -38,22 +38,22 @@ execute: |
test-snapd-sh.with-hostname-control-plug -c "hostnamectl" | MATCH "$hostname"
test-snapd-sh.with-hostname-control-plug -c "hostnamectl set-hostname $hostname"
- echo "When the plug is disconnected"
- snap disconnect test-snapd-sh:hostname-control
-
if [ "$(snap debug confinement)" = partial ]; then
exit 0
fi
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-sh:hostname-control
+
echo "Then the hostname method cannot be accessed through dbus"
- if test-snapd-sh.with-hostname-control-plug -c "hostnamectl set-hostname $hostname" 2>access.error; then
+ if test-snapd-sh.with-hostname-control-plug -c "hostnamectl set-hostname $hostname" 2> access.error; then
echo "Expected permission error trying to set hostname with disconnected plug"
exit 1
fi
MATCH "Permission denied" < access.error
echo "And the /etc/hostname file cannot be written"
- if test-snapd-sh.with-hostname-control-plug -c "echo $hostname > /etc/hostname" 2>hostname.error; then
+ if test-snapd-sh.with-hostname-control-plug -c "echo $hostname > /etc/hostname" 2> hostname.error; then
echo "Expected permission error trying to acess to /etc/hostname with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-joystick/task.yaml b/tests/main/interfaces-joystick/task.yaml
index 714b480b1f..a1b5c0749e 100644
--- a/tests/main/interfaces-joystick/task.yaml
+++ b/tests/main/interfaces-joystick/task.yaml
@@ -59,7 +59,7 @@ execute: |
snap disconnect test-snapd-sh:joystick
echo "Then the snap is not able to read the input device"
- if test-snapd-sh.with-joystick-plug -c "cat /dev/input/js31" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-joystick-plug -c "cat /dev/input/js31" 2> call.error; then
echo "Expected permission error accessing to input device"
exit 1
fi
diff --git a/tests/main/interfaces-juju-client-observe/task.yaml b/tests/main/interfaces-juju-client-observe/task.yaml
index 9320b36095..db18c3bbb8 100644
--- a/tests/main/interfaces-juju-client-observe/task.yaml
+++ b/tests/main/interfaces-juju-client-observe/task.yaml
@@ -35,13 +35,13 @@ execute: |
echo "Then the snap is able to access the juju client configuration"
test-snapd-sh.with-juju-client-observe-plug -c "cat $HOME/.local/share/juju/juju.conf"
- echo "When the plug is disconnected"
- snap disconnect test-snapd-sh:juju-client-observe
-
if [ "$(snap debug confinement)" = partial ]; then
exit 0
fi
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-sh:juju-client-observe
+
echo "Then the snap is not able to read the juju client configuration"
if test-snapd-sh.with-juju-client-observe-plug -c "cat $HOME/.local/share/juju/juju.conf" 2>call.error; then
echo "Expected permission error accessing to input device"
diff --git a/tests/main/interfaces-kernel-module-control/task.yaml b/tests/main/interfaces-kernel-module-control/task.yaml
index c472fcfd8b..f2cd9dc60c 100644
--- a/tests/main/interfaces-kernel-module-control/task.yaml
+++ b/tests/main/interfaces-kernel-module-control/task.yaml
@@ -64,7 +64,7 @@ execute: |
generic-consumer.cmd ls /sys/module | MATCH "$MODULE"
echo "And the snap is not able to write to /sys/module"
- if su -l -c "generic-consumer.cmd touch /sys/module/test" test 2>"${PWD}/touch.error"; then
+ if su -l -c "generic-consumer.cmd touch /sys/module/test" test 2> touch.error; then
echo "Expected permission error writing to /sys/module"
exit 1
fi
@@ -83,7 +83,7 @@ execute: |
snap disconnect generic-consumer:kernel-module-control
echo "Then the snap is not able to list modules"
- if su -l -c "test-snapd-kernel-module-consumer.lsmod" test 2>"${PWD}/list.error"; then
+ if su -l -c "test-snapd-kernel-module-consumer.lsmod" test 2> list.error; then
echo "Expected permission error listing modules with disconnected plug"
exit 1
fi
@@ -107,14 +107,14 @@ execute: |
MATCH "Permission denied" < remove.error
echo "And the snap is not able to read /sys/module"
- if su -l -c "generic-consumer.cmd ls /sys/module" test 2>"${PWD}/read.error"; then
+ if su -l -c "generic-consumer.cmd ls /sys/module" test 2> read.error; then
echo "Expected permission error reading /sys/module with disconnected plug"
exit 1
fi
MATCH "Permission denied" < read.error
echo "And the snap is not able to write to /sys/module"
- if su -l -c "generic-consumer.cmd touch /sys/module/test" test 2>"${PWD}/touch.error"; then
+ if su -l -c "generic-consumer.cmd touch /sys/module/test" test 2> touch.error; then
echo "Expected permission error writing to /sys/module"
exit 1
fi
diff --git a/tests/main/interfaces-libvirt/task.yaml b/tests/main/interfaces-libvirt/task.yaml
index 5337a3ef55..7b003ae868 100644
--- a/tests/main/interfaces-libvirt/task.yaml
+++ b/tests/main/interfaces-libvirt/task.yaml
@@ -62,7 +62,7 @@ execute: |
snap disconnect test-snapd-libvirt-consumer:libvirt
echo "Then the snap is not able to create a domain"
- if su -l -c "test-snapd-libvirt-consumer.machine-up" test 2>"${PWD}/creation.error"; then
+ if su -l -c "test-snapd-libvirt-consumer.machine-up" test 2> creation.error; then
echo "Expected permission error accessing libvirtd socket with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-locale-control/task.yaml b/tests/main/interfaces-locale-control/task.yaml
index 6d2630867b..c684d1b568 100644
--- a/tests/main/interfaces-locale-control/task.yaml
+++ b/tests/main/interfaces-locale-control/task.yaml
@@ -70,7 +70,7 @@ execute: |
snap disconnect locale-control-consumer:locale-control
echo "Then the snap is not able to read the locale configuration"
- if su -l -c "locale-control-consumer.get LANG" test 2>"${PWD}/locale-read.error"; then
+ if su -l -c "locale-control-consumer.get LANG" test 2> locale-read.error; then
echo "Expected permission error accessing locale configuration with disconnected plug"
exit 1
fi
@@ -84,14 +84,16 @@ execute: |
locale-control-consumer.set LANG mylang
grep -q "LANG=\"mylang\"" /etc/default/locale
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect locale-control-consumer:locale-control
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
- echo "Then the snap is not able to read the locale configuration"
- if locale-control-consumer.set LANG mysecondlang 2> locale-write.error; then
- echo "Expected permission error accessing locale configuration with disconnected plug"
- exit 1
- fi
- grep -q "Permission denied" locale-write.error
+ echo "When the plug is disconnected"
+ snap disconnect locale-control-consumer:locale-control
+
+ echo "Then the snap is not able to read the locale configuration"
+ if locale-control-consumer.set LANG mysecondlang 2> locale-write.error; then
+ echo "Expected permission error accessing locale configuration with disconnected plug"
+ exit 1
fi
+ grep -q "Permission denied" locale-write.error
diff --git a/tests/main/interfaces-location-control/task.yaml b/tests/main/interfaces-location-control/task.yaml
index 42552f510d..d18381c82b 100644
--- a/tests/main/interfaces-location-control/task.yaml
+++ b/tests/main/interfaces-location-control/task.yaml
@@ -55,7 +55,7 @@ execute: |
snap disconnect test-snapd-location-control-provider:location-control test-snapd-location-control-provider:location-control-test
echo "And the location provider props cannot be accessed"
- if test-snapd-location-control-provider.consumer Get 2>"${PWD}"/getprop.error; then
+ if test-snapd-location-control-provider.consumer Get 2> getprop.error; then
echo "Expected permission error trying to get props with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-mount-observe/task.yaml b/tests/main/interfaces-mount-observe/task.yaml
index 0231d56313..b31a730631 100644
--- a/tests/main/interfaces-mount-observe/task.yaml
+++ b/tests/main/interfaces-mount-observe/task.yaml
@@ -32,17 +32,6 @@ execute: |
expected="$SNAP_MOUNT_DIR/mount-observe-consumer"
su -l -c "mount-observe-consumer" test | grep -Pq "$expected"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect mount-observe-consumer:mount-observe
-
- echo "Then the mount info is not reachable"
- if su -l -c "mount-observe-consumer" test; then
- echo "Expected error accessing mount info with disconnected plug"
- exit 1
- fi
- fi
-
echo "When the plug is connected"
snap connect mount-observe-consumer:mount-observe
@@ -54,3 +43,16 @@ execute: |
echo "Then the new mount info is reachable"
expected="$SNAP_MOUNT_DIR/test-snapd-tools"
su -l -c "mount-observe-consumer" test | grep -Pq "$expected"
+
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "When the plug is disconnected"
+ snap disconnect mount-observe-consumer:mount-observe
+
+ echo "Then the mount info is not reachable"
+ if su -l -c "mount-observe-consumer" test; then
+ echo "Expected error accessing mount info with disconnected plug"
+ exit 1
+ fi
diff --git a/tests/main/interfaces-network-control-tuntap/task.yaml b/tests/main/interfaces-network-control-tuntap/task.yaml
index 5c61433112..6af57ab3dd 100644
--- a/tests/main/interfaces-network-control-tuntap/task.yaml
+++ b/tests/main/interfaces-network-control-tuntap/task.yaml
@@ -31,13 +31,15 @@ execute: |
echo "The interface is disconnected by default"
snap interfaces -i network-control | MATCH -- '^- +test-snapd-tuntap:network-control$'
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "And cannot allocate the $DEV device"
- test-snapd-tuntap.tuntap "$DEV" 2>&1 | MATCH "Permission denied"
- fi
-
echo "When the plug is connected"
snap connect test-snapd-tuntap:network-control
echo "Then the snap command can allocate the $DEV device"
test-snapd-tuntap.tuntap "$DEV" | MATCH "PASS"
+
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "And cannot allocate the $DEV device"
+ test-snapd-tuntap.tuntap "$DEV" 2>&1 | MATCH "Permission denied"
diff --git a/tests/main/interfaces-network-manager/task.yaml b/tests/main/interfaces-network-manager/task.yaml
index abadb693cd..d6c61b2690 100644
--- a/tests/main/interfaces-network-manager/task.yaml
+++ b/tests/main/interfaces-network-manager/task.yaml
@@ -46,7 +46,7 @@ execute: |
snap disconnect network-manager:nmcli
echo "Then the consumer is not able to access the provided methods"
- if network-manager.nmcli general 2>"${PWD}"/call.error; then
+ if network-manager.nmcli general 2> call.error; then
echo "Expected permission error calling nmcli method with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-network-observe/task.yaml b/tests/main/interfaces-network-observe/task.yaml
index 14a4b8cd61..c683d48cf2 100644
--- a/tests/main/interfaces-network-observe/task.yaml
+++ b/tests/main/interfaces-network-observe/task.yaml
@@ -48,13 +48,16 @@ execute: |
echo "Then the snap command can query network status information"
network-observe-consumer | MATCH "LISTEN.*:$PORT"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect network-observe-consumer:network-observe
-
- echo "Then the snap command can not query network status information"
- if network-observe-consumer 2>net-query.output; then
- echo "Expected error caling command with disconnected plug"
- fi
- grep -Pq "Permission denied" < net-query.output
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
fi
+
+ echo "When the plug is disconnected"
+ snap disconnect network-observe-consumer:network-observe
+
+ echo "Then the snap command can not query network status information"
+ if network-observe-consumer 2> net-query.output; then
+ echo "Expected error caling command with disconnected plug"
+ fi
+ grep -Pq "Permission denied" < net-query.output
+
diff --git a/tests/main/interfaces-network-status/task.yaml b/tests/main/interfaces-network-status/task.yaml
index dc23975ab6..e681c54314 100644
--- a/tests/main/interfaces-network-status/task.yaml
+++ b/tests/main/interfaces-network-status/task.yaml
@@ -53,7 +53,7 @@ execute: |
snap disconnect test-snapd-network-status-provider:network-status test-snapd-network-status-provider:network-status-test
echo "And the snap state cannot be accessed"
- if test-snapd-network-status-provider.consumer GetState 2>"${PWD}"/getstate.error; then
+ if test-snapd-network-status-provider.consumer GetState 2> getstate.error; then
echo "Expected permission error trying to introspect state with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-openvswitch-support/task.yaml b/tests/main/interfaces-openvswitch-support/task.yaml
index 07f3d834e3..756bbd5f28 100644
--- a/tests/main/interfaces-openvswitch-support/task.yaml
+++ b/tests/main/interfaces-openvswitch-support/task.yaml
@@ -36,7 +36,7 @@ execute: |
snap disconnect test-snapd-openvswitch-support:openvswitch-support
echo "Then the snap is not able to get a random uuid"
- if test-snapd-openvswitch-support.random-uuid 2>"${PWD}"/call.error; then
+ if test-snapd-openvswitch-support.random-uuid 2> call.error; then
echo "Expected permission error getting random uuid"
exit 1
fi
diff --git a/tests/main/interfaces-openvswitch/task.yaml b/tests/main/interfaces-openvswitch/task.yaml
index 3ded271425..d06c810c22 100644
--- a/tests/main/interfaces-openvswitch/task.yaml
+++ b/tests/main/interfaces-openvswitch/task.yaml
@@ -79,7 +79,7 @@ execute: |
snap disconnect test-snapd-openvswitch-consumer:openvswitch
echo "Then the snap is not able to create a bridge"
- if test-snapd-openvswitch-consumer.ovs-vsctl add-br br0 2>"${PWD}"/bridge-creation.error; then
+ if test-snapd-openvswitch-consumer.ovs-vsctl add-br br0 2> bridge-creation.error; then
echo "Expected permission error accessing openvswitch socket with disconnected plug"
exit 1
fi
@@ -88,7 +88,7 @@ execute: |
ovs-vsctl add-br br0
echo "And the snap is not able to create a port"
- if test-snapd-openvswitch-consumer.ovs-vsctl add-port br0 tap1 2>"${PWD}"/port-creation.error; then
+ if test-snapd-openvswitch-consumer.ovs-vsctl add-port br0 tap1 2> port-creation.error; then
echo "Expected permission error accessing openvswitch socket with disconnected plug"
exit 1
fi
@@ -97,14 +97,14 @@ execute: |
ovs-vsctl add-port br0 tap1
echo "And the snap is not able to delete a port"
- if test-snapd-openvswitch-consumer.ovs-vsctl del-port br0 tap1 2>"${PWD}"/port-deletion.error; then
+ if test-snapd-openvswitch-consumer.ovs-vsctl del-port br0 tap1 2> port-deletion.error; then
echo "Expected permission error accessing openvswitch socket with disconnected plug"
exit 1
fi
MATCH 'database connection failed \(Permission denied\)' < port-deletion.error
echo "And the snap is not able to delete a bridge"
- if test-snapd-openvswitch-consumer.ovs-vsctl del-br br0 2>"${PWD}"/br-creation.error; then
+ if test-snapd-openvswitch-consumer.ovs-vsctl del-br br0 2> br-creation.error; then
echo "Expected permission error accessing openvswitch socket with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-physical-memory-observe/task.yaml b/tests/main/interfaces-physical-memory-observe/task.yaml
index 39c4885640..fd4e9a530e 100644
--- a/tests/main/interfaces-physical-memory-observe/task.yaml
+++ b/tests/main/interfaces-physical-memory-observe/task.yaml
@@ -41,7 +41,7 @@ execute: |
snap disconnect test-snapd-physical-memory-observe:physical-memory-observe
echo "Then the snap is not able to access the physical memory"
- if test-snapd-physical-memory-observe.head 2>"${PWD}"/call.error; then
+ if test-snapd-physical-memory-observe.head 2> call.error; then
echo "Expected permission error accessing to physical memory with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-process-control/task.yaml b/tests/main/interfaces-process-control/task.yaml
index 3d725ecaa9..539bae1e53 100644
--- a/tests/main/interfaces-process-control/task.yaml
+++ b/tests/main/interfaces-process-control/task.yaml
@@ -45,7 +45,7 @@ execute: |
echo "Then the snap is not able to kill an existing process"
while :; do sleep 1; done &
pid=$!
- if process-control-consumer.signal SIGTERM $pid 2>"${PWD}"/process-kill.error; then
+ if process-control-consumer.signal SIGTERM $pid 2> process-kill.error; then
echo "Expected permission error accessing killing a process with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-raw-usb/task.yaml b/tests/main/interfaces-raw-usb/task.yaml
index 44136fb224..777164a752 100644
--- a/tests/main/interfaces-raw-usb/task.yaml
+++ b/tests/main/interfaces-raw-usb/task.yaml
@@ -42,7 +42,7 @@ execute: |
snap disconnect test-snapd-sh:raw-usb
echo "Then the snap is not able to check for USB devices"
- if test-snapd-sh.with-raw-usb-plug -c "cat /sys/bus/usb/devices/usb1/serial" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-raw-usb-plug -c "cat /sys/bus/usb/devices/usb1/serial" 2> call.error; then
echo "Expected permission error accessing to check for USB devices"
exit 1
fi
diff --git a/tests/main/interfaces-removable-media/task.yaml b/tests/main/interfaces-removable-media/task.yaml
index 722474282f..e6be78d62e 100644
--- a/tests/main/interfaces-removable-media/task.yaml
+++ b/tests/main/interfaces-removable-media/task.yaml
@@ -74,7 +74,7 @@ execute: |
snap disconnect test-snapd-sh:removable-media
echo "Then the snap is not able to access read the test media file"
- if test-snapd-sh.with-removable-media-plug -c "ls /run" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-removable-media-plug -c "ls /run" 2> call.error; then
echo "Expected permission error accessing to removable storage filesystems"
exit 1
fi
diff --git a/tests/main/interfaces-shutdown-introspection/task.yaml b/tests/main/interfaces-shutdown-introspection/task.yaml
index 9045dc71f5..0aa39b96b1 100644
--- a/tests/main/interfaces-shutdown-introspection/task.yaml
+++ b/tests/main/interfaces-shutdown-introspection/task.yaml
@@ -26,13 +26,13 @@ execute: |
expected="<interface name=\"org.freedesktop.login1.Manager\">"
su -l -c "shutdown-introspection-consumer" test | MATCH "$expected"
- echo "When the plug is disconnected"
- snap disconnect shutdown-introspection-consumer:shutdown
-
if [ "$(snap debug confinement)" = partial ]; then
exit
fi
+ echo "When the plug is disconnected"
+ snap disconnect shutdown-introspection-consumer:shutdown
+
echo "Then the snap is not able to get system information"
if su -l -c "shutdown-introspection-consumer" test; then
echo "Expected error with plug disconnected"
diff --git a/tests/main/interfaces-snapd-control/task.yaml b/tests/main/interfaces-snapd-control/task.yaml
index 218fb3bcc1..32ad0d2664 100644
--- a/tests/main/interfaces-snapd-control/task.yaml
+++ b/tests/main/interfaces-snapd-control/task.yaml
@@ -22,22 +22,21 @@ execute: |
echo "The interface is connected by default"
snap interfaces -i snapd-control | MATCH ":snapd-control .*test-snapd-control-consumer"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect test-snapd-control-consumer:snapd-control
-
- echo "Then the snap command is not able to control snapd"
- if test-snapd-control-consumer.list 2>snapd.error; then
- echo "Expected error with plug disconnected"
- exit 1
- fi
- grep -q "Permission denied" snapd.error
- fi
-
- echo "When the plug is connected"
- snap connect test-snapd-control-consumer:snapd-control
-
echo "Then the snap command is able to control snapd"
! test-snapd-control-consumer.list | grep -q test-snapd-tools
test-snapd-control-consumer.install test-snapd-tools
while ! test-snapd-control-consumer.list | grep -q test-snapd-tools; do sleep 1; done
+
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-control-consumer:snapd-control
+
+ echo "Then the snap command is not able to control snapd"
+ if test-snapd-control-consumer.list 2> snapd.error; then
+ echo "Expected error with plug disconnected"
+ exit 1
+ fi
+ grep -q "Permission denied" snapd.error
diff --git a/tests/main/interfaces-ssh-keys/task.yaml b/tests/main/interfaces-ssh-keys/task.yaml
index 54408d9931..fc02b17f66 100644
--- a/tests/main/interfaces-ssh-keys/task.yaml
+++ b/tests/main/interfaces-ssh-keys/task.yaml
@@ -50,7 +50,7 @@ execute: |
snap disconnect test-snapd-sh:ssh-keys
echo "Then the snap is not able to read a ssh private key"
- if test-snapd-sh.with-ssh-keys-plug -c "cat $TESTKEY" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-ssh-keys-plug -c "cat $TESTKEY" 2> call.error; then
echo "Expected permission error accessing to ssh"
exit 1
fi
diff --git a/tests/main/interfaces-ssh-public-keys/task.yaml b/tests/main/interfaces-ssh-public-keys/task.yaml
index 66e02aafd8..7c2a052eff 100644
--- a/tests/main/interfaces-ssh-public-keys/task.yaml
+++ b/tests/main/interfaces-ssh-public-keys/task.yaml
@@ -46,7 +46,7 @@ execute: |
fi
echo "And then the snap is not able to access to private keys"
- if test-snapd-sh.with-ssh-public-keys-plug -c "cat $TESTKEY" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-ssh-public-keys-plug -c "cat $TESTKEY" 2> call.error; then
echo "Expected permission error accessing to ssh"
exit 1
fi
@@ -56,7 +56,7 @@ execute: |
snap disconnect test-snapd-sh:ssh-public-keys
echo "Then the snap is not able to access the ssh public keys"
- if test-snapd-sh.with-ssh-public-keys-plug -c "cat $TESTKEY.pub" 2>"${PWD}"/call.error; then
+ if test-snapd-sh.with-ssh-public-keys-plug -c "cat $TESTKEY.pub" 2> call.error; then
echo "Expected permission error accessing to ssh"
exit 1
fi
diff --git a/tests/main/interfaces-system-dbus/task.yaml b/tests/main/interfaces-system-dbus/task.yaml
new file mode 100644
index 0000000000..7290c1772b
--- /dev/null
+++ b/tests/main/interfaces-system-dbus/task.yaml
@@ -0,0 +1,50 @@
+summary: Ensure that the dbus interface works on the system bus.
+
+prepare: |
+ echo "Install dbus system test snaps"
+ snap install --edge test-snapd-dbus-provider
+ snap install --edge test-snapd-dbus-consumer
+ # we can only talk from an unconfied dbus-send to the service on classic,
+ # on ubuntu-core devices *all* dbus calls are mediated.
+ echo "Ensure the dbus service is up"
+ if ! grep -q ID=ubuntu-core /etc/os-release; then
+ dbus-send --system --print-reply --dest=com.dbustest.HelloWorld /com/dbustest/HelloWorld com.dbustest.HelloWorld.SayHello | MATCH "hello world"
+ fi
+
+execute: |
+ #shellcheck source=tests/lib/dirs.sh
+ . "$TESTSLIB/dirs.sh"
+
+ echo "The dbus consumer plug is disconnected by default"
+ snap interfaces -i dbus | MATCH '^- +test-snapd-dbus-consumer:dbus-system-test'
+
+ echo "When the plug is connected"
+ snap connect test-snapd-dbus-consumer:dbus-system-test test-snapd-dbus-provider:dbus-system-test
+
+ echo "Then the consumer is able to call the provided method"
+ test-snapd-dbus-consumer.dbus-system-consumer | MATCH "hello world"
+
+ echo "Also check if dbus works if plug/slot come from the same snap"
+
+ echo "When the plug is connected (same snap)"
+ snap connect test-snapd-dbus-provider:dbus-system-test-plug test-snapd-dbus-provider:dbus-system-test
+ echo "Then the consumer (same snap) is able to call the provided method"
+ test-snapd-dbus-provider.system-consumer | MATCH "hello world"
+ echo "And the connection to the other snap still works"
+ test-snapd-dbus-consumer.dbus-system-consumer | MATCH "hello world"
+
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "When the plugs are disconnected"
+ snap disconnect test-snapd-dbus-consumer:dbus-system-test
+ snap disconnect test-snapd-dbus-provider:dbus-system-test-plug
+
+ echo "And the consumer is not able to access the provided method"
+ ! test-snapd-dbus-consumer.dbus-system-consumer 2> call.error
+ MATCH "Permission denied" < call.error
+
+ echo "And the consumer is not able to access the provided method (same snap)"
+ ! test-snapd-dbus-provider.system-consumer 2> call.error
+ MATCH "Permission denied" < call.error
diff --git a/tests/main/interfaces-system-observe/task.yaml b/tests/main/interfaces-system-observe/task.yaml
index 8d469724c9..892adb37ab 100644
--- a/tests/main/interfaces-system-observe/task.yaml
+++ b/tests/main/interfaces-system-observe/task.yaml
@@ -42,26 +42,29 @@ execute: |
su -l -c "test-snapd-system-observe-consumer.dbus-introspect" test | MATCH "$expected"
fi
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "And the policy has the ptrace suppression rule"
- MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.test-snapd-system-observe-consumer.consumer
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "And the policy has the ptrace suppression rule"
+ MATCH '^deny ptrace \(trace\),' < /var/lib/snapd/apparmor/profiles/snap.test-snapd-system-observe-consumer.consumer
- echo "When the plug is disconnected"
- snap disconnect test-snapd-system-observe-consumer:system-observe
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-system-observe-consumer:system-observe
+
+ echo "Then the snap is not able to get system information"
+ if su -l -c "test-snapd-system-observe-consumer.consumer" test 2> consumer.error; then
+ echo "Expected error with plug disconnected"
+ exit 1
+ fi
+ MATCH "Permission denied" < consumer.error
- echo "Then the snap is not able to get system information"
- if su -l -c "test-snapd-system-observe-consumer.consumer" test 2>"${PWD}/consumer.error"; then
+ if [[ "$SPREAD_SYSTEM" != ubuntu-14.04-* ]]; then
+ echo "And the snap is not able to introspect hostname1"
+ if su -l -c "test-snapd-system-observe-consumer.dbus-introspect" test 2> introspect.error; then
echo "Expected error with plug disconnected"
exit 1
fi
- MATCH "Permission denied" < consumer.error
-
- if [[ "$SPREAD_SYSTEM" != ubuntu-14.04-* ]]; then
- echo "And the snap is not able to introspect hostname1"
- if su -l -c "test-snapd-system-observe-consumer.dbus-introspect" test 2>"${PWD}/introspect.error"; then
- echo "Expected error with plug disconnected"
- exit 1
- fi
- MATCH "Permission denied" < introspect.error
- fi
+ MATCH "Permission denied" < introspect.error
fi
+
diff --git a/tests/main/interfaces-time-control/task.yaml b/tests/main/interfaces-time-control/task.yaml
index c42b4fde6b..dbb422669e 100644
--- a/tests/main/interfaces-time-control/task.yaml
+++ b/tests/main/interfaces-time-control/task.yaml
@@ -30,24 +30,24 @@ execute: |
echo "The interface is disconnected by default"
snap interfaces -i time-control | MATCH -- '^- +test-snapd-timedate-control-consumer:time-control'
- # When the interface is connected
+ echo "When the interface is connected"
snap connect test-snapd-timedate-control-consumer:time-control
- # Read/write to hwclock access should be possible
+ echo "Then read/write hwclock access should be possible"
test-snapd-timedate-control-consumer.hwclock-time -r -f /dev/rtc
test-snapd-timedate-control-consumer.hwclock-time --systohc -f /dev/rtc
- # Read access should be possible
+ echo "And read access should be possible"
test-snapd-timedate-control-consumer.timedatectl-time status | MATCH "RTC in local TZ:"
- # Save the RTC initial value
+ echo "And writing the initial value to RTC should be possible"
test-snapd-timedate-control-consumer.timedatectl-time status | grep -oP 'RTC in local TZ: \K(.*)' > rtc.txt
- # Set the local rtc and check the status
+ echo "And reading/writing local RTC status should be possible"
test-snapd-timedate-control-consumer.timedatectl-time set-local-rtc yes
[ "$(test-snapd-timedate-control-consumer.timedatectl-time status | grep -oP 'RTC in local TZ: \K(.*)')" = "yes" ]
- # Re-set the local rtc and check the status
+ echo "And resetting the local rtc status should be possible (reading the status is implied AIUI)"
test-snapd-timedate-control-consumer.timedatectl-time set-local-rtc no
[ "$(test-snapd-timedate-control-consumer.timedatectl-time status | grep -oP 'RTC in local TZ: \K(.*)')" = "no" ]
@@ -55,9 +55,11 @@ execute: |
exit 0
fi
- # Disconnect the interface and check access to timedatectl status
+ echo "When the plug is disconnected"
snap disconnect test-snapd-timedate-control-consumer:time-control
- if test-snapd-timedate-control-consumer.timedatectl-time status 2>"${PWD}"/call.error; then
+
+ echo "The timedatectl status cannot be retrieved"
+ if test-snapd-timedate-control-consumer.timedatectl-time status 2> call.error; then
echo "Expected permission error calling timedatectl status with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-timeserver-control/task.yaml b/tests/main/interfaces-timeserver-control/task.yaml
index f8164e8ad0..23828fdca3 100644
--- a/tests/main/interfaces-timeserver-control/task.yaml
+++ b/tests/main/interfaces-timeserver-control/task.yaml
@@ -64,8 +64,10 @@ execute: |
exit 0
fi
- # Disconnect the interface and check access to timedatectl status
+ echo "When the plug is disconnected"
snap disconnect test-snapd-timedate-control-consumer:timeserver-control
+
+ echo "The timedatectl status cannot be retrieved"
if test-snapd-timedate-control-consumer.timedatectl-timeserver status 2> call.error; then
echo "Expected permission error calling timedatectl status with disconnected plug"
exit 1
diff --git a/tests/main/interfaces-timezone-control/task.yaml b/tests/main/interfaces-timezone-control/task.yaml
index b58c73351f..e53f5c2178 100644
--- a/tests/main/interfaces-timezone-control/task.yaml
+++ b/tests/main/interfaces-timezone-control/task.yaml
@@ -54,9 +54,11 @@ execute: |
exit 0
fi
- # Disconnect the interface and check access to timedatectl status
+ echo "When the plug is disconnected"
snap disconnect test-snapd-timedate-control-consumer:timezone-control
- if test-snapd-timedate-control-consumer.timedatectl-timezone status 2>"${PWD}"/call.error; then
+
+ echo "The timedatectl status cannot be retrieved"
+ if test-snapd-timedate-control-consumer.timedatectl-timezone status 2> call.error; then
echo "Expected permission error calling timedatectl status with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-uhid/task.yaml b/tests/main/interfaces-uhid/task.yaml
index 6bbe33bf25..6c093540fe 100644
--- a/tests/main/interfaces-uhid/task.yaml
+++ b/tests/main/interfaces-uhid/task.yaml
@@ -35,7 +35,7 @@ execute: |
snap disconnect test-snapd-uhid:uhid
echo "Then the snap is not able to create/destroy a device on /dev/uhid"
- if test-snapd-uhid.test-device 2>"${PWD}"/call.error; then
+ if test-snapd-uhid.test-device 2> call.error; then
echo "Expected permission error calling uhid with disconnected plug"
exit 1
fi
diff --git a/tests/main/interfaces-upower-observe/task.yaml b/tests/main/interfaces-upower-observe/task.yaml
index 3a89e594d9..f11ef5f000 100644
--- a/tests/main/interfaces-upower-observe/task.yaml
+++ b/tests/main/interfaces-upower-observe/task.yaml
@@ -44,14 +44,16 @@ execute: |
done
test-snapd-upower-observe-consumer.upower --dump | MATCH "$expected"
- if [ "$(snap debug confinement)" = strict ] ; then
- echo "When the plug is disconnected"
- snap disconnect test-snapd-upower-observe-consumer:upower-observe
-
- echo "Then the snap is not able to dump info about the upower devices"
- if test-snapd-upower-observe-consumer.upower --dump 2>"${PWD}"/upower.error; then
- echo "Expected permission error accessing upower info with disconnected plug"
- exit 1
- fi
- MATCH "Permission denied" < upower.error
+ if [ "$(snap debug confinement)" = partial ] ; then
+ exit 0
+ fi
+
+ echo "When the plug is disconnected"
+ snap disconnect test-snapd-upower-observe-consumer:upower-observe
+
+ echo "Then the snap is not able to dump info about the upower devices"
+ if test-snapd-upower-observe-consumer.upower --dump 2> upower.error; then
+ echo "Expected permission error accessing upower info with disconnected plug"
+ exit 1
fi
+ MATCH "Permission denied" < upower.error
diff --git a/tests/main/searching/task.yaml b/tests/main/searching/task.yaml
index adb73201ae..5b606c519e 100644
--- a/tests/main/searching/task.yaml
+++ b/tests/main/searching/task.yaml
@@ -49,9 +49,12 @@ execute: |
# TODO: discuss with the store how we can make this test stable, i.e.
# that section/snap changes do not break us
if [ "$(uname -m)" = "x86_64" ]; then
- snap find --section=video vlc | MATCH vlc
+ snap find --section=photo-and-video vlc | MATCH vlc
else
- snap find --section=video vlc 2>&1 | MATCH 'No matching snaps'
+ # actual output:
+ # Name Version Publisher Notes Summary
+ # mjpg-streamer 2.0 ogra - UVC webcam streaming tool
+ snap find --section=photo-and-video vlc 2>&1 | MATCH -v vlc
fi
# LP: 1740605
diff --git a/tests/main/security-dev-input-event-denied/task.yaml b/tests/main/security-dev-input-event-denied/task.yaml
index 6e293aa977..9174fe5a59 100644
--- a/tests/main/security-dev-input-event-denied/task.yaml
+++ b/tests/main/security-dev-input-event-denied/task.yaml
@@ -47,7 +47,7 @@ execute: |
# 1. Joystick
echo "Then the snap is not able to access an evdev keyboard"
- if test-snapd-event "-event-kbd" 2>"${PWD}"/call.error; then
+ if test-snapd-event "-event-kbd" 2> call.error; then
echo "Expected permission error calling evtest with disconnected plug"
exit 1
fi
@@ -62,7 +62,7 @@ execute: |
# (evdev event*) and -joystick (js*)) and therefore shouldn't be added to
# the device cgroup when the joystick interface is plugged.
echo "Then the snap is still not able to access an evdev keyboard"
- if test-snapd-event "-event-kbd" 2>"${PWD}"/call.error; then
+ if test-snapd-event "-event-kbd" 2> call.error; then
echo "Expected permission error calling evtest with connected joystick plug"
exit 1
fi
@@ -80,7 +80,7 @@ execute: |
# 2. Device Buttons
echo "Then the snap is not able to access an evdev keyboard"
- if test-snapd-event "-event-kbd" 2>"${PWD}"/call.error; then
+ if test-snapd-event "-event-kbd" 2> call.error; then
echo "Expected permission error calling evtest with disconnected plug"
exit 1
fi
@@ -95,7 +95,7 @@ execute: |
# -gpio-keys-event (evdev event*) and therefore shouldn't be added to
# the device cgroup when the device-buttons interface is plugged.
echo "Then the snap is still not able to access an evdev keyboard"
- if test-snapd-event "-event-kbd" 2>"${PWD}"/call.error; then
+ if test-snapd-event "-event-kbd" 2> call.error; then
echo "Expected permission error calling evtest with connected device-buttons plug"
exit 1
fi
diff --git a/tests/main/security-udev-input-subsystem/task.yaml b/tests/main/security-udev-input-subsystem/task.yaml
index 4fbdb540bb..fbe1e9c7d5 100644
--- a/tests/main/security-udev-input-subsystem/task.yaml
+++ b/tests/main/security-udev-input-subsystem/task.yaml
@@ -38,7 +38,7 @@ execute: |
fi
echo "The snap's plug cannot access an evdev keyboard when connected"
- if test-snapd-udev-input-subsystem.plug 2>"${PWD}"/call.error; then
+ if test-snapd-udev-input-subsystem.plug 2> call.error; then
echo "Expected permission error with connected plug"
exit 1
fi
@@ -50,7 +50,7 @@ execute: |
snap interfaces -i mir | MATCH -- '- +test-snapd-udev-input-subsystem:mir-plug'
echo "The snap's plug still cannot access an evdev keyboard"
- if test-snapd-udev-input-subsystem.plug 2>"${PWD}"/call.error; then
+ if test-snapd-udev-input-subsystem.plug 2> call.error; then
echo "Expected permission error with disconnected plug"
exit 1
fi
@@ -61,7 +61,7 @@ execute: |
snap interfaces -i time-control | MATCH ':time-control +-'
echo "The snap's time-control plug cannot access an evdev keyboard when disconnected"
- if test-snapd-udev-input-subsystem.plug-with-time-control 2>"${PWD}"/call.error; then
+ if test-snapd-udev-input-subsystem.plug-with-time-control 2> call.error; then
echo "Expected permission error with disconnected time-control plug"
exit 1
fi
@@ -73,7 +73,7 @@ execute: |
snap interfaces -i time-control | MATCH ":time-control +test-snapd-udev-input-subsystem"
echo "The snap's plug still cannot access an evdev keyboard"
- if test-snapd-udev-input-subsystem.plug-with-time-control 2>"${PWD}"/call.error; then
+ if test-snapd-udev-input-subsystem.plug-with-time-control 2> call.error; then
echo "Expected permission error with disconnected time-control plug"
exit 1
fi
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 5fe112ed13..c383e5dd57 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -171,12 +171,6 @@
"revisionTime": "2017-06-28T23:42:41Z"
},
{
- "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=",
- "path": "golang.org/x/net/context/ctxhttp",
- "revision": "c81e7f25cb61200d8bf0ae971a0bac8cb638d5bc",
- "revisionTime": "2017-06-28T23:42:41Z"
- },
- {
"checksumSHA1": "9gypWnVZJEaH3jMK9KqOp4xgQD4=",
"path": "gopkg.in/check.v1",
"revision": "788fd78401277ebd861206a03c884797c6ec5541",