more of create

single-host
Marc Wäckerlin 8 years ago
parent 33a18f5182
commit e4f48c46d2
  1. 58
      nodejs/docker/docker.js
  2. 49
      nodejs/public/javascripts/servicedock.js
  3. 40
      nodejs/public/stylesheets/servicedock.css
  4. 54
      nodejs/views/index.ejs

@ -73,12 +73,13 @@ var Docker = function() {
var _containers = this; var _containers = this;
var Status = Object.freeze({ this.Status = Object.freeze({
Error: {color: "red", action1: "start", action2: "remove", bash: false}, Error: {color: "red", action1: "start", action2: "remove", bash: false},
Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false}, Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false},
Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false}, Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false},
Paused: {color: "lightgrey", action1: "unpause", action2: null, bash: false}, Paused: {color: "grey", action1: "unpause", action2: null, bash: false},
Running: {color: "lightgreen", action1: "pause", action2: "stop", bash: true} Running: {color: "lightgreen", action1: "pause", action2: "stop", bash: true},
Preview: {color: "lightgrey"}
}); });
var containers = []; var containers = [];
var nodes = []; var nodes = [];
@ -93,7 +94,7 @@ var Docker = function() {
return false; return false;
} }
function getIps(n, ips) { function getIps(n, ips) {
n.ports.forEach(function(p) { if (n.ports) n.ports.forEach(function(p) {
if (!ips[p.ip]) ips[p.ip] = []; if (!ips[p.ip]) ips[p.ip] = [];
ips[p.ip].push(p); ips[p.ip].push(p);
}); });
@ -119,32 +120,33 @@ var Docker = function() {
res+="}\n"; res+="}\n";
return res; return res;
} }
function graphNode(n) { function graphNode(n, omitstats) {
var res = ""; var res = "";
var label = n.image.name+'\\n'+n.name+'\\ncpu: ????? mem: ?????'; var label = n.image.name+'\\n'+n.name
+(omitstats?'':'\\ncpu: ????? mem: ?????');
res += '"'+n.name+'"' res += '"'+n.name+'"'
+' [label="'+label +' [label="'+label
+'",URL="#'+n.name +'",URL="#'+n.name
+'",style=filled,fillcolor='+n.status.color+"];\n"; +'",style=filled,fillcolor='+n.status.color+"];\n";
n.ports.forEach(function(p) { if (n.ports) n.ports.forEach(function(p) {
res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name
+'" [label="'+p.internal+'"];\n'; +'" [label="'+p.internal+'"];\n';
}); });
n.links.forEach(function(l) { if (n.links) n.links.forEach(function(l) {
res += '"'+n.name+'" -> "'+l.container+'" [label="link: '+l.name+'"];\n' res += '"'+n.name+'" -> "'+l.container+'" [label="link: '+l.name+'"];\n'
}); });
return res; return res;
} }
function graphVolumesInside(n) { function graphVolumesInside(n) {
var res = ""; var res = "";
n.volumes.forEach(function(v) { if (n.volumes) n.volumes.forEach(function(v) {
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n'; res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
}); });
return res; return res;
} }
function graphVolumesOutside(n) { function graphVolumesOutside(n) {
var res = ""; var res = "";
n.volumes.forEach(function(v) { if (n.volumes) n.volumes.forEach(function(v) {
if (v.host) if (v.host)
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n'; res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
}); });
@ -152,23 +154,23 @@ var Docker = function() {
} }
function graphVolumesConnections(n) { function graphVolumesConnections(n) {
var res = ""; var res = "";
n.volumes.forEach(function(v) { if (n.volumes) n.volumes.forEach(function(v) {
if (v.host) if (v.host)
res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n'; res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n';
res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n'; res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n';
}); });
n.volumesfrom.forEach(function(o) { if (n.volumesfrom) n.volumesfrom.forEach(function(o) {
res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n'; res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n';
}); });
return res; return res;
} }
this.graph = function(n) { this.graph = function(n, omitstats) {
var res = ""; var res = "";
var ips = []; var ips = [];
n = n || nodes; n = n || nodes;
for (name in n) getIps(n[name], ips); for (name in n) getIps(n[name], ips);
res += graphIpClusters(ips); res += graphIpClusters(ips);
for (name in n) res += graphNode(n[name]); for (name in n) res += graphNode(n[name], omitstats);
res += "{rank=same;\n"; res += "{rank=same;\n";
for (name in n) res += graphVolumesInside(n[name]); for (name in n) res += graphVolumesInside(n[name]);
res+="}\n"; res+="}\n";
@ -179,28 +181,28 @@ var Docker = function() {
return res; return res;
} }
function addNodes(ns, name) { function addNodes(ns, name) {
var n = nodes[name]; var n = nodes[name] || ns[name];
ns[name] = n; ns[name] = n;
n.links.forEach(function(peer) { if (n.links) n.links.forEach(function(peer) {
if (!ns[peer.container]) addNodes(ns, peer.container); if (!ns[peer.container]) addNodes(ns, peer.container);
}); });
n.usedby.forEach(function(peer) { if (n.usedby) n.usedby.forEach(function(peer) {
if (!ns[peer]) addNodes(ns, peer); if (!ns[peer]) addNodes(ns, peer);
}); });
n.volumesfrom.forEach(function(peer) { if (n.volumesfrom) n.volumesfrom.forEach(function(peer) {
if (!ns[peer]) addNodes(ns, peer); if (!ns[peer]) addNodes(ns, peer);
}); });
n.volumesto.forEach(function(peer) { if (n.volumesto) n.volumesto.forEach(function(peer) {
if (!ns[peer]) addNodes(ns, peer); if (!ns[peer]) addNodes(ns, peer);
}); });
} }
this.subnet = function(name) { this.subnet = function(name, nodes) {
var ns = {}; var ns = nodes || {};
addNodes(ns, name); addNodes(ns, name);
return ns; return ns;
} }
this.subgraph = function(name) { this.subgraph = function(name, nodes) {
return this.graph(this.subnet(name)); return this.graph(this.subnet(name, nodes), nodes);
} }
this.configuration = function(name) { this.configuration = function(name) {
var ns = this.subnet(name); var ns = this.subnet(name);
@ -303,11 +305,11 @@ var Docker = function() {
ip: ip ip: ip
}); });
} }
if (c.State.Paused) nodes[name].status = Status.Paused; if (c.State.Paused) nodes[name].status = _containers.Status.Paused;
else if (c.State.Running) nodes[name].status = Status.Running; else if (c.State.Running) nodes[name].status = _containers.Status.Running;
else if (c.State.Restarting) nodes[name].status = Status.Restarting; else if (c.State.Restarting) nodes[name].status = _containers.Status.Restarting;
else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated; else if (c.State.ExitCode == 0) nodes[name].status = _containers.Status.Terminated;
else nodes[name].status = Status.Error; else nodes[name].status = _containers.Status.Error;
nodes[name].volumes = []; nodes[name].volumes = [];
var volumes = c.Volumes || c.Config.Volumes; var volumes = c.Volumes || c.Config.Volumes;
nodes[name].volumes = []; nodes[name].volumes = [];

