Compare commits

...

29 Commits

Author SHA1 Message Date
Kevan Ahlquist d40b487756 Merge branch 'master' into dist 2016-03-23 00:58:24 -05:00
Kevan Ahlquist 8cdb675abc Merge pull request #191 from erwin314/show-port-bindings2
Fixed: Show actual port bindings
2016-03-23 00:14:03 -05:00
Kevan Ahlquist a25829bbd9 Merge pull request #190 from erwin314/master
Fixed: Check if variable is plain object before using isEmptyObject()
2016-03-23 00:13:49 -05:00
Erwin Molendijk 194aa9a750 Fixed: Show actual port bindings 2016-03-21 11:13:21 +01:00
Erwin Molendijk e21af77246 Revert "Changed: Show automatic port bindings when PublishAllPorts is set."
This reverts commit bfe9038630.
2016-03-21 10:36:34 +01:00
Erwin Molendijk bfe9038630 Changed: Show automatic port bindings when PublishAllPorts is set. 2016-03-21 10:21:50 +01:00
Erwin Molendijk a33123469a Fixed: Check if variable is plain object before using isEmptyObject() 2016-03-21 09:25:40 +01:00
Kevan Ahlquist 4700882a1a Pull latest into dist from master. 2016-03-19 18:57:37 -05:00
Kevan Ahlquist f353dc2c41 Add a global interceptor to catch 'conflict.' API responses. 2016-03-19 18:50:11 -05:00
Kevan Ahlquist a2a367725b Merge pull request #189 from crosbymichael/tables
Add column sorting to tables
2016-03-19 18:11:42 -05:00
Kevan Ahlquist db90a0eed7 Add column sorting to networks and volumes pages. 2016-03-19 18:09:17 -05:00
Kevan Ahlquist 93dba3f92f Add column sorting to images page. 2016-03-19 17:45:45 -05:00
Kevan Ahlquist 5a20b9fc04 Add column sorting to containers page. 2016-03-19 17:30:47 -05:00
Kevan Ahlquist 9793c3f3ee Fix jshint errors. 2016-03-07 00:07:46 -06:00
Kevan Ahlquist ddd995696b Merge branch 'master' into dist 2016-03-04 00:12:35 -06:00
Kevan Ahlquist 6963a1ae8a Merge pull request #184 from ramzes642/master
Container edit feature
2016-03-03 00:46:01 -06:00
Roman Usachev da1a5ead39 Bugfixes 2016-02-24 10:07:59 +03:00
Roman Usachev f1b5037ee5 Host mode fix 2016-02-24 09:19:02 +03:00
Roman Usachev cf18a3cd60 Null vars fix 2016-02-24 07:16:07 +03:00
Roman Usachev cc1b67575c Null vars fix 2016-02-24 05:43:04 +03:00
Roman Usachev 50d33a07df Filesystem binds edit 2016-02-24 03:13:24 +03:00
Roman Usachev fc0dedfda7 Ports list now using HostConfig.PortBindings 2016-02-24 01:34:20 +03:00
Roman Usachev 35dbacdfff Port bindings edit, HostConfig fix 2016-02-23 20:47:21 +03:00
Roman Usachev ad0d23d686 Container env vars editing using commit and recreate 2016-02-22 09:10:16 +03:00
Kevan Ahlquist 61831104a4 Merge branch 'master' into dist 2016-02-16 00:14:12 -06:00
Kevan Ahlquist 786b94b285 Allow running containers to be committed. 2016-02-16 00:09:12 -06:00
Kevan Ahlquist 0ddf4a1828 Fix spec for top view. 2016-02-15 23:49:20 -06:00
Kevan Ahlquist 6e8022e687 Merge latest master into dist. 2016-02-15 23:48:13 -06:00
Kevan Ahlquist b244242cb2 Add container name to stats and top pages. 2016-02-15 23:34:55 -06:00
22 changed files with 526 additions and 78 deletions
+18 -2
View File
@@ -23,7 +23,7 @@ angular.module('dockerui', [
'network',
'networks',
'volumes'])
.config(['$routeProvider', function ($routeProvider) {
.config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
'use strict';
$routeProvider.when('/', {
templateUrl: 'app/components/dashboard/dashboard.html',
@@ -67,9 +67,25 @@ angular.module('dockerui', [
controller: 'EventsController'
});
$routeProvider.otherwise({redirectTo: '/'});
// The Docker API likes to return plaintext errors, this catches them and disp
$httpProvider.interceptors.push(function() {
return {
'response': function(response) {
if (typeof(response.data) === 'string' && response.data.startsWith('Conflict.')) {
$.gritter.add({
title: 'Error',
text: response.data,
time: 10000
});
}
return response;
}
};
});
}])
// This is your docker url that the api will use to make requests
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
.constant('DOCKER_ENDPOINT', 'dockerapi')
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
.constant('UI_VERSION', 'v0.9.0');
.constant('UI_VERSION', 'v0.10.0-beta');
+109 -7
View File
@@ -45,8 +45,7 @@
ng-show="container.State.Running && !container.State.Stopped">Restart
</button>
<button class="btn btn-primary"
ng-click="commit()"
ng-show="container.State.Running && !container.State.Paused">Commit
ng-click="commit()">Commit
</button>
</div>
@@ -77,9 +76,43 @@
<tr>
<td>Environment:</td>
<td>
<ul>
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
</ul>
<div ng-show="!editEnv">
<button class="btn btn-default btn-xs pull-right" ng-click="editEnv = true"><i class="glyphicon glyphicon-pencil"></i></button>
<ul>
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
</ul>
</div>
<div class="form-group" ng-show="editEnv">
<label>Env:</label>
<div ng-repeat="envar in newCfg.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 input-sm"
placeholder="NAME"/>
</div>
<div class="form-group">
<label class="sr-only">Variable Value:</label>
<input type="text" ng-model="envar.value" class="form-control input-sm" style="width: 400px"
placeholder="value"/>
</div>
<div class="form-group">
<button class="btn btn-danger btn-sm input-sm form-control"
ng-click="rmEntry(newCfg.Env, envar)"><i class="glyphicon glyphicon-remove"></i>
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(newCfg.Env, {name: '', value: ''})"><i class="glyphicon glyphicon-plus"></i> Add
</button>
<button class="btn btn-primary btn-sm"
ng-click="restartEnv()"
ng-show="!container.State.Restarting">Commit and restart</button>
</div>
</td>
</tr>
<tr>
@@ -105,11 +138,42 @@
<tr>
<td>Ports:</td>
<td>
<ul style="display:inline-table">
<div ng-show="!editPorts">
<button class="btn btn-default btn-xs pull-right" ng-click="editPorts = true"><i class="glyphicon glyphicon-pencil"></i></button>
<ul>
<li ng-repeat="(containerport, hostports) in container.NetworkSettings.Ports">
{{ containerport }} => <span class="label label-default" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
{{ containerport }} =>
<span class="label label-default" style="margin-right: 5px;" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
</li>
</ul>
</div>
<div ng-show="editPorts">
<div ng-repeat="(containerport, hostports) in newCfg.Ports" style="margin-bottom: 5px;">
<label>{{ containerport }}</label>
<div style="margin-left: 20px;">
<div ng-repeat="(k,v) in hostports" class="form-group form-inline">
<div class="form-group">
<input type="text" ng-model="v.HostIp" class="form-control input-sm" placeholder="IP address, ex. 0.0.0.0" />
</div>
<div class="form-group">
<input type="text" ng-model="v.HostPort" class="form-control input-sm"
placeholder="Port" />
</div>
<div class="form-group">
<button class="btn btn-danger btn-sm input-sm form-control"
ng-click="rmEntry(hostports, v)"><i class="glyphicon glyphicon-remove"></i>
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(hostports, {HostIp: '0.0.0.0', HostPort: ''})"><i class="glyphicon glyphicon-plus"></i> Add
</button>
</div>
<button class="btn btn-primary btn-sm"
ng-click="restartEnv()"
ng-show="!container.State.Restarting">Commit and restart</button>
</div>
</td>
</tr>
@@ -131,6 +195,44 @@
<pre>{{ container.Config.Entrypoint.join(' ') }}</pre>
</td>
</tr>
<tr>
<td>Bindings:</td>
<td>
<div ng-show="!editBinds">
<button class="btn btn-default btn-xs pull-right" ng-click="editBinds = true"><i class="glyphicon glyphicon-pencil"></i></button>
<ul>
<li ng-repeat="b in container.HostConfig.Binds">{{ b }}</li>
</ul>
</div>
<div ng-show="editBinds">
<div ng-repeat="(vol, b) in newCfg.Binds" class="form-group form-inline">
<div class="form-group">
<input type="text" ng-model="b.HostPath" class="form-control input-sm"
placeholder="Host path or volume name" style="width: 250px;" />
</div>
<div class="form-group">
<input type="text" ng-model="b.ContPath" ng-readonly="b.DefaultBind" class="form-control input-sm" placeholder="Container path" />
</div>
<div class="form-group">
<label><input type="checkbox" ng-model="b.ReadOnly" /> read only</label>
</div>
<div class="form-group">
<button class="btn btn-danger btn-sm input-sm form-control"
ng-click="rmEntry(newCfg.Binds, b)"><i class="glyphicon glyphicon-remove"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-sm"
ng-click="addEntry(newCfg.Binds, { ContPath: '', HostPath: '', ReadOnly: false, DefaultBind: false })"><i class="glyphicon glyphicon-plus"></i> Add
</button>
<button class="btn btn-primary btn-sm"
ng-click="restartEnv()"
ng-show="!container.State.Restarting">Commit and restart</button>
</div>
</td>
</tr>
<tr>
<td>Volumes:</td>
<td>{{ container.Volumes }}</td>
+172 -4
View File
@@ -1,8 +1,14 @@
angular.module('container', [])
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner', '$timeout',
function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner, $timeout) {
angular.module('container', [])
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Image', 'Messages', 'ViewSpinner', '$timeout',
function ($scope, $routeParams, $location, Container, ContainerCommit, Image, Messages, ViewSpinner, $timeout) {
$scope.changes = [];
$scope.edit = false;
$scope.editEnv = false;
$scope.editPorts = false;
$scope.editBinds = false;
$scope.newCfg = {
Env: [],
Ports: {}
};
var update = function () {
ViewSpinner.spin();
@@ -10,6 +16,51 @@ angular.module('container', [])
$scope.container = d;
$scope.container.edit = false;
$scope.container.newContainerName = d.Name;
// fill up env
if (d.Config.Env) {
$scope.newCfg.Env = d.Config.Env.map(function (entry) {
return {name: entry.split('=')[0], value: entry.split('=')[1]};
});
}
// fill up ports
$scope.newCfg.Ports = {};
angular.forEach(d.Config.ExposedPorts, function(i, port) {
if (d.HostConfig.PortBindings && port in d.HostConfig.PortBindings) {
$scope.newCfg.Ports[port] = d.HostConfig.PortBindings[port];
}
else {
$scope.newCfg.Ports[port] = [];
}
});
// fill up bindings
$scope.newCfg.Binds = [];
var defaultBinds = {};
angular.forEach(d.Config.Volumes, function(value, vol) {
defaultBinds[vol] = { ContPath: vol, HostPath: '', ReadOnly: false, DefaultBind: true };
});
angular.forEach(d.HostConfig.Binds, function(binding, i) {
var mountpoint = binding.split(':')[0];
var vol = binding.split(':')[1] || '';
var ro = binding.split(':').length > 2 && binding.split(':')[2] === 'ro';
var defaultBind = false;
if (vol === '') {
vol = mountpoint;
mountpoint = '';
}
if (vol in defaultBinds) {
delete defaultBinds[vol];
defaultBind = true;
}
$scope.newCfg.Binds.push({ ContPath: vol, HostPath: mountpoint, ReadOnly: ro, DefaultBind: defaultBind });
});
angular.forEach(defaultBinds, function(bind) {
$scope.newCfg.Binds.push(bind);
});
ViewSpinner.stop();
}, function (e) {
if (e.status === 404) {
@@ -20,6 +71,7 @@ angular.module('container', [])
}
ViewSpinner.stop();
});
};
$scope.start = function () {
@@ -58,6 +110,109 @@ angular.module('container', [])
});
};
$scope.restartEnv = function () {
var config = angular.copy($scope.container.Config);
config.Env = $scope.newCfg.Env.map(function(entry) {
return entry.name+"="+entry.value;
});
var portBindings = angular.copy($scope.newCfg.Ports);
angular.forEach(portBindings, function(item, key) {
if (item.length === 0) {
delete portBindings[key];
}
});
var binds = [];
angular.forEach($scope.newCfg.Binds, function(b) {
if (b.ContPath !== '') {
var bindLine = '';
if (b.HostPath !== '') {
bindLine = b.HostPath + ':';
}
bindLine += b.ContPath;
if (b.ReadOnly) {
bindLine += ':ro';
}
if (b.HostPath !== '' || !b.DefaultBind) {
binds.push(bindLine);
}
}
});
ViewSpinner.spin();
ContainerCommit.commit({id: $routeParams.id, tag: $scope.container.Config.Image, config: config }, function (d) {
if ('Id' in d) {
var imageId = d.Id;
Image.inspect({id: imageId}, function(imageData) {
// Append current host config to image with new port bindings
imageData.Config.HostConfig = angular.copy($scope.container.HostConfig);
imageData.Config.HostConfig.PortBindings = portBindings;
imageData.Config.HostConfig.Binds = binds;
if (imageData.Config.HostConfig.NetworkMode === 'host') {
imageData.Config.Hostname = '';
}
Container.create(imageData.Config, function(containerData) {
if (!('Id' in containerData)) {
Messages.error("Failure", "Container failed to create.");
return;
}
// Stop current if running
if ($scope.container.State.Running) {
Container.stop({id: $routeParams.id}, function (d) {
Messages.send("Container stopped", $routeParams.id);
// start new
Container.start({
id: containerData.Id
}, function (d) {
$location.url('/containers/' + containerData.Id + '/');
Messages.send("Container started", $routeParams.id);
}, function (e) {
update();
Messages.error("Failure", "Container failed to start." + e.data);
});
}, function (e) {
update();
Messages.error("Failure", "Container failed to stop." + e.data);
});
} else {
// start new
Container.start({
id: containerData.Id
}, function (d) {
$location.url('/containers/'+containerData.Id+'/');
Messages.send("Container started", $routeParams.id);
}, function (e) {
update();
Messages.error("Failure", "Container failed to start." + e.data);
});
}
}, function(e) {
update();
Messages.error("Failure", "Image failed to get." + e.data);
});
}, function (e) {
update();
Messages.error("Failure", "Image failed to get." + e.data);
});
} else {
update();
Messages.error("Failure", "Container commit failed.");
}
}, function (e) {
update();
Messages.error("Failure", "Container failed to commit." + e.data);
});
};
$scope.commit = function () {
ViewSpinner.spin();
ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function (d) {
@@ -139,6 +294,19 @@ angular.module('container', [])
$scope.container.edit = false;
};
$scope.addEntry = function (array, entry) {
array.push(entry);
};
$scope.rmEntry = function (array, entry) {
var idx = array.indexOf(entry);
array.splice(idx, 1);
};
$scope.toggleEdit = function() {
$scope.edit = !$scope.edit;
};
update();
$scope.getChanges();
}]);
+26 -16
View File
@@ -1,19 +1,29 @@
<div class="containerTop">
<div class="form-group col-xs-2">
<input type="text" class="form-control" placeholder="[options] (aux)" ng-model="ps_args">
<div class="row">
<div class="col-xs-12">
<h1>Top for: {{ containerName }}</h1>
</div>
</div>
<div class="row">
<div class="form-group col-xs-2">
<input type="text" class="form-control" placeholder="[options] (aux)" ng-model="ps_args">
</div>
<button type="button" class="btn btn-default" ng-click="getTop()">Submit</button>
</div>
<div class="row">
<div class="col-xs-12">
<table class="table table-striped">
<thead>
<tr>
<th ng-repeat="title in containerTop.Titles">{{title}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="processInfos in containerTop.Processes">
<td ng-repeat="processInfo in processInfos track by $index">{{processInfo}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<button type="button" class="btn btn-default" ng-click="getTop()">Submit</button>
<table class="table table-striped">
<thead>
<tr>
<th ng-repeat="title in containerTop.Titles">{{title}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="processInfos in containerTop.Processes">
<td ng-repeat="processInfo in processInfos track by $index">{{processInfo}}</td>
</tr>
</tbody>
</table>
</div>
@@ -1,5 +1,5 @@
angular.module('containerTop', [])
.controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, ViewSpinner) {
.controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'Container', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, Container, ViewSpinner) {
$scope.ps_args = '';
/**
@@ -15,5 +15,11 @@ angular.module('containerTop', [])
});
};
Container.get({id: $routeParams.id}, function (d) {
$scope.containerName = d.Name.substring(1);
}, function (e) {
Messages.error("Failure", e.data);
});
$scope.getTop();
}]);
+37 -7
View File
@@ -25,16 +25,46 @@
<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>
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Select</label></th>
<th>
<a href="#/containers/" ng-click="order('Names')">
Name
<span ng-show="sortType == 'Names' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Names' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/containers/" ng-click="order('Image')">
Image
<span ng-show="sortType == 'Image' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Image' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/containers/" ng-click="order('Command')">
Command
<span ng-show="sortType == 'Command' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Command' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/containers/" ng-click="order('Created')">
Created
<span ng-show="sortType == 'Created' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Created' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/containers/" ng-click="order('Status')">
Status
<span ng-show="sortType == 'Status' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Status' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="container in containers | filter:filter | orderBy:predicate">
<tr ng-repeat="container in containers | filter:filter | orderBy:sortType:sortReverse">
<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>
@@ -1,10 +1,16 @@
angular.module('containers', [])
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
function ($scope, Container, Settings, Messages, ViewSpinner) {
$scope.predicate = '-Created';
$scope.sortType = 'Created';
$scope.sortReverse = true;
$scope.toggle = false;
$scope.displayAll = Settings.displayAll;
$scope.order = function(sortType) {
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
$scope.sortType = sortType;
};
var update = function (data) {
ViewSpinner.spin();
Container.query(data, function (d) {
+30 -6
View File
@@ -21,15 +21,39 @@
<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>
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Select</label></th>
<th>
<a href="#/images/" ng-click="order('Id')">
Id
<span ng-show="sortType == 'Id' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Id' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/images/" ng-click="order('RepoTags')">
Repository
<span ng-show="sortType == 'RepoTags' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'RepoTags' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/images/" ng-click="order('VirtualSize')">
VirtualSize
<span ng-show="sortType == 'VirtualSize' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'VirtualSize' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/images/" ng-click="order('Created')">
Created
<span ng-show="sortType == 'Created' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Created' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="image in images | filter:filter | orderBy:predicate">
<tr ng-repeat="image in images | filter:filter | orderBy:sortType:sortReverse">
<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>
+7 -1
View File
@@ -1,8 +1,14 @@
angular.module('images', [])
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
function ($scope, Image, ViewSpinner, Messages) {
$scope.sortType = 'Created';
$scope.sortReverse = true;
$scope.toggle = false;
$scope.predicate = '-Created';
$scope.order = function(sortType) {
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
$scope.sortType = sortType;
};
$scope.showBuilder = function () {
$('#build-modal').modal('show');
+51 -10
View File
@@ -9,7 +9,6 @@
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
</ul>
</li>
<!--<li><a data-toggle="modal" data-target="#pull-modal" href="">Pull</a></li>-->
</ul>
<div class="pull-right form-inline">
@@ -20,18 +19,60 @@
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</th>
<th>Name</th>
<th>Id</th>
<th>Scope</th>
<th>Driver</th>
<th>IPAM Driver</th>
<th>IPAM Subnet</th>
<th>IPAM Gateway</th>
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</label></th>
<th>
<a href="#/networks/" ng-click="order('Name')">
Name
<span ng-show="sortType == 'Name' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Name' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('Id')">
Id
<span ng-show="sortType == 'Id' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Id' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('Scope')">
Scope
<span ng-show="sortType == 'Scope' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Scope' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('Driver')">
Driver
<span ng-show="sortType == 'Driver' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Driver' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('IPAM.Driver')">
IPAM Driver
<span ng-show="sortType == 'IPAM.Driver' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'IPAM.Driver' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('IPAM.Config[0].Subnet')">
IPAM Subnet
<span ng-show="sortType == 'IPAM.Config[0].Subnet' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'IPAM.Config[0].Subnet' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/networks/" ng-click="order('IPAM.Config[0].Gateway')">
IPAM Gateway
<span ng-show="sortType == 'IPAM.Config[0].Gateway' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'IPAM.Config[0].Gateway' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="network in networks | filter:filter | orderBy:predicate">
<tr ng-repeat="network in networks | filter:filter | orderBy:sortType:sortReverse">
<td><input type="checkbox" ng-model="network.Checked"/></td>
<td><a href="#/networks/{{ network.Id }}/">{{ network.Name|truncate:20}}</a></td>
<td>{{ network.Id }}</td>
@@ -1,12 +1,17 @@
angular.module('networks', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/networks', {
$routeProvider.when('/networks/', {
templateUrl: 'app/components/networks/networks.html',
controller: 'NetworksController'
});
}]).controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
function ($scope, Network, ViewSpinner, Messages, $route, errorMsgFilter) {
$scope.sortType = 'Name';
$scope.sortReverse = true;
$scope.toggle = false;
$scope.predicate = '-Created';
$scope.order = function(sortType) {
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
$scope.sortType = sortType;
};
$scope.createNetworkConfig = {
"Name": '',
"Driver": '',
@@ -40,7 +40,7 @@ angular.module('startContainer', ['ui.bootstrap'])
function rmEmptyKeys(col) {
for (var key in col) {
if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
if (col[key] === null || col[key] === undefined || col[key] === '' || ($.isPlainObject(col[key]) && $.isEmptyObject(col[key])) || col[key].length === 0) {
delete col[key];
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<div class="row">
<div class="col-xs-12">
<h1>Stats</h1>
<h1>Stats for: {{ containerName }}</h1>
<h2>CPU</h2>
+6
View File
@@ -172,5 +172,11 @@ angular.module('stats', [])
}
return cpuPercent;
}
Container.get({id: $routeParams.id}, function (d) {
$scope.containerName = d.Name.substring(1);
}, function (e) {
Messages.error("Failure", e.data);
});
}])
;
+23 -5
View File
@@ -19,14 +19,32 @@
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</th>
<th>Name</th>
<th>Driver</th>
<th>Mountpoint</th>
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</label></th>
<th>
<a href="#/volumes/" ng-click="order('Name')">
Name
<span ng-show="sortType == 'Name' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Name' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/volumes/" ng-click="order('Driver')">
Driver
<span ng-show="sortType == 'Driver' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Driver' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a href="#/volumes/" ng-click="order('Mountpoint')">
Mountpoint
<span ng-show="sortType == 'Mountpoint' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Mountpoint' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="volume in volumes | filter:filter | orderBy:predicate">
<tr ng-repeat="volume in volumes | filter:filter | orderBy:sortType:sortReverse">
<td><input type="checkbox" ng-model="volume.Checked"/></td>
<td>{{ volume.Name|truncate:20 }}</td>
<td>{{ volume.Driver }}</td>
+7 -2
View File
@@ -1,12 +1,17 @@
angular.module('volumes', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/volumes', {
$routeProvider.when('/volumes/', {
templateUrl: 'app/components/volumes/volumes.html',
controller: 'VolumesController'
});
}]).controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) {
$scope.sortType = 'Name';
$scope.sortReverse = true;
$scope.toggle = false;
$scope.predicate = '-Created';
$scope.order = function(sortType) {
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
$scope.sortType = sortType;
};
$scope.createVolumeConfig = {
"Name": "",
"Driver": ""
+6 -3
View File
@@ -31,8 +31,10 @@ angular.module('dockerui.services', ['ngResource'])
url: Settings.url + '/commit',
params: {
'container': params.id,
'repo': params.repo
}
'tag': params.tag || null,
'repo': params.repo || null
},
data: params.config
}).success(callback).error(function (data, status, headers, config) {
console.log(error, data);
});
@@ -92,7 +94,8 @@ angular.module('dockerui.services', ['ngResource'])
insert: {method: 'POST', params: {id: '@id', action: 'insert'}},
push: {method: 'POST', params: {id: '@id', action: 'push'}},
tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo', tag: '@tag'}},
remove: {method: 'DELETE', params: {id: '@id'}, isArray: true}
remove: {method: 'DELETE', params: {id: '@id'}, isArray: true},
inspect: {method: 'GET', params: {id: '@id', action: 'json'}}
});
}])
.factory('Version', ['$resource', 'Settings', function VersionFactory($resource, Settings) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "dockerui",
"version": "0.9.0",
"version": "0.10.0-beta",
"homepage": "https://github.com/crosbymichael/dockerui",
"authors": [
"Michael Crosby <crosbymichael@gmail.com>",
+5 -5
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -24,13 +24,13 @@ module.exports = function (grunt) {
'copy'
]);
grunt.registerTask('release', [
'clean:all',
'clean:app',
'if:binaryNotExist',
'html2js',
'uglify',
'clean:tmpl',
'jshint',
'karma:unit',
//'karma:unit',
'concat:index',
'recess:min',
'copy'
+1 -1
View File
@@ -2,7 +2,7 @@
"author": "Michael Crosby & Kevan Ahlquist",
"name": "dockerui",
"homepage": "https://github.com/crosbymichael/dockerui",
"version": "0.9.0",
"version": "0.10.0-beta",
"repository": {
"type": "git",
"url": "git@github.com:crosbymichael/dockerui.git"
@@ -15,12 +15,14 @@ describe("ContainerTopController", function () {
}));
it("should test controller initialize", function () {
$httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/json').respond(200, {Name: '/foo'});
$httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/top?ps_args=').respond(200);
expect($scope.ps_args).toBeDefined();
$httpBackend.flush();
});
it("a correct top request to the Docker remote API", function () {
$httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/json').respond(200, {Name: '/foo'});
$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=').respond(200);
$routeParams.id = '123456789123456789123456789';
$scope.ps_args = 'aux';