|
|
|
@ -11,7 +11,68 @@ |
|
|
|
|
var socket = io.connect(); |
|
|
|
|
var focused = null; |
|
|
|
|
|
|
|
|
|
function DockerContainers() { |
|
|
|
|
function Docker() { |
|
|
|
|
|
|
|
|
|
function same(array1, array2) { |
|
|
|
|
return (array1.length == array2.length) |
|
|
|
|
&& array1.every(function(element, index) { |
|
|
|
|
return element === array2[index];
|
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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.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}, |
|
|
|
@ -31,57 +92,6 @@ function DockerContainers() { |
|
|
|
|
if (nodes[name]) return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
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); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<br/>'); |
|
|
|
|
$("#popup").append('<button id="popup4">logs</button>'); |
|
|
|
|
$("#popup4").click(function() { |
|
|
|
|
emit("logs", name); |
|
|
|
|
}); |
|
|
|
|
if (n.status.bash) { |
|
|
|
|
$("#popup").append('<button id="popup5">bash</button>'); |
|
|
|
|
$("#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('<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(); |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
function getIps(n, ips) { |
|
|
|
|
n.ports.forEach(function(p) { |
|
|
|
|
if (!ips[p.ip]) ips[p.ip] = []; |
|
|
|
@ -111,7 +121,7 @@ function DockerContainers() { |
|
|
|
|
} |
|
|
|
|
function graphNode(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
var label = n.name+'\\n'+n.image; |
|
|
|
|
var label = n.name+'\\n'+n.image.name; |
|
|
|
|
res += '"'+n.name+'"' |
|
|
|
|
+' [label="'+label |
|
|
|
|
+'",URL="#'+n.name |
|
|
|
@ -184,19 +194,61 @@ function DockerContainers() { |
|
|
|
|
if (!ns[peer]) addNodes(ns, peer); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
this.subgraph = function(name) { |
|
|
|
|
var ns = []; |
|
|
|
|
this.subnet = function(name) { |
|
|
|
|
var ns = {}; |
|
|
|
|
addNodes(ns, name); |
|
|
|
|
return this.graph(ns); |
|
|
|
|
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<a.links.length; ++i) if (a.links[i].to == b.name) return 1; // a after b;
|
|
|
|
|
for (var i=0; i<b.links.length; ++i) if (b.links[i].to == a.name) return -1; // a before b;
|
|
|
|
|
return 0; // a and b do not depend on each other
|
|
|
|
|
}); |
|
|
|
|
return creates; |
|
|
|
|
} |
|
|
|
|
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].id = c.Id; |
|
|
|
|
nodes[name].name = name; |
|
|
|
|
nodes[name].image = c.Config.Image; |
|
|
|
|
nodes[name].image = { |
|
|
|
|
name: c.Config.Image, |
|
|
|
|
id: c.Image |
|
|
|
|
}; |
|
|
|
|
nodes[name].env = c.Config.Env; |
|
|
|
|
nodes[name].cmd = c.Config.Cmd; |
|
|
|
|
nodes[name].entrypoint = c.Entrypoint; |
|
|
|
|
nodes[name].ports = []; |
|
|
|
|
var ports = c.NetworkSettings.Ports || c.NetworkSettings.PortBindings; |
|
|
|
|
if (ports) |
|
|
|
@ -278,7 +330,71 @@ function DockerContainers() { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
this.setContainers = function(c) { |
|
|
|
|
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); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<br/>'); |
|
|
|
|
$("#popup").append('<button id="popup4">logs</button>'); |
|
|
|
|
$("#popup4").click(function() { |
|
|
|
|
showLogs(); |
|
|
|
|
emit("logs", name); |
|
|
|
|
}); |
|
|
|
|
if (n.status.bash) { |
|
|
|
|
$("#popup").append('<button id="popup5">bash</button>'); |
|
|
|
|
$("#popup5").click(function() { |
|
|
|
|
showConsole(); |
|
|
|
|
emit("bash-start", name); |
|
|
|
|
$("#bash").submit(function() { |
|
|
|
|
emit("bash-input", {name: name, text: $("#command").val()+"\n"}); |
|
|
|
|
$("#command").val(""); |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<button id="popup6">download</button>'); |
|
|
|
|
$("#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"); |
|
|
|
|
$("#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; |
|
|
|
@ -286,7 +402,12 @@ function DockerContainers() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var dc = new DockerContainers(); |
|
|
|
|
this.images = new this.Images(); |
|
|
|
|
this.containers = new this.Containers(); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var docker = new Docker(); |
|
|
|
|
|
|
|
|
|
function htmlenc(html) { |
|
|
|
|
return $('<div/>').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('<span class="'+data.type+'">\nDONE</span>'); |
|
|
|
|
} 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('<span class="'+data.type+'">\nDONE</span>'); |
|
|
|
|
} 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(); |
|
|
|
|