var Docker = function() { function same(array1, array2) { if (!array1 && !array2) return true; if (!array1 || !array2) return false; return (array1.length == array2.length) && array1.every(function(element, index) { return element === array2[index]; }); } function quote(text) { if (text.match(/[^-_:=\/a-zA-Z0-9]/)) { if (text.match('"')) { if (!text.match("'")) return "'"+text+"'"; else return '"'+text.replace(/"/g, '\\"')+'"'; } else { return '"'+text+'"'; } } else { return text; } } 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 = []; if (c.Config.ExposedPorts) for (p in c.Config.ExposedPorts) nodes[c.Id].ports.push(p); 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.tags = function() { var res = []; for (n in nodes) if (nodes[n].tags) res = res.concat(nodes[n].tags); return res; } this.get = function(tag) { for (n in nodes) if (nodes[n].tags && nodes[n].tags.indexOf(tag)>-1) return nodes[n]; return null; } 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 if (same(nodes[id].entrypoint, instance.entrypoint)) instance.entrypoint = null } 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; this.Status = Object.freeze({ Error: {color: "indianred1", action1: "start", action2: "remove", bash: false}, Terminated: {color: "yellow2", action1: "start", action2: "remove", bash: false}, Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false}, Paused: {color: "grey", action1: "unpause", action2: null, bash: false}, Running: {color: "lightgreen", action1: "pause", action2: "stop", bash: true}, Preview: {color: "orangered"}, Prepared: {color: "lightgrey"} }); 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) { if (n.ports) n.ports.forEach(function(p) { if (!p.ip||p.ip==""||p.ip=="0.0.0.0"||p.ip==0) p.ip=window.location.hostname; 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"; } res += "{rank=same;\n"; for (ip in ips) { ips[ip].forEach(function(p) { res += '"'+p.ip+":"+p.external+'";\n'; }); } res+="}\n"; return res; } function graphNode(n, omitstats) { var res = ""; var label = (n.image?n.image.name:'UNDEFINED')+'\\n' +(n.name?n.name:"UNKNOWN") +(omitstats?'':'\\ncpu: ????? mem: ?????'); res += '"'+n.name+'"' +' [label="'+label +'",URL="#'+n.name +'",fillcolor='+(n.status?n.status.color+',style=filled':'red,shape=octagon,style=filled')+"];\n"; if (n.ports) n.ports.forEach(function(p) { res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name +'" [label="'+p.internal+'"];\n'; }); if (n.links) n.links.forEach(function(l) { res += '"'+n.name+'" -> "'+l.container+'" [label="link: '+l.name+'"];\n' }); return res; } function graphVolumesInside(n) { var res = ""; if (n.volumes) n.volumes.forEach(function(v) { res += '"'+v.id+v.inside+'" [label="'+v.inside+'",shape=box];\n'; }); return res; } function graphVolumesOutside(n) { var res = ""; if (n.volumes) n.volumes.forEach(function(v) { if (v.host) res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n'; }); return res; } function graphVolumesConnections(n, nodes) { var res = ""; if (n.volumes) n.volumes.forEach(function(v) { if (v.host) res += '"'+v.id+v.inside+'" -> "'+v.outside+'" [label="mounted from"]\n'; res += '"'+n.name+'" -> "'+v.id+v.inside+'" [label="volume/'+v.rw+'"]\n'; }); if (n.volumesfrom) n.volumesfrom.forEach(function(o) { res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n'; }); return res; } this.graph = function(n, omitstats) { 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], omitstats); 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"; for (name in n) res += graphVolumesConnections(n[name], n); return res; } function addNodes(ns, name) { var n = nodes[name] || ns[name] || {name: name}; ns[name] = n; if (n.links) n.links.forEach(function(peer) { if (!ns[peer.container]) addNodes(ns, peer.container); }); if (n.usedby) n.usedby.forEach(function(peer) { if (!ns[peer]) addNodes(ns, peer); }); if (n.volumesfrom) n.volumesfrom.forEach(function(peer) { if (!ns[peer]) addNodes(ns, peer); }); if (n.volumesto) n.volumesto.forEach(function(peer) { if (!ns[peer]) addNodes(ns, peer); }); } this.subnet = function(name, nodes) { var ns = nodes || {}; addNodes(ns, name); return ns; } this.subgraph = function(name, nodes) { return this.graph(this.subnet(name, nodes), nodes); } this.configuration = function(name) { var ns = name; if (typeof name == 'string') 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: [] }; if (ns[n].ports) ns[n].ports.forEach(function(p) { if (p.ip && !p.ip.match(/^([0-9]{1,3}\.){3}[0-9]{1,3}$/)) p.ip = null; }); 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.name)>=0) return 1; // a after b if (b.volumesfrom.indexOf(a.name)>=0) return -1; // a before b for (var i=0; i') $("#popup").empty(); if (n.status.action1) { $("#popup").append(''); $("#popup1").click(function() { emit(n.status.action1, name); }); } $("#popup").append(''); $("#popup2").click(function() { if (focused) overview(); else details(name); }); if (n.status.action2) { $("#popup").append(''); $("#popup3").click(function() { emit(n.status.action2, name); }); } $("#popup").append('
'); $("#popup").append(''); $("#popup4").click(function() { showLogs(); emit("logs", name); }); if (n.status.bash) { $("#popup").append(''); $("#popup5").click(function() { showConsole(); emit("bash-start", name); $("#screen").focus(); $("#screen").keypress(function(e) { console.log("keypress", e); if (e.keyCode) emit("bash-input", {name: name, text: String.fromCharCode(e.keyCode)}); else if (e.charCode) emit("bash-input", {name: name, text: String.fromCharCode(e.charCode)}); $("#screen").focus(); }); // $("#bash").submit(function() { // emit("bash-input", {name: name, text: $("#command").val()+"\n"}); // $("#command").val(""); // }) }); } $("#popup").append(''); $("#popup6").click(function() { var download = document.createElement('a'); download.href = 'data:application/json,' + encodeURI(JSON.stringify(_containers.configuration(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; setup(); } } this.images = new this.Images(); this.containers = new this.Containers(); } if (typeof module === 'undefined') module = {}; module.exports = { Docker: Docker }