desktop playback error handling (#10765)

This commit is contained in:
Isaiah Becker-Mayer 2022-03-03 15:07:38 -05:00 committed by GitHub
parent 32457df042
commit 896dbbbc47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 2 deletions

View file

@ -938,6 +938,10 @@ and non interactive tsh bench loads.
- [ ] Verify async recording (`mode: node` or `mode: proxy`)
- [ ] Sessions show up in session recordings UI with desktop icon
- [ ] Sessions can be played back, including play/pause functionality
- [ ] A session that ends with a TDP error message can be played back, ends by displaying the error message,
and the progress bar progresses to the end.
- [ ] Attempting to play back a session that doesn't exist (i.e. by entering a non-existing session id in the url) shows
a relevant error message.
- [ ] RBAC for sessions: ensure users can only see their own recordings when
using the RBAC rule from our
[docs](../../docs/pages/access-controls/reference.mdx#rbac-for-sessions)

View file

@ -872,7 +872,7 @@ func (s *WindowsService) makeTDPSendHandler(ctx context.Context, emitter events.
id *tlsca.Identity, sessionID, desktopAddr string) func(m tdp.Message, b []byte) {
return func(m tdp.Message, b []byte) {
switch b[0] {
case byte(tdp.TypePNGFrame):
case byte(tdp.TypePNGFrame), byte(tdp.TypeError):
e := &events.DesktopRecording{
Metadata: events.Metadata{
Type: libevents.DesktopRecordingEvent,

View file

@ -19,13 +19,16 @@ package desktop
import (
"context"
"errors"
"fmt"
"net"
"os"
"sync"
"time"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"
"golang.org/x/net/websocket"
)
@ -207,6 +210,15 @@ func (pp *Player) streamSessionEvents(ctx context.Context, cancel context.Cancel
// otherwise it just sits at the player UI
if err != nil && !errors.Is(err, context.Canceled) {
pp.log.WithError(err).Errorf("streaming session %v", pp.sID)
var errorText string
if os.IsNotExist(err) || trace.IsNotFound(err) {
errorText = "session not found"
} else {
errorText = "server error"
}
if _, err := pp.ws.Write([]byte(fmt.Sprintf(`{"message": "error", "errorText": "%v"}`, errorText))); err != nil {
pp.log.WithError(err).Error("failed to write \"error\" message over websocket")
}
}
return
case evt := <-eventsC:
@ -227,6 +239,10 @@ func (pp *Player) streamSessionEvents(ctx context.Context, cancel context.Cancel
msg, err := utils.FastMarshal(e)
if err != nil {
pp.log.WithError(err).Errorf("failed to marshal DesktopRecording event into JSON: %v", e)
if _, err := pp.ws.Write([]byte(`{"message":"error","errorText":"server error"}`)); err != nil {
pp.log.WithError(err).Error("failed to write \"error\" message over websocket")
}
return
}
if _, err := pp.ws.Write(msg); err != nil {
// We expect net.ErrClosed to arise when another goroutine returns before

View file

@ -65,7 +65,7 @@ func TestStreamsDesktopEvents(t *testing.T) {
b := make([]byte, 4096)
n, err := ws.Read(b)
require.NoError(t, err)
require.Equal(t, []byte(`{"message":"end"}`), b[:n])
require.JSONEq(t, `{"message":"end"}`, string(b[:n]))
}
func newServer(t *testing.T, streamInterval time.Duration, events []apievents.AuditEvent) *httptest.Server {