Skip to content

Commit

Permalink
Merge pull request #2712 from pedronis/iface-content-rules
Browse files Browse the repository at this point in the history
interfaces/builtin: refine the content interface rules using $SLOT

Allow connection and auto-connection only if plug and slot side content attributes match, using a $SLOT constraint.

Make the content attribute of the content interface mandatory temporarily until we have the right infrastructure to implement the initially intended defaults. (The previous behaviour letting leaving it out was unintended/buggy)
  • Loading branch information
pedronis committed Jan 30, 2017
2 parents 8e3faa8 + 7700ee8 commit ee2506b
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 3 deletions.
5 changes: 5 additions & 0 deletions interfaces/builtin/basedeclaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,14 @@ slots:
slot-snap-type:
- app
- gadget
allow-connection:
plug-attributes:
content: $SLOT(content)
allow-auto-connection:
plug-publisher-id:
- $SLOT_PUBLISHER_ID
plug-attributes:
content: $SLOT(content)
core-support:
allow-installation:
slot-snap-type:
Expand Down
106 changes: 105 additions & 1 deletion interfaces/builtin/basedeclaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,58 @@ plugs:
}

func (s *baseDeclSuite) TestAutoConnectionContent(c *C) {
// content will also depend for now AutoConnect(plug, slot)
// random snaps cannot connect with content
// (Sanitize* will now also block this)
cand := s.connectCand(c, "content", "", "")
err := cand.CheckAutoConnect()
c.Check(err, NotNil)

slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "")
plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "")
plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "")

// same publisher, same content
cand = s.connectCand(c, "stuff", `
name: slot-snap
slots:
stuff:
interface: content
content: mk1
`, `
name: plug-snap
plugs:
stuff:
interface: content
content: mk1
`)
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl1
err = cand.CheckAutoConnect()
c.Check(err, IsNil)

// different publisher, same content
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl2
err = cand.CheckAutoConnect()
c.Check(err, NotNil)

// same publisher, different content
cand = s.connectCand(c, "stuff", `name: slot-snap
slots:
stuff:
interface: content
content: mk1
`, `
name: plug-snap
plugs:
stuff:
interface: content
content: mk2
`)
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl1
err = cand.CheckAutoConnect()
c.Check(err, NotNil)
}

func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverride(c *C) {
Expand Down Expand Up @@ -499,6 +546,7 @@ func (s *baseDeclSuite) TestConnection(c *C) {
// case-by-case basis
noconnect := map[string]bool{
"bluez": true,
"content": true,
"docker": true,
"fwupd": true,
"location-control": true,
Expand Down Expand Up @@ -594,3 +642,59 @@ func (s *baseDeclSuite) TestSanity(c *C) {
}
}
}

func (s *baseDeclSuite) TestConnectionContent(c *C) {
// we let connect explicitly as long as content matches (or is absent on both sides)

// random (Sanitize* will now also block this)
cand := s.connectCand(c, "content", "", "")
err := cand.Check()
c.Check(err, NotNil)

slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "")
plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "")
plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "")

// same publisher, same content
cand = s.connectCand(c, "stuff", `name: slot-snap
slots:
stuff:
interface: content
content: mk1
`, `
name: plug-snap
plugs:
stuff:
interface: content
content: mk1
`)
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl1
err = cand.Check()
c.Check(err, IsNil)

// different publisher, same content
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl2
err = cand.Check()
c.Check(err, IsNil)

