From 9760f444fd40967e9b1caab43174075687910613 Mon Sep 17 00:00:00 2001 From: Ross Dargan Date: Fri, 19 Jan 2018 17:10:42 +0000 Subject: [PATCH 1/6] First ROUGH pass at adding in the exec command --- index.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f8af4d7..d7da375 100644 --- a/index.js +++ b/index.js @@ -99,7 +99,54 @@ app.all('/container/:containerId', function (req, res) { state: "stopped" }); }); - } + } else if (req.body.cmd != null) { +// if (config.get("debug")) + console.log("Attempting to execute command in container " + container.Id); + var options = { + Cmd: [req.body.cmd], + AttachStdout: true, + AttachStderr: true + }; + 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; + } + if (config.get("debug")) + console.log("Container stopped"); + + 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"); +// stream.setEncoding('utf8'); + //stream.pipe(process.stdout); + // res.status(200); //We found the container! This reponse can be trusted + stream.pipe(res); + return; + }); + return; + res.status(200); + res.send("no result returned"); + }); + } }, function (status, message) { if (config.get("debug")) console.log("Failed to get status of Docker container"); @@ -222,4 +269,4 @@ function getContainer(name, cb, error) return cb(containers[0]); }); -} \ No newline at end of file +} From 472246e58214126d89dc18b769fbdf20f2436322 Mon Sep 17 00:00:00 2001 From: Ross Dargan Date: Fri, 19 Jan 2018 17:37:33 +0000 Subject: [PATCH 2/6] Remove first two unicode values --- index.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index d7da375..d4dcc9d 100644 --- a/index.js +++ b/index.js @@ -139,12 +139,23 @@ app.all('/container/:containerId', function (req, res) { // stream.setEncoding('utf8'); //stream.pipe(process.stdout); // res.status(200); //We found the container! This reponse can be trusted - stream.pipe(res); - return; + + 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 () { + res.send(chunks.join('').substr(8)); + }) + + //stream.pipe(res); + //return; }); return; - res.status(200); - res.send("no result returned"); + //res.status(200); + //res.send("no result returned"); }); } }, function (status, message) { From 464b55ad6911105181183c24c58ce3117102d775 Mon Sep 17 00:00:00 2001 From: Ross Dargan Date: Fri, 19 Jan 2018 17:39:54 +0000 Subject: [PATCH 3/6] Update index.js add a little comment --- index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/index.js b/index.js index d4dcc9d..c84ebeb 100644 --- a/index.js +++ b/index.js @@ -134,19 +134,14 @@ app.all('/container/:containerId', function (req, res) { res.send(err); return; } - console.log("executed query"); -// stream.setEncoding('utf8'); - //stream.pipe(process.stdout); - // res.status(200); //We found the container! This reponse can be trusted - 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(chunks.join('').substr(8)); }) From 39a1566c9d79c2b363d41199e6aa36bd36e9ee64 Mon Sep 17 00:00:00 2001 From: Phil Hawthorne Date: Mon, 22 Jan 2018 21:21:16 +1100 Subject: [PATCH 4/6] Move exec to /exec and split command Creates a new endpoint called `exec` which will accept a command variable via POST. It can then be used to run a command inside a running docker container and return the result as a string --- index.js | 127 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/index.js b/index.js index c84ebeb..41afac6 100644 --- a/index.js +++ b/index.js @@ -99,59 +99,6 @@ app.all('/container/:containerId', function (req, res) { state: "stopped" }); }); - } else if (req.body.cmd != null) { -// if (config.get("debug")) - console.log("Attempting to execute command in container " + container.Id); - var options = { - Cmd: [req.body.cmd], - AttachStdout: true, - AttachStderr: true - }; - 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; - } - if (config.get("debug")) - console.log("Container stopped"); - - 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(chunks.join('').substr(8)); - }) - - //stream.pipe(res); - //return; - }); - return; - //res.status(200); - //res.send("no result returned"); - }); } }, function (status, message) { if (config.get("debug")) @@ -218,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": From bfd9ecc8d80f199f3c9e28cd8e274325c886397e Mon Sep 17 00:00:00 2001 From: Phil Hawthorne Date: Mon, 22 Jan 2018 21:24:57 +1100 Subject: [PATCH 5/6] Update readme with docs about exec endpoint Adds new documentation for the exec endpoint --- readme.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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. From 01d83249e61833f0f8fddfea00d9d728b79f3093 Mon Sep 17 00:00:00 2001 From: Phil Hawthorne Date: Tue, 23 Jan 2018 21:26:43 +1100 Subject: [PATCH 6/6] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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": {