fixed login issue on chromium; moved docker into mwaeckerlin/docker.js on github

single-host
Marc Wäckerlin 8 years ago
parent c55d4eed6b
commit bfaf5591a5
  1. 2
      COPYING
  2. 7
      ChangeLog
  3. 2
      INSTALL
  4. 483
      nodejs/docker/docker.js
  5. 8
      nodejs/docker/index.js
  6. 2
      nodejs/makefile.am
  7. 3
      nodejs/package.json.in
  8. 11
      nodejs/public/javascripts/servicedock.js
  9. 4
      nodejs/servicedock.js
  10. 23
      nodejs/sockets/index.js
  11. 8
      nodejs/views/index.ejs

@ -1 +1 @@
/usr/share/automake-1.15/COPYING
/usr/share/automake-1.14/COPYING

@ -1,3 +1,10 @@
2016-11-22 22:22 marc
* ChangeLog, nodejs/etc/servicedock.json,
nodejs/public/javascripts/servicedock.js, nodejs/servicedock.js,
nodejs/sockets/index.js: stats: fixed issue with missing /sys/fs
in docker 1.9, using docker stats; login works
2016-11-22 15:56 marc
* nodejs/authentication, nodejs/package.json.in: authentication is

@ -1 +1 @@
/usr/share/automake-1.15/INSTALL
/usr/share/automake-1.14/INSTALL

