feat: error handling
This commit is contained in:
parent
96117c0a15
commit
99766c8230
@ -106,7 +106,12 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
applyServerState(serverState, state)
|
||||
updateUI()
|
||||
case mpState := <-mp.C():
|
||||
applyMultiplexerState(mpState, state)
|
||||
destErrors := applyMultiplexerState(mpState, state)
|
||||
|
||||
for _, destError := range destErrors {
|
||||
handleDestError(destError, mp, ui)
|
||||
}
|
||||
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
@ -117,18 +122,49 @@ func applyServerState(serverState domain.Source, appState *domain.AppState) {
|
||||
appState.Source = serverState
|
||||
}
|
||||
|
||||
// destinationError holds the information needed to display a destination
|
||||
// error.
|
||||
type destinationError struct {
|
||||
name string
|
||||
url string
|
||||
err error
|
||||
}
|
||||
|
||||
// applyMultiplexerState applies the current multiplexer state to the app state.
|
||||
func applyMultiplexerState(mpState multiplexer.State, appState *domain.AppState) {
|
||||
for i, dest := range appState.Destinations {
|
||||
//
|
||||
// It returns a list of destination errors that should be displayed to the user.
|
||||
func applyMultiplexerState(mpState multiplexer.State, appState *domain.AppState) []destinationError {
|
||||
var errorsToDisplay []destinationError
|
||||
|
||||
for i := range appState.Destinations {
|
||||
dest := &appState.Destinations[i]
|
||||
|
||||
if dest.URL != mpState.URL {
|
||||
continue
|
||||
}
|
||||
|
||||
appState.Destinations[i].Container = mpState.Container
|
||||
appState.Destinations[i].Status = mpState.Status
|
||||
if dest.Container.Err == nil && mpState.Container.Err != nil {
|
||||
errorsToDisplay = append(errorsToDisplay, destinationError{
|
||||
name: dest.Name,
|
||||
url: dest.URL,
|
||||
err: mpState.Container.Err,
|
||||
})
|
||||
}
|
||||
|
||||
dest.Container = mpState.Container
|
||||
dest.Status = mpState.Status
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return errorsToDisplay
|
||||
}
|
||||
|
||||
// handleDestError displays a modal to the user, and stops the destination.
|
||||
func handleDestError(destError destinationError, mp *multiplexer.Actor, ui *terminal.UI) {
|
||||
ui.ShowDestinationErrorModal(destError.name, destError.err)
|
||||
|
||||
mp.StopDestination(destError.url)
|
||||
}
|
||||
|
||||
// newStateFromRunParams creates a new app state from the run parameters.
|
||||
|
@ -263,11 +263,8 @@ func (a *Client) runContainerLoop(
|
||||
case resp := <-respC:
|
||||
// Check if the container is restarting. If it is not then we don't
|
||||
// want to wait for it again and can return early.
|
||||
//
|
||||
// Low priority: is the API call necessary?
|
||||
ctr, err := a.apiClient.ContainerInspect(ctx, containerID)
|
||||
if err != nil {
|
||||
// TODO: error handling?
|
||||
a.logger.Error("Error inspecting container", "err", err, "id", shortID(containerID))
|
||||
containerErrC <- err
|
||||
return
|
||||
@ -282,7 +279,6 @@ func (a *Client) runContainerLoop(
|
||||
}
|
||||
case err := <-errC:
|
||||
// Otherwise, this is probably unexpected and we need to handle it.
|
||||
// TODO: improve handling
|
||||
containerErrC <- err
|
||||
return
|
||||
case <-ctx.Done():
|
||||
|
@ -39,6 +39,7 @@ type Source struct {
|
||||
ExitReason string
|
||||
}
|
||||
|
||||
// DestinationStatus reflects the high-level status of a single destination.
|
||||
type DestinationStatus int
|
||||
|
||||
const (
|
||||
@ -81,4 +82,5 @@ type Container struct {
|
||||
RxSince time.Time
|
||||
RestartCount int
|
||||
ExitCode *int
|
||||
Err error // Err is set if any error was received from the container client.
|
||||
}
|
||||
|
@ -234,6 +234,7 @@ func (s *Actor) actorLoop(containerStateC <-chan domain.Container, errC <-chan e
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to use container.Err?
|
||||
func (s *Actor) handleContainerExit(err error) {
|
||||
if s.state.Container.ExitCode != nil {
|
||||
s.state.ExitReason = fmt.Sprintf("Server process exited with code %d.", *s.state.Container.ExitCode)
|
||||
|
@ -84,6 +84,9 @@ func (a *Actor) StartDestination(url string) {
|
||||
return
|
||||
}
|
||||
|
||||
a.nextIndex++
|
||||
a.currURLs[url] = struct{}{}
|
||||
|
||||
a.logger.Info("Starting live stream", "url", url)
|
||||
|
||||
containerStateC, errC := a.containerClient.RunContainer(a.ctx, container.RunContainerParams{
|
||||
@ -108,9 +111,6 @@ func (a *Actor) StartDestination(url string) {
|
||||
NetworkCountConfig: container.NetworkCountConfig{Rx: "eth1", Tx: "eth0"},
|
||||
})
|
||||
|
||||
a.nextIndex++
|
||||
a.currURLs[url] = struct{}{}
|
||||
|
||||
a.wg.Add(1)
|
||||
go func() {
|
||||
defer a.wg.Done()
|
||||
@ -146,12 +146,6 @@ func (a *Actor) StopDestination(url string) {
|
||||
|
||||
// destLoop is the actor loop for a destination stream.
|
||||
func (a *Actor) destLoop(url string, containerStateC <-chan domain.Container, errC <-chan error) {
|
||||
defer func() {
|
||||
a.actorC <- func() {
|
||||
delete(a.currURLs, url)
|
||||
}
|
||||
}()
|
||||
|
||||
state := &State{URL: url}
|
||||
sendState := func() { a.stateC <- *state }
|
||||
|
||||
@ -171,10 +165,11 @@ func (a *Actor) destLoop(url string, containerStateC <-chan domain.Container, er
|
||||
}
|
||||
sendState()
|
||||
case err := <-errC:
|
||||
// TODO: error handling
|
||||
if err != nil {
|
||||
a.logger.Error("Error from container client", "err", err)
|
||||
}
|
||||
state.Container.Err = err
|
||||
sendState()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +251,6 @@ func (ui *UI) ShowStartupCheckModal() bool {
|
||||
[]string{"Continue", "Exit"},
|
||||
func(buttonIndex int, _ string) {
|
||||
if buttonIndex == 0 {
|
||||
ui.pages.RemovePage("modal")
|
||||
ui.app.SetFocus(ui.destView)
|
||||
done <- true
|
||||
} else {
|
||||
@ -264,6 +263,27 @@ func (ui *UI) ShowStartupCheckModal() bool {
|
||||
return <-done
|
||||
}
|
||||
|
||||
func (ui *UI) ShowDestinationErrorModal(name string, err error) {
|
||||
done := make(chan struct{})
|
||||
|
||||
ui.app.QueueUpdateDraw(func() {
|
||||
ui.showModal(
|
||||
modalGroupStartupCheck,
|
||||
fmt.Sprintf(
|
||||
"Streaming to %s failed:\n\n%s",
|
||||
cmp.Or(name, "this destination"),
|
||||
err,
|
||||
),
|
||||
[]string{"Ok"},
|
||||
func(int, string) {
|
||||
done <- struct{}{}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
// AllowQuit enables the quit action.
|
||||
func (ui *UI) AllowQuit() {
|
||||
ui.mu.Lock()
|
||||
@ -580,7 +600,6 @@ func (ui *UI) confirmQuit() {
|
||||
[]string{"Quit", "Cancel"},
|
||||
func(buttonIndex int, _ string) {
|
||||
if buttonIndex == 0 {
|
||||
ui.logger.Info("quitting")
|
||||
ui.commandCh <- CommandQuit{}
|
||||
return
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user