Add container exec and rename support
This commit is contained in:
parent
43e27eb713
commit
10a5a8e216
|
|
@ -89,6 +89,94 @@ async function getContainerLogs(id) {
|
|||
return logs.toString();
|
||||
}
|
||||
|
||||
async function execCommand(id, command) {
|
||||
const container = docker.getContainer(id);
|
||||
|
||||
// Check if container is running
|
||||
const info = await container.inspect();
|
||||
if (!info.State.Running) {
|
||||
throw new Error('Container is not running');
|
||||
}
|
||||
|
||||
// Create exec instance
|
||||
const exec = await container.exec({
|
||||
Cmd: ['/bin/sh', '-c', command],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
});
|
||||
|
||||
// Start exec and capture output
|
||||
return new Promise((resolve, reject) => {
|
||||
exec.start({ hijack: true, stdin: false }, (err, stream) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let output = '';
|
||||
let errorOutput = '';
|
||||
|
||||
// Docker multiplexes stdout and stderr into a single stream
|
||||
// Each frame has an 8-byte header
|
||||
stream.on('data', (chunk) => {
|
||||
// Parse Docker stream format
|
||||
let offset = 0;
|
||||
while (offset < chunk.length) {
|
||||
if (chunk.length - offset < 8) break;
|
||||
|
||||
const header = chunk.slice(offset, offset + 8);
|
||||
const streamType = header[0]; // 1=stdout, 2=stderr
|
||||
const size = header.readUInt32BE(4);
|
||||
|
||||
offset += 8;
|
||||
|
||||
if (chunk.length - offset < size) break;
|
||||
|
||||
const payload = chunk.slice(offset, offset + size).toString('utf8');
|
||||
|
||||
if (streamType === 1) {
|
||||
output += payload;
|
||||
} else if (streamType === 2) {
|
||||
errorOutput += payload;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', async () => {
|
||||
try {
|
||||
const execInfo = await exec.inspect();
|
||||
console.log(chalk.blue('Command executed:'), chalk.cyan(command), chalk.yellow('Exit code:'), execInfo.ExitCode);
|
||||
|
||||
resolve({
|
||||
output: (output + errorOutput).trim(),
|
||||
exitCode: execInfo.ExitCode,
|
||||
error: execInfo.ExitCode !== 0 ? errorOutput.trim() : null
|
||||
});
|
||||
} catch (inspectError) {
|
||||
reject(inspectError);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (streamError) => {
|
||||
reject(streamError);
|
||||
});
|
||||
|
||||
// Set timeout (30 seconds)
|
||||
setTimeout(() => {
|
||||
stream.destroy();
|
||||
reject(new Error('Command execution timeout'));
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function renameContainer(id, newName) {
|
||||
const container = docker.getContainer(id);
|
||||
await container.rename({ name: newName });
|
||||
console.log(chalk.yellow('Container renamed:'), chalk.cyan(id), chalk.green('→'), chalk.cyan(newName));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
listContainers,
|
||||
getContainerStats,
|
||||
|
|
@ -97,5 +185,7 @@ module.exports = {
|
|||
restartContainer,
|
||||
createContainer,
|
||||
removeContainer,
|
||||
getContainerLogs
|
||||
getContainerLogs,
|
||||
execCommand,
|
||||
renameContainer
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,4 +98,39 @@ router.get('/:id/logs', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
// Execute command in container
|
||||
router.post('/:id/exec', async (req, res) => {
|
||||
try {
|
||||
const { command } = req.body;
|
||||
|
||||
if (!command) {
|
||||
return res.status(400).json({ error: 'Command is required' });
|
||||
}
|
||||
|
||||
const result = await containerService.execCommand(req.params.id, command);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
error: error.message || 'Failed to execute command',
|
||||
exitCode: 1
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Rename container
|
||||
router.post('/:id/rename', async (req, res) => {
|
||||
try {
|
||||
const { name } = req.query;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'New name is required' });
|
||||
}
|
||||
|
||||
await containerService.renameContainer(req.params.id, name);
|
||||
res.json({ message: 'Container renamed successfully' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to rename container' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Loading…
Reference in New Issue