Whitespace diff, ran everything through the formatter.

pull/2/head
Kevan Ahlquist 2015-08-25 00:59:54 -05:00
parent b7daf91723
commit 8a7f8f7c37
39 changed files with 1926 additions and 1566 deletions

View File

@ -38,7 +38,10 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
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.when('/events', {
templateUrl: 'app/components/events/events.html',
controller: 'EventsController'
});
$routeProvider.otherwise({redirectTo: '/'});
}])
// This is your docker url that the api will use to make requests

View File

@ -1,5 +1,5 @@
angular.module('builder', [])
.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
function($scope, Dockerfile, Messages) {
$scope.template = 'app/components/builder/builder.html';
}]);
.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
function ($scope, Dockerfile, Messages) {
$scope.template = 'app/components/builder/builder.html';
}]);

View File

@ -1,143 +1,143 @@
angular.module('container', [])
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
function($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
$scope.changes = [];
$scope.edit = false;
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
$scope.changes = [];
$scope.edit = false;
var update = function() {
ViewSpinner.spin();
Container.get({id: $routeParams.id}, function(d) {
$scope.container = d;
$scope.container.edit = false;
$scope.container.newContainerName = d.Name;
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();
});
};
var update = function () {
ViewSpinner.spin();
Container.get({id: $routeParams.id}, function (d) {
$scope.container = d;
$scope.container.edit = false;
$scope.container.newContainerName = d.Name;
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", $routeParams.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: $routeParams.id}, function (d) {
update();
Messages.send("Container stopped", $routeParams.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: $routeParams.id}, function (d) {
update();
Messages.send("Container killed", $routeParams.id);
}, function (e) {
update();
Messages.error("Failure", "Container failed to die." + 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.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.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();
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.remove = function() {
ViewSpinner.spin();
Container.remove({id: $routeParams.id}, function(d) {
update();
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;
};
update();
$scope.getChanges();
}]);
$scope.getChanges();
}]);

View File

@ -1,76 +1,76 @@
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', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner',
function ($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) {
$scope.stdout = '';
$scope.stderr = '';
$scope.showTimestamps = false;
$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: $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();
});
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();
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();
});
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();
});
}
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();
});
}
// initial call
getLogs();
var logIntervalId = window.setInterval(getLogs, 5000);
// initial call
getLogs();
var logIntervalId = window.setInterval(getLogs, 5000);
$scope.$on("$destroy", function(){
// clearing interval when view changes
clearInterval(logIntervalId);
});
$scope.$on("$destroy", function () {
// clearing interval when view changes
clearInterval(logIntervalId);
});
$scope.scrollTo = function(id) {
$location.hash(id);
$anchorScroll();
};
$scope.scrollTo = function (id) {
$location.hash(id);
$anchorScroll();
};
$scope.toggleTimestamps = function() {
getLogs();
};
$scope.toggleTimestamps = function () {
getLogs();
};
$scope.toggleTail = function() {
getLogs();
};
}]);
$scope.toggleTail = function () {
getLogs();
};
}]);

View File

@ -1,6 +1,7 @@
<div class="row logs">
<div class="col-xs-12">
<h4>Logs for container: <a href="#/containers/{{ container.Id }}/">{{ container.Name }}</a></td></h4>
<div class="btn-group detail">
<button class="btn btn-info" ng-click="scrollTo('stdout')">stdout</button>
<button class="btn btn-warning" ng-click="scrollTo('stderr')">stderr</button>
@ -9,12 +10,12 @@
<div class="col-xs-6">
<a class="btn btn-primary" ng-click="toggleTail()" role="button">Reload logs</a>
<input id="tailLines" type="number" ng-style="{width: '45px'}"
ng-model="tailLines" ng-keypress="($event.which === 13)? toggleTail() : 0"/>
ng-model="tailLines" ng-keypress="($event.which === 13)? toggleTail() : 0"/>
<label for="tailLines">lines</label>
</div>
<div class="col-xs-4">
<input id="timestampToggle" type="checkbox" ng-model="showTimestamps"
ng-change="toggleTimestamps()"/> <label for="timestampToggle">Timestamps</label>
ng-change="toggleTimestamps()"/> <label for="timestampToggle">Timestamps</label>
</div>
</div>
</div>

View File

@ -1,10 +1,10 @@
<h2>Containers:</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>
<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="startAction()">Start</a></li>
<li><a tabindex="-1" href="" ng-click="stopAction()">Stop</a></li>
@ -18,28 +18,29 @@
</ul>
<div class="pull-right">
<input type="checkbox" ng-model="displayAll" id="displayAll" ng-change="toggleGetAll()"/> <label for="displayAll">Display All</label>
<input type="checkbox" ng-model="displayAll" id="displayAll" ng-change="toggleGetAll()"/> <label
for="displayAll">Display All</label>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Action</th>
<th>Name</th>
<th>Image</th>
<th>Command</th>
<th>Created</th>
<th>Status</th>
</tr>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Action</th>
<th>Name</th>
<th>Image</th>
<th>Command</th>
<th>Created</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="container in containers|orderBy:predicate">
<td><input type="checkbox" ng-model="container.Checked" /></td>
<td><a href="#/containers/{{ container.Id }}/">{{ container|containername}}</a></td>
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
<td>{{ container.Command|truncate:40 }}</td>
<td>{{ container.Created|getdate }}</td>
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
</tr>
<tr ng-repeat="container in containers|orderBy:predicate">
<td><input type="checkbox" ng-model="container.Checked"/></td>
<td><a href="#/containers/{{ container.Id }}/">{{ container|containername}}</a></td>
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
<td>{{ container.Command|truncate:40 }}</td>
<td>{{ container.Created|getdate }}</td>
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
</tr>
</tbody>
</table>

View File

@ -1,111 +1,112 @@
angular.module('containers', [])
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
function($scope, Container, Settings, Messages, ViewSpinner) {
$scope.predicate = '-Created';
$scope.toggle = false;
$scope.displayAll = Settings.displayAll;
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
function ($scope, Container, Settings, Messages, ViewSpinner) {
$scope.predicate = '-Created';
$scope.toggle = false;
$scope.displayAll = Settings.displayAll;
var update = function(data) {
ViewSpinner.spin();
Container.query(data, function(d) {
$scope.containers = d.map(function(item) {
return new ContainerViewModel(item); });
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();
var update = function (data) {
ViewSpinner.spin();
Container.query(data, function (d) {
$scope.containers = d.map(function (item) {
return new ContainerViewModel(item);
});
}, function(e) {
if (e.status === 404) {
$('.detail').hide();
Messages.error("Not found", "Container not found.");
} else {
Messages.error("Failure", e.data);
}
complete();
});
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();
}
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();
});
};
}
$scope.toggleSelectAll = function () {
angular.forEach($scope.containers, 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.toggleSelectAll = function() {
angular.forEach($scope.containers, function(i) {
i.Checked = $scope.toggle;
});
};
$scope.startAction = function () {
batch($scope.containers, Container.start, "Started");
};
$scope.toggleGetAll = function() {
Settings.displayAll = $scope.displayAll;
update({all: Settings.displayAll ? 1 : 0});
};
$scope.stopAction = function () {
batch($scope.containers, Container.stop, "Stopped");
};
$scope.startAction = function() {
batch($scope.containers, Container.start, "Started");
};
$scope.restartAction = function () {
batch($scope.containers, Container.restart, "Restarted");
};
$scope.stopAction = function() {
batch($scope.containers, Container.stop, "Stopped");
};
$scope.killAction = function () {
batch($scope.containers, Container.kill, "Killed");
};
$scope.restartAction = function() {
batch($scope.containers, Container.restart, "Restarted");
};
$scope.pauseAction = function () {
batch($scope.containers, Container.pause, "Paused");
};
$scope.killAction = function() {
batch($scope.containers, Container.kill, "Killed");
};
$scope.unpauseAction = function () {
batch($scope.containers, Container.unpause, "Unpaused");
};
$scope.pauseAction = function() {
batch($scope.containers, Container.pause, "Paused");
};
$scope.removeAction = function () {
batch($scope.containers, Container.remove, "Removed");
};
$scope.unpauseAction = function() {
batch($scope.containers, Container.unpause, "Unpaused");
};
$scope.removeAction = function() {
batch($scope.containers, Container.remove, "Removed");
};
update({all: Settings.displayAll ? 1 : 0});
}]);
update({all: Settings.displayAll ? 1 : 0});
}]);

View File

@ -15,7 +15,8 @@
<button class="btn btn-info" ng-click="network.showSelectedUpstream()">Show Selected Upstream</button>
<button class="btn btn-success" ng-click="network.showAll()">Show All</button>
</div>
<input type="checkbox" ng-model="includeStopped" id="includeStopped" ng-change="toggleIncludeStopped()"/> <label for="includeStopped">Include stopped containers</label>
<input type="checkbox" ng-model="includeStopped" id="includeStopped" ng-change="toggleIncludeStopped()"/> <label
for="includeStopped">Include stopped containers</label>
</div>
<div class="row">
<vis-network data="network.data" options="network.options" events="network.events"

View File

@ -1,268 +1,271 @@
angular.module('containersNetwork', ['ngVis'])
.controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function($scope, $location, Container, Messages, VisDataSet) {
.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;
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;
}
}
}
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();
function ContainersNetworkData() {
this.nodes = new VisDataSet();
this.edges = new VisDataSet();
this.addContainerNode = function(container) {
this.nodes.add({
id: container.Id,
label: container.Name,
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
this.addContainerNode = function (container) {
this.nodes.add({
id: container.Id,
label: container.Name,
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
"<li><strong>ID:</strong> " + container.Id + "</li>" +
"<li><strong>Image:</strong> " + container.Image + "</li>" +
"</ul>",
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 = '';
color: (container.Running ? "#8888ff" : "#cccccc")
});
},
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.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.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);
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'}
});
}
}
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.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.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 &&
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.searchDownstream(otherContainer.Id, downstreamContainersIds);
}
}
}
};
};
this.updateShownContainers = function(newShownContainersIds) {
for (var containerId in this.containers) {
if (newShownContainersIds.indexOf(containerId) > -1 &&
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.data.addContainerNode(this.containers[containerId]);
} else if (newShownContainersIds.indexOf(containerId) === -1 &&
this.shownContainersIds.indexOf(containerId) > -1) {
this.data.removeContainersNodes(containerId);
this.data.removeContainersNodes(containerId);
}
}
}
this.shownContainersIds = newShownContainersIds;
};
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.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 &&
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.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);
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);
};
$scope.network = new ContainersNetwork();
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});
var showFailure = function (event) {
Messages.error('Failure', e.data);
};
$scope.includeStopped = false;
$scope.toggleIncludeStopped = function () {
$scope.network.updateShownContainers([]);
update({all: $scope.includeStopped ? 1 : 0});
};
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});
};
}]);
}]);

View File

@ -1,4 +1,3 @@
<div class="col-xs-offset-1">
<!--<div class="sidebar span4">
<div ng-include="template" ng-controller="SideBarController"></div>
@ -7,12 +6,13 @@
<div class="col-xs-10" id="masthead" style="display:none">
<div class="jumbotron">
<h1>DockerUI</h1>
<p class="lead">The Linux container engine</p>
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
</div>
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10">
<div class="col-xs-5">
@ -27,7 +27,8 @@
<div class="col-xs-5 text-right">
<h3>Status</h3>
<canvas id="containers-chart" class="pull-right">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
<div id="chart-legend"></div>
</div>
@ -37,13 +38,15 @@
<div class="row">
<div class="col-xs-10" id="stats">
<h4>Containers created</h4>
<canvas id="containers-started-chart" width="700">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
<canvas id="containers-started-chart" width="700">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
<h4>Images created</h4>
<canvas id="images-created-chart" width="700">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
<canvas id="images-created-chart" width="700">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
</div>
</div>
</div>

View File

