diff --git a/nodejs/public/javascripts/servicedock.js b/nodejs/public/javascripts/servicedock.js
index 1c6b707..484de35 100644
--- a/nodejs/public/javascripts/servicedock.js
+++ b/nodejs/public/javascripts/servicedock.js
@@ -11,282 +11,403 @@
var socket = io.connect();
var focused = null;
-function DockerContainers() {
- var Status = Object.freeze({
- 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 = [];
- function protocol(port) {
- if (port.toString().match("443")) return "https://";
- if (port.toString().match("3304")) return "mysql://";
- if (port.toString().match("22")) return "ssh://";
- return "http://";
+function Docker() {
+
+ function same(array1, array2) {
+ return (array1.length == array2.length)
+ && array1.every(function(element, index) {
+ return element === array2[index];
+ });
}
- this.exists = function(name) {
- if (nodes[name]) return true;
- return false;
+
+ var _docker = this;
+
+ this.Images = function() {
+
+ var _images = this;
+ var images = [];
+ var nodes = [];
+
+ function setup() {
+ delete nodes; nodes = [];
+ images.forEach(function(c, i) {
+ if (!nodes[c.Id]) nodes[c.Id] = {};
+ nodes[c.Id].id = c.Id;
+ nodes[c.Id].tags = c.RepoTags;
+ nodes[c.Id].created = c.Created;
+ nodes[c.Id].author = c.Author;
+ nodes[c.Id].os = c.Os+"/"+c.Architecture;
+ nodes[c.Id].parent = c.Parent;
+ nodes[c.Id].env = c.Config.Env;
+ nodes[c.Id].cmd = c.Config.Cmd;
+ nodes[c.Id].entrypoint = c.Config.Entrypoint;
+ nodes[c.Id].ports = c.Config.ExposedPorts;
+ nodes[c.Id].volumes = c.Config.Volumes;
+ if (c.Parent) {
+ if (!nodes[c.Parent]) nodes[c.Parent] = {};
+ if (!nodes[c.Parent].children) nodes[c.Parent].children = [];
+ nodes[c.Parent].children.push(c.Id);
+ }
+ });
+ }
+ this.cleanup = function(id, instance) {
+ if (!nodes[id]) return
+ nodes[id].env.forEach(function(e) {
+ if ((pos=instance.env.indexOf(e))>-1) instance.env.splice(pos, 1)
+ })
+ if (same(nodes[id].cmd, instance.cmd)) instance.cmd = null
+ else console.log(instance.cmd+" != "+nodes[id].cmd)
+ if (same(nodes[id].entrypoint, instance.entrypoint)) instance.entrypoint = null
+ else console.log(instance.entrypoint+" != "+nodes[id].entrypoint)
+ }
+ this.set = function(c) {
+ if (typeof c == "string") c = JSON.parse(c);
+ if (typeof c != "object") throw "wrong format: "+(typeof c);
+ images = c;
+ setup();
+ }
+
}
- this.contextmenu = function(selector) {
- $('a[xlink\\:href^=#]').click(function(e) {
- name = $(this).attr("xlink:href").replace(/^#/, "");
- var n = nodes[name];
- $(selector).prepend('
')
- $("#popup").empty();
- if (n.status.action1) {
- $("#popup").append('');
- $("#popup1").click(function() {
- emit(n.status.action1, name);
+
+ this.Containers = function() {
+
+ var _containers = this;
+
+ var Status = Object.freeze({
+ 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 = [];
+ function protocol(port) {
+ if (port.toString().match("443")) return "https://";
+ if (port.toString().match("3304")) return "mysql://";
+ if (port.toString().match("22")) return "ssh://";
+ return "http://";
+ }
+ this.exists = function(name) {
+ if (nodes[name]) return true;
+ return false;
+ }
+ function getIps(n, ips) {
+ n.ports.forEach(function(p) {
+ if (!ips[p.ip]) ips[p.ip] = [];
+ ips[p.ip].push(p);
+ });
+ }
+ function graphIpClusters(ips) {
+ var res = "newrank=true;\n";
+ var i = 0;
+ for (ip in ips) {
+ res += "subgraph clusterIp"+(++i)+' {\nlabel="'+ip+'";\n';
+ ips[ip].forEach(function(p) {
+ res += '"'+p.ip+":"+p.external
+ +'" [label="'+p.external+'",URL="'
+ +protocol(p.internal)+p.ip+':'+p.external+'",shape=box];\n';
});
+ res+="}\n";
}
- $("#popup").append('');
- $("#popup2").click(function() {
- if (focused) overview(); else details(name);
+ res += "{rank=same;\n";
+ for (ip in ips) {
+ ips[ip].forEach(function(p) {
+ res += '"'+p.ip+":"+p.external+'";\n';
+ });
+ }
+ res+="}\n";
+ return res;
+ }
+ function graphNode(n) {
+ var res = "";
+ var label = n.name+'\\n'+n.image.name;
+ res += '"'+n.name+'"'
+ +' [label="'+label
+ +'",URL="#'+n.name
+ +'",style=filled,fillcolor='+n.status.color+"];\n";
+ n.ports.forEach(function(p) {
+ res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name
+ +'" [label="'+p.internal+'"];\n';
});
- if (n.status.action2) {
- $("#popup").append('');
- $("#popup3").click(function() {
- emit(n.status.action2, name);
+ n.links.forEach(function(l) {
+ res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n'
});
- }
- $("#popup").append('
');
- $("#popup").append('');
- $("#popup4").click(function() {
- emit("logs", name);
+ return res;
+ }
+ function graphVolumesInside(n) {
+ var res = "";
+ n.volumes.forEach(function(v) {
+ res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
});
- 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);
- $("#popup").mouseleave(function() {
- $("#popup").hide();
- }).click(function() {
- $("#popup").hide();
+ return res;
+ }
+ function graphVolumesOutside(n) {
+ var res = "";
+ n.volumes.forEach(function(v) {
+ if (v.host)
+ res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
});
- $("#popup").show();
- })
- }
- function getIps(n, ips) {
- n.ports.forEach(function(p) {
- if (!ips[p.ip]) ips[p.ip] = [];
- ips[p.ip].push(p);
- });
- }
- function graphIpClusters(ips) {
- var res = "newrank=true;\n";
- var i = 0;
- for (ip in ips) {
- res += "subgraph clusterIp"+(++i)+' {\nlabel="'+ip+'";\n';
- ips[ip].forEach(function(p) {
- res += '"'+p.ip+":"+p.external
- +'" [label="'+p.external+'",URL="'
- +protocol(p.internal)+p.ip+':'+p.external+'",shape=box];\n';
+ return res;
+ }
+ function graphVolumesConnections(n) {
+ var res = "";
+ n.volumes.forEach(function(v) {
+ if (v.host)
+ res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n';
+ res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n';
});
+ n.volumesfrom.forEach(function(o) {
+ res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n';
+ });
+ return res;
+ }
+ this.graph = function(n) {
+ var res = "";
+ var ips = [];
+ n = n || nodes;
+ for (name in n) getIps(n[name], ips);
+ res += graphIpClusters(ips);
+ for (name in n) res += graphNode(n[name]);
+ res += "{rank=same;\n";
+ for (name in n) res += graphVolumesInside(n[name]);
+ res+="}\n";
+ res += "{rank=same;\n";
+ for (name in n) res += graphVolumesOutside(n[name]);
res+="}\n";
+ for (name in n) res += graphVolumesConnections(n[name]);
+ return res;
}
- res += "{rank=same;\n";
- for (ip in ips) {
- ips[ip].forEach(function(p) {
- res += '"'+p.ip+":"+p.external+'";\n';
+ function addNodes(ns, name) {
+ var n = nodes[name];
+ ns[name] = n;
+ n.links.forEach(function(peer) {
+ if (!ns[peer.to]) addNodes(ns, peer.to);
+ });
+ n.usedby.forEach(function(peer) {
+ if (!ns[peer]) addNodes(ns, peer);
+ });
+ n.volumesfrom.forEach(function(peer) {
+ if (!ns[peer]) addNodes(ns, peer);
+ });
+ n.volumesto.forEach(function(peer) {
+ if (!ns[peer]) addNodes(ns, peer);
});
}
- res+="}\n";
- return res;
- }
- function graphNode(n) {
- var res = "";
- var label = n.name+'\\n'+n.image;
- res += '"'+n.name+'"'
- +' [label="'+label
- +'",URL="#'+n.name
- +'",style=filled,fillcolor='+n.status.color+"];\n";
- n.ports.forEach(function(p) {
- res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name
- +'" [label="'+p.internal+'"];\n';
- });
- n.links.forEach(function(l) {
- res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n'
- });
- return res;
- }
- function graphVolumesInside(n) {
- var res = "";
- n.volumes.forEach(function(v) {
- res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
- });
- return res;
- }
- function graphVolumesOutside(n) {
- var res = "";
- n.volumes.forEach(function(v) {
- if (v.host)
- res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
- });
- return res;
- }
- function graphVolumesConnections(n) {
- var res = "";
- n.volumes.forEach(function(v) {
- if (v.host)
- res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n';
- res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n';
- });
- n.volumesfrom.forEach(function(o) {
- res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n';
- });
- return res;
- }
- this.graph = function(n) {
- var res = "";
- var ips = [];
- n = n || nodes;
- for (name in n) getIps(n[name], ips);
- res += graphIpClusters(ips);
- for (name in n) res += graphNode(n[name]);
- res += "{rank=same;\n";
- for (name in n) res += graphVolumesInside(n[name]);
- res+="}\n";
- res += "{rank=same;\n";
- for (name in n) res += graphVolumesOutside(n[name]);
- res+="}\n";
- for (name in n) res += graphVolumesConnections(n[name]);
- return res;
- }
- function addNodes(ns, name) {
- var n = nodes[name];
- ns[name] = n;
- n.links.forEach(function(peer) {
- if (!ns[peer.to]) addNodes(ns, peer.to);
- });
- n.usedby.forEach(function(peer) {
- if (!ns[peer]) addNodes(ns, peer);
- });
- n.volumesfrom.forEach(function(peer) {
- if (!ns[peer]) addNodes(ns, peer);
- });
- n.volumesto.forEach(function(peer) {
- if (!ns[peer]) addNodes(ns, peer);
- });
- }
- this.subgraph = function(name) {
- var ns = [];
- addNodes(ns, name);
- return this.graph(ns);
- }
- function setup() {
- delete nodes; nodes = [];
- containers.forEach(function(c, i) {
- var name = c.Name.replace(/^\//, "");
- if (!nodes[name]) nodes[name] = {};
- nodes[name].id = i;
- nodes[name].name = name;
- nodes[name].image = c.Config.Image;
- nodes[name].ports = [];
- var ports = c.NetworkSettings.Ports || c.NetworkSettings.PortBindings;
- if (ports)
- for (var port in ports)
- if (ports[port])
- for (var expose in ports[port]) {
- var ip = ports[port][expose].HostIp;
- if (!ip||ip==""||ip=="0.0.0.0"||ip==0) ip=window.location.hostname;
- nodes[name].ports.push({
- internal: port,
- external: ports[port][expose].HostPort,
- ip: ip
- });
- }
- if (c.State.Paused) nodes[name].status = Status.Paused;
- else if (c.State.Running) nodes[name].status = Status.Running;
- else if (c.State.Restarting) nodes[name].status = Status.Restarting;
- else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated;
- else nodes[name].status = Status.Error;
- nodes[name].volumes = [];
- var volumes = c.Volumes || c.Config.Volumes;
- nodes[name].volumes = [];
- if (volumes)
- for (var volume in volumes) {
- var rw = "rw";
- var outside = (typeof volumes[volume]=="string")?volumes[volume]:null;
- if (c.Mounts) c.Mounts.forEach(function(mnt) {
- if (mnt.Destination==volume) {
- outside = mnt.Source;
- rw = mnt.RW ? "rw" : "ro";
+ this.subnet = function(name) {
+ var ns = {};
+ addNodes(ns, name);
+ return ns;
+ }
+ this.subgraph = function(name) {
+ return this.graph(this.subnet(name));
+ }
+ this.creation = function(name) {
+ var ns = this.subnet(name);
+ var creates = [];
+ for (n in ns) {
+ var instance = {
+ name: ns[n].name,
+ image: ns[n].image.name,
+ ports: ns[n].ports,
+ env: ns[n].env,
+ cmd: ns[n].cmd,
+ entrypoint: ns[n].entrypoint,
+ volumesfrom: ns[n].volumesfrom,
+ links: ns[n].links,
+ volumes: []
+ };
+ ns[n].volumes.forEach(function(v) {
+ if (v.host) instance.volumes.push({
+ inside: v.inside,
+ outside: v.host
+ });
+ });
+ _docker.images.cleanup(ns[n].image.id, instance);
+ creates.push(instance);
+ }
+ creates.sort(function(a, b) {
+ if (a.volumesfrom.indexOf(b)>=0) return 1; // a after b
+ if (b.volumesfrom.indexOf(a)>=0) return -1; // a before b
+ for (var i=0; i')
+ $("#popup").empty();
+ if (n.status.action1) {
+ $("#popup").append('');
+ $("#popup1").click(function() {
+ emit(n.status.action1, name);
+ });
+ }
+ $("#popup").append('');
+ $("#popup2").click(function() {
+ if (focused) overview(); else details(name);
+ });
+ if (n.status.action2) {
+ $("#popup").append('');
+ $("#popup3").click(function() {
+ emit(n.status.action2, name);
});
}
- nodes[name].volumesfrom = [];
- if (!nodes[name].volumesto) nodes[name].volumesto = [];
- if (c.HostConfig.VolumesFrom) c.HostConfig.VolumesFrom.forEach(function(id) {
- containers.forEach(function(c) {
- if (c.Id == id || c.Name == "/"+id || c.Name == id) {
- var src = c.Name.replace(/^\//, "");
- nodes[name].volumesfrom.push(src);
- if (!nodes[src]) nodes[src] = {};
- if (!nodes[src].volumesto) nodes[src].volumesto = [];
- nodes[src].volumesto.push(name);
- }
+ $("#popup").append('
');
+ $("#popup").append('');
+ $("#popup4").click(function() {
+ showLogs();
+ emit("logs", name);
});
- });
- nodes[name].links = [];
- if (!nodes[name].usedby) nodes[name].usedby = [];
- if (c.HostConfig && c.HostConfig.Links)
- c.HostConfig.Links.forEach(function(l) {
- var target = {
- to: l.replace(/^\/?([^:]*).*$/, "$1"),
- link: l.replace(new RegExp("^.*:/?"+name+"/"), "")
- };
- nodes[name].links.push(target);
- if (!nodes[target.to]) nodes[target.to] = {};
- if (!nodes[target.to].usedby) nodes[target.to].usedby = [];
- nodes[target.to].usedby.push(name);
+ if (n.status.bash) {
+ $("#popup").append('');
+ $("#popup5").click(function() {
+ showConsole();
+ emit("bash-start", name);
+ $("#bash").submit(function() {
+ emit("bash-input", {name: name, text: $("#command").val()+"\n"});
+ $("#command").val("");
+ })
+ });
+ }
+ $("#popup").append('');
+ $("#popup6").click(function() {
+ var download = document.createElement('a');
+ download.href = 'data:application/json,'
+ + encodeURI(JSON.stringify(_containers.creation(name), null, 2));
+ download.target = '_blank';
+ download.download = name+'.json';
+ var clickEvent = new MouseEvent("click", {
+ "view": window,
+ "bubbles": true,
+ "cancelable": false
+ });
+ download.dispatchEvent(clickEvent);
});
- });
- for (name in nodes) { // cleanup duplicate links to volumes when using volumes-from
- var n = nodes[name];
- n.volumesfrom.forEach(function(other) {
- var o = nodes[other];
- o.volumes.forEach(function(ovol) {
- n.volumes.reduceRight(function(x, nvol, i, arr) {
- if (nvol.id == ovol.id)
- arr.splice(i, 1);
- }, [])
- })
+ $("#popup").css("position", "fixed");
+ $("#popup").css("top", e.pageY-$("#popup").height()/4);
+ $("#popup").css("left", e.pageX-$("#popup").width()/2);
+ $("#popup").mouseleave(function() {
+ $("#popup").hide();
+ }).click(function() {
+ $("#popup").hide();
+ });
+ $("#popup").show();
})
}
+ this.set = function(c) {
+ if (typeof c == "string") c = JSON.parse(c);
+ if (typeof c != "object") throw "wrong format: "+(typeof c);
+ containers = c;
+ setup();
+ }
}
- this.setContainers = function(c) {
- if (typeof c == "string") c = JSON.parse(c);
- if (typeof c != "object") throw "wrong format: "+(typeof c);
- containers = c;
- setup();
- }
+
+ this.images = new this.Images();
+ this.containers = new this.Containers();
+
}
-var dc = new DockerContainers();
+var docker = new Docker();
function htmlenc(html) {
return $('').text(html).html();
@@ -312,7 +433,8 @@ function error(data) {
console.log("error: "+data);
} else {
$("#status").html('unknown error: '+JSON.stringify(data));
- console.log("error: "+JSON.stringify(data));
+ console.log("error: ", data);
+ console.log((new Error('stacktrace')));
}
} else {
$("#status").html('error');
@@ -372,7 +494,7 @@ function status(text, msg) {
zoom(0);
$("#main").show();
$("form input:first-child").focus();
- dc.contextmenu("#main");
+ docker.containers.contextmenu("#main");
}
function emit(signal, data) {
@@ -481,23 +603,50 @@ function showviz(vizpath, more) {
function details(name) {
if (name) focused = name;
else if (!focused) return overview();
- showviz(dc.subgraph(focused));
+ showviz(docker.containers.subgraph(focused));
+}
+
+function images(i) {
+ console.log("->rcv images");
+ docker.images.set(i);
}
function containers(c) {
console.log("->rcv containers");
- dc.setContainers(c);
- if (focused && dc.exists(focused))
+ docker.containers.set(c);
+ showImage();
+ if (focused && docker.containers.exists(focused))
details(focused);
else
overview();
}
-function logs(data) {
- console.log("->rcv logs("+data.name+")");
- $("#main").hide();
+function showImage() {
+ $("#close").hide();
+ $("#logs").hide();
$("#console").hide();
+ $("#main").show();
+}
+
+function showConsole() {
+ $("#main").hide();
+ $("#logs").hide();
+ $("#console").show();
+ $("#close").show();
+ $("#command").focus();
+ $("#command").val("");
+ if ($("#screen").val()!="") $("#screen").append("\n");
+}
+
+function showLogs() {
+ $("#main").hide();
$("#logs").show();
+ $("#console").hide();
+ $("#close").show();
+}
+
+function logs(data) {
+ console.log("->rcv logs("+data.name+")");
if (data.type=='done') {
$("#logs").append('\nDONE');
} else {
@@ -586,9 +735,6 @@ function ansifilter(data) {
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 {
@@ -598,7 +744,7 @@ function bash_data(data) {
function overview() {
focused = null;
- showviz(dc.graph());
+ showviz(docker.containers.graph());
}
/// Initial Function: Startup
@@ -609,6 +755,7 @@ function start() {
$("#username").html(window.location.hostname)
try {
status("Starting up ...");
+ emit("images");
emit("containers");
} catch (m) {
error(m);
@@ -624,6 +771,7 @@ function init() {
socket
.on("fail", error)
.on("containers", containers)
+ .on("images", images)
.on("logs", logs)
.on("bash-data", bash_data);
start();
diff --git a/nodejs/public/stylesheets/servicedock.css b/nodejs/public/stylesheets/servicedock.css
index 2aff43a..65ce395 100644
--- a/nodejs/public/stylesheets/servicedock.css
+++ b/nodejs/public/stylesheets/servicedock.css
@@ -195,7 +195,7 @@ table.docker li+li {
color: black;
}
-#menuicon, #imagetools img {
+#menuicon, #imagetools img, #close {
cursor: pointer;
height: 1em;
width: auto;
diff --git a/nodejs/sockets/index.js b/nodejs/sockets/index.js
index b6c4c19..c12058d 100644
--- a/nodejs/sockets/index.js
+++ b/nodejs/sockets/index.js
@@ -70,6 +70,46 @@ module.exports = function() {
updatecontainers();
}
+ function imageinspect(error, stdout, stderr) {
+ if (error || stderr)
+ return fail("inspect docker images failed", {
+ error: error, stderr: stderr, stdout: stdout
+ });
+ emit("images", stdout);
+ }
+
+ function imagelist(error, stdout, stderr) {
+ if (error || stderr)
+ return fail("list docker images failed", {
+ error: error, stderr: stderr, stdout: stdout
+ });
+ exec("docker inspect "+stdout.trim().replace(/\n/g, " "), imageinspect);
+ }
+
+ function updateimages(error, stdout, stderr) {
+ if (error || stderr)
+ return fail("update docker images failed", {
+ error: error, stderr: stderr, stdout: stdout
+ });
+ exec("docker images -q", imagelist);
+ }
+
+ function modify(cmd, name) {
+ if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
+ return fail("illegal instance name");
+ exec("docker "+cmd+" "+name, updatecontainers);
+ }
+
+ function containers() {
+ console.log("-> containers");
+ updatecontainers();
+ }
+
+ function images() {
+ console.log("-> images");
+ updateimages();
+ }
+
function start(name) {
console.log("-> start("+name+")");
modify("start", name);
@@ -111,36 +151,39 @@ module.exports = function() {
var bash_connections = {};
- function bash_start(name) {
- console.log("-> bash-start("+name+")");
+ function new_bash(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");
+ if (bash_connections[name]) return;
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_start(name) {
+ console.log("-> bash-start("+name+")");
+ new_bash(name);
}
function bash_input(data) {
console.log("-> bash-input("+data.name+", "+data.text+")");
- if (!bash_connections[data.name]) return fail("bash not open");
+ new_bash(name);
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();
- // }
+ function bash_end(name, text) {
+ console.log("-> bash-end("+name+")");
+ if (!bash_connections[name]) return;
+ bash_connections[name].stdin.close();
+ delete bash_connections[name]; bash_connections[name] = null;
+ }
socket
.on("containers", containers)
+ .on("images", images)
.on("start", start)
.on("stop", stop)
.on("pause", pause)
@@ -148,8 +191,8 @@ module.exports = function() {
.on("remove", remove)
.on('logs', logs)
.on('bash-start', bash_start)
- .on('bash-input', bash_input);
- //.on('bash-end', bash_end);
+ .on('bash-input', bash_input)
+ .on('bash-end', bash_end);
}
diff --git a/nodejs/views/index.ejs b/nodejs/views/index.ejs
index 7e38332..8db0b8e 100644
--- a/nodejs/views/index.ejs
+++ b/nodejs/views/index.ejs
@@ -27,6 +27,7 @@
+ ×