summaryrefslogtreecommitdiff
diff options
authorPawel Stolowski <stolowski@gmail.com>2018-10-31 14:33:40 +0100
committerPawel Stolowski <stolowski@gmail.com>2018-10-31 14:33:40 +0100
commit3f5dc8c390da42d049369e4d571bf8eaa7b6d16f (patch)
treeaa156d7fd0f60d7a385a8a678abac52825b1bb46
parent0bb1673eca985d42e7ef00e45e4924ec147a7a74 (diff)
Hadle by-hotplug flag in Disconnect and mark connections removed by hotplug with hotplug-gone.disconnect-by-hotplug
-rw-r--r--overlord/ifacestate/export_test.go5
-rw-r--r--overlord/ifacestate/handlers.go17
-rw-r--r--overlord/ifacestate/ifacestate.go4
-rw-r--r--overlord/ifacestate/ifacestate_test.go50
4 files changed, 74 insertions, 2 deletions
diff --git a/overlord/ifacestate/export_test.go b/overlord/ifacestate/export_test.go
index a9a1ff947f..442db53eb2 100644
--- a/overlord/ifacestate/export_test.go
+++ b/overlord/ifacestate/export_test.go
@@ -31,6 +31,7 @@ var (
CheckAutoconnectConflicts = checkAutoconnectConflicts
FindSymmetricAutoconnectTask = findSymmetricAutoconnectTask
ConnectPriv = connect
+ DisconnectPriv = disconnectTasks
GetConns = getConns
SetConns = setConns
DefaultDeviceKey = defaultDeviceKey
@@ -49,6 +50,10 @@ func NewConnectOptsWithAutoSet() connectOpts {
return connectOpts{AutoConnect: true, ByGadget: false}
}
+func NewDisconnectOptsWithByHotplugSet() disconnectOpts {
+ return disconnectOpts{ByHotplug: true}
+}
+
func MockRemoveStaleConnections(f func(st *state.State) error) (restore func()) {
old := removeStaleConnections
removeStaleConnections = f
diff --git a/overlord/ifacestate/handlers.go b/overlord/ifacestate/handlers.go
index c3efcc2f84..cdb49742b8 100644
--- a/overlord/ifacestate/handlers.go
+++ b/overlord/ifacestate/handlers.go
@@ -504,17 +504,30 @@ func (m *InterfaceManager) doDisconnect(task *state.Task, _ *tomb.Tomb) error {
if err := task.Get("auto-disconnect", &autoDisconnect); err != nil && err != state.ErrNoState {
return fmt.Errorf("internal error: failed to read 'auto-disconnect' flag: %s", err)
}
- if conn.Auto && !autoDisconnect {
+
+ // "by-hotplug" flag indicates it's a disconnect triggered by hotplug remove event;
+ // we want to keep information of the connection and just mark it as hotplug-gone.
+ var byHotplug bool
+ if err := task.Get("by-hotplug", &byHotplug); err != nil && err != state.ErrNoState {
+ return fmt.Errorf("internal error: cannot read 'by-hotplug' flag: %s", err)
+ }
+
+ switch {
+ case byHotplug:
+ conn.HotplugGone = true
+ conns[cref.ID()] = conn
+ case conn.Auto && !autoDisconnect:
conn.Undesired = true
conn.DynamicPlugAttrs = nil
conn.DynamicSlotAttrs = nil
conn.StaticPlugAttrs = nil
conn.StaticSlotAttrs = nil
conns[cref.ID()] = conn
- } else {
+ default:
delete(conns, cref.ID())
}
setConns(st, conns)
+
return nil
}
diff --git a/overlord/ifacestate/ifacestate.go b/overlord/ifacestate/ifacestate.go
index dca0f577c5..8bd5a8734d 100644
--- a/overlord/ifacestate/ifacestate.go
+++ b/overlord/ifacestate/ifacestate.go
@@ -299,6 +299,7 @@ func Disconnect(st *state.State, conn *interfaces.Connection) (*state.TaskSet, e
type disconnectOpts struct {
AutoDisconnect bool
+ ByHotplug bool
}
// disconnectTasks creates a set of tasks for disconnect, including hooks, but does not do any conflict checking.
@@ -330,6 +331,9 @@ func disconnectTasks(st *state.State, conn *interfaces.Connection, flags disconn
if flags.AutoDisconnect {
disconnectTask.Set("auto-disconnect", true)
}
+ if flags.ByHotplug {
+ disconnectTask.Set("by-hotplug", true)
+ }
ts := state.NewTaskSet()
var prev *state.Task
diff --git a/overlord/ifacestate/ifacestate_test.go b/overlord/ifacestate/ifacestate_test.go
index cd68a858f2..677823e677 100644
--- a/overlord/ifacestate/ifacestate_test.go
+++ b/overlord/ifacestate/ifacestate_test.go
@@ -2951,6 +2951,56 @@ func (s *interfaceManagerSuite) TestDisconnectDisablesAutoConnect(c *C) {
})
}
+func (s *interfaceManagerSuite) TestDisconnectByHotplug(c *C) {
+ s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"})
+ consumerInfo := s.mockSnap(c, consumerYaml)
+ s.mockSnap(c, coreSnapYaml)
+
+ s.state.Lock()
+ s.state.Set("conns", map[string]interface{}{
+ "consumer:plug core:slot": map[string]interface{}{"interface": "test"},
+ "consumer:plug core:slot2": map[string]interface{}{"interface": "test"},
+ })
+ s.state.Set("hotplug-slots", map[string]interface{}{
+ "slot": map[string]interface{}{
+ "name": "slot",
+ "interface": "test",
+ "hotplug-key": "1234",
+ }})
+ s.state.Unlock()
+
+ s.manager(c)
+
+ s.state.Lock()
+ conn := &interfaces.Connection{
+ Plug: interfaces.NewConnectedPlug(consumerInfo.Plugs["plug"], nil, nil),
+ Slot: interfaces.NewConnectedSlot(&snap.SlotInfo{Snap: &snap.Info{SuggestedName: "core"}, Name: "slot"}, nil, nil),
+ }
+
+ ts, err := ifacestate.DisconnectPriv(s.state, conn, ifacestate.NewDisconnectOptsWithByHotplugSet())
+ c.Assert(err, IsNil)
+
+ change := s.state.NewChange("disconnect", "")
+ change.AddAll(ts)
+ s.state.Unlock()
+
+ s.settle(c)
+
+ 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 core:slot": map[string]interface{}{"interface": "test", "hotplug-gone": true},
+ "consumer:plug core:slot2": map[string]interface{}{"interface": "test"},
+ })
+}
+
func (s *interfaceManagerSuite) TestManagerReloadsConnections(c *C) {
s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"})
s.mockSnap(c, consumerYaml)