-
Notifications
You must be signed in to change notification settings - Fork 680
daemon, o/i/apparmorprompting: implement ShutDown for InterfacesRequestsManager #17147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 7 commits
7e357ca
3801a39
a78f96a
c9fae75
a00f69c
62d68e9
b66c9e1
89f274a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -652,8 +652,7 @@ func (s *apparmorpromptingSuite) testAskWithOutcome(c *C, outcome prompting.Outc | |
| outcomeChan := make(chan prompting.OutcomeType) | ||
| errChan := make(chan error) | ||
| go func() { | ||
| snapdShuttingDown := make(chan struct{}) | ||
| out, err := mgr.Ask(uid, iface, snap, pid, cgroup, snapdShuttingDown) | ||
| out, err := mgr.Ask(uid, iface, snap, pid, cgroup) | ||
| logger.WithLoggerLock(func() { | ||
| c.Check(err, IsNil, Commentf(logbuf.String())) | ||
| }) | ||
|
|
@@ -741,26 +740,17 @@ func (s *apparmorpromptingSuite) TestAskShutdownBeforeSending(c *C) { | |
| iface = "audio-record" | ||
| ) | ||
|
|
||
| // Stop the manager now so that it will not receive the request. | ||
| // | ||
| // Unfortunately, there's not a way to test the snapdShuttingDown channel | ||
| // closing as well, since if the manager has not stopped, there is a race | ||
| // where the run loop may receive the request. So we close the listener to | ||
| // ensure the run loop does not receive the request. | ||
| // | ||
| // XXX: in the future, when we remove the snapdShuttingDown channel in | ||
| // favor of a manager-level shutdown triggered by the daemon stopping, most | ||
| // of this comment can be removed. | ||
| c.Check(mgr.Stop(), IsNil) | ||
| // Shut down the manager now so that it will not receive the request. | ||
| mgr.ShutDown() | ||
|
|
||
| timeoutChan := make(chan struct{}) | ||
| time.AfterFunc(time.Second, func() { close(timeoutChan) }) | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup, timeoutChan) | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup) | ||
| c.Check(outcome, Equals, prompting.OutcomeUnset) | ||
| c.Check(err, Equals, prompting_errors.ErrPromptingClosed) | ||
|
|
||
| c.Check(mgr.Stop(), IsNil) | ||
| } | ||
|
|
||
| func (s *apparmorpromptingSuite) TestAskShutdownBeforeReply(c *C) { | ||
| func (s *apparmorpromptingSuite) TestAskShutdownBeforeReplyWithStop(c *C) { | ||
| proceedWithClose, _, _, restore := apparmorprompting.MockListenerWithDelayedClose() | ||
| defer restore() | ||
|
|
||
|
|
@@ -785,12 +775,10 @@ func (s *apparmorpromptingSuite) TestAskShutdownBeforeReply(c *C) { | |
| mgr, err := apparmorprompting.New(s.noticeMgr) | ||
| c.Assert(err, IsNil) | ||
|
|
||
| neverClose := make(chan struct{}) | ||
|
|
||
| // Call Ask, then signal when response has been validated | ||
| doneChan := make(chan struct{}) | ||
| go func() { | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup, neverClose) | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup) | ||
| c.Check(outcome, Equals, prompting.OutcomeUnset) | ||
| c.Check(err, Equals, prompting_errors.ErrPromptingClosed) | ||
| close(doneChan) | ||
|
|
@@ -861,11 +849,7 @@ func (s *apparmorpromptingSuite) TestAskShutdownBeforeReply(c *C) { | |
| } | ||
| } | ||
|
|
||
| // XXX: this test only exists since there are currently two ways to tell Ask to | ||
| // stop waiting: the manager closing, and the snapdShuttingDown channel closing. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is, there are still two ways to tell Ask to stop waiting. And I think there will always be two ways if we want |
||
| // Once the latter is removed in favor of a proper shutdown of the manager | ||
| // triggered from the daemon, this test should be removed. | ||
| func (s *apparmorpromptingSuite) TestAskShutdownViaChannelBeforeReply(c *C) { | ||
| func (s *apparmorpromptingSuite) TestAskShutdownBeforeReplyWithShutDown(c *C) { | ||
| _, _, restore := apparmorprompting.MockListener() | ||
| defer restore() | ||
|
|
||
|
|
@@ -890,12 +874,10 @@ func (s *apparmorpromptingSuite) TestAskShutdownViaChannelBeforeReply(c *C) { | |
| mgr, err := apparmorprompting.New(s.noticeMgr) | ||
| c.Assert(err, IsNil) | ||
|
|
||
| snapdShuttingDown := make(chan struct{}) | ||
|
|
||
| // Call Ask, then signal when response has been validated | ||
| doneChan := make(chan struct{}) | ||
| go func() { | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup, snapdShuttingDown) | ||
| outcome, err := mgr.Ask(uid, iface, snap, pid, cgroup) | ||
| c.Check(outcome, Equals, prompting.OutcomeUnset) | ||
| c.Check(err, Equals, prompting_errors.ErrPromptingClosed) | ||
| close(doneChan) | ||
|
|
@@ -909,14 +891,14 @@ func (s *apparmorpromptingSuite) TestAskShutdownViaChannelBeforeReply(c *C) { | |
| c.Errorf("manager failed to become ready after receiving request") | ||
| } | ||
|
|
||
| // Now Ask should be waiting for a reply. Close snapdShuttingDown instead. | ||
| close(snapdShuttingDown) | ||
| // Now Ask should be waiting for a reply. Shut down the manager instead. | ||
| mgr.ShutDown() | ||
|
|
||
| select { | ||
| case <-doneChan: | ||
| // all good | ||
| case <-time.After(time.Second): | ||
| c.Errorf("Ask failed to finish after closing snapdShuttingDown") | ||
| c.Errorf("Ask failed to finish after manager shutdown") | ||
| } | ||
|
|
||
| // Check that calls to Reply() also return immediately now that the shutdown | ||
|
|
@@ -925,6 +907,8 @@ func (s *apparmorpromptingSuite) TestAskShutdownViaChannelBeforeReply(c *C) { | |
| clientActivity := false | ||
| _, err = mgr.PromptDB().Reply(uid, promptID, outcome, clientActivity) | ||
| c.Check(err, Equals, prompting_errors.ErrPromptingClosed) | ||
|
|
||
| c.Check(mgr.Stop(), IsNil) | ||
| } | ||
|
|
||
| func (s *apparmorpromptingSuite) TestExistingRuleAllowsNewPrompt(c *C) { | ||
|
|
@@ -1873,8 +1857,7 @@ func (s *apparmorpromptingSuite) TestListenerReadyCausesPromptsHandleReadyingIfO | |
| // Ask for other request in the background so we can see and respond to the prompt | ||
| whenSent := time.Now() | ||
| go func() { | ||
| snapdShuttingDown := make(chan struct{}) | ||
| mgr.Ask(1000, "audio-record", "firefox", 1234, "some-cgroup", snapdShuttingDown) | ||
| mgr.Ask(1000, "audio-record", "firefox", 1234, "some-cgroup") | ||
| }() | ||
| // Wait for a notice | ||
| ctx, cancel := context.WithTimeout(context.Background(), time.Second) | ||
|
|
@@ -1976,11 +1959,10 @@ func (s *apparmorpromptingSuite) TestListenerReadyNotCausesPromptsHandleReadying | |
|
|
||
| // Now add remaining API requests via Ask() | ||
|
|
||
| shutDownChan := make(chan struct{}) | ||
| outcomeChan := make(chan prompting.OutcomeType) | ||
| errChan := make(chan error) | ||
| go func() { | ||
| outcome, err := mgr.Ask(1000, "audio-record", "obs-studio", 12345, "/cgroup-path/snap.obs-studio.obs-studio-someuuid.scope", shutDownChan) | ||
| outcome, err := mgr.Ask(1000, "audio-record", "obs-studio", 12345, "/cgroup-path/snap.obs-studio.obs-studio-someuuid.scope") | ||
| outcomeChan <- outcome | ||
| errChan <- err | ||
| }() | ||
|
|
@@ -1994,7 +1976,7 @@ func (s *apparmorpromptingSuite) TestListenerReadyNotCausesPromptsHandleReadying | |
| } | ||
|
|
||
| go func() { | ||
| outcome, err := mgr.Ask(1000, "audio-record", "signal-desktop", 67890, "/cgroup-path/snap.signal-desktop.signal-desktop.someuuid.scope", shutDownChan) | ||
| outcome, err := mgr.Ask(1000, "audio-record", "signal-desktop", 67890, "/cgroup-path/snap.signal-desktop.signal-desktop.someuuid.scope") | ||
| outcomeChan <- outcome | ||
| errChan <- err | ||
| }() | ||
|
|
@@ -2018,7 +2000,7 @@ func (s *apparmorpromptingSuite) TestListenerReadyNotCausesPromptsHandleReadying | |
| } | ||
|
|
||
| // Signal that snapd is shutting down and Ask calls should return | ||
| close(shutDownChan) | ||
| mgr.ShutDown() | ||
| for i := 0; i < 2; i++ { | ||
| select { | ||
| case outcome := <-outcomeChan: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -315,6 +315,26 @@ func (m *InterfaceManager) Ensure() error { | |
| return nil | ||
| } | ||
|
|
||
| // interfacesRequestsManagerShutDown calls shutdown on the given manager. | ||
| var interfacesRequestsManagerShutDown = func(interfacesRequestsManager *apparmorprompting.InterfacesRequestsManager) { | ||
| interfacesRequestsManager.ShutDown() | ||
| } | ||
|
|
||
| func (m *InterfaceManager) shutDownInterfacesRequestsManger() { | ||
| m.interfacesRequestsManagerMu.Lock() | ||
| defer m.interfacesRequestsManagerMu.Unlock() | ||
| if m.interfacesRequestsManager == nil { | ||
| return | ||
| } | ||
| interfacesRequestsManagerShutDown(m.interfacesRequestsManager) | ||
|
Comment on lines
+324
to
+329
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may be worth moving this into a dedicated |
||
| } | ||
|
|
||
| // ShutDown implements ShutDowner. It prevents the manager from receiving | ||
| // anymore new requests and reject pending ones. | ||
| func (m *InterfaceManager) ShutDown() { | ||
| m.shutDownInterfacesRequestsManger() | ||
| } | ||
|
|
||
| // Stop implements StateStopper. It stops the udev monitor and prompting, | ||
| // if running. | ||
| func (m *InterfaceManager) Stop() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5703,6 +5703,7 @@ slots: | |
| change := s.addRemoveSnapSecurityChange("consumer") | ||
| s.se.Ensure() | ||
| s.se.Wait() | ||
| s.se.ShutDown() | ||
| s.se.Stop() | ||
|
|
||
| // Change succeeds | ||
|
|
@@ -7156,6 +7157,7 @@ func (s *interfaceManagerSuite) TestManagerTransitionConnectionsCore(c *C) { | |
| s.state.Unlock() | ||
| s.se.Ensure() | ||
| s.se.Wait() | ||
| s.se.ShutDown() | ||
| s.se.Stop() | ||
| s.state.Lock() | ||
|
|
||
|
|
@@ -7904,6 +7906,40 @@ func (s *interfaceManagerSuite) TestInitInterfacesRequestsManagerError(c *C) { | |
| c.Check(warns[0].String(), Matches, fmt.Sprintf(`cannot start prompting backend: %v; prompting will be inactive until snapd is restarted`, createError)) | ||
| } | ||
|
|
||
| func (s *interfaceManagerSuite) TestShutDownInterfacesRequestsManager(c *C) { | ||
| shutDownCount := 0 | ||
| restore := ifacestate.MockInterfacesRequestsManagerShutDown(func(m *apparmorprompting.InterfacesRequestsManager) { | ||
| shutDownCount++ | ||
| }) | ||
| defer restore() | ||
| mgr := ifacestate.NewInterfaceManagerWithAppArmorPrompting(true) | ||
| c.Check(mgr.InterfacesRequestsManager(), Equals, nil) | ||
| mgr.ShutDown() | ||
| c.Check(shutDownCount, Equals, 0) | ||
|
|
||
| restore = ifacestate.MockAssessAppArmorPrompting(func(m *ifacestate.InterfaceManager) bool { | ||
| return true | ||
| }) | ||
| defer restore() | ||
| restore = ifacestate.MockInterfacesRequestsControlHandlerServicePresent(func(m *ifacestate.InterfaceManager) (bool, error) { | ||
| return true, nil | ||
| }) | ||
| defer restore() | ||
| fakeManager := &apparmorprompting.InterfacesRequestsManager{} | ||
| restore = ifacestate.MockCreateInterfacesRequestsManager(func(noticeMgr *notices.NoticeManager) (*apparmorprompting.InterfacesRequestsManager, error) { | ||
| return fakeManager, nil | ||
| }) | ||
| defer restore() | ||
|
|
||
| mgr = s.manager(c) | ||
| c.Check(mgr.InterfacesRequestsManager(), Equals, fakeManager) | ||
|
|
||
| mgr.ShutDown() | ||
| c.Check(shutDownCount, Equals, 1) | ||
|
|
||
| mgr.Stop() | ||
| } | ||
|
|
||
| func (s *interfaceManagerSuite) TestStopInterfacesRequestsManagerError(c *C) { | ||
| restore := ifacestate.MockAssessAppArmorPrompting(func(m *ifacestate.InterfaceManager) bool { | ||
| return true | ||
|
|
@@ -9168,6 +9204,7 @@ func (s *interfaceManagerSuite) TestUDevMonitorInit(c *C) { | |
| for i := 0; i < 5; i++ { | ||
| c.Assert(s.se.Ensure(), IsNil) | ||
| } | ||
| s.se.ShutDown() | ||
| s.se.Stop() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @olivercalder this poses an interesting question, should Stop imply ShutDown if ShutDown was not called yet, or we don't strictly needs this?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't strictly need it, as everything which But conceptually this could be a nice thing to implement at the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @olivercalder @natibek I think the way to look at this is that Stop should work correctly even if ShutDown was not called and stop any (remaining) manager activity, so ShutDown is just a way to phase things (that's not too dissimilar for how shutdown and close work for sockets) |
||
|
|
||
| c.Assert(u.ConnectCalls, Equals, 1) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably fine to remove this test, but does it decrease coverage to do so? I think we end up testing the
snapdShuttingDowncases but now missing them.tomb.Dying()cases in the select statements, yes?