4
4
"fmt"
5
5
"github.com/charmbracelet/bubbles/key"
6
6
tea "github.com/charmbracelet/bubbletea"
7
- "github.com/gorilla/websocket"
8
7
"github.com/hashicorp/nomad/api"
9
8
"github.com/itchyny/gojq"
10
9
"github.com/robinovitch61/wander/internal/dev"
@@ -16,7 +15,7 @@ import (
16
15
"github.com/robinovitch61/wander/internal/tui/message"
17
16
"github.com/robinovitch61/wander/internal/tui/nomad"
18
17
"github.com/robinovitch61/wander/internal/tui/style"
19
- "os"
18
+ "os/exec "
20
19
"strings"
21
20
"time"
22
21
)
@@ -77,19 +76,15 @@ type Model struct {
77
76
78
77
updateID int
79
78
79
+ lastExecContent string
80
+
80
81
eventsStream nomad.EventsStream
81
82
event string
82
83
meta map [string ]string
83
84
84
85
logsStream nomad.LogsStream
85
86
lastLogFinished bool
86
87
87
- execWebSocket * websocket.Conn
88
- execPty * os.File
89
- inPty bool
90
- webSocketConnected bool
91
- lastCommandFinished struct { stdOut , stdErr bool }
92
-
93
88
width , height int
94
89
initialized bool
95
90
err error
@@ -110,7 +105,7 @@ func InitialModel(c Config) Model {
110
105
c .LogoColor ,
111
106
c .URL ,
112
107
c .Version ,
113
- nomad .GetPageKeyHelp (firstPage , false , false , false , false , false , false , nomad .StdOut , false , ! c .StartAllTasksView ),
108
+ nomad .GetPageKeyHelp (firstPage , false , false , false , nomad .StdOut , false , ! c .StartAllTasksView ),
114
109
)
115
110
return Model {
116
111
config : c ,
@@ -163,10 +158,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
163
158
cmds = append (cmds , m .getCurrentPageCmd ())
164
159
} else {
165
160
m .setPageWindowSize ()
166
- if m .currentPage == nomad .ExecPage {
167
- viewportHeightWithoutFooter := m .getCurrentPageModel ().ViewportHeight () - 1 // hardcoded as known today, has to change if footer expands
168
- cmds = append (cmds , nomad .ResizeTty (m .execWebSocket , m .width , viewportHeightWithoutFooter ))
169
- }
170
161
}
171
162
172
163
case nomad.PageLoadedMsg :
@@ -265,43 +256,23 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
265
256
m .updateID = nextUpdateID ()
266
257
}
267
258
268
- case message. PageInputReceivedMsg :
259
+ case nomad. ExecCompleteMsg :
269
260
if m .currentPage == nomad .ExecPage {
270
- m .getCurrentPageModel ().SetLoading (true )
271
- return m , nomad .InitiateWebSocket (m .config .URL , m .config .Token , m .alloc .ID , m .taskName , msg .Input )
261
+ m .getCurrentPageModel ().SetDoesNeedNewInput ()
262
+ m .lastExecContent = strings .TrimSpace (msg .Output )
263
+ m .setPage (nomad .ExecCompletePage )
264
+ cmds = append (cmds , m .getCurrentPageCmd ())
272
265
}
273
266
274
- case nomad.ExecWebSocketConnectedMsg :
275
- m .execWebSocket = msg .WebSocketConnection
276
- m .webSocketConnected = true
277
- m .getCurrentPageModel ().SetLoading (false )
278
- m .setInPty (true )
279
- viewportHeightWithoutFooter := m .getCurrentPageModel ().ViewportHeight () - 1 // hardcoded as known today, has to change if footer expands
280
- cmds = append (cmds , nomad .ResizeTty (m .execWebSocket , m .width , viewportHeightWithoutFooter ))
281
- cmds = append (cmds , nomad .ReadExecWebSocketNextMessage (m .execWebSocket ))
282
- cmds = append (cmds , nomad .SendHeartbeatWithDelay ())
283
-
284
- case nomad.ExecWebSocketHeartbeatMsg :
285
- if m .currentPage == nomad .ExecPage && m .webSocketConnected {
286
- cmds = append (cmds , nomad .SendHeartbeat (m .execWebSocket ))
287
- cmds = append (cmds , nomad .SendHeartbeatWithDelay ())
288
- return m , tea .Batch (cmds ... )
289
- }
290
- return m , nil
291
-
292
- case nomad.ExecWebSocketResponseMsg :
267
+ case message.PageInputReceivedMsg :
293
268
if m .currentPage == nomad .ExecPage {
294
- if msg .Close {
295
- m .webSocketConnected = false
296
- m .setInPty (false )
297
- m .getCurrentPageModel ().AppendToViewport ([]page.Row {{Row : constants .ExecWebSocketClosed }}, true )
298
- m .getCurrentPageModel ().ScrollViewportToBottom ()
299
- } else {
300
- m .appendToViewport (msg .StdOut , m .lastCommandFinished .stdOut )
301
- m .appendToViewport (msg .StdErr , m .lastCommandFinished .stdErr )
302
- m .updateLastCommandFinished (msg .StdOut , msg .StdErr )
303
- cmds = append (cmds , nomad .ReadExecWebSocketNextMessage (m .execWebSocket ))
304
- }
269
+ c := exec .Command ("wander" , "exec" , m .alloc .ID , "--task" , m .taskName , msg .Input )
270
+ stdoutProxy := & nomad.StdoutProxy {}
271
+ c .Stdout = stdoutProxy
272
+ m .getCurrentPageModel ().SetDoesNeedNewInput ()
273
+ return m , tea .ExecProcess (c , func (err error ) tea.Msg {
274
+ return nomad.ExecCompleteMsg {Output : string (stdoutProxy .SavedOutput )}
275
+ })
305
276
}
306
277
}
307
278
@@ -328,7 +299,7 @@ func (m Model) View() string {
328
299
}
329
300
330
301
func (m * Model ) initialize () error {
331
- client , err := m .config .client ()
302
+ client , err := m .config .Client ()
332
303
if err != nil {
333
304
return err
334
305
}
@@ -355,9 +326,6 @@ func (m *Model) initialize() error {
355
326
356
327
func (m * Model ) cleanupCmd () tea.Cmd {
357
328
return func () tea.Msg {
358
- if m .execWebSocket != nil {
359
- nomad .CloseWebSocket (m .execWebSocket )()
360
- }
361
329
return message.CleanupCompleteMsg {}
362
330
}
363
331
}
@@ -377,28 +345,12 @@ func (m *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
377
345
addingQToFilter := m .currentPageFilterFocused ()
378
346
saving := m .currentPageViewportSaving ()
379
347
enteringInput := currentPageModel != nil && currentPageModel .EnteringInput ()
380
- typingQLegitimately := msg .String () == "q" && (addingQToFilter || saving || enteringInput || m .inPty )
381
- ctrlCInPty := m .inPty && msg .String () == "ctrl+c"
382
- if (! ctrlCInPty && ! typingQLegitimately ) || m .err != nil {
348
+ typingQLegitimately := msg .String () == "q" && (addingQToFilter || saving || enteringInput )
349
+ if ! typingQLegitimately || m .err != nil {
383
350
return m .cleanupCmd ()
384
351
}
385
352
}
386
353
387
- if m .currentPage == nomad .ExecPage {
388
- var keypress string
389
- if m .inPty {
390
- if key .Matches (msg , keymap .KeyMap .Back ) {
391
- m .setInPty (false )
392
- return nil
393
- } else {
394
- keypress = nomad .GetKeypress (msg )
395
- return nomad .SendWebSocketMessage (m .execWebSocket , keypress )
396
- }
397
- } else if key .Matches (msg , keymap .KeyMap .Forward ) && m .webSocketConnected && ! m .currentPageViewportSaving () {
398
- m .setInPty (true )
399
- }
400
- }
401
-
402
354
if ! m .currentPageFilterFocused () && ! m .currentPageViewportSaving () {
403
355
switch {
404
356
case key .Matches (msg , keymap .KeyMap .Compact ):
@@ -436,9 +388,6 @@ func (m *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
436
388
if ! m .currentPageFilterApplied () {
437
389
switch m .currentPage {
438
390
case nomad .ExecPage :
439
- if ! m .getCurrentPageModel ().EnteringInput () {
440
- cmds = append (cmds , nomad .CloseWebSocket (m .execWebSocket ))
441
- }
442
391
m .getCurrentPageModel ().SetDoesNeedNewInput ()
443
392
}
444
393
@@ -618,32 +567,8 @@ func (m *Model) appendToViewport(content string, startOnNewLine bool) {
618
567
m .getCurrentPageModel ().ScrollViewportToBottom ()
619
568
}
620
569
621
- // updateLastCommandFinished updates lastCommandFinished, which is necessary
622
- // because some data gets received in chunks in which a trailing \n indicates
623
- // finished content, otherwise more content is expected (e.g. the exec
624
- // websocket behaves this way when returning long content)
625
- func (m * Model ) updateLastCommandFinished (stdOut , stdErr string ) {
626
- m .lastCommandFinished .stdOut = false
627
- m .lastCommandFinished .stdErr = false
628
- if strings .HasSuffix (stdOut , "\n " ) {
629
- m .lastCommandFinished .stdOut = true
630
- }
631
- if strings .HasSuffix (stdErr , "\n " ) {
632
- m .lastCommandFinished .stdErr = true
633
- }
634
- }
635
-
636
- func (m * Model ) setInPty (inPty bool ) {
637
- m .inPty = inPty
638
- m .getCurrentPageModel ().SetViewportPromptVisible (inPty )
639
- if inPty {
640
- m .getCurrentPageModel ().ScrollViewportToBottom ()
641
- }
642
- m .updateKeyHelp ()
643
- }
644
-
645
570
func (m * Model ) updateKeyHelp () {
646
- newKeyHelp := nomad .GetPageKeyHelp (m .currentPage , m .currentPageFilterFocused (), m .currentPageFilterApplied (), m .currentPageViewportSaving (), m .getCurrentPageModel (). EnteringInput (), m . inPty , m . webSocketConnected , m . logType , m .compact , m .inJobsMode )
571
+ newKeyHelp := nomad .GetPageKeyHelp (m .currentPage , m .currentPageFilterFocused (), m .currentPageFilterApplied (), m .currentPageViewportSaving (), m .logType , m .compact , m .inJobsMode )
647
572
m .header .SetKeyHelp (newKeyHelp )
648
573
}
649
574
@@ -682,7 +607,23 @@ func (m Model) getCurrentPageCmd() tea.Cmd {
682
607
case nomad .JobTasksPage :
683
608
return nomad .FetchTasksForJob (m .client , m .jobID , m .jobNamespace , m .config .JobTaskColumns )
684
609
case nomad .ExecPage :
685
- return nomad .LoadExecPage ()
610
+ return func () tea.Msg {
611
+ // this does no async work, just moves to request the command input
612
+ return nomad.PageLoadedMsg {Page : nomad .ExecPage , TableHeader : []string {}, AllPageRows : []page.Row {}}
613
+ }
614
+ case nomad .ExecCompletePage :
615
+ return func () tea.Msg {
616
+ // this does no async work, just shows the output of the prior exec session
617
+ var allPageRows []page.Row
618
+ for _ , row := range strings .Split (m .lastExecContent , "\n " ) {
619
+ row = strings .ReplaceAll (row , "\r " , "" )
620
+ if len (row ) == 0 {
621
+ continue
622
+ }
623
+ allPageRows = append (allPageRows , page.Row {Row : formatter .StripOSCommandSequences (formatter .StripANSI (row ))})
624
+ }
625
+ return nomad.PageLoadedMsg {Page : nomad .ExecCompletePage , TableHeader : []string {"Exec Session Output" }, AllPageRows : allPageRows }
626
+ }
686
627
case nomad .AllocSpecPage :
687
628
return nomad .FetchAllocSpec (m .client , m .alloc .ID )
688
629
case nomad .LogsPage :
0 commit comments