@@ -20,6 +20,9 @@ import (
2020 "github.com/stretchr/testify/require"
2121)
2222
23+ var stopTimeout = 8
24+ var expectedContainerStopOptions = container.StopOptions {Timeout : & stopTimeout }
25+
2326type MockDockerClient struct {
2427 mock.Mock
2528}
@@ -136,15 +139,15 @@ func TestNewDockerManager(t *testing.T) {
136139 {ID : "container2" , Names : []string {"/container2" }, Labels : map [string ]string {containerCreatorLabel : containerCreator }},
137140 }
138141 mockDockerClient .On ("ContainerList" , mock .Anything , mock .Anything ).Return (existingContainers , nil )
139- mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , mock . Anything ).Return (nil )
140- mockDockerClient .On ("ContainerStop" , mock .Anything , "container2" , mock . Anything ).Return (nil )
142+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , expectedContainerStopOptions ).Return (nil )
143+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container2" , expectedContainerStopOptions ).Return (nil )
141144 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container1" , mock .Anything ).Return (nil )
142145 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container2" , mock .Anything ).Return (nil )
143146
144147 // Verify that existing containers were stopped and removed.
145148 createAndVerifyManager ()
146- mockDockerClient .AssertCalled (t , "ContainerStop" , mock .Anything , "container1" , mock . Anything )
147- mockDockerClient .AssertCalled (t , "ContainerStop" , mock .Anything , "container2" , mock . Anything )
149+ mockDockerClient .AssertCalled (t , "ContainerStop" , mock .Anything , "container1" , expectedContainerStopOptions )
150+ mockDockerClient .AssertCalled (t , "ContainerStop" , mock .Anything , "container2" , expectedContainerStopOptions )
148151 mockDockerClient .AssertCalled (t , "ContainerRemove" , mock .Anything , "container1" , mock .Anything )
149152 mockDockerClient .AssertCalled (t , "ContainerRemove" , mock .Anything , "container2" , mock .Anything )
150153 mockDockerClient .AssertExpectations (t )
@@ -236,7 +239,7 @@ func TestDockerManager_Stop(t *testing.T) {
236239 },
237240 }
238241
239- MockDockerClient .On ("ContainerStop" , mock .Anything , containerID , container. StopOptions { Timeout : nil } ).Return (nil )
242+ MockDockerClient .On ("ContainerStop" , mock .Anything , containerID , expectedContainerStopOptions ).Return (nil )
240243 MockDockerClient .On ("ContainerRemove" , mock .Anything , containerID , container.RemoveOptions {}).Return (nil )
241244 err := dockerManager .Stop (ctx )
242245 require .NoError (t , err )
@@ -650,7 +653,7 @@ func TestDockerManager_allocGPU(t *testing.T) {
650653 dockerManager .gpuContainers [rc .GPU ] = rc
651654 dockerManager .containers [rc .Name ] = rc
652655 // Mock client methods to simulate the removal of the warm container.
653- mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , container. StopOptions {} ).Return (nil )
656+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , expectedContainerStopOptions ).Return (nil )
654657 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container1" , container.RemoveOptions {}).Return (nil )
655658 },
656659 expectedAllocatedGPU : "gpu0" ,
@@ -694,7 +697,7 @@ func TestDockerManager_destroyContainer(t *testing.T) {
694697 dockerManager .gpuContainers [gpu ] = rc
695698 dockerManager .containers [containerID ] = rc
696699
697- mockDockerClient .On ("ContainerStop" , mock .Anything , containerID , container. StopOptions {} ).Return (nil )
700+ mockDockerClient .On ("ContainerStop" , mock .Anything , containerID , expectedContainerStopOptions ).Return (nil )
698701 mockDockerClient .On ("ContainerRemove" , mock .Anything , containerID , container.RemoveOptions {}).Return (nil )
699702
700703 err := dockerManager .destroyContainer (rc , true )
@@ -844,7 +847,7 @@ func TestDockerManager_watchContainer(t *testing.T) {
844847 tt .mockServerSetup (mockServer )
845848
846849 // Mock destroyContainer to verify it is called.
847- mockDockerClient .On ("ContainerStop" , mock .Anything , rc .Name , mock . Anything ).Return (nil ).Once ()
850+ mockDockerClient .On ("ContainerStop" , mock .Anything , rc .Name , expectedContainerStopOptions ).Return (nil ).Once ()
848851 mockDockerClient .On ("ContainerRemove" , mock .Anything , rc .Name , mock .Anything ).Return (nil ).Once ()
849852
850853 done := make (chan struct {})
@@ -906,7 +909,7 @@ func TestDockerManager_watchContainer(t *testing.T) {
906909 mockDockerClient .AssertNotCalled (t , "ContainerRemove" , mock .Anything , rc .Name , mock .Anything )
907910
908911 // Mock destroyContainer to verify it is called.
909- mockDockerClient .On ("ContainerStop" , mock .Anything , rc .Name , mock . Anything ).Return (nil ).Once ()
912+ mockDockerClient .On ("ContainerStop" , mock .Anything , rc .Name , expectedContainerStopOptions ).Return (nil ).Once ()
910913 mockDockerClient .On ("ContainerRemove" , mock .Anything , rc .Name , mock .Anything ).Return (nil ).Once ()
911914
912915 // after the first failure, there should only 1 more healthcheck for the container to be stopped
@@ -993,8 +996,8 @@ func TestRemoveExistingContainers(t *testing.T) {
993996 {ID : "container2" , Names : []string {"/container2" }, Labels : map [string ]string {containerCreatorLabel : containerCreator }},
994997 }
995998 mockDockerClient .On ("ContainerList" , mock .Anything , mock .Anything ).Return (existingContainers , nil )
996- mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , mock . Anything ).Return (nil )
997- mockDockerClient .On ("ContainerStop" , mock .Anything , "container2" , mock . Anything ).Return (nil )
999+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , expectedContainerStopOptions ).Return (nil )
1000+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container2" , expectedContainerStopOptions ).Return (nil )
9981001 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container1" , mock .Anything ).Return (nil )
9991002 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container2" , mock .Anything ).Return (nil )
10001003
@@ -1036,9 +1039,9 @@ func TestRemoveExistingContainers_InMemoryFilterLegacyAndOwnerID(t *testing.T) {
10361039 {ID : "mine-1" , Names : []string {"/mine-1" }, Labels : map [string ]string {containerCreatorLabel : containerCreator , containerCreatorIDLabel : "owner-A" }}, // match -> remove
10371040 }, nil ).
10381041 Once ()
1039- mockDockerClient .On ("ContainerStop" , mock .Anything , "legacy-1" , mock . Anything ).Return (nil ).Once ()
1042+ mockDockerClient .On ("ContainerStop" , mock .Anything , "legacy-1" , expectedContainerStopOptions ).Return (nil ).Once ()
10401043 mockDockerClient .On ("ContainerRemove" , mock .Anything , "legacy-1" , mock .Anything ).Return (nil ).Once ()
1041- mockDockerClient .On ("ContainerStop" , mock .Anything , "mine-1" , mock . Anything ).Return (nil ).Once ()
1044+ mockDockerClient .On ("ContainerStop" , mock .Anything , "mine-1" , expectedContainerStopOptions ).Return (nil ).Once ()
10421045 mockDockerClient .On ("ContainerRemove" , mock .Anything , "mine-1" , mock .Anything ).Return (nil ).Once ()
10431046
10441047 removed , err := RemoveExistingContainers (ctx , mockDockerClient , "owner-A" )
@@ -1095,7 +1098,7 @@ func TestDockerContainerName(t *testing.T) {
10951098func TestDockerRemoveContainer (t * testing.T ) {
10961099 mockDockerClient := new (MockDockerClient )
10971100
1098- mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , container. StopOptions {} ).Return (nil )
1101+ mockDockerClient .On ("ContainerStop" , mock .Anything , "container1" , expectedContainerStopOptions ).Return (nil )
10991102 mockDockerClient .On ("ContainerRemove" , mock .Anything , "container1" , container.RemoveOptions {}).Return (nil )
11001103
11011104 err := dockerRemoveContainer (mockDockerClient , "container1" )
@@ -1165,6 +1168,63 @@ func TestDockerWaitUntilRunning(t *testing.T) {
11651168 require .Contains (t , err .Error (), "timed out waiting for managed container" )
11661169 mockDockerClient .AssertExpectations (t )
11671170 })
1171+
1172+ t .Run ("FailFastOnExited" , func (t * testing.T ) {
1173+ // If the container is immediately exited, we should fail fast instead of waiting.
1174+ mockDockerClient := new (MockDockerClient )
1175+ // Always return non-running, exited state
1176+ mockDockerClient .On ("ContainerInspect" , mock .Anything , containerID ).Return (types.ContainerJSON {
1177+ ContainerJSONBase : & types.ContainerJSONBase {
1178+ State : & types.ContainerState {
1179+ Status : "exited" ,
1180+ Running : false ,
1181+ ExitCode : 137 ,
1182+ },
1183+ },
1184+ }, nil )
1185+
1186+ err := dockerWaitUntilRunning (ctx , mockDockerClient , containerID , pollingInterval )
1187+ require .Error (t , err )
1188+ require .Contains (t , err .Error (), "terminal state" )
1189+ mockDockerClient .AssertExpectations (t )
1190+ })
1191+
1192+ t .Run ("FailFastOnDead" , func (t * testing.T ) {
1193+ mockDockerClient := new (MockDockerClient )
1194+ mockDockerClient .On ("ContainerInspect" , mock .Anything , containerID ).Return (types.ContainerJSON {
1195+ ContainerJSONBase : & types.ContainerJSONBase {
1196+ State : & types.ContainerState {
1197+ Status : "dead" ,
1198+ Running : false ,
1199+ Error : "killed" ,
1200+ },
1201+ },
1202+ }, nil )
1203+
1204+ err := dockerWaitUntilRunning (ctx , mockDockerClient , containerID , pollingInterval )
1205+ require .Error (t , err )
1206+ require .Contains (t , err .Error (), "container entered terminal state" )
1207+ mockDockerClient .AssertExpectations (t )
1208+ })
1209+
1210+ t .Run ("FailFastOnExitCodeNonZeroWithoutRestarting" , func (t * testing.T ) {
1211+ mockDockerClient := new (MockDockerClient )
1212+ mockDockerClient .On ("ContainerInspect" , mock .Anything , containerID ).Return (types.ContainerJSON {
1213+ ContainerJSONBase : & types.ContainerJSONBase {
1214+ State : & types.ContainerState {
1215+ Status : "created" ,
1216+ Running : false ,
1217+ Restarting : false ,
1218+ ExitCode : 1 ,
1219+ },
1220+ },
1221+ }, nil )
1222+
1223+ err := dockerWaitUntilRunning (ctx , mockDockerClient , containerID , pollingInterval )
1224+ require .Error (t , err )
1225+ require .Contains (t , err .Error (), "exited before running" )
1226+ mockDockerClient .AssertExpectations (t )
1227+ })
11681228}
11691229
11701230func TestHwGPU (t * testing.T ) {
0 commit comments