question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Closing stdin on a Running Container

See original GitHub issue

I am using docker version 2.1.0.

When starting a container the parameter stdin_open can be used to prepare the stdin socket for writing. I have had success doing this with code along these lines,

stdin_data = 'some data to send to stdin'

container = client.containers.run(image, detach=True, stdin_open=True)

socket = container.attach_socket(params={'stdin': 1, 'stream': 1})
os.write(socket.fileno(), stdin_data.encode())
socket.close()

exit_code = container.wait()

I have also found that this approach can easily hang indefinitely, if the underlying container is waiting for input. The reason seems to be be that the socket status is independent of the stdin_open parameter, so the code running in the container never receives an EOF-like sentinel on stdin.

Any suggestions on how to address this?

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:4
  • Comments:7

github_iconTop GitHub Comments

3reactions
mschwagercommented, Aug 18, 2021

Okay, I think I figured it out! The missing ingredient was a _sock.close call. So the complete example:

#!/usr/bin/env python

import docker

client = docker.APIClient()
container = client.create_container(
    'stdin-test',
    stdin_open=True,
)

sock = client.attach_socket(container, params={"stdin": 1, "stdout": 1, "stderr": 1, "stream": 1})
client.start(container)
sock._sock.send(b'sent data')
sock._sock.close()
sock.close()

status = client.wait(container)
status_code = status["StatusCode"]
stdout = client.logs(container, stderr=False).decode()
stderr = client.logs(container, stdout=False).decode()

client.remove_container(container)

print('Exit: {}'.format(status_code))
print('log stdout: {}'.format(stdout))
print('log stderr: {}'.format(stderr))

Running against the Docker image above, which simply runs the following script, produces the following output:

#!/usr/bin/env python

import sys

print("Running output.py")

data = sys.stdin.read()
print("stdout: {}".format(data), file=sys.stdout)
print("stderr: {}".format(data), file=sys.stderr)
$ python run.py 
Exit: 0
log stdout: Running output.py
stdout: b'sent data'
log stderr: stderr: b'sent data'

So we can see that the output.py script received stdin and successfully printed it!

I believe the original issue can be resolved with the following:

socket._sock.close()
socket.close()

The API ergonomics leave something to be desired here. Having the muck with sock._sock is not ideal, but this workaround appears to work as expected 👍

0reactions
mschwagercommented, Jan 22, 2021

Interestingly, it looks like there’s differences between Python 2 and Python 3.

If I remove detach=True the following code works as expected in Python 2:

print('Attaching...')
sock = client.attach_socket(container, params={"stdin": 1, "stream": 1})
print('Sending...')
sock.send(b'sent data')
print('Closing...')
sock.close()
$ python2 run.py 
Starting...
Attaching...
Sending...
Closing...
Waiting...
Exit: 0
stdout: Running output.py
stdout: sent data

stderr: stderr: sent data
$ python3 run.py 
Starting...
Attaching...
Sending...
Traceback (most recent call last):
  File "run.py", line 17, in <module>
    sock.send(b'sent data')
AttributeError: 'SocketIO' object has no attribute 'send'

This is expected due to the Python 2 and 3 differences. However, changing to the following code produces different results:

print('Attaching...')
sock = client.attach_socket(container, params={"stdin": 1, "stream": 1})
print('Sending...')
sock._sock.send(b'sent data')
print('Closing...')
sock.close()
$ python2 run.py 
Starting...
Attaching...
Sending...
Traceback (most recent call last):
  File "run.py", line 17, in <module>
    sock._sock.send(b'sent data')
AttributeError: '_socket.socket' object has no attribute '_sock'
$ python3 run.py 
Starting...
Attaching...
Sending...
Closing...
Waiting...
...
Hangs

So the question is: can we achieve the old Python 2 behavior from _socket.socket with Python 3’s SocketIO?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Docker closes STDIN despite "--interactive" - Server Fault
The issue I'm seeing is that closing the docker run command also closes STDIN. The container continues running and when using docker attach ......
Read more >
Correct way to detach from a container without stopping it
Type Ctrl + p then Ctrl + q . It will help you to turn interactive mode to daemon mode.
Read more >
docker attach - Docker Documentation
To stop a container, use CTRL-c . This key sequence sends SIGKILL to the container. If --sig-proxy is true (the default), CTRL-c sends...
Read more >
Attach and Detach From a Docker Container - Baeldung
Pressing CTRL-c is the usual way of ending a session. But, if we've launched our container without the -d or -it option, the...
Read more >
How do I close stdin in a shell script? - Super User
Is there a way to close stdin? Closing File Descriptors. n<&- Close input file descriptor n. 0<&- or <&- Close stdin. Source Chapter...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found