diff --git a/app/app.js b/app/app.js index ba41b6d47..cbd9deb47 100644 --- a/app/app.js +++ b/app/app.js @@ -12,6 +12,7 @@ angular.module('uifordocker', [ 'containers', 'createContainer', 'docker', + 'events', 'images', 'image', 'containerLogs', @@ -84,6 +85,11 @@ angular.module('uifordocker', [ templateUrl: 'app/components/docker/docker.html', controller: 'DockerController' }) + .state('events', { + url: '/events/', + templateUrl: 'app/components/events/events.html', + controller: 'EventsController' + }) .state('images', { url: '/images/', templateUrl: 'app/components/images/images.html', diff --git a/app/components/events/events.html b/app/components/events/events.html new file mode 100644 index 000000000..17764e1fb --- /dev/null +++ b/app/components/events/events.html @@ -0,0 +1,63 @@ + + + + + + + Events + + +
+
+ + +
+ +
+
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + +
+ + Date + + + + + + Category + + + + + + Details + + + +
{{ event.Time|getdatefromtimestamp }}{{ event.Type }}{{ event.Details }}
+
+
+ +
+
diff --git a/app/components/events/eventsController.js b/app/components/events/eventsController.js new file mode 100644 index 000000000..8450bdb71 --- /dev/null +++ b/app/components/events/eventsController.js @@ -0,0 +1,27 @@ +angular.module('events', []) +.controller('EventsController', ['$scope', 'Settings', 'Messages', 'Events', +function ($scope, Settings, Messages, Events) { + $scope.state = {}; + $scope.sortType = 'Time'; + $scope.sortReverse = true; + + $scope.order = function(sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; + + var from = moment().subtract(24, 'hour').unix(); + var to = moment().unix(); + + Events.query({since: from, until: to}, + function(d) { + $scope.events = d.map(function (item) { + return new EventViewModel(item); + }); + $('#loadEventsSpinner').hide(); + }, + function (e) { + Messages.error("Unable to load events", e.data); + $('#loadEventsSpinner').hide(); + }); +}]); diff --git a/app/shared/filters.js b/app/shared/filters.js index 10fe596ff..e6f096017 100644 --- a/app/shared/filters.js +++ b/app/shared/filters.js @@ -163,6 +163,12 @@ angular.module('dockerui.filters', []) return date.toDateString(); }; }) +.filter('getdatefromtimestamp', function () { + 'use strict'; + return function (timestamp) { + return moment.unix(timestamp).format('YYYY-MM-DD HH:mm:ss'); + }; +}) .filter('errorMsg', function () { return function (object) { var idx = 0; diff --git a/app/shared/services.js b/app/shared/services.js index 75d979db1..0516fd3ac 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -98,6 +98,16 @@ angular.module('dockerui.services', ['ngResource', 'ngSanitize']) inspect: {method: 'GET', params: {id: '@id', action: 'json'}} }); }]) + .factory('Events', ['$resource', 'Settings', function EventFactory($resource, Settings) { + 'use strict'; + // http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#/monitor-docker-s-events + return $resource(Settings.url + '/events', {}, { + query: {method: 'GET', params: {since: '@since', until: '@until'}, isArray: true, transformResponse: [function f(data) { + var str = "[" + data.replace(/\n/g, " ").replace(/\}\s*\{/g, "}, {") + "]"; + return angular.fromJson(str); + }]} + }); + }]) .factory('Version', ['$resource', 'Settings', function VersionFactory($resource, Settings) { 'use strict'; // http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#show-the-docker-version-information diff --git a/app/shared/viewmodel.js b/app/shared/viewmodel.js index 1b222692e..f0bf07f1e 100644 --- a/app/shared/viewmodel.js +++ b/app/shared/viewmodel.js @@ -1,22 +1,128 @@ function ImageViewModel(data) { - this.Id = data.Id; - this.Tag = data.Tag; - this.Repository = data.Repository; - this.Created = data.Created; - this.Checked = false; - this.RepoTags = data.RepoTags; - this.VirtualSize = data.VirtualSize; + this.Id = data.Id; + this.Tag = data.Tag; + this.Repository = data.Repository; + this.Created = data.Created; + this.Checked = false; + this.RepoTags = data.RepoTags; + this.VirtualSize = data.VirtualSize; } function ContainerViewModel(data) { - this.Id = data.Id; - this.Status = data.Status; - this.Names = data.Names; - // Unavailable in Docker < 1.10 - if (data.NetworkSettings) { - this.IP = data.NetworkSettings.Networks[Object.keys(data.NetworkSettings.Networks)[0]].IPAddress; + this.Id = data.Id; + this.Status = data.Status; + this.Names = data.Names; + // Unavailable in Docker < 1.10 + if (data.NetworkSettings) { + this.IP = data.NetworkSettings.Networks[Object.keys(data.NetworkSettings.Networks)[0]].IPAddress; + } + this.Image = data.Image; + this.Command = data.Command; + this.Checked = false; +} + +function EventViewModel(data) { + // Type, Action, Actor unavailable in Docker < 1.10 + this.Time = data.time; + if (data.Type) { + this.Type = data.Type; + this.Details = createEventDetails(data); + } else { + this.Type = data.status; + this.Details = data.from; + } +} + +function createEventDetails(event) { + var eventAttr = event.Actor.Attributes; + var details = ''; + switch (event.Type) { + case 'container': + switch (event.Action) { + case 'stop': + details = 'Container ' + eventAttr.name + ' stopped'; + break; + case 'destroy': + details = 'Container ' + eventAttr.name + ' deleted'; + break; + case 'create': + details = 'Container ' + eventAttr.name + ' created'; + break; + case 'start': + details = 'Container ' + eventAttr.name + ' started'; + break; + case 'kill': + details = 'Container ' + eventAttr.name + ' killed'; + break; + case 'die': + details = 'Container ' + eventAttr.name + ' exited with status code ' + eventAttr.exitCode; + break; + case 'commit': + details = 'Container ' + eventAttr.name + ' committed'; + break; + case 'restart': + details = 'Container ' + eventAttr.name + ' restarted'; + break; + case 'pause': + details = 'Container ' + eventAttr.name + ' paused'; + break; + case 'unpause': + details = 'Container ' + eventAttr.name + ' unpaused'; + break; + default: + details = 'Unsupported event'; + } + break; + case 'image': + switch (event.Action) { + case 'delete': + details = 'Image deleted'; + break; + case 'tag': + details = 'New tag created for ' + eventAttr.name; + break; + case 'untag': + details = 'Image untagged'; + break; + case 'pull': + details = 'Image ' + event.Actor.ID + ' pulled'; + break; + default: + details = 'Unsupported event'; + } + break; + case 'network': + switch (event.Action) { + case 'create': + details = 'Network ' + eventAttr.name + ' created'; + break; + case 'destroy': + details = 'Network ' + eventAttr.name + ' deleted'; + break; + case 'connect': + details = 'Container connected to ' + eventAttr.name + ' network'; + break; + case 'disconnect': + details = 'Container disconnected from ' + eventAttr.name + ' network'; + break; + default: + details = 'Unsupported event'; + } + break; + case 'volume': + switch (event.Action) { + case 'create': + details = 'Volume ' + event.Actor.ID + ' created'; + break; + case 'destroy': + details = 'Volume ' + event.Actor.ID + ' deleted'; + break; + default: + details = 'Unsupported event'; } - this.Image = data.Image; - this.Command = data.Command; - this.Checked = false; + break; + default: + details = 'Unsupported event'; + } + return details; } diff --git a/bower.json b/bower.json index 56800d37d..12974762a 100644 --- a/bower.json +++ b/bower.json @@ -30,7 +30,6 @@ "angular-ui-router": "^0.2.15", "angular-sanitize": "~1.5.0", "angular-mocks": "~1.5.0", - "angular-oboe": "*", "angular-resource": "~1.5.0", "angular-ui-select": "~0.17.1", "bootstrap": "~3.3.6", @@ -39,6 +38,7 @@ "jquery.gritter": "1.7.4", "lodash": "4.12.0", "rdash-ui": "1.0.*", + "moment": "~2.14.1" }, "resolutions": { "angular": "1.5.5" diff --git a/gruntFile.js b/gruntFile.js index 3ff9bbc31..7482b3ecb 100644 --- a/gruntFile.js +++ b/gruntFile.js @@ -68,11 +68,11 @@ module.exports = function (grunt) { jsTpl: ['<%= distdir %>/templates/**/*.js'], jsVendor: [ 'bower_components/jquery/dist/jquery.min.js', - 'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict" 'bower_components/bootstrap/dist/js/bootstrap.min.js', 'bower_components/Chart.js/Chart.min.js', 'bower_components/lodash/dist/lodash.min.js', - 'bower_components/oboe/dist/oboe-browser.js', + 'bower_components/moment/min/moment.min.js', + 'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict" 'assets/js/legend.js' // Not a bower package ], specs: ['test/**/*.spec.js'], @@ -156,7 +156,6 @@ module.exports = function (grunt) { 'bower_components/angular-ui-router/release/angular-ui-router.min.js', 'bower_components/angular-resource/angular-resource.min.js', 'bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js', - 'bower_components/angular-oboe/dist/angular-oboe.min.js', 'bower_components/angular-ui-select/dist/select.min.js'], dest: '<%= distdir %>/js/angular.js' } diff --git a/index.html b/index.html index a42f292a7..434d0f048 100644 --- a/index.html +++ b/index.html @@ -52,6 +52,9 @@ +