@ -146,11 +146,24 @@ function upload(evt) {
} }
} }
function previewCreate() {
var name = $('#name').val();
var nodes = {};
nodes[name] = {
status: docker.containers.Status.Preview,
id: null,
name: name,
image: {
name: $('#image').val(),
id: null
},
links: [],
volumesfrom: [],
};
$('#preview').html(Viz("digraph {\n"+" rankdir="+rankdir+";\n"+docker.containers.subgraph(name, nodes)+"\n}"));
}
function create() { function create() {
$("#create form fieldset input.add").click(function() {
$(this).siblings("select").append('<option>'+$(this).siblings("input").map(function() {return $(this).text()}).get().join(':'))+'</option>')
});
//$("#preview").html(Viz("digraph {\nrankdir="+rankdir+";\n"+docker.containers.graph()+"\n}"));
} }
var zoomlevel = 0; var zoomlevel = 0;
@ -293,10 +306,10 @@ function showCreate() {
$("#main").hide(); $("#main").hide();
$("#logs").hide(); $("#logs").hide();
$("#console").hide(); $("#console").hide();
$("#imagetools").hide(); $("#imagetools").show();
$("#close").show(); $("#close").show();
$("#create").show(); $("#create").show();
create(); previewCreate();
} }
function showConsole() { function showConsole() {
@ -468,6 +481,29 @@ function start() {
} }
} }
function initForms() {
$('#create form *').change(previewCreate);
$("#create form fieldset .add").click(function() {
$(this).siblings("select.collect")
.append('<option '+$(this).siblings("input")
.map(function() {
if (this.hasAttribute('data-name')) {
return this.getAttribute('data-name')+'="'+this.value.replace(/"/g, '&quot;')+'"';
} else return '';
}).get().join(' ')+'>'+$(this).siblings("input")
.map(function() {
var val = this.value;
var sep = this.getAttribute('data-separator') || '';
if (this.type=='checkbox' && !this.checked) {
val=''; sep=''
}
return sep+val;
}).get().join('')+'</option>')
$(this).siblings("input").value();
});
//$("#preview").html(Viz("digraph {\nrankdir="+rankdir+";\n"+docker.containers.graph()+"\n}"));
}
function init() { function init() {
socket.io socket.io
.on("connect", connected) .on("connect", connected)
@ -481,6 +517,7 @@ function init() {
.on("stats", stats) .on("stats", stats)
.on("logs", logs) .on("logs", logs)
.on("bash-data", bash_data); .on("bash-data", bash_data);
initForms();
start(); start();
} }

@ -21,15 +21,19 @@ div {
overflow: auto; overflow: auto;
} }
svg { #main svg, #preview svg {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
width: auto; width: auto;
height: auto; height: auto;
z-index: -1; z-index: -1;
position: relative; position: relative;
display: block;
margin: auto;
}
#preview svg {
padding-top: 2em;
} }
ul li { ul li {
margin-left: 1em; margin-left: 1em;
} }
@ -55,10 +59,22 @@ fieldset {
border: 1px solid black; border: 1px solid black;
} }
select {
width: 100%;
}
.listbutton { .listbutton {
width: 100%; width: 100%;
} }
input:invalid {
background-color: #FDD;
}
form:invalid input[type=submit] {
opacity: .5;
pointer-events: none;
}
table { table {
width: 100%; width: 100%;
} }
@ -132,14 +148,17 @@ table.docker li+li {
display: inline; display: inline;
} }
#status { #statusbar {
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
margin: 0; margin: 0;
padding: 0 1em 0 1em; padding: 0 1em 0 1em;
text-align: right; color: white;
}
#status: {
float: right;
} }
@media (max-width: 45em) { @media (max-width: 45em) {
#username { #username {
@ -159,17 +178,24 @@ table.docker li+li {
color: black; color: black;
} }
#menuicon, #imagetools img, #close { .btn {
cursor: pointer; cursor: pointer;
height: 1em; height: 1em;
width: auto; width: auto;
border: 1px dotted blue;
opacity: 0.8;
}
.btn:hover {
border: 1px dotted white;
opacity: 2.0;
} }
#menu { #menu {
clear: both; clear: both;
position: relative; position: relative;
padding: 1em 0em 1em 0em; padding: .5em 0 .5em 0;
margin: 0 1em 0 1em; margin: 1em 0 0 0;
float: right; float: right;
background-color: lightblue; background-color: lightblue;
list-style-type: none; list-style-type: none;

