Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb9e044e89 | |||
| 520532cb9a | |||
| 44e09ecadf | |||
| 35ced4901a | |||
| 134416c9a3 | |||
| 8f7f4acc0d | |||
| fde0d3ea9f | |||
| 477799af7e | |||
| 72570153a5 | |||
| 9f335b692f | |||
| e88b22bd45 | |||
| 833053a2e1 | |||
| 64c52348f3 | |||
| c3b79e6cc2 | |||
| 422a982d60 | |||
| 6e9fe26fde | |||
| 6bfa3096dc | |||
| 7cd2da4c6e | |||
| 739a5ec299 | |||
| 59e65222eb | |||
| 01d5d11c01 | |||
| 29a59cab44 | |||
| be184c11a6 | |||
| d6ab97ad25 | |||
| 6a0f76890e | |||
| 1946868248 | |||
| 84b02c711a | |||
| 679a681749 |
@@ -0,0 +1,43 @@
|
||||
<!--
|
||||
|
||||
Thanks for opening an issue on Portainer !
|
||||
|
||||
Do you need help or have a question? Come chat with us on gitter: https://gitter.im/portainer/Lobby.
|
||||
|
||||
If you are reporting a new issue, make sure that we do not have any duplicates
|
||||
already open. You can ensure this by searching the issue list for this
|
||||
repository. If there is a duplicate, please close your issue and add a comment
|
||||
to the existing issue instead.
|
||||
|
||||
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
|
||||
|
||||
If you suspect your issue is a bug, please edit your issue description to
|
||||
include the BUG REPORT INFORMATION shown below.
|
||||
|
||||
---------------------------------------------------
|
||||
BUG REPORT INFORMATION
|
||||
---------------------------------------------------
|
||||
You do NOT have to include this information if this is a FEATURE REQUEST
|
||||
-->
|
||||
|
||||
**Description**
|
||||
|
||||
<!--
|
||||
Briefly describe the problem you are having in a few paragraphs.
|
||||
-->
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Any other info e.g. Why do you consider this to be a bug? What did you expect to happen instead?
|
||||
|
||||
**Technical details:**
|
||||
|
||||
* Portainer version:
|
||||
* Target Docker version (the host/cluster you manage):
|
||||
* Target Swarm version (if applicable):
|
||||
* Platform (windows/linux):
|
||||
* Browser:
|
||||
+3
-4
@@ -24,10 +24,9 @@ Each commit message should include a **type**, a **scope** and a **subject**:
|
||||
Lines should not exceed 100 characters. This allows the message to be easier to read on github as well as in various git tools and produces a nice, neat commit log ie:
|
||||
|
||||
```
|
||||
#271 feat(standard): add style config and refactor to match
|
||||
#270 fix(config): only override publicPath when served by webpack
|
||||
#269 feat(eslint-config-defaults): replace eslint-config-airbnb
|
||||
#268 feat(config): allow user to configure webpack stats output
|
||||
#271 feat(containers): add exposed ports in the containers view
|
||||
#270 fix(templates): fix a display issue in the templates view
|
||||
#269 style(dashboard): update dashboard with new layout
|
||||
```
|
||||
|
||||
#### Type
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
The easiest way to manage Docker.
|
||||
|
||||
[](https://microbadger.com/images/portainer/portainer "Latest version on Docker Hub")
|
||||
[](http://microbadger.com/images/portainer/portainer "Image size")
|
||||
[](http://portainer.readthedocs.io/en/stable/?badge=stable)
|
||||
[](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Portainer is a lightweight management UI which allows you to **easily** manage your Docker host or Swarm cluster.
|
||||
@@ -29,7 +31,7 @@ If you don't specify any target, its default behaviour is to use a bind mount on
|
||||
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
|
||||
```
|
||||
|
||||
Have a look at our [wiki](https://github.com/portainer/portainer/wiki/Deployment) for more deployment options.
|
||||
Have a look at our [documentation](http://portainer.readthedocs.io/en/stable/deployment.html) for more deployment options.
|
||||
|
||||
# Configuration
|
||||
|
||||
@@ -61,13 +63,13 @@ Add the `--templates` flag and specify the external location of your templates w
|
||||
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer --templates http://my-host.my-domain/templates.json
|
||||
```
|
||||
|
||||
For more information about hosting your own template definitions and the format, see: https://github.com/portainer/templates
|
||||
For more information about hosting your own template definitions and the format, see the [templates documentation](http://portainer.readthedocs.io/en/stable/templates.html).
|
||||
|
||||
Have a look at our [wiki](https://github.com/portainer/portainer/wiki/Configuration) for more configuration options.
|
||||
Check our [documentation](http://portainer.readthedocs.io/en/stable/configuration.html) for more configuration options.
|
||||
|
||||
# FAQ
|
||||
|
||||
Be sure to check our [FAQ](https://github.com/portainer/portainer/wiki/FAQ) if you are missing some information.
|
||||
Be sure to check our [FAQ](http://portainer.readthedocs.io/en/stable/faq.html) if you are missing some information.
|
||||
|
||||
# Limitations
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
// main is the entry point of the program
|
||||
func main() {
|
||||
kingpin.Version("1.9.0")
|
||||
kingpin.Version("1.9.2")
|
||||
var (
|
||||
endpoint = kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String()
|
||||
addr = kingpin.Flag("bind", "Address and port to serve Portainer").Default(":9000").Short('p').String()
|
||||
|
||||
+1
-1
@@ -188,4 +188,4 @@ angular.module('portainer', [
|
||||
.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('CONFIG_ENDPOINT', 'settings')
|
||||
.constant('TEMPLATES_ENDPOINT', 'templates')
|
||||
.constant('UI_VERSION', 'v1.9.0');
|
||||
.constant('UI_VERSION', 'v1.9.2');
|
||||
|
||||
@@ -9,7 +9,7 @@ function ($scope, $stateParams, Settings, Container, Exec, $timeout, Messages) {
|
||||
|
||||
// Ensure the socket is closed before leaving the view
|
||||
$scope.$on('$stateChangeStart', function (event, next, current) {
|
||||
if (socket !== null) {
|
||||
if (socket && socket !== null) {
|
||||
socket.close();
|
||||
}
|
||||
});
|
||||
@@ -82,16 +82,14 @@ function ($scope, $stateParams, Settings, Container, Exec, $timeout, Messages) {
|
||||
$scope.connected = true;
|
||||
socket.onopen = function(evt) {
|
||||
$('#loadConsoleSpinner').hide();
|
||||
term = new Terminal({
|
||||
cols: width,
|
||||
rows: height,
|
||||
cursorBlink: true
|
||||
});
|
||||
term = new Terminal();
|
||||
|
||||
term.on('data', function (data) {
|
||||
socket.send(data);
|
||||
});
|
||||
term.open(document.getElementById('terminal-container'));
|
||||
term.resize(width, height);
|
||||
term.setOption('cursorBlink', true);
|
||||
|
||||
socket.onmessage = function (e) {
|
||||
term.write(e.data);
|
||||
@@ -102,8 +100,6 @@ function ($scope, $stateParams, Settings, Container, Exec, $timeout, Messages) {
|
||||
};
|
||||
socket.onclose = function(evt) {
|
||||
$scope.connected = false;
|
||||
// term.write("Session terminated");
|
||||
// term.destroy();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
<th>
|
||||
<a ui-sref="containers" ng-click="order('Status')">
|
||||
State
|
||||
<span ng-show="sortType == 'State' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'State' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
<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>
|
||||
<th>
|
||||
@@ -93,11 +93,14 @@
|
||||
<td ng-if="swarm && !swarm_mode">{{ container.hostIP }}</td>
|
||||
<td>
|
||||
<a ng-if="container.Ports.length > 0" ng-repeat="p in container.Ports" class="image-tag" ng-href="http://{{p.host}}:{{p.public}}" target="_blank">
|
||||
<i class="fa fa-external-link" aria-hidden="true"></i> {{ p.private }}
|
||||
<i class="fa fa-external-link" aria-hidden="true"></i> {{p.public}}:{{ p.private }}
|
||||
</a>
|
||||
<span ng-if="container.Ports.length == 0" >-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="containers.length == 0">
|
||||
<td colspan="8" class="text-center text-muted">No containers available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
angular.module('containers', [])
|
||||
.controller('ContainersController', ['$scope', 'Container', 'ContainerHelper', 'Info', 'Settings', 'Messages', 'Config',
|
||||
function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config) {
|
||||
|
||||
$scope.state = {};
|
||||
$scope.state.displayAll = Settings.displayAll;
|
||||
$scope.state.displayIP = false;
|
||||
@@ -9,6 +8,7 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
||||
$scope.sortReverse = false;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.swarm_mode = false;
|
||||
$scope.containers = [];
|
||||
|
||||
$scope.order = function (sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
|
||||
@@ -156,10 +156,10 @@ function ($scope, $state, Config, Info, Container, Image, Volume, Network, Messa
|
||||
function preparePortBindings(config) {
|
||||
var bindings = {};
|
||||
config.HostConfig.PortBindings.forEach(function (portBinding) {
|
||||
if (portBinding.hostPort && portBinding.containerPort) {
|
||||
if (portBinding.containerPort) {
|
||||
var key = portBinding.containerPort + "/" + portBinding.protocol;
|
||||
var binding = {};
|
||||
if (portBinding.hostPort.indexOf(':') > -1) {
|
||||
if (portBinding.hostPort && portBinding.hostPort.indexOf(':') > -1) {
|
||||
var hostAndPort = portBinding.hostPort.split(':');
|
||||
binding.HostIp = hostAndPort[0];
|
||||
binding.HostPort = hostAndPort[1];
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<div ng-repeat="portBinding in config.HostConfig.PortBindings" style="margin-top: 2px;">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">host</span>
|
||||
<input type="text" class="form-control" ng-model="portBinding.hostPort" placeholder="e.g. 80 or 1.2.3.4:80">
|
||||
<input type="text" class="form-control" ng-model="portBinding.hostPort" placeholder="e.g. 80 or 1.2.3.4:80 (optional)">
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">container</span>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Nodes</td>
|
||||
<td>{{ infoData.SystemStatus[3][1] }}</td>
|
||||
<td>{{ infoData.SystemStatus[0][1] == 'primary' ? infoData.SystemStatus[3][1] : infoData.SystemStatus[4][1] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Swarm version</td>
|
||||
@@ -53,7 +53,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total memory</td>
|
||||
<td>{{ infoData.MemTotal|humansize }}</td>
|
||||
<td>{{ infoData.MemTotal|humansize: 2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
<td>{{ image.VirtualSize|humansize }}</td>
|
||||
<td>{{ image.Created|getisodatefromtimestamp }}</td>
|
||||
</tr>
|
||||
<tr ng-if="images.length == 0">
|
||||
<td colspan="5" class="text-center text-muted">No images available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ function ($scope, $state, Config, Image, Messages) {
|
||||
$scope.sortType = 'RepoTags';
|
||||
$scope.sortReverse = true;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.images = [];
|
||||
|
||||
$scope.config = {
|
||||
Image: '',
|
||||
|
||||
@@ -131,6 +131,9 @@
|
||||
<td>{{ network.IPAM.Config[0].Subnet ? network.IPAM.Config[0].Subnet : '-' }}</td>
|
||||
<td>{{ network.IPAM.Config[0].Gateway ? network.IPAM.Config[0].Gateway : '-' }}</td>
|
||||
</tr>
|
||||
<tr ng-if="networks.length == 0">
|
||||
<td colspan="8" class="text-center text-muted">No networks available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ function ($scope, $state, Network, Config, Messages) {
|
||||
$scope.state.advancedSettings = false;
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = false;
|
||||
$scope.networks = [];
|
||||
|
||||
$scope.config = {
|
||||
Name: ''
|
||||
|
||||
@@ -87,7 +87,7 @@ function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateP
|
||||
},
|
||||
{
|
||||
scaleLabel: function (valueObj) {
|
||||
return humansizeFilter(parseInt(valueObj.value, 10));
|
||||
return humansizeFilter(parseInt(valueObj.value, 10), 2);
|
||||
},
|
||||
responsive: true
|
||||
//scaleOverride: true,
|
||||
@@ -100,7 +100,7 @@ function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateP
|
||||
datasets: [networkRxDataset, networkTxDataset]
|
||||
}, {
|
||||
scaleLabel: function (valueObj) {
|
||||
return humansizeFilter(parseInt(valueObj.value, 10));
|
||||
return humansizeFilter(parseInt(valueObj.value, 10), 2);
|
||||
},
|
||||
responsive: true
|
||||
});
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total memory</td>
|
||||
<td ng-if="!swarm_mode">{{ info.MemTotal|humansize }}</td>
|
||||
<td ng-if="swarm_mode">{{ totalMemory|humansize }}</td>
|
||||
<td ng-if="!swarm_mode">{{ info.MemTotal|humansize: 2 }}</td>
|
||||
<td ng-if="swarm_mode">{{ totalMemory|humansize: 2 }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<td>Operating system</td>
|
||||
|
||||
@@ -47,7 +47,8 @@ function ($scope, Info, Version, Node) {
|
||||
// Swarm filters
|
||||
$scope.swarm[systemStatus[2][0]] = systemStatus[2][1];
|
||||
// Swarm node count
|
||||
var node_count = parseInt(systemStatus[3][1], 10);
|
||||
var nodes = systemStatus[0][1] === 'primary' ? systemStatus[3][1] : systemStatus[4][1];
|
||||
var node_count = parseInt(nodes, 10);
|
||||
$scope.swarm[systemStatus[3][0]] = node_count;
|
||||
|
||||
$scope.swarm.Status = [];
|
||||
@@ -55,9 +56,10 @@ function ($scope, Info, Version, Node) {
|
||||
}
|
||||
|
||||
function extractNodesInfo(info, node_count) {
|
||||
// First information for node1 available at element #4 of SystemStatus
|
||||
// First information for node1 available at element #4 of SystemStatus if connected to a primary
|
||||
// If connected to a replica, information for node1 is available at element #5
|
||||
// The next 10 elements are information related to the node
|
||||
var node_offset = 4;
|
||||
var node_offset = info[0][1] === 'primary' ? 4 : 5;
|
||||
for (i = 0; i < node_count; i++) {
|
||||
extractNodeInfo(info, node_offset);
|
||||
node_offset += 9;
|
||||
|
||||
@@ -7,31 +7,11 @@
|
||||
<rd-header-content>Templates</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-rocket" title="Available templates">
|
||||
<div class="pull-right">
|
||||
<i id="loadTemplatesSpinner" class="fa fa-cog fa-2x fa-spin" style="margin-top: 5px;"></i>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-body classes="padding">
|
||||
<div class="template-list">
|
||||
<div ng-repeat="tpl in templates" class="container-template hvr-grow" id="template_{{ $index }}" ng-click="selectTemplate($index)">
|
||||
<img class="logo" ng-src="{{ tpl.logo }}" />
|
||||
<div class="title">{{ tpl.title }}</div>
|
||||
<div class="description">{{ tpl.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="selectedTemplate">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-cogs" title="Configuration"></rd-widget-header>
|
||||
<rd-widget-custom-header icon="selectedTemplate.logo" title="selectedTemplate.image">
|
||||
</rd-widget-custom-header>
|
||||
<rd-widget-body classes="padding">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group" ng-if="globalNetworkCount === 0 && !swarm_mode">
|
||||
@@ -62,15 +42,21 @@
|
||||
<div ng-repeat="var in selectedTemplate.env" ng-if="!var.set" class="form-group">
|
||||
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select ng-if="!swarm && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<select ng-if="(!swarm || swarm && swarm_mode) && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
<select ng-if="swarm && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<select ng-if="swarm && !swarm_mode && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-default btn-sm" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
|
||||
<i id="createContainerSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
@@ -78,10 +64,29 @@
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="selectedTemplate">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" style="text-align: center;">
|
||||
<div>
|
||||
<i id="createContainerSpinner" class="fa fa-cog fa-3x fa-spin" style="margin-bottom: 5px; display: none;"></i>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default btn-lg" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-rocket" title="Available templates">
|
||||
<div class="pull-right">
|
||||
<i id="loadTemplatesSpinner" class="fa fa-cog fa-2x fa-spin" style="margin-top: 5px;"></i>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-body classes="padding">
|
||||
<div class="template-list">
|
||||
<div ng-repeat="tpl in templates" class="container-template hvr-grow" id="template_{{ $index }}" ng-click="selectTemplate($index)">
|
||||
<img class="logo" ng-src="{{ tpl.logo }}" />
|
||||
<div class="title">{{ tpl.title }}</div>
|
||||
<div class="description">{{ tpl.description }}</div>
|
||||
</div>
|
||||
<div ng-if="templates.length == 0" class="text-center text-muted">
|
||||
No templates available.
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
||||
network: "",
|
||||
name: ""
|
||||
};
|
||||
$scope.templates = [];
|
||||
|
||||
var selectedItem = -1;
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
<td>{{ volume.Driver }}</td>
|
||||
<td>{{ volume.Mountpoint }}</td>
|
||||
</tr>
|
||||
<tr ng-if="volumes.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No volumes available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ function ($scope, $state, Volume, Messages) {
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = true;
|
||||
$scope.volumes = [];
|
||||
|
||||
$scope.config = {
|
||||
Name: ''
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
angular
|
||||
.module('portainer')
|
||||
.directive('rdWidgetCustomHeader', function rdWidgetCustomHeader() {
|
||||
var directive = {
|
||||
requires: '^rdWidget',
|
||||
scope: {
|
||||
title: '=',
|
||||
icon: '='
|
||||
},
|
||||
transclude: true,
|
||||
template: '<div class="widget-header"><div class="row"><span class="pull-left"><img class="custom-header-ico" ng-src="{{icon}}"></img> <span class="small text-muted"> {{title}} </span> </span><span class="pull-right col-xs-6 col-sm-4" ng-transclude></span></div></div>',
|
||||
restrict: 'E'
|
||||
};
|
||||
return directive;
|
||||
});
|
||||
@@ -123,15 +123,13 @@ angular.module('portainer.filters', [])
|
||||
})
|
||||
.filter('humansize', function () {
|
||||
'use strict';
|
||||
return function (bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes === 0) {
|
||||
return 'n/a';
|
||||
return function (bytes, round) {
|
||||
if (!round) {
|
||||
round = 1;
|
||||
}
|
||||
if (bytes || bytes === 0) {
|
||||
return filesize(bytes, {base: 10, round: round});
|
||||
}
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
|
||||
var value = bytes / Math.pow(1024, i);
|
||||
var decimalPlaces = (i < 1) ? 0 : (i - 1);
|
||||
return value.toFixed(decimalPlaces) + ' ' + sizes[[i]];
|
||||
};
|
||||
})
|
||||
.filter('containername', function () {
|
||||
|
||||
@@ -105,6 +105,9 @@ function createEventDetails(event) {
|
||||
case 'unpause':
|
||||
details = 'Container ' + eventAttr.name + ' unpaused';
|
||||
break;
|
||||
case 'attach':
|
||||
details = 'Container ' + eventAttr.name + ' attached';
|
||||
break;
|
||||
default:
|
||||
if (event.Action.indexOf('exec_create') === 0) {
|
||||
details = 'Exec instance created';
|
||||
@@ -159,6 +162,12 @@ function createEventDetails(event) {
|
||||
case 'destroy':
|
||||
details = 'Volume ' + event.Actor.ID + ' deleted';
|
||||
break;
|
||||
case 'mount':
|
||||
details = 'Volume ' + event.Actor.ID + ' mounted';
|
||||
break;
|
||||
case 'unmount':
|
||||
details = 'Volume ' + event.Actor.ID + ' unmounted';
|
||||
break;
|
||||
default:
|
||||
details = 'Unsupported event';
|
||||
}
|
||||
|
||||
+7
-2
@@ -216,6 +216,11 @@ input[type="radio"] {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.custom-header-ico {
|
||||
max-width: 16px;
|
||||
max-height: 16px;
|
||||
}
|
||||
|
||||
.container-template {
|
||||
font-size: 1em;
|
||||
width: 256px;
|
||||
@@ -233,12 +238,12 @@ input[type="radio"] {
|
||||
}
|
||||
|
||||
.container-template--selected {
|
||||
background-color: #f6f6f6;
|
||||
background-color: #ececec;
|
||||
color: #2d3e63;
|
||||
}
|
||||
|
||||
.container-template:hover {
|
||||
background-color: #f6f6f6;
|
||||
background-color: #ececec;
|
||||
color: #2d3e63;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 880 B |
Binary file not shown.
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 880 B |
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "portainer",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"homepage": "https://github.com/portainer/portainer",
|
||||
"authors": [
|
||||
"Anthony Lapenna <anthony.lapenna at gmail dot com>"
|
||||
@@ -34,13 +34,14 @@
|
||||
"angular-ui-select": "~0.17.1",
|
||||
"bootstrap": "~3.3.6",
|
||||
"font-awesome": "~4.6.3",
|
||||
"filesize": "~3.3.0",
|
||||
"Hover": "2.0.2",
|
||||
"jquery": "1.11.1",
|
||||
"jquery.gritter": "1.7.4",
|
||||
"lodash": "4.12.0",
|
||||
"rdash-ui": "1.0.*",
|
||||
"moment": "~2.14.1",
|
||||
"xterm.js": "~1.0.0"
|
||||
"xterm.js": "~2.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.5.5"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=$1
|
||||
|
||||
if [[ $# -ne 1 ]] ; then
|
||||
echo "Usage: $(basename $0) <VERSION>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grunt release
|
||||
rm -rf /tmp/portainer-build && mkdir -pv /tmp/portainer-build/portainer
|
||||
mv dist/* /tmp/portainer-build/portainer
|
||||
cd /tmp/portainer-build
|
||||
tar cvpfz portainer-${VERSION}.tar.gz portainer
|
||||
|
||||
exit 0
|
||||
+4
-3
@@ -71,8 +71,9 @@ module.exports = function (grunt) {
|
||||
'bower_components/bootstrap/dist/js/bootstrap.min.js',
|
||||
'bower_components/Chart.js/Chart.min.js',
|
||||
'bower_components/lodash/dist/lodash.min.js',
|
||||
'bower_components/filesize/lib/filesize.min.js',
|
||||
'bower_components/moment/min/moment.min.js',
|
||||
'bower_components/xterm.js/src/xterm.js',
|
||||
'bower_components/xterm.js/dist/xterm.js',
|
||||
'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict"
|
||||
'assets/js/legend.js' // Not a bower package
|
||||
],
|
||||
@@ -87,7 +88,7 @@ module.exports = function (grunt) {
|
||||
'bower_components/font-awesome/css/font-awesome.min.css',
|
||||
'bower_components/rdash-ui/dist/css/rdash.min.css',
|
||||
'bower_components/angular-ui-select/dist/select.min.css',
|
||||
'bower_components/xterm.js/src/xterm.css',
|
||||
'bower_components/xterm.js/dist/xterm.css',
|
||||
'bower_components/Hover/css/hover-min.css'
|
||||
]
|
||||
},
|
||||
@@ -274,7 +275,7 @@ module.exports = function (grunt) {
|
||||
command: [
|
||||
'docker stop portainer',
|
||||
'docker rm portainer',
|
||||
'docker run -d -p 9000:9000 -v /tmp/portainer:/data --name portainer portainer -H tcp://10.0.7.10:4000 --swarm -d /data'
|
||||
'docker run -d -p 9000:9000 -v /tmp/portainer:/data --name portainer portainer -H tcp://10.0.7.10:2375 --swarm -d /data'
|
||||
].join(';')
|
||||
},
|
||||
runSsl: {
|
||||
|
||||
+2
-2
@@ -59,13 +59,13 @@
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="volumes">Volumes <span class="menu-icon fa fa-cubes"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="swarm_mode || !swarm">
|
||||
<li class="sidebar-list" ng-if="!swarm">
|
||||
<a ui-sref="events">Events <span class="menu-icon fa fa-history"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="(swarm && !swarm_mode) || (swarm_mode && swarm_manager)">
|
||||
<a ui-sref="swarm">Swarm <span class="menu-icon fa fa-object-group"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="swarm_mode || !swarm">
|
||||
<li class="sidebar-list" ng-if="!swarm">
|
||||
<a ui-sref="docker">Docker <span class="menu-icon fa fa-cogs"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
|
||||
@@ -82,32 +82,6 @@ describe('filters', function () {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('humansize', function () {
|
||||
it('should return n/a when size is zero', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(0)).toBe('n/a');
|
||||
}));
|
||||
|
||||
it('should handle Bytes values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(512)).toBe('512 Bytes');
|
||||
}));
|
||||
|
||||
it('should handle KB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024)).toBe('5 KB');
|
||||
}));
|
||||
|
||||
it('should handle MB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024)).toBe('5.0 MB');
|
||||
}));
|
||||
|
||||
it('should handle GB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024 * 1024)).toBe('5.00 GB');
|
||||
}));
|
||||
|
||||
it('should handle TB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024 * 1024 * 1024)).toBe('5.000 TB');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('containername', function () {
|
||||
it('should strip the leading slash from container name', inject(function (containernameFilter) {
|
||||
var container = {
|
||||
|
||||
Reference in New Issue
Block a user