Skip to content

containerAttachStream's stderrEvent is emitted on wrong stream #96

@knaerzche

Description

@knaerzche

I'm trying to capture demultiplexed stderr and stdout streams from a running container with following example code:

$container = await(
    $client->containerCreate(
        [
            'Image' => 'alpine',
            'Tty' => false,
            'Cmd' => [
                'sh',
                '-c',
                'i=0; while true; do echo "OUT $i"; echo "ERR $i" 1>&2; i=$((i+1)); sleep 1; done'
            ],
        ]
    )
);

$stream = $client
    ->containerAttachStream(
        $container['Id'],
        stream:true,
        stderrEvent: 'stderr',
    )
    ->on('data', function ($data) {
        echo 'stdout: ' . $data . "\n";
    })
    ->on('stderr', function ($data) {
        echo 'stderr: ' . $data . "\n";
    }
);


await(
    $client
        ->containerStart($container['Id']
    )
);

There's never anything logged for 'stderr' event and I'm not sure that ever worked.
The reason behind is that ReadableDemultiplexStream::containerAttachStream returns an UnwrapReadableStream:`

...
        // first inspect container to check TTY setting, then attach with appropriate log parser
        return \React\Promise\Stream\unwrapReadable($this->containerInspect($container)->then(function ($info) use ($url, $browser, $parser, $stderrEvent) {
            $stream = $parser->parsePlainStream($browser->requestStreaming('POST', $url));

            if (!$info['Config']['Tty']) {
                $stream = $parser->demultiplexStream($stream, $stderrEvent);
            }

            return $stream;
        }));
....

If I understand things correctly this is "only" a proxy stream, not the actual stream.
I understand you want to check Tty setting before using a ReadableDemultiplexStream. The problem is that the stream event listeners are subcribing to events of UnwrapReadableStream which in turn only emits (hardcoded) data, end, error and close events: https://github.com/reactphp/promise-stream/blob/f5fff26ec6ac4ea89168e49554a304bfedae1e7f/src/UnwrapReadableStream.php#L54 and there seems no way to add more.

One possible solution is to not use an promise, but await:

        // first inspect container to check TTY setting, then attach with appropriate log parser
        $info = await($this->containerInspect($container));
        $stream = $parser->parsePlainStream($browser->requestStreaming('POST', $url));

        if (!$info['Config']['Tty']) {
            $stream = $parser->demultiplexStream($stream, $stderrEvent);
        }

        return $stream;

But clue/reactphp-docker does currently not depend on react/async - so I'm not sure thats a correct fix for that issue.
I have no idea how to proper fix it or how to get the actual stream which emitts stderr events in my client code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions