Interactive Docker exec with docker-py

44 Views Asked by At

I'm trying to implement something equivalent to:

docker exec -it <some_container> /bin/bash

Ie. run a command in a container and connect stdin and stdout of my program to stdin and stdout of the command.

My reading of the documentation seems to imply that the following should do the job:

import docker
client = docker.from_env()
container, = client.containers.list()
container.exec_run(['/usr/local/bin/bash'], stdin=True, tty=True)

(There's a single container running bash:latest)

But it doesn't seem like the program's stdin is connected to that of the exec'd command. The command line just sits there, echoing my input, but not reacting to it in any way.

I've also tried to interact with the raw socket (returned by exec_run—not docker.sock):

_, s = container.exec_run(['/usr/local/bin/bash'], stdin=True, tty=True, socket=True)
s.write('echo hello world')

But I'm getting UnsupportedOperation: File or stream is not writable.

Question: What would I have to do to allow the user of my code to interact with the std IO of a command executed in a container using docker-py?

1

There are 1 best solutions below

0
user23608914 On

The result of exec_run is a socket.SocketIO object. Use its socket's recv(), sendall().

e.g.

import docker
import threading
import queue

client = docker.from_env()
container = client.containers.get('YOUR CONTAINER NAME')
_, sock = container.exec_run(cmd='bash', stdin=True, socket=True)
ch = queue.Queue()

def read():
    while res := sock._sock.recv(4096):
        print(res[8:].decode())

def write():
    while item := ch.get():
        item = item + '\n'
        item = bytes(item, 'utf-8')
        sock._sock.sendall(item)

writer = threading.Thread(target=write)
writer.start()

reader = threading.Thread(target=read)
reader.start()

while cmd := input():
    ch.put(cmd)
    if cmd == 'exit':
        break

sock._sock.close()
ch.put(None)

writer.join()
reader.join()