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

Merge branch 'master' into events

Conflicts:
	app/app.js
	index.html
This commit is contained in:
Kevan Ahlquist 2015-05-14 01:33:08 -05:00
commit ef3596ff32
25 changed files with 616 additions and 4 deletions

View file

@ -1,4 +1,4 @@
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events'])
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events'])
.config(['$routeProvider', function ($routeProvider) {
'use strict';
$routeProvider.when('/', {
@ -21,6 +21,10 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
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'

View file

@ -40,6 +40,14 @@
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">

View file

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

View file

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

View file

@ -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);
}
});
}]);

View file

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

View file

@ -20,6 +20,23 @@ angular.module('dockerui.services', ['ngResource'])
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false}
});
})
.factory('ContainerCommit', function ($resource, $http, Settings) {
'use strict';
return {
commit: function (params, callback) {
$http({
method: 'POST',
url: Settings.url + '/commit',
params: {
'container': params.id,
'repo': params.repo
}
}).success(callback).error(function (data, status, headers, config) {
console.log(error, data);
});
}
};
})
.factory('ContainerLogs', function ($resource, $http, Settings) {
'use strict';
return {