// same publisher, different content
cand = s.connectCand(c, "stuff", `
name: slot-snap
slots:
stuff:
interface: content
content: mk1
`, `
name: plug-snap
plugs:
stuff:
interface: content
content: mk2
`)
cand.SlotSnapDeclaration = slotDecl1
cand.PlugSnapDeclaration = plugDecl1
err = cand.Check()
c.Check(err, NotNil)
}
11 changes: 10 additions & 1 deletion interfaces/builtin/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (iface *ContentInterface) SanitizeSlot(slot *interfaces.Slot) error {
if iface.Name() != slot.Interface {
panic(fmt.Sprintf("slot is not of interface %q", iface))
}
content, ok := slot.Attrs["content"].(string)
if !ok || len(content) == 0 {
return fmt.Errorf("content slot must have a content attribute set")
}

// check that we have either a read or write path
rpath := iface.path(slot, "read")
Expand All @@ -68,6 +72,10 @@ func (iface *ContentInterface) SanitizePlug(plug *interfaces.Plug) error {
if iface.Name() != plug.Interface {
panic(fmt.Sprintf("plug is not of interface %q", iface))
}
content, ok := plug.Attrs["content"].(string)
if !ok || len(content) == 0 {
return fmt.Errorf("content plug must have a content attribute set")
}
target, ok := plug.Attrs["target"].(string)
if !ok || len(target) == 0 {
return fmt.Errorf("content plug must contain target path")
Expand Down Expand Up @@ -184,5 +192,6 @@ func (iface *ContentInterface) PermanentPlugSnippet(plug *interfaces.Plug, secur
}

func (iface *ContentInterface) AutoConnect(plug *interfaces.Plug, slot *interfaces.Slot) bool {
return plug.Attrs["content"] == slot.Attrs["content"]
// allow what declarations allowed
return true
}
38 changes: 37 additions & 1 deletion interfaces/builtin/content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ version: 1.0
slots:
content-slot:
interface: content
content: mycont
read:
- shared/read
`
Expand All @@ -55,12 +56,28 @@ slots:
c.Assert(err, IsNil)
}

func (s *ContentSuite) TestSanitizeSlotNoContentLabel(c *C) {
const mockSnapYaml = `name: content-slot-snap
version: 1.0
slots:
content-slot:
interface: content
read:
- shared/read
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
slot := &interfaces.Slot{SlotInfo: info.Slots["content-slot"]}
err := s.iface.SanitizeSlot(slot)
c.Assert(err, ErrorMatches, `content slot must have a content attribute set`)
}

func (s *ContentSuite) TestSanitizeSlotNoPaths(c *C) {
const mockSnapYaml = `name: content-slot-snap
version: 1.0
slots:
content-slot:
interface: content
content: mycont
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
slot := &interfaces.Slot{SlotInfo: info.Slots["content-slot"]}
Expand All @@ -74,6 +91,7 @@ version: 1.0
slots:
content-slot:
interface: content
content: mycont
read: []
write: []
`
Expand All @@ -83,12 +101,13 @@ slots:
c.Assert(err, ErrorMatches, "read or write path must be set")
}

func (s *ContentSuite) TestSanitizeSlotHasRealtivePath(c *C) {
func (s *ContentSuite) TestSanitizeSlotHasRelativePath(c *C) {
const mockSnapYaml = `name: content-slot-snap
version: 1.0
slots:
content-slot:
interface: content
content: mycont
`
for _, rw := range []string{"read: [../foo]", "write: [../bar]"} {
info := snaptest.MockInfo(c, mockSnapYaml+" "+rw, nil)
Expand All @@ -104,6 +123,7 @@ version: 1.0
plugs:
content-plug:
interface: content
content: mycont
target: import
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
Expand All @@ -112,12 +132,27 @@ plugs:
c.Assert(err, IsNil)
}

func (s *ContentSuite) TestSanitizePlugNoContentLabel(c *C) {
const mockSnapYaml = `name: content-slot-snap
version: 1.0
plugs:
content-plug:
interface: content
target: import
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
plug := &interfaces.Plug{PlugInfo: info.Plugs["content-plug"]}
err := s.iface.SanitizePlug(plug)
c.Assert(err, ErrorMatches, `content plug must have a content attribute set`)
}

func (s *ContentSuite) TestSanitizePlugSimpleNoTarget(c *C) {
const mockSnapYaml = `name: content-slot-snap
version: 1.0
plugs:
content-plug:
interface: content
content: mycont
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
plug := &interfaces.Plug{PlugInfo: info.Plugs["content-plug"]}
Expand All @@ -131,6 +166,7 @@ version: 1.0
plugs:
content-plug:
interface: content
content: mycont
target: ../foo
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
Expand Down

0 comments on commit ee2506b

Please sign in to comment.