diff --git a/README.md b/README.md index 4c16dd5c3..d0a466feb 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,47 @@ -# Portainer -The easiest way to manage Docker. +

+ +

[![Microbadger version](https://images.microbadger.com/badges/version/portainer/portainer.svg)](https://microbadger.com/images/portainer/portainer "Latest version on Docker Hub") [![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer "Image size") -[![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=stable)](http://portainer.readthedocs.io/en/stable/?badge=stable) +[![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=latest)](http://portainer.readthedocs.io/en/latest/?badge=latest) [![Gitter](https://badges.gitter.im/portainer/Lobby.svg)](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. +**_Portainer_** is a lightweight management UI which allows you to **easily** manage your Docker host or Swarm cluster. -# Usage +**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker for Linux engine. A Docker for Windows version is on its way ! -It's really simple to deploy it using Docker: +**_Portainer_** allows you to manage your Docker containers, images, volumes, networks and more ! It is compatible with the *standalone Docker* engine and with *Docker Swarm*. -```shell -$ docker run -d -p 9000:9000 portainer/portainer -H tcp://: -``` +## Demo -Just point it at your targeted Docker host and then access Portainer by hitting [http://localhost:9000](http://localhost:9000) with a web browser. + -If your target is a Docker Swarm cluster or a Docker cluster using *swarm mode*, just add the flag `--swarm`: +You can try out the public demo instance: http://demo.portainer.io/ (login with the username **demo** and the password **tryportainer**). -```shell -$ docker run -d -p 9000:9000 portainer/portainer -H tcp://: --swarm -``` +Please note that the public demo cluster is **reset every 15min**. -If you don't specify any target, its default behaviour is to use a bind mount on the Docker socket so you can easily deploy it to manage your local Docker host: +## Getting started -```shell -$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer -``` +* [Deploy Portainer](https://portainer.readthedocs.io/en/latest/deployment.html) +* [Documentation](https://portainer.readthedocs.io) -Have a look at our [documentation](http://portainer.readthedocs.io/en/stable/deployment.html) for more deployment options. +## Getting help -# Configuration +* Issues: https://github.com/portainer/portainer/issues +* FAQ: https://portainer.readthedocs.io/en/latest/faq.html +* Gitter (chat): https://gitter.im/portainer/Lobby +* Slack: http://portainer.io/slack/ -Portainer is easy to tune using CLI flags. +## Reporting bugs and contributing -## Hiding specific containers +* Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new). +* Want to help us build **_portainer_**? Follow our [contribution guidelines](https://portainer.readthedocs.io/en/latest/contribute.html) to build it locally and make a pull request. We need all the help we can get! -Portainer allows you to hide container with a specific label by using the `-l` flag. +## Limitations -For example, take a container started with the label `owner=acme`: -```shell -$ docker run -d --label owner=acme nginx -``` - -Simply add the `-l owner=acme` option on the CLI when starting Portainer: -```shell -$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer -l owner=acme -``` - -## Use your own templates - -Portainer allows you to rapidly deploy containers using `App Templates`. - -By default [Portainer templates](https://raw.githubusercontent.com/portainer/templates/master/templates.json) will be used but you can also define your own templates. - -Add the `--templates` flag and specify the external location of your templates when starting Portainer: - -```shell -$ 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 the [templates documentation](http://portainer.readthedocs.io/en/stable/templates.html). - -Check our [documentation](http://portainer.readthedocs.io/en/stable/configuration.html) for more configuration options. - -# FAQ - -Be sure to check our [FAQ](http://portainer.readthedocs.io/en/stable/faq.html) if you are missing some information. - -# Limitations - -Portainer has full support for the following Docker versions: +**_Portainer_** has full support for the following Docker versions: * Docker 1.10 to Docker 1.12 (including `swarm-mode`) * Docker Swarm >= 1.2.3 diff --git a/api/main.go b/api/main.go index a4dce7ec3..c92fb57b4 100644 --- a/api/main.go +++ b/api/main.go @@ -6,7 +6,7 @@ import ( // main is the entry point of the program func main() { - kingpin.Version("1.9.2") + kingpin.Version("1.9.3") 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() diff --git a/app/app.js b/app/app.js index 258429903..5a1fda021 100644 --- a/app/app.js +++ b/app/app.js @@ -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.2'); + .constant('UI_VERSION', 'v1.9.3'); diff --git a/app/components/containers/containers.html b/app/components/containers/containers.html index 1e13ffac3..6369dcc37 100644 --- a/app/components/containers/containers.html +++ b/app/components/containers/containers.html @@ -98,6 +98,9 @@ - + + Loading... + No containers available. diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js index bbbe506d0..caef7c94b 100644 --- a/app/components/containers/containersController.js +++ b/app/components/containers/containersController.js @@ -8,7 +8,6 @@ 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; @@ -34,6 +33,10 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config) return model; }); $('#loadContainersSpinner').hide(); + }, function (e) { + $('#loadContainersSpinner').hide(); + Messages.error("Failure", e, "Unable to retrieve containers"); + $scope.containers = []; }); }; diff --git a/app/components/images/images.html b/app/components/images/images.html index 1405432c0..cad9b590b 100644 --- a/app/components/images/images.html +++ b/app/components/images/images.html @@ -107,6 +107,9 @@ {{ image.VirtualSize|humansize }} {{ image.Created|getisodatefromtimestamp }} + + Loading... + No images available. diff --git a/app/components/images/imagesController.js b/app/components/images/imagesController.js index 76ff6e29d..0d17817c4 100644 --- a/app/components/images/imagesController.js +++ b/app/components/images/imagesController.js @@ -5,7 +5,6 @@ function ($scope, $state, Config, Image, Messages) { $scope.sortType = 'RepoTags'; $scope.sortReverse = true; $scope.state.selectedItemCount = 0; - $scope.images = []; $scope.config = { Image: '', @@ -98,6 +97,7 @@ function ($scope, $state, Config, Image, Messages) { }, function (e) { $('#loadImagesSpinner').hide(); Messages.error("Failure", e, "Unable to retrieve images"); + $scope.images = []; }); } diff --git a/app/components/networks/networks.html b/app/components/networks/networks.html index 20ebf0f5b..6d75df9c5 100644 --- a/app/components/networks/networks.html +++ b/app/components/networks/networks.html @@ -131,6 +131,9 @@ {{ network.IPAM.Config[0].Subnet ? network.IPAM.Config[0].Subnet : '-' }} {{ network.IPAM.Config[0].Gateway ? network.IPAM.Config[0].Gateway : '-' }} + + Loading... + No networks available. diff --git a/app/components/networks/networksController.js b/app/components/networks/networksController.js index 818f4f58b..4178cd047 100644 --- a/app/components/networks/networksController.js +++ b/app/components/networks/networksController.js @@ -6,8 +6,6 @@ function ($scope, $state, Network, Config, Messages) { $scope.state.advancedSettings = false; $scope.sortType = 'Name'; $scope.sortReverse = false; - $scope.networks = []; - $scope.config = { Name: '' }; @@ -93,6 +91,7 @@ function ($scope, $state, Network, Config, Messages) { }, function (e) { $('#loadNetworksSpinner').hide(); Messages.error("Failure", e, "Unable to retrieve networks"); + $scope.networks = []; }); } diff --git a/app/components/services/services.html b/app/components/services/services.html index 8cc618a28..ede13aabd 100644 --- a/app/components/services/services.html +++ b/app/components/services/services.html @@ -69,6 +69,12 @@ + + Loading... + + + No services available. + diff --git a/app/components/services/servicesController.js b/app/components/services/servicesController.js index ac7054e01..52ff25a86 100644 --- a/app/components/services/servicesController.js +++ b/app/components/services/servicesController.js @@ -1,8 +1,6 @@ angular.module('services', []) .controller('ServicesController', ['$scope', '$stateParams', '$state', 'Service', 'ServiceHelper', 'Messages', function ($scope, $stateParams, $state, Service, ServiceHelper, Messages) { - - $scope.services = []; $scope.state = {}; $scope.state.selectedItemCount = 0; $scope.sortType = 'Name'; @@ -77,6 +75,7 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Messages) { }, function(e) { $('#loadServicesSpinner').hide(); Messages.error("Failure", e, "Unable to retrieve services"); + $scope.services = []; }); } diff --git a/app/components/templates/templates.html b/app/components/templates/templates.html index b5a479d75..4c18a38a0 100644 --- a/app/components/templates/templates.html +++ b/app/components/templates/templates.html @@ -77,11 +77,14 @@
-
+
{{ tpl.title }}
{{ tpl.description }}
+
+ Loading... +
No templates available.
diff --git a/app/components/templates/templatesController.js b/app/components/templates/templatesController.js index dca02bbea..3d8eb783b 100644 --- a/app/components/templates/templatesController.js +++ b/app/components/templates/templatesController.js @@ -1,13 +1,11 @@ angular.module('templates', []) .controller('TemplatesController', ['$scope', '$q', '$state', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'Volume', 'Network', 'Templates', 'Messages', function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper, Image, Volume, Network, Templates, Messages) { - $scope.templates = []; $scope.selectedTemplate = null; $scope.formValues = { network: "", name: "" }; - $scope.templates = []; var selectedItem = -1; @@ -161,6 +159,7 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper, }, function (e) { $('#loadTemplatesSpinner').hide(); Messages.error("Failure", e, "Unable to retrieve apps list"); + $scope.templates = []; }); } diff --git a/app/components/volumes/volumes.html b/app/components/volumes/volumes.html index 4c3ae2072..88791d2ac 100644 --- a/app/components/volumes/volumes.html +++ b/app/components/volumes/volumes.html @@ -59,6 +59,9 @@ {{ volume.Driver }} {{ volume.Mountpoint }} + + Loading... + No volumes available. diff --git a/app/components/volumes/volumesController.js b/app/components/volumes/volumesController.js index fc0dc8888..202b0835b 100644 --- a/app/components/volumes/volumesController.js +++ b/app/components/volumes/volumesController.js @@ -5,8 +5,6 @@ function ($scope, $state, Volume, Messages) { $scope.state.selectedItemCount = 0; $scope.sortType = 'Name'; $scope.sortReverse = true; - $scope.volumes = []; - $scope.config = { Name: '' }; @@ -61,6 +59,7 @@ function ($scope, $state, Volume, Messages) { }, function (e) { $('#loadVolumesSpinner').hide(); Messages.error("Failure", e, "Unable to retrieve volumes"); + $scope.volumes = []; }); } fetchVolumes(); diff --git a/app/directives/widget-custom-header.js b/app/directives/widget-custom-header.js index c1444b3f6..54150f1f0 100644 --- a/app/directives/widget-custom-header.js +++ b/app/directives/widget-custom-header.js @@ -8,7 +8,7 @@ angular icon: '=' }, transclude: true, - template: '
{{title}}
', + template: '
{{title}}
', restrict: 'E' }; return directive; diff --git a/assets/css/app.css b/assets/css/app.css index d544242af..a73a44e89 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -217,16 +217,67 @@ input[type="radio"] { } .custom-header-ico { - max-width: 16px; - max-height: 16px; + max-width: 32px; + max-height: 32px; + margin-right: 2px; +} + +.btn-responsive { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +@media screen and (min-width: 1107px) { + .btn-responsive { + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + } +} + +/* Underline From Center */ +.hvr-underline-from-center { + display: inline-block; + vertical-align: middle; + -webkit-transform: translateZ(0); + transform: translateZ(0); + box-shadow: 0 0 1px rgba(0, 0, 0, 0); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -moz-osx-font-smoothing: grayscale; + position: relative; + overflow: hidden; +} +.hvr-underline-from-center:before { + content: ""; + position: absolute; + z-index: -1; + left: 50%; + right: 50%; + bottom: 0; + background: #85898b; + height: 2px; + -webkit-transition-property: left, right; + transition-property: left, right; + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; + -webkit-transition-timing-function: ease-out; + transition-timing-function: ease-out; +} +.hvr-underline-from-center:hover:before, .hvr-underline-from-center:focus:before, .hvr-underline-from-center:active:before { + left: 0; + right: 0; } .container-template { font-size: 1em; width: 256px; height: 128px; - margin: 10px; - padding: 10px; + margin: 15px; + padding: 5px; display: flex; flex-direction: column; justify-content: center; @@ -259,20 +310,5 @@ input[type="radio"] { .container-template .description { text-align: center; font-size: 0.8em; -} - -.btn-responsive { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} - -@media screen and (min-width: 1107px) { - .btn-responsive { - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - border-radius: 4px; - } + margin-bottom: 5px; } diff --git a/bower.json b/bower.json index d675341bc..31de21537 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "portainer", - "version": "1.9.2", + "version": "1.9.3", "homepage": "https://github.com/portainer/portainer", "authors": [ "Anthony Lapenna " @@ -35,7 +35,6 @@ "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", diff --git a/gruntFile.js b/gruntFile.js index ff3acd980..d1b81485c 100644 --- a/gruntFile.js +++ b/gruntFile.js @@ -88,8 +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/dist/xterm.css', - 'bower_components/Hover/css/hover-min.css' + 'bower_components/xterm.js/dist/xterm.css' ] }, clean: { diff --git a/package.json b/package.json index 4f1b118f1..06410a665 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Portainer.io", "name": "portainer", "homepage": "http://portainer.io", - "version": "1.9.2", + "version": "1.9.3", "repository": { "type": "git", "url": "git@github.com:portainer/portainer.git"