exec: rework API to get exit code

This commit is contained in:
Girish Ramakrishnan
2022-05-16 10:26:30 -07:00
parent 6bd478b8b0
commit b5c2a0ff44
6 changed files with 123 additions and 33 deletions
+37 -7
View File
@@ -66,7 +66,9 @@ exports = module.exports = {
stop,
restart,
exec,
createExec,
startExec,
getExec,
checkManifestConstraints,
downloadManifest,
@@ -2335,7 +2337,7 @@ function checkManifestConstraints(manifest) {
return null;
}
async function exec(app, options) {
async function createExec(app, options) {
assert.strictEqual(typeof app, 'object');
assert(options && typeof options === 'object');
@@ -2346,7 +2348,7 @@ async function exec(app, options) {
throw new BoxError(BoxError.BAD_STATE, 'App not installed or running');
}
const execOptions = {
const createOptions = {
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
@@ -2358,6 +2360,18 @@ async function exec(app, options) {
Cmd: cmd
};
return await docker.createExec(app.containerId, createOptions);
}
async function startExec(app, execId, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof execId, 'string');
assert(options && typeof options === 'object');
if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) {
throw new BoxError(BoxError.BAD_STATE, 'App not installed or running');
}
const startOptions = {
Detach: false,
Tty: options.tty,
@@ -2373,10 +2387,26 @@ async function exec(app, options) {
stderr: true
};
const stream = await docker.execContainer(app.containerId, { execOptions, startOptions, rows: options.rows, columns: options.columns });
const stream = await docker.startExec(execId, startOptions);
if (options.rows && options.columns) {
// there is a race where resizing too early results in a 404 "no such exec"
// https://git.cloudron.io/cloudron/box/issues/549
setTimeout(async function () {
await safe(docker.resizeExec(execId, { h: options.rows, w: options.columns }, { debug }));
}, 2000);
}
return stream;
}
async function getExec(app, execId) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof execId, 'string');
return await docker.getExec(execId);
}
function canAutoupdateApp(app, updateInfo) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof updateInfo, 'object');
@@ -2601,7 +2631,7 @@ async function downloadFile(app, filePath) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof filePath, 'string');
const statStream = await exec(app, { cmd: [ 'stat', '--printf=%F-%s', filePath ], tty: true });
const statStream = await startExec(app, { cmd: [ 'stat', '--printf=%F-%s', filePath ], tty: true });
const data = await drainStream(statStream);
const parts = data.split('-');
@@ -2622,7 +2652,7 @@ async function downloadFile(app, filePath) {
throw new BoxError(BoxError.NOT_FOUND, 'only files or dirs can be downloaded');
}
const inputStream = await exec(app, { cmd, tty: false });
const inputStream = await startExec(app, { cmd, tty: false });
// transforms the docker stream into a normal stream
const stdoutStream = new TransformStream({
@@ -2663,7 +2693,7 @@ async function uploadFile(app, sourceFilePath, destFilePath) {
const escapedDestFilePath = safe.child_process.execSync(`printf %q '${destFilePath.replace(/'/g, '\'\\\'\'')}'`, { shell: '/bin/bash', encoding: 'utf8' });
debug(`uploadFile: ${sourceFilePath} -> ${escapedDestFilePath}`);
const destStream = await exec(app, { cmd: [ 'bash', '-c', `cat - > ${escapedDestFilePath}` ], tty: false });
const destStream = await startExec(app, { cmd: [ 'bash', '-c', `cat - > ${escapedDestFilePath}` ], tty: false });
return new Promise((resolve, reject) => {
const done = once(error => reject(new BoxError(BoxError.FS_ERROR, error.message)));