From fa4ec04c470496afec56bb44ed8d73b9cdea5cb0 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 12:14:34 +1300 Subject: [PATCH] feat(state): introduce endpoint state (#529) --- api/bolt/endpoint_service.go | 12 ++ api/http/endpoint_handler.go | 17 ++- api/portainer.go | 1 + app/app.js | 15 +-- app/components/auth/authController.js | 11 +- app/components/containers/containers.html | 8 +- .../containers/containersController.js | 4 +- .../createContainerController.js | 4 +- .../createContainer/createcontainer.html | 6 +- app/components/dashboard/dashboard.html | 6 +- app/components/endpointInit/endpointInit.html | 14 ++- .../endpointInit/endpointInitController.js | 45 ++++++-- app/components/main/mainController.js | 6 +- app/components/networks/networks.html | 4 +- app/components/networks/networksController.js | 2 +- app/components/sidebar/sidebar.html | 8 +- app/components/sidebar/sidebarController.js | 12 +- app/components/swarm/swarm.html | 28 ++--- app/components/swarm/swarmController.js | 2 +- app/components/templates/templates.html | 8 +- .../templates/templatesController.js | 4 +- app/shared/helpers.js | 31 +++++ app/shared/services.js | 107 +++++++++++------- assets/css/app.css | 1 - gruntfile.js | 6 +- index.html | 27 ++++- 26 files changed, 267 insertions(+), 122 deletions(-) diff --git a/api/bolt/endpoint_service.go b/api/bolt/endpoint_service.go index 9046bf30f..16bd36984 100644 --- a/api/bolt/endpoint_service.go +++ b/api/bolt/endpoint_service.go @@ -160,3 +160,15 @@ func (service *EndpointService) SetActive(endpoint *portainer.Endpoint) error { return nil }) } + +// DeleteActive deletes the active endpoint. +func (service *EndpointService) DeleteActive() error { + return service.store.db.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(activeEndpointBucketName)) + err := bucket.Delete(internal.Itob(activeEndpointID)) + if err != nil { + return err + } + return nil + }) +} diff --git a/api/http/endpoint_handler.go b/api/http/endpoint_handler.go index 42a77cbec..9f5ca31c0 100644 --- a/api/http/endpoint_handler.go +++ b/api/http/endpoint_handler.go @@ -260,6 +260,7 @@ type putEndpointsRequest struct { } // handleDeleteEndpoint handles DELETE requests on /endpoints/:id +// DELETE /endpoints/0 deletes the active endpoint func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] @@ -270,7 +271,14 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h return } - endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID)) + var endpoint *portainer.Endpoint + if id == "0" { + endpoint, err = handler.EndpointService.GetActive() + endpointID = int(endpoint.ID) + } else { + endpoint, err = handler.EndpointService.Endpoint(portainer.EndpointID(endpointID)) + } + if err == portainer.ErrEndpointNotFound { Error(w, err, http.StatusNotFound, handler.Logger) return @@ -284,6 +292,13 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h Error(w, err, http.StatusInternalServerError, handler.Logger) return } + if id == "0" { + err = handler.EndpointService.DeleteActive() + if err != nil { + Error(w, err, http.StatusInternalServerError, handler.Logger) + return + } + } if endpoint.TLS { err = handler.FileService.DeleteTLSFiles(portainer.EndpointID(endpointID)) diff --git a/api/portainer.go b/api/portainer.go index c03679e84..24a9d0f27 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -94,6 +94,7 @@ type ( DeleteEndpoint(ID EndpointID) error GetActive() (*Endpoint, error) SetActive(endpoint *Endpoint) error + DeleteActive() error } // CryptoService represents a service for encrypting/hashing data. diff --git a/app/app.js b/app/app.js index 2c2a58589..7e5c61290 100644 --- a/app/app.js +++ b/app/app.js @@ -49,8 +49,8 @@ angular.module('portainer', [ .setPrefix('portainer'); jwtOptionsProvider.config({ - tokenGetter: ['localStorageService', function(localStorageService) { - return localStorageService.get('JWT'); + tokenGetter: ['LocalStorage', function(LocalStorage) { + return LocalStorage.getJWT(); }], unauthenticatedRedirector: ['$state', function($state) { $state.go('auth', {error: 'Your session has expired'}); @@ -528,21 +528,18 @@ angular.module('portainer', [ }; }); }]) - .run(['$rootScope', '$state', 'Authentication', 'authManager', 'EndpointMode', function ($rootScope, $state, Authentication, authManager, EndpointMode) { + .run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', function ($rootScope, $state, Authentication, authManager, StateManager) { authManager.checkAuthOnRefresh(); authManager.redirectWhenUnauthenticated(); + Authentication.init(); + StateManager.init(); + $rootScope.$state = $state; $rootScope.$on('tokenHasExpired', function($state) { $state.go('auth', {error: 'Your session has expired'}); }); - - $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) { - if (toState.name !== 'endpointInit' && (fromState.name === 'auth' || fromState.name === '' || fromState.name === 'endpointInit') && Authentication.isAuthenticated()) { - EndpointMode.determineEndpointMode(); - } - }); }]) // This is your docker url that the api will use to make requests // You need to set this to the api endpoint without the port i.e. http://192.168.1.9 diff --git a/app/components/auth/authController.js b/app/components/auth/authController.js index 89d99b6d3..2e30f2e5d 100644 --- a/app/components/auth/authController.js +++ b/app/components/auth/authController.js @@ -1,6 +1,6 @@ angular.module('auth', []) -.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'Messages', -function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, Messages) { +.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'Messages', +function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, StateManager, Messages) { $scope.authData = { username: 'admin', @@ -60,7 +60,12 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au var password = $sanitize($scope.authData.password); Authentication.login(username, password).then(function success() { EndpointService.getActive().then(function success(data) { - $state.go('dashboard'); + StateManager.updateEndpointState(true) + .then(function success() { + $state.go('dashboard'); + }, function error(err) { + Messages.error("Failure", err, 'Unable to connect to the Docker endpoint'); + }); }, function error(err) { if (err.status === 404) { $state.go('endpointInit'); diff --git a/app/components/containers/containers.html b/app/components/containers/containers.html index 1aaa749f8..89cb19f04 100644 --- a/app/components/containers/containers.html +++ b/app/components/containers/containers.html @@ -68,7 +68,7 @@ - + Host IP @@ -88,11 +88,11 @@ {{ container.Status }} - {{ container|swarmcontainername}} - {{ container|containername}} + {{ container|swarmcontainername}} + {{ container|containername}} {{ container.Image }} {{ container.IP ? container.IP : '-' }} - {{ container.hostIP }} + {{ container.hostIP }} {{p.public}}:{{ p.private }} diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js index 92fe95a9e..a5038c371 100644 --- a/app/components/containers/containersController.js +++ b/app/components/containers/containersController.js @@ -28,7 +28,7 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages, if (model.IP) { $scope.state.displayIP = true; } - if ($scope.endpointMode.provider === 'DOCKER_SWARM') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') { model.hostIP = $scope.swarm_hosts[_.split(container.Names[0], '/')[1]]; } return model; @@ -161,7 +161,7 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages, Config.$promise.then(function (c) { $scope.containersToHideLabels = c.hiddenLabels; - if ($scope.endpointMode.provider === 'DOCKER_SWARM') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') { Info.get({}, function (d) { $scope.swarm_hosts = retrieveSwarmHostsInfo(d); update({all: Settings.displayAll ? 1 : 0}); diff --git a/app/components/createContainer/createContainerController.js b/app/components/createContainer/createContainerController.js index be70bdec7..606e84d04 100644 --- a/app/components/createContainer/createContainerController.js +++ b/app/components/createContainer/createContainerController.js @@ -72,7 +72,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai Network.query({}, function (d) { var networks = d; - if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { networks = d.filter(function (network) { if (network.Scope === 'global') { return network; @@ -220,7 +220,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai var containerName = container; if (container && typeof container === 'object') { containerName = $filter('trimcontainername')(container.Names[0]); - if ($scope.endpointMode.provider === 'DOCKER_SWARM') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') { containerName = $filter('swarmcontainername')(container); } } diff --git a/app/components/createContainer/createcontainer.html b/app/components/createContainer/createcontainer.html index d7697b0b8..5be50586a 100644 --- a/app/components/createContainer/createcontainer.html +++ b/app/components/createContainer/createcontainer.html @@ -269,7 +269,7 @@
-
+
@@ -289,10 +289,10 @@
- -
diff --git a/app/components/dashboard/dashboard.html b/app/components/dashboard/dashboard.html index f9beebe91..e2aed5b41 100644 --- a/app/components/dashboard/dashboard.html +++ b/app/components/dashboard/dashboard.html @@ -6,7 +6,7 @@
-
+
@@ -33,7 +33,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/app/components/endpointInit/endpointInit.html b/app/components/endpointInit/endpointInit.html index 7d0f46eb5..5993ec833 100644 --- a/app/components/endpointInit/endpointInit.html +++ b/app/components/endpointInit/endpointInit.html @@ -21,10 +21,10 @@
- +
- +
@@ -41,7 +41,10 @@

{{ state.error }}

- + + + +
@@ -122,7 +125,10 @@

{{ state.error }}

- + + + +
diff --git a/app/components/endpointInit/endpointInitController.js b/app/components/endpointInit/endpointInitController.js index 2478bf8cf..b6aa99cdd 100644 --- a/app/components/endpointInit/endpointInitController.js +++ b/app/components/endpointInit/endpointInitController.js @@ -1,6 +1,6 @@ angular.module('endpointInit', []) -.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'Messages', -function ($scope, $state, EndpointService, Messages) { +.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'StateManager', 'Messages', +function ($scope, $state, EndpointService, StateManager, Messages) { $scope.state = { error: '', uploadInProgress: false @@ -15,27 +15,39 @@ function ($scope, $state, EndpointService, Messages) { TLSKey: null }; - EndpointService.getActive().then(function success(data) { + if (!_.isEmpty($scope.applicationState.endpoint)) { $state.go('dashboard'); - }, function error(err) { - if (err.status !== 404) { - Messages.error("Failure", err, 'Unable to verify Docker endpoint existence'); - } - }); + } + + $scope.cleanError = function() { + $scope.state.error = ''; + }; $scope.createLocalEndpoint = function() { + $('#initEndpointSpinner').show(); $scope.state.error = ''; var name = "local"; var URL = "unix:///var/run/docker.sock"; var TLS = false; EndpointService.createLocalEndpoint(name, URL, TLS, true).then(function success(data) { - $state.go('dashboard'); + StateManager.updateEndpointState(false) + .then(function success() { + $state.go('dashboard'); + }, function error(err) { + EndpointService.deleteEndpoint(0) + .then(function success() { + $('#initEndpointSpinner').hide(); + $scope.state.error = 'Unable to connect to the Docker endpoint'; + }); + }); }, function error(err) { + $('#initEndpointSpinner').hide(); $scope.state.error = 'Unable to create endpoint'; }); }; $scope.createRemoteEndpoint = function() { + $('#initEndpointSpinner').show(); $scope.state.error = ''; var name = $scope.formValues.Name; var URL = $scope.formValues.URL; @@ -43,9 +55,20 @@ function ($scope, $state, EndpointService, Messages) { var TLSCAFile = $scope.formValues.TLSCACert; var TLSCertFile = $scope.formValues.TLSCert; var TLSKeyFile = $scope.formValues.TLSKey; - EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, TLS ? false : true).then(function success(data) { - $state.go('dashboard'); + EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, TLS ? false : true) + .then(function success(data) { + StateManager.updateEndpointState(false) + .then(function success() { + $state.go('dashboard'); + }, function error(err) { + EndpointService.deleteEndpoint(0) + .then(function success() { + $('#initEndpointSpinner').hide(); + $scope.state.error = 'Unable to connect to the Docker endpoint'; + }); + }); }, function error(err) { + $('#initEndpointSpinner').hide(); $scope.state.uploadInProgress = false; $scope.state.error = err.msg; }, function update(evt) { diff --git a/app/components/main/mainController.js b/app/components/main/mainController.js index 3a48c9bc4..3bfd98171 100644 --- a/app/components/main/mainController.js +++ b/app/components/main/mainController.js @@ -1,6 +1,6 @@ angular.module('main', []) -.controller('MainController', ['$scope', '$cookieStore', -function ($scope, $cookieStore) { +.controller('MainController', ['$scope', '$cookieStore', 'StateManager', +function ($scope, $cookieStore, StateManager) { /** * Sidebar Toggle & Cookie Control @@ -10,6 +10,8 @@ function ($scope, $cookieStore) { return window.innerWidth; }; + $scope.applicationState = StateManager.getState(); + $scope.$watch($scope.getWidth, function(newValue, oldValue) { if (newValue >= mobileView) { if (angular.isDefined($cookieStore.get('toggle'))) { diff --git a/app/components/networks/networks.html b/app/components/networks/networks.html index 3903365bf..1037bfe45 100644 --- a/app/components/networks/networks.html +++ b/app/components/networks/networks.html @@ -23,12 +23,12 @@
-
+
Note: The network will be created using the overlay driver and will allow containers to communicate across the hosts of your cluster.
-
+
Note: The network will be created using the bridge driver.
diff --git a/app/components/networks/networksController.js b/app/components/networks/networksController.js index 0ec8f30ee..7cc291d60 100644 --- a/app/components/networks/networksController.js +++ b/app/components/networks/networksController.js @@ -13,7 +13,7 @@ function ($scope, $state, Network, Config, Messages, Settings) { function prepareNetworkConfiguration() { var config = angular.copy($scope.config); - if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { config.Driver = 'overlay'; // Force IPAM Driver to 'default', should not be required. // See: https://github.com/docker/docker/issues/25735 diff --git a/app/components/sidebar/sidebar.html b/app/components/sidebar/sidebar.html index fda02fb20..044285e71 100644 --- a/app/components/sidebar/sidebar.html +++ b/app/components/sidebar/sidebar.html @@ -22,7 +22,7 @@ - - - - diff --git a/app/components/sidebar/sidebarController.js b/app/components/sidebar/sidebarController.js index 5d42e3cdb..f58d8399a 100644 --- a/app/components/sidebar/sidebarController.js +++ b/app/components/sidebar/sidebarController.js @@ -1,6 +1,6 @@ angular.module('sidebar', []) -.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'EndpointMode', 'Messages', -function ($scope, $state, Settings, Config, EndpointService, EndpointMode, Messages) { +.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'StateManager', 'Messages', +function ($scope, $state, Settings, Config, EndpointService, StateManager, Messages) { Config.$promise.then(function (c) { $scope.logo = c.logo; @@ -10,8 +10,12 @@ function ($scope, $state, Settings, Config, EndpointService, EndpointMode, Messa $scope.switchEndpoint = function(endpoint) { EndpointService.setActive(endpoint.Id).then(function success(data) { - EndpointMode.determineEndpointMode(); - $state.reload(); + StateManager.updateEndpointState(true) + .then(function success() { + $state.reload(); + }, function error(err) { + Messages.error("Failure", err, "Unable to connect to the Docker endpoint"); + }); }, function error(err) { Messages.error("Failure", err, "Unable to switch to new endpoint"); }); diff --git a/app/components/swarm/swarm.html b/app/components/swarm/swarm.html index 71ed078ca..ba90f3408 100644 --- a/app/components/swarm/swarm.html +++ b/app/components/swarm/swarm.html @@ -16,14 +16,14 @@ Nodes - {{ swarm.Nodes }} - {{ info.Swarm.Nodes }} + {{ swarm.Nodes }} + {{ info.Swarm.Nodes }} - + Images {{ info.Images }} - + Swarm version {{ docker.Version|swarmversion }} @@ -31,29 +31,29 @@ Docker API version {{ docker.ApiVersion }} - + Strategy {{ swarm.Strategy }} Total CPU - {{ info.NCPU }} - {{ totalCPU }} + {{ info.NCPU }} + {{ totalCPU }} Total memory - {{ info.MemTotal|humansize: 2 }} - {{ totalMemory|humansize: 2 }} + {{ info.MemTotal|humansize: 2 }} + {{ totalMemory|humansize: 2 }} - + Operating system {{ info.OperatingSystem }} - + Kernel version {{ info.KernelVersion }} - + Go version {{ docker.GoVersion }} @@ -65,7 +65,7 @@
-
+
@@ -133,7 +133,7 @@
-
+
diff --git a/app/components/swarm/swarmController.js b/app/components/swarm/swarmController.js index 2f7659407..d8fe95150 100644 --- a/app/components/swarm/swarmController.js +++ b/app/components/swarm/swarmController.js @@ -22,7 +22,7 @@ function ($scope, Info, Version, Node, Settings) { Info.get({}, function (d) { $scope.info = d; - if ($scope.endpointMode.provider === 'DOCKER_SWARM_MODE') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { Node.query({}, function(d) { $scope.nodes = d; var CPU = 0, memory = 0; diff --git a/app/components/templates/templates.html b/app/components/templates/templates.html index f6b402aef..98d53ba0e 100644 --- a/app/components/templates/templates.html +++ b/app/components/templates/templates.html @@ -13,12 +13,12 @@ -
+
When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the networks view to create one.
-
+
App templates cannot be used with swarm-mode at the moment. You can still use them to quickly deploy containers to the Docker host. @@ -41,10 +41,10 @@
- - diff --git a/app/components/templates/templatesController.js b/app/components/templates/templatesController.js index 384516d75..dbf6ce900 100644 --- a/app/components/templates/templatesController.js +++ b/app/components/templates/templatesController.js @@ -115,7 +115,7 @@ function ($scope, $q, $state, $filter, $anchorScroll, Config, Info, Container, C if (v.value || v.set) { var val; if (v.type && v.type === 'container') { - if ($scope.endpointMode.provider === 'DOCKER_SWARM' && $scope.formValues.network.Scope === 'global') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' && $scope.formValues.network.Scope === 'global') { val = $filter('swarmcontainername')(v.value); } else { var container = v.value; @@ -206,7 +206,7 @@ function ($scope, $q, $state, $filter, $anchorScroll, Config, Info, Container, C var containersToHideLabels = c.hiddenLabels; Network.query({}, function (d) { var networks = d; - if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') { + if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { networks = d.filter(function (network) { if (network.Scope === 'global') { return network; diff --git a/app/shared/helpers.js b/app/shared/helpers.js index 1bfc282c6..545c32b2d 100644 --- a/app/shared/helpers.js +++ b/app/shared/helpers.js @@ -1,4 +1,35 @@ angular.module('portainer.helpers', []) +.factory('InfoHelper', [function InfoHelperFactory() { + 'use strict'; + return { + determineEndpointMode: function(info) { + var mode = { + provider: '', + role: '' + }; + if (_.startsWith(info.ServerVersion, 'swarm')) { + mode.provider = "DOCKER_SWARM"; + if (info.SystemStatus[0][1] === 'primary') { + mode.role = "PRIMARY"; + } else { + mode.role = "REPLICA"; + } + } else { + if (!info.Swarm || _.isEmpty(info.Swarm.NodeID)) { + mode.provider = "DOCKER_STANDALONE"; + } else { + mode.provider = "DOCKER_SWARM_MODE"; + if (info.Swarm.ControlAvailable) { + mode.role = "MANAGER"; + } else { + mode.role = "WORKER"; + } + } + } + return mode; + } + }; +}]) .factory('ImageHelper', [function ImageHelperFactory() { 'use strict'; return { diff --git a/app/shared/services.js b/app/shared/services.js index 6cb3a27dd..e1c671e85 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -240,44 +240,11 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) initAdminUser: { method: 'POST', params: { username: 'admin', action: 'init' } } }); }]) - .factory('EndpointMode', ['$rootScope', 'Info', function EndpointMode($rootScope, Info) { - 'use strict'; - return { - determineEndpointMode: function() { - Info.get({}, function(d) { - var mode = { - provider: '', - role: '' - }; - if (_.startsWith(d.ServerVersion, 'swarm')) { - mode.provider = "DOCKER_SWARM"; - if (d.SystemStatus[0][1] === 'primary') { - mode.role = "PRIMARY"; - } else { - mode.role = "REPLICA"; - } - } else { - if (!d.Swarm || _.isEmpty(d.Swarm.NodeID)) { - mode.provider = "DOCKER_STANDALONE"; - } else { - mode.provider = "DOCKER_SWARM_MODE"; - if (d.Swarm.ControlAvailable) { - mode.role = "MANAGER"; - } else { - mode.role = "WORKER"; - } - } - } - $rootScope.endpointMode = mode; - }); - } - }; - }]) - .factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'localStorageService', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, localStorageService) { + .factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'LocalStorage', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, LocalStorage) { 'use strict'; return { init: function() { - var jwt = localStorageService.get('JWT'); + var jwt = LocalStorage.getJWT(); if (jwt) { var tokenPayload = jwtHelper.decodeToken(jwt); $rootScope.username = tokenPayload.username; @@ -287,7 +254,7 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) return $q(function (resolve, reject) { Auth.login({username: username, password: password}).$promise .then(function(data) { - localStorageService.set('JWT', data.jwt); + LocalStorage.storeJWT(data.jwt); $rootScope.username = username; resolve(); }, function() { @@ -296,10 +263,10 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) }); }, logout: function() { - localStorageService.remove('JWT'); + LocalStorage.deleteJWT(); }, isAuthenticated: function() { - var jwt = localStorageService.get('JWT'); + var jwt = LocalStorage.getJWT(); return jwt && !jwtHelper.isTokenExpired(jwt); } }; @@ -359,7 +326,69 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) setActiveEndpoint: { method: 'POST', params: { id: '@id', action: 'active' } } }); }]) - .factory('EndpointService', ['$q', '$timeout', 'Endpoints', 'FileUploadService', function EndpointServiceFactory($q, $timeout, Endpoints, FileUploadService) { + .factory('LocalStorage', ['localStorageService', function LocalStorageFactory(localStorageService) { + 'use strict'; + return { + storeEndpointState: function(state) { + localStorageService.set('ENDPOINT_STATE', state); + }, + getEndpointState: function() { + return localStorageService.get('ENDPOINT_STATE'); + }, + storeJWT: function(jwt) { + localStorageService.set('JWT', jwt); + }, + getJWT: function() { + return localStorageService.get('JWT'); + }, + deleteJWT: function() { + localStorageService.remove('JWT'); + } + }; + }]) + .factory('StateManager', ['$q', 'Info', 'InfoHelper', 'Version', 'LocalStorage', function StateManagerFactory($q, Info, InfoHelper, Version, LocalStorage) { + 'use strict'; + + var state = { + loading: true, + application: {}, + endpoint: {} + }; + + return { + init: function() { + var endpointState = LocalStorage.getEndpointState(); + if (endpointState) { + state.endpoint = endpointState; + } + state.loading = false; + }, + updateEndpointState: function(loading) { + var deferred = $q.defer(); + if (loading) { + state.loading = true; + } + $q.all([Info.get({}).$promise, Version.get({}).$promise]) + .then(function success(data) { + var endpointMode = InfoHelper.determineEndpointMode(data[0]); + var endpointAPIVersion = parseFloat(data[1].ApiVersion); + state.endpoint.mode = endpointMode; + state.endpoint.apiVersion = endpointAPIVersion; + LocalStorage.storeEndpointState(state.endpoint); + state.loading = false; + deferred.resolve(); + }, function error(err) { + state.loading = false; + deferred.reject({msg: 'Unable to connect to the Docker endpoint', err: err}); + }); + return deferred.promise; + }, + getState: function() { + return state; + } + }; + }]) + .factory('EndpointService', ['$q', 'Endpoints', 'FileUploadService', function EndpointServiceFactory($q, Endpoints, FileUploadService) { 'use strict'; return { getActive: function() { diff --git a/assets/css/app.css b/assets/css/app.css index 9a870e85f..cce7e440b 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -213,7 +213,6 @@ input[type="radio"] { } .page-wrapper { - margin-top: 25px; height: 100%; width: 100%; display: flex; diff --git a/gruntfile.js b/gruntfile.js index 6e86f4404..bbec6da3a 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -398,14 +398,14 @@ module.exports = function (grunt) { command: [ 'docker stop portainer', 'docker rm portainer', - 'docker run --privileged -d -p 9000:9000 -v /tmp/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer -d /data' + 'docker run --privileged -d -p 9000:9000 -v /tmp/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer' ].join(';') }, runSwarm: { 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:2375 -d /data' + 'docker run -d -p 9000:9000 -v /tmp/portainer:/data --name portainer portainer -H tcp://10.0.7.10:2375' ].join(';') }, runSwarmLocal: { @@ -419,7 +419,7 @@ module.exports = function (grunt) { command: [ 'docker stop portainer', 'docker rm portainer', - 'docker run -d -p 9000:9000 -v /tmp/portainer:/data -v /tmp/docker-ssl:/certs --name portainer portainer -H tcp://10.0.7.10:2376 -d /data --tlsverify' + 'docker run -d -p 9000:9000 -v /tmp/portainer:/data -v /tmp/docker-ssl:/certs --name portainer portainer -H tcp://10.0.7.10:2376 --tlsverify' ].join(';') }, cleanImages: { diff --git a/index.html b/index.html index 0b0bd1a1d..06ca19d56 100644 --- a/index.html +++ b/index.html @@ -29,15 +29,36 @@ -
+
-
+
+
+ +
+
+ +
+ + +
+ + +
+ Connecting to the Docker enpoint... + +
+ +
+
+ +
+ -
+