@ -19,17 +19,12 @@
<div id="header" class="header"> <div id="header" class="header">
<h1>ServiceDock <%= packageversion %> - Docker as a Service</h1> <h1>ServiceDock <%= packageversion %> - Docker as a Service</h1>
<div id="togglemenu"> <div id="togglemenu">
<span id="username">[unknown]</span>
<span id="connectionstatus">
<span id="good" title="connected" style="display: none">&#x2714;</span>
<span id="bad" title="disconnected">&#x2718;</span>
</span>
<span id="imagetools" style="display: none"> <span id="imagetools" style="display: none">
<img onclick="zoom(1)" src="images/zoom.svg" /> <img class="btn" onclick="zoom(1)" src="images/zoom.svg" />
<img onclick="rotateviz()" src="images/rotate.svg" /> <img class="btn" onclick="rotateviz()" src="images/rotate.svg" />
</span> </span>
<span id="close" onclick="showImage()" style="display: none">×</span> <span class="btn" id="close" onclick="showImage()" style="display: none">×</span>
<img id="menuicon" onclick="togglemenu()" onmouseover="$('#menu').show();" src="images/menu.svg" /> <img class="btn" id="menuicon" onclick="togglemenu()" src="images/menu.svg" />
</div> </div>
</div> </div>
@ -54,52 +49,52 @@
<form> <form>
<fieldset> <fieldset>
<legend>Name</legend> <legend>Name</legend>
<input placeholder="name" type="text" id="name" size="20" /> <input placeholder="name" type="text" id="name" pattern="^[-_a-zA-Z0-9]*$" size="20" required />
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Open Ports</legend> <legend>Open Ports</legend>
<input placeholder="ip" type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$" id="portip" size="15" style="width: 13ex" />:<input placeholder="ext" type="number" id="portext" size="4" style="width: 6ex" />:<input placeholder="int" type="number" id="portint" size="3" style="width: 6ex" /><button id="portadd" type="button" class="add">+</button><button id="portremove" type="button">-</button><br/> <input placeholder="ip" type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$" id="portip" size="15" style="width: 13ex" data-name="ip" />:<input placeholder="ext" type="number" id="portext" size="4" style="width: 6ex" data-name="external" data-separator=":" />:<input placeholder="int" type="number" id="portint" size="3" style="width: 6ex" data-name="internal" data-separator=":" /><button id="portadd" type="button" class="add">+</button><button id="portremove" type="button">-</button><br/>
<select size="5" id="createports"> <select class="collect" size="5" id="createports">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Environment</legend> <legend>Environment</legend>
<input placeholder="name" type="text" id="varname" size="10" />=<input placeholder="value" type="text" id="varvalue" size="10" /><button id="varadd" type="button" class="add">+</button><button id="varremove" type="button">-</button><br/> <input placeholder="name" type="text" id="varname" size="10" />=<input placeholder="value" type="text" id="varvalue" size="10" data-separator="=" /><button id="varadd" type="button" class="add">+</button><button id="varremove" type="button">-</button><br/>
<select size="5" id="createvars"> <select class="collect" size="5" id="createvars">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Volumes</legend> <legend>Mount Volumes</legend>
<input placeholder="on host" type="text" id="volumeext" size="10" />:<input placeholder="inside container" type="text" id="volumeint" size="10" />:<input placeholder="ro" type="checkbox" id="volumero" /><label for="volumero" >ro</label><button id="volumeadd" type="button" class="add">+</button><button id="volumeremove" type="button">-</button><br/> <input placeholder="on host" type="text" id="volumeext" size="10" data-name="outside" />:<input placeholder="inside container" type="text" id="volumeint" size="10" data-name="inside" data-separator=":" />:<input placeholder="ro" value="ro" type="checkbox" id="volumero" data-name="rw" data-separator=":" /><label for="volumero" >ro</label><button id="volumeadd" type="button" class="add">+</button><button id="volumeremove" type="button">-</button><br/>
<select size="5" id="createvolumes"> <select class="collect" size="5" id="createvolumes">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Volumes From</legend> <legend>Volumes From</legend>
<input placeholder="container" type="text" id="volumesfrom" size="10" /><button id="volumesfromadd" type="button" class="add">+</button><button id="volumesfromremove" type="button">-</button><br/> <input placeholder="container" type="text" id="volumesfrom" size="10" /><button id="volumesfromadd" type="button" class="add">+</button><button id="volumesfromremove" type="button">-</button><br/>
<select size="5" id="createcolumesfroms"> <select class="collect" size="5" id="createcolumesfroms" >
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Links</legend> <legend>Links</legend>
<input placeholder="container" type="text" id="linkcontainer" size="10" />:<input placeholder="name" type="text" id="linkname" size="10" /><button id="linkadd" type="button" class="add">+</button><button id="linkremove" type="button">-</button><br/> <input placeholder="container" type="text" id="linkcontainer" size="10" data-name="container" />:<input placeholder="name" type="text" id="linkname" size="10" data-name="name" data-separator=":" /><button id="linkadd" type="button" class="add">+</button><button id="linkremove" type="button">-</button><br/>
<select size="5" id="createlinks"> <select class="collect" size="5" id="createlinks">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Entry Point</legend> <legend>Entry Point</legend>
<input placeholder="entrypoint" type="text" id="entrypoint" size="10" /><button id="entrypointadd" type="button" class="add">+</button><button id="entrypointremove" type="button">-</button><br/> <input placeholder="entrypoint" type="text" id="entrypoint" size="10" /><button id="entrypointadd" type="button" class="add">+</button><button id="entrypointremove" type="button">-</button><br/>
<select size="5" id="createentrypoints"> <select class="collect" size="5" id="createentrypoints">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Image</legend> <legend>Image</legend>
<input placeholder="image" type="text" id="image" size="20" required /> <input placeholder="image" type="text" id="image" size="20" pattern="^[-_:/a-zA-Z0-9]*$" required />
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Command</legend> <legend>Command</legend>
<input placeholder="command" type="text" id="command" size="10" /><button id="commandadd" type="button" class="add">+</button><button id="commandremove" type="button">-</button><br/> <input placeholder="command" type="text" id="command" size="10" /><button id="commandadd" type="button" class="add">+</button><button id="commandremove" type="button">-</button><br/>
<select size="5" id="createcommands"> <select class="collect" size="5" id="createcommands">
</select> </select>
</fieldset> </fieldset>
<fieldset> <fieldset>
@ -122,10 +117,15 @@
</form> --> </form> -->
</div> </div>
<div id="status"> <div id="statusbar">
<span id="status">
<noscript>JavaScript is required for the interface.</noscript> <noscript>JavaScript is required for the interface.</noscript>
</span>
<span id="username">[unknown]</span>
<span id="connectionstatus">
<span id="good" title="connected" style="display: none">&#x2714;</span>
<span id="bad" title="disconnected">&#x2718;</span>
</span>
</div> </div>
</body> </body>

Loading…
Cancel
Save