@@ -120,6 +120,25 @@ slots:
}
}
+func (s *ContentSuite ) TestSanitizeSlotSourceAndLegacy (c *C ) {
+ slot := MockSlot (c, ` name: snap
+slots:
+ content:
+ source:
+ write: [$SNAP_DATA/stuff]
+ read: [$SNAP/shared]
+` , nil , " content" )
+ c.Assert (slot.Sanitize (s.iface ), ErrorMatches, ` move the "read" attribute into the "source" section` )
+ slot = MockSlot (c, ` name: snap
+slots:
+ content:
+ source:
+ read: [$SNAP/shared]
+ write: [$SNAP_DATA/stuff]
+` , nil , " content" )
+ c.Assert (slot.Sanitize (s.iface ), ErrorMatches, ` move the "write" attribute into the "source" section` )
+}
+
func (s *ContentSuite ) TestSanitizePlugSimple (c *C ) {
const mockSnapYaml = ` name: content-slot-snap
version: 1.0
@@ -376,3 +395,209 @@ slots:
func (s *ContentSuite ) TestInterfaces (c *C ) {
c.Check (builtin.Interfaces (), testutil.DeepContains , s.iface )
}
+
+func (s *ContentSuite ) TestModernContentInterface (c *C ) {
+ plug := MockPlug (c, ` name: consumer
+plugs:
+ content:
+ target: $SNAP_COMMON/import
+apps:
+ app:
+ command: foo
+` , &snap.SideInfo {Revision: snap.R (1 )}, " content" )
+
+ slot := MockSlot (c, ` name: producer
+slots:
+ content:
+ source:
+ read:
+ - $SNAP_COMMON/read-common
+ - $SNAP_DATA/read-data
+ - $SNAP/read-snap
+ write:
+ - $SNAP_COMMON/write-common
+ - $SNAP_DATA/write-data
+` , &snap.SideInfo {Revision: snap.R (2 )}, " content" )
+
+ // Create the mount and apparmor specifications.
+ mountSpec := &mount.Specification {}
+ c.Assert (mountSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+ apparmorSpec := &apparmor.Specification {}
+ c.Assert (apparmorSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+
+ // Analyze the mount specification.
+ expectedMnt := []mount.Entry {{
+ Name: " /var/snap/producer/common/read-common" ,
+ Dir: " /var/snap/consumer/common/import/read-common" ,
+ Options: []string {" bind" , " ro" },
+ }, {
+ Name: " /var/snap/producer/2/read-data" ,
+ Dir: " /var/snap/consumer/common/import/read-data" ,
+ Options: []string {" bind" , " ro" },
+ }, {
+ Name: " /snap/producer/2/read-snap" ,
+ Dir: " /var/snap/consumer/common/import/read-snap" ,
+ Options: []string {" bind" , " ro" },
+ }, {
+ Name: " /var/snap/producer/common/write-common" ,
+ Dir: " /var/snap/consumer/common/import/write-common" ,
+ Options: []string {" bind" },
+ }, {
+ Name: " /var/snap/producer/2/write-data" ,
+ Dir: " /var/snap/consumer/common/import/write-data" ,
+ Options: []string {" bind" },
+ }}
+ c.Assert (mountSpec.MountEntries (), DeepEquals, expectedMnt)
+
+ // Analyze the apparmor specification.
+ c.Assert (apparmorSpec.SecurityTags (), DeepEquals, []string {" snap.consumer.app" })
+ expected := `
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files. Due
+# to a limitation in the kernel's LSM hooks for AF_UNIX, these
+# are needed for using named sockets within the exported
+# directory.
+/var/snap/producer/common/write-common/** mrwklix,
+/var/snap/producer/2/write-data/** mrwklix,
+
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files
+# read-only.
+/var/snap/producer/common/read-common/** mrkix,
+/var/snap/producer/2/read-data/** mrkix,
+/snap/producer/2/read-snap/** mrkix,
+`
+ c.Assert (apparmorSpec.SnippetForTag (" snap.consumer.app" ), Equals, expected)
+}
+
+func (s *ContentSuite ) TestModernContentInterfacePlugins (c *C ) {
+ // Define one app snap and two snaps plugin snaps.
+ plug := MockPlug (c, ` name: app
+plugs:
+ plugins:
+ interface: content
+ content: plugin-for-app
+ target: $SNAP/plugins
+apps:
+ app:
+ command: foo
+
+` , &snap.SideInfo {Revision: snap.R (1 )}, " plugins" )
+
+ // XXX: realistically the plugin may be a single file and we don't support
+ // those very well.
+ slotOne := MockSlot (c, ` name: plugin-one
+slots:
+ plugin-for-app:
+ interface: content
+ source:
+ read: [$SNAP/plugin]
+` , &snap.SideInfo {Revision: snap.R (1 )}, " plugin-for-app" )
+
+ slotTwo := MockSlot (c, ` name: plugin-two
+slots:
+ plugin-for-app:
+ interface: content
+ source:
+ read: [$SNAP/plugin]
+` , &snap.SideInfo {Revision: snap.R (1 )}, " plugin-for-app" )
+
+ // Create the mount and apparmor specifications.
+ mountSpec := &mount.Specification {}
+ apparmorSpec := &apparmor.Specification {}
+ for _ , slot := range []*interfaces.Slot {slotOne, slotTwo} {
+ c.Assert (mountSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+ c.Assert (apparmorSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+ }
+
+ // Analyze the mount specification.
+ expectedMnt := []mount.Entry {{
+ Name: " /snap/plugin-one/1/plugin" ,
+ Dir: " /snap/app/1/plugins/plugin" ,
+ Options: []string {" bind" , " ro" },
+ }, {
+ Name: " /snap/plugin-two/1/plugin" ,
+ Dir: " /snap/app/1/plugins/plugin-2" ,
+ Options: []string {" bind" , " ro" },
+ }}
+ c.Assert (mountSpec.MountEntries (), DeepEquals, expectedMnt)
+
+ // Analyze the apparmor specification.
+ //
+ // NOTE: the paths below refer to the original locations and are *NOT*
+ // altered like the mount entries above. This is intended. See the comment
+ // below for explanation as to why those are necessary.
+ c.Assert (apparmorSpec.SecurityTags (), DeepEquals, []string {" snap.app.app" })
+ expected := `
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files
+# read-only.
+/snap/plugin-one/1/plugin/** mrkix,
+
+
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files
+# read-only.
+/snap/plugin-two/1/plugin/** mrkix,
+`
+ c.Assert (apparmorSpec.SnippetForTag (" snap.app.app" ), Equals, expected)
+}
+
+func (s *ContentSuite ) TestModernContentSameReadAndWriteClash (c *C ) {
+ plug := MockPlug (c, ` name: consumer
+plugs:
+ content:
+ target: $SNAP_COMMON/import
+apps:
+ app:
+ command: foo
+` , &snap.SideInfo {Revision: snap.R (1 )}, " content" )
+
+ slot := MockSlot (c, ` name: producer
+slots:
+ content:
+ source:
+ read:
+ - $SNAP_DATA/directory
+ write:
+ - $SNAP_DATA/directory
+` , &snap.SideInfo {Revision: snap.R (2 )}, " content" )
+
+ // Create the mount and apparmor specifications.
+ mountSpec := &mount.Specification {}
+ c.Assert (mountSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+ apparmorSpec := &apparmor.Specification {}
+ c.Assert (apparmorSpec.AddConnectedPlug (s.iface , plug, nil , slot, nil ), IsNil)
+
+ // Analyze the mount specification
+ expectedMnt := []mount.Entry {{
+ Name: " /var/snap/producer/2/directory" ,
+ Dir: " /var/snap/consumer/common/import/directory" ,
+ Options: []string {" bind" , " ro" },
+ }, {
+ Name: " /var/snap/producer/2/directory" ,
+ Dir: " /var/snap/consumer/common/import/directory-2" ,
+ Options: []string {" bind" },
+ }}
+ c.Assert (mountSpec.MountEntries (), DeepEquals, expectedMnt)
+
+ // Analyze the apparmor specification.
+ //
+ // NOTE: Although there are duplicate entries with different permissions
+ // one is a superset of the other so they do not conflict.
+ c.Assert (apparmorSpec.SecurityTags (), DeepEquals, []string {" snap.consumer.app" })
+ expected := `
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files. Due
+# to a limitation in the kernel's LSM hooks for AF_UNIX, these
+# are needed for using named sockets within the exported
+# directory.
+/var/snap/producer/2/directory/** mrwklix,
+
+# In addition to the bind mount, add any AppArmor rules so that
+# snaps may directly access the slot implementation's files
+# read-only.
+/var/snap/producer/2/directory/** mrkix,
+`
+ c.Assert (apparmorSpec.SnippetForTag (" snap.consumer.app" ), Equals, expected)
+}
Maybe a bit more context here? i.e.
move the "read" attribute of slot %q into the "source" sectionand pass slot.Name in ? Same below.