Compare commits

...

34 Commits

Author SHA1 Message Date
Kevan Ahlquist 3df4342f08 Merge latest from master and bump version to 0.9.0. 2016-01-31 23:24:10 -06:00
Kevan Ahlquist 49d4c5800f Added swarm on docker-machine example. 2016-01-24 17:03:20 -06:00
Kevan Ahlquist 89e7e46980 Merge master into dist 2016-01-23 16:45:14 -06:00
Kevan Ahlquist a198382c06 Bump stats endpoint timeout to 5 seconds, retry on error. 2016-01-23 16:39:31 -06:00
Kevan Ahlquist 2a4bc92d08 Merge master into 'dist'. 2016-01-18 23:41:44 -06:00
Kevan Ahlquist 316017c516 Merge pull request #172 from chazmuzz/master
Use NetworkSettings.Ports rather than HostConfig.PortBindings
2016-01-18 23:34:09 -06:00
Charlie Murray f62cb483ce Use NetworkSettings.Ports rather than HostConfig.PortBindings to diplay host port bindings 2016-01-18 11:05:08 +00:00
Kevan Ahlquist cd232bed13 Merge branch 'master' into dist 2016-01-10 22:08:16 -06:00
Kevan Ahlquist 44c88f1ed5 Merge pull request #170 from kevana/stats-fix
Fix network stats error for 1.9.0+ clients, hide networks and volumes…
2016-01-10 22:00:38 -06:00
Kevan Ahlquist 9d1193c0b5 Fix network stats error for 1.9.0+ clients, hide networks and volumes better on older clients. 2016-01-10 21:51:51 -06:00
Kevan Ahlquist c56f94c6d2 Merge branch 'master' into dist 2016-01-10 20:58:00 -06:00
Kevan Ahlquist 7a14f43b4a Merge branch 'master' into dist
Conflicts:
	app/app.js
	bower.json
	package.json
