diff --git a/.gitignore b/.gitignore index 3df26d9c0..0294ee823 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,6 @@ logs/* node_modules bower_components .idea -dist -dockerui *.iml +dist +dist/* diff --git a/LICENSE b/LICENSE index 44a15a631..5a39d3588 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com) +UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com), Anthony Lapenna (anthonylapenna at cloudinovasi dot id) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/app/app.js b/app/app.js index 49ae54992..3bb521af6 100644 --- a/app/app.js +++ b/app/app.js @@ -1,99 +1,116 @@ angular.module('uifordocker', [ - 'uifordocker.templates', - 'ngRoute', - 'dockerui.services', - 'dockerui.filters', - 'masthead', - 'footer', - 'dashboard', - 'container', - 'containers', - 'containersNetwork', - 'images', - 'image', - 'pullImage', - 'startContainer', - 'sidebar', - 'info', - 'builder', - 'containerLogs', - 'containerTop', - 'events', - 'stats', - 'network', - 'networks', - 'volumes']) - .config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) { - 'use strict'; + 'uifordocker.templates', + 'ui.bootstrap', + 'ui.router', + 'ui.select', + 'ngCookies', + 'ngRoute', + 'ngSanitize', + 'dockerui.services', + 'dockerui.filters', + 'dashboard', + 'container', + 'containers', + 'images', + 'image', + 'pullImage', + 'startContainer', + 'containerLogs', + 'stats', + 'swarm', + 'network', + 'networks', + 'createNetwork', + 'volumes', + 'createVolume']) + .config(['$stateProvider', '$urlRouterProvider', '$httpProvider', function ($stateProvider, $urlRouterProvider, $httpProvider) { + 'use strict'; - $httpProvider.defaults.xsrfCookieName = 'csrfToken'; - $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token'; + $httpProvider.defaults.xsrfCookieName = 'csrfToken'; + $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token'; - $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('/containers/:id/stats', { - templateUrl: 'app/components/stats/stats.html', - controller: 'StatsController' - }); - $routeProvider.when('/containers_network', { - templateUrl: 'app/components/containersNetwork/containersNetwork.html', - controller: 'ContainersNetworkController' - }); - $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.when('/events', { - templateUrl: 'app/components/events/events.html', - controller: 'EventsController' - }); - $routeProvider.otherwise({redirectTo: '/'}); + $urlRouterProvider.otherwise('/'); - // The Docker API likes to return plaintext errors, this catches them and disp - $httpProvider.interceptors.push(function() { - return { - 'response': function(response) { - if (typeof(response.data) === 'string' && response.data.startsWith('Conflict.')) { - $.gritter.add({ - title: 'Error', - text: $('
').text(response.data).html(), - time: 10000 - }); - } - var csrfToken = response.headers('X-Csrf-Token'); - if (csrfToken) { - document.cookie = 'csrfToken=' + csrfToken; - } - return response; - } - }; - }); - }]) - // 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.10.1-beta'); + $stateProvider + .state('index', { + url: '/', + templateUrl: 'app/components/dashboard/dashboard.html', + controller: 'DashboardController' + }) + .state('containers', { + url: '/containers/', + templateUrl: 'app/components/containers/containers.html', + controller: 'ContainersController' + }) + .state('container', { + url: "^/containers/:id", + templateUrl: 'app/components/container/container.html', + controller: 'ContainerController' + }) + .state('stats', { + url: "^/containers/:id/stats", + templateUrl: 'app/components/stats/stats.html', + controller: 'StatsController' + }) + .state('logs', { + url: "^/containers/:id/logs", + templateUrl: 'app/components/containerLogs/containerlogs.html', + controller: 'ContainerLogsController' + }) + .state('images', { + url: '/images/', + templateUrl: 'app/components/images/images.html', + controller: 'ImagesController' + }) + .state('image', { + url: '^/images/:id/', + templateUrl: 'app/components/image/image.html', + controller: 'ImageController' + }) + .state('networks', { + url: '/networks/', + templateUrl: 'app/components/networks/networks.html', + controller: 'NetworksController' + }) + .state('network', { + url: '^/networks/:id/', + templateUrl: 'app/components/network/network.html', + controller: 'NetworkController' + }) + .state('volumes', { + url: '/volumes/', + templateUrl: 'app/components/volumes/volumes.html', + controller: 'VolumesController' + }) + .state('swarm', { + url: '/swarm/', + templateUrl: 'app/components/swarm/swarm.html', + controller: 'SwarmController' + }); + + // The Docker API likes to return plaintext errors, this catches them and disp + $httpProvider.interceptors.push(function() { + return { + 'response': function(response) { + if (typeof(response.data) === 'string' && response.data.startsWith('Conflict.')) { + $.gritter.add({ + title: 'Error', + text: $('
').text(response.data).html(), + time: 10000 + }); + } + var csrfToken = response.headers('X-Csrf-Token'); + if (csrfToken) { + document.cookie = 'csrfToken=' + csrfToken; + } + return response; + } + }; + }); + }]) + + // 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.11.0'); diff --git a/app/components/builder/builder.html b/app/components/builder/builder.html deleted file mode 100644 index 8aaf49494..000000000 --- a/app/components/builder/builder.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/app/components/builder/builderController.js b/app/components/builder/builderController.js deleted file mode 100644 index f89ce4e05..000000000 --- a/app/components/builder/builderController.js +++ /dev/null @@ -1,5 +0,0 @@ -angular.module('builder', []) - .controller('BuilderController', ['$scope', - function ($scope) { - $scope.template = 'app/components/builder/builder.html'; - }]); diff --git a/app/components/container/container.html b/app/components/container/container.html index 2078f1214..4e149c264 100644 --- a/app/components/container/container.html +++ b/app/components/container/container.html @@ -1,298 +1,186 @@ -
- -
-

Container: {{ container.Name }} - -

-
-
-

- Container: - - - -

-
- -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Created:{{ container.Created | date: 'medium' }}
Path:{{ container.Path }}
Args: -
{{ container.Args.join(' ') || 'None' }}
-
Exposed Ports: -
    -
  • {{ k }}
  • -
-
Environment: -
- -
    -
  • {{ k }}
  • -
-
-
- - -
-
-
- - -
-
- - -
-
- -
-
-
- - -
- - -
Labels: - - - - - - - - - -
KeyValue
{{ k }}{{ v }}
-
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.join(' ') }}
-
Bindings: -
- - -
    -
  • {{ b }}
  • -
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- - - -
-
Volumes:{{ container.Volumes }}
SysInitpath:{{ container.SysInitPath }}
Image:{{ container.Image }}
State: - - -
    -
  • {{key}} : {{ val }}
  • -
-
-
-
Logs:stdout/stderr
Stats:stats
Top:Top
- -
-
- Changes: +
+
+ + +
+
-
- +
+
{{ container.Name|trimcontainername }}
+
+ Name +
-
- -
-
    -
  • - {{ change.Path }} {{ change.Kind }} -
  • -
-
- -
- -
- -
+
+
+
+ Name + + +
+
+
+
+
+
+ + +
+ +
+
{{ container.State|getstatetext }}
+
State
+
+
+
+
+
+ + +
+ +
+
+
+ + + + + + + + +
+
+ Stats + Logs +
+
+
+ Actions +
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Created{{ container.Created | date: 'medium' }}
Path{{ container.Path }}
Args{{ container.Args.join(' ') || 'None' }}
Exposed Ports +
    +
  • {{ k }}
  • +
+
Environment +
    +
  • {{ k }}
  • +
+
Labels + + + + + +
{{ k }}{{ v }}
+
Publish all ports{{ 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.join(' ') }}
Bindings +
    +
  • {{ b }}
  • +
+
Volumes{{ container.Volumes }}
SysInitpath{{ container.SysInitPath }}
Image{{ container.Image }}
+
+
+
+
+ + + + + + + + + + +
{{key}}{{ val }}
+
+
+
+
diff --git a/app/components/container/containerController.js b/app/components/container/containerController.js index 08590676f..b33f1eea4 100644 --- a/app/components/container/containerController.js +++ b/app/components/container/containerController.js @@ -1,312 +1,208 @@ - angular.module('container', []) - .controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Image', 'Messages', 'ViewSpinner', '$timeout', - function ($scope, $routeParams, $location, Container, ContainerCommit, Image, Messages, ViewSpinner, $timeout) { - $scope.changes = []; - $scope.editEnv = false; - $scope.editPorts = false; - $scope.editBinds = false; - $scope.newCfg = { - Env: [], - Ports: {} - }; +angular.module('container', []) +.controller('ContainerController', ['$scope', '$stateParams', '$state', '$filter', 'Container', 'ContainerCommit', 'Image', 'Messages', 'ViewSpinner', '$timeout', +function ($scope, $stateParams, $state, $filter, Container, ContainerCommit, Image, Messages, ViewSpinner, $timeout) { + $scope.changes = []; + $scope.editEnv = false; + $scope.editPorts = false; + $scope.editBinds = false; + $scope.newCfg = { + Env: [], + Ports: {} + }; - var update = function () { - ViewSpinner.spin(); - Container.get({id: $routeParams.id}, function (d) { - $scope.container = d; - $scope.container.edit = false; - $scope.container.newContainerName = d.Name; + var update = function () { + ViewSpinner.spin(); + Container.get({id: $stateParams.id}, function (d) { + $scope.container = d; + $scope.container.edit = false; + $scope.container.newContainerName = $filter('trimcontainername')(d.Name); - // fill up env - if (d.Config.Env) { - $scope.newCfg.Env = d.Config.Env.map(function (entry) { - return {name: entry.split('=')[0], value: entry.split('=')[1]}; - }); - } + // fill up env + if (d.Config.Env) { + $scope.newCfg.Env = d.Config.Env.map(function (entry) { + return {name: entry.split('=')[0], value: entry.split('=')[1]}; + }); + } - // fill up ports - $scope.newCfg.Ports = {}; - angular.forEach(d.Config.ExposedPorts, function(i, port) { - if (d.HostConfig.PortBindings && port in d.HostConfig.PortBindings) { - $scope.newCfg.Ports[port] = d.HostConfig.PortBindings[port]; - } - else { - $scope.newCfg.Ports[port] = []; - } - }); + // fill up ports + $scope.newCfg.Ports = {}; + angular.forEach(d.Config.ExposedPorts, function(i, port) { + if (d.HostConfig.PortBindings && port in d.HostConfig.PortBindings) { + $scope.newCfg.Ports[port] = d.HostConfig.PortBindings[port]; + } + else { + $scope.newCfg.Ports[port] = []; + } + }); - // fill up bindings - $scope.newCfg.Binds = []; - var defaultBinds = {}; - angular.forEach(d.Config.Volumes, function(value, vol) { - defaultBinds[vol] = { ContPath: vol, HostPath: '', ReadOnly: false, DefaultBind: true }; - }); - angular.forEach(d.HostConfig.Binds, function(binding, i) { - var mountpoint = binding.split(':')[0]; - var vol = binding.split(':')[1] || ''; - var ro = binding.split(':').length > 2 && binding.split(':')[2] === 'ro'; - var defaultBind = false; - if (vol === '') { - vol = mountpoint; - mountpoint = ''; - } + // fill up bindings + $scope.newCfg.Binds = []; + var defaultBinds = {}; + angular.forEach(d.Config.Volumes, function(value, vol) { + defaultBinds[vol] = { ContPath: vol, HostPath: '', ReadOnly: false, DefaultBind: true }; + }); + angular.forEach(d.HostConfig.Binds, function(binding, i) { + var mountpoint = binding.split(':')[0]; + var vol = binding.split(':')[1] || ''; + var ro = binding.split(':').length > 2 && binding.split(':')[2] === 'ro'; + var defaultBind = false; + if (vol === '') { + vol = mountpoint; + mountpoint = ''; + } - if (vol in defaultBinds) { - delete defaultBinds[vol]; - defaultBind = true; - } - $scope.newCfg.Binds.push({ ContPath: vol, HostPath: mountpoint, ReadOnly: ro, DefaultBind: defaultBind }); - }); - angular.forEach(defaultBinds, function(bind) { - $scope.newCfg.Binds.push(bind); - }); + if (vol in defaultBinds) { + delete defaultBinds[vol]; + defaultBind = true; + } + $scope.newCfg.Binds.push({ ContPath: vol, HostPath: mountpoint, ReadOnly: ro, DefaultBind: defaultBind }); + }); + angular.forEach(defaultBinds, function(bind) { + $scope.newCfg.Binds.push(bind); + }); - 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(); - }); + 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", $stateParams.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: $stateParams.id}, function (d) { + update(); + Messages.send("Container stopped", $stateParams.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: $stateParams.id}, function (d) { + update(); + Messages.send("Container killed", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to die." + e.data); + }); + }; - $scope.restartEnv = function () { - var config = angular.copy($scope.container.Config); + $scope.commit = function () { + ViewSpinner.spin(); + ContainerCommit.commit({id: $stateParams.id, repo: $scope.container.Config.Image}, function (d) { + update(); + Messages.send("Container commited", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to commit." + e.data); + }); + }; + $scope.pause = function () { + ViewSpinner.spin(); + Container.pause({id: $stateParams.id}, function (d) { + update(); + Messages.send("Container paused", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to pause." + e.data); + }); + }; - config.Env = $scope.newCfg.Env.map(function(entry) { - return entry.name+"="+entry.value; - }); + $scope.unpause = function () { + ViewSpinner.spin(); + Container.unpause({id: $stateParams.id}, function (d) { + update(); + Messages.send("Container unpaused", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to unpause." + e.data); + }); + }; - var portBindings = angular.copy($scope.newCfg.Ports); - angular.forEach(portBindings, function(item, key) { - if (item.length === 0) { - delete portBindings[key]; - } - }); + $scope.remove = function () { + ViewSpinner.spin(); + Container.remove({id: $stateParams.id}, function (d) { + update(); + $state.go('containers', {}, {reload: true}); + Messages.send("Container removed", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to remove." + e.data); + }); + }; + $scope.restart = function () { + ViewSpinner.spin(); + Container.restart({id: $stateParams.id}, function (d) { + update(); + Messages.send("Container restarted", $stateParams.id); + }, function (e) { + update(); + Messages.error("Failure", "Container failed to restart." + e.data); + }); + }; - var binds = []; - angular.forEach($scope.newCfg.Binds, function(b) { - if (b.ContPath !== '') { - var bindLine = ''; - if (b.HostPath !== '') { - bindLine = b.HostPath + ':'; - } - bindLine += b.ContPath; - if (b.ReadOnly) { - bindLine += ':ro'; - } - if (b.HostPath !== '' || !b.DefaultBind) { - binds.push(bindLine); - } - } - }); + $scope.hasContent = function (data) { + return data !== null && data !== undefined; + }; + $scope.getChanges = function () { + ViewSpinner.spin(); + Container.changes({id: $stateParams.id}, function (d) { + $scope.changes = d; + ViewSpinner.stop(); + }); + }; - ViewSpinner.spin(); - ContainerCommit.commit({id: $routeParams.id, tag: $scope.container.Config.Image, config: config }, function (d) { - if ('Id' in d) { - var imageId = d.Id; - Image.inspect({id: imageId}, function(imageData) { - // Append current host config to image with new port bindings - imageData.Config.HostConfig = angular.copy($scope.container.HostConfig); - imageData.Config.HostConfig.PortBindings = portBindings; - imageData.Config.HostConfig.Binds = binds; - if (imageData.Config.HostConfig.NetworkMode === 'host') { - imageData.Config.Hostname = ''; - } + $scope.renameContainer = function () { + // #FIXME fix me later to handle http status to show the correct error message + Container.rename({id: $stateParams.id, 'name': $scope.container.newContainerName}, function (data) { + if (data.name) { + $scope.container.Name = data.name; + Messages.send("Container renamed", $stateParams.id); + } else { + $scope.container.newContainerName = $scope.container.Name; + Messages.error("Failure", "Container failed to rename."); + } + }); + $scope.container.edit = false; + }; - Container.create(imageData.Config, function(containerData) { - if (!('Id' in containerData)) { - Messages.error("Failure", "Container failed to create."); - return; - } - // Stop current if running - if ($scope.container.State.Running) { - Container.stop({id: $routeParams.id}, function (d) { - Messages.send("Container stopped", $routeParams.id); - // start new - Container.start({ - id: containerData.Id - }, function (d) { - $location.url('/containers/' + containerData.Id + '/'); - Messages.send("Container started", $routeParams.id); - }, function (e) { - update(); - Messages.error("Failure", "Container failed to start." + e.data); - }); - }, function (e) { - update(); - Messages.error("Failure", "Container failed to stop." + e.data); - }); - } else { - // start new - Container.start({ - id: containerData.Id - }, function (d) { - $location.url('/containers/'+containerData.Id+'/'); - Messages.send("Container started", $routeParams.id); - }, function (e) { - update(); - Messages.error("Failure", "Container failed to start." + e.data); - }); - } + $scope.addEntry = function (array, entry) { + array.push(entry); + }; + $scope.rmEntry = function (array, entry) { + var idx = array.indexOf(entry); + array.splice(idx, 1); + }; - }, function(e) { - update(); - Messages.error("Failure", "Image failed to get." + e.data); - }); - }, function (e) { - update(); - Messages.error("Failure", "Image failed to get." + e.data); - }); - - } else { - update(); - Messages.error("Failure", "Container commit failed."); - } - - - }, function (e) { - update(); - Messages.error("Failure", "Container failed to commit." + 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.remove = function () { - ViewSpinner.spin(); - Container.remove({id: $routeParams.id}, function (d) { - update(); - $location.path('/containers'); - 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.addEntry = function (array, entry) { - array.push(entry); - }; - $scope.rmEntry = function (array, entry) { - var idx = array.indexOf(entry); - array.splice(idx, 1); - }; - - $scope.toggleEdit = function() { - $scope.edit = !$scope.edit; - }; - - update(); - $scope.getChanges(); - }]); + $scope.toggleEdit = function() { + $scope.edit = !$scope.edit; + }; + update(); + $scope.getChanges(); +}]); diff --git a/app/components/containerLogs/containerLogsController.js b/app/components/containerLogs/containerLogsController.js index 27977f11d..0f1280792 100644 --- a/app/components/containerLogs/containerLogsController.js +++ b/app/components/containerLogs/containerLogsController.js @@ -1,76 +1,79 @@ 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', '$stateParams', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner', +function ($scope, $stateParams, $anchorScroll, ContainerLogs, Container, ViewSpinner) { + $scope.state = {}; + $scope.state.displayTimestampsOut = false; + $scope.state.displayTimestampsErr = false; + $scope.stdout = ''; + $scope.stderr = ''; + $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: $stateParams.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(); + getLogsStdout(); + getLogsStderr(); + 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(); - }); - } + function getLogsStderr() { + ContainerLogs.get($stateParams.id, { + stdout: 0, + stderr: 1, + timestamps: $scope.state.displayTimestampsErr, + 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; + }); + } - // initial call - getLogs(); - var logIntervalId = window.setInterval(getLogs, 5000); + function getLogsStdout() { + ContainerLogs.get($stateParams.id, { + stdout: 1, + stderr: 0, + timestamps: $scope.state.displayTimestampsOut, + 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; + }); + } - $scope.$on("$destroy", function () { - // clearing interval when view changes - clearInterval(logIntervalId); - }); + // initial call + getLogs(); + var logIntervalId = window.setInterval(getLogs, 5000); - $scope.scrollTo = function (id) { - $location.hash(id); - $anchorScroll(); - }; + $scope.$on("$destroy", function () { + // clearing interval when view changes + clearInterval(logIntervalId); + }); - $scope.toggleTimestamps = function () { - getLogs(); - }; + $scope.toggleTimestampsOut = function () { + getLogsStdout(); + }; - $scope.toggleTail = function () { - getLogs(); - }; - }]); + $scope.toggleTimestampsErr = function () { + getLogsStderr(); + }; +}]); diff --git a/app/components/containerLogs/containerlogs.html b/app/components/containerLogs/containerlogs.html index 406d04e66..0a20d5ddc 100644 --- a/app/components/containerLogs/containerlogs.html +++ b/app/components/containerLogs/containerlogs.html @@ -1,43 +1,47 @@ -
-
-

Logs for container: {{ container.Name }}

- -
- - +
+
+ + +
+
-
-
- Reload logs - - -
-
- -
-
-
- -
-
-
-

STDOUT

-
-
-
{{stdout}}
-
-
-
-
-
-
-

STDERR

-
-
-
{{stderr}}
-
-
-
+
{{ container.Name|trimcontainername }}
+
Name
+ + +
+
+ +
+
+ + + + + + + +
+
{{stdout}}
+
+
+
+
+
+ +
+
+ + + + + + + +
+
{{stderr}}
+
+
+
+
diff --git a/app/components/containerTop/containerTop.html b/app/components/containerTop/containerTop.html deleted file mode 100644 index 4db3ca5ce..000000000 --- a/app/components/containerTop/containerTop.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
-
-

Top for: {{ containerName }}

-
-
-
-
- -
- -
-
-
- - - - - - - - - - - -
{{title}}
{{processInfo}}
-
-
-
\ No newline at end of file diff --git a/app/components/containerTop/containerTopController.js b/app/components/containerTop/containerTopController.js deleted file mode 100644 index fe658c7bf..000000000 --- a/app/components/containerTop/containerTopController.js +++ /dev/null @@ -1,25 +0,0 @@ -angular.module('containerTop', []) - .controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'Container', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, Container, 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(); - }); - }; - - Container.get({id: $routeParams.id}, function (d) { - $scope.containerName = d.Name.substring(1); - }, function (e) { - Messages.error("Failure", e.data); - }); - - $scope.getTop(); - }]); \ No newline at end of file diff --git a/app/components/containers/containers.html b/app/components/containers/containers.html index 5e66102ad..8d3c847f5 100644 --- a/app/components/containers/containers.html +++ b/app/components/containers/containers.html @@ -1,76 +1,82 @@ +
-

Containers:

- -
- - -
-   - -
-
- - - - - + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+
+
+ + +
+
+ +
+ + + + + - + - + - + - + - - - - - - - - - - - - -
- Name - - + Name + + - + - Image - - + Image + + - + - Command - - + Command + + - + - Created - - + Created + + - + - Status - - + Status + + -
{{ container|containername}}{{ container.Image }}{{ container.Command|truncate:40 }}{{ container.Created|getdate }}{{ container.Status }}
+
{{ container|containername}}{{ container.Image }}{{ container.Command|truncate:40 }}{{ container.Created|getdate }}{{ container.Status }}
+
+ + +
diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js index 02f7dbc88..0672cda7f 100644 --- a/app/components/containers/containersController.js +++ b/app/components/containers/containersController.js @@ -1,118 +1,138 @@ angular.module('containers', []) - .controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner', - function ($scope, Container, Settings, Messages, ViewSpinner) { - $scope.sortType = 'Created'; - $scope.sortReverse = true; - $scope.toggle = false; - $scope.displayAll = Settings.displayAll; +.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner', +function ($scope, Container, Settings, Messages, ViewSpinner) { - $scope.order = function (sortType) { - $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; - $scope.sortType = sortType; - }; + $scope.state = {}; + $scope.state.displayAll = Settings.displayAll; + $scope.sortType = 'Created'; + $scope.sortReverse = true; + $scope.state.toggle = false; + $scope.state.selectedItemCount = 0; - var update = function (data) { - ViewSpinner.spin(); - Container.query(data, function (d) { - $scope.containers = d.map(function (item) { - return new ContainerViewModel(item); - }); - ViewSpinner.stop(); - }); - }; + $scope.order = function (sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; - 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(); - }); + var update = function (data) { + ViewSpinner.spin(); + $scope.state.selectedItemCount = 0; + Container.query(data, function (d) { + $scope.containers = d.filter(function (container) { + return container.Image !== 'swarm'; + }).map(function (container) { + return new ContainerViewModel(container); + }); + 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(); - } - }; + } - $scope.toggleSelectAll = function () { - angular.forEach($scope.filteredContainers, 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.selectItem = function (item) { + if (item.Checked) { + $scope.state.selectedItemCount++; + } else { + $scope.state.selectedItemCount--; + } + }; - $scope.startAction = function () { - batch($scope.containers, Container.start, "Started"); - }; + $scope.toggleSelectAll = function () { + $scope.state.selectedItem = $scope.state.toggle; + angular.forEach($scope.state.filteredContainers, function (i) { + i.Checked = $scope.state.toggle; + }); + if ($scope.state.toggle) { + $scope.state.selectedItemCount = $scope.state.filteredContainers.length; + } else { + $scope.state.selectedItemCount = 0; + } + }; - $scope.stopAction = function () { - batch($scope.containers, Container.stop, "Stopped"); - }; + $scope.toggleGetAll = function () { + Settings.displayAll = $scope.state.displayAll; + update({all: Settings.displayAll ? 1 : 0}); + }; - $scope.restartAction = function () { - batch($scope.containers, Container.restart, "Restarted"); - }; + $scope.startAction = function () { + batch($scope.containers, Container.start, "Started"); + }; - $scope.killAction = function () { - batch($scope.containers, Container.kill, "Killed"); - }; + $scope.stopAction = function () { + batch($scope.containers, Container.stop, "Stopped"); + }; - $scope.pauseAction = function () { - batch($scope.containers, Container.pause, "Paused"); - }; + $scope.restartAction = function () { + batch($scope.containers, Container.restart, "Restarted"); + }; - $scope.unpauseAction = function () { - batch($scope.containers, Container.unpause, "Unpaused"); - }; + $scope.killAction = function () { + batch($scope.containers, Container.kill, "Killed"); + }; - $scope.removeAction = function () { - batch($scope.containers, Container.remove, "Removed"); - }; + $scope.pauseAction = function () { + batch($scope.containers, Container.pause, "Paused"); + }; - update({all: Settings.displayAll ? 1 : 0}); - }]); + $scope.unpauseAction = function () { + batch($scope.containers, Container.unpause, "Unpaused"); + }; + + $scope.removeAction = function () { + batch($scope.containers, Container.remove, "Removed"); + }; + + update({all: Settings.displayAll ? 1 : 0}); +}]); diff --git a/app/components/containersNetwork/containersNetwork.html b/app/components/containersNetwork/containersNetwork.html deleted file mode 100644 index ba265e880..000000000 --- a/app/components/containersNetwork/containersNetwork.html +++ /dev/null @@ -1,25 +0,0 @@ -
-

Containers Network

- -
-
- - -
-
-
-
- - - - -
- -
-
- -
-
diff --git a/app/components/containersNetwork/containersNetworkController.js b/app/components/containersNetwork/containersNetworkController.js deleted file mode 100644 index 26546afb2..000000000 --- a/app/components/containersNetwork/containersNetworkController.js +++ /dev/null @@ -1,271 +0,0 @@ -angular.module('containersNetwork', ['ngVis']) - .controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function ($scope, $location, Container, Messages, VisDataSet) { - - function ContainerNode(data) { - this.Id = data.Id; - // names have the following format: /Name - this.Name = data.Name.substring(1); - this.Image = data.Config.Image; - this.Running = data.State.Running; - var dataLinks = data.HostConfig.Links; - if (dataLinks != null) { - this.Links = {}; - for (var i = 0; i < dataLinks.length; i++) { - // links have the following format: /TargetContainerName:/SourceContainerName/LinkAlias - var link = dataLinks[i].split(":"); - var target = link[0].substring(1); - var alias = link[1].substring(link[1].lastIndexOf("/") + 1); - // only keep shortest alias - if (this.Links[target] == null || alias.length < this.Links[target].length) { - this.Links[target] = alias; - } - } - } - var dataVolumes = data.HostConfig.VolumesFrom; - //converting array into properties for simpler and faster access - if (dataVolumes != null) { - this.VolumesFrom = {}; - for (var j = 0; j < dataVolumes.length; j++) { - this.VolumesFrom[dataVolumes[j]] = true; - } - } - } - - function ContainersNetworkData() { - this.nodes = new VisDataSet(); - this.edges = new VisDataSet(); - - this.addContainerNode = function (container) { - this.nodes.add({ - id: container.Id, - label: container.Name, - title: "
    " + - "
  • ID: " + container.Id + "
  • " + - "
  • Image: " + container.Image + "
  • " + - "
", - 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 = ''; - }); - }, - 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.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.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.shownContainersIds.indexOf(containerId) > -1) { - this.data.removeContainersNodes(containerId); - } - } - 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.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.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); - }; - - 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/createNetwork/createNetwork.html b/app/components/createNetwork/createNetwork.html new file mode 100644 index 000000000..6c317a9f9 --- /dev/null +++ b/app/components/createNetwork/createNetwork.html @@ -0,0 +1,45 @@ + diff --git a/app/components/createNetwork/createNetworkController.js b/app/components/createNetwork/createNetworkController.js new file mode 100644 index 000000000..a0e56882e --- /dev/null +++ b/app/components/createNetwork/createNetworkController.js @@ -0,0 +1,41 @@ +angular.module('createNetwork', []) +.controller('CreateNetworkController', ['$scope', '$state', 'Messages', 'Network', 'ViewSpinner', 'errorMsgFilter', +function ($scope, $state, Messages, Network, ViewSpinner, errorMsgFilter) { + $scope.template = 'app/components/createNetwork/createNetwork.html'; + + $scope.init = function () { + $scope.createNetworkConfig = { + "Name": '', + "Driver": '', + "IPAM": { + "Config": [{}] + } + }; + }; + + $scope.init(); + + $scope.createNetwork = function addNetwork(createNetworkConfig) { + if (_.isEmpty(createNetworkConfig.IPAM.Config[0])) { + delete createNetworkConfig.IPAM; + } + $('#error-message').hide(); + ViewSpinner.spin(); + $('#create-network-modal').modal('hide'); + Network.create(createNetworkConfig, function (d) { + if (d.Id) { + Messages.send("Network created", d.Id); + } else { + Messages.error('Failure', errorMsgFilter(d)); + } + ViewSpinner.stop(); + $scope.init(); + $state.go('networks', {}, {reload: true}); + }, function (e) { + ViewSpinner.stop(); + $scope.error = "Cannot pull image " + imageName + " Reason: " + e.data; + $('#create-network-modal').modal('show'); + $('#error-message').show(); + }); + }; +}]); diff --git a/app/components/createVolume/createVolume.html b/app/components/createVolume/createVolume.html new file mode 100644 index 000000000..7e6a5987c --- /dev/null +++ b/app/components/createVolume/createVolume.html @@ -0,0 +1,40 @@ + diff --git a/app/components/createVolume/createVolumeController.js b/app/components/createVolume/createVolumeController.js new file mode 100644 index 000000000..9193b9f71 --- /dev/null +++ b/app/components/createVolume/createVolumeController.js @@ -0,0 +1,39 @@ +angular.module('createVolume', []) +.controller('CreateVolumeController', ['$scope', '$state', 'Messages', 'Volume', 'ViewSpinner', 'errorMsgFilter', +function ($scope, $state, Messages, Volume, ViewSpinner, errorMsgFilter) { + $scope.template = 'app/components/createVolume/createVolume.html'; + + $scope.init = function () { + $scope.createVolumeConfig = { + "Name": "", + "Driver": "", + "DriverOpts": {} + }; + $scope.availableDrivers = ['local', 'local-persist']; + $scope.selectedDriver = { value: $scope.availableDrivers[0] }; + }; + + $scope.init(); + + $scope.addVolume = function addVolume(createVolumeConfig) { + $('#error-message').hide(); + ViewSpinner.spin(); + $('#create-volume-modal').modal('hide'); + createVolumeConfig.Driver = $scope.selectedDriver.value; + console.log(JSON.stringify(createVolumeConfig, null, 4)); + Volume.create(createVolumeConfig, function (d) { + if (d.Name) { + Messages.send("Volume created", d.Name); + } else { + Messages.error('Failure', errorMsgFilter(d)); + } + ViewSpinner.stop(); + $state.go('volumes', {}, {reload: true}); + }, function (e) { + ViewSpinner.stop(); + $scope.error = "Cannot create volume " + createVolumeConfig.Name + " Reason: " + e.data; + $('#create-volume-modal').modal('show'); + $('#error-message').show(); + }); + }; +}]); diff --git a/app/components/dashboard/dashboard.html b/app/components/dashboard/dashboard.html index 769d743fa..de814476a 100644 --- a/app/components/dashboard/dashboard.html +++ b/app/components/dashboard/dashboard.html @@ -1,52 +1,71 @@ -
- -
- diff --git a/app/components/images/imagesController.js b/app/components/images/imagesController.js index 3f68dbae0..113e7d718 100644 --- a/app/components/images/imagesController.js +++ b/app/components/images/imagesController.js @@ -1,60 +1,75 @@ angular.module('images', []) - .controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages', - function ($scope, Image, ViewSpinner, Messages) { - $scope.sortType = 'Created'; - $scope.sortReverse = true; - $scope.toggle = false; +.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages', +function ($scope, Image, ViewSpinner, Messages) { + $scope.state = {}; + $scope.sortType = 'Created'; + $scope.sortReverse = true; + $scope.state.toggle = false; + $scope.state.selectedItemCount = 0; - $scope.order = function(sortType) { - $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; - $scope.sortType = sortType; - }; + $scope.order = function(sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; - $scope.showBuilder = function () { - $('#build-modal').modal('show'); - }; + $scope.toggleSelectAll = function () { + angular.forEach($scope.state.filteredImages, function (i) { + i.Checked = $scope.state.toggle; + }); + if ($scope.state.toggle) { + $scope.state.selectedItemCount = $scope.state.filteredImages.length; + } else { + $scope.state.selectedItemCount = 0; + } + }; - $scope.removeAction = function () { - ViewSpinner.spin(); - var counter = 0; - var complete = function () { - counter = counter - 1; - if (counter === 0) { - ViewSpinner.stop(); - } - }; - angular.forEach($scope.images, function (i) { - if (i.Checked) { - counter = counter + 1; - Image.remove({id: i.Id}, function (d) { - angular.forEach(d, function (resource) { - Messages.send("Image deleted", resource.Deleted); - }); - var index = $scope.images.indexOf(i); - $scope.images.splice(index, 1); - complete(); - }, function (e) { - Messages.error("Failure", e.data); - complete(); - }); - } - }); - }; + $scope.selectItem = function (item) { + if (item.Checked) { + $scope.state.selectedItemCount++; + } else { + $scope.state.selectedItemCount--; + } + }; - $scope.toggleSelectAll = function () { - angular.forEach($scope.filteredImages, function (i) { - i.Checked = $scope.toggle; - }); - }; + $scope.removeAction = function () { + ViewSpinner.spin(); + var counter = 0; + var complete = function () { + counter = counter - 1; + if (counter === 0) { + ViewSpinner.stop(); + } + }; + angular.forEach($scope.images, function (i) { + if (i.Checked) { + counter = counter + 1; + Image.remove({id: i.Id}, function (d) { + angular.forEach(d, function (resource) { + Messages.send("Image deleted", resource.Deleted); + }); + var index = $scope.images.indexOf(i); + $scope.images.splice(index, 1); + complete(); + }, function (e) { + Messages.error("Failure", e.data); + complete(); + }); + } + }); + }; - ViewSpinner.spin(); - Image.query({}, function (d) { - $scope.images = d.map(function (item) { - return new ImageViewModel(item); - }); - ViewSpinner.stop(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - }]); + function fetchImages() { + ViewSpinner.spin(); + Image.query({}, function (d) { + $scope.images = d.map(function (item) { + return new ImageViewModel(item); + }); + ViewSpinner.stop(); + }, function (e) { + Messages.error("Failure", e.data); + ViewSpinner.stop(); + }); + } + + fetchImages(); +}]); diff --git a/app/components/info/info.html b/app/components/info/info.html deleted file mode 100644 index 7c335d6c8..000000000 --- a/app/components/info/info.html +++ /dev/null @@ -1,110 +0,0 @@ -
-

Docker Information

- -
-

- API Endpoint: {{ endpoint }}
- API Version: {{ docker.ApiVersion }}
- Docker version: {{ docker.Version }}
- Git Commit: {{ docker.GitCommit }}
- Go Version: {{ docker.GoVersion }}
-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Containers:{{ info.Containers }}
Images:{{ info.Images }}
Debug:{{ info.Debug }}
CPUs:{{ info.NCPU }}
Total Memory:{{ info.MemTotal|humansize }}
Operating System:{{ info.OperatingSystem }}
Kernel Version:{{ info.KernelVersion }}
ID:{{ info.ID }}
Labels:{{ info.Labels }}
File Descriptors:{{ info.NFd }}
Goroutines:{{ info.NGoroutines }}
Storage Driver:{{ info.Driver }}
Storage Driver Status: -

- {{ val[0] }}: {{ val[1] }} -

-
Execution Driver:{{ info.ExecutionDriver }}
Events:Events
IPv4 Forwarding:{{ info.IPv4Forwarding }}
Index Server Address:{{ info.IndexServerAddress }}
Init Path:{{ info.InitPath }}
Docker Root Directory:{{ info.DockerRootDir }}
Init SHA1{{ info.InitSha1 }}
Memory Limit:{{ info.MemoryLimit }}
Swap Limit:{{ info.SwapLimit }}
-
diff --git a/app/components/info/infoController.js b/app/components/info/infoController.js deleted file mode 100644 index 794e03e13..000000000 --- a/app/components/info/infoController.js +++ /dev/null @@ -1,14 +0,0 @@ -angular.module('info', []) - .controller('InfoController', ['$scope', 'Info', 'Version', 'Settings', - function ($scope, Info, Version, Settings) { - $scope.info = {}; - $scope.docker = {}; - $scope.endpoint = Settings.endpoint; - - Version.get({}, function (d) { - $scope.docker = d; - }); - Info.get({}, function (d) { - $scope.info = d; - }); - }]); diff --git a/app/components/masthead/masthead.html b/app/components/masthead/masthead.html deleted file mode 100644 index 09cdcf93d..000000000 --- a/app/components/masthead/masthead.html +++ /dev/null @@ -1,21 +0,0 @@ -
-

UI For Docker

- - -
- -
-
diff --git a/app/components/masthead/mastheadController.js b/app/components/masthead/mastheadController.js deleted file mode 100644 index 579ef6e5a..000000000 --- a/app/components/masthead/mastheadController.js +++ /dev/null @@ -1,15 +0,0 @@ -angular.module('masthead', []) - .controller('MastheadController', ['$scope', 'Version', function ($scope, Version) { - $scope.template = 'app/components/masthead/masthead.html'; - $scope.showNetworksVolumes = false; - - Version.get(function(d) { - if (d.ApiVersion >= 1.21) { - $scope.showNetworksVolumes = true; - } - }); - - $scope.refresh = function() { - location.reload(); - }; - }]); diff --git a/app/components/network/network.html b/app/components/network/network.html index 46412b329..d8e38e719 100644 --- a/app/components/network/network.html +++ b/app/components/network/network.html @@ -1,110 +1,120 @@ -
- -

Network: {{ network.Name }}

- - - - - - - - - - - - - - - - - - - - - - + + +
Name:{{ network.Name }}
Id:{{ network.Id }}
Scope:{{ network.Scope }}
Driver:{{ network.Driver }}
IPAM: +
+
+ + +
+ +
+
{{ network.Name }}
+
Name
+
+
+
+
+ + +
+ +
+
+
+ + +
+
+
+ Actions +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - -
Id{{ network.Id }}
Scope{{ network.Scope }}
Driver{{ network.Driver }}
IPAM - - - - - - - - - - - - + + + + + + + + + + + +
Driver:{{ network.IPAM.Driver }}
Subnet:{{ network.IPAM.Config[0].Subnet }}
Gateway:{{ network.IPAM.Config[0].Gateway }}
Driver{{ network.IPAM.Driver }}
Subnet{{ network.IPAM.Config[0].Subnet }}
Gateway{{ network.IPAM.Config[0].Gateway }}
-
Containers: +
Containers - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + +
Id:{{ Id }} - -
EndpointID:{{ container.EndpointID}}
MacAddress:{{ container.MacAddress}}
IPv4Address:{{ container.IPv4Address}}
IPv6Address:{{ container.IPv6Address}}
Id{{ Id }}
EndpointID{{ container.EndpointID}}
MacAddress{{ container.MacAddress}}
IPv4Address{{ container.IPv4Address}}
IPv6Address{{ container.IPv6Address}}
+ +
-
-
- -
- -
-
Options: +
Options - - - - - - - - + + + +
KeyValue
{{ k }}{{ v }}
{{ k }}{{ v }}
-
- - -
- - -
- -
-
\ No newline at end of file +
+ + +
+
diff --git a/app/components/network/networkController.js b/app/components/network/networkController.js index cb3cca7d4..19af7874f 100644 --- a/app/components/network/networkController.js +++ b/app/components/network/networkController.js @@ -1,56 +1,37 @@ angular.module('network', []).config(['$routeProvider', function ($routeProvider) { - $routeProvider.when('/networks/:id/', { - templateUrl: 'app/components/network/network.html', - controller: 'NetworkController' +}]).controller('NetworkController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$state', '$stateParams', 'errorMsgFilter', +function ($scope, Network, ViewSpinner, Messages, $state, $stateParams, errorMsgFilter) { + + $scope.disconnect = function disconnect(networkId, containerId) { + ViewSpinner.spin(); + Network.disconnect({id: $stateParams.id}, {Container: containerId}, function (d) { + ViewSpinner.stop(); + Messages.send("Container disconnected", containerId); + $state.go('network', {id: $stateParams.id}, {reload: true}); + }, function (e) { + ViewSpinner.stop(); + Messages.error("Failure", e.data); }); -}]).controller('NetworkController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$routeParams', '$location', 'errorMsgFilter', - function ($scope, Network, ViewSpinner, Messages, $routeParams, $location, errorMsgFilter) { + }; - $scope.disconnect = function disconnect(networkId, containerId) { - ViewSpinner.spin(); - Network.disconnect({id: $routeParams.id}, {Container: containerId}, function (d) { - ViewSpinner.stop(); - Messages.send("Container disconnected", containerId); - $location.path('/networks/' + $routeParams.id); // Refresh the current page. - }, function (e) { - ViewSpinner.stop(); - Messages.error("Failure", e.data); - }); - }; - $scope.connect = function connect(networkId, containerId) { - ViewSpinner.spin(); - Network.connect({id: $routeParams.id}, {Container: containerId}, function (d) { - ViewSpinner.stop(); - var errmsg = errorMsgFilter(d); - if (errmsg) { - Messages.error('Error', errmsg); - } else { - Messages.send("Container connected", d); - } - $location.path('/networks/' + $routeParams.id); // Refresh the current page. - }, function (e) { - ViewSpinner.stop(); - Messages.error("Failure", e.data); - }); - }; - $scope.remove = function remove(networkId) { - ViewSpinner.spin(); - Network.remove({id: $routeParams.id}, function (d) { - ViewSpinner.stop(); - Messages.send("Network removed", d); - $location.path('/networks'); // Go to the networks page - }, function (e) { - ViewSpinner.stop(); - Messages.error("Failure", e.data); - }); - }; + $scope.remove = function remove(networkId) { + ViewSpinner.spin(); + Network.remove({id: $stateParams.id}, function (d) { + ViewSpinner.stop(); + Messages.send("Network removed", ""); + $state.go('networks', {}); + }, function (e) { + ViewSpinner.stop(); + Messages.error("Failure", e.data); + }); + }; - ViewSpinner.spin(); - Network.get({id: $routeParams.id}, function (d) { - $scope.network = d; - ViewSpinner.stop(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - }]); + ViewSpinner.spin(); + Network.get({id: $stateParams.id}, function (d) { + $scope.network = d; + ViewSpinner.stop(); + }, function (e) { + Messages.error("Failure", e.data); + ViewSpinner.stop(); + }); +}]); diff --git a/app/components/networks/networks.html b/app/components/networks/networks.html index 8a85a0dc8..fb8fb9e2e 100644 --- a/app/components/networks/networks.html +++ b/app/components/networks/networks.html @@ -1,121 +1,91 @@ -

Networks:

+
-
- - -
- -
+
+ + + + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + + + + + Id + + + + + + Scope + + + + + + Driver + + + + + + IPAM Driver + + + + + + IPAM Subnet + + + + + + IPAM Gateway + + + +
{{ network.Name|truncate:20}}{{ network.Id }}{{ network.Scope }}{{ network.Driver }}{{ network.IPAM.Driver }}{{ network.IPAM.Config[0].Subnet }}{{ network.IPAM.Config[0].Gateway }}
+
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - - - - - Id - - - - - - Scope - - - - - - Driver - - - - - - IPAM Driver - - - - - - IPAM Subnet - - - - - - IPAM Gateway - - - -
{{ network.Name|truncate:20}}{{ network.Id }}{{ network.Scope }}{{ network.Driver }}{{ network.IPAM.Driver }}{{ network.IPAM.Config[0].Subnet }}{{ network.IPAM.Config[0].Gateway }}
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
\ No newline at end of file diff --git a/app/components/networks/networksController.js b/app/components/networks/networksController.js index 6afd3a167..c3e8dbc8b 100644 --- a/app/components/networks/networksController.js +++ b/app/components/networks/networksController.js @@ -1,87 +1,71 @@ -angular.module('networks', []).config(['$routeProvider', function ($routeProvider) { - $routeProvider.when('/networks/', { - templateUrl: 'app/components/networks/networks.html', - controller: 'NetworksController' +angular.module('networks', []) +.controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', 'errorMsgFilter', +function ($scope, Network, ViewSpinner, Messages, errorMsgFilter) { + + $scope.state = {}; + $scope.state.toggle = false; + $scope.state.selectedItemCount = 0; + $scope.sortType = 'Name'; + $scope.sortReverse = true; + + $scope.order = function(sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; + + $scope.toggleSelectAll = function () { + angular.forEach($scope.state.filteredNetworks, function (i) { + i.Checked = $scope.state.toggle; }); -}]).controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter', - function ($scope, Network, ViewSpinner, Messages, $route, errorMsgFilter) { - $scope.sortType = 'Name'; - $scope.sortReverse = true; - $scope.toggle = false; - $scope.order = function(sortType) { - $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; - $scope.sortType = sortType; - }; - $scope.createNetworkConfig = { - "Name": '', - "Driver": '', - "IPAM": { - "Config": [{ - "Subnet": '', - "IPRange": '', - "Gateway": '' - }] - } - }; + if ($scope.state.toggle) { + $scope.state.selectedItemCount = $scope.state.filteredNetworks.length; + } else { + $scope.state.selectedItemCount = 0; + } + }; + $scope.selectItem = function (item) { + if (item.Checked) { + $scope.state.selectedItemCount++; + } else { + $scope.state.selectedItemCount--; + } + }; + $scope.removeAction = function () { + ViewSpinner.spin(); + var counter = 0; + var complete = function () { + counter = counter - 1; + if (counter === 0) { + ViewSpinner.stop(); + } + }; + angular.forEach($scope.networks, function (network) { + if (network.Checked) { + counter = counter + 1; + Network.remove({id: network.Id}, function (d) { + Messages.send("Network deleted", network.Id); + var index = $scope.networks.indexOf(network); + $scope.networks.splice(index, 1); + complete(); + }, function (e) { + Messages.error("Failure", e.data); + complete(); + }); + } + }); + }; - $scope.removeAction = function () { - ViewSpinner.spin(); - var counter = 0; - var complete = function () { - counter = counter - 1; - if (counter === 0) { - ViewSpinner.stop(); - } - }; - angular.forEach($scope.networks, function (network) { - if (network.Checked) { - counter = counter + 1; - Network.remove({id: network.Id}, function (d) { - Messages.send("Network deleted", network.Id); - var index = $scope.networks.indexOf(network); - $scope.networks.splice(index, 1); - complete(); - }, function (e) { - Messages.error("Failure", e.data); - complete(); - }); - } - }); - }; - - $scope.toggleSelectAll = function () { - angular.forEach($scope.filteredNetworks, function (i) { - i.Checked = $scope.toggle; - }); - }; - - $scope.addNetwork = function addNetwork(createNetworkConfig) { - ViewSpinner.spin(); - Network.create(createNetworkConfig, function (d) { - if (d.Id) { - Messages.send("Network created", d.Id); - } else { - Messages.error('Failure', errorMsgFilter(d)); - } - ViewSpinner.stop(); - fetchNetworks(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - }; - - function fetchNetworks() { - ViewSpinner.spin(); - Network.query({}, function (d) { - $scope.networks = d; - ViewSpinner.stop(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - } - fetchNetworks(); - }]); + function fetchNetworks() { + ViewSpinner.spin(); + Network.query({}, function (d) { + $scope.networks = d; + ViewSpinner.stop(); + }, function (e) { + Messages.error("Failure", e.data); + ViewSpinner.stop(); + }); + } + fetchNetworks(); +}]); diff --git a/app/components/pullImage/pullImage.html b/app/components/pullImage/pullImage.html index 39e1cca52..777a6390e 100644 --- a/app/components/pullImage/pullImage.html +++ b/app/components/pullImage/pullImage.html @@ -1,44 +1,35 @@
- + + +
@@ -164,15 +167,15 @@ placeholder="value"/>
- + + +
- + + +
@@ -187,14 +190,14 @@
- + + +
- + + +
@@ -203,14 +206,14 @@
- + + +
- + + +
@@ -219,14 +222,14 @@
- + + +
- + + +
@@ -235,16 +238,14 @@
- + + +
- + + +
@@ -253,14 +254,14 @@
- + + +
- + + +
@@ -269,14 +270,14 @@
- + + +
- + + +
@@ -304,16 +305,14 @@ ng-options="name for name in containerNames track by name" class="form-control"> - + + +
- + + +
@@ -346,16 +345,15 @@ placeholder="127.0.0.1"/>
- + + +
- + + +
@@ -373,16 +371,15 @@ placeholder="docker"/>
- + + +
- + + +
@@ -398,15 +395,14 @@ - + + +
- + + +
@@ -426,16 +422,14 @@ - + + +
- + + + diff --git a/app/components/stats/stats.html b/app/components/stats/stats.html index 864940432..b36e99e84 100644 --- a/app/components/stats/stats.html +++ b/app/components/stats/stats.html @@ -1,67 +1,67 @@
-
-

Stats for: {{ containerName }}

- -

CPU

- -
-
- -
+
+ + +
+
- -

Memory

- -
-
- -
-
- - - - - - - - - - - - - -
Max usage{{ data.memory_stats.max_usage | humansize }}
Limit{{ data.memory_stats.limit | humansize }}
Fail count{{ data.memory_stats.failcnt }}
- - - - - - - -
{{ key }}{{ value }}
-
-
-
+
{{ containerName }}
+
+ Name
- -

Network {{ networkName}}

-
-
- -
-
-
- - - - - - - -
{{ key }}{{ value }}
-
-
-
-
-
+
+
+
+
+ +
+
+ + + + + + +
+
+ + + + + + +
+
+ +
+
+ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + +
{{title}}
{{processInfo}}
+
+
+
diff --git a/app/components/stats/statsController.js b/app/components/stats/statsController.js index 057378812..9adddeacd 100644 --- a/app/components/stats/statsController.js +++ b/app/components/stats/statsController.js @@ -1,182 +1,194 @@ angular.module('stats', []) - .controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', '$routeParams', 'humansizeFilter', '$sce', function (Settings, $scope, Messages, $timeout, Container, $routeParams, humansizeFilter, $sce) { - // TODO: Force scale to 0-100 for cpu, fix charts on dashboard, - // TODO: Force memory scale to 0 - max memory +.controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', 'ContainerTop', '$stateParams', 'humansizeFilter', '$sce', '$document', +function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateParams, humansizeFilter, $sce, $document) { + // TODO: Force scale to 0-100 for cpu, fix charts on dashboard, + // TODO: Force memory scale to 0 - max memory + $scope.ps_args = ''; + $scope.getTop = function () { + ContainerTop.get($stateParams.id, { + ps_args: $scope.ps_args + }, function (data) { + $scope.containerTop = data; + }); + }; + $document.ready(function(){ + var cpuLabels = []; + var cpuData = []; + var memoryLabels = []; + var memoryData = []; + var networkLabels = []; + var networkTxData = []; + var networkRxData = []; + for (var i = 0; i < 20; i++) { + cpuLabels.push(''); + cpuData.push(0); + memoryLabels.push(''); + memoryData.push(0); + networkLabels.push(''); + networkTxData.push(0); + networkRxData.push(0); + } + var cpuDataset = { // CPU Usage + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,1)", + pointColor: "rgba(151,187,205,1)", + pointStrokeColor: "#fff", + data: cpuData + }; + var memoryDataset = { + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,1)", + pointColor: "rgba(151,187,205,1)", + pointStrokeColor: "#fff", + data: memoryData + }; + var networkRxDataset = { + label: "Rx Bytes", + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,1)", + pointColor: "rgba(151,187,205,1)", + pointStrokeColor: "#fff", + data: networkRxData + }; + var networkTxDataset = { + label: "Tx Bytes", + fillColor: "rgba(255,180,174,0.5)", + strokeColor: "rgba(255,180,174,1)", + pointColor: "rgba(255,180,174,1)", + pointStrokeColor: "#fff", + data: networkTxData + }; + var networkLegendData = [ + { + //value: '', + color: 'rgba(151,187,205,0.5)', + title: 'Rx Data' + }, + { + //value: '', + color: 'rgba(255,180,174,0.5)', + title: 'Tx Data' + } + ]; - var cpuLabels = []; - var cpuData = []; - var memoryLabels = []; - var memoryData = []; - var networkLabels = []; - var networkTxData = []; - var networkRxData = []; - for (var i = 0; i < 20; i++) { - cpuLabels.push(''); - cpuData.push(0); - memoryLabels.push(''); - memoryData.push(0); - networkLabels.push(''); - networkTxData.push(0); - networkRxData.push(0); - } - var cpuDataset = { // CPU Usage - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,1)", - pointColor: "rgba(151,187,205,1)", - pointStrokeColor: "#fff", - data: cpuData - }; - var memoryDataset = { - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,1)", - pointColor: "rgba(151,187,205,1)", - pointStrokeColor: "#fff", - data: memoryData - }; - var networkRxDataset = { - label: "Rx Bytes", - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,1)", - pointColor: "rgba(151,187,205,1)", - pointStrokeColor: "#fff", - data: networkRxData - }; - var networkTxDataset = { - label: "Tx Bytes", - fillColor: "rgba(255,180,174,0.5)", - strokeColor: "rgba(255,180,174,1)", - pointColor: "rgba(255,180,174,1)", - pointStrokeColor: "#fff", - data: networkTxData - }; - var networkLegendData = [ - { - //value: '', - color: 'rgba(151,187,205,0.5)', - title: 'Rx Data' - }, - { - //value: '', - color: 'rgba(255,180,174,0.5)', - title: 'Rx Data' - }]; - legend($('#network-legend').get(0), networkLegendData); + legend($('#network-legend').get(0), networkLegendData); - Chart.defaults.global.animationSteps = 30; // Lower from 60 to ease CPU load. - var cpuChart = new Chart($('#cpu-stats-chart').get(0).getContext("2d")).Line({ - labels: cpuLabels, - datasets: [cpuDataset] - }, { - responsive: true + Chart.defaults.global.animationSteps = 30; // Lower from 60 to ease CPU load. + var cpuChart = new Chart($('#cpu-stats-chart').get(0).getContext("2d")).Line({ + labels: cpuLabels, + datasets: [cpuDataset] + }, { + responsive: true + }); + + var memoryChart = new Chart($('#memory-stats-chart').get(0).getContext('2d')).Line({ + labels: memoryLabels, + datasets: [memoryDataset] + }, + { + scaleLabel: function (valueObj) { + return humansizeFilter(parseInt(valueObj.value, 10)); + }, + responsive: true + //scaleOverride: true, + //scaleSteps: 10, + //scaleStepWidth: Math.ceil(initialStats.memory_stats.limit / 10), + //scaleStartValue: 0 + }); + var networkChart = new Chart($('#network-stats-chart').get(0).getContext("2d")).Line({ + labels: networkLabels, + datasets: [networkRxDataset, networkTxDataset] + }, { + scaleLabel: function (valueObj) { + return humansizeFilter(parseInt(valueObj.value, 10)); + }, + responsive: true + }); + $scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend()); + + function updateStats() { + Container.stats({id: $stateParams.id}, function (d) { + var arr = Object.keys(d).map(function (key) { + return d[key]; }); - - var memoryChart = new Chart($('#memory-stats-chart').get(0).getContext('2d')).Line({ - labels: memoryLabels, - datasets: [memoryDataset] - }, - { - scaleLabel: function (valueObj) { - return humansizeFilter(parseInt(valueObj.value, 10)); - }, - responsive: true - //scaleOverride: true, - //scaleSteps: 10, - //scaleStepWidth: Math.ceil(initialStats.memory_stats.limit / 10), - //scaleStartValue: 0 - }); - var networkChart = new Chart($('#network-stats-chart').get(0).getContext("2d")).Line({ - labels: networkLabels, - datasets: [networkRxDataset, networkTxDataset] - }, { - scaleLabel: function (valueObj) { - return humansizeFilter(parseInt(valueObj.value, 10)); - }, - responsive: true - }); - $scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend()); - - function updateStats() { - Container.stats({id: $routeParams.id}, function (d) { - var arr = Object.keys(d).map(function (key) { - return d[key]; - }); - if (arr.join('').indexOf('no such id') !== -1) { - Messages.error('Unable to retrieve stats', 'Is this container running?'); - return; - } - - // Update graph with latest data - $scope.data = d; - updateCpuChart(d); - updateMemoryChart(d); - updateNetworkChart(d); - timeout = $timeout(updateStats, 5000); - }, function () { - Messages.error('Unable to retrieve stats', 'Is this container running?'); - timeout = $timeout(updateStats, 5000); - }); + if (arr.join('').indexOf('no such id') !== -1) { + Messages.error('Unable to retrieve stats', 'Is this container running?'); + return; } - var timeout; - $scope.$on('$destroy', function () { - $timeout.cancel(timeout); - }); + // Update graph with latest data + $scope.data = d; + updateCpuChart(d); + updateMemoryChart(d); + updateNetworkChart(d); + timeout = $timeout(updateStats, 5000); + }, function () { + Messages.error('Unable to retrieve stats', 'Is this container running?'); + timeout = $timeout(updateStats, 5000); + }); + } - updateStats(); + var timeout; + $scope.$on('$destroy', function () { + $timeout.cancel(timeout); + }); - function updateCpuChart(data) { - cpuChart.addData([calculateCPUPercent(data)], new Date(data.read).toLocaleTimeString()); - cpuChart.removeData(); - } + updateStats(); - function updateMemoryChart(data) { - memoryChart.addData([data.memory_stats.usage], new Date(data.read).toLocaleTimeString()); - memoryChart.removeData(); - } + function updateCpuChart(data) { + cpuChart.addData([calculateCPUPercent(data)], new Date(data.read).toLocaleTimeString()); + cpuChart.removeData(); + } - var lastRxBytes = 0, lastTxBytes = 0; + function updateMemoryChart(data) { + memoryChart.addData([data.memory_stats.usage], new Date(data.read).toLocaleTimeString()); + memoryChart.removeData(); + } - function updateNetworkChart(data) { - // 1.9+ contains an object of networks, for now we'll just show stats for the first network - // TODO: Show graphs for all networks - if (data.networks) { - $scope.networkName = Object.keys(data.networks)[0]; - data.network = data.networks[$scope.networkName]; - } - var rxBytes = 0, txBytes = 0; - if (lastRxBytes !== 0 || lastTxBytes !== 0) { - // These will be zero on first call, ignore to prevent large graph spike - rxBytes = data.network.rx_bytes - lastRxBytes; - txBytes = data.network.tx_bytes - lastTxBytes; - } - lastRxBytes = data.network.rx_bytes; - lastTxBytes = data.network.tx_bytes; - networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString()); - networkChart.removeData(); - } + var lastRxBytes = 0, lastTxBytes = 0; - function calculateCPUPercent(stats) { - // Same algorithm the official client uses: https://github.com/docker/docker/blob/master/api/client/stats.go#L195-L208 - var prevCpu = stats.precpu_stats; - var curCpu = stats.cpu_stats; + function updateNetworkChart(data) { + // 1.9+ contains an object of networks, for now we'll just show stats for the first network + // TODO: Show graphs for all networks + if (data.networks) { + $scope.networkName = Object.keys(data.networks)[0]; + data.network = data.networks[$scope.networkName]; + } + var rxBytes = 0, txBytes = 0; + if (lastRxBytes !== 0 || lastTxBytes !== 0) { + // These will be zero on first call, ignore to prevent large graph spike + rxBytes = data.network.rx_bytes - lastRxBytes; + txBytes = data.network.tx_bytes - lastTxBytes; + } + lastRxBytes = data.network.rx_bytes; + lastTxBytes = data.network.tx_bytes; + networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString()); + networkChart.removeData(); + } - var cpuPercent = 0.0; + function calculateCPUPercent(stats) { + // Same algorithm the official client uses: https://github.com/docker/docker/blob/master/api/client/stats.go#L195-L208 + var prevCpu = stats.precpu_stats; + var curCpu = stats.cpu_stats; - // calculate the change for the cpu usage of the container in between readings - var cpuDelta = curCpu.cpu_usage.total_usage - prevCpu.cpu_usage.total_usage; - // calculate the change for the entire system between readings - var systemDelta = curCpu.system_cpu_usage - prevCpu.system_cpu_usage; + var cpuPercent = 0.0; - if (systemDelta > 0.0 && cpuDelta > 0.0) { - cpuPercent = (cpuDelta / systemDelta) * curCpu.cpu_usage.percpu_usage.length * 100.0; - } - return cpuPercent; - } + // calculate the change for the cpu usage of the container in between readings + var cpuDelta = curCpu.cpu_usage.total_usage - prevCpu.cpu_usage.total_usage; + // calculate the change for the entire system between readings + var systemDelta = curCpu.system_cpu_usage - prevCpu.system_cpu_usage; - Container.get({id: $routeParams.id}, function (d) { - $scope.containerName = d.Name.substring(1); - }, function (e) { - Messages.error("Failure", e.data); - }); - }]) -; \ No newline at end of file + if (systemDelta > 0.0 && cpuDelta > 0.0) { + cpuPercent = (cpuDelta / systemDelta) * curCpu.cpu_usage.percpu_usage.length * 100.0; + } + return cpuPercent; + } + }); + + Container.get({id: $stateParams.id}, function (d) { + $scope.containerName = d.Name.substring(1); + }, function (e) { + Messages.error("Failure", e.data); + }); + $scope.getTop(); +}]); diff --git a/app/components/swarm/swarm.html b/app/components/swarm/swarm.html new file mode 100644 index 000000000..fd968c88d --- /dev/null +++ b/app/components/swarm/swarm.html @@ -0,0 +1,130 @@ +
+
+ + +
+ +
+
{{ docker.Version }}
+
Swarm version
+
+
+
+
+ + +
+ +
+
{{ docker.ApiVersion }}
+
API version
+
+
+
+
+ + +
+ +
+
{{ docker.GoVersion }}
+
Go version
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nodes{{ swarm.Nodes }}
Containers{{ info.Containers }}
Images{{ info.Images }}
Strategy{{ swarm.Strategy }}
CPUs{{ info.NCPU }}
Total Memory{{ info.MemTotal|humansize }}
Operating System{{ info.OperatingSystem }}
Kernel Version{{ info.KernelVersion }}
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + Name + + + + + + IP + + + + + + Containers + + + + + + Status + + + +
{{ node.name }}{{ node.ip }}{{ node.containers }}{{ node.status }}
+
+
+
+
diff --git a/app/components/swarm/swarmController.js b/app/components/swarm/swarmController.js new file mode 100644 index 000000000..3734383da --- /dev/null +++ b/app/components/swarm/swarmController.js @@ -0,0 +1,62 @@ +angular.module('swarm', []) + .controller('SwarmController', ['$scope', 'Info', 'Version', 'Settings', + function ($scope, Info, Version, Settings) { + + $scope.sortType = 'Name'; + $scope.sortReverse = true; + $scope.info = {}; + $scope.docker = {}; + $scope.swarm = {}; + + $scope.order = function(sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; + + Version.get({}, function (d) { + $scope.docker = d; + }); + Info.get({}, function (d) { + $scope.info = d; + extractSwarmInfo(d); + }); + + function extractSwarmInfo(info) { + // Swarm info is available in SystemStatus object + var systemStatus = info.SystemStatus; + // Swarm strategy + $scope.swarm[systemStatus[1][0]] = systemStatus[1][1]; + // Swarm filters + $scope.swarm[systemStatus[2][0]] = systemStatus[2][1]; + // Swarm node count + var node_count = parseInt(systemStatus[3][1], 10); + $scope.swarm[systemStatus[3][0]] = node_count; + + $scope.swarm.Status = []; + extractNodesInfo(systemStatus, node_count); + } + + function extractNodesInfo(info, node_count) { + // First information for node1 available at element #4 of SystemStatus + // The next 10 elements are information related to the node + var node_offset = 4; + for (i = 0; i < node_count; i++) { + extractNodeInfo(info, node_offset); + node_offset += 9; + } + } + + function extractNodeInfo(info, offset) { + var node = {}; + node.name = info[offset][0]; + node.ip = info[offset][1]; + node.status = info[offset + 1][1]; + node.containers = info[offset + 2][1]; + node.cpu = info[offset + 3][1]; + node.memory = info[offset + 4][1]; + node.labels = info[offset + 5][1]; + node.error = info[offset + 6][1]; + node.version = info[offset + 8][1]; + $scope.swarm.Status.push(node); + } + }]); diff --git a/app/components/volumes/volumes.html b/app/components/volumes/volumes.html index 10e4aa642..1f0f4eb1d 100644 --- a/app/components/volumes/volumes.html +++ b/app/components/volumes/volumes.html @@ -1,74 +1,59 @@ -

Volumes:

+
-
- - -
- -
+
+ + + + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
+ + Name + + + + + + Driver + + + + + + Mountpoint + + + +
{{ volume.Name|truncate:20 }}{{ volume.Driver }}{{ volume.Mountpoint }}
+
+
+
- - - - - - - - - - - - - - - - - -
- - Name - - - - - - Driver - - - - - - Mountpoint - - - -
{{ volume.Name|truncate:20 }}{{ volume.Driver }}{{ volume.Mountpoint }}
-
-
- -
- - -
-
- - -
- - -
-
\ No newline at end of file diff --git a/app/components/volumes/volumesController.js b/app/components/volumes/volumesController.js index c5ae29091..f7db7f797 100644 --- a/app/components/volumes/volumesController.js +++ b/app/components/volumes/volumesController.js @@ -1,80 +1,70 @@ -angular.module('volumes', []).config(['$routeProvider', function ($routeProvider) { - $routeProvider.when('/volumes/', { - templateUrl: 'app/components/volumes/volumes.html', - controller: 'VolumesController' +angular.module('volumes', []) +.controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter', +function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) { + $scope.state = {}; + $scope.state.toggle = false; + $scope.state.selectedItemCount = 0; + $scope.sortType = 'Name'; + $scope.sortReverse = true; + + $scope.order = function(sortType) { + $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; + $scope.sortType = sortType; + }; + + $scope.toggleSelectAll = function () { + angular.forEach($scope.state.filteredVolumes, function (i) { + i.Checked = $scope.state.toggle; }); -}]).controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter', - function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) { - $scope.sortType = 'Name'; - $scope.sortReverse = true; - $scope.toggle = false; - $scope.order = function(sortType) { - $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; - $scope.sortType = sortType; - }; - $scope.createVolumeConfig = { - "Name": "", - "Driver": "" - }; + if ($scope.state.toggle) { + $scope.state.selectedItemCount = $scope.state.filteredVolumes.length; + } else { + $scope.state.selectedItemCount = 0; + } + }; + $scope.selectItem = function (item) { + if (item.Checked) { + $scope.state.selectedItemCount++; + } else { + $scope.state.selectedItemCount--; + } + }; + $scope.removeAction = function () { + ViewSpinner.spin(); + var counter = 0; + var complete = function () { + counter = counter - 1; + if (counter === 0) { + ViewSpinner.stop(); + } + }; + angular.forEach($scope.volumes, function (volume) { + if (volume.Checked) { + counter = counter + 1; + Volume.remove({name: volume.Name}, function (d) { + Messages.send("Volume deleted", volume.Name); + var index = $scope.volumes.indexOf(volume); + $scope.volumes.splice(index, 1); + complete(); + }, function (e) { + Messages.error("Failure", e.data); + complete(); + }); + } + }); + }; - $scope.removeAction = function () { - ViewSpinner.spin(); - var counter = 0; - var complete = function () { - counter = counter - 1; - if (counter === 0) { - ViewSpinner.stop(); - } - }; - angular.forEach($scope.volumes, function (volume) { - if (volume.Checked) { - counter = counter + 1; - Volume.remove({name: volume.Name}, function (d) { - Messages.send("Volume deleted", volume.Name); - var index = $scope.volumes.indexOf(volume); - $scope.volumes.splice(index, 1); - complete(); - }, function (e) { - Messages.error("Failure", e.data); - complete(); - }); - } - }); - }; - - $scope.toggleSelectAll = function () { - angular.forEach($scope.filteredVolumes, function (i) { - i.Checked = $scope.toggle; - }); - }; - - $scope.addVolume = function addVolume(createVolumeConfig) { - ViewSpinner.spin(); - Volume.create(createVolumeConfig, function (d) { - if (d.Name) { - Messages.send("Volume created", d.Name); - } else { - Messages.error('Failure', errorMsgFilter(d)); - } - ViewSpinner.stop(); - fetchVolumes(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - }; - - function fetchVolumes() { - ViewSpinner.spin(); - Volume.query({}, function (d) { - $scope.volumes = d.Volumes; - ViewSpinner.stop(); - }, function (e) { - Messages.error("Failure", e.data); - ViewSpinner.stop(); - }); - } - fetchVolumes(); - }]); + function fetchVolumes() { + ViewSpinner.spin(); + Volume.query({}, function (d) { + $scope.volumes = d.Volumes; + ViewSpinner.stop(); + }, function (e) { + Messages.error("Failure", e.data); + ViewSpinner.stop(); + }); + } + fetchVolumes(); +}]); diff --git a/app/directives/loading.js b/app/directives/loading.js new file mode 100644 index 000000000..b645f870c --- /dev/null +++ b/app/directives/loading.js @@ -0,0 +1,9 @@ +angular +.module('uifordocker') +.directive('rdLoading', function rdLoading() { + var directive = { + restrict: 'AE', + template: '
' + }; + return directive; +}); diff --git a/app/directives/widget-body.js b/app/directives/widget-body.js new file mode 100644 index 000000000..1f7cfc8a5 --- /dev/null +++ b/app/directives/widget-body.js @@ -0,0 +1,15 @@ +angular +.module('uifordocker') +.directive('rdWidgetBody', function rdWidgetBody() { + var directive = { + requires: '^rdWidget', + scope: { + loading: '@?', + classes: '@?' + }, + transclude: true, + template: '
', + restrict: 'E' + }; + return directive; +}); diff --git a/app/directives/widget-footer.js b/app/directives/widget-footer.js new file mode 100644 index 000000000..c4e5b9a49 --- /dev/null +++ b/app/directives/widget-footer.js @@ -0,0 +1,11 @@ +angular +.module('uifordocker') +.directive('rdWidgetFooter', function rdWidgetFooter() { + var directive = { + requires: '^rdWidget', + transclude: true, + template: '', + restrict: 'E' + }; + return directive; +}); diff --git a/app/directives/widget-header.js b/app/directives/widget-header.js new file mode 100644 index 000000000..ec1cb2efc --- /dev/null +++ b/app/directives/widget-header.js @@ -0,0 +1,15 @@ +angular +.module('uifordocker') +.directive('rdWidgetHeader', function rdWidgetTitle() { + var directive = { + requires: '^rdWidget', + scope: { + title: '@', + icon: '@' + }, + transclude: true, + template: '
{{title}}
', + restrict: 'E' + }; + return directive; +}); diff --git a/app/directives/widget-taskbar.js b/app/directives/widget-taskbar.js new file mode 100644 index 000000000..2a0f7ebda --- /dev/null +++ b/app/directives/widget-taskbar.js @@ -0,0 +1,14 @@ +angular +.module('uifordocker') +.directive('rdWidgetTaskbar', function rdWidgetTaskbar() { + var directive = { + requires: '^rdWidget', + scope: { + classes: '@?' + }, + transclude: true, + template: '
', + restrict: 'E' + }; + return directive; +}); diff --git a/app/directives/widget.js b/app/directives/widget.js new file mode 100644 index 000000000..77c1f53cf --- /dev/null +++ b/app/directives/widget.js @@ -0,0 +1,13 @@ +angular +.module('uifordocker') +.directive('rdWidget', function rdWidget() { + var directive = { + scope: { + "ngModel": "=" + }, + transclude: true, + template: '
', + restrict: 'EA' + }; + return directive; +}); diff --git a/app/shared/filters.js b/app/shared/filters.js index 894d7542f..daf5bda64 100644 --- a/app/shared/filters.js +++ b/app/shared/filters.js @@ -23,12 +23,23 @@ angular.module('dockerui.filters', []) return function (text) { if (text === 'Ghost') { return 'important'; + } else if (text === 'Unhealthy') { + return 'danger'; } else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') { return 'warning'; } return 'success'; }; }) + .filter('trimcontainername', function () { + 'use strict'; + return function (name) { + if (name) { + return (name.indexOf('/') === 0 ? name.replace('/','') : name); + } + return ''; + }; + }) .filter('getstatetext', function () { 'use strict'; return function (state) { diff --git a/assets/css/app.css b/assets/css/app.css index da63a9c10..8c629650b 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -114,3 +114,28 @@ cursor: pointer; } +.logo { + display: inline; + width: 110px; + max-height: 38px; +} + +.containerNameInput { + width: 85%; + border:none; + background:none; + border-bottom: 1px solid black; +} + +.containerNameInput:active, .containerNameInput:focus { + outline:none; +} + +#network-legend { + text-align: center; +} + +#network-legend span { + display: inline; + font-size: 18px; +} diff --git a/assets/ico/apple-touch-icon-precomposed.png b/assets/ico/apple-touch-icon-precomposed.png index d326ea52b..074c906a8 100644 Binary files a/assets/ico/apple-touch-icon-precomposed.png and b/assets/ico/apple-touch-icon-precomposed.png differ diff --git a/assets/ico/favicon.ico b/assets/ico/favicon.ico index 99f65e463..074c906a8 100644 Binary files a/assets/ico/favicon.ico and b/assets/ico/favicon.ico differ diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 000000000..eb7fcc7aa Binary files /dev/null and b/assets/images/logo.png differ diff --git a/bower.json b/bower.json index e23d91f33..d74d9a590 100644 --- a/bower.json +++ b/bower.json @@ -23,20 +23,24 @@ ], "dependencies": { "Chart.js": "1.0.2", - "angular": "1.3.15", - "angular-sanitize": "1.3.15", - "angular-bootstrap": "0.12.0", - "angular-mocks": "1.3.15", + "angular": "~1.5.0", + "angular-cookies": "~1.5.0", + "angular-bootstrap": "~1.0.3", + "angular-ui-router": "^0.2.15", + "angular-sanitize": "~1.5.0", + "angular-mocks": "~1.5.0", "angular-oboe": "*", - "angular-resource": "1.3.15", - "angular-route": "1.3.15", - "angular-visjs": "0.0.7", - "bootstrap": "3.3.0", + "angular-resource": "~1.5.0", + "angular-route": "~1.5.0", + "bootstrap": "~3.3.6", + "font-awesome": "~4.5.0", "jquery": "1.11.1", "jquery.gritter": "1.7.4", + "lodash": "4.12.0", + "rdash-ui": "1.0.*", "spin.js": "1.3" }, "resolutions": { - "angular": "1.3.15" + "angular": "1.5.5" } } diff --git a/dist/ui-for-docker b/dist/ui-for-docker new file mode 100755 index 000000000..651310bb2 Binary files /dev/null and b/dist/ui-for-docker differ diff --git a/dockerui-checksum.txt b/dockerui-checksum.txt new file mode 100644 index 000000000..59f4c3570 --- /dev/null +++ b/dockerui-checksum.txt @@ -0,0 +1 @@ +965f7ce53139cf9e75e5b8a8206a4af5791eb8f8 dockerui diff --git a/examples/nginx-basic-auth/Dockerfile b/examples/nginx-basic-auth/Dockerfile index 3871e7779..505a3951c 100644 --- a/examples/nginx-basic-auth/Dockerfile +++ b/examples/nginx-basic-auth/Dockerfile @@ -1,4 +1,4 @@ -FROM nginx:1.9.9 +FROM nginx:latest COPY default.conf /etc/nginx/conf.d/default.conf COPY users.htpasswd /etc/nginx/users.htpasswd diff --git a/examples/nginx-basic-auth/docker-compose.yml b/examples/nginx-basic-auth/docker-compose.yml index 793e69360..aef0d3c79 100644 --- a/examples/nginx-basic-auth/docker-compose.yml +++ b/examples/nginx-basic-auth/docker-compose.yml @@ -1,8 +1,6 @@ dockerui: - image: dockerui/dockerui - privileged: true - volumes: - - /var/run/docker.sock:/var/run/docker.sock + image: cloudinovasi/ui-for-docker + command: -e http://: nginx: build: . diff --git a/examples/swarm/Dockerfile b/examples/swarm/Dockerfile deleted file mode 100644 index 9d6bbe583..000000000 --- a/examples/swarm/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM debian - -RUN apt-get update && apt-get install -y socat diff --git a/examples/swarm/README.md b/examples/swarm/README.md deleted file mode 100644 index 196dd3b7e..000000000 --- a/examples/swarm/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# UI For Docker with Swarm - -This example works with swarm clusters created with docker-machine. - -## Usage - -Make sure your client is pointed directly to the Docker daemon on the swarm-master's node (not through swarm). - -``` -docker-compose up -d -``` diff --git a/examples/swarm/docker-compose.yml b/examples/swarm/docker-compose.yml deleted file mode 100644 index 1f08ffcdb..000000000 --- a/examples/swarm/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -dockerui: - image: uifd/ui-for-docker - command: -e http://127.0.0.1:2375 - net: "host" - -socat: - build: . - net: "host" - command: socat -d -d TCP-L:2375,fork,bind=localhost ssl:127.0.0.1:3376,cert=/var/lib/boot2docker/server.pem,cafile=/var/lib/boot2docker/ca.pem,key=/var/lib/boot2docker/server-key.pem - volumes: - - /var/lib/boot2docker:/var/lib/boot2docker diff --git a/gruntFile.js b/gruntFile.js index 83a7cc978..e14a558e7 100644 --- a/gruntFile.js +++ b/gruntFile.js @@ -29,7 +29,7 @@ module.exports = function (grunt) { 'html2js', 'uglify', 'clean:tmpl', - //'jshint', + 'jshint', //'karma:unit', 'concat:index', 'recess:min', @@ -37,8 +37,9 @@ module.exports = function (grunt) { ]); grunt.registerTask('test-watch', ['karma:watch']); grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']); - grunt.registerTask('runSwarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm']); + grunt.registerTask('runSwarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm', 'watch:buildSwarm']); grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']); + grunt.registerTask('clear', ['clean:app']); // Print a timestamp (useful for when watching) grunt.registerTask('timestamp', function () { @@ -68,8 +69,8 @@ module.exports = function (grunt) { 'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict" 'bower_components/bootstrap/dist/js/bootstrap.js', 'bower_components/spin.js/spin.js', - 'bower_components/vis/dist/vis.js', 'bower_components/Chart.js/Chart.js', + 'bower_components/lodash/dist/lodash.js', 'bower_components/oboe/dist/oboe-browser.js', 'assets/js/legend.js' // Not a bower package ], @@ -81,7 +82,9 @@ module.exports = function (grunt) { cssVendor: [ 'bower_components/bootstrap/dist/css/bootstrap.css', 'bower_components/jquery.gritter/css/jquery.gritter.css', - 'bower_components/vis/dist/vis.css' + 'bower_components/font-awesome/css/font-awesome.min.css', + 'bower_components/rdash-ui/dist/css/rdash.css', + 'bower_components/angular-ui-select/dist/select.css' ] }, clean: { @@ -92,7 +95,9 @@ module.exports = function (grunt) { copy: { assets: { files: [ - {dest: '<%= distdir %>/fonts/', src: '**', expand: true, cwd: 'bower_components/bootstrap/fonts/'}, + {dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/bootstrap/fonts/'}, + {dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/font-awesome/fonts/'}, + {dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/rdash-ui/dist/fonts/'}, { dest: '<%= distdir %>/images/', src: ['**', '!trees.jpg'], @@ -100,18 +105,10 @@ module.exports = function (grunt) { cwd: 'bower_components/jquery.gritter/images/' }, { - dest: '<%= distdir %>/img', - src: [ - 'network/downArrow.png', - 'network/leftArrow.png', - 'network/upArrow.png', - 'network/rightArrow.png', - 'network/minus.png', - 'network/plus.png', - 'network/zoomExtends.png' - ], + dest: '<%= distdir %>/images/', + src: ['**'], expand: true, - cwd: 'bower_components/vis/dist/img' + cwd: 'assets/images/' }, {dest: '<%= distdir %>/ico', src: '**', expand: true, cwd: 'assets/ico'} ] @@ -154,11 +151,13 @@ module.exports = function (grunt) { angular: { src: ['bower_components/angular/angular.js', 'bower_components/angular-sanitize/angular-sanitize.js', + 'bower_components/angular-cookies/angular-cookies.js', 'bower_components/angular-route/angular-route.js', + 'bower_components/angular-ui-router/release/angular-ui-router.js', 'bower_components/angular-resource/angular-resource.js', 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js', 'bower_components/angular-oboe/dist/angular-oboe.js', - 'bower_components/angular-visjs/angular-vis.js'], + 'bower_components/angular-ui-select/dist/select.js'], dest: '<%= distdir %>/angular.js' } }, @@ -222,6 +221,10 @@ module.exports = function (grunt) { * Tried using a host volume with -v, copying files with `docker cp`, restating container, none worked * Rebuilding image on each change was only method that worked, takes ~4s per change to update */ + }, + buildSwarm: { + files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'], + tasks: ['build', 'shell:buildImage', 'shell:runSwarm', 'shell:cleanImages'] } }, jshint: { @@ -265,7 +268,7 @@ module.exports = function (grunt) { command: [ 'docker stop ui-for-docker', 'docker rm ui-for-docker', - 'docker run --net=host -d --name ui-for-docker ui-for-docker -e http://127.0.0.1:2374' + 'docker run --net=host -d --name ui-for-docker ui-for-docker -e http://10.0.7.11:4000' ].join(';') }, cleanImages: { diff --git a/index.html b/index.html index 693b0e345..dc7d64edd 100644 --- a/index.html +++ b/index.html @@ -1,41 +1,92 @@ - - UI For Docker - - - + + CloudInovasi UI for Docker + + + - - + + + + - - + + + - - - - - - - + + + - + +
-
-
+ + + -
+
+
-
-
-
+ +
+
+
+
+ Dashboard +
+ +
+
+
+ + +
+
+
+