From ede37e2e793ade635e2429580be4d3defb339523 Mon Sep 17 00:00:00 2001 From: Houssem BELHADJ AHMED Date: Tue, 31 Mar 2015 23:37:03 +0200 Subject: [PATCH] Add Docker top feature --- app/app.js | 36 ++- app/components/container/container.html | 211 +++++++++--------- app/components/containerTop/containerTop.html | 19 ++ .../containerTop/containerTopController.js | 19 ++ app/shared/services.js | 143 +++++++----- index.html | 24 +- .../components/containerController.spec.js | 6 +- .../components/containerTopController.spec.js | 31 +++ 8 files changed, 307 insertions(+), 182 deletions(-) create mode 100644 app/components/containerTop/containerTop.html create mode 100644 app/components/containerTop/containerTopController.js create mode 100644 test/unit/app/components/containerTopController.spec.js diff --git a/app/app.js b/app/app.js index c63ff313a..e833e192f 100644 --- a/app/app.js +++ b/app/app.js @@ -1,12 +1,34 @@ -angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs']) +angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop']) .config(['$routeProvider', function ($routeProvider) { 'use strict'; - $routeProvider.when('/', {templateUrl: 'app/components/dashboard/dashboard.html', controller: 'DashboardController'}); - $routeProvider.when('/containers/', {templateUrl: 'app/components/containers/containers.html', controller: 'ContainersController'}); - $routeProvider.when('/containers/:id/', {templateUrl: 'app/components/container/container.html', controller: 'ContainerController'}); - $routeProvider.when('/containers/:id/logs/', {templateUrl: 'app/components/containerLogs/containerlogs.html', controller: 'ContainerLogsController'}); - $routeProvider.when('/images/', {templateUrl: 'app/components/images/images.html', controller: 'ImagesController'}); - $routeProvider.when('/images/:id*/', {templateUrl: 'app/components/image/image.html', controller: 'ImageController'}); + $routeProvider.when('/', { + templateUrl: 'app/components/dashboard/dashboard.html', + controller: 'DashboardController' + }); + $routeProvider.when('/containers/', { + templateUrl: 'app/components/containers/containers.html', + controller: 'ContainersController' + }); + $routeProvider.when('/containers/:id/', { + templateUrl: 'app/components/container/container.html', + controller: 'ContainerController' + }); + $routeProvider.when('/containers/:id/logs/', { + templateUrl: 'app/components/containerLogs/containerlogs.html', + controller: 'ContainerLogsController' + }); + $routeProvider.when('/containers/:id/top', { + templateUrl: 'app/components/containerTop/containerTop.html', + controller: 'ContainerTopController' + }); + $routeProvider.when('/images/', { + templateUrl: 'app/components/images/images.html', + controller: 'ImagesController' + }); + $routeProvider.when('/images/:id*/', { + templateUrl: 'app/components/image/image.html', + controller: 'ImageController' + }); $routeProvider.when('/info', {templateUrl: 'app/components/info/info.html', controller: 'InfoController'}); $routeProvider.otherwise({redirectTo: '/'}); }]) diff --git a/app/components/container/container.html b/app/components/container/container.html index a714e92cf..5d9b16401 100644 --- a/app/components/container/container.html +++ b/app/components/container/container.html @@ -1,9 +1,10 @@
- +

Container: {{ container.Name }} + ng-click="container.edit = true;">Rename +

@@ -11,116 +12,126 @@ Container: + ng-click="renameContainer()">Edit +
- - - - - + + + + +
- - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
Created:{{ container.Created }}
Path:{{ container.Path }}
Args:{{ container.Args }}
Exposed Ports: -
    -
  • {{ k }}
  • -
-
Environment: -
    -
  • {{ k }}
  • -
-
Created:{{ container.Created }}
Path:{{ container.Path }}
Args:{{ container.Args }}
Exposed Ports: +
    +
  • {{ k }}
  • +
+
Environment: +
    +
  • {{ k }}
  • +
+
Publish All:{{ container.HostConfig.PublishAllPorts }}
Ports: -
    -
  • - {{ containerport }} => {{ v.HostIp }}:{{ v.HostPort }} -
  • -
-
Publish All:{{ container.HostConfig.PublishAllPorts }}
Ports: +
    +
  • + {{ containerport }} => {{ v.HostIp }}:{{ v.HostPort }} +
  • +