2016-01-10 20:49:34 -06:00
Kevan Ahlquist 22d35eb32f Merge pull request #162 from kevana/networks-volumes
Networks and Volumes
2016-01-10 20:39:14 -06:00
Kevan Ahlquist 2683ddb1ee Add refresh button. 2016-01-10 20:36:22 -06:00
Kevan Ahlquist fe7646e939 Use localStorage to only show 'learn more' banner on first load. 2016-01-10 20:02:04 -06:00
Kevan Ahlquist 00528edd7c Merge branch 'master' into networks-volumes 2016-01-10 19:34:03 -06:00
Michael Crosby 06c2ac4d5c Merge pull request #168 from linquize/chart-max-steps
Show maximum 10 steps of chart
2016-01-04 09:28:58 -08:00
Michael Crosby a447d64d83 Merge pull request #169 from linquize/desc
Change the description of DockerUI
2016-01-04 09:27:17 -08:00
Linquize 9260f23d41 DockerUI is the UI for Docker container engine
It is not only on Linux, but on Windows too.
2016-01-03 13:55:27 +08:00
Linquize d7082c3959 Show maximum 10 steps of chart 2016-01-03 12:59:43 +08:00
Kevan Ahlquist 437e171639 Merge pull request #163 from israelroldan/issue-158
Redirect to container list when removing a container (Fix for #158)
2015-12-26 17:54:23 -06:00
Israel Roldan 6de45cd422 Redirect to container list when removing a container (Fixes #158) 2015-12-24 00:04:47 -06:00
Kevan Ahlquist a065e0e259 Merge pull request #160 from crosbymichael/icons
Add custom logo, nginx basic auth docker-compose example
2015-12-20 21:25:34 -06:00
Kevan Ahlquist eede27e263 Implement volumes, hide networks and volumes tabs for APIs older than v1.21. 2015-12-20 21:17:01 -06:00
Kevan Ahlquist 8a900254ae Remove unused Dockerfile service, rename services to match API endpoint names: Docker => Version, System => Info. 2015-12-20 20:23:17 -06:00
Kevan Ahlquist 5f4641af67 Catch plaintext errors for creation and connection, implement network creation, 2015-12-20 20:07:57 -06:00
Kevan Ahlquist 8a4be8b93a Implemented single network view, connect, disconnect, remove. 2015-12-20 19:13:53 -06:00
Kevan Ahlquist a712d5b77e Progress on Networks. 2015-12-17 23:35:04 -06:00
Kevan Ahlquist 5947e262fc Added docker-compose config for nginx-basic-auth. 2015-12-17 01:31:07 -06:00
Kevan Ahlquist c3f22fe989 Add custom logo. 2015-12-17 00:08:29 -06:00
Kevan Ahlquist f9aedad71a Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-12-14 22:46:01 -06:00
Kevan Ahlquist d4cfa11569 Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-12-14 22:45:22 -06:00
Kevan Ahlquist 2f003a5bb5 Merge pull request #159 from crosbymichael/tags-support
Support RepoTags for versions of the Docker API older than 1.21.
2015-12-14 22:24:41 -06:00
Kevan Ahlquist 75085b213a Support RepoTags for versions of the Docker API older than 1.21. 2015-12-13 23:28:28 -06:00
50 changed files with 948 additions and 96 deletions
+2 -4
View File
@@ -1,4 +1,2 @@
node_modules
bower_components
.git
Dockerfile
*
!dist
+26 -3
View File
@@ -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.8.0')
.constant('DOCKER_API_VERSION', 'v1.20');
.constant('UI_VERSION', 'v0.9.0');
+2 -2
View File
@@ -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';
}]);
+1 -1
View File
@@ -106,7 +106,7 @@
<td>Ports:</td>
<td>
<ul style="display:inline-table">
<li ng-repeat="(containerport, hostports) in container.HostConfig.PortBindings">
<li ng-repeat="(containerport, hostports) in container.NetworkSettings.Ports">
{{ containerport }} => <span class="label label-default" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
</li>
</ul>
@@ -1,6 +1,6 @@
angular.module('container', [])
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner', '$timeout',
function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner, $timeout) {
$scope.changes = [];
$scope.edit = false;
@@ -94,6 +94,7 @@ angular.module('container', [])
ViewSpinner.spin();
Container.remove({id: $routeParams.id}, function (d) {
update();
$location.path('/containers');
Messages.send("Container removed", $routeParams.id);
}, function (e) {
update();
+1 -1
View File
@@ -7,7 +7,7 @@
<div class="jumbotron">
<h1>DockerUI</h1>
<p class="lead">The Linux container engine</p>
<p class="lead">The UI for Docker container engine</p>
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
</div>
</div>
@@ -21,6 +21,7 @@ angular.module('dashboard', [])
if (Settings.firstLoad) {
opts.animation = true;
Settings.firstLoad = false;
localStorage.setItem('firstLoad', false);
$('#masthead').show();
setTimeout(function () {
+2 -2
View File
@@ -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;
});
}]);
+20 -1
View File
@@ -63,10 +63,29 @@ angular.module('image', [])
return defer.promise;
}
/**
* Get RepoTags from the /images/query endpoint instead of /image/json,
* for backwards compatibility with Docker API versions older than 1.21
* @param {string} imageId
*/
function getRepoTags(imageId) {
Image.query({}, function (d) {
d.forEach(function(image) {
if (image.Id === imageId && image.RepoTags[0] !== '<none>:<none>') {
$scope.RepoTags = image.RepoTags;
}
});
});
}
Image.get({id: $routeParams.id}, function (d) {
$scope.image = d;
$scope.id = d.Id;
$scope.RepoTags = d.RepoTags;
if (d.RepoTags) {
$scope.RepoTags = d.RepoTags;
} else {
getRepoTags($scope.id);
}
getContainersFromImage($q, Container, $scope.id).then(function (containers) {
LineChart.build('#containers-started-chart', containers, function (c) {
+4 -5
View File
@@ -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;
});
}]);
+18 -7
View File
@@ -1,10 +1,21 @@
<div class="masthead">
<h3 class="text-muted">DockerUI</h3>
<ul class="nav well">
<li><a href="#/">Dashboard</a></li>
<li><a href="#/containers/">Containers</a></li>
<li><a href="#/containers_network/">Containers Network</a></li>
<li><a href="#/images/">Images</a></li>
<li><a href="#/info/">Info</a></li>
</ul>
<div class="col-xs-11">
<ul class="nav well">
<li><a href="#/">Dashboard</a></li>
<li><a href="#/containers/">Containers</a></li>
<li><a href="#/containers_network/">Containers Network</a></li>
<li><a href="#/images/">Images</a></li>
<li ng-if="showNetworksVolumes"><a href="#/networks/">Networks</a></li>
<li ng-if="showNetworksVolumes"><a href="#/volumes/">Volumes</a></li>
<li><a href="#/info/">Info</a></li>
</ul>
</div>
<div class="col-xs-1">
<button class="btn btn-primary" ng-click="refresh()">
<span class="glyphicon glyphicon-refresh" aria-hidden="true"></span>
Refresh
</button>
</div>
</div>
+12 -1
View File
@@ -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();
};
}]);
+110
View File
@@ -0,0 +1,110 @@
<div class="detail">
<h4>Network: {{ network.Name }}</h4>
<table class="table table-striped">
<tbody>
<tr>
<td>Name:</td>
<td>{{ network.Name }}</td>
</tr>
<tr>
<td>Id:</td>
<td>{{ network.Id }}</td>
</tr>
<tr>
<td>Scope:</td>
<td>{{ network.Scope }}</td>
</tr>
<tr>
<td>Driver:</td>
<td>{{ network.Driver }}</td>
</tr>
<tr>
<td>IPAM:</td>
<td>
<table class="table table-striped">
<tr>
<td>Driver:</td>
<td>{{ network.IPAM.Driver }}</td>
</tr>
<tr>
<td>Subnet:</td>
<td>{{ network.IPAM.Config[0].Subnet }}</td>
</tr>
<tr>
<td>Gateway:</td>
<td>{{ network.IPAM.Config[0].Gateway }}</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>Containers:</td>
<td>
<table class="table table-striped" ng-repeat="(Id, container) in network.Containers">
<tr>
<td>Id:</td>
<td><a href="#/containers/{{ Id }}">{{ Id }}</a></td>
<td>
<button ng-click="disconnect(network.Id, Id)" class="btn btn-danger btn-sm">
Disconnect from network
</button>
</td>
</tr>
<tr>
<td>EndpointID:</td>
<td>{{ container.EndpointID}}</td>
</tr>
<tr>
<td>MacAddress:</td>
<td>{{ container.MacAddress}}</td>
</tr>
<tr>
<td>IPv4Address:</td>
<td>{{ container.IPv4Address}}</td>
</tr>
<tr>
<td>IPv6Address:</td>
<td>{{ container.IPv6Address}}</td>
</tr>
</table>
<form class="form-inline">
<div class="form-group">
<label>Container ID:
<input ng-model="containerId" placeholder="3613f73ba0e4" class="form-control">
</label>
</div>
<button ng-click="connect(network.Id, containerId)" class="btn btn-primary">
Connect
</button>
</form>
</td>
</tr>
<tr>
<td>Options:</td>
<td>
<table role="table" class="table table-striped">
<tr>
<th>Key</th>
<th>Value</th>
</tr>
<tr ng-repeat="(k, v) in network.Options">
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<hr/>
<div class="btn-remove">
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="removeImage(id)">Remove Network
</button>
</div>
</div>
@@ -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();
});
}]);
+80
View File
@@ -0,0 +1,80 @@
<h2>Networks:</h2>
<div>
<ul class="nav nav-pills pull-left">
<li class="dropdown">
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b
class="caret"></b></a>
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
</ul>
</li>
<!--<li><a data-toggle="modal" data-target="#pull-modal" href="">Pull</a></li>-->
</ul>
<div class="pull-right form-inline">
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label
class="sr-only" for="filter">Filter</label>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</th>
<th>Name</th>
<th>Id</th>
<th>Scope</th>
<th>Driver</th>
<th>IPAM Driver</th>
<th>IPAM Subnet</th>
<th>IPAM Gateway</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="network in networks | filter:filter | orderBy:predicate">
<td><input type="checkbox" ng-model="network.Checked"/></td>
<td><a href="#/networks/{{ network.Id }}/">{{ network.Name|truncate:20}}</a></td>
<td>{{ network.Id }}</td>
<td>{{ network.Scope }}</td>
<td>{{ network.Driver }}</td>
<td>{{ network.IPAM.Driver }}</td>
<td>{{ network.IPAM.Config[0].Subnet }}</td>
<td>{{ network.IPAM.Config[0].Gateway }}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-xs-offset-3 col-xs-6">
<form role="form" class="">
<div class="form-group">
<label>Name:</label>
<input type="text" placeholder='isolated_nw'
ng-model="createNetworkConfig.Name" class="form-control"/>
</div>
<div class="form-group">
<label>Driver:</label>
<input type="text" placeholder='bridge'
ng-model="createNetworkConfig.Driver" class="form-control"/>
</div>
<div class="form-group">
<label>Subnet:</label>
<input type="text" placeholder='172.20.0.0/16'
ng-model="createNetworkConfig.IPAM.Config[0].Subnet" class="form-control"/>
</div>
<div class="form-group">
<label>IPRange:</label>
<input type="text" placeholder='172.20.10.0/24'
ng-model="createNetworkConfig.IPAM.Config[0].IPRange" class="form-control"/>
</div>
<div class="form-group">
<label>Gateway:</label>
<input type="text" placeholder='172.20.10.11'
ng-model="createNetworkConfig.IPAM.Config[0].Gateway" class="form-control"/>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addNetwork(createNetworkConfig)">
Create Network
</button>
</form>
</div>
</div>
@@ -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();
}]);
@@ -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 () {
+1 -1
View File
@@ -44,7 +44,7 @@
</div>
</div>
<h1>Network</h1>
<h1>Network {{ networkName}}</h1>
<div class="row">
<div class="col-sm-7">
<canvas id="network-stats-chart" width="650" height="300"></canvas>
+9 -20
View File
@@ -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 = [];
@@ -124,9 +110,10 @@ angular.module('stats', [])
updateCpuChart(d);
updateMemoryChart(d);
updateNetworkChart(d);
timeout = $timeout(updateStats, 2000);
timeout = $timeout(updateStats, 5000);
}, function () {
Messages.error('Unable to retrieve stats', 'Is this container running?');
timeout = $timeout(updateStats, 5000);
});
}
@@ -138,13 +125,11 @@ angular.module('stats', [])
updateStats();
function updateCpuChart(data) {
console.log('updateCpuChart', data);
cpuChart.addData([calculateCPUPercent(data)], new Date(data.read).toLocaleTimeString());
cpuChart.removeData();
}
function updateMemoryChart(data) {
console.log('updateMemoryChart', data);
memoryChart.addData([data.memory_stats.usage], new Date(data.read).toLocaleTimeString());
memoryChart.removeData();
}
@@ -152,6 +137,12 @@ angular.module('stats', [])
var lastRxBytes = 0, lastTxBytes = 0;
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
@@ -160,7 +151,6 @@ angular.module('stats', [])
}
lastRxBytes = data.network.rx_bytes;
lastTxBytes = data.network.tx_bytes;
console.log('updateNetworkChart', data);
networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString());
networkChart.removeData();
}
@@ -178,7 +168,6 @@ angular.module('stats', [])
var systemDelta = curCpu.system_cpu_usage - prevCpu.system_cpu_usage;
if (systemDelta > 0.0 && cpuDelta > 0.0) {
//console.log('size thing:', curCpu.cpu_usage.percpu_usage);
cpuPercent = (cpuDelta / systemDelta) * curCpu.cpu_usage.percpu_usage.length * 100.0;
}
return cpuPercent;
+56
View File
@@ -0,0 +1,56 @@
<h2>Volumes:</h2>
<div>
<ul class="nav nav-pills pull-left">
<li class="dropdown">
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b
class="caret"></b></a>
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
</ul>
</li>
</ul>
<div class="pull-right form-inline">
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label
class="sr-only" for="filter">Filter</label>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</th>
<th>Name</th>
<th>Driver</th>
<th>Mountpoint</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="volume in volumes | filter:filter | orderBy:predicate">
<td><input type="checkbox" ng-model="volume.Checked"/></td>
<td>{{ volume.Name|truncate:20 }}</td>
<td>{{ volume.Driver }}</td>
<td>{{ volume.Mountpoint }}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-xs-offset-3 col-xs-6">
<form role="form" class="">
<div class="form-group">
<label>Name:</label>
<input type="text" placeholder='tardis'
ng-model="createVolumeConfig.Name" class="form-control"/>
</div>
<div class="form-group">
<label>Driver:</label>
<input type="text" placeholder='local'
ng-model="createVolumeConfig.Driver" class="form-control"/>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addVolume(createVolumeConfig)">
Create Volume
</button>
</form>
</div>
</div>
@@ -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();
}]);
+33 -27
View File
@@ -18,7 +18,7 @@ angular.module('dockerui.services', ['ngResource'])
create: {method: 'POST', params: {action: 'create'}},
remove: {method: 'DELETE', params: {id: '@id', v: 0}},
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false},
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 2000}
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 5000}
});
}])
.factory('ContainerCommit', ['$resource', '$http', 'Settings', function ContainerCommitFactory($resource, $http, Settings) {
@@ -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,9 +226,10 @@ 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);
var dataset = {
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,1)",
@@ -237,10 +242,11 @@ angular.module('dockerui.services', ['ngResource'])
datasets: [dataset]
},
{
scaleStepWidth: 1,
scaleStepWidth: Math.ceil(max / steps),
pointDotRadius: 1,
scaleIntegersOnly: true,
scaleOverride: true,
scaleSteps: max
scaleSteps: steps
});
}
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "dockerui",
"version": "0.8.0",
"version": "0.9.0",
"homepage": "https://github.com/crosbymichael/dockerui",
"authors": [
"Michael Crosby <crosbymichael@gmail.com>",
+6 -5
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+1 -4
View File
@@ -22,10 +22,7 @@
<!-- Fav and touch icons -->
<link rel="shortcut icon" href="ico/favicon.ico">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-57-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-precomposed.png">
</head>
<body>
+4
View File
@@ -0,0 +1,4 @@
FROM nginx:1.9.9
COPY default.conf /etc/nginx/conf.d/default.conf
COPY users.htpasswd /etc/nginx/users.htpasswd
+17
View File
@@ -0,0 +1,17 @@
upstream dockerui {
server dockerui:9000;
}
server {
listen 80;
server_name localhost;
location / {
auth_basic "Docker UI";
auth_basic_user_file /etc/nginx/users.htpasswd;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://dockerui;
}
}
@@ -0,0 +1,12 @@
dockerui:
image: dockerui/dockerui
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
nginx:
build: .
links:
- dockerui
ports:
- 80:80
+1
View File
@@ -0,0 +1 @@
user:{PLAIN}password
+3
View File
@@ -0,0 +1,3 @@
FROM debian
RUN apt-get update && apt-get install -y socat
+11
View File
@@ -0,0 +1,11 @@
# DockerUI 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
```
+11
View File
@@ -0,0 +1,11 @@
dockerui:
image: dockerui/dockerui
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
+29 -2
View File
@@ -14,10 +14,30 @@ 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('runSwarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm']);
grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']);
// Print a timestamp (useful for when watching)
@@ -240,6 +260,13 @@ module.exports = function (grunt) {
'docker run --privileged -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --name dockerui dockerui'
].join(';')
},
runSwarm: {
command: [
'docker stop dockerui',
'docker rm dockerui',
'docker run --net=host -d --name dockerui dockerui -e http://127.0.0.1:2374'
].join(';')
},
cleanImages: {
command: 'docker rmi $(docker images -q -f dangling=true)'
}
+1 -4
View File
@@ -22,10 +22,7 @@
<!-- Fav and touch icons -->
<link rel="shortcut icon" href="ico/favicon.ico">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-57-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-precomposed.png">
</head>
<body>
+1 -1
View File
@@ -2,7 +2,7 @@
"author": "Michael Crosby & Kevan Ahlquist",
"name": "dockerui",
"homepage": "https://github.com/crosbymichael/dockerui",
"version": "0.8.0",
"version": "0.9.0",
"repository": {
"type": "git",
"url": "git@github.com:crosbymichael/dockerui.git"
@@ -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"
}
});
}
});
@@ -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": {}
}
]);
}
});
@@ -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"
}
]
});
}
});