@ -1,483 +0,0 @@
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<a.links.length; ++i) if (a.links[i].container == b.name) return 1; // a after b;
for (var i=0; i<b.links.length; ++i) if (b.links[i].container == a.name) return -1; // a before b;
if ((b.volumesfrom.length || b.links.length) && !(a.volumesfrom.length || a.links.length)) return 1; // a after b;
if ((a.volumesfrom.length || a.links.length) && !(b.volumesfrom.length || b.links.length)) return 1; // a after b;
return 0; // a and b do not depend on each other
});
return creates;
}
this.creation = function(configuration) {
var res = [];
if (typeof configuration === 'string')
configuration = JSON.parse(configuration);
if (!configuration || !configuration.length)
throw new Error("undefined configuration");
for (var key=0; key<configuration.length; ++key) {
var n = configuration[key];
var cmd = "docker create";
if (n.name) cmd+=" --name "+quote(n.name);
if (n.ports) n.ports.forEach(function(p) {
cmd += " --publish "+quote((p.ip?p.ip+":":"")+p.external+":"+p.internal)
});
if (n.env) n.env.forEach(function(e) {
cmd += ' --env '+quote(e);
});
if (n.volumes) n.volumes.forEach(function(v) {
cmd += ' --volume '+quote(v.outside+':'+v.inside);
});
if (n.volumesfrom) n.volumesfrom.forEach(function(v) {
cmd += ' --volumes-from '+quote(v);
})
if (n.links) n.links.forEach(function(l) {
cmd += ' --link '+quote(l.container+':'+l.name);
});
if (n.entrypoint && n.entrypoint.length) cmd += ' --entrypoint '+quote(n.entrypoint.join(" "));
if (!n.image) throw new Error("container: "+key+"; undefined image");
cmd += " "+quote(n.image);
if (n.cmd) n.cmd.forEach(function(c) {
cmd+= ' '+quote(c);
});
res.push(cmd);
}
return res;
}
this.names = function(more) {
if (more) return Object.keys(nodes).concat(Object.keys(more))
else return Object.keys(nodes);
}
function setup() {
delete nodes; nodes = [];
containers.forEach(function(c, i) {
var name = c.Name.replace(/^\//, "");
if (!nodes[name]) nodes[name] = {};
nodes[name].id = c.Id;
nodes[name].name = name;
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)
for (var port in ports)
if (ports[port])
for (var expose in ports[port]) {
var ip = ports[port][expose].HostIp;
if (!ip||ip==""||ip=="0.0.0.0"||ip==0) ip=window.location.hostname;
nodes[name].ports.push({
internal: port,
external: ports[port][expose].HostPort,
ip: ip
});
}
if (c.State.Paused) nodes[name].status = _containers.Status.Paused;
else if (c.State.Running) nodes[name].status = _containers.Status.Running;
else if (c.State.Restarting) nodes[name].status = _containers.Status.Restarting;
else if (c.State.ExitCode == 0) nodes[name].status = _containers.Status.Terminated;
else nodes[name].status = _containers.Status.Error;
nodes[name].volumes = [];
var volumes = c.Volumes || c.Config.Volumes;
nodes[name].volumes = [];
if (volumes)
for (var volume in volumes) {
var rw = "rw";
var outside = (typeof volumes[volume]=="string")?volumes[volume]:null;
if (c.Mounts) c.Mounts.forEach(function(mnt) {
if (mnt.Destination==volume) {
outside = mnt.Source;
rw = mnt.RW ? "rw" : "ro";
}
});
nodes[name].volumes.push({
id: volume+':'+(outside?outside:name),
rw:rw,
inside: volume,
outside: outside,
host: outside && !outside.match(/^\/var\/lib\/docker/)
? outside : null
});
}
nodes[name].volumesfrom = [];
if (!nodes[name].volumesto) nodes[name].volumesto = [];
if (c.HostConfig.VolumesFrom) c.HostConfig.VolumesFrom.forEach(function(id) {
containers.forEach(function(c) {
if (c.Id == id || c.Name == "/"+id || c.Name == id) {
var src = c.Name.replace(/^\//, "");
nodes[name].volumesfrom.push(src);
if (!nodes[src]) nodes[src] = {};
if (!nodes[src].volumesto) nodes[src].volumesto = [];
nodes[src].volumesto.push(name);
}
});
});
nodes[name].links = [];
if (!nodes[name].usedby) nodes[name].usedby = [];
if (c.HostConfig && c.HostConfig.Links)
c.HostConfig.Links.forEach(function(l) {
var target = {
container: l.replace(/^\/?([^:]*).*$/, "$1"),
name: l.replace(new RegExp("^.*:/?"+name+"/"), "")
};
nodes[name].links.push(target);
if (!nodes[target.container]) nodes[target.container] = {};
if (!nodes[target.container].usedby) nodes[target.container].usedby = [];
nodes[target.container].usedby.push(name);
});
});
for (name in nodes) { // cleanup duplicate links to volumes when using volumes-from
var n = nodes[name];
n.volumesfrom.forEach(function(other) {
var o = nodes[other];
o.volumes.forEach(function(ovol) {
n.volumes.reduceRight(function(x, nvol, i, arr) {
if (nvol.id == ovol.id)
arr.splice(i, 1);
}, [])
})
})
}
}
this.contextmenu = function(selector) {
$('a[xlink\\:href^=#]').click(function(e) {
var 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);
$("#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('<button id="popup6">download</button>');
$("#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
}

@ -1,8 +0,0 @@
module.exports = function(app) {
if (app) app.get('/docker/docker.js', function(req, res) {
res.sendfile('docker.js', {root: __dirname});
});
return require(__dirname+'/docker.js');
}

@ -4,7 +4,7 @@
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
EXTRA_DIST = servicedock.js package.json.in public routes sockets docker views
EXTRA_DIST = servicedock.js package.json.in public routes sockets views
nodejsdir = ${pkgdatadir}/nodejs

@ -10,7 +10,8 @@
"pty.js": "~0.3.0",
"async": "~1.5.2",
"socketio-auth": "0.0.5",
"authentication.js": ">=0.0.2",
"authentication.js": ">= 0.0.2",
"docker.js": ">= 0.0.0",
"ldapauth": "git+https://github.com/DimensionSoftware/node-ldapauth.git"
},
"description": "@DESCRIPTION@",

@ -113,8 +113,8 @@ function connect() {
$("#connectionstatus #good").hide();
success("login to server");
socket.emit('authentication', {
username: $("#username").val(),
password: $("#password").val()
username: $("#usernamefield").val(),
password: $("#passwordfield").val()
});
}
@ -314,7 +314,7 @@ function showviz(vizpath, more) {
.attr('font-weight', 'bold')
.attr('font-size', '16')
.each(function() {$(this).attr('y', parseFloat($(this).attr('y'))+1.0)});
$('#main a > ellipse + text + text + text').attr('font-size', '12');
$('#main a > ellipse + text + text + text, #main a > ellipse + text + text + text + text').attr('font-size', '10');
} catch(e) {
(res = res.split("\n")).forEach(function(v, i, a) {
a[i] = ("000"+(i+1)).slice(-3)+": "+v;
@ -362,8 +362,11 @@ function stats(data) {
lines.forEach(function(line) {
if (!line) return;
elements = line.split(/ +/);
$('#main text + text:contains("'+elements[0]+'") + text')
$('#main text + text:contains("'+elements[0]+'") + text + text')
.html('cpu: '+elements[1]+' mem: '+elements[7]);
$('#main text + text:contains("'+elements[0]+'") + text')
.html('net: '+elements[8]+elements[9]+' '+elements[11]+elements[12]
+' block: '+elements[13]+elements[14]+' '+elements[16]+elements[17]);
});
}

@ -24,9 +24,9 @@ try {
var io = require('socket.io').listen(app);
var package = require(__dirname+'/package.json');
var config = require(package.path.config);
var docker = require(__dirname+'/docker')(app);
var docker = require('docker.js')(app);
var authentication = require('authentication.js')(config.restrict);
var sockets = require(__dirname+'/sockets')(io, authentication);
var sockets = require(__dirname+'/sockets')(app, io, docker, authentication);
// Configuration
process.argv.forEach(function(val, index) {

@ -1,8 +1,7 @@
module.exports = function(io, authentication) {
module.exports = function(app, io, docker, authentication) {
var pty = require('pty.js');
var proc = require('child_process');
var docker = require(__dirname+'/../docker')();
var module={};
var running="";
@ -186,11 +185,13 @@ module.exports = function(io, authentication) {
if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
return this.fail("illegal instance name");
if (bash_connections[name]) return;
bash_connections[name] =
pty.spawn("docker", ["exec", "-it", name, "bash", "-i"]);
bash_connections[name] = pty.spawn("docker", ["exec", "-it", name, "bash", "-i"]);
bash_connections[name].stdout.on('data', function(data) {
emit('bash-data', {name: name, type: 'stdout', text: data.toString()});
});
bash_connections[name].stderr.on('data', function(data) {
emit('bash-data', {name: name, type: 'stderr', text: data.toString()});
});
}
function bash_start(name) {
@ -241,11 +242,15 @@ module.exports = function(io, authentication) {
authenticate: function (socket, data, callback) {
console.log("=> authenticate: ", data.username);
//get credentials sent by the client
var username = data.username;
var password = data.password;
authentication(data.username, data.password,
function() {console.log("####LOGIN-SUCESS####");callback(null, true)},
function() {console.log("####LOGIN-FAIL####");callback(new Error("wrong credentials"))});
function() {
console.log("####LOGIN-SUCESS####");
callback(null, true)
},
function() {
console.log("####LOGIN-FAIL####");
callback(new Error("wrong credentials"))
});
},
postAuthenticate: connection,
timeout: "none"
@ -259,7 +264,7 @@ module.exports = function(io, authentication) {
// Regular Update of Stats
setInterval(function() {
exec('docker stats --no-stream'+running, stats);
if (running) exec('docker stats --no-stream'+running, stats);
}, 1000);
return module;

@ -48,10 +48,10 @@
<div id="login">
<h1>Login</h1>
<form onsubmit="connect()">
<label for="username">User Name</label>
<input id="username" type="text" />
<label for="password">Password</label>
<input id="password" type="password" />
<label for="usernamefield">User Name</label>
<input placeholder="User Name" id="usernamefield" type="text" />
<label for="passwordfield">Password</label>
<input placeholder="Password" id="passwordfield" type="password" />
<button type="button" onclick="connect()">Login</button>
</form>
</div>

Loading…
Cancel
Save