|
|
|
@ -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('<button id="popup1">'+n.status.action1+'</button>'); |
|
|
|
|
$("#popup1").click(function() { |
|
|
|
|
socket.emit(n.status.action1, name); |
|
|
|
|
emit(n.status.action1, name); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<button id="popup2">'+(focused?"overview":"focus")+'</button>'); |
|
|
|
@ -50,11 +50,27 @@ function DockerContainers() { |
|
|
|
|
if (n.status.action2) { |
|
|
|
|
$("#popup").append('<button id="popup3">'+n.status.action2+'</button>'); |
|
|
|
|
$("#popup3").click(function() { |
|
|
|
|
socket.emit(n.status.action2, name); |
|
|
|
|
emit(n.status.action2, name); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<br/>'); |
|
|
|
|
$("#popup").append('<button id="popup4">download</button>'); |
|
|
|
|
$("#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); |
|
|
|
@ -66,49 +82,6 @@ function DockerContainers() { |
|
|
|
|
$("#popup").show(); |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
this.details = function(name) { |
|
|
|
|
var res = ` |
|
|
|
|
<div id="tabs"> |
|
|
|
|
<ul> |
|
|
|
|
<li><a href="#tabs-1">Overview</a></li> |
|
|
|
|
<li><a href="#tabs-2">Logs</a></li> |
|
|
|
|
<li><a href="#tabs-3">Dump</a></li> |
|
|
|
|
</ul> |
|
|
|
|
<div id="tabs-1"> |
|
|
|
|
<table class="details docker"> |
|
|
|
|
<thead> |
|
|
|
|
<tr> |
|
|
|
|
<th>Name</th> |
|
|
|
|
<th>Ports</th> |
|
|
|
|
<th>Volumes</th> |
|
|
|
|
<th>Links</th> |
|
|
|
|
<th>Environments</th> |
|
|
|
|
<th>Image</th> |
|
|
|
|
<th>Command</th> |
|
|
|
|
</tr> |
|
|
|
|
</thead> |
|
|
|
|
<tbody> |
|
|
|
|
`;
|
|
|
|
|
var n = nodes[name]; |
|
|
|
|
res += ` |
|
|
|
|
</tbody> |
|
|
|
|
</table> |
|
|
|
|
</div> |
|
|
|
|
<div id="tabs-2"> |
|
|
|
|
</div> |
|
|
|
|
<div id="tabs-3"> |
|
|
|
|
<pre>`;
|
|
|
|
|
res += JSON.stringify(containers[nodes[name].id], null, 4); |
|
|
|
|
res += ` |
|
|
|
|
</pre> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<script> |
|
|
|
|
$(function() {$("#tabs").tabs();}); |
|
|
|
|
</script> |
|
|
|
|
`;
|
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function getIps(n, ips) { |
|
|
|
|
n.ports.forEach(function(p) { |
|
|
|
|
if (!ips[p.ip]) ips[p.ip] = []; |
|
|
|
@ -185,7 +158,7 @@ function DockerContainers() { |
|
|
|
|
n = n || nodes; |
|
|
|
|
for (name in n) getIps(n[name], ips); |
|
|
|
|
res += graphIpClusters(ips); |
|
|
|
|
for (name in n) res += graphNode(n[name]);
|
|
|
|
|
for (name in n) res += graphNode(n[name]); |
|
|
|
|
res += "{rank=same;\n"; |
|
|
|
|
for (name in n) res += graphVolumesInside(n[name]); |
|
|
|
|
res+="}\n"; |
|
|
|
@ -243,7 +216,6 @@ function DockerContainers() { |
|
|
|
|
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; |
|
|
|
|
console.log("STATUS", name, c.State, nodes[name].status); |
|
|
|
|
nodes[name].volumes = []; |
|
|
|
|
var volumes = c.Volumes || c.Config.Volumes; |
|
|
|
|
nodes[name].volumes = []; |
|
|
|
@ -316,6 +288,14 @@ function DockerContainers() { |
|
|
|
|
|
|
|
|
|
var dc = new DockerContainers(); |
|
|
|
|
|
|
|
|
|
function htmlenc(html) { |
|
|
|
|
return $('<div/>').text(html).html(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function htmldec(data) { |
|
|
|
|
return $('<div/>').html(data).text(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Show error messsage
|
|
|
|
|
/** Fades in an error message and logs to console. |
|
|
|
|
@param data (optional) The error can be a string or any structure. |
|
|
|
@ -389,6 +369,7 @@ function status(text, msg) { |
|
|
|
|
$("#popup").hide(); |
|
|
|
|
if (msg) success(msg); |
|
|
|
|
else setTimeout("$('#status').fadeOut('slow')", 5000); |
|
|
|
|
zoom(0); |
|
|
|
|
$("#main").show(); |
|
|
|
|
$("form input:first-child").focus(); |
|
|
|
|
dc.contextmenu("#main"); |
|
|
|
@ -488,7 +469,6 @@ function showviz(vizpath, more) { |
|
|
|
|
} |
|
|
|
|
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}"; |
|
|
|
|
try { |
|
|
|
|
zoomlevel = 0; |
|
|
|
|
status(more?Viz(res)+more:Viz(res)); |
|
|
|
|
} catch(e) { |
|
|
|
|
(res = res.split("\n")).forEach(function(v, i, a) { |
|
|
|
@ -504,50 +484,116 @@ function details(name) { |
|
|
|
|
showviz(dc.subgraph(focused)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function action(container, action) { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
|
$.ajax({url: "action.php?container="+container+"&action="+action, success: function(res) { |
|
|
|
|
success(res); |
|
|
|
|
manage(); |
|
|
|
|
}}).fail(function() { |
|
|
|
|
error("offline"); |
|
|
|
|
}); |
|
|
|
|
function containers(c) { |
|
|
|
|
console.log("->rcv containers"); |
|
|
|
|
dc.setContainers(c); |
|
|
|
|
if (focused && dc.exists(focused)) |
|
|
|
|
details(focused); |
|
|
|
|
else |
|
|
|
|
overview(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Manage Docker Services */ |
|
|
|
|
function manage() { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
|
$.ajax({url: "manage.php", success: function(res) { |
|
|
|
|
status(res); |
|
|
|
|
}}).fail(function() { |
|
|
|
|
error("offline"); |
|
|
|
|
}); |
|
|
|
|
function logs(data) { |
|
|
|
|
console.log("->rcv logs("+data.name+")"); |
|
|
|
|
$("#main").hide(); |
|
|
|
|
$("#console").hide(); |
|
|
|
|
$("#logs").show(); |
|
|
|
|
if (data.type=='done') { |
|
|
|
|
$("#logs").append('<span class="'+data.type+'">\nDONE</span>'); |
|
|
|
|
} else { |
|
|
|
|
$("#logs").append('<span class="'+data.type+'">'+htmlenc(data.text)+'</span>'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Show an Overview of all Docker Images */ |
|
|
|
|
function imgs() { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
|
$.ajax({url: "images.php", success: function(res) { |
|
|
|
|
try { |
|
|
|
|
showviz(res); |
|
|
|
|
} catch(e) { |
|
|
|
|
(res = res.split("\n")).forEach(function(v, i, a) { |
|
|
|
|
a[i] = ("000"+(i+1)).slice(-3)+": "+v; |
|
|
|
|
function strInsert(str, pos, txt) { |
|
|
|
|
return str.slice(0, pos)+txt+str.slice(pos); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function ansifilter(data) { |
|
|
|
|
console.log("ansifilter"); |
|
|
|
|
var res = data; |
|
|
|
|
var pos = -1; |
|
|
|
|
var spans = 0; |
|
|
|
|
while ((pos=res.indexOf("\x1B[")) >= 0) { |
|
|
|
|
var end = res.indexOf("m", pos+2); |
|
|
|
|
if (end>0) { |
|
|
|
|
var control= res.slice(pos+2, end); |
|
|
|
|
res = res.slice(0, pos)+res.slice(end+1); |
|
|
|
|
control.split(';').forEach(function(c) { |
|
|
|
|
switch (parseInt(c)) { |
|
|
|
|
// set
|
|
|
|
|
case 1: res = strInsert(res, pos, '<span class="bold">'); ++spans; break; |
|
|
|
|
case 2: res = strInsert(res, pos, '<span class="dim">'); ++spans; break; |
|
|
|
|
case 4: res = strInsert(res, pos, '<span class="underlined">'); ++spans; break; |
|
|
|
|
case 5: res = strInsert(res, pos, '<span class="blink">'); ++spans; break; |
|
|
|
|
case 7: res = strInsert(res, pos, '<span class="reverse">'); ++spans; break; |
|
|
|
|
case 8: res = strInsert(res, pos, '<span class="hidden">'); ++spans; break; |
|
|
|
|
// reset
|
|
|
|
|
case 0: for (;spans;--spans) res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 21: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 22: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 23: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 25: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 27: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
case 28: if (spans) --spans; res = strInsert(res, pos, "</span>"); break; |
|
|
|
|
// fg colors
|
|
|
|
|
case 39: res = strInsert(res, pos, '<span class="fgdefault">'); ++spans; break; |
|
|
|
|
case 30: res = strInsert(res, pos, '<span class="fgblack">'); ++spans; break; |
|
|
|
|
case 31: res = strInsert(res, pos, '<span class="fgred">'); ++spans; break; |
|
|
|
|
case 32: res = strInsert(res, pos, '<span class="fggreen">'); ++spans; break; |
|
|
|
|
case 33: res = strInsert(res, pos, '<span class="fgyellow">'); ++spans; break; |
|
|
|
|
case 34: res = strInsert(res, pos, '<span class="fgblue">'); ++spans; break; |
|
|
|
|
case 35: res = strInsert(res, pos, '<span class="fgmagenta">'); ++spans; break; |
|
|
|
|
case 36: res = strInsert(res, pos, '<span class="fgcyan">'); ++spans; break; |
|
|
|
|
case 37: res = strInsert(res, pos, '<span class="fglightgrey">'); ++spans; break; |
|
|
|
|
case 90: res = strInsert(res, pos, '<span class="fgdarkgrey">'); ++spans; break; |
|
|
|
|
case 91: res = strInsert(res, pos, '<span class="fglightred">'); ++spans; break; |
|
|
|
|
case 92: res = strInsert(res, pos, '<span class="fglightgreen">'); ++spans; break; |
|
|
|
|
case 93: res = strInsert(res, pos, '<span class="fglightyellow">'); ++spans; break; |
|
|
|
|
case 94: res = strInsert(res, pos, '<span class="fglightblue">'); ++spans; break; |
|
|
|
|
case 95: res = strInsert(res, pos, '<span class="fglightmagenta">'); ++spans; break; |
|
|
|
|
case 96: res = strInsert(res, pos, '<span class="fglightcyan">'); ++spans; break; |
|
|
|
|
case 97: res = strInsert(res, pos, '<span class="fgwhite">'); ++spans; break; |
|
|
|
|
// bg colors
|
|
|
|
|
case 49: res = strInsert(res, pos, '<span class="bgdefault">'); ++spans; break; |
|
|
|
|
case 40: res = strInsert(res, pos, '<span class="bgblack">'); ++spans; break; |
|
|
|
|
case 41: res = strInsert(res, pos, '<span class="bgred">'); ++spans; break; |
|
|
|
|
case 42: res = strInsert(res, pos, '<span class="bggreen">'); ++spans; break; |
|
|
|
|
case 43: res = strInsert(res, pos, '<span class="bgyellow">'); ++spans; break; |
|
|
|
|
case 44: res = strInsert(res, pos, '<span class="bgblue">'); ++spans; break; |
|
|
|
|
case 45: res = strInsert(res, pos, '<span class="bgmagenta">'); ++spans; break; |
|
|
|
|
case 46: res = strInsert(res, pos, '<span class="bgcyan">'); ++spans; break; |
|
|
|
|
case 47: res = strInsert(res, pos, '<span class="bglightgrey">'); ++spans; break; |
|
|
|
|
case 100: res = strInsert(res, pos, '<span class="bgdarkgrey">'); ++spans; break; |
|
|
|
|
case 101: res = strInsert(res, pos, '<span class="bglightred">'); ++spans; break; |
|
|
|
|
case 102: res = strInsert(res, pos, '<span class="bglightgreen">'); ++spans; break; |
|
|
|
|
case 103: res = strInsert(res, pos, '<span class="bglightyellow">'); ++spans; break; |
|
|
|
|
case 104: res = strInsert(res, pos, '<span class="bglightblue">'); ++spans; break; |
|
|
|
|
case 105: res = strInsert(res, pos, '<span class="bglightmagenta">'); ++spans; break; |
|
|
|
|
case 106: res = strInsert(res, pos, '<span class="bglightcyan">'); ++spans; break; |
|
|
|
|
case 107: res = strInsert(res, pos, '<span class="bgwhite">'); ++spans; break; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
status("<h2>Exception Caught:</h2><p>"+e+"<p><pre>"+res.join("\n")+"</pre>"); |
|
|
|
|
} else { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
}}).fail(function() { |
|
|
|
|
error("offline"); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
for (;spans;--spans) res += "</span>"; |
|
|
|
|
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('<span class="'+data.type+'">\nDONE</span>'); |
|
|
|
|
} else { |
|
|
|
|
$("#screen").append('<span class="'+data.type+'">'+ansifilter(htmlenc(data.text))+'</span>'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|