diff --git a/nodejs/package.json.in b/nodejs/package.json.in
index 9d546cb..d9fbcc9 100644
--- a/nodejs/package.json.in
+++ b/nodejs/package.json.in
@@ -3,10 +3,11 @@
"version": "@PACKAGE_VERSION@",
"private": true,
"dependencies": {
- "express": "~2.5.8",
- "stylus": "~0.53.0",
- "ejs": ">= 0.0.1",
- "socket.io": "~1.4.4"
+ "express": "~2.5.8",
+ "stylus": "~0.53.0",
+ "ejs": ">= 0.0.1",
+ "socket.io": "~1.4.4",
+ "pty.js": "~0.3.0"
},
"description": "Docker as a Service",
"main": "servicedock.js",
diff --git a/nodejs/public/javascripts/servicedock.js b/nodejs/public/javascripts/servicedock.js
index ad5f0cb..1c6b707 100644
--- a/nodejs/public/javascripts/servicedock.js
+++ b/nodejs/public/javascripts/servicedock.js
@@ -13,11 +13,11 @@ var focused = null;
function DockerContainers() {
var Status = Object.freeze({
- Error: {color: "red", action1: "start", action2: "remove"},
- Terminated: {color: "yellow", action1: "start", action2: "remove"},
- Restarting: {color: "lightblue", action1: "start", action2: "remove"},
- Paused: {color: "lightgrey", action1: "unpause", action2: null},
- Running: {color: "lightgreen", action1: "pause", action2: "stop"}
+ Error: {color: "red", action1: "start", action2: "remove", bash: false},
+ Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false},
+ Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false},
+ Paused: {color: "lightgrey", action1: "unpause", action2: null, bash: false},
+ Running: {color: "lightgreen", action1: "pause", action2: "stop", bash: true}
});
var containers = [];
var nodes = [];
@@ -40,7 +40,7 @@ function DockerContainers() {
if (n.status.action1) {
$("#popup").append('');
$("#popup1").click(function() {
- socket.emit(n.status.action1, name);
+ emit(n.status.action1, name);
});
}
$("#popup").append('');
@@ -50,11 +50,27 @@ function DockerContainers() {
if (n.status.action2) {
$("#popup").append('');
$("#popup3").click(function() {
- socket.emit(n.status.action2, name);
+ emit(n.status.action2, name);
});
}
$("#popup").append('
');
- $("#popup").append('');
+ $("#popup").append('');
+ $("#popup4").click(function() {
+ emit("logs", name);
+ });
+ if (n.status.bash) {
+ $("#popup").append('');
+ $("#popup5").click(function() {
+ emit("bash-start", name);
+ $("#console").show();
+ $("#main").hide();
+ $("#bash").submit(function() {
+ emit("bash-input", {name: name, text: $("#command").val()+"\n"});
+ $("#command").val("");
+ })
+ });
+ }
+ $("#popup").append('');
$("#popup").css("position", "fixed");
$("#popup").css("top", e.pageY-$("#popup").height()/4);
$("#popup").css("left", e.pageX-$("#popup").width()/2);
@@ -66,49 +82,6 @@ function DockerContainers() {
$("#popup").show();
})
}
- this.details = function(name) {
- var res = `
-
Name | -Ports | -Volumes | -Links | -Environments | -Image | -Command | -
---|
`; - res += JSON.stringify(containers[nodes[name].id], null, 4); - res += ` --
"+e+"
"+res.join("\n")+""); + } else { + break; } - }}).fail(function() { - error("offline"); - }); + } + for (;spans;--spans) res += ""; + console.log(res); + return res.replace(/\r\r\n/g, '\n'); } -function containers(c) { - console.log("->rcv containers"); - dc.setContainers(c); - if (focused && dc.exists(focused)) - details(focused); - else - overview(); +function bash_data(data) { + console.log("->rcv bash-data("+data.name+")", data); + $("#main").hide(); + $("#logs").hide(); + $("#console").show(); + if (data.type=='done') { + $("#screen").append('\nDONE'); + } else { + $("#screen").append(''+ansifilter(htmlenc(data.text))+''); + } } function overview() { @@ -577,7 +623,9 @@ function init() { .on("error", disconnected); socket .on("fail", error) - .on("containers", containers); + .on("containers", containers) + .on("logs", logs) + .on("bash-data", bash_data); start(); } diff --git a/nodejs/public/stylesheets/servicedock.css b/nodejs/public/stylesheets/servicedock.css index d126f0e..2aff43a 100644 --- a/nodejs/public/stylesheets/servicedock.css +++ b/nodejs/public/stylesheets/servicedock.css @@ -3,6 +3,10 @@ padding: 0; } +body { + background-color: blue; +} + @media (min-resolution: 120dpi) { html { font-size: 120%; @@ -228,18 +232,22 @@ table.docker li+li { padding-top: 0.5em; } -#main { +#main, #logs, #console { position: fixed; - top: 2em; + top: 1.5em; left: 0; right: 0; - bottom: 2em; - padding: 0em 1em 0em 1em; + bottom: 1.5em; + padding: 1em 1em 1em 1em; clear: both; overflow: auto; z-index: 0; } +#main { + background-color: white; +} + #popup { position: fixed; background-color: lightblue; @@ -308,3 +316,62 @@ table.docker li+li { #preview img { height: 4em; } + +#logs { + background-color: lightgray; +} +#logs .stdout { + color: black; +} +.stderr { + color: red; +} +.done { + color: green; +} + +#console { + background-color: black; + color: white +} +.bold {font-weight: bold} +.dim {color: grey} +.underline {text-decoration: underline} +.blink {animation: blinker 1s linear infinite} +@keyframes blinker {50% { opacity: 0.0;}} +.reverse {color: black; background-color: white} +.hidden {color: black} +.fgdefault {color: white} +.fgblack {color: black} +.fgred {color: red} +.fggreen {color: green} +.fgyellow {color: yellow} +.fgblue {color: blue} +.fgmagenta {color: magenta} +.fgcyan {color: cyan} +.fglightgrey {color: lightgrey} +.fgdarkgrey {color: darkgrey} +.fglightred {color: lightred} +.fglightgreen {color: lightgreen} +.fglightyellow {color: lightyellow} +.fglightblue {color: lightblue} +.fglightmagenta {color: lightmagenta} +.fglightcyan {color: lightcyan} +.fgwhite {color: white} +.bgdefault {background-color: black} +.bgblack {background-color: black} +.bgred {background-color: red} +.bggreen {background-color: green} +.bgyellow {background-color: yellow} +.bgblue {background-color: blue} +.bgmagenta {background-color: magenta} +.bgcyan {background-color: cyan} +.bglightgrey {background-color: lightgrey} +.bgdarkgrey {background-color: darkgrey} +.bglightred {background-color: lightred} +.bglightgreen {background-color: lightgreen} +.bglightyellow {background-color: lightyellow} +.bglightblue {background-color: lightblue} +.bglightmagenta {background-color: lightmagenta} +.bglightcyan {background-color: lightcyan} +.bgwhite {background-color: white} diff --git a/nodejs/sockets/index.js b/nodejs/sockets/index.js index a5670e3..b6c4c19 100644 --- a/nodejs/sockets/index.js +++ b/nodejs/sockets/index.js @@ -5,6 +5,7 @@ module.exports = function() { module.connection = function(socket) { //var sys = require('sys'); + var pty = require('pty.js'); var proc = require('child_process'); console.log("new client"); @@ -60,9 +61,7 @@ module.exports = function() { function modify(cmd, name) { if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i)) - return fail("illegal instance name", { - error: error, stderr: stderr, stdout: stdout - }); + return fail("illegal instance name"); exec("docker "+cmd+" "+name, updatecontainers); } @@ -96,14 +95,62 @@ module.exports = function() { modify("rm", name); } + function logs(name) { + console.log("-> logs("+name+")"); + var l = proc.spawn("docker", ["logs", "-f", name]) + .on('close', function(code) { + emit('logs', {name: name, type: 'done'}); + }); + l.stdout.on('data', function(data) { + emit('logs', {name: name, type: 'stdout', text: data.toString()}); + }); + l.stderr.on('data', function(data) { + emit('logs', {name: name, type: 'stderr', text: data.toString()}); + }); + } + + var bash_connections = {}; + + function bash_start(name) { + console.log("-> bash-start("+name+")"); + if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i)) + return fail("illegal instance name"); + if (bash_connections[name]) return fail("bash already open"); + bash_connections[name] = + pty.spawn("docker", ["exec", "-it", name, "bash", "-i"]); + bash_connections[name].stdout.on('data', function(data) { + emit('bash-data', {name: name, type: 'stdout', text: data.toString()}); + }); + // bash_connections[name].stderr.on('data', function(data) { + // emit('bash-data', {name: name, type: 'stdout', text: data.toString()}); + // }); + } + + function bash_input(data) { + console.log("-> bash-input("+data.name+", "+data.text+")"); + if (!bash_connections[data.name]) return fail("bash not open"); + bash_connections[data.name].stdin.resume(); + bash_connections[data.name].stdin.write(data.text); + } + + // function bash_end(name, text) { + // console.log("-> bash-end("+name+")"); + // if (!bash_connections[name]) return fail("bash not open"); + // bash_connections[name].stdin.close(); + // } + socket .on("containers", containers) .on("start", start) .on("stop", stop) .on("pause", pause) .on("unpause", unpause) - .on("remove", remove); - + .on("remove", remove) + .on('logs', logs) + .on('bash-start', bash_start) + .on('bash-input', bash_input); + //.on('bash-end', bash_end); + } return module; diff --git a/nodejs/views/index.ejs b/nodejs/views/index.ejs index 5934c35..7e38332 100644 --- a/nodejs/views/index.ejs +++ b/nodejs/views/index.ejs @@ -44,6 +44,16 @@ + + + +