public async execInContainer()

in src/docker/DockerodeApiClient/DockerodeApiClient.ts [71:144]


    public async execInContainer(context: IActionContext, ref: string, command: string[] | DockerExecCommandProvider, options?: DockerExecOptions, token?: CancellationToken): Promise<{ stdout: string, stderr: string }> {

        // NOTE: Dockerode's exec() doesn't seem to work with Windows against the socket endpoint.
        //       https://github.com/apocas/dockerode/issues/534

        const commandProvider = Array.isArray(command) ? () => command : command;

        if (isWindows()) {
            let dockerCommand = `${dockerExePath(context)} exec `;

            if (options?.user) {
                dockerCommand += `--user "${options.user}" `;
            }

            dockerCommand += `"${ref}" ${commandProvider('windows').join(' ')}`;

            // Copy the Docker environment settings in
            const newEnv: NodeJS.ProcessEnv = cloneObject(process.env);
            addDockerSettingsToEnv(newEnv, process.env);

            const { stdout, stderr } = await execStreamAsync(dockerCommand, { env: newEnv }, token);

            return { stdout, stderr };
        } else {
            const container = this.dockerodeClient.getContainer(ref);

            const exec = await container.exec({
                AttachStderr: true,
                AttachStdout: true,
                Cmd: commandProvider('linux'),
                User: options?.user
            });

            const execStream = await exec.start({
            });

            return new Promise<{ stdout: string, stderr: string }>(
                (resolve, reject) => {
                    const stdoutChunks: Buffer[] = [];
                    const stderrChunks: Buffer[] = [];

                    const stdout = new stream.PassThrough();
                    const stderr = new stream.PassThrough();

                    // TODO: Get demuxStream() included in type definition.
                    container.modem.demuxStream(execStream, stdout, stderr);

                    stdout.on('data', chunk => {
                        stdoutChunks.push(chunk);
                    });

                    stderr.on('data', chunk => {
                        stderrChunks.push(chunk);
                    });

                    execStream.on('end', async () => {
                        try {
                            const inspectInfo = await exec.inspect();

                            const stdoutOutput = bufferToString(Buffer.concat(stdoutChunks));
                            const stderrOutput = bufferToString(Buffer.concat(stderrChunks));

                            if (inspectInfo.ExitCode) {
                                reject(new Error(stderrOutput || stdoutOutput));
                            } else {
                                resolve({ stdout: stdoutOutput, stderr: stderrOutput });
                            }
                        } catch (err) {
                            reject(err);
                        }
                    });
                });
        }
    }