func (s *interfaceManagerSuite) TestConnectTaskCheckInterfaceMismatch(c *C) { s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "otherplug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() c.Check(change.Err(), ErrorMatches, `(?s).*cannot connect consumer:otherplug \("test2" interface\) to producer:slot \("test" interface\).*`) task := change.Tasks()[0] c.Check(task.Kind(), Equals, "connect") c.Check(task.Status(), Equals, state.ErrorStatus) c.Check(change.Status(), Equals, state.ErrorStatus) }
func (s *interfaceManagerSuite) TestConnectTaskNoSuchPlug(c *C) { s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "whatplug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() c.Check(change.Err(), ErrorMatches, `(?s).*snap "consumer" has no plug named "whatplug".*`) c.Check(change.Status(), Equals, state.ErrorStatus) }
// changeInterfaces controls the interfaces system. // Plugs can be connected to and disconnected from slots. // When enableInternalInterfaceActions is true plugs and slots can also be // explicitly added and removed. func changeInterfaces(c *Command, r *http.Request, user *auth.UserState) Response { var a interfaceAction decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&a); err != nil { return BadRequest("cannot decode request body into an interface action: %v", err) } if a.Action == "" { return BadRequest("interface action not specified") } if !c.d.enableInternalInterfaceActions && a.Action != "connect" && a.Action != "disconnect" { return BadRequest("internal interface actions are disabled") } if len(a.Plugs) > 1 || len(a.Slots) > 1 { return NotImplemented("many-to-many operations are not implemented") } if a.Action != "connect" && a.Action != "disconnect" { return BadRequest("unsupported interface action: %q", a.Action) } if len(a.Plugs) == 0 || len(a.Slots) == 0 { return BadRequest("at least one plug and slot is required") } var summary string var taskset *state.TaskSet var err error state := c.d.overlord.State() state.Lock() defer state.Unlock() switch a.Action { case "connect": summary = fmt.Sprintf("Connect %s:%s to %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) taskset, err = ifacestate.Connect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) case "disconnect": summary = fmt.Sprintf("Disconnect %s:%s from %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) taskset, err = ifacestate.Disconnect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) } if err != nil { return BadRequest("%v", err) } change := state.NewChange(a.Action+"-snap", summary) change.Set("snap-names", []string{a.Plugs[0].Snap, a.Slots[0].Snap}) change.AddAll(taskset) state.EnsureBefore(0) return AsyncResponse(nil, &Meta{Change: change.ID()}) }
func (s *interfaceManagerSuite) TestConnectTaskCheckAllowed(c *C) { restore := assertstest.MockBuiltinBaseDeclaration([]byte(` type: base-declaration authority-id: canonical series: 16 slots: test: allow-connection: plug-publisher-id: - $SLOT_PUBLISHER_ID `)) defer restore() s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnapDecl(c, "consumer", "one-publisher", nil) s.mockSnap(c, consumerYaml) s.mockSnapDecl(c, "producer", "one-publisher", nil) s.mockSnap(c, producerYaml) s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() c.Assert(change.Err(), IsNil) c.Check(change.Status(), Equals, state.DoneStatus) repo := mgr.Repository() plug := repo.Plug("consumer", "plug") slot := repo.Slot("producer", "slot") c.Assert(plug.Connections, HasLen, 1) c.Check(plug.Connections[0], DeepEquals, interfaces.SlotRef{Snap: "producer", Name: "slot"}) c.Check(slot.Connections[0], DeepEquals, interfaces.PlugRef{Snap: "consumer", Name: "plug"}) }
func (s *interfaceManagerSuite) TestConnectTask(c *C) { s.state.Lock() defer s.state.Unlock() ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) task := ts.Tasks()[0] c.Assert(task.Kind(), Equals, "connect") var plug interfaces.PlugRef err = task.Get("plug", &plug) c.Assert(err, IsNil) c.Assert(plug.Snap, Equals, "consumer") c.Assert(plug.Name, Equals, "plug") var slot interfaces.SlotRef err = task.Get("slot", &slot) c.Assert(err, IsNil) c.Assert(slot.Snap, Equals, "producer") c.Assert(slot.Name, Equals, "slot") }
func (s *interfaceManagerSuite) TestConnectTaskCheckNotAllowed(c *C) { restore := assertstest.MockBuiltinBaseDeclaration([]byte(` type: base-declaration authority-id: canonical series: 16 slots: test: allow-connection: plug-publisher-id: - $SLOT_PUBLISHER_ID `)) defer restore() s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnapDecl(c, "consumer", "consumer-publisher", nil) s.mockSnap(c, consumerYaml) s.mockSnapDecl(c, "producer", "producer-publisher", nil) s.mockSnap(c, producerYaml) s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() c.Check(change.Err(), ErrorMatches, `(?s).*connection not allowed by slot rule of interface "test".*`) c.Check(change.Status(), Equals, state.ErrorStatus) }
func (s *interfaceManagerSuite) TestEnsureProcessesConnectTask(c *C) { s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() c.Assert(change.Err(), IsNil) task := change.Tasks()[0] c.Check(task.Kind(), Equals, "connect") c.Check(task.Status(), Equals, state.DoneStatus) c.Check(change.Status(), Equals, state.DoneStatus) repo := mgr.Repository() plug := repo.Plug("consumer", "plug") slot := repo.Slot("producer", "slot") c.Assert(plug.Connections, HasLen, 1) c.Assert(slot.Connections, HasLen, 1) c.Check(plug.Connections[0], DeepEquals, interfaces.SlotRef{Snap: "producer", Name: "slot"}) c.Check(slot.Connections[0], DeepEquals, interfaces.PlugRef{Snap: "consumer", Name: "plug"}) }
func (s *interfaceManagerSuite) testConnectTaskCheck(c *C, setup func(), check func(*state.Change)) { restore := assertstest.MockBuiltinBaseDeclaration([]byte(` type: base-declaration authority-id: canonical series: 16 slots: test: allow-connection: plug-publisher-id: - $SLOT_PUBLISHER_ID `)) defer restore() s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) setup() s.state.Lock() change := s.state.NewChange("kind", "summary") ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change.AddAll(ts) s.state.Unlock() mgr := s.manager(c) mgr.Ensure() mgr.Wait() s.state.Lock() defer s.state.Unlock() check(change) }
func (s *interfaceManagerSuite) TestConnectSetsUpSecurity(c *C) { s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) mgr := s.manager(c) s.state.Lock() ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change := s.state.NewChange("connect", "") change.AddAll(ts) s.state.Unlock() mgr.Ensure() mgr.Wait() mgr.Stop() s.state.Lock() defer s.state.Unlock() c.Assert(change.Err(), IsNil) c.Check(change.Status(), Equals, state.DoneStatus) c.Assert(s.secBackend.SetupCalls, HasLen, 2) c.Assert(s.secBackend.RemoveCalls, HasLen, 0) c.Check(s.secBackend.SetupCalls[0].SnapInfo.Name(), Equals, "producer") c.Check(s.secBackend.SetupCalls[1].SnapInfo.Name(), Equals, "consumer") c.Check(s.secBackend.SetupCalls[0].Options, Equals, interfaces.ConfinementOptions{}) c.Check(s.secBackend.SetupCalls[1].Options, Equals, interfaces.ConfinementOptions{}) }
func (s *interfaceManagerSuite) TestConnectTracksConnectionsInState(c *C) { s.mockIface(c, &interfaces.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) mgr := s.manager(c) s.state.Lock() ts, err := ifacestate.Connect(s.state, "consumer", "plug", "producer", "slot") c.Assert(err, IsNil) ts.Tasks()[0].Set("snap-setup", &snapstate.SnapSetup{ SideInfo: &snap.SideInfo{ RealName: "consumer", }, }) change := s.state.NewChange("connect", "") change.AddAll(ts) s.state.Unlock() mgr.Ensure() mgr.Wait() mgr.Stop() s.state.Lock() defer s.state.Unlock() c.Assert(change.Err(), IsNil) c.Check(change.Status(), Equals, state.DoneStatus) var conns map[string]interface{} err = s.state.Get("conns", &conns) c.Assert(err, IsNil) c.Check(conns, DeepEquals, map[string]interface{}{ "consumer:plug producer:slot": map[string]interface{}{ "interface": "test", }, }) }