@ -1,72 +1,76 @@
angular.module('dashboard', [])
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function($scope, Container, Image, Settings, LineChart) {
$scope.predicate = '-Created';
$scope.containers = [];
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function ($scope, Container, Image, Settings, LineChart) {
$scope.predicate = '-Created';
$scope.containers = [];
var getStarted = function(data) {
$scope.totalContainers = data.length;
LineChart.build('#containers-started-chart', data, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
var s = $scope;
Image.query({}, function(d) {
s.totalImages = d.length;
LineChart.build('#images-created-chart', d, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
var getStarted = function (data) {
$scope.totalContainers = data.length;
LineChart.build('#containers-started-chart', data, function (c) {
return new Date(c.Created * 1000).toLocaleDateString();
});
var s = $scope;
Image.query({}, function (d) {
s.totalImages = d.length;
LineChart.build('#images-created-chart', d, function (c) {
return new Date(c.Created * 1000).toLocaleDateString();
});
});
};
var opts = {animation: false};
if (Settings.firstLoad) {
$('#stats').hide();
opts.animation = true;
Settings.firstLoad = false;
$('#masthead').show();
setTimeout(function () {
$('#masthead').slideUp('slow');
$('#stats').slideDown('slow');
}, 5000);
}
Container.query({all: 1}, function (d) {
var running = 0;
var ghost = 0;
var stopped = 0;
for (var i = 0; i < d.length; i++) {
var item = d[i];
if (item.Status === "Ghost") {
ghost += 1;
} else if (item.Status.indexOf('Exit') !== -1) {
stopped += 1;
} else {
running += 1;
$scope.containers.push(new ContainerViewModel(item));
}
}
getStarted(d);
var c = new Chart($('#containers-chart').get(0).getContext("2d"));
var data = [
{
value: running,
color: '#5bb75b',
title: 'Running'
}, // running
{
value: stopped,
color: '#C7604C',
title: 'Stopped'
}, // stopped
{
value: ghost,
color: '#E2EAE9',
title: 'Ghost'
} // ghost
];
c.Doughnut(data, opts);
var lgd = $('#chart-legend').get(0);
legend(lgd, data);
});
};
var opts = {animation:false};
if (Settings.firstLoad) {
$('#stats').hide();
opts.animation = true;
Settings.firstLoad = false;
$('#masthead').show();
setTimeout(function() {
$('#masthead').slideUp('slow');
$('#stats').slideDown('slow');
}, 5000);
}
Container.query({all: 1}, function(d) {
var running = 0;
var ghost = 0;
var stopped = 0;
for (var i = 0; i < d.length; i++) {
var item = d[i];
if (item.Status === "Ghost") {
ghost += 1;
} else if (item.Status.indexOf('Exit') !== -1) {
stopped += 1;
} else {
running += 1;
$scope.containers.push(new ContainerViewModel(item));
}
}
getStarted(d);
var c = new Chart($('#containers-chart').get(0).getContext("2d"));
var data = [
{
value: running,
color: '#5bb75b',
title: 'Running'
}, // running
{
value: stopped,
color: '#C7604C',
title: 'Stopped'
}, // stopped
{
value: ghost,
color: '#E2EAE9',
title: 'Ghost'
} // ghost
];
c.Doughnut(data, opts);
var lgd = $('#chart-legend').get(0);
legend(lgd, data);
});
}]);
}]);

View File

@ -1,6 +1,7 @@
<div class="row">
<div class="col-xs-12">
<h2>Events</h2>
<form class="form-inline">
<div class="form-group">
<label for="since">Since:</label>
@ -15,18 +16,18 @@
<br>
<table class="table">
<tbody>
<tr>
<th>Event</th>
<th>From</th>
<th>ID</th>
<th>Time</th>
</tr>
<tr ng-repeat="event in dockerEvents">
<td ng-bind="event.status"/>
<td ng-bind="event.from"/>
<td ng-bind="event.id"/>
<td ng-bind="event.time * 1000 | date:'medium'"/>
</tr>
<tr>
<th>Event</th>
<th>From</th>
<th>ID</th>
<th>Time</th>
</tr>
<tr ng-repeat="event in dockerEvents">
<td ng-bind="event.status"/>
<td ng-bind="event.from"/>
<td ng-bind="event.id"/>
<td ng-bind="event.time * 1000 | date:'medium'"/>
</tr>
</tbody>
</table>
</div>

View File

