first part of create neew nodes
This commit is contained in:
		@@ -11,449 +11,6 @@
 | 
			
		||||
var socket = io.connect();
 | 
			
		||||
var focused = null;
 | 
			
		||||
 | 
			
		||||
function Docker() {
 | 
			
		||||
 | 
			
		||||
    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('"')) {
 | 
			
		||||
            if (!text.match("'")) return "'"+text+"'";
 | 
			
		||||
            else return '"'+text.replace(/"/g, '\\"')+'"';
 | 
			
		||||
        } else {
 | 
			
		||||
            if (text.match(' ')) 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 = c.Config.ExposedPorts;
 | 
			
		||||
                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.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;
 | 
			
		||||
        
 | 
			
		||||
        var Status = Object.freeze({
 | 
			
		||||
            Error:      {color: "red", action1: "start", action2: "remove", bash: false},
 | 
			
		||||
            Terminated: {color: "yellow", action1: "start", action2: "remove", bash: false},
 | 
			
		||||
            Restarting: {color: "lightblue", action1: "start", action2: "remove", bash: false},
 | 
			
		||||
            Paused:     {color: "lightgrey", action1: "unpause", action2: null, bash: false},
 | 
			
		||||
            Running:    {color: "lightgreen", action1: "pause", action2: "stop", bash: true}
 | 
			
		||||
        });
 | 
			
		||||
        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) {
 | 
			
		||||
            n.ports.forEach(function(p) {
 | 
			
		||||
                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) {
 | 
			
		||||
            var res = "";
 | 
			
		||||
            var label = n.image.name+'\\n'+n.name+'\\ncpu: ????? mem: ?????';
 | 
			
		||||
            res += '"'+n.name+'"'
 | 
			
		||||
                +' [label="'+label
 | 
			
		||||
                +'",URL="#'+n.name
 | 
			
		||||
                +'",style=filled,fillcolor='+n.status.color+"];\n";
 | 
			
		||||
            n.ports.forEach(function(p) {
 | 
			
		||||
                res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name
 | 
			
		||||
                    +'" [label="'+p.internal+'"];\n';
 | 
			
		||||
            });
 | 
			
		||||
            n.links.forEach(function(l) {
 | 
			
		||||
                res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n'
 | 
			
		||||
            });
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        function graphVolumesInside(n) {
 | 
			
		||||
            var res = "";
 | 
			
		||||
            n.volumes.forEach(function(v) {
 | 
			
		||||
                res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
 | 
			
		||||
            });
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        function graphVolumesOutside(n) {
 | 
			
		||||
            var res = "";
 | 
			
		||||
            n.volumes.forEach(function(v) {
 | 
			
		||||
                if (v.host)
 | 
			
		||||
                    res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
 | 
			
		||||
            });
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        function graphVolumesConnections(n) {
 | 
			
		||||
            var res = "";
 | 
			
		||||
            n.volumes.forEach(function(v) {
 | 
			
		||||
                if (v.host)
 | 
			
		||||
                    res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n';
 | 
			
		||||
                res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n';
 | 
			
		||||
            });
 | 
			
		||||
            n.volumesfrom.forEach(function(o) {
 | 
			
		||||
                res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n';
 | 
			
		||||
            });
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        this.graph = function(n) {
 | 
			
		||||
            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]);
 | 
			
		||||
            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]);
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        function addNodes(ns, name) {
 | 
			
		||||
            var n = nodes[name];
 | 
			
		||||
            ns[name] = n;
 | 
			
		||||
            n.links.forEach(function(peer) {
 | 
			
		||||
                if (!ns[peer.to]) addNodes(ns, peer.to);
 | 
			
		||||
            });
 | 
			
		||||
            n.usedby.forEach(function(peer) {
 | 
			
		||||
                if (!ns[peer]) addNodes(ns, peer);
 | 
			
		||||
            });
 | 
			
		||||
            n.volumesfrom.forEach(function(peer) {
 | 
			
		||||
                if (!ns[peer]) addNodes(ns, peer);
 | 
			
		||||
            });
 | 
			
		||||
            n.volumesto.forEach(function(peer) {
 | 
			
		||||
                if (!ns[peer]) addNodes(ns, peer);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        this.subnet = function(name) {
 | 
			
		||||
            var ns = {};
 | 
			
		||||
            addNodes(ns, name);
 | 
			
		||||
            return ns;
 | 
			
		||||
        }
 | 
			
		||||
        this.subgraph = function(name) {
 | 
			
		||||
            return this.graph(this.subnet(name));
 | 
			
		||||
        }
 | 
			
		||||
        this.configuration = function(name) {
 | 
			
		||||
            var 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: []
 | 
			
		||||
                };
 | 
			
		||||
                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].to == b.name) return 1; // a after b;
 | 
			
		||||
                for (var i=0; i<b.links.length; ++i) if (b.links[i].to == 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(name) {
 | 
			
		||||
            var res = [];
 | 
			
		||||
            this.configuration(name).forEach(function(n) {
 | 
			
		||||
                var cmd = "docker create -d --name "+quote(n.name);
 | 
			
		||||
                n.ports.forEach(function(p) {
 | 
			
		||||
                    cmd += " --publish "+(p.ip?quote(p.ip)+":":"")+p.external+":"+p.internal;
 | 
			
		||||
                });
 | 
			
		||||
                n.env.forEach(function(e) {
 | 
			
		||||
                    cmd += ' --env '+quote(e);
 | 
			
		||||
                });
 | 
			
		||||
                n.volumes.forEach(function(v) {
 | 
			
		||||
                    cmd += ' --volume '+quote(v.outside)+':'+quote(v.inside);
 | 
			
		||||
                });
 | 
			
		||||
                n.volumesfrom.forEach(function(v) {
 | 
			
		||||
                    cmd += ' --volumes-from '+quote(v);
 | 
			
		||||
                })
 | 
			
		||||
                n.links.forEach(function(l) {
 | 
			
		||||
                    cmd += ' --link '+quote(l.link)+':'+quote(l.to);
 | 
			
		||||
                });
 | 
			
		||||
                if (n.entrypoint && n.entrypoint.length) cmd += ' --entrypoint '+quote(n.entrypoint.join(" "));
 | 
			
		||||
                cmd += " "+n.image;
 | 
			
		||||
                if (n.cmd) n.cmd.forEach(function(c) {
 | 
			
		||||
                    cmd+= ' '+quote(c);
 | 
			
		||||
                });
 | 
			
		||||
                res.push(cmd);
 | 
			
		||||
            })
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        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 = Status.Paused;
 | 
			
		||||
                else if (c.State.Running) nodes[name].status = Status.Running;
 | 
			
		||||
                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 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 = {
 | 
			
		||||
                            to:   l.replace(/^\/?([^:]*).*$/, "$1"),
 | 
			
		||||
                            link: l.replace(new RegExp("^.*:/?"+name+"/"), "")
 | 
			
		||||
                        };
 | 
			
		||||
                        nodes[name].links.push(target);
 | 
			
		||||
                        if (!nodes[target.to]) nodes[target.to] = {};
 | 
			
		||||
                        if (!nodes[target.to].usedby) nodes[target.to].usedby = [];
 | 
			
		||||
                        nodes[target.to].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();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var docker = new Docker();
 | 
			
		||||
 | 
			
		||||
function htmlenc(html) {
 | 
			
		||||
@@ -572,6 +129,30 @@ function togglemenu() {
 | 
			
		||||
    $("#menu").toggle();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Upload a configuration and send it to server
 | 
			
		||||
function upload(evt) {
 | 
			
		||||
    if (!window.FileReader)
 | 
			
		||||
        return error("your browser does not support file upload", true);
 | 
			
		||||
    for (var i=0, f; f=evt.target.files[i]; ++i) {
 | 
			
		||||
        var file = f;
 | 
			
		||||
        var reader = new FileReader();
 | 
			
		||||
        reader.onload = function(evt) {
 | 
			
		||||
            if (evt.target.error) return error("error reading file", true);
 | 
			
		||||
            if (evt.target.readyState==0) return notice("waiting for data …");
 | 
			
		||||
            if (evt.target.readyState==1) return notice("loading data …");
 | 
			
		||||
            emit("create", evt.target.result);
 | 
			
		||||
        }
 | 
			
		||||
        reader.readAsText(file);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
function zoom(incr = 0) {
 | 
			
		||||
    zoomlevel = (zoomlevel+incr)%2;
 | 
			
		||||
@@ -619,12 +200,12 @@ function showviz(vizpath, more) {
 | 
			
		||||
    res = "digraph {\n"+"  rankdir="+rankdir+";\n"+viz+"\n}";
 | 
			
		||||
    try {
 | 
			
		||||
        status(more?Viz(res)+more:Viz(res));
 | 
			
		||||
        $('a > ellipse + text').attr('font-size', '12');
 | 
			
		||||
        $('a > ellipse + text + text')
 | 
			
		||||
        $('#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)});
 | 
			
		||||
        $('a > ellipse + text + text + text').attr('font-size', '12');
 | 
			
		||||
        $('#main a > ellipse + text + text + text').attr('font-size', '12');
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        (res = res.split("\n")).forEach(function(v, i, a) {
 | 
			
		||||
            a[i] = ("000"+(i+1)).slice(-3)+": "+v;
 | 
			
		||||
@@ -672,7 +253,7 @@ function stats(data) {
 | 
			
		||||
        var s = data[name];
 | 
			
		||||
        var o = oldstats[name];
 | 
			
		||||
        if (!o|| !s) continue;
 | 
			
		||||
        $('text + text:contains("'+name+'") + text')
 | 
			
		||||
        $('#main text + text:contains("'+name+'") + text')
 | 
			
		||||
            .html('cpu: '
 | 
			
		||||
                  +(Math.round((s.cpuacct.usage.data-o.cpuacct.usage.data)
 | 
			
		||||
                               /(s.cpuacct.usage.date-o.cpuacct.usage.date)
 | 
			
		||||
@@ -700,6 +281,7 @@ function containers(c) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function showImage() {
 | 
			
		||||
    $("#create").hide();
 | 
			
		||||
    $("#logs").hide();
 | 
			
		||||
    $("#console").hide();
 | 
			
		||||
    $("#close").hide();
 | 
			
		||||
@@ -707,8 +289,19 @@ function showImage() {
 | 
			
		||||
    $("#main").show();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function showCreate() {
 | 
			
		||||
    $("#main").hide();
 | 
			
		||||
    $("#logs").hide();
 | 
			
		||||
    $("#console").hide();
 | 
			
		||||
    $("#imagetools").hide();
 | 
			
		||||
    $("#close").show();
 | 
			
		||||
    $("#create").show();
 | 
			
		||||
    create();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function showConsole() {
 | 
			
		||||
    $("#main").hide();
 | 
			
		||||
    $("#create").hide();
 | 
			
		||||
    $("#logs").hide();
 | 
			
		||||
    $("#imagetools").hide();
 | 
			
		||||
    $("#console").show();
 | 
			
		||||
@@ -720,6 +313,7 @@ function showConsole() {
 | 
			
		||||
 | 
			
		||||
function showLogs() {
 | 
			
		||||
    $("#main").hide();
 | 
			
		||||
    $("#create").hide();
 | 
			
		||||
    $("#console").hide();
 | 
			
		||||
    $("#imagetools").hide();
 | 
			
		||||
    $("#close").show();
 | 
			
		||||
 
 | 
			
		||||
@@ -30,69 +30,33 @@ svg {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form {
 | 
			
		||||
  padding: .5em 0 1em 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ul li {
 | 
			
		||||
  margin-left: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 45em) {
 | 
			
		||||
  form {
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form > * {
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
}
 | 
			
		||||
form input[type="submit"] {
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
}
 | 
			
		||||
form input#msg {
 | 
			
		||||
  flex-grow: 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.buttongroup {
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
form {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  justify-content: flex-start;
 | 
			
		||||
  align-items: stretch;
 | 
			
		||||
  align-content: flex-start;
 | 
			
		||||
}
 | 
			
		||||
.buttongroup .toolbutton {
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
  flex-basis: 0;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
 | 
			
		||||
form fieldset {
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  flex-basis: auto; /* or: content */
 | 
			
		||||
}
 | 
			
		||||
.toolbutton label img {
 | 
			
		||||
  height: 1.5em;
 | 
			
		||||
}
 | 
			
		||||
.toolbutton input {
 | 
			
		||||
  display:none;
 | 
			
		||||
}
 | 
			
		||||
.toolbutton.bad:first-line,
 | 
			
		||||
.toolbutton.good:first-line {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
.toolbutton {
 | 
			
		||||
 | 
			
		||||
fieldset {
 | 
			
		||||
  margin: .5ex;
 | 
			
		||||
  padding: .5ex;
 | 
			
		||||
  border: 1px solid black;
 | 
			
		||||
  background-color: #777;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  margin: 1px;
 | 
			
		||||
  padding: 1px;
 | 
			
		||||
}
 | 
			
		||||
.toolbutton.bad {
 | 
			
		||||
  background-color: #f77;
 | 
			
		||||
}
 | 
			
		||||
.toolbutton.good {
 | 
			
		||||
  background-color: #7f7;
 | 
			
		||||
 | 
			
		||||
.listbutton {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table {
 | 
			
		||||
@@ -231,8 +195,14 @@ table.docker li+li {
 | 
			
		||||
  margin-top: 0.5em;
 | 
			
		||||
  padding-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
#menu li input {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
#menu li:hover {
 | 
			
		||||
  background-color: #777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#main, #logs, #console {
 | 
			
		||||
#main, #logs, #console, #create {
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 1.5em;
 | 
			
		||||
  left: 0;
 | 
			
		||||
@@ -244,7 +214,7 @@ table.docker li+li {
 | 
			
		||||
  bottom: 1.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#main {
 | 
			
		||||
#main, #create {
 | 
			
		||||
  background-color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ var io  = require('socket.io').listen(app);
 | 
			
		||||
var sockets = require(__dirname+'/sockets')(io);
 | 
			
		||||
var package = require(__dirname+'/package.json');
 | 
			
		||||
var config = require(package.path.config);
 | 
			
		||||
var docker = require(__dirname+'/docker')(app);
 | 
			
		||||
 | 
			
		||||
// Configuration
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ module.exports = function(io) {
 | 
			
		||||
 | 
			
		||||
    var pty = require('pty.js');
 | 
			
		||||
    var proc = require('child_process');
 | 
			
		||||
    var docker = require(__dirname+'/../docker')();
 | 
			
		||||
 | 
			
		||||
    var module={};
 | 
			
		||||
    var idtoname = {};
 | 
			
		||||
@@ -22,8 +23,9 @@ module.exports = function(io) {
 | 
			
		||||
            return fail("inspect docker containers failed", {
 | 
			
		||||
                error: error, stderr: stderr, stdout: stdout
 | 
			
		||||
            });
 | 
			
		||||
        idtoname = {};
 | 
			
		||||
        JSON.parse(stdout).forEach(function(n) {
 | 
			
		||||
            idtoname[n.Id] = n.Name.replace(/^\//, '');;
 | 
			
		||||
            if (n.State.Running) idtoname[n.Id] = n.Name.replace(/^\//, '');
 | 
			
		||||
        });
 | 
			
		||||
        if (oldcontainer && oldcontainer==stdout) return; // do not resend same containers
 | 
			
		||||
        oldcontainer = stdout;
 | 
			
		||||
@@ -98,10 +100,17 @@ module.exports = function(io) {
 | 
			
		||||
            exec("docker "+cmd+" "+name, updatecontainers);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function modify(cmd, name) {
 | 
			
		||||
            if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
 | 
			
		||||
                return 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 fail("create container failed", {
 | 
			
		||||
                            error: error, stderr: stderr, stdout: stdout
 | 
			
		||||
                        });
 | 
			
		||||
                    createContainer(cmds);
 | 
			
		||||
                })
 | 
			
		||||
            else
 | 
			
		||||
                updatecontainers();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function containers() {
 | 
			
		||||
@@ -141,6 +150,13 @@ module.exports = function(io) {
 | 
			
		||||
            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])
 | 
			
		||||
@@ -195,6 +211,7 @@ module.exports = function(io) {
 | 
			
		||||
            .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)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
    <script type="text/javascript" src="javascripts/jquery-ui.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="javascripts/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="javascripts/servicedock.js"></script>
 | 
			
		||||
    <title>ServiceDock: Docker as a Service</title>
 | 
			
		||||
  </head>
 | 
			
		||||
@@ -33,11 +34,15 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <ul id="menu" style="display: none" onmouseleave="$('#menu').hide();">
 | 
			
		||||
      <li onclick="$('#menu').hide();overview()">Overview</li>
 | 
			
		||||
      <li onclick="$('#menu').hide();manage()">Manage</li>
 | 
			
		||||
      <li onclick="$('#menu').hide();imgs()">Images</li>
 | 
			
		||||
      <li onclick="$('#menu').hide();about()">About</li>
 | 
			
		||||
      <li onclick="$('#menu').hide(); showCreate()">Create</li>
 | 
			
		||||
      <li><label for="upload">Upload</label><input autocomplete="off" type="file" accept="*.json" id="upload"/></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <script type="text/javascript">
 | 
			
		||||
      $(function() { 
 | 
			
		||||
        if (!window.FileReader) $("#upload").hide(); // not supported by browser
 | 
			
		||||
      })
 | 
			
		||||
      $("#upload").change(function(evt){$("#menu").hide(); upload(evt)});
 | 
			
		||||
    </script>
 | 
			
		||||
 | 
			
		||||
    <div id="main">
 | 
			
		||||
 | 
			
		||||
@@ -45,6 +50,68 @@
 | 
			
		||||
      
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div id="create" style="display: none">
 | 
			
		||||
      <form>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <legend>Name</legend>
 | 
			
		||||
          <input placeholder="name" type="text" id="name" size="20" />
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createports">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createvars">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <legend>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/>
 | 
			
		||||
          <select size="5" id="createvolumes">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createcolumesfroms">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createlinks">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createentrypoints">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <legend>Image</legend>
 | 
			
		||||
          <input placeholder="image" type="text" id="image" size="20" required />
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <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/>
 | 
			
		||||
          <select size="5" id="createcommands">
 | 
			
		||||
          </select>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
        <fieldset>
 | 
			
		||||
          <legend>Action</legend>
 | 
			
		||||
          <input class="listbutton" type="submit" id="doappend" value="Append to Queue" /><br/>
 | 
			
		||||
          <input class="listbutton" type="submit" id="dodownload" value="Download Changes" /><br/>
 | 
			
		||||
          <input class="listbutton" type="submit" id="docreate" value="Execute on Server" />
 | 
			
		||||
        </fieldset>
 | 
			
		||||
      </form>
 | 
			
		||||
      <div id="preview"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <pre id="logs" style="display: none"> </pre>
 | 
			
		||||
 | 
			
		||||
    <div id="console" style="display: none">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user