mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
Now that we have app sets in the interfaces repo, keep a pointer to them in ConnectedPlug/Slot types. Use this to build label expressions in the interfaces. * many: add a pointer to a SnapAppSet into Connected(Plug|Slot) to that interfaces can build a complete label expression, including component hooks * interfaces: update doc comments on ConnectedPlug/Slot.AppSet * interfaces: remove TODO that has been addressed * interfaces: use app set pointer for instance name check * snap: add Runnable type that represents the runnable parts of a snap * interfaces, o/ifacestate: use snap.Runnable rather than interfaces.Runnable * interfaces, i/builtin, o/ifacestate: panic on failed invariant check in NewConnectedPlug/Slot * interfaces: add methods to app set for getting runnables that can connect to plug/slot * interfaces: build label expressions using runnables * interfaces: doc comment for SlotRunnables * interfaces: implement Slot/PlugRunnables with shared helper * interfaces: log and skip security tags that do not match expected pattern * snap, interfaces: move runnable constructors to methods on AppInfo and HookInfo * interfaces: refactor to allow labelExpr to operate directly on a ConnectedPlug/Slot * snap: move around Runnable methods
204 lines
6.2 KiB
Go
204 lines
6.2 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* Copyright (C) 2021 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 systemd_test
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
. "gopkg.in/check.v1"
|
|
|
|
"github.com/snapcore/snapd/interfaces"
|
|
"github.com/snapcore/snapd/interfaces/ifacetest"
|
|
"github.com/snapcore/snapd/interfaces/systemd"
|
|
"github.com/snapcore/snapd/snap"
|
|
"github.com/snapcore/snapd/snap/snaptest"
|
|
)
|
|
|
|
type specSuite struct{}
|
|
|
|
var _ = Suite(&specSuite{})
|
|
|
|
func (s *specSuite) TestAddService(c *C) {
|
|
spec := systemd.Specification{}
|
|
c.Assert(spec.Services(), IsNil)
|
|
svc1 := &systemd.Service{ExecStart: "one"}
|
|
err := spec.AddService("svc1", svc1)
|
|
c.Assert(err, IsNil)
|
|
svc2 := &systemd.Service{ExecStart: "two"}
|
|
err = spec.AddService("svc2", svc2)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(spec.Services(), DeepEquals, map[string]*systemd.Service{
|
|
"svc1": svc1,
|
|
"svc2": svc2,
|
|
})
|
|
}
|
|
|
|
func (s *specSuite) TestClashingSameIface(c *C) {
|
|
info1 := snaptest.MockInfo(c, `name: snap1
|
|
version: 0
|
|
plugs:
|
|
plug1:
|
|
interface: test
|
|
`, nil)
|
|
|
|
iface := &ifacetest.TestInterface{
|
|
InterfaceName: "test",
|
|
}
|
|
svc1 := &systemd.Service{ExecStart: "one"}
|
|
svc2 := &systemd.Service{ExecStart: "two"}
|
|
|
|
iface.SystemdPermanentPlugCallback = func(spec *systemd.Specification, plug *snap.PlugInfo) error {
|
|
if err := spec.AddService("foo", svc1); err != nil {
|
|
return err
|
|
}
|
|
return spec.AddService("foo", svc2)
|
|
}
|
|
|
|
spec := systemd.Specification{}
|
|
err := spec.AddPermanentPlug(iface, info1.Plugs["plug1"])
|
|
c.Assert(err, ErrorMatches, `internal error: interface "test" has inconsistent system needs: service for "foo" used to be defined as .*, now re-defined as .*`)
|
|
}
|
|
|
|
func (s *specSuite) TestClashingTwoIfaces(c *C) {
|
|
info1 := snaptest.MockInfo(c, `name: snap1
|
|
version: 0
|
|
plugs:
|
|
plug1:
|
|
interface: test1
|
|
plug2:
|
|
interface: test2
|
|
`, nil)
|
|
|
|
iface1 := &ifacetest.TestInterface{
|
|
InterfaceName: "test1",
|
|
}
|
|
iface2 := &ifacetest.TestInterface{
|
|
InterfaceName: "test2",
|
|
}
|
|
svc1 := &systemd.Service{ExecStart: "one"}
|
|
svc2 := &systemd.Service{ExecStart: "two"}
|
|
|
|
iface1.SystemdPermanentPlugCallback = func(spec *systemd.Specification, plug *snap.PlugInfo) error {
|
|
return spec.AddService("foo", svc1)
|
|
}
|
|
|
|
iface2.SystemdPermanentPlugCallback = func(spec *systemd.Specification, plug *snap.PlugInfo) error {
|
|
return spec.AddService("foo", svc2)
|
|
}
|
|
|
|
spec := systemd.Specification{}
|
|
err := spec.AddPermanentPlug(iface1, info1.Plugs["plug1"])
|
|
c.Assert(err, IsNil)
|
|
err = spec.AddPermanentPlug(iface2, info1.Plugs["plug2"])
|
|
c.Assert(err, ErrorMatches, `internal error: interface "test2" and "test1" have conflicting system needs: service for "foo" used to be defined as .* by "test1", now re-defined as .*`)
|
|
}
|
|
|
|
func (s *specSuite) TestDifferentObjectsNotClashing(c *C) {
|
|
svc1 := &systemd.Service{ExecStart: "one and the same"}
|
|
svc2 := &systemd.Service{ExecStart: "one and the same"}
|
|
spec := systemd.Specification{}
|
|
err := spec.AddService("foo", svc1)
|
|
c.Assert(err, IsNil)
|
|
err = spec.AddService("foo", svc2)
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func mockConnectedPlug(c *C, yaml string, si *snap.SideInfo, plugName string) (*interfaces.ConnectedPlug, *snap.PlugInfo) {
|
|
info := snaptest.MockInfo(c, yaml, si)
|
|
|
|
set, err := interfaces.NewSnapAppSet(info, nil)
|
|
c.Assert(err, IsNil)
|
|
|
|
if plugInfo, ok := info.Plugs[plugName]; ok {
|
|
return interfaces.NewConnectedPlug(plugInfo, set, nil, nil), plugInfo
|
|
}
|
|
panic(fmt.Sprintf("cannot find plug %q in snap %q", plugName, info.InstanceName()))
|
|
}
|
|
|
|
func mockConnectedSlot(c *C, yaml string, si *snap.SideInfo, slotName string) (*interfaces.ConnectedSlot, *snap.SlotInfo) {
|
|
info := snaptest.MockInfo(c, yaml, si)
|
|
|
|
set, err := interfaces.NewSnapAppSet(info, nil)
|
|
c.Assert(err, IsNil)
|
|
|
|
if slotInfo, ok := info.Slots[slotName]; ok {
|
|
return interfaces.NewConnectedSlot(slotInfo, set, nil, nil), slotInfo
|
|
}
|
|
panic(fmt.Sprintf("cannot find slot %q in snap %q", slotName, info.InstanceName()))
|
|
}
|
|
|
|
func (s *specSuite) TestAddMethods(c *C) {
|
|
const plugYaml = `name: snap1
|
|
version: 0
|
|
plugs:
|
|
plug1:
|
|
interface: test
|
|
`
|
|
plug, plugInfo := mockConnectedPlug(c, plugYaml, nil, "plug1")
|
|
const slotYaml = `name: snap2
|
|
version: 0
|
|
slots:
|
|
slot2:
|
|
interface: test
|
|
`
|
|
slot, slotInfo := mockConnectedSlot(c, slotYaml, nil, "slot2")
|
|
|
|
spec := systemd.Specification{}
|
|
|
|
iface := &ifacetest.TestInterface{
|
|
InterfaceName: "test",
|
|
SystemdConnectedPlugCallback: func(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
|
|
spec.AddService(fmt.Sprintf("%s-%s", plug.Name(), slot.Name()), &systemd.Service{ExecStart: "connected-plug"})
|
|
return nil
|
|
},
|
|
SystemdConnectedSlotCallback: func(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
|
|
spec.AddService(fmt.Sprintf("%s-%s", slot.Name(), plug.Name()), &systemd.Service{ExecStart: "connected-slot"})
|
|
return nil
|
|
},
|
|
SystemdPermanentPlugCallback: func(spec *systemd.Specification, plug *snap.PlugInfo) error {
|
|
spec.AddService(plug.Name, &systemd.Service{ExecStart: "permanent-plug"})
|
|
return nil
|
|
},
|
|
SystemdPermanentSlotCallback: func(spec *systemd.Specification, slot *snap.SlotInfo) error {
|
|
spec.AddService(slot.Name, &systemd.Service{ExecStart: "permanent-slot"})
|
|
return nil
|
|
},
|
|
}
|
|
|
|
err := spec.AddPermanentSlot(iface, slotInfo)
|
|
c.Assert(err, IsNil)
|
|
|
|
err = spec.AddPermanentPlug(iface, plugInfo)
|
|
c.Assert(err, IsNil)
|
|
|
|
err = spec.AddConnectedSlot(iface, plug, slot)
|
|
c.Assert(err, IsNil)
|
|
|
|
err = spec.AddConnectedPlug(iface, plug, slot)
|
|
c.Assert(err, IsNil)
|
|
|
|
c.Check(spec.Services(), DeepEquals, map[string]*systemd.Service{
|
|
"plug1-slot2": {ExecStart: "connected-plug"},
|
|
"slot2-plug1": {ExecStart: "connected-slot"},
|
|
"plug1": {ExecStart: "permanent-plug"},
|
|
"slot2": {ExecStart: "permanent-slot"},
|
|
})
|
|
}
|