apocas / docker-modem

Docker Remote API network stack driver.
Apache License 2.0
234 stars 112 forks source link

Requests that return HttpDuplex (eg docker exec start) hang when using named pipe on windows. #83

Open pirog opened 7 years ago

pirog commented 7 years ago

Found an interesting situation that i was able to track down a bit. I am unsure if this is a bug with docker-modem or potentially how Docker for Windows sets up the named pipe to begin with.

Here is a file to reproduce (uses latest node-docker-api but upgraded to docker-modem 1.0.0), the same thing happens if you use analogous code for dockerode

'use strict'
const Docker = require('node-docker-api').Docker

const promisifyStream = (stream) => new Promise((resolve, reject) => {
  stream.on('data', (d) => console.log(d.toString()))
  stream.on('end', () => {
      if (process.stdin.setRawMode) {
        process.stdin.setRawMode(false);
      }
      process.stdin.pause();
      resolve()
  })
  stream.on('error', reject)
})

var thing
const docker = new Docker({ socketPath: '//./pipe/docker_engine' })
//const docker = new Docker({host: '127.0.0.1', port: 2375})
let _container

  docker.container.create({
    Image: 'debian:jessie',
    Cmd: [ '/bin/bash', '-c', 'tail -f /var/log/dmesg' ]
  })
  .then((container) => container.start())
  .then((container) => {
    _container = container
    return container.exec.create({
        AttachStdin: true,
        AttachStdout: true, 
        AttachStderr: true,
        Cmd: ['echo', 'thing'],
        Tty: true
    })
  })
  .then((exec) => {
    return exec.start({ 
      hijack: false,
      Detach: false,
      Tty: true,
      stdin: true,
    })
  })
  .then((stream) => {
    thing = stream
  })
  .then(() => {
    thing.pipe(process.stdout);    

    // Restart stdin with correct encoding
    process.stdin.resume();
    process.stdin.setEncoding('utf8');

    // Make sure rawMode matches up
    if (process.stdin.setRawMode) {
      process.stdin.setRawMode(true);
    }        

    // Send our processes stdin into the container
    process.stdin.pipe(thing);

  })
  .then(() => promisifyStream(thing))
  .then(() => _container.delete({force: true}))
  .catch((error) => console.log(error))

If you connect over tcp (need to allow insecure connections in the docker for windows app first), it works as expected but if you switch over to the named pipe it will hang after "thing" is written to stdout. What is extremely interesting is after this happens you can get the stream to close and the process to resume by pressing any key three times.

Another interesting data point here is it looks like docker-compose itself avoids using the API for this on Windows, deciding to just delegate directly to a shell call to docker exec. see: https://github.com/docker/compose/blob/master/compose/cli/main.py#L411

My best guesses at this point are:

  1. there is is some additional buffer with named pipes that is not being correctly flushed and its causing things to hang up
  2. writes are not happening or getting lost duringish reads

Any thoughts about why this could be happening?

JoonasVali commented 5 years ago

Hi, I'm most likely running in to the same problem with Dockerode 2.5.4 which relies on Docker-modem 1.0.4 . Anything with exec.start gets stuck and won't terminate properly.

nicks commented 2 years ago

docker desktop 4.12 has a compatibility shim that should fix this. https://docs.docker.com/desktop/release-notes/#bug-fixes-and-minor-changes see my comments about it here: https://github.com/apocas/dockerode/issues/534