diff --git a/app/app.js b/app/app.js
index 222d6decd..8ec6554ba 100644
--- a/app/app.js
+++ b/app/app.js
@@ -1,4 +1,28 @@
-angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'pullImage', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events', 'stats'])
+angular.module('dockerui', [
+ 'dockerui.templates',
+ 'ngRoute',
+ 'dockerui.services',
+ 'dockerui.filters',
+ 'masthead',
+ 'footer',
+ 'dashboard',
+ 'container',
+ 'containers',
+ 'containersNetwork',
+ 'images',
+ 'image',
+ 'pullImage',
+ 'startContainer',
+ 'sidebar',
+ 'info',
+ 'builder',
+ 'containerLogs',
+ 'containerTop',
+ 'events',
+ 'stats',
+ 'network',
+ 'networks',
+ 'volumes'])
.config(['$routeProvider', function ($routeProvider) {
'use strict';
$routeProvider.when('/', {
@@ -48,5 +72,4 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
// 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.9.0-beta')
- .constant('DOCKER_API_VERSION', 'v1.20');
+ .constant('UI_VERSION', 'v0.9.0-beta');
diff --git a/app/components/builder/builderController.js b/app/components/builder/builderController.js
index 5455aae4a..f89ce4e05 100644
--- a/app/components/builder/builderController.js
+++ b/app/components/builder/builderController.js
@@ -1,5 +1,5 @@
angular.module('builder', [])
- .controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
- function ($scope, Dockerfile, Messages) {
+ .controller('BuilderController', ['$scope',
+ function ($scope) {
$scope.template = 'app/components/builder/builder.html';
}]);
diff --git a/app/components/dashboard/dashboardController.js b/app/components/dashboard/dashboardController.js
index 07646c007..e5863dc59 100644
--- a/app/components/dashboard/dashboardController.js
+++ b/app/components/dashboard/dashboardController.js
@@ -21,6 +21,7 @@ angular.module('dashboard', [])
if (Settings.firstLoad) {
opts.animation = true;
Settings.firstLoad = false;
+ localStorage.setItem('firstLoad', false);
$('#masthead').show();
setTimeout(function () {
diff --git a/app/components/footer/footerController.js b/app/components/footer/footerController.js
index 021ab5721..3e7cd2532 100644
--- a/app/components/footer/footerController.js
+++ b/app/components/footer/footerController.js
@@ -1,9 +1,9 @@
angular.module('footer', [])
- .controller('FooterController', ['$scope', 'Settings', 'Docker', function ($scope, Settings, Docker) {
+ .controller('FooterController', ['$scope', 'Settings', 'Version', function ($scope, Settings, Version) {
$scope.template = 'app/components/footer/statusbar.html';
$scope.uiVersion = Settings.uiVersion;
- Docker.get({}, function (d) {
+ Version.get({}, function (d) {
$scope.apiVersion = d.ApiVersion;
});
}]);
diff --git a/app/components/info/infoController.js b/app/components/info/infoController.js
index 9cbf9fb07..794e03e13 100644
--- a/app/components/info/infoController.js
+++ b/app/components/info/infoController.js
@@ -1,15 +1,14 @@
angular.module('info', [])
- .controller('InfoController', ['$scope', 'System', 'Docker', 'Settings', 'Messages',
- function ($scope, System, Docker, Settings, Messages) {
+ .controller('InfoController', ['$scope', 'Info', 'Version', 'Settings',
+ function ($scope, Info, Version, Settings) {
$scope.info = {};
$scope.docker = {};
$scope.endpoint = Settings.endpoint;
- $scope.apiVersion = Settings.version;
- Docker.get({}, function (d) {
+ Version.get({}, function (d) {
$scope.docker = d;
});
- System.get({}, function (d) {
+ Info.get({}, function (d) {
$scope.info = d;
});
}]);
diff --git a/app/components/masthead/masthead.html b/app/components/masthead/masthead.html
index c7246c80f..0e358e7cb 100644
--- a/app/components/masthead/masthead.html
+++ b/app/components/masthead/masthead.html
@@ -1,10 +1,21 @@
diff --git a/app/components/masthead/mastheadController.js b/app/components/masthead/mastheadController.js
index 9d12cba42..713ff8bf3 100644
--- a/app/components/masthead/mastheadController.js
+++ b/app/components/masthead/mastheadController.js
@@ -1,4 +1,15 @@
angular.module('masthead', [])
- .controller('MastheadController', ['$scope', function ($scope) {
+ .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
new file mode 100644
index 000000000..46412b329
--- /dev/null
+++ b/app/components/network/network.html
@@ -0,0 +1,110 @@
+
+
+
Network: {{ network.Name }}
+
+
+
+
+ Name: |
+ {{ network.Name }} |
+
+
+ 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 }} |
+
+
+ |
+
+
+ Containers: |
+
+
+
+ Id: |
+ {{ Id }} |
+
+
+ |
+
+
+ EndpointID: |
+ {{ container.EndpointID}} |
+
+
+ MacAddress: |
+ {{ container.MacAddress}} |
+
+
+ IPv4Address: |
+ {{ container.IPv4Address}} |
+
+
+ IPv6Address: |
+ {{ container.IPv6Address}} |
+
+
+
+ |
+
+
+ Options: |
+
+
+
+ Key |
+ Value |
+
+
+ {{ k }} |
+ {{ v }} |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/network/networkController.js b/app/components/network/networkController.js
new file mode 100644
index 000000000..cb3cca7d4
--- /dev/null
+++ b/app/components/network/networkController.js
@@ -0,0 +1,56 @@
+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', '$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);
+ });
+ };
+
+ ViewSpinner.spin();
+ Network.get({id: $routeParams.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
new file mode 100644
index 000000000..6cfe2eb85
--- /dev/null
+++ b/app/components/networks/networks.html
@@ -0,0 +1,80 @@
+Networks:
+
+
+
+
\ No newline at end of file
diff --git a/app/components/networks/networksController.js b/app/components/networks/networksController.js
new file mode 100644
index 000000000..fa8551cb9
--- /dev/null
+++ b/app/components/networks/networksController.js
@@ -0,0 +1,82 @@
+angular.module('networks', []).config(['$routeProvider', function ($routeProvider) {
+ $routeProvider.when('/networks', {
+ templateUrl: 'app/components/networks/networks.html',
+ controller: 'NetworksController'
+ });
+}]).controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
+ function ($scope, Network, ViewSpinner, Messages, $route, errorMsgFilter) {
+ $scope.toggle = false;
+ $scope.predicate = '-Created';
+ $scope.createNetworkConfig = {
+ "Name": '',
+ "Driver": '',
+ "IPAM": {
+ "Config": [{
+ "Subnet": '',
+ "IPRange": '',
+ "Gateway": ''
+ }]
+ }
+ };
+
+
+
+ $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.networks, 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();
+ }]);
diff --git a/app/components/pullImage/pullImageController.js b/app/components/pullImage/pullImageController.js
index 48e05f8c4..d53fe9c2c 100644
--- a/app/components/pullImage/pullImageController.js
+++ b/app/components/pullImage/pullImageController.js
@@ -1,6 +1,6 @@
angular.module('pullImage', [])
- .controller('PullImageController', ['$scope', '$log', 'Dockerfile', 'Messages', 'Image', 'ViewSpinner',
- function ($scope, $log, Dockerfile, Messages, Image, ViewSpinner) {
+ .controller('PullImageController', ['$scope', '$log', 'Messages', 'Image', 'ViewSpinner',
+ function ($scope, $log, Messages, Image, ViewSpinner) {
$scope.template = 'app/components/pullImage/pullImage.html';
$scope.init = function () {
diff --git a/app/components/stats/statsController.js b/app/components/stats/statsController.js
index 272ed6939..40d0b4159 100644
--- a/app/components/stats/statsController.js
+++ b/app/components/stats/statsController.js
@@ -1,21 +1,7 @@
angular.module('stats', [])
.controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', '$routeParams', 'humansizeFilter', '$sce', function (Settings, $scope, Messages, $timeout, Container, $routeParams, humansizeFilter, $sce) {
- // TODO: Implement memory chart, force scale to 0-100 for cpu, 0 to limit for memory, fix charts on dashboard,
+ // TODO: Force scale to 0-100 for cpu, fix charts on dashboard,
// TODO: Force memory scale to 0 - max memory
- //var initialStats = {}; // Used to set scale of memory graph.
- //
- //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;
- // }
- // initialStats = d;
- //}, function () {
- // Messages.error('Unable to retrieve stats', 'Is this container running?');
- //});
var cpuLabels = [];
var cpuData = [];
diff --git a/app/components/volumes/volumes.html b/app/components/volumes/volumes.html
new file mode 100644
index 000000000..af8921776
--- /dev/null
+++ b/app/components/volumes/volumes.html
@@ -0,0 +1,56 @@
+Volumes:
+
+
+
+
\ No newline at end of file
diff --git a/app/components/volumes/volumesController.js b/app/components/volumes/volumesController.js
new file mode 100644
index 000000000..bed59316a
--- /dev/null
+++ b/app/components/volumes/volumesController.js
@@ -0,0 +1,75 @@
+angular.module('volumes', []).config(['$routeProvider', function ($routeProvider) {
+ $routeProvider.when('/volumes', {
+ templateUrl: 'app/components/volumes/volumes.html',
+ controller: 'VolumesController'
+ });
+}]).controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
+ function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) {
+ $scope.toggle = false;
+ $scope.predicate = '-Created';
+ $scope.createVolumeConfig = {
+ "Name": "",
+ "Driver": ""
+ };
+
+
+
+ $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.volumes, 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();
+ }]);
diff --git a/app/shared/services.js b/app/shared/services.js
index 805f1e399..f2b9da742 100644
--- a/app/shared/services.js
+++ b/app/shared/services.js
@@ -95,7 +95,7 @@ angular.module('dockerui.services', ['ngResource'])
remove: {method: 'DELETE', params: {id: '@id'}, isArray: true}
});
}])
- .factory('Docker', ['$resource', 'Settings', function DockerFactory($resource, Settings) {
+ .factory('Version', ['$resource', 'Settings', function VersionFactory($resource, Settings) {
'use strict';
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#show-the-docker-version-information
return $resource(Settings.url + '/version', {}, {
@@ -110,27 +110,48 @@ angular.module('dockerui.services', ['ngResource'])
update: {method: 'POST'}
});
}])
- .factory('System', ['$resource', 'Settings', function SystemFactory($resource, Settings) {
+ .factory('Info', ['$resource', 'Settings', function InfoFactory($resource, Settings) {
'use strict';
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#display-system-wide-information
return $resource(Settings.url + '/info', {}, {
get: {method: 'GET'}
});
}])
- .factory('Settings', ['DOCKER_ENDPOINT', 'DOCKER_PORT', 'DOCKER_API_VERSION', 'UI_VERSION', function SettingsFactory(DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) {
+ .factory('Network', ['$resource', 'Settings', function NetworkFactory($resource, Settings) {
+ 'use strict';
+ // http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#2-5-networks
+ return $resource(Settings.url + '/networks/:id/:action', {id: '@id'}, {
+ query: {method: 'GET', isArray: true},
+ get: {method: 'GET'},
+ create: {method: 'POST', params: {action: 'create'}},
+ remove: {method: 'DELETE'},
+ connect: {method: 'POST', params: {action: 'connect'}},
+ disconnect: {method: 'POST', params: {action: 'disconnect'}}
+ });
+ }])
+ .factory('Volume', ['$resource', 'Settings', function VolumeFactory($resource, Settings) {
+ 'use strict';
+ // http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#2-5-networks
+ return $resource(Settings.url + '/volumes/:name/:action', {name: '@name'}, {
+ query: {method: 'GET'},
+ get: {method: 'GET'},
+ create: {method: 'POST', params: {action: 'create'}},
+ remove: {method: 'DELETE'}
+ });
+ }])
+ .factory('Settings', ['DOCKER_ENDPOINT', 'DOCKER_PORT', 'UI_VERSION', function SettingsFactory(DOCKER_ENDPOINT, DOCKER_PORT, UI_VERSION) {
'use strict';
var url = DOCKER_ENDPOINT;
if (DOCKER_PORT) {
url = url + DOCKER_PORT + '\\' + DOCKER_PORT;
}
+ var firstLoad = (localStorage.getItem('firstLoad') || 'true') === 'true';
return {
displayAll: false,
endpoint: DOCKER_ENDPOINT,
- version: DOCKER_API_VERSION,
- rawUrl: DOCKER_ENDPOINT + DOCKER_PORT + '/' + DOCKER_API_VERSION,
uiVersion: UI_VERSION,
url: url,
- firstLoad: true
+ firstLoad: firstLoad
};
}])
.factory('ViewSpinner', function ViewSpinnerFactory() {
@@ -176,23 +197,6 @@ angular.module('dockerui.services', ['ngResource'])
}
};
}])
- .factory('Dockerfile', ['Settings', function DockerfileFactory(Settings) {
- 'use strict';
- // http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#build-image-from-a-dockerfile
- var url = Settings.rawUrl + '/build';
- return {
- build: function (file, callback) {
- var data = new FormData();
- var dockerfile = new Blob([file], {type: 'text/text'});
- data.append('Dockerfile', dockerfile);
-
- var request = new XMLHttpRequest();
- request.onload = callback;
- request.open('POST', url);
- request.send(data);
- }
- };
- }])
.factory('LineChart', ['Settings', function LineChartFactory(Settings) {
'use strict';
return {
@@ -222,7 +226,7 @@ angular.module('dockerui.services', ['ngResource'])
labels.push(k);
data.push(map[k]);
if (map[k] > max) {
- max = map[k];
+ max = map[k];
}
}
var steps = Math.min(max, 10);
diff --git a/gruntFile.js b/gruntFile.js
index f184ed9c7..605539f4d 100644
--- a/gruntFile.js
+++ b/gruntFile.js
@@ -14,8 +14,27 @@ module.exports = function (grunt) {
// Default task.
grunt.registerTask('default', ['jshint', 'build', 'karma:unit']);
- grunt.registerTask('build', ['clean:app', 'if:binaryNotExist', 'html2js', 'concat', 'clean:tmpl', 'recess:build', 'copy']);
- grunt.registerTask('release', ['clean:all', 'if:binaryNotExist', 'html2js', 'uglify', 'clean:tmpl', 'jshint', 'karma:unit', 'concat:index', 'recess:min', 'copy']);
+ grunt.registerTask('build', [
+ 'clean:app',
+ 'if:binaryNotExist',
+ 'html2js',
+ 'concat',
+ 'clean:tmpl',
+ 'recess:build',
+ 'copy'
+ ]);
+ grunt.registerTask('release', [
+ 'clean:all',
+ 'if:binaryNotExist',
+ 'html2js',
+ 'uglify',
+ 'clean:tmpl',
+ 'jshint',
+ 'karma:unit',
+ 'concat:index',
+ 'recess:min',
+ 'copy'
+ ]);
grunt.registerTask('test-watch', ['karma:watch']);
grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']);
grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']);
diff --git a/test/unit/app/components/networkController.spec.js b/test/unit/app/components/networkController.spec.js
new file mode 100644
index 000000000..649a66630
--- /dev/null
+++ b/test/unit/app/components/networkController.spec.js
@@ -0,0 +1,83 @@
+describe('NetworkController', function () {
+ var $scope, $httpBackend, $routeParams;
+
+ beforeEach(module('dockerui'));
+ beforeEach(inject(function (_$httpBackend_, $controller, _$routeParams_) {
+ $scope = {};
+ $httpBackend = _$httpBackend_;
+ $routeParams = _$routeParams_;
+ $routeParams.id = 'f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b';
+ $controller('NetworkController', {
+ '$scope': $scope,
+ '$routeParams': $routeParams
+ });
+ }));
+
+ it('initializes correctly', function () {
+ expectGetNetwork();
+ $httpBackend.flush();
+ });
+
+ it('issues a correct connect call to the remote API', function () {
+ expectGetNetwork();
+ $httpBackend.expectPOST('dockerapi/networks/f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b/connect', {'Container': 'containerId'}).respond(200);
+ $scope.connect($routeParams.id, 'containerId');
+ $httpBackend.flush();
+ });
+ it('issues a correct disconnect call to the remote API', function () {
+ expectGetNetwork();
+ $httpBackend.expectPOST('dockerapi/networks/f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b/disconnect', {'Container': 'containerId'}).respond(200);
+ $scope.disconnect($routeParams.id, 'containerId');
+ $httpBackend.flush();
+ });
+ it('issues a correct remove call to the remote API', function () {
+ expectGetNetwork();
+ $httpBackend.expectDELETE('dockerapi/networks/f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b').respond(204);
+ $scope.remove($routeParams.id);
+ $httpBackend.flush();
+ });
+
+ function expectGetNetwork() {
+ $httpBackend.expectGET('dockerapi/networks/f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b').respond({
+ "Name": "bridge",
+ "Id": "f1e1ce1613ccd374a75caf5e2c3ab35520d1944f91498c1974ec86fb4019c79b",
+ "Scope": "local",
+ "Driver": "bridge",
+ "IPAM": {
+ "Driver": "default",
+ "Config": [{
+ "Subnet": "172.17.0.1/16",
+ "Gateway": "172.17.0.1"
+ }]
+ },
+ "Containers": {
+ "727fe76cd0bd65033baab3045508784a166fbc67d177e91c1874b6b29eae946a": {
+ "EndpointID": "c17ec80e2cfc8eaedc7737b7bb6f954adff439767197ef89c4a5b4127d07b267",
+ "MacAddress": "02:42:ac:11:00:03",
+ "IPv4Address": "172.17.0.3/16",
+ "IPv6Address": ""
+ },
+ "8c32c2446c3dfe0defac2dc8b5fd927cd394f15e08051c677a681bf36877175b": {
+ "EndpointID": "cf7e795c978ab194d1af4a3efdc177d84c075582ba30a7cff414c7d516236af1",
+ "MacAddress": "02:42:ac:11:00:04",
+ "IPv4Address": "172.17.0.4/16",
+ "IPv6Address": ""
+ },
+ "cfe81fc97b1f857fdb3061fe487a064b8b57d8f112910954ac16910400d2e058": {
+ "EndpointID": "611929ffcff2ced1db8e88f77e009c4fb4a4736395251cd97553b242e2e23bf1",
+ "MacAddress": "02:42:ac:11:00:02",
+ "IPv4Address": "172.17.0.2/16",
+ "IPv6Address": ""
+ }
+ },
+ "Options": {
+ "com.docker.network.bridge.default_bridge": "true",
+ "com.docker.network.bridge.enable_icc": "true",
+ "com.docker.network.bridge.enable_ip_masquerade": "true",
+ "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
+ "com.docker.network.bridge.name": "docker0",
+ "com.docker.network.driver.mtu": "1500"
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/test/unit/app/components/networksController.spec.js b/test/unit/app/components/networksController.spec.js
new file mode 100644
index 000000000..0f3ce3280
--- /dev/null
+++ b/test/unit/app/components/networksController.spec.js
@@ -0,0 +1,107 @@
+describe('NetworksController', function () {
+ var $scope, $httpBackend, $routeParams;
+
+ beforeEach(module('dockerui'));
+ beforeEach(inject(function (_$httpBackend_, $controller, _$routeParams_) {
+ $scope = {};
+ $httpBackend = _$httpBackend_;
+ $routeParams = _$routeParams_;
+ $controller('NetworksController', {
+ '$scope': $scope,
+ '$routeParams': $routeParams
+ });
+ }));
+
+ it('initializes correctly', function () {
+ expectGetNetwork();
+ $httpBackend.flush();
+ });
+
+
+ it('issues correct remove calls to the remote API', function () {
+ expectGetNetwork();
+ $httpBackend.flush();
+ $scope.networks[0].Checked = true;
+ $scope.networks[2].Checked = true;
+ $httpBackend.expectDELETE('dockerapi/networks/f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566').respond(204);
+ $httpBackend.expectDELETE('dockerapi/networks/13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e').respond(204);
+ $scope.removeAction();
+ $httpBackend.flush();
+ });
+ it('issues a correct network creation call to the remote API', function () {
+ expectGetNetwork();
+ var createBody = {
+ "Name":"isolated_nw",
+ "Driver":"bridge",
+ "IPAM":{
+ "Config":[{
+ "Subnet":"172.20.0.0/16",
+ "IPRange":"172.20.10.0/24",
+ "Gateway":"172.20.10.11"
+ }]
+ }};
+ $httpBackend.expectPOST('dockerapi/networks/create', createBody).respond(201);
+ expectGetNetwork();
+ $scope.addNetwork(createBody);
+ $httpBackend.flush();
+ });
+
+ function expectGetNetwork() {
+ $httpBackend.expectGET('dockerapi/networks').respond([
+ {
+ "Name": "bridge",
+ "Id": "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566",
+ "Scope": "local",
+ "Driver": "bridge",
+ "IPAM": {
+ "Driver": "default",
+ "Config": [
+ {
+ "Subnet": "172.17.0.0/16"
+ }
+ ]
+ },
+ "Containers": {
+ "39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867": {
+ "EndpointID": "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda",
+ "MacAddress": "02:42:ac:11:00:02",
+ "IPv4Address": "172.17.0.2/16",
+ "IPv6Address": ""
+ }
+ },
+ "Options": {
+ "com.docker.network.bridge.default_bridge": "true",
+ "com.docker.network.bridge.enable_icc": "true",
+ "com.docker.network.bridge.enable_ip_masquerade": "true",
+ "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
+ "com.docker.network.bridge.name": "docker0",
+ "com.docker.network.driver.mtu": "1500"
+ }
+ },
+ {
+ "Name": "none",
+ "Id": "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794",
+ "Scope": "local",
+ "Driver": "null",
+ "IPAM": {
+ "Driver": "default",
+ "Config": []
+ },
+ "Containers": {},
+ "Options": {}
+ },
+ {
+ "Name": "host",
+ "Id": "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e",
+ "Scope": "local",
+ "Driver": "host",
+ "IPAM": {
+ "Driver": "default",
+ "Config": []
+ },
+ "Containers": {},
+ "Options": {}
+ }
+ ]);
+ }
+});
\ No newline at end of file
diff --git a/test/unit/app/components/volumesController.spec.js b/test/unit/app/components/volumesController.spec.js
new file mode 100644
index 000000000..ef567c07a
--- /dev/null
+++ b/test/unit/app/components/volumesController.spec.js
@@ -0,0 +1,64 @@
+describe('VolumesController', function () {
+ var $scope, $httpBackend, $routeParams;
+
+ beforeEach(module('dockerui'));
+ beforeEach(inject(function (_$httpBackend_, $controller, _$routeParams_) {
+ $scope = {};
+ $httpBackend = _$httpBackend_;
+ $routeParams = _$routeParams_;
+ $controller('VolumesController', {
+ '$scope': $scope,
+ '$routeParams': $routeParams
+ });
+ }));
+
+ it('initializes correctly', function () {
+ expectGetVolumes();
+ $httpBackend.flush();
+ });
+
+
+ it('issues correct remove calls to the remote API', function () {
+ expectGetVolumes();
+ $httpBackend.flush();
+ $scope.volumes[0].Checked = true;
+ $scope.volumes[2].Checked = true;
+ $httpBackend.expectDELETE('dockerapi/volumes/tardis').respond(200);
+ $httpBackend.expectDELETE('dockerapi/volumes/bar').respond(200);
+ $scope.removeAction();
+ $httpBackend.flush();
+ });
+ it('issues a correct volume creation call to the remote API', function () {
+ expectGetVolumes();
+ var createBody = {
+ "Name": "tardis",
+ "Driver": "local"
+ };
+ $httpBackend.expectPOST('dockerapi/volumes/create', createBody).respond(201);
+ expectGetVolumes();
+ $scope.addVolume(createBody);
+ $httpBackend.flush();
+ });
+
+ function expectGetVolumes() {
+ $httpBackend.expectGET('dockerapi/volumes').respond({
+ "Volumes": [
+ {
+ "Name": "tardis",
+ "Driver": "local",
+ "Mountpoint": "/var/lib/docker/volumes/tardis"
+ },
+ {
+ "Name": "foo",
+ "Driver": "local",
+ "Mountpoint": "/var/lib/docker/volumes/foo"
+ },
+ {
+ "Name": "bar",
+ "Driver": "local",
+ "Mountpoint": "/var/lib/docker/volumes/bar"
+ }
+ ]
+ });
+ }
+});
\ No newline at end of file