diff --git a/app/app.js b/app/app.js index d9a48daf6..35dbcdea9 100644 --- a/app/app.js +++ b/app/app.js @@ -1,4 +1,4 @@ -angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'pullImage', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events']) +angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'pullImage', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events', 'stats']) .config(['$routeProvider', function ($routeProvider) { 'use strict'; $routeProvider.when('/', { @@ -21,6 +21,10 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services' templateUrl: 'app/components/containerTop/containerTop.html', controller: 'ContainerTopController' }); + $routeProvider.when('/containers/:id/stats', { + templateUrl: 'app/components/stats/stats.html', + controller: 'StatsController' + }); $routeProvider.when('/containers_network', { templateUrl: 'app/components/containersNetwork/containersNetwork.html', controller: 'ContainersNetworkController' @@ -34,12 +38,15 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services' controller: 'ImageController' }); $routeProvider.when('/info', {templateUrl: 'app/components/info/info.html', controller: 'InfoController'}); - $routeProvider.when('/events', {templateUrl: 'app/components/events/events.html', controller: 'EventsController'}); + $routeProvider.when('/events', { + templateUrl: 'app/components/events/events.html', + controller: 'EventsController' + }); $routeProvider.otherwise({redirectTo: '/'}); }]) // This is your docker url that the api will use to make requests // You need to set this to the api endpoint without the port i.e. http://192.168.1.9 .constant('DOCKER_ENDPOINT', 'dockerapi') .constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243 - .constant('UI_VERSION', 'v0.6.0') - .constant('DOCKER_API_VERSION', 'v1.17'); + .constant('UI_VERSION', 'v0.8.0') + .constant('DOCKER_API_VERSION', 'v1.20'); diff --git a/app/components/builder/builderController.js b/app/components/builder/builderController.js index eff32d9ca..5455aae4a 100644 --- a/app/components/builder/builderController.js +++ b/app/components/builder/builderController.js @@ -1,5 +1,5 @@ angular.module('builder', []) -.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages', -function($scope, Dockerfile, Messages) { - $scope.template = 'app/components/builder/builder.html'; -}]); + .controller('BuilderController', ['$scope', 'Dockerfile', 'Messages', + function ($scope, Dockerfile, Messages) { + $scope.template = 'app/components/builder/builder.html'; + }]); diff --git a/app/components/container/container.html b/app/components/container/container.html index 4af90ab41..ee9bd2629 100644 --- a/app/components/container/container.html +++ b/app/components/container/container.html @@ -133,6 +133,10 @@ Logs: stdout/stderr + + Stats: + stats + Top: Top diff --git a/app/components/container/containerController.js b/app/components/container/containerController.js index dbcf57376..8f46d2211 100644 --- a/app/components/container/containerController.js +++ b/app/components/container/containerController.js @@ -1,143 +1,143 @@ angular.module('container', []) -.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner', -function($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) { - $scope.changes = []; - $scope.edit = false; + .controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner', + function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) { + $scope.changes = []; + $scope.edit = false; - var update = function() { - ViewSpinner.spin(); - Container.get({id: $routeParams.id}, function(d) { - $scope.container = d; - $scope.container.edit = false; - $scope.container.newContainerName = d.Name; - ViewSpinner.stop(); - }, function(e) { - if (e.status === 404) { - $('.detail').hide(); - Messages.error("Not found", "Container not found."); - } else { - Messages.error("Failure", e.data); - } - ViewSpinner.stop(); - }); - }; + var update = function () { + ViewSpinner.spin(); + Container.get({id: $routeParams.id}, function (d) { + $scope.container = d; + $scope.container.edit = false; + $scope.container.newContainerName = d.Name; + ViewSpinner.stop(); + }, function (e) { + if (e.status === 404) { + $('.detail').hide(); + Messages.error("Not found", "Container not found."); + } else { + Messages.error("Failure", e.data); + } + ViewSpinner.stop(); + }); + }; - $scope.start = function(){ - ViewSpinner.spin(); - Container.start({ - id: $scope.container.Id, - HostConfig: $scope.container.HostConfig - }, function(d) { - update(); - Messages.send("Container started", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to start." + e.data); - }); - }; + $scope.start = function () { + ViewSpinner.spin(); + Container.start({ + id: $scope.container.Id, + HostConfig: $scope.container.HostConfig + }, function (d) { + update(); + Messages.send("Container started", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to start." + e.data); + }); + }; - $scope.stop = function() { - ViewSpinner.spin(); - Container.stop({id: $routeParams.id}, function(d) { - update(); - Messages.send("Container stopped", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to stop." + e.data); - }); - }; + $scope.stop = function () { + ViewSpinner.spin(); + Container.stop({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container stopped", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to stop." + e.data); + }); + }; - $scope.kill = function() { - ViewSpinner.spin(); - Container.kill({id: $routeParams.id}, function(d) { - update(); - Messages.send("Container killed", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to die." + e.data); - }); - }; + $scope.kill = function () { + ViewSpinner.spin(); + Container.kill({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container killed", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to die." + e.data); + }); + }; - $scope.commit = function() { - ViewSpinner.spin(); - ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function(d) { - update(); - Messages.send("Container commited", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to commit." + e.data); - }); - }; - $scope.pause = function() { - ViewSpinner.spin(); - Container.pause({id: $routeParams.id}, function(d) { - update(); - Messages.send("Container paused", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to pause." + e.data); - }); - }; + $scope.commit = function () { + ViewSpinner.spin(); + ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function (d) { + update(); + Messages.send("Container commited", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to commit." + e.data); + }); + }; + $scope.pause = function () { + ViewSpinner.spin(); + Container.pause({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container paused", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to pause." + e.data); + }); + }; - $scope.unpause = function() { - ViewSpinner.spin(); - Container.unpause({id: $routeParams.id}, function(d) { - update(); - Messages.send("Container unpaused", $routeParams.id); - }, function(e) { - update(); - Messages.error("Failure", "Container failed to unpause." + e.data); - }); - }; + $scope.unpause = function () { + ViewSpinner.spin(); + Container.unpause({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container unpaused", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to unpause." + e.data); + }); + }; + + $scope.remove = function () { + ViewSpinner.spin(); + Container.remove({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container removed", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to remove." + e.data); + }); + }; + + $scope.restart = function () { + ViewSpinner.spin(); + Container.restart({id: $routeParams.id}, function (d) { + update(); + Messages.send("Container restarted", $routeParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to restart." + e.data); + }); + }; + + $scope.hasContent = function (data) { + return data !== null && data !== undefined; + }; + + $scope.getChanges = function () { + ViewSpinner.spin(); + Container.changes({id: $routeParams.id}, function (d) { + $scope.changes = d; + ViewSpinner.stop(); + }); + }; + + $scope.renameContainer = function () { + // #FIXME fix me later to handle http status to show the correct error message + Container.rename({id: $routeParams.id, 'name': $scope.container.newContainerName}, function (data) { + if (data.name) { + $scope.container.Name = data.name; + Messages.send("Container renamed", $routeParams.id); + } else { + $scope.container.newContainerName = $scope.container.Name; + Messages.error("Failure", "Container failed to rename."); + } + }); + $scope.container.edit = false; + }; - $scope.remove = function() { - ViewSpinner.spin(); - Container.remove({id: $routeParams.id}, function(d) { update(); - Messages.send("Container removed", $routeParams.id); - }, function(e){ - update(); - Messages.error("Failure", "Container failed to remove." + e.data); - }); - }; - - $scope.restart = function() { - ViewSpinner.spin(); - Container.restart({id: $routeParams.id}, function(d) { - update(); - Messages.send("Container restarted", $routeParams.id); - }, function(e){ - update(); - Messages.error("Failure", "Container failed to restart." + e.data); - }); - }; - - $scope.hasContent = function(data) { - return data !== null && data !== undefined; - }; - - $scope.getChanges = function() { - ViewSpinner.spin(); - Container.changes({id: $routeParams.id}, function(d) { - $scope.changes = d; - ViewSpinner.stop(); - }); - }; - - $scope.renameContainer = function () { - // #FIXME fix me later to handle http status to show the correct error message - Container.rename({id: $routeParams.id, 'name': $scope.container.newContainerName}, function(data){ - if (data.name){ - $scope.container.Name = data.name; - Messages.send("Container renamed", $routeParams.id); - }else { - $scope.container.newContainerName = $scope.container.Name; - Messages.error("Failure", "Container failed to rename."); - } - }); - $scope.container.edit = false; - }; - - update(); - $scope.getChanges(); -}]); + $scope.getChanges(); + }]); diff --git a/app/components/containerLogs/containerLogsController.js b/app/components/containerLogs/containerLogsController.js index 6eb6984ba..27977f11d 100644 --- a/app/components/containerLogs/containerLogsController.js +++ b/app/components/containerLogs/containerLogsController.js @@ -1,76 +1,76 @@ angular.module('containerLogs', []) -.controller('ContainerLogsController', ['$scope', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner', -function($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) { - $scope.stdout = ''; - $scope.stderr = ''; - $scope.showTimestamps = false; - $scope.tailLines = 2000; + .controller('ContainerLogsController', ['$scope', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner', + function ($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) { + $scope.stdout = ''; + $scope.stderr = ''; + $scope.showTimestamps = false; + $scope.tailLines = 2000; - ViewSpinner.spin(); - Container.get({id: $routeParams.id}, function(d) { - $scope.container = d; - ViewSpinner.stop(); - }, function(e) { - if (e.status === 404) { - Messages.error("Not found", "Container not found."); - } else { - Messages.error("Failure", e.data); - } - ViewSpinner.stop(); - }); + ViewSpinner.spin(); + Container.get({id: $routeParams.id}, function (d) { + $scope.container = d; + ViewSpinner.stop(); + }, function (e) { + if (e.status === 404) { + Messages.error("Not found", "Container not found."); + } else { + Messages.error("Failure", e.data); + } + ViewSpinner.stop(); + }); - function getLogs() { - ViewSpinner.spin(); - ContainerLogs.get($routeParams.id, { - stdout: 1, - stderr: 0, - timestamps: $scope.showTimestamps, - tail: $scope.tailLines - }, function(data, status, headers, config) { - // Replace carriage returns with newlines to clean up output - data = data.replace(/[\r]/g, '\n'); - // Strip 8 byte header from each line of output - data = data.substring(8); - data = data.replace(/\n(.{8})/g, '\n'); - $scope.stdout = data; - ViewSpinner.stop(); - }); + function getLogs() { + ViewSpinner.spin(); + ContainerLogs.get($routeParams.id, { + stdout: 1, + stderr: 0, + timestamps: $scope.showTimestamps, + tail: $scope.tailLines + }, function (data, status, headers, config) { + // Replace carriage returns with newlines to clean up output + data = data.replace(/[\r]/g, '\n'); + // Strip 8 byte header from each line of output + data = data.substring(8); + data = data.replace(/\n(.{8})/g, '\n'); + $scope.stdout = data; + ViewSpinner.stop(); + }); - ContainerLogs.get($routeParams.id, { - stdout: 0, - stderr: 1, - timestamps: $scope.showTimestamps, - tail: $scope.tailLines - }, function(data, status, headers, config) { - // Replace carriage returns with newlines to clean up output - data = data.replace(/[\r]/g, '\n'); - // Strip 8 byte header from each line of output - data = data.substring(8); - data = data.replace(/\n(.{8})/g, '\n'); - $scope.stderr = data; - ViewSpinner.stop(); - }); - } + ContainerLogs.get($routeParams.id, { + stdout: 0, + stderr: 1, + timestamps: $scope.showTimestamps, + tail: $scope.tailLines + }, function (data, status, headers, config) { + // Replace carriage returns with newlines to clean up output + data = data.replace(/[\r]/g, '\n'); + // Strip 8 byte header from each line of output + data = data.substring(8); + data = data.replace(/\n(.{8})/g, '\n'); + $scope.stderr = data; + ViewSpinner.stop(); + }); + } - // initial call - getLogs(); - var logIntervalId = window.setInterval(getLogs, 5000); + // initial call + getLogs(); + var logIntervalId = window.setInterval(getLogs, 5000); - $scope.$on("$destroy", function(){ - // clearing interval when view changes - clearInterval(logIntervalId); - }); + $scope.$on("$destroy", function () { + // clearing interval when view changes + clearInterval(logIntervalId); + }); - $scope.scrollTo = function(id) { - $location.hash(id); - $anchorScroll(); - }; + $scope.scrollTo = function (id) { + $location.hash(id); + $anchorScroll(); + }; - $scope.toggleTimestamps = function() { - getLogs(); - }; + $scope.toggleTimestamps = function () { + getLogs(); + }; - $scope.toggleTail = function() { - getLogs(); - }; -}]); + $scope.toggleTail = function () { + getLogs(); + }; + }]); diff --git a/app/components/containerLogs/containerlogs.html b/app/components/containerLogs/containerlogs.html index 8dfa62b8a..406d04e66 100644 --- a/app/components/containerLogs/containerlogs.html +++ b/app/components/containerLogs/containerlogs.html @@ -1,6 +1,7 @@

