summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2022-01-11 09:33:47 +0100
committerGitHub <noreply@github.com>2022-01-11 09:33:47 +0100
commit5ccd8a1921c61604d1a4847cd9bd9348da910cab (patch)
tree85d0e139f32dda588d716ee1166e865fc7d20202
parent80b86a3ac52b085f114b1614c6b256132e70ffe7 (diff)
parent7812114ccfcb061f2d2e410b87e9a3e865da56f7 (diff)
Merge pull request #11066 from stolowski/notifications/close-notification
usersession: implement method to close notifications via usersession REST API
-rw-r--r--usersession/agent/export_test.go1
-rw-r--r--usersession/agent/rest_api.go41
-rw-r--r--usersession/agent/rest_api_test.go27
-rw-r--r--usersession/client/client.go16
-rw-r--r--usersession/client/client_test.go17
5 files changed, 102 insertions, 0 deletions
diff --git a/usersession/agent/export_test.go b/usersession/agent/export_test.go
index 027381d370..b0baf18c21 100644
--- a/usersession/agent/export_test.go
+++ b/usersession/agent/export_test.go
@@ -28,6 +28,7 @@ var (
SessionInfoCmd = sessionInfoCmd
ServiceControlCmd = serviceControlCmd
PendingRefreshNotificationCmd = pendingRefreshNotificationCmd
+ FinishRefreshNotificationCmd = finishRefreshNotificationCmd
)
func MockStopTimeouts(stop, kill time.Duration) (restore func()) {
diff --git a/usersession/agent/rest_api.go b/usersession/agent/rest_api.go
index 2a44096402..d5c7ed3c3a 100644
--- a/usersession/agent/rest_api.go
+++ b/usersession/agent/rest_api.go
@@ -36,6 +36,7 @@ import (
"github.com/snapcore/snapd/i18n"
"github.com/snapcore/snapd/systemd"
"github.com/snapcore/snapd/timeout"
+ "github.com/snapcore/snapd/usersession/client"
)
var restApi = []*Command{
@@ -65,6 +66,11 @@ var (
Path: "/v1/notifications/pending-refresh",
POST: postPendingRefreshNotification,
}
+
+ finishRefreshNotificationCmd = &Command{
+ Path: "/v1/notifications/finish-refresh",
+ POST: postRefreshFinishedNotification,
+ }
)
func sessionInfo(c *Command, r *http.Request) Response {
@@ -305,3 +311,38 @@ func postPendingRefreshNotification(c *Command, r *http.Request) Response {
}
return SyncResponse(nil)
}
+
+func postRefreshFinishedNotification(c *Command, r *http.Request) Response {
+ if ok, resp := validateJSONRequest(r); !ok {
+ return resp
+ }
+
+ decoder := json.NewDecoder(r.Body)
+
+ var finishRefresh client.FinishedSnapRefreshInfo
+ if err := decoder.Decode(&finishRefresh); err != nil {
+ return BadRequest("cannot decode request body into finish refresh notification info: %v", err)
+ }
+
+ // Note that since the connection is shared, we are not closing it.
+ if c.s.bus == nil {
+ return SyncResponse(&resp{
+ Type: ResponseTypeError,
+ Status: 500,
+ Result: &errorResult{
+ Message: "cannot connect to the session bus",
+ },
+ })
+ }
+
+ if err := c.s.notificationMgr.CloseNotification(notification.ID(finishRefresh.InstanceName)); err != nil {
+ return SyncResponse(&resp{
+ Type: ResponseTypeError,
+ Status: 500,
+ Result: &errorResult{
+ Message: fmt.Sprintf("cannot send close notification message: %v", err),
+ },
+ })
+ }
+ return SyncResponse(nil)
+}
diff --git a/usersession/agent/rest_api_test.go b/usersession/agent/rest_api_test.go
index 67e3b9024b..b3ab28b2ae 100644
--- a/usersession/agent/rest_api_test.go
+++ b/usersession/agent/rest_api_test.go
@@ -596,3 +596,30 @@ func (s *restSuite) TestPostPendingRefreshNotificationNotificationServerFailure(
c.Check(rsp.Type, Equals, agent.ResponseTypeError)
c.Check(rsp.Result, DeepEquals, map[string]interface{}{"message": "cannot send notification message: org.freedesktop.DBus.Error.Failed"})
}
+
+func (s *restSuite) testPostFinishRefreshNotificationBody(c *C, refreshInfo *client.FinishedSnapRefreshInfo) {
+ reqBody, err := json.Marshal(refreshInfo)
+ c.Assert(err, IsNil)
+ req := httptest.NewRequest("POST", "/v1/notifications/finish-refresh", bytes.NewBuffer(reqBody))
+ req.Header.Set("Content-Type", "application/json")
+ rec := httptest.NewRecorder()
+ agent.FinishRefreshNotificationCmd.POST(agent.PendingRefreshNotificationCmd, req).ServeHTTP(rec, req)
+ c.Check(rec.Code, Equals, 200)
+ c.Check(rec.HeaderMap.Get("Content-Type"), Equals, "application/json")
+
+ var rsp resp
+ c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), IsNil)
+ c.Check(rsp.Type, Equals, agent.ResponseTypeSync)
+ c.Check(rsp.Result, IsNil)
+}
+
+func (s *restSuite) TestPostCloseRefreshNotification(c *C) {
+ // add a notification first
+ refreshInfo := &client.PendingSnapRefreshInfo{InstanceName: "some-snap"}
+ s.testPostPendingRefreshNotificationBody(c, refreshInfo)
+
+ closeInfo := &client.FinishedSnapRefreshInfo{InstanceName: "some-snap"}
+ s.testPostFinishRefreshNotificationBody(c, closeInfo)
+ notifications := s.notify.GetAll()
+ c.Assert(notifications, HasLen, 0)
+}
diff --git a/usersession/client/client.go b/usersession/client/client.go
index 9554257fcc..757de57a16 100644
--- a/usersession/client/client.go
+++ b/usersession/client/client.go
@@ -325,3 +325,19 @@ func (client *Client) PendingRefreshNotification(ctx context.Context, refreshInf
_, err = client.doMany(ctx, "POST", "/v1/notifications/pending-refresh", nil, headers, reqBody)
return err
}
+
+// FinishedSnapRefreshInfo holds information about a finished refresh provided to userd.
+type FinishedSnapRefreshInfo struct {
+ InstanceName string `json:"instance-name"`
+}
+
+// FinishRefreshNotification closes notification about a snap refresh.
+func (client *Client) FinishRefreshNotification(ctx context.Context, closeInfo *FinishedSnapRefreshInfo) error {
+ headers := map[string]string{"Content-Type": "application/json"}
+ reqBody, err := json.Marshal(closeInfo)
+ if err != nil {
+ return err
+ }
+ _, err = client.doMany(ctx, "POST", "/v1/notifications/finish-refresh", nil, headers, reqBody)
+ return err
+}
diff --git a/usersession/client/client_test.go b/usersession/client/client_test.go
index 299cf5c9b2..ddaefeb92f 100644
--- a/usersession/client/client_test.go
+++ b/usersession/client/client_test.go
@@ -22,6 +22,7 @@ package client_test
import (
"context"
"fmt"
+ "io/ioutil"
"net"
"net/http"
"os"
@@ -462,6 +463,22 @@ func (s *clientSuite) TestPendingRefreshNotification(c *C) {
c.Check(atomic.LoadInt32(&n), Equals, int32(2))
}
+func (s *clientSuite) TestFinishRefreshNotification(c *C) {
+ var n int32
+ s.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ atomic.AddInt32(&n, 1)
+ c.Assert(r.URL.Path, Equals, "/v1/notifications/finish-refresh")
+ body, err := ioutil.ReadAll(r.Body)
+ c.Check(err, IsNil)
+ c.Check(string(body), DeepEquals, `{"instance-name":"some-snap"}`)
+ })
+ err := s.cli.FinishRefreshNotification(context.Background(), &client.FinishedSnapRefreshInfo{InstanceName: "some-snap"})
+ c.Assert(err, IsNil)
+ // two calls because clientSuite simulates two user sessions (two
+ // snapd-session-agent.socket sockets).
+ c.Check(atomic.LoadInt32(&n), Equals, int32(2))
+}
+
func (s *clientSuite) TestPendingRefreshNotificationOneClient(c *C) {
cli := client.NewForUids(1000)
var n int32