|
|
|
/*! @file
|
|
|
|
|
|
|
|
@id $Id$
|
|
|
|
|
|
|
|
This is the main application as it is fully run in the user's browser.
|
|
|
|
|
|
|
|
*/
|
|
|
|
// 1 2 3 4 5 6 7 8
|
|
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
|
|
|
|
var socket = io.connect();
|
|
|
|
|
|
|
|
function DockerContainers() {
|
|
|
|
var Status = Object.freeze({
|
|
|
|
Error: "red",
|
|
|
|
Terminated: "yellow",
|
|
|
|
Restarting: "lightblue",
|
|
|
|
Paused: "lightgrey",
|
|
|
|
Running: "lightgreen"
|
|
|
|
});
|
|
|
|
var containers = [];
|
|
|
|
var nodes = [];
|
|
|
|
this.graph = function() {
|
|
|
|
var res = "";
|
|
|
|
console.log("nodes["+nodes.length+"]=", nodes);
|
|
|
|
for (name in nodes) {
|
|
|
|
var n = nodes[name];
|
|
|
|
var label = n.name+'\\n'+n.image;
|
|
|
|
res += '"'+n.name+'"'
|
|
|
|
+' [label="'+label
|
|
|
|
+'",URL="details('+"'"+n.name+"'"
|
|
|
|
+')",style=filled,fillcolor='+n.status+"];\n";
|
|
|
|
}
|
|
|
|
res += "{rank=same;\n";
|
|
|
|
for (name in nodes) {
|
|
|
|
var n = nodes[name];
|
|
|
|
n.volumes.forEach(function(v) {
|
|
|
|
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
res+="}\n";
|
|
|
|
res += "{rank=same;\n";
|
|
|
|
for (name in nodes) {
|
|
|
|
var n = nodes[name];
|
|
|
|
n.volumes.forEach(function(v) {
|
|
|
|
if (v.host)
|
|
|
|
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
res+="}\n";
|
|
|
|
for (name in nodes) {
|
|
|
|
var n = nodes[name];
|
|
|
|
n.volumes.forEach(function(v) {
|
|
|
|
if (v.host)
|
|
|
|
res += '"'+v.id+'" -> "'+v.outside+'"\n';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for (name in nodes) {
|
|
|
|
var n = nodes[name];
|
|
|
|
n.volumes.forEach(function(v) {
|
|
|
|
res += '"'+n.name+'" -> "'+v.id+'"\n';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function setup() {
|
|
|
|
delete nodes; nodes=[];
|
|
|
|
containers.forEach(function(c) {
|
|
|
|
var name = c.Name.replace(/^\//, "");
|
|
|
|
nodes[name] = {};
|
|
|
|
console.log("container: "+name);
|
|
|
|
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=="127.0.0.1"||ip=="0.0.0.0") ip=null;
|
|
|
|
nodes[name].ports.push({
|
|
|
|
internal: port,
|
|
|
|
external: ports[port][expose].HostPort,
|
|
|
|
ip: ip
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (c.State.Running) nodes[name].status = Status.Running;
|
|
|
|
else if (c.State.Paused) nodes[name].status = Status.Paused;
|
|
|
|
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 outside = (typeof volumes[volume]=="string")?volumes[volume]:null;
|
|
|
|
nodes[name].volumes.push({
|
|
|
|
id: volume+':'+(outside?outside:name),
|
|
|
|
inside: volume,
|
|
|
|
outside: outside,
|
|
|
|
host: outside && !outside.match(/^\/var\/lib\/docker/)
|
|
|
|
? volumes[volume] : null
|
|
|
|
});
|
|
|
|
}
|
|
|
|
nodes[name].volumesfrom = c.VolumesFrom;
|
|
|
|
nodes[name].links = [];
|
|
|
|
if (c.HostConfig && c.HostConfig.Links)
|
|
|
|
c.HostConfig.Links.forEach(function(l) {
|
|
|
|
nodes[name].links.push({
|
|
|
|
to: l.replace(/^\/?([^:]*).*$/, "$1"),
|
|
|
|
link: l.replace(new RegExp("^.*:/?"+name+"/"), "")
|
|
|
|
});
|
|
|
|
});
|
|
|
|
console.log(nodes[name]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.setContainers = function(c) {
|
|
|
|
if (typeof c == "string") c = JSON.parse(c);
|
|
|
|
if (typeof c != "object") throw "wrong format: "+(typeof c);
|
|
|
|
containers = c;
|
|
|
|
setup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var dc = new DockerContainers();
|
|
|
|
|
|
|
|
/// Show error messsage
|
|
|
|
/** Fades in an error message and logs to console.
|
|
|
|
@param data (optional) The error can be a string or any structure.
|
|
|
|
Strings are shown to the user, structures are logged only.
|
|
|
|
@param stay (optional) If not given as @c true, reloads page after 5s. */
|
|
|
|
function error(data, stay) {
|
|
|
|
$("#status").fadeOut("slow", function() {
|
|
|
|
$("#status").addClass("error")
|
|
|
|
$("#status").removeClass("notice")
|
|
|
|
$("#status").removeClass("success")
|
|
|
|
if (data) {
|
|
|
|
if (typeof data == 'string') {
|
|
|
|
$("#status").html(data);
|
|
|
|
console.log("error: "+data);
|
|
|
|
} else {
|
|
|
|
$("#status").html('unknown error: '+JSON.stringify(data));
|
|
|
|
console.log("error: "+JSON.stringify(data));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$("#status").html('error');
|
|
|
|
console.log("error");
|
|
|
|
}
|
|
|
|
$("#status").fadeIn("slow");
|
|
|
|
if (!stay) setTimeout(start, 5000);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Show notice messsage
|
|
|
|
/** Fades in an notice message and logs to console.
|
|
|
|
@param text (optional) The data is a string. */
|
|
|
|
function notice(text) {
|
|
|
|
$("#status").fadeOut("slow", function() {
|
|
|
|
$("#status").addClass("notice")
|
|
|
|
$("#status").removeClass("error")
|
|
|
|
$("#status").removeClass("success")
|
|
|
|
if (text) {
|
|
|
|
$("#status").html(text);
|
|
|
|
console.log("notice: "+text);
|
|
|
|
} else {
|
|
|
|
$("#status").html('');
|
|
|
|
console.log("notice");
|
|
|
|
}
|
|
|
|
$("#status").fadeIn("slow");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Show notice messsage
|
|
|
|
/** Fades in an success message and logs to console.
|
|
|
|
@param text (optional) The data is a string. */
|
|
|
|
function success(text) {
|
|
|
|
$("#status").fadeOut("slow", function() {
|
|
|
|
$("#status").addClass("success")
|
|
|
|
$("#status").removeClass("error")
|
|
|
|
$("#status").removeClass("notice")
|
|
|
|
if (text) {
|
|
|
|
$("#status").html(text);
|
|
|
|
console.log("success: "+text);
|
|
|
|
} else {
|
|
|
|
$("#status").html('');
|
|
|
|
console.log("success");
|
|
|
|
}
|
|
|
|
$("#status").fadeIn("slow");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Show status message in the main screen area
|
|
|
|
/** @param text Text is a message or some complex HTML from the server.
|
|
|
|
@param msg The success message text */
|
|
|
|
function status(text, msg) {
|
|
|
|
$("#main").fadeOut("slow", function() {
|
|
|
|
$("#main").html(text);
|
|
|
|
if (msg) success(msg);
|
|
|
|
else setTimeout("$('#status').fadeOut('slow')", 5000);
|
|
|
|
$("#main").fadeIn("slow", function() {
|
|
|
|
$("form input:first-child").focus();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function emit(signal, data) {
|
|
|
|
console.log("<-snd "+signal);
|
|
|
|
socket.emit(signal, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function connected() {
|
|
|
|
console.log("server connected");
|
|
|
|
$("#connectionstatus #bad").hide();
|
|
|
|
$("#connectionstatus #good").show();
|
|
|
|
success("server connected");
|
|
|
|
}
|
|
|
|
|
|
|
|
function disconnected() {
|
|
|
|
console.log("server disconnected");
|
|
|
|
$("#connectionstatus #good").hide();
|
|
|
|
$("#connectionstatus #bad").show();
|
|
|
|
error("server disconnected", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function connectionstatus() {
|
|
|
|
if (socket.connected) connected(); else disconnected();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Toggle Menu Display
|
|
|
|
function togglemenu() {
|
|
|
|
$("#menu").toggle();
|
|
|
|
}
|
|
|
|
|
|
|
|
function about(c) {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
$.ajax({url: "about.php", success: function(res) {
|
|
|
|
try {
|
|
|
|
var a = JSON.parse(res);
|
|
|
|
status("<h2>"+a.description+"</h2>"+
|
|
|
|
"<p>"+a.project+"-"+a.version+"</p>"+
|
|
|
|
"<p>"+a.docker+"</p>"+
|
|
|
|
"<h3>README</h3>"+
|
|
|
|
"<pre>"+a.readme+"</pre>");
|
|
|
|
} catch(e) {
|
|
|
|
status("<pre>"+res+"</pre>");
|
|
|
|
error("Exception Caught: "+e);
|
|
|
|
}
|
|
|
|
}}).fail(function() {
|
|
|
|
error("offline");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var zoomlevel = 0;
|
|
|
|
function zoom(incr = 0) {
|
|
|
|
zoomlevel = (zoomlevel+incr)%2;
|
|
|
|
switch (zoomlevel) {
|
|
|
|
case 0: {
|
|
|
|
$("#main svg").css("width", "auto");
|
|
|
|
$("#main svg").css("height", "auto");
|
|
|
|
$("#main svg").css("max-width", "100%");
|
|
|
|
$("#main svg").css("max-height", "100%");
|
|
|
|
} break;
|
|
|
|
case 1: {
|
|
|
|
$("#main svg").css("width", "100%");
|
|
|
|
$("#main svg").css("height", "auto");
|
|
|
|
$("#main svg").css("max-width", "100%");
|
|
|
|
$("#main svg").css("max-height", "none");
|
|
|
|
} break;
|
|
|
|
case 2: {
|
|
|
|
$("#main.svg").css("width", "auto");
|
|
|
|
$("#main.svg").css("height", "100%");
|
|
|
|
$("#main.svg").css("max-width", "none");
|
|
|
|
$("#main.svg").css("max-height", "100%");
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var viz = null;
|
|
|
|
var rankdir = "LR";
|
|
|
|
function rotateviz() {
|
|
|
|
if (!viz) return;
|
|
|
|
if (rankdir == "LR")
|
|
|
|
rankdir = "TB";
|
|
|
|
else
|
|
|
|
rankdir = "LR";
|
|
|
|
showviz(viz);
|
|
|
|
}
|
|
|
|
function showviz(vizpath) {
|
|
|
|
$("#imagetools").show();
|
|
|
|
viz = vizpath;
|
|
|
|
console.log("DRAW: "+viz);
|
|
|
|
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}";
|
|
|
|
try {
|
|
|
|
zoomlevel = 0;
|
|
|
|
status(Viz(res));
|
|
|
|
} catch(e) {
|
|
|
|
(res = res.split("\n")).forEach(function(v, i, a) {
|
|
|
|
a[i] = ("000"+(i+1)).slice(-3)+": "+v;
|
|
|
|
});
|
|
|
|
status("<h2>Exception Caught:</h2><p>"+e+"<p><pre>"+res.join("\n")+"</pre>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function details(c) {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
$.ajax({url: "details.php?container="+c, success: function(res) {
|
|
|
|
try {
|
|
|
|
status(res);
|
|
|
|
} catch(e) {
|
|
|
|
status("<pre>"+res+"</pre>");
|
|
|
|
error("Exception Caught: "+e);
|
|
|
|
}
|
|
|
|
}}).fail(function() {
|
|
|
|
error("offline");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function action(container, action) {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
$.ajax({url: "action.php?container="+container+"&action="+action, success: function(res) {
|
|
|
|
success(res);
|
|
|
|
manage();
|
|
|
|
}}).fail(function() {
|
|
|
|
error("offline");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Manage Docker Services */
|
|
|
|
function manage() {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
$.ajax({url: "manage.php", success: function(res) {
|
|
|
|
status(res);
|
|
|
|
}}).fail(function() {
|
|
|
|
error("offline");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Show an Overview of all Docker Services */
|
|
|
|
function overview() {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
});
|
|
|
|
status("<h2>Exception Caught:</h2><p>"+e+"<p><pre>"+res.join("\n")+"</pre>");
|
|
|
|
}
|
|
|
|
}}).fail(function() {
|
|
|
|
error("offline");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function containers(c) {
|
|
|
|
console.log("->rcv containers");
|
|
|
|
dc.setContainers(c);
|
|
|
|
showviz(dc.graph());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initial Function: Startup
|
|
|
|
/** Decide whether to login or to create a new user */
|
|
|
|
function start() {
|
|
|
|
$("#imagetools").hide();
|
|
|
|
$("#menu").hide();
|
|
|
|
$("#username").html(window.location.hostname)
|
|
|
|
try {
|
|
|
|
status("Starting up ...");
|
|
|
|
emit("containers");
|
|
|
|
} catch (m) {
|
|
|
|
error(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
socket.io.on("connect", connected);
|
|
|
|
socket.io.on("reconnect", connected);
|
|
|
|
socket.io.on("disconnect", disconnected);
|
|
|
|
socket.io.on("error", disconnected);
|
|
|
|
socket.on("containers", containers);
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// On Load, Call @ref start
|
|
|
|
/*
|
|
|
|
$(window.onbeforeunload = function() {
|
|
|
|
return "Are you sure you want to navigate away?";
|
|
|
|
});
|
|
|
|
*/
|
|
|
|
$(init);
|