most docker client parts have been moved to docker.js; this version works

single-host
Marc Wäckerlin 8 years ago
parent bfaf5591a5
commit e1c8152f37
  1. 7
      nodejs/package.json.in
  2. 150
      nodejs/public/javascripts/servicedock.js
  3. 5
      nodejs/servicedock.js
  4. 252
      nodejs/sockets/index.js
  5. 6
      nodejs/views/index.ejs

@ -7,12 +7,9 @@
"stylus": "~0.53.0",
"ejs": ">= 0.0.1",
"socket.io": "~1.4.4",
"pty.js": "~0.3.0",
"async": "~1.5.2",
"socketio-auth": "0.0.5",
"authentication.js": ">= 0.0.2",
"docker.js": ">= 0.0.0",
"ldapauth": "git+https://github.com/DimensionSoftware/node-ldapauth.git"
"authentication.js": ">= 1.0.0",
"docker.js": ">= 0.2.4"
},
"description": "@DESCRIPTION@",
"main": "@PACKAGE_NAME@.js",

@ -9,9 +9,7 @@
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
var socket = null;
var focused = null;
var docker = new Docker();
var docker = null;
function htmlenc(html) {
return $('<div/>').text(html).html();
@ -97,7 +95,6 @@ function status(text, msg) {
zoom(0);
$("#main").show();
$("form input:first-child").focus();
docker.containers.contextmenu("#main");
}
function emit(signal, data) {
@ -206,7 +203,7 @@ function previewCreate() {
});
$('#dosend').unbind().click(function() {
if (Object.keys(tobecreated).length>0) {
emit("create", docker.containers.configuration(tobecreated));
emit("docker.container.create", docker.containers.configuration(tobecreated));
tobecreated = {};
showImage();
}
@ -285,110 +282,6 @@ function zoom(incr = 0) {
}
}
var viz = null;
var vizmore = null;
var rankdir = "LR";
function rotateviz() {
if (!viz) return;
if (rankdir == "LR")
rankdir = "TB";
else
rankdir = "LR";
showviz();
previewCreate();
}
function showviz(vizpath, more) {
if (!vizpath) {
vizpath = viz;
more = vizmore;
} else {
viz = vizpath;
vizmore = more;
}
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}";
try {
status(more?Viz(res)+more:Viz(res));
stats();
$('#main a > ellipse + text').attr('font-size', '12');
$('#main a > ellipse + text + text')
.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, #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;
});
status("<h2>Exception Caught:</h2><p>"+e+"<p><pre>"+res.join("\n")+"</pre>");
}
}
function details(name) {
if (name) focused = name;
else if (!focused) return overview();
showviz(docker.containers.subgraph(focused));
}
/// Convert number of bytes to readable text
function size(num) {
if (num>0.6*1024) {
if (num>0.6*1024*1024) {
if (num>0.6*1024*1024*1024) {
if (num>0.6*1024*1024*1024*1024) {
return Math.round(num/1024/1024/1024/1024)+"TB";
} else {
return Math.round(num/1024/1024/1024)+"GB";
}
} else {
return Math.round(num/1024/1024)+"MB";
}
} else {
return Math.round(num/1024)+"kB";
}
} else {
return num+"B";
}
}
var laststats=null;
function stats(data) {
if (data)
console.log("->rcv stats");
else
data=laststats;
if (!data) return;
var lines = data.split("\n");
var head = lines.shift();
lines.forEach(function(line) {
if (!line) return;
elements = line.split(/ +/);
$('#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]);
});
}
function images(i) {
console.log("->rcv images");
docker.images.set(i);
$('#imagedata').empty().append(docker.images.tags().map(function(i) {
var option = document.createElement('option');
option.value = i;
return option;
}));
}
function containers(c) {
console.log("->rcv containers");
docker.containers.set(c);
if (focused && docker.containers.exists(focused))
details(focused);
else
overview();
}
function showLogin() {
$("#close").hide();
$("#console").hide();
@ -443,6 +336,16 @@ function showLogs() {
$("#main").hide();
}
function images(i) {
console.log("->rcv images");
docker.images.set(i);
$('#imagedata').empty().append(docker.images.tags().map(function(i) {
var option = document.createElement('option');
option.value = i;
return option;
}));
}
function logs(data) {
console.log("->rcv logs("+data.name+")");
if (data.type=='done') {
@ -568,11 +471,6 @@ function bash_data(data) {
$("#screen").focus();
}
function overview() {
focused = null;
showviz(docker.containers.graph());
}
/// Initial Function: Startup
/** To be called after login */
function start() {
@ -582,8 +480,8 @@ function start() {
$("#menu").hide();
try {
status("Starting up ...");
emit("images");
emit("containers");
emit("docker.images");
emit("docker.containers");
showImage();
} catch (m) {
error(m);
@ -601,8 +499,8 @@ function initForms() {
var obj = this;
this.getAttribute('data-name').split(' ').forEach(function(n) {
res += 'data-'+n+'="'+
(obj.type!='checkbox'||obj.checked
?obj.value:obj.getAttribute('data-false'))
(obj.type!='checkbox'||obj.checked
?obj.value:obj.getAttribute('data-false'))
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
+'"';
@ -629,6 +527,10 @@ function initForms() {
function init() {
socket = io.connect();
docker = new Docker(socket, '#main', error);
$("#server").html($("#username").value+'@'+window.location.hostname)
initForms();
showLogin();
socket
.io
.on("connect", connect)
@ -638,15 +540,9 @@ function init() {
socket
.on("authenticated", authenticated)
.on("unauthorized", unauthorized)
.on("fail", error)
.on("containers", containers)
.on("images", images)
.on("stats", stats)
.on("logs", logs)
.on("bash-data", bash_data);
$("#server").html($("#username").value+'@'+window.location.hostname)
initForms();
showLogin();
.on("docker.images", images)
.on("docker.container.logs", logs)
.on("docker.container.bash.data", bash_data);
}
/// On Load, Call @ref start

@ -20,13 +20,12 @@ try {
var express = require('express')
, routes = require(__dirname+'/routes');
var app = module.exports = express.createServer();
var app = express.createServer();
var io = require('socket.io').listen(app);
var package = require(__dirname+'/package.json');
var config = require(package.path.config);
var docker = require('docker.js')(app);
var authentication = require('authentication.js')(config.restrict);
var sockets = require(__dirname+'/sockets')(app, io, docker, authentication);
var sockets = require(__dirname+'/sockets')(app, io, authentication);
// Configuration
process.argv.forEach(function(val, index) {

@ -1,247 +1,17 @@
module.exports = function(app, io, docker, authentication) {
var pty = require('pty.js');
var proc = require('child_process');
var module={};
var running="";
function broadcast(signal, data) {
console.log("<= signal: "+signal);
io.sockets.emit(signal, data);
}
function exec(cmd, callback) {
if (cmd.length>40) {
console.log("== "+cmd.slice(0, 30+cmd.slice(30).indexOf(' '))+" ...");
} else {
console.log("== "+cmd);
}
proc.exec(cmd, {maxBuffer: 10*1024*1024}, callback);
}
function fail(txt, data) {
console.log("** "+txt, data);
}
var oldcontainer = null;
function containerinspect(error, stdout, stderr) {
if (error || stderr)
return fail("inspect docker containers failed", {
error: error, stderr: stderr, stdout: stdout
});
running = "";
JSON.parse(stdout).forEach(function(n) {
if (n.State.Running) running+=" "+n.Name.replace(/^\//, '');
});
if (oldcontainer!=stdout) broadcast("containers", stdout);
oldcontainer = stdout;
}
module.exports = function(app, io, authentication) {
function containerlist(error, stdout, stderr) {
if (error || stderr)
return fail("list docker containers failed", {
error: error, stderr: stderr, stdout: stdout
});
var containers = stdout.trim().replace(/\n/g, " ");
exec("docker inspect "+containers, containerinspect);
}
function updatecontainers(error, stdout, stderr) {
if (error || stderr)
return fail("update docker container failed", {
error: error, stderr: stderr, stdout: stdout
});
exec("docker ps -aq --no-trunc ", containerlist);
}
var oldimage = null;
function imageinspect(error, stdout, stderr) {
if (error || stderr)
return fail("inspect docker images failed", {
error: error, stderr: stderr, stdout: stdout
});
if (oldimage && oldimage==stdout) return; // do not resend same images
oldimage = stdout;
broadcast("images", stdout);
}
function imagelist(error, stdout, stderr) {
if (error || stderr)
return fail("list docker images failed", {
error: error, stderr: stderr, stdout: stdout
});
exec("docker inspect "+stdout.trim().replace(/\n/g, " "),
imageinspect);
}
function updateimages(error, stdout, stderr) {
if (error || stderr)
return fail("update docker images failed", {
error: error, stderr: stderr, stdout: stdout
});
exec("docker images -q --no-trunc", imagelist);
}
var docker = require('docker.js')(app, io);
// New Authenticated Client
function connection(socket, userdata) {
console.log("=> new connection from "+userdata.username);
function emit(signal, data, info) {
if (typeof data == 'string' && !data.match("\n")) {
console.log("<- signal: "+signal+"("+data+")");
} else {
console.log("<- signal: "+signal);
}
if (info) console.log(info);
socket.emit(signal, data);
}
function fail(txt, data) {
console.log("** "+txt, data);
emit("fail", txt);
}
function modify(cmd, name) {
if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
return this.fail("illegal instance name");
exec("docker "+cmd+" "+name, updatecontainers);
}
function createContainer(cmds) {
if (cmds.length>0)
exec(cmds.shift(), function(error, stdout, stderr) {
if (error || stderr)
return this.fail("create container failed", {
error: error, stderr: stderr, stdout: stdout
});
createContainer(cmds);
})
else
updatecontainers();
}
function containers() {
console.log("-> containers");
if (oldcontainer) emit("containers", oldcontainer);
else updatecontainers();
}
function images() {
console.log("-> images");
if (oldimage) emit("images", oldimage);
else updateimages();
}
function start(name) {
console.log("-> start("+name+")");
modify("start", name);
}
function stop(name) {
console.log("-> stop("+name+")");
modify("stop", name);
}
function pause(name) {
console.log("-> pause("+name+")");
modify("pause", name);
}
function unpause(name) {
console.log("-> unpause("+name+")");
modify("unpause", name);
}
function remove(name) {
console.log("-> remove("+name+")");
modify("rm", name);
}
function create(data) {
console.log("-> create");
var d = new docker.Docker();
var dc = new d.Containers();
createContainer(dc.creation(data));
}
function logs(name) {
console.log("-> logs("+name+")");
var l = proc.spawn("docker", ["logs", "-f", name])
.on('close', function(code) {
emit('logs', {name: name, type: 'done'});
});
l.stdout.on('data', function(data) {
emit('logs', {name: name, type: 'stdout', text: data.toString()});
});
l.stderr.on('data', function(data) {
emit('logs', {name: name, type: 'stderr', text: data.toString()});
});
}
var bash_connections = {};
function new_bash(name) {
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].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) {
console.log("-> bash-start("+name+")");
new_bash(name);
}
function bash_input(data) {
console.log("-> bash-input("+data.name+", "+data.text+")");
new_bash(data.name);
bash_connections[data.name].stdin.resume();
bash_connections[data.name].stdin.write(data.text);
}
function bash_end(name, text) {
console.log("-> bash-end("+name+")");
if (!bash_connections[name]) return;
bash_connections[name].stdin.close();
delete bash_connections[name]; bash_connections[name] = null;
}
socket
.on("containers", containers)
.on("images", images)
.on("start", start)
.on("stop", stop)
.on("pause", pause)
.on("unpause", unpause)
.on("remove", remove)
.on("create", create)
.on('logs', logs)
.on('bash-start', bash_start)
.on('bash-input', bash_input)
.on('bash-end', bash_end);
docker.connect(socket);
}
function stats(error, stdout, stderr) {
if (error || stderr)
return fail("get containers stats failed", {
error: error, stderr: stderr, stdout: stdout
});
broadcast("stats", stdout);
}
// Handle Connection
// Handle Connection Authentication
require('socketio-auth')(io, {
authenticate: function (socket, data, callback) {
console.log("=> authenticate: ", data.username);
//get credentials sent by the client
authentication(data.username, data.password,
function() {
console.log("####LOGIN-SUCESS####");
@ -256,16 +26,6 @@ module.exports = function(app, io, docker, authentication) {
timeout: "none"
});
// Regular Update of Images and Containers
setInterval(function() {
updateimages();
updatecontainers();
}, 10000);
// Regular Update of Stats
setInterval(function() {
if (running) exec('docker stats --no-stream'+running, stats);
}, 1000);
var module = {};
return module;
}

@ -7,9 +7,9 @@
<link href="stylesheets/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="javascripts/jquery.js"></script>
<script type="text/javascript" src="javascripts/jquery-ui.js"></script>
<script type="text/javascript" src="javascripts/viz.js"></script>
<script type="text/javascript" src="/viz.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="/docker/docker.js"></script>
<script type="text/javascript" src="/docker.js"></script>
<script type="text/javascript" src="javascripts/servicedock.js"></script>
<title>ServiceDock: Docker as a Service</title>
</head>
@ -21,7 +21,7 @@
<div id="togglemenu">
<span id="imagetools" style="display: none">
<img class="btn" onclick="zoom(1)" src="images/zoom.svg" />
<img class="btn" onclick="rotateviz()" src="images/rotate.svg" />
<img class="btn" onclick="docker.rotate()" src="images/rotate.svg" />
</span>
<span class="btn" id="close" onclick="showImage()" style="display: none">×</span>
<img class="btn" id="menuicon" onclick="togglemenu()" src="images/menu.svg" />

Loading…
Cancel
Save