@@ -27,11 +27,12 @@ import (
2727)
2828
2929type terminalConfig struct {
30- enabled bool
31- containerName string
32- command []string
33- allowedOrigins map [string ]struct {}
34- sessionMode terminalSessionMode
30+ enabled bool
31+ containerName string
32+ command []string
33+ allowedOrigins map [string ]struct {}
34+ sessionMode terminalSessionMode
35+ activityDebounce time.Duration
3536}
3637
3738type terminalSessionMode string
@@ -43,11 +44,12 @@ const (
4344
4445func newTerminalConfig () terminalConfig {
4546 return terminalConfig {
46- enabled : parseBoolEnv ("SPRITZ_TERMINAL_ENABLED" , true ),
47- containerName : envOrDefault ("SPRITZ_TERMINAL_CONTAINER" , "spritz" ),
48- command : splitCommand (envOrDefault ("SPRITZ_TERMINAL_COMMAND" , "bash -l" )),
49- allowedOrigins : splitSet (os .Getenv ("SPRITZ_TERMINAL_ORIGINS" )),
50- sessionMode : parseTerminalSessionMode (os .Getenv ("SPRITZ_TERMINAL_SESSION_MODE" )),
47+ enabled : parseBoolEnv ("SPRITZ_TERMINAL_ENABLED" , true ),
48+ containerName : envOrDefault ("SPRITZ_TERMINAL_CONTAINER" , "spritz" ),
49+ command : splitCommand (envOrDefault ("SPRITZ_TERMINAL_COMMAND" , "bash -l" )),
50+ allowedOrigins : splitSet (os .Getenv ("SPRITZ_TERMINAL_ORIGINS" )),
51+ sessionMode : parseTerminalSessionMode (os .Getenv ("SPRITZ_TERMINAL_SESSION_MODE" )),
52+ activityDebounce : parseDurationEnv ("SPRITZ_TERMINAL_ACTIVITY_DEBOUNCE" , 5 * time .Second ),
5153 }
5254}
5355
@@ -233,16 +235,17 @@ func (s *server) streamTerminal(ctx context.Context, namespace, name string, pod
233235 stdinReader , stdinWriter := io .Pipe ()
234236 sizeQueue := newTerminalSizeQueue ()
235237 wsWriter := & terminalWSWriter {conn : conn }
238+ reportActivity := debounceTerminalActivity (s .terminal .activityDebounce , func () {
239+ refreshCtx , cancel := context .WithTimeout (context .Background (), 2 * time .Second )
240+ defer cancel ()
241+ if err := s .markSpritzActivity (refreshCtx , namespace , name , time .Now ()); err != nil {
242+ log .Printf ("spritz terminal: failed to refresh activity name=%s namespace=%s pod=%s err=%v" , name , namespace , pod .Name , err )
243+ }
244+ })
236245
237246 readErr := make (chan error , 1 )
238247 go func () {
239- readErr <- readTerminalInput (ctx , conn , stdinWriter , sizeQueue , func () {
240- refreshCtx , cancel := context .WithTimeout (context .Background (), 2 * time .Second )
241- defer cancel ()
242- if err := s .markSpritzActivity (refreshCtx , namespace , name , time .Now ()); err != nil {
243- log .Printf ("spritz terminal: failed to refresh activity name=%s namespace=%s pod=%s err=%v" , name , namespace , pod .Name , err )
244- }
245- })
248+ readErr <- readTerminalInput (ctx , conn , stdinWriter , sizeQueue , reportActivity )
246249 }()
247250
248251 streamErr := executor .StreamWithContext (ctx , remotecommand.StreamOptions {
@@ -303,6 +306,28 @@ func readTerminalInput(ctx context.Context, conn *websocket.Conn, stdin *io.Pipe
303306 }
304307}
305308
309+ func debounceTerminalActivity (interval time.Duration , report func ()) func () {
310+ if report == nil {
311+ return func () {}
312+ }
313+ if interval <= 0 {
314+ return report
315+ }
316+ var mu sync.Mutex
317+ var last time.Time
318+ return func () {
319+ now := time .Now ()
320+ mu .Lock ()
321+ if ! last .IsZero () && now .Sub (last ) < interval {
322+ mu .Unlock ()
323+ return
324+ }
325+ last = now
326+ mu .Unlock ()
327+ report ()
328+ }
329+ }
330+
306331func handleResize (payload []byte , sizeQueue * terminalSizeQueue ) bool {
307332 var msg resizeMessage
308333 if err := json .Unmarshal (payload , & msg ); err != nil {
0 commit comments