/*! @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("

"+a.description+"

"+ "

"+a.project+"-"+a.version+"

"+ "

"+a.docker+"

"+ "

README

"+ "
"+a.readme+"
"); } catch(e) { status("
"+res+"
"); 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("

Exception Caught:

"+e+"

"+res.join("\n")+"
"); } } function details(c) { $("#imagetools").hide(); $.ajax({url: "details.php?container="+c, success: function(res) { try { status(res); } catch(e) { status("
"+res+"
"); 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("

Exception Caught:

"+e+"

"+res.join("\n")+"
"); } }}).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);