@ -1,6 +1,6 @@
angular.module('events', ['ngOboe'])
.controller('EventsController', ['Settings', '$scope', 'Oboe', 'Messages', '$timeout', function(Settings, $scope, oboe, Messages, $timeout) {
$scope.updateEvents = function() {
.controller('EventsController', ['Settings', '$scope', 'Oboe', 'Messages', '$timeout', function (Settings, $scope, oboe, Messages, $timeout) {
$scope.updateEvents = function () {
$scope.dockerEvents = [];
// TODO: Clean up URL building
@ -16,18 +16,18 @@ angular.module('events', ['ngOboe'])
}
oboe({
url: url,
pattern: '{id status time}'
})
.then(function(node) {
url: url,
pattern: '{id status time}'
})
.then(function (node) {
// finished loading
$timeout(function() {
$timeout(function () {
$scope.$apply();
});
}, function(error) {
}, function (error) {
// handle errors
Messages.error("Failure", error.data);
}, function(node) {
}, function (node) {
// node received
$scope.dockerEvents.push(node);
});

View File

@ -1,7 +1,9 @@
angular.module('footer', [])
.controller('FooterController', ['$scope', 'Settings', 'Docker', function($scope, Settings, Docker) {
$scope.template = 'app/components/footer/statusbar.html';
.controller('FooterController', ['$scope', 'Settings', 'Docker', function ($scope, Settings, Docker) {
$scope.template = 'app/components/footer/statusbar.html';
$scope.uiVersion = Settings.uiVersion;
Docker.get({}, function(d) { $scope.apiVersion = d.ApiVersion; });
}]);
$scope.uiVersion = Settings.uiVersion;
Docker.get({}, function (d) {
$scope.apiVersion = d.ApiVersion;
});
}]);

View File

@ -1,3 +1,6 @@
<footer class="center well">
<p><small>Docker API Version: <strong>{{ apiVersion }}</strong> UI Version: <strong>{{ uiVersion }}</strong> <a class="pull-right" href="https://github.com/crosbymichael/dockerui">dockerui</a></small></p>
<p>
<small>Docker API Version: <strong>{{ apiVersion }}</strong> UI Version: <strong>{{ uiVersion }}</strong> <a
class="pull-right" href="https://github.com/crosbymichael/dockerui">dockerui</a></small>
</p>
</footer>

View File

@ -5,59 +5,60 @@
</div>
<div class="detail">
<h4>Image: {{ tag }}</h4>
<div class="btn-group detail">
<button class="btn btn-success" data-toggle="modal" data-target="#create-modal">Create</button>
<button class="btn btn-success" data-toggle="modal" data-target="#create-modal">Create</button>
</div>
<div>
<h4>Containers created:</h4>
<canvas id="containers-started-chart" width="750">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
<h4>Containers created:</h4>
<canvas id="containers-started-chart" width="750">
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</canvas>
</div>
<table class="table table-striped">
<tbody>
<tr>
<td>Created:</td>
<td>{{ image.Created }}</td>
</tr>
<tr>
<td>Parent:</td>
<td><a href="#/images/{{ image.Parent }}/">{{ image.Parent }}</a></td>
</tr>
<tr>
<td>Size (Virtual Size):</td>
<td>{{ image.Size|humansize }} ({{ image.VirtualSize|humansize }})</td>
</tr>
<tbody>
<tr>
<td>Created:</td>
<td>{{ image.Created }}</td>
</tr>
<tr>
<td>Parent:</td>
<td><a href="#/images/{{ image.Parent }}/">{{ image.Parent }}</a></td>
</tr>
<tr>
<td>Size (Virtual Size):</td>
<td>{{ image.Size|humansize }} ({{ image.VirtualSize|humansize }})</td>
</tr>
<tr>
<td>Hostname:</td>
<td>{{ image.ContainerConfig.Hostname }}</td>
</tr>
<tr>
<td>User:</td>
<td>{{ image.ContainerConfig.User }}</td>
</tr>
<tr>
<td>Cmd:</td>
<td>{{ image.ContainerConfig.Cmd }}</td>
</tr>
<tr>
<td>Volumes:</td>
<td>{{ image.ContainerConfig.Volumes }}</td>
</tr>
<tr>
<td>Volumes from:</td>
<td>{{ image.ContainerConfig.VolumesFrom }}</td>
</tr>
<tr>
<td>Built with:</td>
<td>Docker {{ image.DockerVersion }} on {{ image.Os}}, {{ image.Architecture }}</td>
</tr>
<tr>
<td>Hostname:</td>
<td>{{ image.ContainerConfig.Hostname }}</td>
</tr>
<tr>
<td>User:</td>
<td>{{ image.ContainerConfig.User }}</td>
</tr>
<tr>
<td>Cmd:</td>
<td>{{ image.ContainerConfig.Cmd }}</td>
</tr>
<tr>
<td>Volumes:</td>
<td>{{ image.ContainerConfig.Volumes }}</td>
</tr>
<tr>
<td>Volumes from:</td>
<td>{{ image.ContainerConfig.VolumesFrom }}</td>
</tr>
<tr>
<td>Built with:</td>
<td>Docker {{ image.DockerVersion }} on {{ image.Os}}, {{ image.Architecture }}</td>
</tr>
</tbody>
</table>
@ -69,17 +70,18 @@
<div class="span5">
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getHistory()"></i>
</div>
</div>
</div>
<div class="well well-large">
<ul>
<li ng-repeat="change in history">
<strong>{{ change.Id }}</strong>: Created: {{ change.Created|getdate }} Created by: {{ change.CreatedBy }}
<strong>{{ change.Id }}</strong>: Created: {{ change.Created|getdate }} Created by: {{ change.CreatedBy
}}
</li>
</ul>
</div>
<hr />
<hr/>
<div class="row-fluid">
<form class="form-inline" role="form">
@ -94,12 +96,12 @@
<input type="checkbox" ng-model="tag.force" class="form-control"/> Force?
</label>
</div>
<input type="button" ng-click="updateTag()" value="Tag" class="btn btn-primary"/>
<input type="button" ng-click="updateTag()" value="Tag" class="btn btn-primary"/>
</fieldset>
</form>
</div>
<hr />
<hr/>
<div class="btn-remove">
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Image</button>

View File

@ -1,72 +1,74 @@
angular.module('image', [])
.controller('ImageController', ['$scope', '$q', '$routeParams', '$location', 'Image', 'Container', 'Messages', 'LineChart',
function($scope, $q, $routeParams, $location, Image, Container, Messages, LineChart) {
$scope.history = [];
$scope.tag = {repo: '', force: false};
.controller('ImageController', ['$scope', '$q', '$routeParams', '$location', 'Image', 'Container', 'Messages', 'LineChart',
function ($scope, $q, $routeParams, $location, Image, Container, Messages, LineChart) {
$scope.history = [];
$scope.tag = {repo: '', force: false};
$scope.remove = function() {
Image.remove({id: $routeParams.id}, function(d) {
Messages.send("Image Removed", $routeParams.id);
}, function(e) {
$scope.error = e.data;
$('#error-message').show();
});
};
$scope.remove = function () {
Image.remove({id: $routeParams.id}, function (d) {
Messages.send("Image Removed", $routeParams.id);
}, function (e) {
$scope.error = e.data;
$('#error-message').show();
});
};
$scope.getHistory = function() {
Image.history({id: $routeParams.id}, function(d) {
$scope.history = d;
});
};
$scope.getHistory = function () {
Image.history({id: $routeParams.id}, function (d) {
$scope.history = d;
});
};
$scope.updateTag = function() {
var tag = $scope.tag;
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) {
Messages.send("Tag Added", $routeParams.id);
}, function(e) {
$scope.error = e.data;
$('#error-message').show();
});
};
$scope.updateTag = function () {
var tag = $scope.tag;
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function (d) {
Messages.send("Tag Added", $routeParams.id);
}, function (e) {
$scope.error = e.data;
$('#error-message').show();
});
};
function getContainersFromImage($q, Container, tag) {
var defer = $q.defer();
Container.query({all:1, notruc:1}, function(d) {
var containers = [];
for (var i = 0; i < d.length; i++) {
var c = d[i];
if (c.Image === tag) {
containers.push(new ContainerViewModel(c));
}
function getContainersFromImage($q, Container, tag) {
var defer = $q.defer();
Container.query({all: 1, notruc: 1}, function (d) {
var containers = [];
for (var i = 0; i < d.length; i++) {
var c = d[i];
if (c.Image === tag) {
containers.push(new ContainerViewModel(c));
}
}
defer.resolve(containers);
});
return defer.promise;
}
defer.resolve(containers);
});
return defer.promise;
}
Image.get({id: $routeParams.id}, function (d) {
$scope.image = d;
$scope.tag = d.id;
var t = $routeParams.tag;
if (t && t !== ":") {
$scope.tag = t;
var promise = getContainersFromImage($q, Container, t);
Image.get({id: $routeParams.id}, function(d) {
$scope.image = d;
$scope.tag = d.id;
var t = $routeParams.tag;
if (t && t !== ":") {
$scope.tag = t;
var promise = getContainersFromImage($q, Container, t);
promise.then(function(containers) {
LineChart.build('#containers-started-chart', containers, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
promise.then(function (containers) {
LineChart.build('#containers-started-chart', containers, function (c) {
return new Date(c.Created * 1000).toLocaleDateString();
});
});
}
}, function (e) {
if (e.status === 404) {
$('.detail').hide();
$scope.error = "Image not found.<br />" + $routeParams.id;
} else {
$scope.error = e.data;
}
$('#error-message').show();
});
}
}, function(e) {
if (e.status === 404) {
$('.detail').hide();
$scope.error = "Image not found.<br />" + $routeParams.id;
} else {
$scope.error = e.data;
}
$('#error-message').show();
});
$scope.getHistory();
}]);
$scope.getHistory();
}]);

View File

@ -5,7 +5,8 @@
<ul class="nav nav-pills">
<li class="dropdown">
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b class="caret"></b></a>
<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>
@ -14,21 +15,21 @@
</ul>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Action</th>
<th>Id</th>
<th>Repository</th>
<th>VirtualSize</th>
<th>Created</th>
</tr>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Action</th>
<th>Id</th>
<th>Repository</th>
<th>VirtualSize</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="image in images | orderBy:predicate">
<td><input type="checkbox" ng-model="image.Checked" /></td>
<td><a href="#/images/{{ image.Id }}/?tag={{ image|repotag }}">{{ image.Id|truncate:20}}</a></td>
<td>{{ image|repotag }}</td>
<td>{{ image.VirtualSize|humansize }}</td>
<td>{{ image.Created|getdate }}</td>
</tr>
<tr ng-repeat="image in images | orderBy:predicate">
<td><input type="checkbox" ng-model="image.Checked"/></td>
<td><a href="#/images/{{ image.Id }}/?tag={{ image|repotag }}">{{ image.Id|truncate:20}}</a></td>
<td>{{ image|repotag }}</td>
<td>{{ image.VirtualSize|humansize }}</td>
<td>{{ image.Created|getdate }}</td>
</tr>
</tbody>
</table>

View File

@ -1,52 +1,54 @@
angular.module('images', [])
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
function($scope, Image, ViewSpinner, Messages) {
$scope.toggle = false;
$scope.predicate = '-Created';
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
function ($scope, Image, ViewSpinner, Messages) {
$scope.toggle = false;
$scope.predicate = '-Created';
$scope.showBuilder = function() {
$('#build-modal').modal('show');
};
$scope.showBuilder = function () {
$('#build-modal').modal('show');
};
$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.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.toggleSelectAll = function() {
angular.forEach($scope.images, function(i) {
i.Checked = $scope.toggle;
});
};
$scope.toggleSelectAll = function () {
angular.forEach($scope.images, function (i) {
i.Checked = $scope.toggle;
});
};
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();
});
}]);
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();
});
}]);

View File

@ -1,109 +1,110 @@
<div class="detail">
<h2>Docker Information</h2>
<div>
<p class="lead">
<strong>API Endpoint: </strong>{{ endpoint }}<br />
<strong>API Version: </strong>{{ docker.ApiVersion }}<br />
<strong>Docker version: </strong>{{ docker.Version }}<br />
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br />
<strong>Go Version: </strong>{{ docker.GoVersion }}<br />
<strong>API Endpoint: </strong>{{ endpoint }}<br/>
<strong>API Version: </strong>{{ docker.ApiVersion }}<br/>
<strong>Docker version: </strong>{{ docker.Version }}<br/>
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br/>
<strong>Go Version: </strong>{{ docker.GoVersion }}<br/>
</p>
</div>
<table class="table table-striped">
<tbody>
<tr>
<td>Containers:</td>
<td>{{ info.Containers }}</td>
</tr>
<tr>
<td>Images:</td>
<td>{{ info.Images }}</td>
</tr>
<tr>
<td>Debug:</td>
<td>{{ info.Debug }}</td>
</tr>
<tr>
<td>CPUs:</td>
<td>{{ info.NCPU }}</td>
</tr>
<tr>
<td>Total Memory:</td>
<td>{{ info.MemTotal|humansize }}</td>
</tr>
<tr>
<td>Operating System:</td>
<td>{{ info.OperatingSystem }}</td>
</tr>
<tr>
<td>Kernel Version:</td>
<td>{{ info.KernelVersion }}</td>
</tr>
<tr>
<td>ID:</td>
<td>{{ info.ID }}</td>
</tr>
<tr>
<td>Labels:</td>
<td>{{ info.Labels }}</td>
</tr>
<tr>
<td>File Descriptors:</td>
<td>{{ info.NFd }}</td>
</tr>
<tr>
<td>Goroutines:</td>
<td>{{ info.NGoroutines }}</td>
</tr>
<tr>
<td>Storage Driver:</td>
<td>{{ info.Driver }}</td>
</tr>
<tr>
<td>Storage Driver Status:</td>
<td>
<tbody>
<tr>
<td>Containers:</td>
<td>{{ info.Containers }}</td>
</tr>
<tr>
<td>Images:</td>
<td>{{ info.Images }}</td>
</tr>
<tr>
<td>Debug:</td>
<td>{{ info.Debug }}</td>
</tr>
<tr>
<td>CPUs:</td>
<td>{{ info.NCPU }}</td>
</tr>
<tr>
<td>Total Memory:</td>
<td>{{ info.MemTotal|humansize }}</td>
</tr>
<tr>
<td>Operating System:</td>
<td>{{ info.OperatingSystem }}</td>
</tr>
<tr>
<td>Kernel Version:</td>
<td>{{ info.KernelVersion }}</td>
</tr>
<tr>
<td>ID:</td>
<td>{{ info.ID }}</td>
</tr>
<tr>
<td>Labels:</td>
<td>{{ info.Labels }}</td>
</tr>
<tr>
<td>File Descriptors:</td>
<td>{{ info.NFd }}</td>
</tr>
<tr>
<td>Goroutines:</td>
<td>{{ info.NGoroutines }}</td>
</tr>
<tr>
<td>Storage Driver:</td>
<td>{{ info.Driver }}</td>
</tr>
<tr>
<td>Storage Driver Status:</td>
<td>
<p ng-repeat="val in info.DriverStatus">
{{ val[0] }}: {{ val[1] }}
</p>
</td>
</tr>
<tr>
<td>Execution Driver:</td>
<td>{{ info.ExecutionDriver }}</td>
</tr>
<tr>
<td>Events:</td>
<td><a href="#/events">Events</a></td>
</tr>
<tr>
<td>IPv4 Forwarding:</td>
<td>{{ info.IPv4Forwarding }}</td>
</tr>
<tr>
<td>Index Server Address:</td>
<td>{{ info.IndexServerAddress }}</td>
</tr>
<tr>
<td>Init Path:</td>
<td>{{ info.InitPath }}</td>
</tr>
<tr>
<td>Docker Root Directory:</td>
<td>{{ info.DockerRootDir }}</td>
</tr>
<tr>
<td>Init SHA1</td>
<td>{{ info.InitSha1 }}</td>
</tr>
<tr>
<td>Memory Limit:</td>
<td>{{ info.MemoryLimit }}</td>
</tr>
<tr>
<td>Swap Limit:</td>
<td>{{ info.SwapLimit }}</td>
</tr>
</td>
</tr>
<tr>
<td>Execution Driver:</td>
<td>{{ info.ExecutionDriver }}</td>
</tr>
<tr>
<td>Events:</td>
<td><a href="#/events">Events</a></td>
</tr>
<tr>
<td>IPv4 Forwarding:</td>
<td>{{ info.IPv4Forwarding }}</td>
</tr>
<tr>
<td>Index Server Address:</td>
<td>{{ info.IndexServerAddress }}</td>
</tr>
<tr>
<td>Init Path:</td>
<td>{{ info.InitPath }}</td>
</tr>
<tr>
<td>Docker Root Directory:</td>
<td>{{ info.DockerRootDir }}</td>
</tr>
<tr>
<td>Init SHA1</td>
<td>{{ info.InitSha1 }}</td>
</tr>
<tr>
<td>Memory Limit:</td>
<td>{{ info.MemoryLimit }}</td>
</tr>
<tr>
<td>Swap Limit:</td>
<td>{{ info.SwapLimit }}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -1,11 +1,15 @@
angular.module('info', [])
.controller('InfoController', ['$scope', 'System', 'Docker', 'Settings', 'Messages',
function($scope, System, Docker, Settings, Messages) {
$scope.info = {};
$scope.docker = {};
$scope.endpoint = Settings.endpoint;
$scope.apiVersion = Settings.version;
.controller('InfoController', ['$scope', 'System', 'Docker', 'Settings', 'Messages',
function ($scope, System, Docker, Settings, Messages) {
$scope.info = {};
$scope.docker = {};
$scope.endpoint = Settings.endpoint;
$scope.apiVersion = Settings.version;
Docker.get({}, function(d) { $scope.docker = d; });
System.get({}, function(d) { $scope.info = d; });
}]);
Docker.get({}, function (d) {
$scope.docker = d;
});
System.get({}, function (d) {
$scope.info = d;
});
}]);

View File

@ -1,10 +1,10 @@
<div class="masthead">
<div class="masthead">
<h3 class="text-muted">DockerUI</h3>
<ul class="nav well">
<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>
</ul>
</div>

View File

@ -1,4 +1,4 @@
angular.module('masthead', [])
.controller('MastheadController', ['$scope', function($scope) {
$scope.template = 'app/components/masthead/masthead.html';
}]);
.controller('MastheadController', ['$scope', function ($scope) {
$scope.template = 'app/components/masthead/masthead.html';
}]);

View File

@ -13,19 +13,23 @@
</div>-->
<div class="form-group">
<label>Registry:</label>
<input type="text" ng-model="config.registry" class="form-control" placeholder="Registry. Leave empty to user docker hub"/>
<input type="text" ng-model="config.registry" class="form-control"
placeholder="Registry. Leave empty to user docker hub"/>
</div>
<div class="form-group">
<label>Repo:</label>
<input type="text" ng-model="config.repo" class="form-control" placeholder="Repository - usually your username."/>
<input type="text" ng-model="config.repo" class="form-control"
placeholder="Repository - usually your username."/>
</div>
<div class="form-group">
<label>Image Name:</label>
<input type="text" ng-model="config.fromImage" class="form-control" placeholder="Image name" required/>
<input type="text" ng-model="config.fromImage" class="form-control" placeholder="Image name"
required/>
</div>
<div class="form-group">
<label>Tag Name:</label>
<input type="text" ng-model="config.tag" class="form-control" placeholder="Tag name. If empty it will download ALL tags."/>
<input type="text" ng-model="config.tag" class="form-control"
placeholder="Tag name. If empty it will download ALL tags."/>
</div>
</form>
</div>

View File

@ -1,9 +1,9 @@
angular.module('pullImage', [])
.controller('PullImageController', ['$scope', '$log', 'Dockerfile', 'Messages', 'Image', 'ViewSpinner',
function($scope, $log, Dockerfile, Messages, Image, ViewSpinner) {
function ($scope, $log, Dockerfile, Messages, Image, ViewSpinner) {
$scope.template = 'app/components/pullImage/pullImage.html';
$scope.init = function() {
$scope.init = function () {
$scope.config = {
registry: '',
repo: '',
@ -18,7 +18,7 @@ angular.module('pullImage', [])
Messages.error('Error', errorMsgFilter(e));
}
$scope.pull = function() {
$scope.pull = function () {
$('#error-message').hide();
var config = angular.copy($scope.config);
var imageName = (config.registry ? config.registry + '/' : '' ) +
@ -28,10 +28,10 @@ angular.module('pullImage', [])
ViewSpinner.spin();
$('#pull-modal').modal('hide');
Image.create(config, function(data) {
Image.create(config, function (data) {
ViewSpinner.stop();
if (data.constructor === Array) {
var f = data.length > 0 && data[data.length-1].hasOwnProperty('error');
var f = data.length > 0 && data[data.length - 1].hasOwnProperty('error');
//check for error
if (f) {
var d = data[data.length - 1];
@ -46,7 +46,7 @@ angular.module('pullImage', [])
Messages.send("Image Added", imageName);
$scope.init();
}
}, function(e) {
}, function (e) {
ViewSpinner.stop();
$scope.error = "Cannot pull image " + imageName + " Reason: " + e.data;
$('#pull-modal').modal('show');

View File

@ -1,11 +1,11 @@
<div class="well">
<strong>Running containers:</strong>
<br />
<br/>
<strong>Endpoint: </strong>{{ endpoint }}
<ul>
<li ng-repeat="container in containers">
<a href="#/containers/{{ container.Id }}/">{{ container.Id|truncate:20 }}</a>
<span class="pull-right label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
<span class="pull-right label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
</li>
</ul>
</div>

View File

@ -1,11 +1,11 @@
angular.module('sidebar', [])
.controller('SideBarController', ['$scope', 'Container', 'Settings',
function($scope, Container, Settings) {
$scope.template = 'partials/sidebar.html';
$scope.containers = [];
$scope.endpoint = Settings.endpoint;
.controller('SideBarController', ['$scope', 'Container', 'Settings',
function ($scope, Container, Settings) {
$scope.template = 'partials/sidebar.html';
$scope.containers = [];
$scope.endpoint = Settings.endpoint;
Container.query({all: 0}, function(d) {
$scope.containers = d;
});
}]);
Container.query({all: 0}, function (d) {
$scope.containers = d;
});
}]);

View File

@ -1,147 +1,153 @@
angular.module('startContainer', ['ui.bootstrap'])
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter',
function($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) {
$scope.template = 'app/components/startContainer/startcontainer.html';
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter',
function ($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) {
$scope.template = 'app/components/startContainer/startcontainer.html';
Container.query({all: 1}, function(d) {
$scope.containerNames = d.map(function(container){
return containernameFilter(container);
});
});
Container.query({all: 1}, function (d) {
$scope.containerNames = d.map(function (container) {
return containernameFilter(container);
});
});
$scope.config = {
Env: [],
Volumes: [],
SecurityOpts: [],
HostConfig: {
PortBindings: [],
Binds: [],
Links: [],
Dns: [],
DnsSearch: [],
VolumesFrom: [],
CapAdd: [],
CapDrop: [],
Devices: [],
LxcConf: [],
ExtraHosts: []
}
};
$scope.menuStatus = {
containerOpen: true,
hostConfigOpen: false
};
function failedRequestHandler(e, Messages) {
Messages.error('Error', errorMsgFilter(e));
}
function rmEmptyKeys(col) {
for (var key in col) {
if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
delete col[key];
}
}
}
function getNames(arr) {
return arr.map(function(item) {return item.name;});
}
$scope.create = function() {
// Copy the config before transforming fields to the remote API format
var config = angular.copy($scope.config);
config.Image = $routeParams.id;
if (config.Cmd && config.Cmd[0] === "[") {
config.Cmd = angular.fromJson(config.Cmd);
} else if (config.Cmd) {
config.Cmd = config.Cmd.split(' ');
}
config.Env = config.Env.map(function(envar) {return envar.name + '=' + envar.value;});
config.Volumes = getNames(config.Volumes);
config.SecurityOpts = getNames(config.SecurityOpts);
config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom);
config.HostConfig.Binds = getNames(config.HostConfig.Binds);
config.HostConfig.Links = getNames(config.HostConfig.Links);
config.HostConfig.Dns = getNames(config.HostConfig.Dns);
config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch);
config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd);
config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop);
config.HostConfig.LxcConf = config.HostConfig.LxcConf.reduce(function(prev, cur, idx){
prev[cur.name] = cur.value;
return prev;
}, {});
config.HostConfig.ExtraHosts = config.HostConfig.ExtraHosts.map(function(entry) {return entry.host + ':' + entry.ip;});
var ExposedPorts = {};
var PortBindings = {};
config.HostConfig.PortBindings.forEach(function(portBinding) {
var intPort = portBinding.intPort + "/tcp";
if (portBinding.protocol === "udp") {
intPort = portBinding.intPort + "/udp";
}
var binding = {
HostIp: portBinding.ip,
HostPort: portBinding.extPort
$scope.config = {
Env: [],
Volumes: [],
SecurityOpts: [],
HostConfig: {
PortBindings: [],
Binds: [],
Links: [],
Dns: [],
DnsSearch: [],
VolumesFrom: [],
CapAdd: [],
CapDrop: [],
Devices: [],
LxcConf: [],
ExtraHosts: []
}
};
if (portBinding.intPort) {
ExposedPorts[intPort] = {};
if (intPort in PortBindings) {
PortBindings[intPort].push(binding);
} else {
PortBindings[intPort] = [binding];
}
} else {
Messages.send('Warning', 'Internal port must be specified for PortBindings');
$scope.menuStatus = {
containerOpen: true,
hostConfigOpen: false
};
function failedRequestHandler(e, Messages) {
Messages.error('Error', errorMsgFilter(e));
}
});
config.ExposedPorts = ExposedPorts;
config.HostConfig.PortBindings = PortBindings;
// Remove empty fields from the request to avoid overriding defaults
rmEmptyKeys(config.HostConfig);
rmEmptyKeys(config);
var ctor = Container;
var loc = $location;
var s = $scope;
Container.create(config, function(d) {
if (d.Id) {
var reqBody = config.HostConfig || {};
reqBody.id = d.Id;
ctor.start(reqBody, function(cd) {
if (cd.id) {
Messages.send('Container Started', d.Id);
$('#create-modal').modal('hide');
loc.path('/containers/' + d.Id + '/');
} else {
failedRequestHandler(cd, Messages);
ctor.remove({id: d.Id}, function() {
Messages.send('Container Removed', d.Id);
});
}
}, function(e) {
failedRequestHandler(e, Messages);
});
} else {
failedRequestHandler(d, Messages);
function rmEmptyKeys(col) {
for (var key in col) {
if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
delete col[key];
}
}
}, function(e) {
failedRequestHandler(e, Messages);
});
};
}
$scope.addEntry = function(array, entry) {
array.push(entry);
};
$scope.rmEntry = function(array, entry) {
var idx = array.indexOf(entry);
array.splice(idx, 1);
};
}]);
function getNames(arr) {
return arr.map(function (item) {
return item.name;
});
}
$scope.create = function () {
// Copy the config before transforming fields to the remote API format
var config = angular.copy($scope.config);
config.Image = $routeParams.id;
if (config.Cmd && config.Cmd[0] === "[") {
config.Cmd = angular.fromJson(config.Cmd);
} else if (config.Cmd) {
config.Cmd = config.Cmd.split(' ');
}
config.Env = config.Env.map(function (envar) {
return envar.name + '=' + envar.value;
});
config.Volumes = getNames(config.Volumes);
config.SecurityOpts = getNames(config.SecurityOpts);
config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom);
config.HostConfig.Binds = getNames(config.HostConfig.Binds);
config.HostConfig.Links = getNames(config.HostConfig.Links);
config.HostConfig.Dns = getNames(config.HostConfig.Dns);
config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch);
config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd);
config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop);
config.HostConfig.LxcConf = config.HostConfig.LxcConf.reduce(function (prev, cur, idx) {
prev[cur.name] = cur.value;
return prev;
}, {});
config.HostConfig.ExtraHosts = config.HostConfig.ExtraHosts.map(function (entry) {
return entry.host + ':' + entry.ip;
});
var ExposedPorts = {};
var PortBindings = {};
config.HostConfig.PortBindings.forEach(function (portBinding) {
var intPort = portBinding.intPort + "/tcp";
if (portBinding.protocol === "udp") {
intPort = portBinding.intPort + "/udp";
}
var binding = {
HostIp: portBinding.ip,
HostPort: portBinding.extPort
};
if (portBinding.intPort) {
ExposedPorts[intPort] = {};
if (intPort in PortBindings) {
PortBindings[intPort].push(binding);
} else {
PortBindings[intPort] = [binding];
}
} else {
Messages.send('Warning', 'Internal port must be specified for PortBindings');
}
});
config.ExposedPorts = ExposedPorts;
config.HostConfig.PortBindings = PortBindings;
// Remove empty fields from the request to avoid overriding defaults
rmEmptyKeys(config.HostConfig);
rmEmptyKeys(config);
var ctor = Container;
var loc = $location;
var s = $scope;
Container.create(config, function (d) {
if (d.Id) {
var reqBody = config.HostConfig || {};
reqBody.id = d.Id;
ctor.start(reqBody, function (cd) {
if (cd.id) {
Messages.send('Container Started', d.Id);
$('#create-modal').modal('hide');
loc.path('/containers/' + d.Id + '/');
} else {
failedRequestHandler(cd, Messages);
ctor.remove({id: d.Id}, function () {
Messages.send('Container Removed', d.Id);
});
}
}, function (e) {
failedRequestHandler(e, Messages);
});
} else {
failedRequestHandler(d, Messages);
}
}, function (e) {
failedRequestHandler(e, Messages);
});
};
$scope.addEntry = function (array, entry) {
array.push(entry);
};
$scope.rmEntry = function (array, entry) {
var idx = array.indexOf(entry);
array.splice(idx, 1);
};
}]);

View File

@ -6,301 +6,413 @@
<h3>Create And Start Container From Image</h3>
</div>
<div class="modal-body">
<form role="form">
<accordion close-others="true">
<accordion-group heading="Container options" is-open="menuStatus.containerOpen">
<fieldset>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>Cmd:</label>
<input type="text" placeholder='["/bin/echo", "Hello world"]' ng-model="config.Cmd" class="form-control"/>
<small>Input commands as a raw string or JSON array</small>
</div>
<div class="form-group">
<label>Entrypoint:</label>
<input type="text" ng-model="config.Entrypoint" class="form-control" placeholder="./entrypoint.sh"/>
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" ng-model="config.name" class="form-control"/>
</div>
<div class="form-group">
<label>Hostname:</label>
<input type="text" ng-model="config.Hostname" class="form-control"/>
</div>
<div class="form-group">
<label>Domainname:</label>
<input type="text" ng-model="config.Domainname" class="form-control"/>
</div>
<div class="form-group">
<label>User:</label>
<input type="text" ng-model="config.User" class="form-control"/>
</div>
<div class="form-group">
<label>Memory:</label>
<input type="number" ng-model="config.Memory" class="form-control"/>
</div>
<div class="form-group">
<label>Volumes:</label>
<div ng-repeat="volume in config.Volumes">
<div class="form-group form-inline">
<input type="text" ng-model="volume.name" class="form-control" placeholder="/var/data"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.Volumes, volume)">Remove</button>
<form role="form">
<accordion close-others="true">
<accordion-group heading="Container options" is-open="menuStatus.containerOpen">
<fieldset>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>Cmd:</label>
<input type="text" placeholder='["/bin/echo", "Hello world"]'
ng-model="config.Cmd" class="form-control"/>
<small>Input commands as a raw string or JSON array</small>
</div>
<div class="form-group">
<label>Entrypoint:</label>
<input type="text" ng-model="config.Entrypoint" class="form-control"
placeholder="./entrypoint.sh"/>
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" ng-model="config.name" class="form-control"/>
</div>
<div class="form-group">
<label>Hostname:</label>
<input type="text" ng-model="config.Hostname" class="form-control"/>
</div>
<div class="form-group">
<label>Domainname:</label>
<input type="text" ng-model="config.Domainname" class="form-control"/>
</div>
<div class="form-group">
<label>User:</label>
<input type="text" ng-model="config.User" class="form-control"/>
</div>
<div class="form-group">
<label>Memory:</label>
<input type="number" ng-model="config.Memory" class="form-control"/>
</div>
<div class="form-group">
<label>Volumes:</label>
<div ng-repeat="volume in config.Volumes">
<div class="form-group form-inline">
<input type="text" ng-model="volume.name" class="form-control"
placeholder="/var/data"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.Volumes, volume)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.Volumes, {name: ''})">Add Volume
</button>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.Volumes, {name: ''})">Add Volume</button>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label>MemorySwap:</label>
<input type="number" ng-model="config.MemorySwap" class="form-control"/>
</div>
<div class="form-group">
<label>CpuShares:</label>
<input type="number" ng-model="config.CpuShares" class="form-control"/>
</div>
<div class="form-group">
<label>Cpuset:</label>
<input type="text" ng-model="config.Cpuset" class="form-control" placeholder="1,2"/>
<small>Input as comma-separated list of numbers</small>
</div>
<div class="form-group">
<label>WorkingDir:</label>
<input type="text" ng-model="config.WorkingDir" class="form-control" placeholder="/app"/>
</div>
<div class="form-group">
<label>MacAddress:</label>
<input type="text" ng-model="config.MacAddress" class="form-control" placeholder="12:34:56:78:9a:bc"/>
</div>
<div class="form-group">
<label for="networkDisabled">NetworkDisabled:</label>
<input id="networkDisabled" type="checkbox" ng-model="config.NetworkDisabled"/>
</div>
<div class="form-group">
<label for="tty">Tty:</label>
<input id="tty" type="checkbox" ng-model="config.Tty"/>
</div>
<div class="form-group">
<label for="openStdin">OpenStdin:</label>
<input id="openStdin" type="checkbox" ng-model="config.OpenStdin"/>
</div>
<div class="form-group">
<label for="stdinOnce">StdinOnce:</label>
<input id="stdinOnce" type="checkbox" ng-model="config.StdinOnce"/>
</div>
<div class="form-group">
<label>SecurityOpts:</label>
<div ng-repeat="opt in config.SecurityOpts">
<div class="form-group form-inline">
<input type="text" ng-model="opt.name" class="form-control" placeholder="label:type:svirt_apache"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.SecurityOpts, opt)">Remove</button>
<div class="col-xs-6">
<div class="form-group">
<label>MemorySwap:</label>
<input type="number" ng-model="config.MemorySwap" class="form-control"/>
</div>
<div class="form-group">
<label>CpuShares:</label>
<input type="number" ng-model="config.CpuShares" class="form-control"/>
</div>
<div class="form-group">
<label>Cpuset:</label>
<input type="text" ng-model="config.Cpuset" class="form-control"
placeholder="1,2"/>
<small>Input as comma-separated list of numbers</small>
</div>
<div class="form-group">
<label>WorkingDir:</label>
<input type="text" ng-model="config.WorkingDir" class="form-control"
placeholder="/app"/>
</div>
<div class="form-group">
<label>MacAddress:</label>
<input type="text" ng-model="config.MacAddress" class="form-control"
placeholder="12:34:56:78:9a:bc"/>
</div>
<div class="form-group">
<label for="networkDisabled">NetworkDisabled:</label>
<input id="networkDisabled" type="checkbox"
ng-model="config.NetworkDisabled"/>
</div>
<div class="form-group">
<label for="tty">Tty:</label>
<input id="tty" type="checkbox" ng-model="config.Tty"/>
</div>
<div class="form-group">
<label for="openStdin">OpenStdin:</label>
<input id="openStdin" type="checkbox" ng-model="config.OpenStdin"/>
</div>
<div class="form-group">
<label for="stdinOnce">StdinOnce:</label>
<input id="stdinOnce" type="checkbox" ng-model="config.StdinOnce"/>
</div>
<div class="form-group">
<label>SecurityOpts:</label>
<div ng-repeat="opt in config.SecurityOpts">
<div class="form-group form-inline">
<input type="text" ng-model="opt.name" class="form-control"
placeholder="label:type:svirt_apache"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.SecurityOpts, opt)">Remove
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.SecurityOpts, {name: ''})">Add Option</button>
</div>
</div>
</div>
<hr>
<div class="form-group">
<label>Env:</label>
<div ng-repeat="envar in config.Env">
<div class="form-group form-inline">
<div class="form-group">
<label class="sr-only">Variable Name:</label>
<input type="text" ng-model="envar.name" class="form-control" placeholder="NAME"/>
</div>
<div class="form-group">
<label class="sr-only">Variable Value:</label>
<input type="text" ng-model="envar.value" class="form-control" placeholder="value"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.Env, envar)">Remove</button>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.SecurityOpts, {name: ''})">Add Option
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.Env, {name: '', value: ''})">Add environment variable</button>
</div>
</fieldset>
</accordion-group>
<accordion-group heading="HostConfig options" is-open="menuStatus.hostConfigOpen">
<fieldset>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>Binds:</label>
<div ng-repeat="bind in config.HostConfig.Binds">
<div class="form-group form-inline">
<input type="text" ng-model="bind.name" class="form-control" placeholder="/host:/container"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Binds, bind)">Remove</button>
<hr>
<div class="form-group">
<label>Env:</label>
<div ng-repeat="envar in config.Env">
<div class="form-group form-inline">
<div class="form-group">
<label class="sr-only">Variable Name:</label>
<input type="text" ng-model="envar.name" class="form-control"
placeholder="NAME"/>
</div>
<div class="form-group">
<label class="sr-only">Variable Value:</label>
<input type="text" ng-model="envar.value" class="form-control"
placeholder="value"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.Env, envar)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Binds, {name: ''})">Add Bind</button>
</div>
<div class="form-group">
<label>Links:</label>
<div ng-repeat="link in config.HostConfig.Links">
<div class="form-group form-inline">
<input type="text" ng-model="link.name" class="form-control" placeholder="web:db">
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Links, link)">Remove</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Links, {name: ''})">Add Link</button>
</div>
<div class="form-group">
<label>Dns:</label>
<div ng-repeat="entry in config.HostConfig.Dns">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control" placeholder="8.8.8.8"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.Dns, entry)">Remove</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Dns, {name: ''})">Add entry</button>
</div>
<div class="form-group">
<label>DnsSearch:</label>
<div ng-repeat="entry in config.HostConfig.DnsSearch">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control" placeholder="example.com"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">Remove</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">Add entry</button>
</div>
<div class="form-group">
<label>CapAdd:</label>
<div ng-repeat="entry in config.HostConfig.CapAdd">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control" placeholder="cap_sys_admin"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.CapAdd, entry)">Remove</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">Add entry</button>
</div>
<div class="form-group">
<label>CapDrop:</label>
<div ng-repeat="entry in config.HostConfig.CapDrop">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control" placeholder="cap_sys_admin"/>
<button type="button" class="btn btn-danger btn-sm" ng-click="rmEntry(config.HostConfig.CapDrop, entry)">Remove</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">Add entry</button>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.Env, {name: '', value: ''})">Add environment
variable
</button>
</div>
<div class="col-xs-6">
<div class="form-group">
<label>NetworkMode:</label>
<input type="text" ng-model="config.HostConfig.NetworkMode" class="form-control" placeholder="bridge"/>
</div>
<div class="form-group">
<label for="publishAllPorts">PublishAllPorts:</label>
<input id="publishAllPorts" type="checkbox" ng-model="config.HostConfig.PublishAllPorts"/>
</div>
<div class="form-group">
<label for="privileged">Privileged:</label>
<input id="privileged" type="checkbox" ng-model="config.HostConfig.Privileged"/>
</div>
<div class="form-group">
<label>VolumesFrom:</label>
<div ng-repeat="volume in config.HostConfig.VolumesFrom">
<div class="form-group form-inline">
<select ng-model="volume.name" ng-options="name for name in containerNames track by name" class="form-control"/>
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.HostConfig.VolumesFrom, volume)">Remove</button>
</fieldset>
</accordion-group>
<accordion-group heading="HostConfig options" is-open="menuStatus.hostConfigOpen">
<fieldset>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>Binds:</label>
<div ng-repeat="bind in config.HostConfig.Binds">
<div class="form-group form-inline">
<input type="text" ng-model="bind.name" class="form-control"
placeholder="/host:/container"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.Binds, bind)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.Binds, {name: ''})">Add Bind
</button>
</div>
<div class="form-group">
<label>Links:</label>
<div ng-repeat="link in config.HostConfig.Links">
<div class="form-group form-inline">
<input type="text" ng-model="link.name" class="form-control"
placeholder="web:db">
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.Links, link)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.Links, {name: ''})">Add Link
</button>
</div>
<div class="form-group">
<label>Dns:</label>
<div ng-repeat="entry in config.HostConfig.Dns">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control"
placeholder="8.8.8.8"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.Dns, entry)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.Dns, {name: ''})">Add entry
</button>
</div>
<div class="form-group">
<label>DnsSearch:</label>
<div ng-repeat="entry in config.HostConfig.DnsSearch">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control"
placeholder="example.com"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">
Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">Add
entry
</button>
</div>
<div class="form-group">
<label>CapAdd:</label>
<div ng-repeat="entry in config.HostConfig.CapAdd">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control"
placeholder="cap_sys_admin"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.CapAdd, entry)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">Add entry
</button>
</div>
<div class="form-group">
<label>CapDrop:</label>
<div ng-repeat="entry in config.HostConfig.CapDrop">
<div class="form-group form-inline">
<input type="text" ng-model="entry.name" class="form-control"
placeholder="cap_sys_admin"/>
<button type="button" class="btn btn-danger btn-sm"
ng-click="rmEntry(config.HostConfig.CapDrop, entry)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">Add entry
</button>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.VolumesFrom, {name: ''})">Add volume</button>
</div>
<div class="form-group">
<label>RestartPolicy:</label>
<select ng-model="config.HostConfig.RestartPolicy.name">
<option value="">disabled</option>
<option value="always">always</option>
<option value="on-failure">on-failure</option>
</select>
<label>MaximumRetryCount:</label>
<input type="number" ng-model="config.HostConfig.RestartPolicy.MaximumRetryCount"/>
</div>
</div>
</div>
<hr>
<div class="form-group">
<label>ExtraHosts:</label>
<div ng-repeat="entry in config.HostConfig.ExtraHosts">
<div class="form-group form-inline">
<div class="col-xs-6">
<div class="form-group">
<label class="sr-only">Hostname:</label>
<input type="text" ng-model="entry.host" class="form-control" placeholder="hostname"/>
<label>NetworkMode:</label>
<input type="text" ng-model="config.HostConfig.NetworkMode"
class="form-control" placeholder="bridge"/>
</div>
<div class="form-group">
<label class="sr-only">IP Address:</label>
<input type="text" ng-model="entry.ip" class="form-control" placeholder="127.0.0.1"/>
<label for="publishAllPorts">PublishAllPorts:</label>
<input id="publishAllPorts" type="checkbox"
ng-model="config.HostConfig.PublishAllPorts"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.HostConfig.ExtraHosts, entry)">Remove</button>
<label for="privileged">Privileged:</label>
<input id="privileged" type="checkbox"
ng-model="config.HostConfig.Privileged"/>
</div>
<div class="form-group">
<label>VolumesFrom:</label>
<div ng-repeat="volume in config.HostConfig.VolumesFrom">
<div class="form-group form-inline">
<select ng-model="volume.name"
ng-options="name for name in containerNames track by name"
class="form-control"/>
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.HostConfig.VolumesFrom, volume)">
Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.VolumesFrom, {name: ''})">Add
volume
</button>
</div>
<div class="form-group">
<label>RestartPolicy:</label>
<select ng-model="config.HostConfig.RestartPolicy.name">
<option value="">disabled</option>
<option value="always">always</option>
<option value="on-failure">on-failure</option>
</select>
<label>MaximumRetryCount:</label>
<input type="number"
ng-model="config.HostConfig.RestartPolicy.MaximumRetryCount"/>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.ExtraHosts, {host: '', ip: ''})">Add extra host</button>
</div>
<div class="form-group">
<label>LxcConf:</label>
<div ng-repeat="entry in config.HostConfig.LxcConf">
<div class="form-group form-inline">
<div class="form-group">
<label class="sr-only">Name:</label>
<input type="text" ng-model="entry.name" class="form-control" placeholder="lxc.utsname"/>
</div>
<div class="form-group">
<label class="sr-only">Value:</label>
<input type="text" ng-model="entry.value" class="form-control" placeholder="docker"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.HostConfig.LxcConf, entry)">Remove</button>
<hr>
<div class="form-group">
<label>ExtraHosts:</label>
<div ng-repeat="entry in config.HostConfig.ExtraHosts">
<div class="form-group form-inline">
<div class="form-group">
<label class="sr-only">Hostname:</label>
<input type="text" ng-model="entry.host" class="form-control"
placeholder="hostname"/>
</div>
<div class="form-group">
<label class="sr-only">IP Address:</label>
<input type="text" ng-model="entry.ip" class="form-control"
placeholder="127.0.0.1"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.HostConfig.ExtraHosts, entry)">Remove
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.ExtraHosts, {host: '', ip: ''})">Add
extra host
</button>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.LxcConf, {name: '', value: ''})">Add Entry</button>
</div>
<div class="form-group">
<label>Devices:</label>
<div ng-repeat="device in config.HostConfig.Devices">
<div class="form-group form-inline inline-four">
<label class="sr-only">PathOnHost:</label>
<input type="text" ng-model="device.PathOnHost" class="form-control" placeholder="PathOnHost"/>
<label class="sr-only">PathInContainer:</label>
<input type="text" ng-model="device.PathInContainer" class="form-control" placeholder="PathInContainer"/>
<label class="sr-only">CgroupPermissions:</label>
<input type="text" ng-model="device.CgroupPermissions" class="form-control" placeholder="CgroupPermissions"/>
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.HostConfig.Devices, device)">Remove</button>
<div class="form-group">
<label>LxcConf:</label>
<div ng-repeat="entry in config.HostConfig.LxcConf">
<div class="form-group form-inline">
<div class="form-group">
<label class="sr-only">Name:</label>
<input type="text" ng-model="entry.name" class="form-control"
placeholder="lxc.utsname"/>
</div>
<div class="form-group">
<label class="sr-only">Value:</label>
<input type="text" ng-model="entry.value" class="form-control"
placeholder="docker"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.HostConfig.LxcConf, entry)">Remove
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.LxcConf, {name: '', value: ''})">Add
Entry
</button>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.Devices, { PathOnHost: '', PathInContainer: '', CgroupPermissions: ''})">Add Device</button>
</div>
<div class="form-group">
<label>PortBindings:</label>
<div ng-repeat="portBinding in config.HostConfig.PortBindings">
<div class="form-group form-inline inline-four">
<label class="sr-only">Host IP:</label>
<input type="text" ng-model="portBinding.ip" class="form-control" placeholder="Host IP Address"/>
<label class="sr-only">Host Port:</label>
<input type="text" ng-model="portBinding.extPort" class="form-control" placeholder="Host Port"/>
<label class="sr-only">Container port:</label>
<input type="text" ng-model="portBinding.intPort" class="form-control" placeholder="Container Port"/>
<select ng-model="portBinding.protocol">
<option value="">tcp</option>
<option value="udp">udp</option>
</select>
<button class="btn btn-danger btn-xs form-control" ng-click="rmEntry(config.HostConfig.PortBindings, portBinding)">Remove</button>
<div class="form-group">
<label>Devices:</label>
<div ng-repeat="device in config.HostConfig.Devices">
<div class="form-group form-inline inline-four">
<label class="sr-only">PathOnHost:</label>
<input type="text" ng-model="device.PathOnHost" class="form-control"
placeholder="PathOnHost"/>
<label class="sr-only">PathInContainer:</label>
<input type="text" ng-model="device.PathInContainer" class="form-control"
placeholder="PathInContainer"/>
<label class="sr-only">CgroupPermissions:</label>
<input type="text" ng-model="device.CgroupPermissions" class="form-control"
placeholder="CgroupPermissions"/>
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.HostConfig.Devices, device)">Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.Devices, { PathOnHost: '', PathInContainer: '', CgroupPermissions: ''})">
Add Device
</button>
</div>
<button type="button" class="btn btn-success btn-sm" ng-click="addEntry(config.HostConfig.PortBindings, {ip: '', extPort: '', intPort: ''})">Add Port Binding</button>
</div>
</fieldset>
</accordion-group>
</accordion>
<div class="form-group">
<label>PortBindings:</label>
<div ng-repeat="portBinding in config.HostConfig.PortBindings">
<div class="form-group form-inline inline-four">
<label class="sr-only">Host IP:</label>
<input type="text" ng-model="portBinding.ip" class="form-control"
placeholder="Host IP Address"/>
<label class="sr-only">Host Port:</label>
<input type="text" ng-model="portBinding.extPort" class="form-control"
placeholder="Host Port"/>
<label class="sr-only">Container port:</label>
<input type="text" ng-model="portBinding.intPort" class="form-control"
placeholder="Container Port"/>
<select ng-model="portBinding.protocol">
<option value="">tcp</option>
<option value="udp">udp</option>
</select>
<button class="btn btn-danger btn-xs form-control"
ng-click="rmEntry(config.HostConfig.PortBindings, portBinding)">
Remove
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(config.HostConfig.PortBindings, {ip: '', extPort: '', intPort: ''})">
Add Port Binding
</button>
</div>
</fieldset>
</accordion-group>
</accordion>
</form>
</div>
<div class="modal-footer">

