diff --git a/api/types/container/state.go b/api/types/container/state.go index b9fb2c3375..78d5c4fe85 100644 --- a/api/types/container/state.go +++ b/api/types/container/state.go @@ -1,5 +1,10 @@ package container +import ( + "fmt" + "strings" +) + // ContainerState is a string representation of the container's current state. // // It currently is an alias for string, but may become a distinct type in the future. @@ -15,6 +20,21 @@ const ( StateDead ContainerState = "dead" // StateDead indicates that the container failed to be deleted. Containers in this state are attempted to be cleaned up when the daemon restarts. ) +var validStates = []ContainerState{ + StateCreated, StateRunning, StatePaused, StateRestarting, StateRemoving, StateExited, StateDead, +} + +// ValidateContainerState checks if the provided string is a valid +// container [ContainerState]. +func ValidateContainerState(s ContainerState) error { + switch s { + case StateCreated, StateRunning, StatePaused, StateRestarting, StateRemoving, StateExited, StateDead: + return nil + default: + return errInvalidParameter{error: fmt.Errorf("invalid value for state (%s): must be one of %s", s, strings.Join(validStates, ", "))} + } +} + // StateStatus is used to return container wait results. // Implements exec.ExitCode interface. // This type is needed as State include a sync.Mutex field which make diff --git a/api/types/container/state_test.go b/api/types/container/state_test.go new file mode 100644 index 0000000000..79b7984c98 --- /dev/null +++ b/api/types/container/state_test.go @@ -0,0 +1,33 @@ +package container + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestValidateContainerState(t *testing.T) { + tests := []struct { + state ContainerState + expectedErr string + }{ + {state: StatePaused}, + {state: StateRestarting}, + {state: StateRunning}, + {state: StateDead}, + {state: StateCreated}, + {state: StateExited}, + {state: StateRemoving}, + {state: "invalid-state-string", expectedErr: `invalid value for state (invalid-state-string): must be one of created, running, paused, restarting, removing, exited, dead`}, + } + for _, tc := range tests { + t.Run(tc.state, func(t *testing.T) { + err := ValidateContainerState(tc.state) + if tc.expectedErr == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.expectedErr) + } + }) + } +} diff --git a/container/state.go b/container/state.go index 8ab87c2714..53a76259a4 100644 --- a/container/state.go +++ b/container/state.go @@ -147,15 +147,10 @@ func (s *State) StateString() container.ContainerState { } // IsValidStateString checks if the provided string is a valid container state. +// +// Deprecated: use [container.ValidateContainerState] instead. func IsValidStateString(s container.ContainerState) bool { - switch s { - case container.StateCreated, container.StateRunning, container.StatePaused, - container.StateRestarting, container.StateRemoving, container.StateExited, - container.StateDead: - return true - default: - return false - } + return container.ValidateContainerState(s) == nil } // WaitCondition is an enum type for different states to wait for. diff --git a/container/state_test.go b/container/state_test.go index ecaf0bacfc..c4acca76e6 100644 --- a/container/state_test.go +++ b/container/state_test.go @@ -181,27 +181,3 @@ func TestCorrectStateWaitResultAfterRestart(t *testing.T) { t.Fatalf("expected exit code %v, got %v", want.ExitCode, got.ExitCode()) } } - -func TestIsValidStateString(t *testing.T) { - states := []struct { - state container.ContainerState - expected bool - }{ - {state: container.StatePaused, expected: true}, - {state: container.StateRestarting, expected: true}, - {state: container.StateRunning, expected: true}, - {state: container.StateDead, expected: true}, - {state: "start"}, - {state: container.StateCreated, expected: true}, - {state: container.StateExited, expected: true}, - {state: container.StateRemoving, expected: true}, - {state: "stop"}, - } - - for _, s := range states { - v := IsValidStateString(s.state) - if v != s.expected { - t.Fatalf("Expected %t, but got %t", s.expected, v) - } - } -} diff --git a/daemon/list.go b/daemon/list.go index 251c17b409..6b167e566f 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -279,10 +279,9 @@ func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, conf } err = psFilters.WalkValues("status", func(value string) error { - if !container.IsValidStateString(value) { - return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'status=%s'", value)) + if err := containertypes.ValidateContainerState(value); err != nil { + return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'status=%s': %w", value, err)) } - config.All = true return nil })