runApplication.js

import Docker from "dockerode"
import Logger from "imergo-logger"

const __docker = new Docker({
    socketPath: "/var/run/docker.sock"
})
const __logger = Logger("test-containers")

/**
 * Runs an application in a Docker container.
 *
 * @async
 * @function runApplication
 * @param {Object} options - The options for running the application.
 * @param {Docker} [options.docker = new Docker({ socketPath: "/var/run/docker.sock" })] - The Docker instance to use.
 * @param {Logger} [options.logger = Logger("test-containers")] - The logger to use.
 * @param {string} options.containerName - The name of the container to run the application in.
 * @param {ReadableStream} options.scriptReadStream - The readable stream of the compressed file containing the application to run.
 * @param {string} [options.scriptRootDir = "/root"] - The root directory in the container to place and expand the archive.
 * @param {Object} options.execOptions - The options for executing the application in the container.
 * @returns {Promise<string|Error>} A promise that resolves with the output of the script, or rejects with an error.
 *
 * @example
 * const scriptReadStream = fs.createReadStream("/path/to/your/script.tar.gz");
 * const execOptions = {
 *   Cmd: ["node", "yourScript.js"],
 *   AttachStdout: true,
 *   AttachStderr: true
 * };
 * runApplication({
 *   docker,
 *   containerName: "myContainer",
 *   scriptReadStream,
 *   execOptions
 * }).then(console.log).catch(console.error);
 */
export default async function runApplication({
        docker= __docker,
        logger= __logger,
        containerName,
        scriptReadStream,
        scriptRootDir= "/root",
        execOptions
    }) {
    logger.info(`Running script in ${containerName} container`)
    const containers = await docker.listContainers({
        filters: {
            name: [ containerName ],
            status: [ "running" ]
        }
    })
    if (containers.length)
    {
        const container = docker.getContainer(containers[ 0 ].Id)
        logger.info(`Using existing container: ${container.id}`)
        logger.info(`Uploading file to container`)
        await container.putArchive(scriptReadStream, { path: scriptRootDir })
        return executeInContainer(container, execOptions)
    }
}

async function executeInContainer(container, execOptions) {
    const __execOptions = {
        AttachStderr: true,
        AttachStdout: true,
        ...execOptions
    }

    const exec = await container.exec(__execOptions)
    return new Promise(async (resolve, reject) => {
        return exec.start({}, (error, stream) => {
                if(error)
                {
                    return reject(error)
                }
                let output = ""
                stream.on("data", chunk => output += chunk.toString().replace(/[^\w\s]/gi, ""))
                stream.on("end", async () => {
                    return resolve(output)
                })
                // stream.on("finish", () => stream.socket.exit())
                stream.on("error", error => reject(error))
            })
    })
}