diff --git a/index.js b/index.js index f8af4d7..41afac6 100644 --- a/index.js +++ b/index.js @@ -99,7 +99,7 @@ app.all('/container/:containerId', function (req, res) { state: "stopped" }); }); - } + } }, function (status, message) { if (config.get("debug")) console.log("Failed to get status of Docker container"); @@ -165,6 +165,80 @@ app.get('/container/:containerId/restart', function (req, res) { }) }); +app.post('/container/:containerId/exec', function(req, res) { + var containerId = req.params.containerId; + console.log("Exec " + containerId); + + var command = req.body.command ? req.body.command : false; + if (command == "" || !command) { + res.send({ + status: false, + error: "No command specified" + }); + res.status(400); + return; + } + + getContainer(containerId, function (container) { + if (config.get("debug")) + console.log("Attempting to execute command in container " + container.Id); + var options = { + Cmd: command.split(" "), + AttachStdout: true, + AttachStderr: true + }; + if (config.get('debug')) + console.log(options); + + var container = docker.getContainer(container.Id); + container.exec(options, function (err, exec) { + if (err) { + if (config.get("debug")) { + console.log("Failed to get container " + container.Id); + console.log(err); + } + + res.status(500); + res.send(err); + return; + } + + exec.start(function (err, stream) { + if (err) { + if (config.get("debug")) { + console.log("Failed to execute in container " + container.Id); + console.log(err); + } + + res.status(500); + res.send(err); + return; + } + console.log("executed query"); + const chunks = []; + stream.on("data", function (chunk) { + chunks.push(chunk.toString()); + }); + + // Send the buffer or you can put it into a var + stream.on("end", function () { + // We remove the first 8 chars as the contain a unicode START OF HEADING followed by ENQUIRY. + res.send({ + status: true, + result: chunks.join('').substr(8) + }); + }); + }); + return; + }); + }, function (status, message) { + res.status(status); + if (message) { + res.send(message); + } + }); +}) + //Attempt to connect to the Docker daemon switch (config.get("docker_connection:type")) { case "http": @@ -222,4 +296,4 @@ function getContainer(name, cb, error) return cb(containers[0]); }); -} \ No newline at end of file +} diff --git a/package.json b/package.json index b5d1120..06aa043 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ha-dockermon", - "version": "0.0.1", + "version": "0.0.2", "description": "A RESTful HTTP endpoint which can return the status of Docker containers for Home Automation controllers like Home Assistant", "main": "index.js", "scripts": { diff --git a/readme.md b/readme.md index 5e14e67..c5a172c 100644 --- a/readme.md +++ b/readme.md @@ -97,6 +97,21 @@ Calls to this URL will issue a `docker restart ` on the host mac Useful in a Home Assistant script to restart containers (including Home Assistant itself). +#### POST /container/{container name}/exec + +Allows you to execute commands inside a running container. When making a call to this endpoint you must send the correct `Content-Type` headers and JSON as the body. An example request with cURL is below. + +You must also send a `command` variable which contains the command you would like to run in the container. + +```bash +curl --request POST \ +--url http://127.0.0.1:8126/container/grafana/exec \ +--header 'content-type: application/octet-stream' \ +--data '{"command": "ls -a"}' +``` + +The response will be a json object with a `result` key containing the output from the command executed. + ### Home Assistant RESTful Switch You can use this service as a [RESTful switch](https://home-assistant.io/components/switch.rest/) inside Home Assistant.