Logs for container: {{ container.Name }}

+
@@ -9,12 +10,12 @@
Reload logs + ng-model="tailLines" ng-keypress="($event.which === 13)? toggleTail() : 0"/>
+ ng-change="toggleTimestamps()"/>
diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js index 3a88987ab..dbd8b0d75 100644 --- a/app/components/containers/containersController.js +++ b/app/components/containers/containersController.js @@ -1,111 +1,112 @@ angular.module('containers', []) -.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner', -function($scope, Container, Settings, Messages, ViewSpinner) { - $scope.predicate = '-Created'; - $scope.toggle = false; - $scope.displayAll = Settings.displayAll; + .controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner', + function ($scope, Container, Settings, Messages, ViewSpinner) { + $scope.predicate = '-Created'; + $scope.toggle = false; + $scope.displayAll = Settings.displayAll; - var update = function(data) { - ViewSpinner.spin(); - Container.query(data, function(d) { - $scope.containers = d.map(function(item) { - return new ContainerViewModel(item); }); - ViewSpinner.stop(); - }); - }; - - var batch = function(items, action, msg) { - ViewSpinner.spin(); - var counter = 0; - var complete = function() { - counter = counter -1; - if (counter === 0) { - ViewSpinner.stop(); - update({all: Settings.displayAll ? 1 : 0}); - } - }; - angular.forEach(items, function(c) { - if (c.Checked) { - if(action === Container.start){ - Container.get({id: c.Id}, function(d) { - c = d; - counter = counter + 1; - action({id: c.Id, HostConfig: c.HostConfig || {}}, function(d) { - Messages.send("Container " + msg, c.Id); - var index = $scope.containers.indexOf(c); - complete(); - }, function(e) { - Messages.error("Failure", e.data); - complete(); + var update = function (data) { + ViewSpinner.spin(); + Container.query(data, function (d) { + $scope.containers = d.map(function (item) { + return new ContainerViewModel(item); }); - }, function(e) { - if (e.status === 404) { - $('.detail').hide(); - Messages.error("Not found", "Container not found."); - } else { - Messages.error("Failure", e.data); - } - complete(); - }); + ViewSpinner.stop(); + }); + }; + + var batch = function (items, action, msg) { + ViewSpinner.spin(); + var counter = 0; + var complete = function () { + counter = counter - 1; + if (counter === 0) { + ViewSpinner.stop(); + update({all: Settings.displayAll ? 1 : 0}); + } + }; + angular.forEach(items, function (c) { + if (c.Checked) { + if (action === Container.start) { + Container.get({id: c.Id}, function (d) { + c = d; + counter = counter + 1; + action({id: c.Id, HostConfig: c.HostConfig || {}}, function (d) { + Messages.send("Container " + msg, c.Id); + var index = $scope.containers.indexOf(c); + complete(); + }, function (e) { + Messages.error("Failure", e.data); + complete(); + }); + }, function (e) { + if (e.status === 404) { + $('.detail').hide(); + Messages.error("Not found", "Container not found."); + } else { + Messages.error("Failure", e.data); + } + complete(); + }); + } + else { + counter = counter + 1; + action({id: c.Id}, function (d) { + Messages.send("Container " + msg, c.Id); + var index = $scope.containers.indexOf(c); + complete(); + }, function (e) { + Messages.error("Failure", e.data); + complete(); + }); + + } + + } + }); + if (counter === 0) { + ViewSpinner.stop(); } - else{ - counter = counter + 1; - action({id: c.Id}, function(d) { - Messages.send("Container " + msg, c.Id); - var index = $scope.containers.indexOf(c); - complete(); - }, function(e) { - Messages.error("Failure", e.data); - complete(); - }); + }; - } + $scope.toggleSelectAll = function () { + angular.forEach($scope.containers, function (i) { + i.Checked = $scope.toggle; + }); + }; - } - }); - if (counter === 0) { - ViewSpinner.stop(); - } - }; + $scope.toggleGetAll = function () { + Settings.displayAll = $scope.displayAll; + update({all: Settings.displayAll ? 1 : 0}); + }; - $scope.toggleSelectAll = function() { - angular.forEach($scope.containers, function(i) { - i.Checked = $scope.toggle; - }); - }; + $scope.startAction = function () { + batch($scope.containers, Container.start, "Started"); + }; - $scope.toggleGetAll = function() { - Settings.displayAll = $scope.displayAll; - update({all: Settings.displayAll ? 1 : 0}); - }; + $scope.stopAction = function () { + batch($scope.containers, Container.stop, "Stopped"); + }; - $scope.startAction = function() { - batch($scope.containers, Container.start, "Started"); - }; + $scope.restartAction = function () { + batch($scope.containers, Container.restart, "Restarted"); + }; - $scope.stopAction = function() { - batch($scope.containers, Container.stop, "Stopped"); - }; + $scope.killAction = function () { + batch($scope.containers, Container.kill, "Killed"); + }; - $scope.restartAction = function() { - batch($scope.containers, Container.restart, "Restarted"); - }; + $scope.pauseAction = function () { + batch($scope.containers, Container.pause, "Paused"); + }; - $scope.killAction = function() { - batch($scope.containers, Container.kill, "Killed"); - }; + $scope.unpauseAction = function () { + batch($scope.containers, Container.unpause, "Unpaused"); + }; - $scope.pauseAction = function() { - batch($scope.containers, Container.pause, "Paused"); - }; + $scope.removeAction = function () { + batch($scope.containers, Container.remove, "Removed"); + }; - $scope.unpauseAction = function() { - batch($scope.containers, Container.unpause, "Unpaused"); - }; - - $scope.removeAction = function() { - batch($scope.containers, Container.remove, "Removed"); - }; - - update({all: Settings.displayAll ? 1 : 0}); -}]); + update({all: Settings.displayAll ? 1 : 0}); + }]); diff --git a/app/components/containersNetwork/containersNetwork.html b/app/components/containersNetwork/containersNetwork.html index f213de493..ba265e880 100644 --- a/app/components/containersNetwork/containersNetwork.html +++ b/app/components/containersNetwork/containersNetwork.html @@ -15,7 +15,8 @@
- +
" + + this.addContainerNode = function (container) { + this.nodes.add({ + id: container.Id, + label: container.Name, + title: "", - color: (container.Running ? "#8888ff" : "#cccccc") - }); - }; - - this.hasEdge = function(from, to) { - return this.edges.getIds({ - filter: function (item) { - return item.from == from.Id && item.to == to.Id; - } }).length > 0; - }; - - this.addLinkEdgeIfExists = function(from, to) { - if (from.Links != null && from.Links[to.Name] != null && !this.hasEdge(from, to)) { - this.edges.add({ - from: from.Id, - to: to.Id, - label: from.Links[to.Name] }); - } - }; - - this.addVolumeEdgeIfExists = function(from, to) { - if (from.VolumesFrom != null && (from.VolumesFrom[to.Id] != null || from.VolumesFrom[to.Name] != null) && !this.hasEdge(from, to)) { - this.edges.add({ - from: from.Id, - to: to.Id, - color: { color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}}); - } - }; - - this.removeContainersNodes = function(containersIds) { - this.nodes.remove(containersIds); - }; - } - - function ContainersNetwork() { - this.data = new ContainersNetworkData(); - this.containers = {}; - this.selectedContainersIds = []; - this.shownContainersIds = []; - this.events = { - select : function(event) { - $scope.network.selectedContainersIds = event.nodes; - $scope.$apply( function() { - $scope.query = ''; + color: (container.Running ? "#8888ff" : "#cccccc") }); - }, - doubleClick : function(event) { - $scope.$apply( function() { - $location.path('/containers/' + event.nodes[0]); - }); - } - }; - this.options = { - navigation: true, - keyboard: true, - height: '500px', width: '700px', - nodes: { - shape: 'box' - }, - edges: { - style: 'arrow' - }, - physics: { - barnesHut : { - springLength: 200 + }; + + this.hasEdge = function (from, to) { + return this.edges.getIds({ + filter: function (item) { + return item.from === from.Id && item.to === to.Id; + } + }).length > 0; + }; + + this.addLinkEdgeIfExists = function (from, to) { + if (from.Links != null && from.Links[to.Name] != null && !this.hasEdge(from, to)) { + this.edges.add({ + from: from.Id, + to: to.Id, + label: from.Links[to.Name] + }); } - } - }; + }; - this.addContainer = function(data) { - var container = new ContainerNode(data); - this.containers[container.Id] = container; - this.shownContainersIds.push(container.Id); - this.data.addContainerNode(container); - for (var otherContainerId in this.containers) { - var otherContainer = this.containers[otherContainerId]; - this.data.addLinkEdgeIfExists(container, otherContainer); - this.data.addLinkEdgeIfExists(otherContainer, container); - this.data.addVolumeEdgeIfExists(container, otherContainer); - this.data.addVolumeEdgeIfExists(otherContainer, container); - } - }; - - this.selectContainers = function(query) { - if (this.component != null) { - this.selectedContainersIds = this.searchContainers(query); - this.component.selectNodes(this.selectedContainersIds); - } - }; - - this.searchContainers = function(query) { - if (query.trim() === "") { - return []; - } - var selectedContainersIds = []; - for (var i=0; i < this.shownContainersIds.length; i++) { - var container = this.containers[this.shownContainersIds[i]]; - if (container.Name.indexOf(query) > -1 || - container.Image.indexOf(query) > -1 || - container.Id.indexOf(query) > -1) { - selectedContainersIds.push(container.Id); + this.addVolumeEdgeIfExists = function (from, to) { + if (from.VolumesFrom != null && (from.VolumesFrom[to.Id] != null || from.VolumesFrom[to.Name] != null) && !this.hasEdge(from, to)) { + this.edges.add({ + from: from.Id, + to: to.Id, + color: {color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'} + }); } - } - return selectedContainersIds; - }; + }; - this.hideSelected = function() { - var i=0; - while ( i < this.shownContainersIds.length ) { - if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) { - this.shownContainersIds.splice(i, 1); - } else { - i++; - } - } - this.data.removeContainersNodes(this.selectedContainersIds); - $scope.query = ''; + this.removeContainersNodes = function (containersIds) { + this.nodes.remove(containersIds); + }; + } + + function ContainersNetwork() { + this.data = new ContainersNetworkData(); + this.containers = {}; this.selectedContainersIds = []; - }; + this.shownContainersIds = []; + this.events = { + select: function (event) { + $scope.network.selectedContainersIds = event.nodes; + $scope.$apply(function () { + $scope.query = ''; + }); + }, + doubleClick: function (event) { + $scope.$apply(function () { + $location.path('/containers/' + event.nodes[0]); + }); + } + }; + this.options = { + navigation: true, + keyboard: true, + height: '500px', width: '700px', + nodes: { + shape: 'box' + }, + edges: { + style: 'arrow' + }, + physics: { + barnesHut: { + springLength: 200 + } + } + }; - this.searchDownstream = function(containerId, downstreamContainersIds) { - if (downstreamContainersIds.indexOf(containerId) > -1) { - return; - } - downstreamContainersIds.push(containerId); - var container = this.containers[containerId]; - if (container.Links == null && container.VolumesFrom == null) { - return; - } - for (var otherContainerId in this.containers) { - var otherContainer = this.containers[otherContainerId]; - if (container.Links != null && container.Links[otherContainer.Name] != null) { - this.searchDownstream(otherContainer.Id, downstreamContainersIds); - } else if (container.VolumesFrom != null && + this.addContainer = function (data) { + var container = new ContainerNode(data); + this.containers[container.Id] = container; + this.shownContainersIds.push(container.Id); + this.data.addContainerNode(container); + for (var otherContainerId in this.containers) { + var otherContainer = this.containers[otherContainerId]; + this.data.addLinkEdgeIfExists(container, otherContainer); + this.data.addLinkEdgeIfExists(otherContainer, container); + this.data.addVolumeEdgeIfExists(container, otherContainer); + this.data.addVolumeEdgeIfExists(otherContainer, container); + } + }; + + this.selectContainers = function (query) { + if (this.component != null) { + this.selectedContainersIds = this.searchContainers(query); + this.component.selectNodes(this.selectedContainersIds); + } + }; + + this.searchContainers = function (query) { + if (query.trim() === "") { + return []; + } + var selectedContainersIds = []; + for (var i = 0; i < this.shownContainersIds.length; i++) { + var container = this.containers[this.shownContainersIds[i]]; + if (container.Name.indexOf(query) > -1 || + container.Image.indexOf(query) > -1 || + container.Id.indexOf(query) > -1) { + selectedContainersIds.push(container.Id); + } + } + return selectedContainersIds; + }; + + this.hideSelected = function () { + var i = 0; + while (i < this.shownContainersIds.length) { + if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) { + this.shownContainersIds.splice(i, 1); + } else { + i++; + } + } + this.data.removeContainersNodes(this.selectedContainersIds); + $scope.query = ''; + this.selectedContainersIds = []; + }; + + this.searchDownstream = function (containerId, downstreamContainersIds) { + if (downstreamContainersIds.indexOf(containerId) > -1) { + return; + } + downstreamContainersIds.push(containerId); + var container = this.containers[containerId]; + if (container.Links == null && container.VolumesFrom == null) { + return; + } + for (var otherContainerId in this.containers) { + var otherContainer = this.containers[otherContainerId]; + if (container.Links != null && container.Links[otherContainer.Name] != null) { + this.searchDownstream(otherContainer.Id, downstreamContainersIds); + } else if (container.VolumesFrom != null && container.VolumesFrom[otherContainer.Id] != null) { - this.searchDownstream(otherContainer.Id, downstreamContainersIds); + this.searchDownstream(otherContainer.Id, downstreamContainersIds); + } } - } - }; + }; - this.updateShownContainers = function(newShownContainersIds) { - for (var containerId in this.containers) { - if (newShownContainersIds.indexOf(containerId) > -1 && + this.updateShownContainers = function (newShownContainersIds) { + for (var containerId in this.containers) { + if (newShownContainersIds.indexOf(containerId) > -1 && this.shownContainersIds.indexOf(containerId) === -1) { - this.data.addContainerNode(this.containers[containerId]); - } else if (newShownContainersIds.indexOf(containerId) === -1 && + this.data.addContainerNode(this.containers[containerId]); + } else if (newShownContainersIds.indexOf(containerId) === -1 && this.shownContainersIds.indexOf(containerId) > -1) { - this.data.removeContainersNodes(containerId); + this.data.removeContainersNodes(containerId); + } } - } - this.shownContainersIds = newShownContainersIds; - }; + this.shownContainersIds = newShownContainersIds; + }; - this.showSelectedDownstream = function() { - var downstreamContainersIds = []; - for (var i=0; i < this.selectedContainersIds.length; i++) { - this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds); - } - this.updateShownContainers(downstreamContainersIds); - }; + this.showSelectedDownstream = function () { + var downstreamContainersIds = []; + for (var i = 0; i < this.selectedContainersIds.length; i++) { + this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds); + } + this.updateShownContainers(downstreamContainersIds); + }; - this.searchUpstream = function(containerId, upstreamContainersIds) { - if (upstreamContainersIds.indexOf(containerId) > -1) { - return; - } - upstreamContainersIds.push(containerId); - var container = this.containers[containerId]; - for (var otherContainerId in this.containers) { - var otherContainer = this.containers[otherContainerId]; - if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) { - this.searchUpstream(otherContainer.Id, upstreamContainersIds); - } else if (otherContainer.VolumesFrom != null && + this.searchUpstream = function (containerId, upstreamContainersIds) { + if (upstreamContainersIds.indexOf(containerId) > -1) { + return; + } + upstreamContainersIds.push(containerId); + var container = this.containers[containerId]; + for (var otherContainerId in this.containers) { + var otherContainer = this.containers[otherContainerId]; + if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) { + this.searchUpstream(otherContainer.Id, upstreamContainersIds); + } else if (otherContainer.VolumesFrom != null && otherContainer.VolumesFrom[container.Id] != null) { - this.searchUpstream(otherContainer.Id, upstreamContainersIds); + this.searchUpstream(otherContainer.Id, upstreamContainersIds); + } } - } - }; + }; - this.showSelectedUpstream = function() { - var upstreamContainersIds = []; - for (var i=0; i < this.selectedContainersIds.length; i++) { - this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds); - } - this.updateShownContainers(upstreamContainersIds); - }; - - this.showAll = function() { - for (var containerId in this.containers) { - if (this.shownContainersIds.indexOf(containerId) === -1) { - this.data.addContainerNode(this.containers[containerId]); - this.shownContainersIds.push(containerId); + this.showSelectedUpstream = function () { + var upstreamContainersIds = []; + for (var i = 0; i < this.selectedContainersIds.length; i++) { + this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds); } - } + this.updateShownContainers(upstreamContainersIds); + }; + + this.showAll = function () { + for (var containerId in this.containers) { + if (this.shownContainersIds.indexOf(containerId) === -1) { + this.data.addContainerNode(this.containers[containerId]); + this.shownContainersIds.push(containerId); + } + } + }; + + } + + $scope.network = new ContainersNetwork(); + + var showFailure = function (event) { + Messages.error('Failure', e.data); }; - } + var addContainer = function (container) { + $scope.network.addContainer(container); + }; - $scope.network = new ContainersNetwork(); + var update = function (data) { + Container.query(data, function (d) { + for (var i = 0; i < d.length; i++) { + Container.get({id: d[i].Id}, addContainer, showFailure); + } + }); + }; + update({all: 0}); - var showFailure = function (event) { - Messages.error('Failure', e.data); - }; + $scope.includeStopped = false; + $scope.toggleIncludeStopped = function () { + $scope.network.updateShownContainers([]); + update({all: $scope.includeStopped ? 1 : 0}); + }; - var addContainer = function (container) { - $scope.network.addContainer(container); - }; - - var update = function (data) { - Container.query(data, function(d) { - for (var i = 0; i < d.length; i++) { - Container.get({id: d[i].Id}, addContainer, showFailure); - } - }); - }; - update({all: 0}); - - $scope.includeStopped = false; - $scope.toggleIncludeStopped = function() { - $scope.network.updateShownContainers([]); - update({all: $scope.includeStopped ? 1 : 0}); - }; - -}]); + }]); diff --git a/app/components/dashboard/dashboard.html b/app/components/dashboard/dashboard.html index 7dd6f34dc..8b2209e6d 100644 --- a/app/components/dashboard/dashboard.html +++ b/app/components/dashboard/dashboard.html @@ -1,4 +1,3 @@ -
- +
- +
- +
- +
diff --git a/app/components/pullImage/pullImageController.js b/app/components/pullImage/pullImageController.js index 9fc242fb5..48e05f8c4 100644 --- a/app/components/pullImage/pullImageController.js +++ b/app/components/pullImage/pullImageController.js @@ -1,16 +1,16 @@ angular.module('pullImage', []) .controller('PullImageController', ['$scope', '$log', 'Dockerfile', 'Messages', 'Image', 'ViewSpinner', - function($scope, $log, Dockerfile, Messages, Image, ViewSpinner) { + function ($scope, $log, Dockerfile, Messages, Image, ViewSpinner) { $scope.template = 'app/components/pullImage/pullImage.html'; - $scope.init = function() { + $scope.init = function () { $scope.config = { registry: '', repo: '', fromImage: '', tag: 'latest' - } - } + }; + }; $scope.init(); @@ -18,7 +18,7 @@ angular.module('pullImage', []) Messages.error('Error', errorMsgFilter(e)); } - $scope.pull = function() { + $scope.pull = function () { $('#error-message').hide(); var config = angular.copy($scope.config); var imageName = (config.registry ? config.registry + '/' : '' ) + @@ -28,10 +28,10 @@ angular.module('pullImage', []) ViewSpinner.spin(); $('#pull-modal').modal('hide'); - Image.create(config, function(data) { + Image.create(config, function (data) { ViewSpinner.stop(); if (data.constructor === Array) { - var f = data.length > 0 && data[data.length-1].hasOwnProperty('error'); + var f = data.length > 0 && data[data.length - 1].hasOwnProperty('error'); //check for error if (f) { var d = data[data.length - 1]; @@ -46,11 +46,11 @@ angular.module('pullImage', []) Messages.send("Image Added", imageName); $scope.init(); } - }, function(e) { + }, function (e) { ViewSpinner.stop(); $scope.error = "Cannot pull image " + imageName + " Reason: " + e.data; $('#pull-modal').modal('show'); $('#error-message').show(); }); - } + }; }]); diff --git a/app/components/sidebar/sidebar.html b/app/components/sidebar/sidebar.html index e7b559ec3..b93b8e5e3 100644 --- a/app/components/sidebar/sidebar.html +++ b/app/components/sidebar/sidebar.html @@ -1,11 +1,11 @@
Running containers: -
+
Endpoint: {{ endpoint }}
diff --git a/app/components/sidebar/sidebarController.js b/app/components/sidebar/sidebarController.js index f182a3481..3f58d675e 100644 --- a/app/components/sidebar/sidebarController.js +++ b/app/components/sidebar/sidebarController.js @@ -1,11 +1,11 @@ angular.module('sidebar', []) -.controller('SideBarController', ['$scope', 'Container', 'Settings', -function($scope, Container, Settings) { - $scope.template = 'partials/sidebar.html'; - $scope.containers = []; - $scope.endpoint = Settings.endpoint; + .controller('SideBarController', ['$scope', 'Container', 'Settings', + function ($scope, Container, Settings) { + $scope.template = 'partials/sidebar.html'; + $scope.containers = []; + $scope.endpoint = Settings.endpoint; - Container.query({all: 0}, function(d) { - $scope.containers = d; - }); -}]); + Container.query({all: 0}, function (d) { + $scope.containers = d; + }); + }]); diff --git a/app/components/startContainer/startContainerController.js b/app/components/startContainer/startContainerController.js index 571a360ad..46d8ba710 100644 --- a/app/components/startContainer/startContainerController.js +++ b/app/components/startContainer/startContainerController.js @@ -1,147 +1,153 @@ angular.module('startContainer', ['ui.bootstrap']) -.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter', -function($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) { - $scope.template = 'app/components/startContainer/startcontainer.html'; + .controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter', + function ($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) { + $scope.template = 'app/components/startContainer/startcontainer.html'; - Container.query({all: 1}, function(d) { - $scope.containerNames = d.map(function(container){ - return containernameFilter(container); - }); - }); + Container.query({all: 1}, function (d) { + $scope.containerNames = d.map(function (container) { + return containernameFilter(container); + }); + }); - $scope.config = { - Env: [], - Volumes: [], - SecurityOpts: [], - HostConfig: { - PortBindings: [], - Binds: [], - Links: [], - Dns: [], - DnsSearch: [], - VolumesFrom: [], - CapAdd: [], - CapDrop: [], - Devices: [], - LxcConf: [], - ExtraHosts: [] - } - }; - - $scope.menuStatus = { - containerOpen: true, - hostConfigOpen: false - }; - - function failedRequestHandler(e, Messages) { - Messages.error('Error', errorMsgFilter(e)); - } - - function rmEmptyKeys(col) { - for (var key in col) { - if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) { - delete col[key]; - } - } - } - - function getNames(arr) { - return arr.map(function(item) {return item.name;}); - } - - $scope.create = function() { - // Copy the config before transforming fields to the remote API format - var config = angular.copy($scope.config); - - config.Image = $routeParams.id; - - if (config.Cmd && config.Cmd[0] === "[") { - config.Cmd = angular.fromJson(config.Cmd); - } else if (config.Cmd) { - config.Cmd = config.Cmd.split(' '); - } - - config.Env = config.Env.map(function(envar) {return envar.name + '=' + envar.value;}); - - config.Volumes = getNames(config.Volumes); - config.SecurityOpts = getNames(config.SecurityOpts); - - config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom); - config.HostConfig.Binds = getNames(config.HostConfig.Binds); - config.HostConfig.Links = getNames(config.HostConfig.Links); - config.HostConfig.Dns = getNames(config.HostConfig.Dns); - config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch); - config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd); - config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop); - config.HostConfig.LxcConf = config.HostConfig.LxcConf.reduce(function(prev, cur, idx){ - prev[cur.name] = cur.value; - return prev; - }, {}); - config.HostConfig.ExtraHosts = config.HostConfig.ExtraHosts.map(function(entry) {return entry.host + ':' + entry.ip;}); - - var ExposedPorts = {}; - var PortBindings = {}; - config.HostConfig.PortBindings.forEach(function(portBinding) { - var intPort = portBinding.intPort + "/tcp"; - if (portBinding.protocol === "udp") { - intPort = portBinding.intPort + "/udp"; - } - var binding = { - HostIp: portBinding.ip, - HostPort: portBinding.extPort + $scope.config = { + Env: [], + Volumes: [], + SecurityOpts: [], + HostConfig: { + PortBindings: [], + Binds: [], + Links: [], + Dns: [], + DnsSearch: [], + VolumesFrom: [], + CapAdd: [], + CapDrop: [], + Devices: [], + LxcConf: [], + ExtraHosts: [] + } }; - if (portBinding.intPort) { - ExposedPorts[intPort] = {}; - if (intPort in PortBindings) { - PortBindings[intPort].push(binding); - } else { - PortBindings[intPort] = [binding]; - } - } else { - Messages.send('Warning', 'Internal port must be specified for PortBindings'); + + $scope.menuStatus = { + containerOpen: true, + hostConfigOpen: false + }; + + function failedRequestHandler(e, Messages) { + Messages.error('Error', errorMsgFilter(e)); } - }); - config.ExposedPorts = ExposedPorts; - config.HostConfig.PortBindings = PortBindings; - // Remove empty fields from the request to avoid overriding defaults - rmEmptyKeys(config.HostConfig); - rmEmptyKeys(config); - - var ctor = Container; - var loc = $location; - var s = $scope; - Container.create(config, function(d) { - if (d.Id) { - var reqBody = config.HostConfig || {}; - reqBody.id = d.Id; - ctor.start(reqBody, function(cd) { - if (cd.id) { - Messages.send('Container Started', d.Id); - $('#create-modal').modal('hide'); - loc.path('/containers/' + d.Id + '/'); - } else { - failedRequestHandler(cd, Messages); - ctor.remove({id: d.Id}, function() { - Messages.send('Container Removed', d.Id); - }); - } - }, function(e) { - failedRequestHandler(e, Messages); - }); - } else { - failedRequestHandler(d, Messages); + function rmEmptyKeys(col) { + for (var key in col) { + if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) { + delete col[key]; + } } - }, function(e) { - failedRequestHandler(e, Messages); - }); - }; + } - $scope.addEntry = function(array, entry) { - array.push(entry); - }; - $scope.rmEntry = function(array, entry) { - var idx = array.indexOf(entry); - array.splice(idx, 1); - }; -}]); + function getNames(arr) { + return arr.map(function (item) { + return item.name; + }); + } + + $scope.create = function () { + // Copy the config before transforming fields to the remote API format + var config = angular.copy($scope.config); + + config.Image = $routeParams.id; + + if (config.Cmd && config.Cmd[0] === "[") { + config.Cmd = angular.fromJson(config.Cmd); + } else if (config.Cmd) { + config.Cmd = config.Cmd.split(' '); + } + + config.Env = config.Env.map(function (envar) { + return envar.name + '=' + envar.value; + }); + + config.Volumes = getNames(config.Volumes); + config.SecurityOpts = getNames(config.SecurityOpts); + + config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom); + config.HostConfig.Binds = getNames(config.HostConfig.Binds); + config.HostConfig.Links = getNames(config.HostConfig.Links); + config.HostConfig.Dns = getNames(config.HostConfig.Dns); + config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch); + config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd); + config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop); + config.HostConfig.LxcConf = config.HostConfig.LxcConf.reduce(function (prev, cur, idx) { + prev[cur.name] = cur.value; + return prev; + }, {}); + config.HostConfig.ExtraHosts = config.HostConfig.ExtraHosts.map(function (entry) { + return entry.host + ':' + entry.ip; + }); + + var ExposedPorts = {}; + var PortBindings = {}; + config.HostConfig.PortBindings.forEach(function (portBinding) { + var intPort = portBinding.intPort + "/tcp"; + if (portBinding.protocol === "udp") { + intPort = portBinding.intPort + "/udp"; + } + var binding = { + HostIp: portBinding.ip, + HostPort: portBinding.extPort + }; + if (portBinding.intPort) { + ExposedPorts[intPort] = {}; + if (intPort in PortBindings) { + PortBindings[intPort].push(binding); + } else { + PortBindings[intPort] = [binding]; + } + } else { + Messages.send('Warning', 'Internal port must be specified for PortBindings'); + } + }); + config.ExposedPorts = ExposedPorts; + config.HostConfig.PortBindings = PortBindings; + + // Remove empty fields from the request to avoid overriding defaults + rmEmptyKeys(config.HostConfig); + rmEmptyKeys(config); + + var ctor = Container; + var loc = $location; + var s = $scope; + Container.create(config, function (d) { + if (d.Id) { + var reqBody = config.HostConfig || {}; + reqBody.id = d.Id; + ctor.start(reqBody, function (cd) { + if (cd.id) { + Messages.send('Container Started', d.Id); + $('#create-modal').modal('hide'); + loc.path('/containers/' + d.Id + '/'); + } else { + failedRequestHandler(cd, Messages); + ctor.remove({id: d.Id}, function () { + Messages.send('Container Removed', d.Id); + }); + } + }, function (e) { + failedRequestHandler(e, Messages); + }); + } else { + failedRequestHandler(d, Messages); + } + }, function (e) { + failedRequestHandler(e, Messages); + }); + }; + + $scope.addEntry = function (array, entry) { + array.push(entry); + }; + $scope.rmEntry = function (array, entry) { + var idx = array.indexOf(entry); + array.splice(idx, 1); + }; + }]); diff --git a/app/components/startContainer/startcontainer.html b/app/components/startContainer/startcontainer.html index eaa774f88..cdfc1a6fd 100644 --- a/app/components/startContainer/startcontainer.html +++ b/app/components/startContainer/startcontainer.html @@ -6,301 +6,413 @@

Create And Start Container From Image