possibility to download configuration

single-host
Marc Wäckerlin 9 years ago
parent 6a5dad3c93
commit 43448d9ed7
  1. 676
      nodejs/public/javascripts/servicedock.js
  2. 2
      nodejs/public/stylesheets/servicedock.css
  3. 71
      nodejs/sockets/index.js
  4. 1
      nodejs/views/index.ejs

@ -11,282 +11,403 @@
var socket = io.connect(); var socket = io.connect();
var focused = null; var focused = null;
function DockerContainers() { function Docker() {
var Status = Object.freeze({
Error: {color: "red", action1: "start", action2: "remove", bash: false}, function same(array1, array2) {
Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false}, return (array1.length == array2.length)
Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false}, && array1.every(function(element, index) {
Paused: {color: "lightgrey", action1: "unpause", action2: null, bash: false}, return element === array2[index];
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; var _docker = this;
return false;
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) { this.Containers = function() {
name = $(this).attr("xlink:href").replace(/^#/, "");
var n = nodes[name]; var _containers = this;
$(selector).prepend('<div id="popup"></div>')
$("#popup").empty(); var Status = Object.freeze({
if (n.status.action1) { Error: {color: "red", action1: "start", action2: "remove", bash: false},
$("#popup").append('<button id="popup1">'+n.status.action1+'</button>'); Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false},
$("#popup1").click(function() { Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false},
emit(n.status.action1, name); 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('<button id="popup2">'+(focused?"overview":"focus")+'</button>'); res += "{rank=same;\n";
$("#popup2").click(function() { for (ip in ips) {
if (focused) overview(); else details(name); 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) { n.links.forEach(function(l) {
$("#popup").append('<button id="popup3">'+n.status.action2+'</button>'); res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n'
$("#popup3").click(function() {
emit(n.status.action2, name);
}); });
} return res;
$("#popup").append('<br/>'); }
$("#popup").append('<button id="popup4">logs</button>'); function graphVolumesInside(n) {
$("#popup4").click(function() { var res = "";
emit("logs", name); n.volumes.forEach(function(v) {
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
}); });
if (n.status.bash) { return res;
$("#popup").append('<button id="popup5">bash</button>'); }
$("#popup5").click(function() { function graphVolumesOutside(n) {
emit("bash-start", name); var res = "";
$("#console").show(); n.volumes.forEach(function(v) {
$("#main").hide(); if (v.host)
$("#bash").submit(function() { res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
emit("bash-input", {name: name, text: $("#command").val()+"\n"});
$("#command").val("");
})
});
}
$("#popup").append('<button id="popup6">download</button>');
$("#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(); return res;
}) }
} function graphVolumesConnections(n) {
function getIps(n, ips) { var res = "";
n.ports.forEach(function(p) { n.volumes.forEach(function(v) {
if (!ips[p.ip]) ips[p.ip] = []; if (v.host)
ips[p.ip].push(p); res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n';
}); res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n';
}
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';
}); });
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"; res+="}\n";
for (name in n) res += graphVolumesConnections(n[name]);
return res;
} }
res += "{rank=same;\n"; function addNodes(ns, name) {
for (ip in ips) { var n = nodes[name];
ips[ip].forEach(function(p) { ns[name] = n;
res += '"'+p.ip+":"+p.external+'";\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"; this.subnet = function(name) {
return res; var ns = {};
} addNodes(ns, name);
function graphNode(n) { return ns;
var res = ""; }
var label = n.name+'\\n'+n.image; this.subgraph = function(name) {
res += '"'+n.name+'"' return this.graph(this.subnet(name));
+' [label="'+label }
+'",URL="#'+n.name this.creation = function(name) {
+'",style=filled,fillcolor='+n.status.color+"];\n"; var ns = this.subnet(name);
n.ports.forEach(function(p) { var creates = [];
res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name for (n in ns) {
+'" [label="'+p.internal+'"];\n'; var instance = {
}); name: ns[n].name,
n.links.forEach(function(l) { image: ns[n].image.name,
res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n' ports: ns[n].ports,
}); env: ns[n].env,
return res; cmd: ns[n].cmd,
} entrypoint: ns[n].entrypoint,
function graphVolumesInside(n) { volumesfrom: ns[n].volumesfrom,
var res = ""; links: ns[n].links,
n.volumes.forEach(function(v) { volumes: []
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n'; };
}); ns[n].volumes.forEach(function(v) {
return res; if (v.host) instance.volumes.push({
} inside: v.inside,
function graphVolumesOutside(n) { outside: v.host
var res = ""; });
n.volumes.forEach(function(v) { });
if (v.host) _docker.images.cleanup(ns[n].image.id, instance);
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n'; creates.push(instance);
}); }
return res; creates.sort(function(a, b) {
} if (a.volumesfrom.indexOf(b)>=0) return 1; // a after b
function graphVolumesConnections(n) { if (b.volumesfrom.indexOf(a)>=0) return -1; // a before b
var res = ""; for (var i=0; i<a.links.length; ++i) if (a.links[i].to == b.name) return 1; // a after b;
n.volumes.forEach(function(v) { for (var i=0; i<b.links.length; ++i) if (b.links[i].to == a.name) return -1; // a before b;
if (v.host) return 0; // a and b do not depend on each other
res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n'; });
res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n'; return creates;
}); }
n.volumesfrom.forEach(function(o) { function setup() {
res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n'; delete nodes; nodes = [];
}); containers.forEach(function(c, i) {
return res; var name = c.Name.replace(/^\//, "");
} if (!nodes[name]) nodes[name] = {};
this.graph = function(n) { nodes[name].id = c.Id;
var res = ""; nodes[name].name = name;
var ips = []; nodes[name].image = {
n = n || nodes; name: c.Config.Image,
for (name in n) getIps(n[name], ips); id: c.Image
res += graphIpClusters(ips); };
for (name in n) res += graphNode(n[name]); nodes[name].env = c.Config.Env;
res += "{rank=same;\n"; nodes[name].cmd = c.Config.Cmd;
for (name in n) res += graphVolumesInside(n[name]); nodes[name].entrypoint = c.Entrypoint;
res+="}\n"; nodes[name].ports = [];
res += "{rank=same;\n"; var ports = c.NetworkSettings.Ports || c.NetworkSettings.PortBindings;
for (name in n) res += graphVolumesOutside(n[name]); if (ports)
res+="}\n"; for (var port in ports)
for (name in n) res += graphVolumesConnections(n[name]); if (ports[port])
return res; for (var expose in ports[port]) {
} var ip = ports[port][expose].HostIp;
function addNodes(ns, name) { if (!ip||ip==""||ip=="0.0.0.0"||ip==0) ip=window.location.hostname;
var n = nodes[name]; nodes[name].ports.push({
ns[name] = n; internal: port,
n.links.forEach(function(peer) { external: ports[port][expose].HostPort,
if (!ns[peer.to]) addNodes(ns, peer.to); ip: ip
}); });
n.usedby.forEach(function(peer) { }
if (!ns[peer]) addNodes(ns, peer); if (c.State.Paused) nodes[name].status = Status.Paused;
}); else if (c.State.Running) nodes[name].status = Status.Running;
n.volumesfrom.forEach(function(peer) { else if (c.State.Restarting) nodes[name].status = Status.Restarting;
if (!ns[peer]) addNodes(ns, peer); else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated;
}); else nodes[name].status = Status.Error;
n.volumesto.forEach(function(peer) { nodes[name].volumes = [];
if (!ns[peer]) addNodes(ns, peer); var volumes = c.Volumes || c.Config.Volumes;
}); nodes[name].volumes = [];
} if (volumes)
this.subgraph = function(name) { for (var volume in volumes) {
var ns = []; var rw = "rw";
addNodes(ns, name); var outside = (typeof volumes[volume]=="string")?volumes[volume]:null;
return this.graph(ns); if (c.Mounts) c.Mounts.forEach(function(mnt) {
} if (mnt.Destination==volume) {
function setup() { outside = mnt.Source;
delete nodes; nodes = []; rw = mnt.RW ? "rw" : "ro";
containers.forEach(function(c, i) { }
var name = c.Name.replace(/^\//, ""); });
if (!nodes[name]) nodes[name] = {}; nodes[name].volumes.push({
nodes[name].id = i; id: volume+':'+(outside?outside:name),
nodes[name].name = name; rw:rw,
nodes[name].image = c.Config.Image; inside: volume,
nodes[name].ports = []; outside: outside,
var ports = c.NetworkSettings.Ports || c.NetworkSettings.PortBindings; host: outside && !outside.match(/^\/var\/lib\/docker/)
if (ports) ? outside : null
for (var port in ports) });
if (ports[port]) }
for (var expose in ports[port]) { nodes[name].volumesfrom = [];
var ip = ports[port][expose].HostIp; if (!nodes[name].volumesto) nodes[name].volumesto = [];
if (!ip||ip==""||ip=="0.0.0.0"||ip==0) ip=window.location.hostname; if (c.HostConfig.VolumesFrom) c.HostConfig.VolumesFrom.forEach(function(id) {
nodes[name].ports.push({ containers.forEach(function(c) {
internal: port, if (c.Id == id || c.Name == "/"+id || c.Name == id) {
external: ports[port][expose].HostPort, var src = c.Name.replace(/^\//, "");
ip: ip nodes[name].volumesfrom.push(src);
}); if (!nodes[src]) nodes[src] = {};
} if (!nodes[src].volumesto) nodes[src].volumesto = [];
if (c.State.Paused) nodes[name].status = Status.Paused; nodes[src].volumesto.push(name);
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";
} }
}); });
nodes[name].volumes.push({ });
id: volume+':'+(outside?outside:name), nodes[name].links = [];
rw:rw, if (!nodes[name].usedby) nodes[name].usedby = [];
inside: volume, if (c.HostConfig && c.HostConfig.Links)
outside: outside, c.HostConfig.Links.forEach(function(l) {
host: outside && !outside.match(/^\/var\/lib\/docker/) var target = {
? outside : null 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);
});
});
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);
}, [])
})
})
}
}
this.contextmenu = function(selector) {
$('a[xlink\\:href^=#]').click(function(e) {
name = $(this).attr("xlink:href").replace(/^#/, "");
var n = nodes[name];
$(selector).prepend('<div id="popup"></div>')
$("#popup").empty();
if (n.status.action1) {
$("#popup").append('<button id="popup1">'+n.status.action1+'</button>');
$("#popup1").click(function() {
emit(n.status.action1, name);
});
}
$("#popup").append('<button id="popup2">'+(focused?"overview":"focus")+'</button>');
$("#popup2").click(function() {
if (focused) overview(); else details(name);
});
if (n.status.action2) {
$("#popup").append('<button id="popup3">'+n.status.action2+'</button>');
$("#popup3").click(function() {
emit(n.status.action2, name);
}); });
} }
nodes[name].volumesfrom = []; $("#popup").append('<br/>');
if (!nodes[name].volumesto) nodes[name].volumesto = []; $("#popup").append('<button id="popup4">logs</button>');
if (c.HostConfig.VolumesFrom) c.HostConfig.VolumesFrom.forEach(function(id) { $("#popup4").click(function() {
containers.forEach(function(c) { showLogs();
if (c.Id == id || c.Name == "/"+id || c.Name == id) { emit("logs", name);
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);
}
}); });
}); if (n.status.bash) {
nodes[name].links = []; $("#popup").append('<button id="popup5">bash</button>');
if (!nodes[name].usedby) nodes[name].usedby = []; $("#popup5").click(function() {
if (c.HostConfig && c.HostConfig.Links) showConsole();
c.HostConfig.Links.forEach(function(l) { emit("bash-start", name);
var target = { $("#bash").submit(function() {
to: l.replace(/^\/?([^:]*).*$/, "$1"), emit("bash-input", {name: name, text: $("#command").val()+"\n"});
link: l.replace(new RegExp("^.*:/?"+name+"/"), "") $("#command").val("");
}; })
nodes[name].links.push(target); });
if (!nodes[target.to]) nodes[target.to] = {}; }
if (!nodes[target.to].usedby) nodes[target.to].usedby = []; $("#popup").append('<button id="popup6">download</button>');
nodes[target.to].usedby.push(name); $("#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);
}); });
}); $("#popup").css("position", "fixed");
for (name in nodes) { // cleanup duplicate links to volumes when using volumes-from $("#popup").css("top", e.pageY-$("#popup").height()/4);
var n = nodes[name]; $("#popup").css("left", e.pageX-$("#popup").width()/2);
n.volumesfrom.forEach(function(other) { $("#popup").mouseleave(function() {
var o = nodes[other]; $("#popup").hide();
o.volumes.forEach(function(ovol) { }).click(function() {
n.volumes.reduceRight(function(x, nvol, i, arr) { $("#popup").hide();
if (nvol.id == ovol.id) });
arr.splice(i, 1); $("#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); this.images = new this.Images();
if (typeof c != "object") throw "wrong format: "+(typeof c); this.containers = new this.Containers();
containers = c;
setup();
}
} }
var dc = new DockerContainers(); var docker = new Docker();
function htmlenc(html) { function htmlenc(html) {
return $('<div/>').text(html).html(); return $('<div/>').text(html).html();
@ -312,7 +433,8 @@ function error(data) {
console.log("error: "+data); console.log("error: "+data);
} else { } else {
$("#status").html('unknown error: '+JSON.stringify(data)); $("#status").html('unknown error: '+JSON.stringify(data));
console.log("error: "+JSON.stringify(data)); console.log("error: ", data);
console.log((new Error('stacktrace')));
} }
} else { } else {
$("#status").html('error'); $("#status").html('error');
@ -372,7 +494,7 @@ function status(text, msg) {
zoom(0); zoom(0);
$("#main").show(); $("#main").show();
$("form input:first-child").focus(); $("form input:first-child").focus();
dc.contextmenu("#main"); docker.containers.contextmenu("#main");
} }
function emit(signal, data) { function emit(signal, data) {
@ -481,23 +603,50 @@ function showviz(vizpath, more) {
function details(name) { function details(name) {
if (name) focused = name; if (name) focused = name;
else if (!focused) return overview(); 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) { function containers(c) {
console.log("->rcv containers"); console.log("->rcv containers");
dc.setContainers(c); docker.containers.set(c);
if (focused && dc.exists(focused)) showImage();
if (focused && docker.containers.exists(focused))
details(focused); details(focused);
else else
overview(); overview();
} }
function logs(data) { function showImage() {
console.log("->rcv logs("+data.name+")"); $("#close").hide();
$("#main").hide(); $("#logs").hide();
$("#console").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(); $("#logs").show();
$("#console").hide();
$("#close").show();
}
function logs(data) {
console.log("->rcv logs("+data.name+")");
if (data.type=='done') { if (data.type=='done') {
$("#logs").append('<span class="'+data.type+'">\nDONE</span>'); $("#logs").append('<span class="'+data.type+'">\nDONE</span>');
} else { } else {
@ -586,9 +735,6 @@ function ansifilter(data) {
function bash_data(data) { function bash_data(data) {
console.log("->rcv bash-data("+data.name+")", data); console.log("->rcv bash-data("+data.name+")", data);
$("#main").hide();
$("#logs").hide();
$("#console").show();
if (data.type=='done') { if (data.type=='done') {
$("#screen").append('<span class="'+data.type+'">\nDONE</span>'); $("#screen").append('<span class="'+data.type+'">\nDONE</span>');
} else { } else {
@ -598,7 +744,7 @@ function bash_data(data) {
function overview() { function overview() {
focused = null; focused = null;
showviz(dc.graph()); showviz(docker.containers.graph());
} }
/// Initial Function: Startup /// Initial Function: Startup
@ -609,6 +755,7 @@ function start() {
$("#username").html(window.location.hostname) $("#username").html(window.location.hostname)
try { try {
status("Starting up ..."); status("Starting up ...");
emit("images");
emit("containers"); emit("containers");
} catch (m) { } catch (m) {
error(m); error(m);
@ -624,6 +771,7 @@ function init() {
socket socket
.on("fail", error) .on("fail", error)
.on("containers", containers) .on("containers", containers)
.on("images", images)
.on("logs", logs) .on("logs", logs)
.on("bash-data", bash_data); .on("bash-data", bash_data);
start(); start();

@ -195,7 +195,7 @@ table.docker li+li {
color: black; color: black;
} }
#menuicon, #imagetools img { #menuicon, #imagetools img, #close {
cursor: pointer; cursor: pointer;
height: 1em; height: 1em;
width: auto; width: auto;

@ -70,6 +70,46 @@ module.exports = function() {
updatecontainers(); 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) { function start(name) {
console.log("-> start("+name+")"); console.log("-> start("+name+")");
modify("start", name); modify("start", name);
@ -111,36 +151,39 @@ module.exports = function() {
var bash_connections = {}; var bash_connections = {};
function bash_start(name) { function new_bash(name) {
console.log("-> bash-start("+name+")");
if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i)) if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
return fail("illegal instance name"); return fail("illegal instance name");
if (bash_connections[name]) return fail("bash already open"); if (bash_connections[name]) return;
bash_connections[name] = bash_connections[name] =
pty.spawn("docker", ["exec", "-it", name, "bash", "-i"]); pty.spawn("docker", ["exec", "-it", name, "bash", "-i"]);
bash_connections[name].stdout.on('data', function(data) { bash_connections[name].stdout.on('data', function(data) {
emit('bash-data', {name: name, type: 'stdout', text: data.toString()}); 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) { function bash_input(data) {
console.log("-> bash-input("+data.name+", "+data.text+")"); 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.resume();
bash_connections[data.name].stdin.write(data.text); bash_connections[data.name].stdin.write(data.text);
} }
// function bash_end(name, text) { function bash_end(name, text) {
// console.log("-> bash-end("+name+")"); console.log("-> bash-end("+name+")");
// if (!bash_connections[name]) return fail("bash not open"); if (!bash_connections[name]) return;
// bash_connections[name].stdin.close(); bash_connections[name].stdin.close();
// } delete bash_connections[name]; bash_connections[name] = null;
}
socket socket
.on("containers", containers) .on("containers", containers)
.on("images", images)
.on("start", start) .on("start", start)
.on("stop", stop) .on("stop", stop)
.on("pause", pause) .on("pause", pause)
@ -148,8 +191,8 @@ module.exports = function() {
.on("remove", remove) .on("remove", remove)
.on('logs', logs) .on('logs', logs)
.on('bash-start', bash_start) .on('bash-start', bash_start)
.on('bash-input', bash_input); .on('bash-input', bash_input)
//.on('bash-end', bash_end); .on('bash-end', bash_end);
} }

@ -27,6 +27,7 @@
<img onclick="zoom(1)" src="images/zoom.svg" /> <img onclick="zoom(1)" src="images/zoom.svg" />
<img onclick="rotateviz()" src="images/rotate.svg" /> <img onclick="rotateviz()" src="images/rotate.svg" />
</span> </span>
<span id="close" onclick="showImage()" style="display: none">×</span>
<img id="menuicon" onclick="togglemenu()" onmouseover="$('#menu').show();" src="images/menu.svg" /> <img id="menuicon" onclick="togglemenu()" onmouseover="$('#menu').show();" src="images/menu.svg" />
</div> </div>
</div> </div>

Loading…
Cancel
Save