View File

@ -5,16 +5,16 @@
<br>
<table class="table">
<tbody>
<tr>
<th>Time read</th>
<th>CPU usage</th>
<th>Stat</th>
</tr>
<tr ng-repeat="stat in dockerStats">
<td ng-bind="stat.read"/>
<td ng-bind="calculateCPUPercent(stat)"/>
<td ng-bind="stat"/>
</tr>
<tr>
<th>Time read</th>
<th>CPU usage</th>
<th>Stat</th>
</tr>
<tr ng-repeat="stat in dockerStats">
<td ng-bind="stat.read"/>
<td ng-bind="calculateCPUPercent(stat)"/>
<td ng-bind="stat"/>
</tr>
</tbody>
</table>
</div>

View File

@ -12,7 +12,9 @@ angular.module('stats', [])
function updateStats() {
Container.stats({id: $routeParams.id}, function (d) {
console.log(d);
var arr = Object.keys(d).map(function (key) {return d[key];});
var arr = Object.keys(d).map(function (key) {
return d[key];
});
if (arr.join('').indexOf('no such id') !== -1) {
Messages.error('Unable to retrieve container stats', 'Has this container been removed?');
return;

View File

@ -1,12 +1,12 @@
angular.module('dockerui.filters', [])
.filter('truncate', function() {
.filter('truncate', function () {
'use strict';
return function(text, length, end) {
return function (text, length, end) {
if (isNaN(length)) {
length = 10;
}
if (end === undefined){
if (end === undefined) {
end = '...';
}
@ -18,9 +18,9 @@ angular.module('dockerui.filters', [])
}
};
})
.filter('statusbadge', function() {
.filter('statusbadge', function () {
'use strict';
return function(text) {
return function (text) {
if (text === 'Ghost') {
return 'important';
} else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') {
@ -29,9 +29,9 @@ angular.module('dockerui.filters', [])
return 'success';
};
})
.filter('getstatetext', function() {
.filter('getstatetext', function () {
'use strict';
return function(state) {
return function (state) {
if (state === undefined) {
return '';
}
@ -47,9 +47,9 @@ angular.module('dockerui.filters', [])
return 'Stopped';
};
})
.filter('getstatelabel', function() {
.filter('getstatelabel', function () {
'use strict';
return function(state) {
return function (state) {
if (state === undefined) {
return '';
}
@ -63,45 +63,47 @@ angular.module('dockerui.filters', [])
return '';
};
})
.filter('humansize', function() {
.filter('humansize', function () {
'use strict';
return function(bytes) {
return function (bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return 'n/a';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]];
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]];
};
})
.filter('containername', function() {
.filter('containername', function () {
'use strict';
return function(container) {
return function (container) {
var name = container.Names[0];
return name.substring(1, name.length);
};
})
.filter('repotag', function() {
.filter('repotag', function () {
'use strict';
return function(image) {
return function (image) {
if (image.RepoTags && image.RepoTags.length > 0) {
var tag = image.RepoTags[0];
if (tag === '<none>:<none>') { tag = ''; }
if (tag === '<none>:<none>') {
tag = '';
}
return tag;
}
return '';
return '';
};
})
.filter('getdate', function() {
.filter('getdate', function () {
'use strict';
return function(data) {
return function (data) {
//Multiply by 1000 for the unix format
var date = new Date(data * 1000);
return date.toDateString();
};
})
.filter('errorMsg', function() {
return function(object) {
.filter('errorMsg', function () {
return function (object) {
var idx = 0;
var msg = '';
while (object[idx] && typeof(object[idx]) === 'string') {

View File

@ -25,16 +25,16 @@ angular.module('dockerui.services', ['ngResource'])
'use strict';
return {
commit: function (params, callback) {
$http({
method: 'POST',
url: Settings.url + '/commit',
params: {
'container': params.id,
'repo': params.repo
}
}).success(callback).error(function (data, status, headers, config) {
console.log(error, data);
});
$http({
method: 'POST',
url: Settings.url + '/commit',
params: {
'container': params.id,
'repo': params.repo
}
}).success(callback).error(function (data, status, headers, config) {
console.log(error, data);
});
}
};
})
@ -80,11 +80,13 @@ angular.module('dockerui.services', ['ngResource'])
get: {method: 'GET', params: {action: 'json'}},
search: {method: 'GET', params: {action: 'search'}},
history: {method: 'GET', params: {action: 'history'}, isArray: true},
create: {method: 'POST', isArray: true, transformResponse: [function f(data) {
var str = data.replace(/\n/g, " ").replace(/\}\W*\{/g, "}, {");
return angular.fromJson("[" + str + "]");
}],
params: {action: 'create', fromImage: '@fromImage', repo: '@repo', tag: '@tag', registry: '@registry'}},
create: {
method: 'POST', isArray: true, transformResponse: [function f(data) {
var str = data.replace(/\n/g, " ").replace(/\}\W*\{/g, "}, {");
return angular.fromJson("[" + str + "]");
}],
params: {action: 'create', fromImage: '@fromImage', repo: '@repo', tag: '@tag', registry: '@registry'}
},
insert: {method: 'POST', params: {id: '@id', action: 'insert'}},
push: {method: 'POST', params: {id: '@id', action: 'push'}},
tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo'}},

View File

@ -1,4 +1,3 @@
function ImageViewModel(data) {
this.Id = data.Id;
this.Tag = data.Tag;
@ -10,12 +9,12 @@ function ImageViewModel(data) {
}
function ContainerViewModel(data) {
this.Id = data.Id;
this.Image = data.Image;
this.Command = data.Command;
this.Created = data.Created;
this.SizeRw = data.SizeRw;
this.Status = data.Status;
this.Checked = false;
this.Names = data.Names;
this.Id = data.Id;
this.Image = data.Image;
this.Command = data.Command;
this.Created = data.Created;
this.SizeRw = data.SizeRw;
this.Status = data.Status;
this.Checked = false;
this.Names = data.Names;
}

View File

@ -11,19 +11,19 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-html2js');
// Default task.
grunt.registerTask('default', ['jshint','build','karma:unit']);
grunt.registerTask('build', ['clean','html2js','concat','recess:build', 'copy']);
grunt.registerTask('release', ['clean','html2js','uglify','jshint','karma:unit','concat:index', 'recess:min', 'copy']);
grunt.registerTask('default', ['jshint', 'build', 'karma:unit']);
grunt.registerTask('build', ['clean', 'html2js', 'concat', 'recess:build', 'copy']);
grunt.registerTask('release', ['clean', 'html2js', 'uglify', 'jshint', 'karma:unit', 'concat:index', 'recess:min', 'copy']);
grunt.registerTask('test-watch', ['karma:watch']);
// Print a timestamp (useful for when watching)
grunt.registerTask('timestamp', function() {
grunt.registerTask('timestamp', function () {
grunt.log.subhead(Date());
});
var karmaConfig = function(configFile, customOptions) {
var options = { configFile: configFile, keepalive: true };
var travisOptions = process.env.TRAVIS && { browsers: ['Firefox'], reporters: 'dots' };
var karmaConfig = function (configFile, customOptions) {
var options = {configFile: configFile, keepalive: true};
var travisOptions = process.env.TRAVIS && {browsers: ['Firefox'], reporters: 'dots'};
return grunt.util._.extend(options, customOptions, travisOptions);
};
@ -31,8 +31,7 @@ module.exports = function (grunt) {
grunt.initConfig({
distdir: 'dist',
pkg: grunt.file.readJSON('package.json'),
banner:
'/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
@ -50,12 +49,12 @@ module.exports = function (grunt) {
clean: ['<%= distdir %>/*'],
copy: {
assets: {
files: [{ dest: '<%= distdir %>/assets', src : '**', expand: true, cwd: 'assets/' }]
files: [{dest: '<%= distdir %>/assets', src: '**', expand: true, cwd: 'assets/'}]
}
},
karma: {
unit: { options: karmaConfig('test/unit/karma.conf.js') },
watch: { options: karmaConfig('test/unit/karma.conf.js', { singleRun:false, autoWatch: true}) }
unit: {options: karmaConfig('test/unit/karma.conf.js')},
watch: {options: karmaConfig('test/unit/karma.conf.js', {singleRun: false, autoWatch: true})}
},
html2js: {
app: {
@ -67,14 +66,14 @@ module.exports = function (grunt) {
module: '<%= pkg.name %>.templates'
}
},
concat:{
dist:{
concat: {
dist: {
options: {
banner: "<%= banner %>",
process: true
},
src:['<%= src.js %>', '<%= src.jsTpl %>'],
dest:'<%= distdir %>/<%= pkg.name %>.js'
src: ['<%= src.js %>', '<%= src.jsTpl %>'],
dest: '<%= distdir %>/<%= pkg.name %>.js'
},
index: {
src: ['index.html'],
@ -84,32 +83,32 @@ module.exports = function (grunt) {
}
},
angular: {
src:['assets/js/angularjs/1.3.15/angular.min.js',
'assets/js/angularjs/1.3.15/angular-route.min.js',
'assets/js/angularjs/1.3.15/angular-resource.min.js',
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
'assets/js/angular-oboe.min.js'],
src: ['assets/js/angularjs/1.3.15/angular.min.js',
'assets/js/angularjs/1.3.15/angular-route.min.js',
'assets/js/angularjs/1.3.15/angular-resource.min.js',
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
'assets/js/angular-oboe.min.js'],
dest: '<%= distdir %>/angular.js'
}
},
uglify: {
dist:{
dist: {
options: {
banner: "<%= banner %>"
},
src:['<%= src.js %>' ,'<%= src.jsTpl %>'],
dest:'<%= distdir %>/<%= pkg.name %>.js'
src: ['<%= src.js %>', '<%= src.jsTpl %>'],
dest: '<%= distdir %>/<%= pkg.name %>.js'
},
angular: {
src:['<%= concat.angular.src %>'],
src: ['<%= concat.angular.src %>'],
dest: '<%= distdir %>/angular.js'
}
},
recess: {
build: {
files: {
'<%= distdir %>/<%= pkg.name %>.css':
['<%= src.css %>'] },
'<%= distdir %>/<%= pkg.name %>.css': ['<%= src.css %>']
},
options: {
compile: true,
noOverqualifying: false // TODO: Added because of .nav class, rename
@ -124,29 +123,29 @@ module.exports = function (grunt) {
}
}
},
watch:{
watch: {
all: {
files:['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl.app %>', '<%= src.tpl.common %>', '<%= src.html %>'],
tasks:['default','timestamp']
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl.app %>', '<%= src.tpl.common %>', '<%= src.html %>'],
tasks: ['default', 'timestamp']
},
build: {
files:['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl.app %>', '<%= src.tpl.common %>', '<%= src.html %>'],
tasks:['build','timestamp']
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl.app %>', '<%= src.tpl.common %>', '<%= src.html %>'],
tasks: ['build', 'timestamp']
}
},
jshint:{
files:['gruntFile.js', '<%= src.js %>', '<%= src.jsTpl %>', '<%= src.specs %>', '<%= src.scenarios %>'],
options:{
curly:true,
eqeqeq:true,
immed:true,
latedef:true,
newcap:true,
noarg:true,
sub:true,
boss:true,
eqnull:true,
globals:{
jshint: {
files: ['gruntFile.js', '<%= src.js %>', '<%= src.jsTpl %>', '<%= src.specs %>', '<%= src.scenarios %>'],
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
boss: true,
eqnull: true,
globals: {
angular: false,
'$': false
}

View File

@ -1,35 +1,35 @@
{
"author": "Michael Crosby & Kevan Ahlquist",
"name": "dockerui",
"homepage": "https://github.com/crosbymichael/dockerui",
"version": "0.6.0",
"repository": {
"type": "git",
"url": "git@github.com:crosbymichael/dockerui.git"
},
"bugs": {
"url": "https://github.com/crosbymichael/dockerui/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://raw.githubusercontent.com/crosbymichael/dockerui/master/LICENSE"
}
],
"engines": {
"node": ">= 0.8.4"
},
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.0",
"grunt-recess": "~0.3",
"grunt-contrib-clean": "~0.4.0",
"grunt-contrib-copy": "~0.4.0",
"grunt-contrib-jshint": "~0.2.0",
"grunt-contrib-concat": "~0.1.3",
"grunt-contrib-uglify": "~0.1.1",
"grunt-karma": "~0.4.4",
"grunt-html2js": "~0.1.0",
"grunt-contrib-watch": "~0.3.1"
"author": "Michael Crosby & Kevan Ahlquist",
"name": "dockerui",
"homepage": "https://github.com/crosbymichael/dockerui",
"version": "0.6.0",
"repository": {
"type": "git",
"url": "git@github.com:crosbymichael/dockerui.git"
},
"bugs": {
"url": "https://github.com/crosbymichael/dockerui/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://raw.githubusercontent.com/crosbymichael/dockerui/master/LICENSE"
}
],
"engines": {
"node": ">= 0.8.4"
},
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.0",
"grunt-recess": "~0.3",
"grunt-contrib-clean": "~0.4.0",
"grunt-contrib-copy": "~0.4.0",
"grunt-contrib-jshint": "~0.2.0",
"grunt-contrib-concat": "~0.1.3",
"grunt-contrib-uglify": "~0.1.1",
"grunt-karma": "~0.4.4",
"grunt-html2js": "~0.1.0",
"grunt-contrib-watch": "~0.3.1"
}
}

View File

@ -1,19 +1,19 @@
describe('startContainerController', function() {
describe('startContainerController', function () {
var scope, $location, createController, mockContainer, $httpBackend;
beforeEach(angular.mock.module('dockerui'));
beforeEach(inject(function($rootScope, $controller, _$location_) {
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function() {
createController = function () {
return $controller('StartContainerController', {
'$scope': scope
});
};
angular.mock.inject(function(_Container_, _$httpBackend_) {
angular.mock.inject(function (_Container_, _$httpBackend_) {
mockContainer = _Container_;
$httpBackend = _$httpBackend_;
});
@ -34,8 +34,9 @@ describe('startContainerController', function() {
'Status': 'Up 2 minutes'
}]);
}
describe('Create and start a container with port bindings', function() {
it('should issue a correct create request to the Docker remote API', function() {
describe('Create and start a container with port bindings', function () {
it('should issue a correct create request to the Docker remote API', function () {
var controller = createController();
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
var expectedBody = {
@ -76,8 +77,8 @@ describe('startContainerController', function() {
});
});
describe('Create and start a container with environment variables', function() {
it('should issue a correct create request to the Docker remote API', function() {
describe('Create and start a container with environment variables', function () {
it('should issue a correct create request to the Docker remote API', function () {
var controller = createController();
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
var expectedBody = {
@ -110,8 +111,8 @@ describe('startContainerController', function() {
});
});
describe('Create and start a container with volumesFrom', function() {
it('should issue a correct create request to the Docker remote API', function() {
describe('Create and start a container with volumesFrom', function () {
it('should issue a correct create request to the Docker remote API', function () {
var controller = createController();
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
var expectedBody = {
@ -133,15 +134,15 @@ describe('startContainerController', function() {
});
scope.config.name = 'container-name';
scope.config.HostConfig.VolumesFrom = [{name: 'parent'}, {name:'other:ro'}];
scope.config.HostConfig.VolumesFrom = [{name: 'parent'}, {name: 'other:ro'}];
scope.create();
$httpBackend.flush();
});
});
describe('Create and start a container with multiple options', function() {
it('should issue a correct create request to the Docker remote API', function() {
describe('Create and start a container with multiple options', function () {
it('should issue a correct create request to the Docker remote API', function () {
var controller = createController();
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
var expectedBody = {
@ -154,8 +155,12 @@ describe('startContainerController', function() {
DnsSearch: ['example.com'],
CapAdd: ['cap_sys_admin'],
CapDrop: ['cap_foo_bar'],
Devices: [{ 'PathOnHost': '/dev/deviceName', 'PathInContainer': '/dev/deviceName', 'CgroupPermissions': 'mrw'}],
LxcConf: {'lxc.utsname':'docker'},
Devices: [{
'PathOnHost': '/dev/deviceName',
'PathInContainer': '/dev/deviceName',
'CgroupPermissions': 'mrw'
}],
LxcConf: {'lxc.utsname': 'docker'},
ExtraHosts: ['hostname:127.0.0.1'],
RestartPolicy: {name: 'always', MaximumRetryCount: 5}
},
@ -190,7 +195,11 @@ describe('startContainerController', function() {
scope.config.HostConfig.PublishAllPorts = true;
scope.config.HostConfig.Privileged = true;
scope.config.HostConfig.RestartPolicy = {name: 'always', MaximumRetryCount: 5};
scope.config.HostConfig.Devices = [{ 'PathOnHost': '/dev/deviceName', 'PathInContainer': '/dev/deviceName', 'CgroupPermissions': 'mrw'}];
scope.config.HostConfig.Devices = [{
'PathOnHost': '/dev/deviceName',
'PathInContainer': '/dev/deviceName',
'CgroupPermissions': 'mrw'
}];
scope.config.HostConfig.LxcConf = [{name: 'lxc.utsname', value: 'docker'}];
scope.config.HostConfig.ExtraHosts = [{host: 'hostname', ip: '127.0.0.1'}];

View File

@ -2,40 +2,40 @@ describe('filters', function () {
beforeEach(module('dockerui.filters'));
describe('truncate', function () {
it('should truncate the string to 10 characters ending in "..." by default', inject(function(truncateFilter) {
it('should truncate the string to 10 characters ending in "..." by default', inject(function (truncateFilter) {
expect(truncateFilter('this is 20 chars long')).toBe('this is...');
}));
it('should truncate the string to 7 characters ending in "..."', inject(function(truncateFilter) {
it('should truncate the string to 7 characters ending in "..."', inject(function (truncateFilter) {
expect(truncateFilter('this is 20 chars long', 7)).toBe('this...');
}));
it('should truncate the string to 10 characters ending in "???"', inject(function(truncateFilter) {
it('should truncate the string to 10 characters ending in "???"', inject(function (truncateFilter) {
expect(truncateFilter('this is 20 chars long', 10, '???')).toBe('this is???');
}));
});
describe('statusbadge', function () {
it('should be "important" when input is "Ghost"', inject(function(statusbadgeFilter) {
it('should be "important" when input is "Ghost"', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Ghost')).toBe('important');
}));
it('should be "success" when input is "Exit 0"', inject(function(statusbadgeFilter) {
it('should be "success" when input is "Exit 0"', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Exit 0')).toBe('success');
}));
it('should be "warning" when exit code is non-zero', inject(function(statusbadgeFilter) {
it('should be "warning" when exit code is non-zero', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Exit 1')).toBe('warning');
}));
});
describe('getstatetext', function () {
it('should return an empty string when state is undefined', inject(function(getstatetextFilter) {
it('should return an empty string when state is undefined', inject(function (getstatetextFilter) {
expect(getstatetextFilter(undefined)).toBe('');
}));
it('should detect a Ghost state', inject(function(getstatetextFilter) {
it('should detect a Ghost state', inject(function (getstatetextFilter) {
var state = {
Ghost: true,
Running: true,
@ -44,7 +44,7 @@ describe('filters', function () {
expect(getstatetextFilter(state)).toBe('Ghost');
}));
it('should detect a Paused state', inject(function(getstatetextFilter) {
it('should detect a Paused state', inject(function (getstatetextFilter) {
var state = {
Ghost: false,
Running: true,
@ -53,7 +53,7 @@ describe('filters', function () {
expect(getstatetextFilter(state)).toBe('Running (Paused)');
}));
it('should detect a Running state', inject(function(getstatetextFilter) {
it('should detect a Running state', inject(function (getstatetextFilter) {
var state = {
Ghost: false,
Running: true,
@ -62,7 +62,7 @@ describe('filters', function () {
expect(getstatetextFilter(state)).toBe('Running');
}));
it('should detect a Stopped state', inject(function(getstatetextFilter) {
it('should detect a Stopped state', inject(function (getstatetextFilter) {
var state = {
Ghost: false,
Running: false,
@ -73,11 +73,11 @@ describe('filters', function () {
});
describe('getstatelabel', function () {
it('should return an empty string when state is undefined', inject(function(getstatelabelFilter) {
it('should return an empty string when state is undefined', inject(function (getstatelabelFilter) {
expect(getstatelabelFilter(undefined)).toBe('');
}));
it('should return label-important when a ghost state is detected', inject(function(getstatelabelFilter) {
it('should return label-important when a ghost state is detected', inject(function (getstatelabelFilter) {
var state = {
Ghost: true,
Running: true,
@ -86,7 +86,7 @@ describe('filters', function () {
expect(getstatelabelFilter(state)).toBe('label-important');
}));
it('should return label-success when a running state is detected', inject(function(getstatelabelFilter) {
it('should return label-success when a running state is detected', inject(function (getstatelabelFilter) {
var state = {
Ghost: false,
Running: true,
@ -97,33 +97,33 @@ describe('filters', function () {
});
describe('humansize', function () {
it('should return n/a when size is zero', inject(function(humansizeFilter) {
it('should return n/a when size is zero', inject(function (humansizeFilter) {
expect(humansizeFilter(0)).toBe('n/a');
}));
it('should handle Bytes values', inject(function(humansizeFilter) {
it('should handle Bytes values', inject(function (humansizeFilter) {
expect(humansizeFilter(512)).toBe('512 Bytes');
}));
it('should handle KB values', inject(function(humansizeFilter) {
it('should handle KB values', inject(function (humansizeFilter) {
expect(humansizeFilter(5120)).toBe('5 KB');
}));
it('should handle MB values', inject(function(humansizeFilter) {
it('should handle MB values', inject(function (humansizeFilter) {
expect(humansizeFilter(5 * Math.pow(10, 6))).toBe('5 MB');
}));
it('should handle GB values', inject(function(humansizeFilter) {
it('should handle GB values', inject(function (humansizeFilter) {
expect(humansizeFilter(5 * Math.pow(10, 9))).toBe('5 GB');
}));
it('should handle TB values', inject(function(humansizeFilter) {
it('should handle TB values', inject(function (humansizeFilter) {
expect(humansizeFilter(5 * Math.pow(10, 12))).toBe('5 TB');
}));
});
describe('containername', function () {
it('should strip the leading slash from container name', inject(function(containernameFilter) {
it('should strip the leading slash from container name', inject(function (containernameFilter) {
var container = {
Names: ['/elegant_ardinghelli']
};
@ -133,14 +133,14 @@ describe('filters', function () {
});
describe('repotag', function () {
it('should not display empty repo tag', inject(function(repotagFilter) {
it('should not display empty repo tag', inject(function (repotagFilter) {
var image = {
RepoTags: ['<none>:<none>']
};
expect(repotagFilter(image)).toBe('');
}));
it('should display a normal repo tag', inject(function(repotagFilter) {
it('should display a normal repo tag', inject(function (repotagFilter) {
var image = {
RepoTags: ['ubuntu:latest']
};
@ -149,17 +149,207 @@ describe('filters', function () {
});
describe('getdate', function () {
it('should convert the Docker date to a human readable form', inject(function(getdateFilter) {
it('should convert the Docker date to a human readable form', inject(function (getdateFilter) {
expect(getdateFilter(1420424998)).toBe('Sun Jan 04 2015');
}));
});
describe('errorMsgFilter', function() {
describe('errorMsgFilter', function () {
it('should convert the $resource object to a string message',
inject(function(errorMsgFilter) {
var response = {'0':'C','1':'o','2':'n','3':'f','4':'l','5':'i','6':'c','7':'t','8':',','9':' ','10':'T','11':'h','12':'e','13':' ','14':'n','15':'a','16':'m','17':'e','18':' ','19':'u','20':'b','21':'u','22':'n','23':'t','24':'u','25':'-','26':'s','27':'l','28':'e','29':'e','30':'p','31':'-','32':'r','33':'u','34':'n','35':'t','36':'i','37':'m','38':'e','39':' ','40':'i','41':'s','42':' ','43':'a','44':'l','45':'r','46':'e','47':'a','48':'d','49':'y','50':' ','51':'a','52':'s','53':'s','54':'i','55':'g','56':'n','57':'e','58':'d','59':' ','60':'t','61':'o','62':' ','63':'b','64':'6','65':'9','66':'e','67':'5','68':'3','69':'a','70':'6','71':'2','72':'2','73':'c','74':'8','75':'.','76':' ','77':'Y','78':'o','79':'u','80':' ','81':'h','82':'a','83':'v','84':'e','85':' ','86':'t','87':'o','88':' ','89':'d','90':'e','91':'l','92':'e','93':'t','94':'e','95':' ','96':'(','97':'o','98':'r','99':' ','100':'r','101':'e','102':'n','103':'a','104':'m','105':'e','106':')','107':' ','108':'t','109':'h','110':'a','111':'t','112':' ','113':'c','114':'o','115':'n','116':'t','117':'a','118':'i','119':'n','120':'e','121':'r','122':' ','123':'t','124':'o','125':' ','126':'b','127':'e','128':' ','129':'a','130':'b','131':'l','132':'e','133':' ','134':'t','135':'o','136':' ','137':'a','138':'s','139':'s','140':'i','141':'g','142':'n','143':' ','144':'u','145':'b','146':'u','147':'n','148':'t','149':'u','150':'-','151':'s','152':'l','153':'e','154':'e','155':'p','156':'-','157':'r','158':'u','159':'n','160':'t','161':'i','162':'m','163':'e','164':' ','165':'t','166':'o','167':' ','168':'a','169':' ','170':'c','171':'o','172':'n','173':'t','174':'a','175':'i','176':'n','177':'e','178':'r','179':' ','180':'a','181':'g','182':'a','183':'i','184':'n','185':'.','186':'\n','$promise':{},'$resolved':true};
inject(function (errorMsgFilter) {
var response = {
'0': 'C',
'1': 'o',
'2': 'n',
'3': 'f',
'4': 'l',
'5': 'i',
'6': 'c',
'7': 't',
'8': ',',
'9': ' ',
'10': 'T',
'11': 'h',
'12': 'e',
'13': ' ',
'14': 'n',
'15': 'a',
'16': 'm',
'17': 'e',
'18': ' ',
'19': 'u',
'20': 'b',
'21': 'u',
'22': 'n',
'23': 't',
'24': 'u',
'25': '-',
'26': 's',
'27': 'l',
'28': 'e',
'29': 'e',
'30': 'p',
'31': '-',
'32': 'r',
'33': 'u',
'34': 'n',
'35': 't',
'36': 'i',
'37': 'm',
'38': 'e',
'39': ' ',
'40': 'i',
'41': 's',
'42': ' ',
'43': 'a',
'44': 'l',
'45': 'r',
'46': 'e',
'47': 'a',
'48': 'd',
'49': 'y',
'50': ' ',
'51': 'a',
'52': 's',
'53': 's',
'54': 'i',
'55': 'g',
'56': 'n',
'57': 'e',
'58': 'd',
'59': ' ',
'60': 't',
'61': 'o',
'62': ' ',
'63': 'b',
'64': '6',
'65': '9',
'66': 'e',
'67': '5',
'68': '3',
'69': 'a',
'70': '6',
'71': '2',
'72': '2',
'73': 'c',
'74': '8',
'75': '.',
'76': ' ',
'77': 'Y',
'78': 'o',
'79': 'u',
'80': ' ',
'81': 'h',
'82': 'a',
'83': 'v',
'84': 'e',
'85': ' ',
'86': 't',
'87': 'o',
'88': ' ',
'89': 'd',
'90': 'e',
'91': 'l',
'92': 'e',
'93': 't',
'94': 'e',
'95': ' ',
'96': '(',
'97': 'o',
'98': 'r',
'99': ' ',
'100': 'r',
'101': 'e',
'102': 'n',
'103': 'a',
'104': 'm',
'105': 'e',
'106': ')',
'107': ' ',
'108': 't',
'109': 'h',
'110': 'a',
'111': 't',
'112': ' ',
'113': 'c',
'114': 'o',
'115': 'n',
'116': 't',
'117': 'a',
'118': 'i',
'119': 'n',
'120': 'e',
'121': 'r',
'122': ' ',
'123': 't',
'124': 'o',
'125': ' ',
'126': 'b',
'127': 'e',
'128': ' ',
'129': 'a',
'130': 'b',
'131': 'l',
'132': 'e',
'133': ' ',
'134': 't',
'135': 'o',
'136': ' ',
'137': 'a',
'138': 's',
'139': 's',
'140': 'i',
'141': 'g',
'142': 'n',
'143': ' ',
'144': 'u',
'145': 'b',
'146': 'u',
'147': 'n',
'148': 't',
'149': 'u',
'150': '-',
'151': 's',
'152': 'l',
'153': 'e',
'154': 'e',
'155': 'p',
'156': '-',
'157': 'r',
'158': 'u',
'159': 'n',
'160': 't',
'161': 'i',
'162': 'm',
'163': 'e',
'164': ' ',
'165': 't',
'166': 'o',
'167': ' ',
'168': 'a',
'169': ' ',
'170': 'c',
'171': 'o',
'172': 'n',
'173': 't',
'174': 'a',
'175': 'i',
'176': 'n',
'177': 'e',
'178': 'r',
'179': ' ',
'180': 'a',
'181': 'g',
'182': 'a',
'183': 'i',
'184': 'n',
'185': '.',
'186': '\n',
'$promise': {},
'$resolved': true
};
var message = 'Conflict, The name ubuntu-sleep-runtime is already assigned to b69e53a622c8. You have to delete (or rename) that container to be able to assign ubuntu-sleep-runtime to a container again.\n';
expect(errorMsgFilter(response)).toBe(message);
}));
}));
});
});

View File

@ -3,19 +3,19 @@ basePath = '../..';
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
'assets/js/jquery-1.11.1.min.js',
'assets/js/jquery.gritter.min.js',
'assets/js/bootstrap.min.js',
'assets/js/spin.js',
'dist/angular.js',
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
'assets/js/angular-vis.js',
'test/assets/angular/angular-mocks.js',
'app/**/*.js',
'test/unit/**/*.spec.js',
'dist/templates/**/*.js'
JASMINE,
JASMINE_ADAPTER,
'assets/js/jquery-1.11.1.min.js',
'assets/js/jquery.gritter.min.js',
'assets/js/bootstrap.min.js',
'assets/js/spin.js',
'dist/angular.js',
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
'assets/js/angular-vis.js',
'test/assets/angular/angular-mocks.js',
'app/**/*.js',
'test/unit/**/*.spec.js',
'dist/templates/**/*.js'
];
// use dots reporter, as travis terminal does not support escaping sequences