Skip to content

docker: do not lose early exec output - #151

Merged
castler merged 2 commits into
eclipse-score:mainfrom
castler:fix-docker-exec-lost-output
Nov 3, 2026
Merged

castler merged 2 commits into
eclipse-score:mainfrom
castler:fix-docker-exec-lost-output

Conversation

@castler

@castler castler commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Problem

Flaky nightly failures in eclipse-score/communication (#1248, #1084, #986):

  • execute_async() fails with ValueError: invalid literal for int() ...: '2026/10/26 ... lola log info ...' – the first stdout chunk is application output instead of the PID line.
  • target.execute("echo Hello World!") returns exit code 0 with empty output.

Root cause

docker-py 7.1.0 reads exec output from the raw socket (response.raw._fp.fp.raw), bypassing the BufferedReader that http.client used to parse the HTTP response headers. That reader reads up to 8 KiB at once, so any output the daemon sent in the same burst as the headers (i.e. the very first output of the command) is already in its buffer and is silently dropped. This happens whenever the client reads the reply late, e.g. on a loaded CI host; the output that is lost is exactly the first bytes (echo $$ PID line, or the complete output of a short command).

Reproduction

Delaying http.client.HTTPConnection.getresponse by 0.1 s makes docker-py lose the PID line 50/50 times and the first stdout chunk becomes the next output line (the #1248/#1084 symptom); with 0.5 s the whole output of a short command is lost (the #986 symptom).

Fix

A small APIClient subclass that parses the exec frames from the buffered reader (read/read1), so nothing is dropped. As a side effect frames are never split mid-PID. The ITF Docker clients use it.

Tests

test_exec_output_not_lost.py emulates the slow reader. It fails on the old code ('' == 'Hello World!\n', Failed to extract PID) and passes with the fix; test_docker, test_ssh, test_dlt, test_dlt_on_target pass.

docker-py reads exec output from the raw socket, but http.client has already buffered anything that arrived together with the HTTP response headers. If the client reads the reply late (loaded host), the first output (the PID line of execute_async, or the whole output of a short command) is dropped. Read the frames through the buffered reader instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castler
castler enabled auto-merge October 31, 2026 15:46

@limdor limdor left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with pydocker APIClient and I do not know how _read_from_socket should look like. However, I took a look to the PR and this is my understanding and proposal.

I first reviewed the newly added test. To me the tests make total sense and I also checked that without the additional changes, the tests are broken (10 out of 10). With the additional changes, the tests are passing (10 out of 10).

I did a bit of research and it seems to be a known issue with pydocker (see docker/docker-py#3332 and docker/docker-py#2042) and there are event 2 open PRs to address the issue (see docker/docker-py#3333 and docker/docker-py#3438).

How I see it, the newly added code in this PR, is basically reimplementing the _read_from_socket on the docker api client but disabling the timeout and not missing content. I cannot say if this is fully correct but considering that the tests are passing I wouls propose to merge it.

The only change that I would suggest before merging, is to add a comment in self._client = _ExecDockerClient.from_env(timeout=DOCKER_CLIENT_TIMEOUT) mentioning the existing issues in docker-py and the opened PRs, and mention that this is a workaround until the issues are fixed and it should be removed once the upstream issues/PRs are merged.

@castler

castler commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Added a comment on _ExecDockerClient (used by both from_env call sites) linking docker-py#3332, #2042 and the open PRs #3333/#3438, and noting the workaround should be removed once they are resolved.

@castler
castler added this pull request to the merge queue Nov 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 3, 2026
@castler
castler added this pull request to the merge queue Nov 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 3, 2026
@castler
castler added this pull request to the merge queue Nov 3, 2026
Merged via the queue into eclipse-score:main with commit 33e1af4 Nov 3, 2026
10 checks passed
@castler
castler deleted the fix-docker-exec-lost-output branch November 3, 2026 14:14
@github-project-automation github-project-automation Bot moved this from Backlog to Done in TST - Testing Community Nov 3, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
etas-contrib-automation Bot pushed a commit to etas-contrib/score_communication that referenced this pull request Nov 3, 2026
Bumps `score_itf` 0.6.0 -> 0.7.0.

- eclipse-score/itf#150: restart the QNX guest when its kernel panics
during boot (eclipse-score#1513, eclipse-score/qnx_sdp#9)
- eclipse-score/itf#151: do not lose early docker exec output
- eclipse-score/itf#144: SSH auth support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants