Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(DockterExecutor): mount project folder and set user and group ids
- Loading branch information
Showing
3 changed files
with
73 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,75 @@ | ||
import Docker from 'dockerode' | ||
import os from 'os' | ||
import path from 'path' | ||
import stream from 'stream' | ||
import { resolve } from 'dns' | ||
|
||
/** | ||
* Executes a Docker image (ie. starts a container from the image) | ||
* Executes a Docker environment. | ||
* | ||
* Currently, this class simply runs the image and captures any | ||
* output. In the future it may do more advanced things like mounting data and output | ||
* directories into the image. | ||
* This class has a single method, `execute`, which starts a container from an | ||
* image and runs the command specified in the Dockerfile `CMD` instruction. | ||
* | ||
* It mounts the project's directory into the container a `/work` and uses it | ||
* as the working directory. | ||
* | ||
* It also sets the current user and group as the | ||
* user and group in the container. This means that within the container the | ||
* command that runs has the same permissions as the current user does in the | ||
* `/work` directory. | ||
* | ||
* Finally, it removes the container (but not the image). | ||
* | ||
* This then is the equivalent of running the container with Docker from within | ||
* the project directory using, | ||
* | ||
* docker run --rm --volume $(pwd):/work --workdir=/work --user=$(id -u):$(id -g) <image> | ||
*/ | ||
export default class DockerExecutor { | ||
|
||
async execute (name: string) { | ||
const docker = new Docker() | ||
async execute (name: string, folder: string) { | ||
// Capture stdout so we can attempt to parse it | ||
// to JSON | ||
let out = '' | ||
let stdout = new stream.Writable({ | ||
write (chunk, encoding, callback) { | ||
out += chunk.toString() | ||
callback() | ||
} | ||
}) | ||
|
||
// Capture output stream | ||
let output = '' | ||
let outputStream = new stream.Writable() | ||
outputStream._write = (chunk) => { | ||
output += chunk | ||
} | ||
// Just write errors through to local console error | ||
let stderr = new stream.Writable({ | ||
write (chunk, encoding, callback) { | ||
console.error(chunk.toString()) | ||
callback() | ||
} | ||
}) | ||
|
||
const container = await docker.run(name, [], outputStream, { | ||
// Options from https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate | ||
// Get and set user:group | ||
const userInfo = os.userInfo() | ||
const user = `${userInfo.uid}:${userInfo.gid}` | ||
|
||
// Run the container! | ||
// Options from https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate | ||
const docker = new Docker() | ||
const container = await docker.run(name, [], [stdout, stderr], { | ||
Tty: false, | ||
HostConfig: { | ||
Binds: [ | ||
`${path.resolve(folder)}:/work` | ||
] | ||
}, | ||
WorkingDir: '/work', | ||
User: user | ||
}) | ||
container.remove() | ||
|
||
let value | ||
// Attempt to parse output as JSON | ||
try { | ||
value = JSON.parse(output) | ||
return JSON.parse(out) | ||
} catch { | ||
value = output.trim() | ||
return out.trim() | ||
} | ||
|
||
return value | ||
} | ||
} |