+
Hostname:{{ container.Config.Hostname }}
IPAddress:{{ container.NetworkSettings.IPAddress }}
Cmd:{{ container.Config.Cmd }}
Entrypoint:{{ container.Config.Entrypoint }}
Volumes:{{ container.Volumes }}
Hostname:{{ container.Config.Hostname }}
IPAddress:{{ container.NetworkSettings.IPAddress }}
Cmd:{{ container.Config.Cmd }}
Entrypoint:{{ container.Config.Entrypoint }}
Volumes:{{ container.Volumes }}
SysInitpath:{{ container.SysInitPath }}
Image:{{ container.Image }}
State:{{ container.State|getstatetext }}
Logs:stdout/stderr
SysInitpath:{{ container.SysInitPath }}
Image:{{ container.Image }}
State:{{ container.State|getstatetext }}
Logs:stdout/stderr
Top:Top
- +
Changes: @@ -128,7 +139,7 @@
-
+
    @@ -138,7 +149,7 @@
-
+
diff --git a/app/components/containerTop/containerTop.html b/app/components/containerTop/containerTop.html new file mode 100644 index 000000000..138906581 --- /dev/null +++ b/app/components/containerTop/containerTop.html @@ -0,0 +1,19 @@ +
+
+ +
+ + + + + + + + + + + + + +
{{title}}
{{processInfo}}
+
\ No newline at end of file diff --git a/app/components/containerTop/containerTopController.js b/app/components/containerTop/containerTopController.js new file mode 100644 index 000000000..d6f500271 --- /dev/null +++ b/app/components/containerTop/containerTopController.js @@ -0,0 +1,19 @@ +angular.module('containerTop', []) + .controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, ViewSpinner) { + $scope.ps_args = ''; + + /** + * Get container processes + */ + $scope.getTop = function () { + ViewSpinner.spin(); + ContainerTop.get($routeParams.id, { + ps_args: $scope.ps_args + }, function (data) { + $scope.containerTop = data; + ViewSpinner.stop(); + }); + }; + + $scope.getTop(); + }]); \ No newline at end of file diff --git a/app/shared/services.js b/app/shared/services.js index 575a9a90e..5ed45934b 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -1,56 +1,75 @@ angular.module('dockerui.services', ['ngResource']) - .factory('Container', function($resource, Settings) { + .factory('Container', function ($resource, Settings) { 'use strict'; // Resource for interacting with the docker containers // http://docs.docker.io/en/latest/api/docker_remote_api.html#containers return $resource(Settings.url + '/containers/:id/:action', { name: '@name' }, { - query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, - get: {method: 'GET', params: { action:'json'}}, + query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true}, + get: {method: 'GET', params: {action: 'json'}}, start: {method: 'POST', params: {id: '@id', action: 'start'}}, stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}}, - restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart' }}, + restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart'}}, kill: {method: 'POST', params: {id: '@id', action: 'kill'}}, pause: {method: 'POST', params: {id: '@id', action: 'pause'}}, unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}}, - changes: {method: 'GET', params: {action:'changes'}, isArray: true}, - create: {method: 'POST', params: {action:'create'}}, - remove: {method: 'DELETE', params: {id: '@id', v:0}}, + changes: {method: 'GET', params: {action: 'changes'}, isArray: true}, + create: {method: 'POST', params: {action: 'create'}}, + remove: {method: 'DELETE', params: {id: '@id', v: 0}}, rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false} }); }) - .factory('ContainerLogs', function($resource, $http, Settings) { + .factory('ContainerLogs', function ($resource, $http, Settings) { 'use strict'; return { - get: function(id, params, callback) { + get: function (id, params, callback) { $http({ method: 'GET', - url: Settings.url + '/containers/'+id+'/logs', - params: {'stdout': params.stdout || 0, 'stderr': params.stderr || 0, 'timestamps': params.timestamps || 0, 'tail': params.tail || 'all'} - }).success(callback).error(function(data, status, headers, config) { + url: Settings.url + '/containers/' + id + '/logs', + params: { + 'stdout': params.stdout || 0, + 'stderr': params.stderr || 0, + 'timestamps': params.timestamps || 0, + 'tail': params.tail || 'all' + } + }).success(callback).error(function (data, status, headers, config) { console.log(error, data); }); } }; }) - .factory('Image', function($resource, Settings) { + .factory('ContainerTop', function ($http, Settings) { + 'use strict'; + return { + get: function (id, params, callback, errorCallback) { + $http({ + method: 'GET', + url: Settings.url + '/containers/' + id + '/top', + params: { + ps_args: params.ps_args + } + }).success(callback); + } + }; + }) + .factory('Image', function ($resource, Settings) { 'use strict'; // Resource for docker images // http://docs.docker.io/en/latest/api/docker_remote_api.html#images return $resource(Settings.url + '/images/:id/:action', {}, { - query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, - get: {method: 'GET', params: { action:'json'}}, - search: {method: 'GET', params: { action:'search'}}, - history: {method: 'GET', params: { action:'history'}, isArray: true}, - create: {method: 'POST', params: {action:'create'}}, - insert: {method: 'POST', params: {id: '@id', action:'insert'}}, - push: {method: 'POST', params: {id: '@id', action:'push'}}, - tag: {method: 'POST', params: {id: '@id', action:'tag', force: 0, repo: '@repo'}}, + query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true}, + get: {method: 'GET', params: {action: 'json'}}, + search: {method: 'GET', params: {action: 'search'}}, + history: {method: 'GET', params: {action: 'history'}, isArray: true}, + create: {method: 'POST', params: {action: 'create'}}, + insert: {method: 'POST', params: {id: '@id', action: 'insert'}}, + push: {method: 'POST', params: {id: '@id', action: 'push'}}, + tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo'}}, remove: {method: 'DELETE', params: {id: '@id'}, isArray: true} }); }) - .factory('Docker', function($resource, Settings) { + .factory('Docker', function ($resource, Settings) { 'use strict'; // Information for docker // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information @@ -58,7 +77,7 @@ angular.module('dockerui.services', ['ngResource']) get: {method: 'GET'} }); }) - .factory('Auth', function($resource, Settings) { + .factory('Auth', function ($resource, Settings) { 'use strict'; // Auto Information for docker // http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration @@ -67,7 +86,7 @@ angular.module('dockerui.services', ['ngResource']) update: {method: 'POST'} }); }) - .factory('System', function($resource, Settings) { + .factory('System', function ($resource, Settings) { 'use strict'; // System for docker // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information @@ -75,7 +94,7 @@ angular.module('dockerui.services', ['ngResource']) get: {method: 'GET'} }); }) - .factory('Settings', function(DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) { + .factory('Settings', function (DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) { 'use strict'; var url = DOCKER_ENDPOINT; if (DOCKER_PORT) { @@ -91,52 +110,56 @@ angular.module('dockerui.services', ['ngResource']) firstLoad: true }; }) - .factory('ViewSpinner', function() { + .factory('ViewSpinner', function () { 'use strict'; var spinner = new Spinner(); var target = document.getElementById('view'); return { - spin: function() { spinner.spin(target); }, - stop: function() { spinner.stop(); } + spin: function () { + spinner.spin(target); + }, + stop: function () { + spinner.stop(); + } }; }) - .factory('Messages', function($rootScope) { + .factory('Messages', function ($rootScope) { 'use strict'; return { - send: function(title, text) { + send: function (title, text) { $.gritter.add({ title: title, text: text, time: 2000, - before_open: function() { - if($('.gritter-item-wrapper').length === 3) { + before_open: function () { + if ($('.gritter-item-wrapper').length === 3) { return false; - } + } } - }); + }); }, - error: function(title, text) { + error: function (title, text) { $.gritter.add({ title: title, text: text, time: 10000, - before_open: function() { - if($('.gritter-item-wrapper').length === 4) { + before_open: function () { + if ($('.gritter-item-wrapper').length === 4) { return false; - } + } } }); } }; }) - .factory('Dockerfile', function(Settings) { + .factory('Dockerfile', function (Settings) { 'use strict'; - var url = Settings.rawUrl + '/build'; + var url = Settings.rawUrl + '/build'; return { - build: function(file, callback) { + build: function (file, callback) { var data = new FormData(); - var dockerfile = new Blob([file], { type: 'text/text' }); + var dockerfile = new Blob([file], {type: 'text/text'}); data.append('Dockerfile', dockerfile); var request = new XMLHttpRequest(); @@ -146,18 +169,18 @@ angular.module('dockerui.services', ['ngResource']) } }; }) - .factory('LineChart', function(Settings) { + .factory('LineChart', function (Settings) { 'use strict'; - var url = Settings.rawUrl + '/build'; + var url = Settings.rawUrl + '/build'; return { - build: function(id, data, getkey){ + build: function (id, data, getkey) { var chart = new Chart($(id).get(0).getContext("2d")); var map = {}; for (var i = 0; i < data.length; i++) { var c = data[i]; var key = getkey(c); - + var count = map[key]; if (count === undefined) { count = 0; @@ -176,22 +199,22 @@ angular.module('dockerui.services', ['ngResource']) data.push(map[k]); } var dataset = { - fillColor : "rgba(151,187,205,0.5)", - strokeColor : "rgba(151,187,205,1)", - pointColor : "rgba(151,187,205,1)", - pointStrokeColor : "#fff", - data : data + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,1)", + pointColor: "rgba(151,187,205,1)", + pointStrokeColor: "#fff", + data: data }; chart.Line({ - labels: labels, - datasets: [dataset] - }, - { - scaleStepWidth: 1, - pointDotRadius:1, - scaleOverride: true, - scaleSteps: labels.length - }); + labels: labels, + datasets: [dataset] + }, + { + scaleStepWidth: 1, + pointDotRadius: 1, + scaleOverride: true, + scaleSteps: labels.length + }); } }; }); diff --git a/index.html b/index.html index 8620969fa..2b63ca291 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ - + DockerUI @@ -15,9 +15,9 @@ - + @@ -36,19 +36,19 @@ - + - + -
-
+
+
-
+
-
-
-
+
+
+
- + diff --git a/test/unit/app/components/containerController.spec.js b/test/unit/app/components/containerController.spec.js index 3dcb698a0..a64643e3a 100644 --- a/test/unit/app/components/containerController.spec.js +++ b/test/unit/app/components/containerController.spec.js @@ -1,4 +1,4 @@ -describe('ContainerController', function() { +describe('ContainerController', function () { var $scope, $httpBackend, mockContainer, $routeParams; beforeEach(module('dockerui')); @@ -27,7 +27,7 @@ describe('ContainerController', function() { }); } - it("a correct create request to the Docker remote API", function () { + it("a correct rename request to the Docker remote API", function () { $routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f'; $scope.container = { @@ -41,7 +41,7 @@ describe('ContainerController', function() { var newContainerName = "newName"; expectGetContainer(); - $httpBackend.expectGET('dockerapi/containers/changes?').respond([{"Kind":1,"Path":"/docker.sock"}]); + $httpBackend.expectGET('dockerapi/containers/changes?').respond([{"Kind": 1, "Path": "/docker.sock"}]); $httpBackend.expectPOST('dockerapi/containers/' + $routeParams.id + '/rename?name=newName'). respond({ diff --git a/test/unit/app/components/containerTopController.spec.js b/test/unit/app/components/containerTopController.spec.js new file mode 100644 index 000000000..69557b382 --- /dev/null +++ b/test/unit/app/components/containerTopController.spec.js @@ -0,0 +1,31 @@ +describe("ContainerTopController", function () { + var $scope, $httpBackend, $routeParams; + + beforeEach(angular.mock.module('dockerui')); + + beforeEach(inject(function (_$rootScope_, _$httpBackend_, $controller, _$routeParams_) { + $scope = _$rootScope_.$new(); + $httpBackend = _$httpBackend_; + $routeParams = _$routeParams_; + $routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f'; + $controller('ContainerTopController', { + '$scope': $scope, + '$routeParams': $routeParams + }); + })); + + it("should test controller initialize", function () { + $httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/top?ps_args=').respond(200); + expect($scope.ps_args).toBeDefined(); + $httpBackend.flush(); + }); + + it("a correct top request to the Docker remote API", function () { + $httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=').respond(200); + $routeParams.id = '123456789123456789123456789'; + $scope.ps_args = 'aux'; + $httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=' + $scope.ps_args).respond(200); + $scope.getTop(); + $httpBackend.flush(); + }); +}); \ No newline at end of file