|
@ -41,6 +41,7 @@ DockerUI listens on port 9000 by default. If you run DockerUI inside a container
|
|||
* [Gritter](https://github.com/jboesch/Gritter)
|
||||
* [Spin.js](https://github.com/fgnass/spin.js/)
|
||||
* [Golang](https://golang.org/)
|
||||
* [Vis.js](http://visjs.org/)
|
||||
|
||||
|
||||
### Todo:
|
||||
|
|
40
app/app.js
|
@ -1,12 +1,38 @@
|
|||
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs'])
|
||||
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop'])
|
||||
.config(['$routeProvider', function ($routeProvider) {
|
||||
'use strict';
|
||||
$routeProvider.when('/', {templateUrl: 'app/components/dashboard/dashboard.html', controller: 'DashboardController'});
|
||||
$routeProvider.when('/containers/', {templateUrl: 'app/components/containers/containers.html', controller: 'ContainersController'});
|
||||
$routeProvider.when('/containers/:id/', {templateUrl: 'app/components/container/container.html', controller: 'ContainerController'});
|
||||
$routeProvider.when('/containers/:id/logs/', {templateUrl: 'app/components/containerLogs/containerlogs.html', controller: 'ContainerLogsController'});
|
||||
$routeProvider.when('/images/', {templateUrl: 'app/components/images/images.html', controller: 'ImagesController'});
|
||||
$routeProvider.when('/images/:id*/', {templateUrl: 'app/components/image/image.html', controller: 'ImageController'});
|
||||
$routeProvider.when('/', {
|
||||
templateUrl: 'app/components/dashboard/dashboard.html',
|
||||
controller: 'DashboardController'
|
||||
});
|
||||
$routeProvider.when('/containers/', {
|
||||
templateUrl: 'app/components/containers/containers.html',
|
||||
controller: 'ContainersController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/', {
|
||||
templateUrl: 'app/components/container/container.html',
|
||||
controller: 'ContainerController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/logs/', {
|
||||
templateUrl: 'app/components/containerLogs/containerlogs.html',
|
||||
controller: 'ContainerLogsController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/top', {
|
||||
templateUrl: 'app/components/containerTop/containerTop.html',
|
||||
controller: 'ContainerTopController'
|
||||
});
|
||||
$routeProvider.when('/containers_network', {
|
||||
templateUrl: 'app/components/containersNetwork/containersNetwork.html',
|
||||
controller: 'ContainersNetworkController'
|
||||
});
|
||||
$routeProvider.when('/images/', {
|
||||
templateUrl: 'app/components/images/images.html',
|
||||
controller: 'ImagesController'
|
||||
});
|
||||
$routeProvider.when('/images/:id*/', {
|
||||
templateUrl: 'app/components/image/image.html',
|
||||
controller: 'ImageController'
|
||||
});
|
||||
$routeProvider.when('/info', {templateUrl: 'app/components/info/info.html', controller: 'InfoController'});
|
||||
$routeProvider.otherwise({redirectTo: '/'});
|
||||
}])
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<div class="detail">
|
||||
|
||||
|
||||
<div ng-if="!container.edit">
|
||||
<h4>Container: {{ container.Name }}
|
||||
<button class="btn btn-primary"
|
||||
ng-click="container.edit = true;">Rename</button>
|
||||
ng-click="container.edit = true;">Rename
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div ng-if="container.edit">
|
||||
|
@ -11,116 +12,134 @@
|
|||
Container:
|
||||
<input type="text" ng-model="container.newContainerName">
|
||||
<button class="btn btn-success"
|
||||
ng-click="renameContainer()">Edit</button>
|
||||
ng-click="renameContainer()">Edit
|
||||
</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="container.edit = false;">×</button>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="btn-group detail">
|
||||
<button class="btn btn-success"
|
||||
ng-click="start()"
|
||||
ng-show="!container.State.Running">Start</button>
|
||||
<button class="btn btn-warning"
|
||||
ng-click="stop()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Stop</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="kill()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Kill</button>
|
||||
<button class="btn btn-info"
|
||||
ng-click="pause()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Pause</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="unpause()"
|
||||
ng-show="container.State.Running && container.State.Paused">Unpause</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="start()"
|
||||
ng-show="!container.State.Running">Start
|
||||
</button>
|
||||
<button class="btn btn-warning"
|
||||
ng-click="stop()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Stop
|
||||
</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="kill()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Kill
|
||||
</button>
|
||||
<button class="btn btn-info"
|
||||
ng-click="pause()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Pause
|
||||
</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="unpause()"
|
||||
ng-show="container.State.Running && container.State.Paused">Unpause
|
||||
</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="restart()"
|
||||
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
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ container.Created }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Path:</td>
|
||||
<td>{{ container.Path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Args:</td>
|
||||
<td>{{ container.Args }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exposed Ports:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ container.Created }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Path:</td>
|
||||
<td>{{ container.Path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Args:</td>
|
||||
<td>{{ container.Args }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exposed Ports:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Publish All:</td>
|
||||
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ports:</td>
|
||||
<td>
|
||||
<ul style="display:inline-table">
|
||||
<li ng-repeat="(containerport, hostports) in container.HostConfig.PortBindings">
|
||||
{{ containerport }} => <span class="label label-default" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<tr>
|
||||
<td>Publish All:</td>
|
||||
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ports:</td>
|
||||
<td>
|
||||
<ul style="display:inline-table">
|
||||
<li ng-repeat="(containerport, hostports) in container.HostConfig.PortBindings">
|
||||
{{ containerport }} => <span class="label label-default" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ container.Config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IPAddress:</td>
|
||||
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ container.Config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrypoint:</td>
|
||||
<td>{{ container.Config.Entrypoint }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ container.Volumes }}</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ container.Config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IPAddress:</td>
|
||||
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ container.Config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrypoint:</td>
|
||||
<td>{{ container.Config.Entrypoint }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ container.Volumes }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>SysInitpath:</td>
|
||||
<td>{{ container.SysInitPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image:</td>
|
||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td><span class="label {{ container.State|getstatelabel }}">{{ container.State|getstatetext }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Logs:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/logs">stdout/stderr</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SysInitpath:</td>
|
||||
<td>{{ container.SysInitPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image:</td>
|
||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td><span class="label {{ container.State|getstatelabel }}">{{ container.State|getstatetext }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Logs:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/logs">stdout/stderr</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Top:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/top">Top</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span1">
|
||||
Changes:
|
||||
|
@ -128,7 +147,7 @@
|
|||
<div class="span5">
|
||||
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getChanges()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well well-large">
|
||||
<ul>
|
||||
|
@ -138,7 +157,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr/>
|
||||
|
||||
<div class="btn-remove">
|
||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Container</button>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('container', [])
|
||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'ViewSpinner',
|
||||
function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
|
||||
function($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
|
||||
$scope.changes = [];
|
||||
$scope.edit = false;
|
||||
|
||||
|
@ -58,6 +58,16 @@ function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
|||
});
|
||||
};
|
||||
|
||||
$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) {
|
||||
|
@ -91,6 +101,17 @@ function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
|||
});
|
||||
};
|
||||
|
||||
$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;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<div class="containerTop">
|
||||
<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>
|
||||
|
||||
<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>
|
|
@ -0,0 +1,19 @@
|
|||
angular.module('containerTop', [])
|
||||
.controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, ViewSpinner) {
|
||||
$scope.ps_args = '';
|
||||
|
||||
/**
|
||||
* Get container processes
|
||||
*/
|
||||
$scope.getTop = function () {
|
||||
ViewSpinner.spin();
|
||||
ContainerTop.get($routeParams.id, {
|
||||
ps_args: $scope.ps_args
|
||||
}, function (data) {
|
||||
$scope.containerTop = data;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getTop();
|
||||
}]);
|
|
@ -0,0 +1,23 @@
|
|||
<div class="detail">
|
||||
<h2>Containers Network</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="query" autofocus="true" class="form-control"
|
||||
placeholder="Search" ng-change="network.selectContainers(query)"/>
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-warning" ng-click="network.hideSelected()">Hide Selected</button>
|
||||
<button class="btn btn-info" ng-click="network.showSelectedDownstream()">Show Selected Downstream</button>
|
||||
<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>
|
||||
</div>
|
||||
<div class="row">
|
||||
<vis-network data="network.data" options="network.options" events="network.events"
|
||||
component="network.component"/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,249 @@
|
|||
angular.module('containersNetwork', ['ngVis'])
|
||||
.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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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\">" +
|
||||
"<li><strong>ID:</strong> " + container.Id + "</li>" +
|
||||
"<li><strong>Image:</strong> " + container.Image + "</li>" +
|
||||
"</ul>"});
|
||||
};
|
||||
|
||||
this.addLinkEdgeIfExists = function(from, to) {
|
||||
if (from.Links != null && from.Links[to.Name] != null) {
|
||||
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) {
|
||||
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 = '';
|
||||
});
|
||||
},
|
||||
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.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.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.shownContainersIds.indexOf(containerId) > -1) {
|
||||
this.data.removeContainersNodes(containerId);
|
||||
}
|
||||
}
|
||||
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.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.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);
|
||||
};
|
||||
|
||||
Container.query({all: 0}, function(d) {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
Container.get({id: d[i].Id}, addContainer, showFailure);
|
||||
}
|
||||
});
|
||||
|
||||
}]);
|
|
@ -3,6 +3,7 @@
|
|||
<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>
|
||||
|
|
|
@ -1,56 +1,92 @@
|
|||
angular.module('dockerui.services', ['ngResource'])
|
||||
.factory('Container', function($resource, Settings) {
|
||||
.factory('Container', function ($resource, Settings) {
|
||||
'use strict';
|
||||
// Resource for interacting with the docker containers
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#containers
|
||||
return $resource(Settings.url + '/containers/:id/:action', {
|
||||
name: '@name'
|
||||
}, {
|
||||
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: { action:'json'}},
|
||||
query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
start: {method: 'POST', params: {id: '@id', action: 'start'}},
|
||||
stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}},
|
||||
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart' }},
|
||||
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart'}},
|
||||
kill: {method: 'POST', params: {id: '@id', action: 'kill'}},
|
||||
pause: {method: 'POST', params: {id: '@id', action: 'pause'}},
|
||||
unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}},
|
||||
changes: {method: 'GET', params: {action:'changes'}, isArray: true},
|
||||
create: {method: 'POST', params: {action:'create'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id', v:0}},
|
||||
changes: {method: 'GET', params: {action: 'changes'}, isArray: true},
|
||||
create: {method: 'POST', params: {action: 'create'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id', v: 0}},
|
||||
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false}
|
||||
});
|
||||
})
|
||||
.factory('ContainerLogs', function($resource, $http, Settings) {
|
||||
.factory('ContainerCommit', function ($resource, $http, Settings) {
|
||||
'use strict';
|
||||
return {
|
||||
get: function(id, params, callback) {
|
||||
commit: function (params, callback) {
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: Settings.url + '/commit',
|
||||
params: {
|
||||
'container': params.id,
|
||||
'repo': params.repo
|
||||
}
|
||||
}).success(callback).error(function (data, status, headers, config) {
|
||||
console.log(error, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('ContainerLogs', function ($resource, $http, Settings) {
|
||||
'use strict';
|
||||
return {
|
||||
get: function (id, params, callback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/'+id+'/logs',
|
||||
params: {'stdout': params.stdout || 0, 'stderr': params.stderr || 0, 'timestamps': params.timestamps || 0, 'tail': params.tail || 'all'}
|
||||
}).success(callback).error(function(data, status, headers, config) {
|
||||
url: Settings.url + '/containers/' + id + '/logs',
|
||||
params: {
|
||||
'stdout': params.stdout || 0,
|
||||
'stderr': params.stderr || 0,
|
||||
'timestamps': params.timestamps || 0,
|
||||
'tail': params.tail || 'all'
|
||||
}
|
||||
}).success(callback).error(function (data, status, headers, config) {
|
||||
console.log(error, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Image', function($resource, Settings) {
|
||||
.factory('ContainerTop', function ($http, Settings) {
|
||||
'use strict';
|
||||
return {
|
||||
get: function (id, params, callback, errorCallback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/' + id + '/top',
|
||||
params: {
|
||||
ps_args: params.ps_args
|
||||
}
|
||||
}).success(callback);
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Image', function ($resource, Settings) {
|
||||
'use strict';
|
||||
// Resource for docker images
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#images
|
||||
return $resource(Settings.url + '/images/:id/:action', {}, {
|
||||
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: { action:'json'}},
|
||||
search: {method: 'GET', params: { action:'search'}},
|
||||
history: {method: 'GET', params: { action:'history'}, isArray: true},
|
||||
create: {method: 'POST', params: {action:'create'}},
|
||||
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'}},
|
||||
query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
search: {method: 'GET', params: {action: 'search'}},
|
||||
history: {method: 'GET', params: {action: 'history'}, isArray: true},
|
||||
create: {method: 'POST', params: {action: 'create'}},
|
||||
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'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id'}, isArray: true}
|
||||
});
|
||||
})
|
||||
.factory('Docker', function($resource, Settings) {
|
||||
.factory('Docker', function ($resource, Settings) {
|
||||
'use strict';
|
||||
// Information for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
|
||||
|
@ -58,7 +94,7 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
get: {method: 'GET'}
|
||||
});
|
||||
})
|
||||
.factory('Auth', function($resource, Settings) {
|
||||
.factory('Auth', function ($resource, Settings) {
|
||||
'use strict';
|
||||
// Auto Information for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration
|
||||
|
@ -67,7 +103,7 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
update: {method: 'POST'}
|
||||
});
|
||||
})
|
||||
.factory('System', function($resource, Settings) {
|
||||
.factory('System', function ($resource, Settings) {
|
||||
'use strict';
|
||||
// System for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
|
||||
|
@ -75,7 +111,7 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
get: {method: 'GET'}
|
||||
});
|
||||
})
|
||||
.factory('Settings', function(DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) {
|
||||
.factory('Settings', function (DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) {
|
||||
'use strict';
|
||||
var url = DOCKER_ENDPOINT;
|
||||
if (DOCKER_PORT) {
|
||||
|
@ -91,52 +127,56 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
firstLoad: true
|
||||
};
|
||||
})
|
||||
.factory('ViewSpinner', function() {
|
||||
.factory('ViewSpinner', function () {
|
||||
'use strict';
|
||||
var spinner = new Spinner();
|
||||
var target = document.getElementById('view');
|
||||
|
||||
return {
|
||||
spin: function() { spinner.spin(target); },
|
||||
stop: function() { spinner.stop(); }
|
||||
spin: function () {
|
||||
spinner.spin(target);
|
||||
},
|
||||
stop: function () {
|
||||
spinner.stop();
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Messages', function($rootScope) {
|
||||
.factory('Messages', function ($rootScope) {
|
||||
'use strict';
|
||||
return {
|
||||
send: function(title, text) {
|
||||
send: function (title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 2000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length === 3) {
|
||||
before_open: function () {
|
||||
if ($('.gritter-item-wrapper').length === 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function(title, text) {
|
||||
error: function (title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 10000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length === 4) {
|
||||
before_open: function () {
|
||||
if ($('.gritter-item-wrapper').length === 4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Dockerfile', function(Settings) {
|
||||
.factory('Dockerfile', function (Settings) {
|
||||
'use strict';
|
||||
var url = Settings.rawUrl + '/build';
|
||||
var url = Settings.rawUrl + '/build';
|
||||
return {
|
||||
build: function(file, callback) {
|
||||
build: function (file, callback) {
|
||||
var data = new FormData();
|
||||
var dockerfile = new Blob([file], { type: 'text/text' });
|
||||
var dockerfile = new Blob([file], {type: 'text/text'});
|
||||
data.append('Dockerfile', dockerfile);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
|
@ -146,18 +186,18 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
}
|
||||
};
|
||||
})
|
||||
.factory('LineChart', function(Settings) {
|
||||
.factory('LineChart', function (Settings) {
|
||||
'use strict';
|
||||
var url = Settings.rawUrl + '/build';
|
||||
var url = Settings.rawUrl + '/build';
|
||||
return {
|
||||
build: function(id, data, getkey){
|
||||
build: function (id, data, getkey) {
|
||||
var chart = new Chart($(id).get(0).getContext("2d"));
|
||||
var map = {};
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = data[i];
|
||||
var key = getkey(c);
|
||||
|
||||
|
||||
var count = map[key];
|
||||
if (count === undefined) {
|
||||
count = 0;
|
||||
|
@ -176,22 +216,22 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
data.push(map[k]);
|
||||
}
|
||||
var dataset = {
|
||||
fillColor : "rgba(151,187,205,0.5)",
|
||||
strokeColor : "rgba(151,187,205,1)",
|
||||
pointColor : "rgba(151,187,205,1)",
|
||||
pointStrokeColor : "#fff",
|
||||
data : data
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: data
|
||||
};
|
||||
chart.Line({
|
||||
labels: labels,
|
||||
datasets: [dataset]
|
||||
},
|
||||
{
|
||||
scaleStepWidth: 1,
|
||||
pointDotRadius:1,
|
||||
scaleOverride: true,
|
||||
scaleSteps: labels.length
|
||||
});
|
||||
labels: labels,
|
||||
datasets: [dataset]
|
||||
},
|
||||
{
|
||||
scaleStepWidth: 1,
|
||||
pointDotRadius: 1,
|
||||
scaleOverride: true,
|
||||
scaleSteps: labels.length
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
@ -0,0 +1,238 @@
|
|||
angular.module('ngVis', [])
|
||||
|
||||
.factory('VisDataSet', function () {
|
||||
'use strict';
|
||||
return function (data, options) {
|
||||
// Create the new dataSets
|
||||
return new vis.DataSet(data, options);
|
||||
};
|
||||
})
|
||||
|
||||
/**
|
||||
* TimeLine directive
|
||||
*/
|
||||
.directive('visTimeline', function () {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'EA',
|
||||
transclude: false,
|
||||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var timelineEvents = [
|
||||
'rangechange',
|
||||
'rangechanged',
|
||||
'timechange',
|
||||
'timechanged'
|
||||
];
|
||||
|
||||
// Declare the timeline
|
||||
var timeline = null;
|
||||
|
||||
scope.$watch('data', function () {
|
||||
// Sanity check
|
||||
if (scope.data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've actually changed the data set, then recreate the graph
|
||||
// We can always update the data by adding more data to the existing data set
|
||||
if (timeline != null) {
|
||||
timeline.destroy();
|
||||
}
|
||||
|
||||
// Create the timeline object
|
||||
timeline = new vis.Timeline(element[0]);
|
||||
scope.component = timeline;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
if (timelineEvents.indexOf(String(event)) >= 0) {
|
||||
timeline.on(event, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// Set the options first
|
||||
timeline.setOptions(scope.options);
|
||||
|
||||
// Add groups and items
|
||||
if (scope.data.groups != null) {
|
||||
timeline.setGroups(scope.data.groups);
|
||||
}
|
||||
if (scope.data.items != null) {
|
||||
timeline.setItems(scope.data.items);
|
||||
}
|
||||
|
||||
// onLoad callback
|
||||
if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||
scope.events.onload(timeline);
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watchCollection('options', function (options) {
|
||||
if(timeline == null) {
|
||||
return;
|
||||
}
|
||||
timeline.setOptions(options);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Directive for network chart.
|
||||
*/
|
||||
.directive('visNetwork', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
transclude: false,
|
||||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var networkEvents = [
|
||||
'rangechange',
|
||||
'rangechanged',
|
||||
'timechange',
|
||||
'timechanged'
|
||||
];
|
||||
|
||||
var network = new vis.Network(element[0], scope.data, scope.options);
|
||||
scope.component = network;
|
||||
|
||||
scope.$watch('data', function () {
|
||||
// Sanity check
|
||||
if (scope.data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've actually changed the data set, then recreate the graph
|
||||
// We can always update the data by adding more data to the existing data set
|
||||
// if (network !== undefined) {
|
||||
// network.destroy();
|
||||
// }
|
||||
|
||||
// Create the graph2d object
|
||||
network = new vis.Network(element[0]);
|
||||
scope.component = network;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
if (networkEvents.indexOf(String(event)) >= 0) {
|
||||
network.on(event, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// Set the options first
|
||||
network.setOptions(scope.options);
|
||||
network.setData(scope.data);
|
||||
|
||||
|
||||
// onLoad callback
|
||||
// if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||
// scope.events.onload(graph);
|
||||
// }
|
||||
});
|
||||
|
||||
scope.$watchCollection('options', function (options) {
|
||||
if(network == null) {
|
||||
return;
|
||||
}
|
||||
network.setOptions(options);
|
||||
});
|
||||
|
||||
scope.$watch('events', function (events) {
|
||||
angular.forEach(events, function (callback, event) {
|
||||
if (['select', 'click', 'hoverNode', 'doubleClick'].indexOf(String(event)) >= 0) {
|
||||
network.on(event, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
/**
|
||||
* Directive for graph2d.
|
||||
*/
|
||||
.directive('visGraph2d', function () {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'EA',
|
||||
transclude: false,
|
||||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var graphEvents = [
|
||||
'rangechange',
|
||||
'rangechanged',
|
||||
'timechange',
|
||||
'timechanged'
|
||||
];
|
||||
|
||||
// Create the chart
|
||||
var graph = new vis.Graph2d(element[0]);
|
||||
scope.component = graph;
|
||||
|
||||
scope.$watch('data', function () {
|
||||
// Sanity check
|
||||
if (scope.data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've actually changed the data set, then recreate the graph
|
||||
// We can always update the data by adding more data to the existing data set
|
||||
if (graph != null) {
|
||||
graph.destroy();
|
||||
}
|
||||
|
||||
// Create the graph2d object
|
||||
graph = new vis.Graph2d(element[0]);
|
||||
scope.component = graph;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
if (graphEvents.indexOf(String(event)) >= 0) {
|
||||
graph.on(event, callback);
|
||||
}
|
||||
});
|
||||
|
||||
// Set the options first
|
||||
graph.setOptions(scope.options);
|
||||
|
||||
// Add groups and items
|
||||
if (scope.data.groups != null) {
|
||||
graph.setGroups(scope.data.groups);
|
||||
}
|
||||
if (scope.data.items != null) {
|
||||
graph.setItems(scope.data.items);
|
||||
}
|
||||
|
||||
// onLoad callback
|
||||
if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||
scope.events.onload(graph);
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watchCollection('options', function (options) {
|
||||
if(graph == null) {
|
||||
return;
|
||||
}
|
||||
graph.setOptions(options);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
;
|
27
index.html
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="<%= pkg.name %>">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DockerUI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
@ -9,15 +9,16 @@
|
|||
|
||||
<link href="assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="assets/css/jquery.gritter.css" rel="stylesheet">
|
||||
<link href="assets/css/vis.min.css" rel="stylesheet">
|
||||
|
||||
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
<script src="assets/js/jquery-1.11.1.min.js"></script>
|
||||
<script src="assets/js/bootstrap.min.js"></script>
|
||||
|
||||
|
@ -27,6 +28,8 @@
|
|||
<script src="assets/js/jquery.gritter.min.js"></script>
|
||||
<script src="assets/js/Chart.min.js"></script>
|
||||
<script src="assets/js/legend.js"></script>
|
||||
<script src="assets/js/vis.min.js"></script>
|
||||
<script src="assets/js/angular-vis.js"></script>
|
||||
|
||||
<script src="<%= pkg.name %>.js"></script>
|
||||
|
||||
|
@ -36,19 +39,19 @@
|
|||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="assets/ico/apple-touch-icon-57-precomposed.png">
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
|
||||
<div id="view" ng-view></div>
|
||||
<div id="view" ng-view></div>
|
||||
|
||||
<div class="container-bottom"></div>
|
||||
<div ng-include="template" ng-controller="FooterController"></div>
|
||||
</div>
|
||||
<div class="container-bottom"></div>
|
||||
<div ng-include="template" ng-controller="FooterController"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('ContainerController', function() {
|
||||
describe('ContainerController', function () {
|
||||
var $scope, $httpBackend, mockContainer, $routeParams;
|
||||
|
||||
beforeEach(module('dockerui'));
|
||||
|
@ -27,7 +27,7 @@ describe('ContainerController', function() {
|
|||
});
|
||||
}
|
||||
|
||||
it("a correct create request to the Docker remote API", function () {
|
||||
it("a correct rename request to the Docker remote API", function () {
|
||||
|
||||
$routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f';
|
||||
$scope.container = {
|
||||
|
@ -41,7 +41,7 @@ describe('ContainerController', function() {
|
|||
var newContainerName = "newName";
|
||||
expectGetContainer();
|
||||
|
||||
$httpBackend.expectGET('dockerapi/containers/changes?').respond([{"Kind":1,"Path":"/docker.sock"}]);
|
||||
$httpBackend.expectGET('dockerapi/containers/changes?').respond([{"Kind": 1, "Path": "/docker.sock"}]);
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + $routeParams.id + '/rename?name=newName').
|
||||
respond({
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
describe("ContainerTopController", function () {
|
||||
var $scope, $httpBackend, $routeParams;
|
||||
|
||||
beforeEach(angular.mock.module('dockerui'));
|
||||
|
||||
beforeEach(inject(function (_$rootScope_, _$httpBackend_, $controller, _$routeParams_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$httpBackend = _$httpBackend_;
|
||||
$routeParams = _$routeParams_;
|
||||
$routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f';
|
||||
$controller('ContainerTopController', {
|
||||
'$scope': $scope,
|
||||
'$routeParams': $routeParams
|
||||
});
|
||||
}));
|
||||
|
||||
it("should test controller initialize", function () {
|
||||
$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/' + $routeParams.id + '/top?ps_args=').respond(200);
|
||||
$routeParams.id = '123456789123456789123456789';
|
||||
$scope.ps_args = 'aux';
|
||||
$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=' + $scope.ps_args).respond(200);
|
||||
$scope.getTop();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
|
@ -13,6 +13,7 @@ files = [
|
|||
'assets/js/angularjs/1.2.6/angular-route.min.js',
|
||||
'assets/js/angularjs/1.2.6/angular-resource.min.js',
|
||||
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
|
||||
'assets/js/angular-vis.js',
|
||||
'test/assets/angular/angular-mocks.js',
|
||||
'app/**/*.js',
|
||||
'test/unit/**/*.spec.js',
|
||||
|
@ -57,4 +58,4 @@ browsers = ['Chrome'];
|
|||
|
||||
// Continuous Integration mode
|
||||
// if true, it capture browsers, run tests and exit
|
||||
singleRun = true;
|
||||
singleRun = true;
|
||||
|
|