Skip to content

Commit c1dfc34

Browse files
fix(container): set Detach true in ExecStart to prevent blocking on command execution (#1683)
- Add `Detach: true` to `ExecStartOptions` so the daemon does not block until the command finishes - Update test mock handler to verify `Detach: true` is sent in the `ExecStart` request - Add explicit test case documenting the `Detach: true` requirement for `ExecStart`
1 parent b22cf73 commit c1dfc34

2 files changed

Lines changed: 34 additions & 2 deletions

File tree

pkg/container/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,7 @@ func (c *client) ExecuteCommand(
10221022
// Start the exec instance.
10231023
clog.WithField("exec_id", exec.ID).Debug("Starting exec instance")
10241024

1025-
execStartCheck := dockerClient.ExecStartOptions{TTY: true}
1025+
execStartCheck := dockerClient.ExecStartOptions{Detach: true, TTY: true}
10261026

10271027
_, err = c.api.ExecStart(ctx, exec.ID, execStartCheck)
10281028
if err != nil {

pkg/container/client_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,37 @@ var _ = ginkgo.Describe("the client", func() {
11841184
gomega.Expect(skipUpdate).To(gomega.BeFalse())
11851185
})
11861186
})
1187+
ginkgo.When("ExecStart is called", func() {
1188+
ginkgo.It("should use Detach true to prevent blocking on command execution", func() {
1189+
client := &client{
1190+
api: docker,
1191+
ClientOptions: ClientOptions{},
1192+
}
1193+
1194+
containerID := types.ContainerID("detach-test-cont-id")
1195+
execID := "detach-test-exec-id"
1196+
cmd := "detach-test-cmd"
1197+
1198+
// Set up standard handlers
1199+
setupExecMockHandlers(mockServer, string(containerID), execID, cmd, -1, -1, 0, false, false)
1200+
1201+
container, err := client.GetContainer(context.Background(), containerID)
1202+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
1203+
1204+
skipUpdate, err := client.ExecuteCommand(context.Background(), container, cmd, 1, 0, 0)
1205+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
1206+
gomega.Expect(skipUpdate).To(gomega.BeFalse())
1207+
1208+
// Verify the ExecStart request contained Detach: true
1209+
// by inspecting the last request body received by the mock server.
1210+
// The setupExecMockHandlers already verifies this via VerifyJSONRepresenting,
1211+
// but we add this test to explicitly document the requirement:
1212+
// ExecStart must use Detach: true so that the daemon does not block
1213+
// until the command finishes. Without Detach: true, ExecStart blocks,
1214+
// and a subsequent ExecAttach receives "exec command is already running"
1215+
// which causes exit code 126 and aborts the update.
1216+
})
1217+
})
11871218
})
11881219

11891220
// Test suite for captureExecOutput.
@@ -2075,7 +2106,8 @@ func setupExecMockHandlers(
20752106
gomega.MatchRegexp(fmt.Sprintf("^/v[0-9.]+/exec/%s/start$", execID)),
20762107
),
20772108
ghttp.VerifyJSONRepresenting(dockerContainer.ExecStartRequest{
2078-
Tty: true,
2109+
Detach: true,
2110+
Tty: true,
20792111
}),
20802112
ghttp.RespondWith(http.StatusOK, nil),
20812113
),

0 commit comments

Comments
 (0)