@@ -2,10 +2,8 @@ package container // import "github.com/docker/docker/integration/container"
22
33import (
44 "context"
5- "encoding/json"
65 "fmt"
76 "os"
8- "path/filepath"
97 "strconv"
108 "strings"
119 "testing"
@@ -19,6 +17,7 @@ import (
1917 "golang.org/x/sys/unix"
2018 "gotest.tools/v3/assert"
2119 is "gotest.tools/v3/assert/cmp"
20+ "gotest.tools/v3/assert/opt"
2221 "gotest.tools/v3/skip"
2322)
2423
@@ -204,21 +203,10 @@ func TestRestartDaemonWithRestartingContainer(t *testing.T) {
204203
205204 d .Stop (t )
206205
207- configPath := filepath .Join (d .Root , "containers" , id , "config.v2.json" )
208- configBytes , err := os .ReadFile (configPath )
209- assert .NilError (t , err )
210-
211- var c realcontainer.Container
212-
213- assert .NilError (t , json .Unmarshal (configBytes , & c ))
214-
215- c .State = realcontainer .NewState ()
216- c .SetRestarting (& realcontainer.ExitStatus {ExitCode : 1 })
217- c .HasBeenStartedBefore = true
218-
219- configBytes , err = json .Marshal (& c )
220- assert .NilError (t , err )
221- assert .NilError (t , os .WriteFile (configPath , configBytes , 0600 ))
206+ d .TamperWithContainerConfig (t , id , func (c * realcontainer.Container ) {
207+ c .SetRestarting (& realcontainer.ExitStatus {ExitCode : 1 })
208+ c .HasBeenStartedBefore = true
209+ })
222210
223211 d .Start (t )
224212
@@ -231,3 +219,71 @@ func TestRestartDaemonWithRestartingContainer(t *testing.T) {
231219 assert .NilError (t , err )
232220 }
233221}
222+
223+ // TestHardRestartWhenContainerIsRunning simulates a case where dockerd is
224+ // killed while a container is running, and the container's task no longer
225+ // exists when dockerd starts back up. This can happen if the system is
226+ // hard-rebooted, for example.
227+ //
228+ // Regression test for moby/moby#45788
229+ func TestHardRestartWhenContainerIsRunning (t * testing.T ) {
230+ skip .If (t , testEnv .IsRemoteDaemon , "cannot start daemon on remote test run" )
231+ skip .If (t , testEnv .DaemonInfo .OSType == "windows" )
232+
233+ t .Parallel ()
234+
235+ d := daemon .New (t )
236+ defer d .Cleanup (t )
237+
238+ d .StartWithBusybox (t , "--iptables=false" )
239+ defer d .Stop (t )
240+
241+ ctx := context .Background ()
242+ client := d .NewClientT (t )
243+
244+ // Just create the containers, no need to start them.
245+ // We really want to make sure there is no process running when docker starts back up.
246+ // We will manipulate the on disk state later.
247+ nopolicy := container .Create (ctx , t , client , container .WithCmd ("/bin/sh" , "-c" , "exit 1" ))
248+ onfailure := container .Create (ctx , t , client , container .WithRestartPolicy ("on-failure" ), container .WithCmd ("/bin/sh" , "-c" , "sleep 60" ))
249+
250+ d .Stop (t )
251+
252+ for _ , id := range []string {nopolicy , onfailure } {
253+ d .TamperWithContainerConfig (t , id , func (c * realcontainer.Container ) {
254+ c .SetRunning (nil , nil , true )
255+ c .HasBeenStartedBefore = true
256+ })
257+ }
258+
259+ d .Start (t )
260+
261+ t .Run ("RestartPolicy=none" , func (t * testing.T ) {
262+ ctx , cancel := context .WithTimeout (ctx , 5 * time .Second )
263+ defer cancel ()
264+ inspect , err := client .ContainerInspect (ctx , nopolicy )
265+ assert .NilError (t , err )
266+ assert .Check (t , is .Equal (inspect .State .Status , "exited" ))
267+ assert .Check (t , is .Equal (inspect .State .ExitCode , 255 ))
268+ finishedAt , err := time .Parse (time .RFC3339Nano , inspect .State .FinishedAt )
269+ if assert .Check (t , err ) {
270+ assert .Check (t , is .DeepEqual (finishedAt , time .Now (), opt .TimeWithThreshold (time .Minute )))
271+ }
272+ })
273+
274+ t .Run ("RestartPolicy=on-failure" , func (t * testing.T ) {
275+ ctx , cancel := context .WithTimeout (ctx , 5 * time .Second )
276+ defer cancel ()
277+ inspect , err := client .ContainerInspect (ctx , onfailure )
278+ assert .NilError (t , err )
279+ assert .Check (t , is .Equal (inspect .State .Status , "running" ))
280+ assert .Check (t , is .Equal (inspect .State .ExitCode , 0 ))
281+ finishedAt , err := time .Parse (time .RFC3339Nano , inspect .State .FinishedAt )
282+ if assert .Check (t , err ) {
283+ assert .Check (t , is .DeepEqual (finishedAt , time .Now (), opt .TimeWithThreshold (time .Minute )))
284+ }
285+
286+ stopTimeout := 0
287+ assert .Assert (t , client .ContainerStop (ctx , onfailure , containerapi.StopOptions {Timeout : & stopTimeout }))
288+ })
289+ }
0 commit comments