From e3ca34e8e0501d4674c578a0e0934c2b222d04e6 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Wed, 5 Mar 2025 04:51:06 +0100 Subject: [PATCH] refactor: container status constants --- .gitignore | 2 +- container/container.go | 12 ++++++------ container/container_test.go | 14 +++++++------- container/integration_test.go | 11 ++++++----- domain/types.go | 19 +++++++++++++++---- mediaserver/actor.go | 2 +- multiplexer/multiplexer.go | 2 +- terminal/terminal.go | 16 ++++++++-------- 8 files changed, 45 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index c5425e5..c3b5d86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/octoplex.log +*.log /mediamtx.yml diff --git a/container/container.go b/container/container.go index a07ea53..370f232 100644 --- a/container/container.go +++ b/container/container.go @@ -185,7 +185,7 @@ func (a *Client) RunContainer(ctx context.Context, params RunContainerParams) (< sendError(fmt.Errorf("container create: %w", err)) return } - containerStateC <- domain.Container{ID: createResp.ID, State: "created"} + containerStateC <- domain.Container{ID: createResp.ID, Status: domain.ContainerStatusCreated} if err = a.apiClient.NetworkConnect(ctx, a.networkID, createResp.ID, nil); err != nil { sendError(fmt.Errorf("network connect: %w", err)) @@ -198,7 +198,7 @@ func (a *Client) RunContainer(ctx context.Context, params RunContainerParams) (< } a.logger.Info("Started container", "id", shortID(createResp.ID), "duration", time.Since(now)) - containerStateC <- domain.Container{ID: createResp.ID, State: "running"} + containerStateC <- domain.Container{ID: createResp.ID, Status: domain.ContainerStatusRunning} a.runContainerLoop(ctx, createResp.ID, params.NetworkCountConfig, containerStateC, errC) }() @@ -216,7 +216,7 @@ func (a *Client) pullImageIfNeeded(ctx context.Context, imageName string, contai return nil } - containerStateC <- domain.Container{State: "pulling"} + containerStateC <- domain.Container{Status: domain.ContainerStatusPulling} pullReader, err := a.apiClient.ImagePull(ctx, imageName, image.PullOptions{}) if err != nil { @@ -292,7 +292,7 @@ func (a *Client) runContainerLoop( } }() - state := &domain.Container{ID: containerID, State: "running"} + state := &domain.Container{ID: containerID, Status: domain.ContainerStatusRunning} sendState := func() { stateC <- *state } sendState() @@ -308,7 +308,7 @@ func (a *Client) runContainerLoop( containerState = "exited" } - state.State = containerState + state.Status = containerState state.CPUPercent = 0 state.MemoryUsageBytes = 0 state.HealthState = "unhealthy" @@ -338,7 +338,7 @@ func (a *Client) runContainerLoop( } if evt.Action == "start" { - state.State = "running" + state.Status = domain.ContainerStatusRunning sendState() continue } diff --git a/container/container_test.go b/container/container_test.go index 53a9588..544767c 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -89,10 +89,10 @@ func TestClientRunContainer(t *testing.T) { require.NoError(t, <-errC) }() - assert.Equal(t, "pulling", (<-containerStateC).State) - assert.Equal(t, "created", (<-containerStateC).State) - assert.Equal(t, "running", (<-containerStateC).State) - assert.Equal(t, "running", (<-containerStateC).State) + assert.Equal(t, "pulling", (<-containerStateC).Status) + assert.Equal(t, "created", (<-containerStateC).Status) + assert.Equal(t, "running", (<-containerStateC).Status) + assert.Equal(t, "running", (<-containerStateC).Status) // Enough time for events channel to receive a message: time.Sleep(100 * time.Millisecond) @@ -100,7 +100,7 @@ func TestClientRunContainer(t *testing.T) { containerWaitC <- dockercontainer.WaitResponse{StatusCode: 1} state := <-containerStateC - assert.Equal(t, "exited", state.State) + assert.Equal(t, "exited", state.Status) assert.Equal(t, "unhealthy", state.HealthState) require.NotNil(t, state.ExitCode) assert.Equal(t, 1, *state.ExitCode) @@ -146,8 +146,8 @@ func TestClientRunContainerErrorStartingContainer(t *testing.T) { HostConfig: &dockercontainer.HostConfig{}, }) - assert.Equal(t, "pulling", (<-containerStateC).State) - assert.Equal(t, "created", (<-containerStateC).State) + assert.Equal(t, "pulling", (<-containerStateC).Status) + assert.Equal(t, "created", (<-containerStateC).Status) err = <-errC require.EqualError(t, err, "container start: error starting container") diff --git a/container/integration_test.go b/container/integration_test.go index 3595507..c81dd59 100644 --- a/container/integration_test.go +++ b/container/integration_test.go @@ -9,6 +9,7 @@ import ( "time" "git.netflux.io/rob/octoplex/container" + "git.netflux.io/rob/octoplex/domain" "git.netflux.io/rob/octoplex/shortid" "git.netflux.io/rob/octoplex/testhelpers" typescontainer "github.com/docker/docker/api/types/container" @@ -196,11 +197,11 @@ func TestContainerRestart(t *testing.T) { testhelpers.ChanRequireNoError(t, errC) containerState := <-containerStateC - assert.Equal(t, "pulling", containerState.State) + assert.Equal(t, "pulling", containerState.Status) containerState = <-containerStateC - assert.Equal(t, "created", containerState.State) + assert.Equal(t, "created", containerState.Status) containerState = <-containerStateC - assert.Equal(t, "running", containerState.State) + assert.Equal(t, "running", containerState.Status) err = nil // reset error done := make(chan struct{}) @@ -210,9 +211,9 @@ func TestContainerRestart(t *testing.T) { var count int for { containerState = <-containerStateC - if containerState.State == "restarting" { + if containerState.Status == domain.ContainerStatusRestarting { break - } else if containerState.State == "exited" { + } else if containerState.Status == domain.ContainerStatusExited { err = errors.New("container exited unexpectedly") } else if count >= 5 { err = errors.New("container did not enter restarting state") diff --git a/domain/types.go b/domain/types.go index 32c2938..d19da79 100644 --- a/domain/types.go +++ b/domain/types.go @@ -55,13 +55,24 @@ type Destination struct { URL string } -// Container represents the current state of an individual container. +// Container status strings. // -// The source of truth is always the Docker daemon, this struct is used only -// for passing asynchronous state. +// TODO: refactor to strictly reflect Docker status strings. +const ( + ContainerStatusPulling = "pulling" // Does not correspond to a Docker status. + ContainerStatusCreated = "created" + ContainerStatusRunning = "running" + ContainerStatusPaused = "paused" + ContainerStatusRestarting = "restarting" + ContainerStatusRemoving = "removing" + ContainerStatusExited = "exited" + ContainerStatusDead = "dead" +) + +// Container represents the current state of an individual container. type Container struct { ID string - State string + Status string HealthState string CPUPercent float64 MemoryUsageBytes uint64 diff --git a/mediaserver/actor.go b/mediaserver/actor.go index 76c6b52..80cc132 100644 --- a/mediaserver/actor.go +++ b/mediaserver/actor.go @@ -172,7 +172,7 @@ func (s *Actor) actorLoop(containerStateC <-chan domain.Container, errC <-chan e case containerState := <-containerStateC: s.state.Container = containerState - if s.state.Container.State == "exited" { + if s.state.Container.Status == domain.ContainerStatusExited { fetchStateT.Stop() s.handleContainerExit(nil) } diff --git a/multiplexer/multiplexer.go b/multiplexer/multiplexer.go index ea076e5..f006eb5 100644 --- a/multiplexer/multiplexer.go +++ b/multiplexer/multiplexer.go @@ -146,7 +146,7 @@ func (a *Actor) destLoop(url string, containerStateC <-chan domain.Container, er case containerState := <-containerStateC: state.Container = containerState - if containerState.State == "running" { + if containerState.Status == "running" { if hasElapsedSince(5*time.Second, containerState.RxSince) { state.Status = domain.DestinationStatusLive } else { diff --git a/terminal/terminal.go b/terminal/terminal.go index dd7cfd9..a8abb1d 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -326,7 +326,7 @@ func (ui *UI) redrawFromState(state domain.AppState) { } ui.sourceViews.status.SetText("[black:green]receiving" + durStr) - } else if state.Source.Container.State == "running" && state.Source.Container.HealthState == "healthy" { + } else if state.Source.Container.Status == domain.ContainerStatusRunning && state.Source.Container.HealthState == "healthy" { ui.sourceViews.status.SetText("[black:yellow]ready") } else { ui.sourceViews.status.SetText("[white:red]not ready") @@ -335,19 +335,19 @@ func (ui *UI) redrawFromState(state domain.AppState) { ui.sourceViews.health.SetText("[white]" + cmp.Or(rightPad(state.Source.Container.HealthState, 9), dash)) cpuPercent := dash - if state.Source.Container.State == "running" { + if state.Source.Container.Status == domain.ContainerStatusRunning { cpuPercent = fmt.Sprintf("%.1f", state.Source.Container.CPUPercent) } ui.sourceViews.cpu.SetText("[white]" + cpuPercent) memUsage := dash - if state.Source.Container.State == "running" { + if state.Source.Container.Status == domain.ContainerStatusRunning { memUsage = fmt.Sprintf("%.1f", float64(state.Source.Container.MemoryUsageBytes)/1024/1024) } ui.sourceViews.mem.SetText("[white]" + memUsage) rxRate := dash - if state.Source.Container.State == "running" { + if state.Source.Container.Status == domain.ContainerStatusRunning { rxRate = fmt.Sprintf("%d", state.Source.Container.RxRate) } ui.sourceViews.rx.SetText("[white]" + rxRate) @@ -385,7 +385,7 @@ func (ui *UI) redrawFromState(state domain.AppState) { ui.destView.SetCell(i+1, 2, tview.NewTableCell("[white]"+rightPad("off-air", statusLen))) } - ui.destView.SetCell(i+1, 3, tview.NewTableCell("[white]"+rightPad(cmp.Or(dest.Container.State, dash), 10))) + ui.destView.SetCell(i+1, 3, tview.NewTableCell("[white]"+rightPad(cmp.Or(dest.Container.Status, dash), 10))) healthState := dash if dest.Status == domain.DestinationStatusLive { @@ -394,19 +394,19 @@ func (ui *UI) redrawFromState(state domain.AppState) { ui.destView.SetCell(i+1, 4, tview.NewTableCell("[white]"+rightPad(healthState, 7))) cpuPercent := dash - if dest.Container.State == "running" { + if dest.Container.Status == domain.ContainerStatusRunning { cpuPercent = fmt.Sprintf("%.1f", dest.Container.CPUPercent) } ui.destView.SetCell(i+1, 5, tview.NewTableCell("[white]"+rightPad(cpuPercent, 4))) memoryUsage := dash - if dest.Container.State == "running" { + if dest.Container.Status == domain.ContainerStatusRunning { memoryUsage = fmt.Sprintf("%.1f", float64(dest.Container.MemoryUsageBytes)/1000/1000) } ui.destView.SetCell(i+1, 6, tview.NewTableCell("[white]"+rightPad(memoryUsage, 4))) txRate := dash - if dest.Container.State == "running" { + if dest.Container.Status == domain.ContainerStatusRunning { txRate = "[white]" + rightPad(strconv.Itoa(dest.Container.TxRate), 4) } ui.destView.SetCell(i+1, 7, tview.NewTableCell(txRate))