1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

Whitespace diff, ran everything through the formatter.

This commit is contained in:
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

@ -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;