From 35d5d7596637381b79ff573cf86f3d9791dfea5d Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 9 Jan 2017 21:24:17 +1300 Subject: [PATCH 01/25] fix(api): update default value for data directory and TLS certs on Windows (#482) --- api/cli/defaults_windows.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/cli/defaults_windows.go b/api/cli/defaults_windows.go index 3a4106c74..9e4be3a80 100644 --- a/api/cli/defaults_windows.go +++ b/api/cli/defaults_windows.go @@ -2,11 +2,11 @@ package cli const ( defaultBindAddress = ":9000" - defaultDataDirectory = "C:\\data" + defaultDataDirectory = "C:\\ProgramData\\Portainer" defaultAssetsDirectory = "." defaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates.json" defaultTLSVerify = "false" - defaultTLSCACertPath = "C:\\certs\\ca.pem" - defaultTLSCertPath = "C:\\certs\\cert.pem" - defaultTLSKeyPath = "C:\\certs\\key.pem" + defaultTLSCACertPath = "C:\\ProgramData\\Portainer\\certs\\ca.pem" + defaultTLSCertPath = "C:\\ProgramData\\Portainer\\certs\\cert.pem" + defaultTLSKeyPath = "C:\\ProgramData\\Portainer\\certs\\key.pem" ) From 2bdc9322de18497ce3f4f15ed71a8e0ee2d8bd0e Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 9 Jan 2017 21:50:19 +1300 Subject: [PATCH 02/25] style(containers): update header text for published ports (#483) --- app/components/containers/containers.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/containers/containers.html b/app/components/containers/containers.html index 367763841..1aaa749f8 100644 --- a/app/components/containers/containers.html +++ b/app/components/containers/containers.html @@ -77,7 +77,7 @@ - Exposed Ports + Published Ports From 27e584fc143b136a5cdcce4e75d9be371013021a Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Thu, 12 Jan 2017 18:17:28 +1300 Subject: [PATCH 03/25] fix(api): check if admin user already exists when calling the /users/admin/init endpoint (#494) --- api/errors.go | 3 ++- api/http/user_handler.go | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/api/errors.go b/api/errors.go index 8fcd44758..48ea6ab75 100644 --- a/api/errors.go +++ b/api/errors.go @@ -7,7 +7,8 @@ const ( // User errors. const ( - ErrUserNotFound = Error("User not found") + ErrUserNotFound = Error("User not found") + ErrAdminAlreadyInitialized = Error("Admin user already initialized") ) // Endpoint errors. diff --git a/api/http/user_handler.go b/api/http/user_handler.go index b47cda7ca..38243b0a6 100644 --- a/api/http/user_handler.go +++ b/api/http/user_handler.go @@ -227,18 +227,28 @@ func (handler *UserHandler) handlePostAdminInit(w http.ResponseWriter, r *http.R return } - user := &portainer.User{ - Username: "admin", - } - user.Password, err = handler.CryptoService.Hash(req.Password) - if err != nil { - Error(w, portainer.ErrCryptoHashFailure, http.StatusBadRequest, handler.Logger) + user, err := handler.UserService.User("admin") + if err == portainer.ErrUserNotFound { + user := &portainer.User{ + Username: "admin", + } + user.Password, err = handler.CryptoService.Hash(req.Password) + if err != nil { + Error(w, portainer.ErrCryptoHashFailure, http.StatusBadRequest, handler.Logger) + return + } + + err = handler.UserService.UpdateUser(user) + if err != nil { + Error(w, err, http.StatusInternalServerError, handler.Logger) + return + } + } else if err != nil { + Error(w, err, http.StatusInternalServerError, handler.Logger) return } - - err = handler.UserService.UpdateUser(user) - if err != nil { - Error(w, err, http.StatusInternalServerError, handler.Logger) + if user != nil { + Error(w, portainer.ErrAdminAlreadyInitialized, http.StatusForbidden, handler.Logger) return } } From cd260511442429621c38c2932c9d0077ce3cc612 Mon Sep 17 00:00:00 2001 From: Kilhog Date: Thu, 12 Jan 2017 06:44:53 +0100 Subject: [PATCH 04/25] #476 fix(UX): Rename 'local' endpoint doesn't overwrite "unix://" (#477) * #476 fix(UX): Rename 'local' endpoint doesn't overwrite "unix://" * #477 fix(PR): Rename 'TYPE' in 'type' --- app/components/endpoint/endpointController.js | 3 ++- app/shared/services.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/components/endpoint/endpointController.js b/app/components/endpoint/endpointController.js index 1d8d4cedc..0c254b3df 100644 --- a/app/components/endpoint/endpointController.js +++ b/app/components/endpoint/endpointController.js @@ -19,7 +19,8 @@ function ($scope, $state, $stateParams, $filter, EndpointService, Messages) { var TLSCACert = $scope.formValues.TLSCACert !== $scope.endpoint.TLSCACert ? $scope.formValues.TLSCACert : null; var TLSCert = $scope.formValues.TLSCert !== $scope.endpoint.TLSCert ? $scope.formValues.TLSCert : null; var TLSKey = $scope.formValues.TLSKey !== $scope.endpoint.TLSKey ? $scope.formValues.TLSKey : null; - EndpointService.updateEndpoint(ID, name, URL, TLS, TLSCACert, TLSCert, TLSKey).then(function success(data) { + var type = $scope.endpointType; + EndpointService.updateEndpoint(ID, name, URL, TLS, TLSCACert, TLSCert, TLSKey, type).then(function success(data) { Messages.send("Endpoint updated", $scope.endpoint.Name); $state.go('endpoints'); }, function error(err) { diff --git a/app/shared/services.js b/app/shared/services.js index 1aba157aa..6cb3a27dd 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -374,11 +374,11 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) endpoints: function() { return Endpoints.query({}).$promise; }, - updateEndpoint: function(ID, name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile) { + updateEndpoint: function(ID, name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, type) { var endpoint = { id: ID, Name: name, - URL: "tcp://" + URL, + URL: type === 'local' ? ("unix://" + URL) : ("tcp://" + URL), TLS: TLS }; var deferred = $q.defer(); From e4d98082dc53cac1ee47bfcf94c35c5e56b4b840 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Sat, 14 Jan 2017 14:22:39 +1300 Subject: [PATCH 05/25] fix(api): disable data directory creation (#495) * fix(api): disable data directory creation * feat(dockerhub): update volume instruction value for Windows Dockerfiles --- api/file/file.go | 12 +++++++----- build/windows/microsoftservercore/Dockerfile | 2 +- build/windows/nanoserver/Dockerfile | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/api/file/file.go b/api/file/file.go index c9ad71a4a..63837d525 100644 --- a/api/file/file.go +++ b/api/file/file.go @@ -34,12 +34,14 @@ func NewService(dataStorePath, fileStorePath string) (*Service, error) { fileStorePath: path.Join(dataStorePath, fileStorePath), } - err := createDirectoryIfNotExist(dataStorePath, 0755) - if err != nil { - return nil, err - } + // Checking if a mount directory exists is broken with Go on Windows. + // This will need to be reviewed after the issue has been fixed in Go. + // err := createDirectoryIfNotExist(dataStorePath, 0755) + // if err != nil { + // return nil, err + // } - err = service.createDirectoryInStoreIfNotExist(TLSStorePath) + err := service.createDirectoryInStoreIfNotExist(TLSStorePath) if err != nil { return nil, err } diff --git a/build/windows/microsoftservercore/Dockerfile b/build/windows/microsoftservercore/Dockerfile index dfcd8b15a..5d67a2899 100644 --- a/build/windows/microsoftservercore/Dockerfile +++ b/build/windows/microsoftservercore/Dockerfile @@ -2,7 +2,7 @@ FROM microsoft/windowsservercore COPY dist / -VOLUME C:\\data +VOLUME C:\\ProgramData\\Portainer WORKDIR / diff --git a/build/windows/nanoserver/Dockerfile b/build/windows/nanoserver/Dockerfile index ae8a25a39..7855cba8e 100644 --- a/build/windows/nanoserver/Dockerfile +++ b/build/windows/nanoserver/Dockerfile @@ -2,7 +2,7 @@ FROM microsoft/nanoserver COPY dist / -VOLUME C:\\data +VOLUME C:\\ProgramData\\Portainer WORKDIR / From 3a6e9d2fbee207206d2e59b6590babbce08986e8 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Sat, 21 Jan 2017 11:17:51 +1300 Subject: [PATCH 06/25] fix(api): fix an issue introduced by the latest version of package gorilla/mux (#520) --- api/http/upload_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/http/upload_handler.go b/api/http/upload_handler.go index 24b992392..a0d53c36b 100644 --- a/api/http/upload_handler.go +++ b/api/http/upload_handler.go @@ -26,7 +26,7 @@ func NewUploadHandler(middleWareService *middleWareService) *UploadHandler { Logger: log.New(os.Stderr, "", log.LstdFlags), middleWareService: middleWareService, } - h.Handle("/upload/tls/{endpointID}/{certificate:(ca|cert|key)}", middleWareService.addMiddleWares(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h.Handle("/upload/tls/{endpointID}/{certificate:(?:ca|cert|key)}", middleWareService.addMiddleWares(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h.handlePostUploadTLS(w, r) }))) return h From 7d78871eeec90e367f3e486edbd1b68898e1a313 Mon Sep 17 00:00:00 2001 From: lpfeup Date: Sat, 21 Jan 2017 05:01:32 +0000 Subject: [PATCH 07/25] #446 fix(container-stats): fix issue in stats view with empty network data (#502) --- app/components/stats/statsController.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/components/stats/statsController.js b/app/components/stats/statsController.js index 88e6e1f34..eed96ef18 100644 --- a/app/components/stats/statsController.js +++ b/app/components/stats/statsController.js @@ -162,16 +162,18 @@ function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateP $scope.networkName = Object.keys(data.networks)[0]; data.network = data.networks[$scope.networkName]; } - var rxBytes = 0, txBytes = 0; - if (lastRxBytes !== 0 || lastTxBytes !== 0) { - // These will be zero on first call, ignore to prevent large graph spike - rxBytes = data.network.rx_bytes - lastRxBytes; - txBytes = data.network.tx_bytes - lastTxBytes; + if(data.network) { + var rxBytes = 0, txBytes = 0; + if (lastRxBytes !== 0 || lastTxBytes !== 0) { + // These will be zero on first call, ignore to prevent large graph spike + rxBytes = data.network.rx_bytes - lastRxBytes; + txBytes = data.network.tx_bytes - lastTxBytes; + } + lastRxBytes = data.network.rx_bytes; + lastTxBytes = data.network.tx_bytes; + networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString()); + networkChart.removeData(); } - lastRxBytes = data.network.rx_bytes; - lastTxBytes = data.network.tx_bytes; - networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString()); - networkChart.removeData(); } function calculateCPUPercent(stats) { From 579241db921a595ac46df4de43a1a03ed759f7ae Mon Sep 17 00:00:00 2001 From: lpfeup Date: Sat, 21 Jan 2017 05:04:28 +0000 Subject: [PATCH 08/25] #503 fix(container-stats): fix container stats timer not being properly canceled. (#504) --- app/components/stats/statsController.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/components/stats/statsController.js b/app/components/stats/statsController.js index eed96ef18..58064f980 100644 --- a/app/components/stats/statsController.js +++ b/app/components/stats/statsController.js @@ -114,6 +114,12 @@ function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateP }); $scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend()); + function setUpdateStatsTimeout() { + if(!destroyed) { + timeout = $timeout(updateStats, 5000); + } + } + function updateStats() { Container.stats({id: $stateParams.id}, function (d) { var arr = Object.keys(d).map(function (key) { @@ -129,15 +135,17 @@ function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateP updateCpuChart(d); updateMemoryChart(d); updateNetworkChart(d); - timeout = $timeout(updateStats, 5000); + setUpdateStatsTimeout(); }, function () { Messages.error('Unable to retrieve stats', {}, 'Is this container running?'); - timeout = $timeout(updateStats, 5000); + setUpdateStatsTimeout(); }); } + var destroyed = false; var timeout; $scope.$on('$destroy', function () { + destroyed = true; $timeout.cancel(timeout); }); From 7ebe4af77d46fdfc59d33df31471762e6819cbd0 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Sun, 22 Jan 2017 14:42:12 +1300 Subject: [PATCH 09/25] fix(images): fix an issue when deleting images with multiple tags (#526) --- app/shared/responseHandlers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/shared/responseHandlers.js b/app/shared/responseHandlers.js index 16bdc248a..8b60fea6f 100644 --- a/app/shared/responseHandlers.js +++ b/app/shared/responseHandlers.js @@ -56,7 +56,7 @@ function deleteImageHandler(data) { response.push({message: data}); } // A JSON object is returned on failure (Docker = 1.12) - else if (!isJSONArray) { + else if (!isJSONArray(data)) { var json = angular.fromJson(data); response.push(json); } From fa4ec04c470496afec56bb44ed8d73b9cdea5cb0 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 12:14:34 +1300 Subject: [PATCH 10/25] 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... + +
+ +
+
+ +
+ -
+
From 70f139514ff01ac990c95da923ebf9e698ee88c2 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 16:06:51 +1300 Subject: [PATCH 11/25] =?UTF-8?q?fix(network-details):=20add=20a=20fallbac?= =?UTF-8?q?k=20for=20listing=20containers=20when=20APIV=E2=80=A6=20(#531)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/network/networkController.js | 73 ++++++++++++++------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/app/components/network/networkController.js b/app/components/network/networkController.js index 656a6b062..77287618d 100644 --- a/app/components/network/networkController.js +++ b/app/components/network/networkController.js @@ -1,6 +1,6 @@ angular.module('network', []) -.controller('NetworkController', ['$scope', '$state', '$stateParams', 'Network', 'Container', 'ContainerHelper', 'Messages', -function ($scope, $state, $stateParams, Network, Container, ContainerHelper, Messages) { +.controller('NetworkController', ['$scope', '$state', '$stateParams', '$filter', 'Config', 'Network', 'Container', 'ContainerHelper', 'Messages', +function ($scope, $state, $stateParams, $filter, Config, Network, Container, ContainerHelper, Messages) { $scope.removeNetwork = function removeNetwork(networkId) { $('#loadingViewSpinner').show(); @@ -38,34 +38,63 @@ function ($scope, $state, $stateParams, Network, Container, ContainerHelper, Mes function getNetwork() { $('#loadingViewSpinner').show(); - Network.get({id: $stateParams.id}, function (d) { - $scope.network = d; - getContainersInNetwork(d); + Network.get({id: $stateParams.id}, function success(data) { + $scope.network = data; + getContainersInNetwork(data); + }, function error(err) { $('#loadingViewSpinner').hide(); - }, function (e) { - $('#loadingViewSpinner').hide(); - Messages.error("Failure", e, "Unable to retrieve network info"); + Messages.error("Failure", err, "Unable to retrieve network info"); }); } + function filterContainersInNetwork(network, containers) { + if ($scope.containersToHideLabels) { + containers = ContainerHelper.hideContainers(containers, $scope.containersToHideLabels); + } + var containersInNetwork = []; + containers.forEach(function(container) { + var containerInNetwork = network.Containers[container.Id]; + containerInNetwork.Id = container.Id; + // Name is not available in Docker 1.9 + if (!containerInNetwork.Name) { + containerInNetwork.Name = $filter('trimcontainername')(container.Names[0]); + } + containersInNetwork.push(containerInNetwork); + }); + $scope.containersInNetwork = containersInNetwork; + } + function getContainersInNetwork(network) { if (network.Containers) { - Container.query({ - filters: {network: [$stateParams.id]} - }, function (containersInNetworkResult) { - if ($scope.containersToHideLabels) { - containersInNetworkResult = ContainerHelper.hideContainers(containersInNetworkResult, $scope.containersToHideLabels); - } - var containersInNetwork = []; - containersInNetworkResult.forEach(function(container) { - var containerInNetwork = network.Containers[container.Id]; - containerInNetwork.Id = container.Id; - containersInNetwork.push(containerInNetwork); + if ($scope.applicationState.endpoint.apiVersion < 1.24) { + Container.query({}, function success(data) { + var containersInNetwork = data.filter(function filter(container) { + if (container.HostConfig.NetworkMode === network.Name) { + return container; + } + }); + filterContainersInNetwork(network, containersInNetwork); + $('#loadingViewSpinner').hide(); + }, function error(err) { + $('#loadingViewSpinner').hide(); + Messages.error("Failure", err, "Unable to retrieve containers in network"); }); - $scope.containersInNetwork = containersInNetwork; - }); + } else { + Container.query({ + filters: {network: [$stateParams.id]} + }, function success(data) { + filterContainersInNetwork(network, data); + $('#loadingViewSpinner').hide(); + }, function error(err) { + $('#loadingViewSpinner').hide(); + Messages.error("Failure", err, "Unable to retrieve containers in network"); + }); + } } } - getNetwork(); + Config.$promise.then(function (c) { + $scope.containersToHideLabels = c.hiddenLabels; + getNetwork(); + }); }]); From 8cf6d34362537d44c0eafc7abd95b23621c62ae0 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 16:10:12 +1300 Subject: [PATCH 12/25] style(container-creation): remove useless labels section (#532) --- app/components/createContainer/createcontainer.html | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/components/createContainer/createcontainer.html b/app/components/createContainer/createcontainer.html index 5be50586a..9a27c741a 100644 --- a/app/components/createContainer/createcontainer.html +++ b/app/components/createContainer/createcontainer.html @@ -95,16 +95,6 @@
- -
- -
- - label - -
-
- From 980f65a08aadf558a4038cc708102ce03ab28218 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 16:29:49 +1300 Subject: [PATCH 13/25] feat(api): initializes the endpoint with an empty slice instead of a pointer --- api/bolt/endpoint_service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/bolt/endpoint_service.go b/api/bolt/endpoint_service.go index 16bd36984..f8bbdd795 100644 --- a/api/bolt/endpoint_service.go +++ b/api/bolt/endpoint_service.go @@ -44,7 +44,7 @@ func (service *EndpointService) Endpoint(ID portainer.EndpointID) (*portainer.En // Endpoints return an array containing all the endpoints. func (service *EndpointService) Endpoints() ([]portainer.Endpoint, error) { - var endpoints []portainer.Endpoint + var endpoints = make([]portainer.Endpoint, 0) err := service.store.db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(endpointBucketName)) From 7e0b0a05de2052d1a480595018d23878e20e6f60 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 23 Jan 2017 17:04:34 +1300 Subject: [PATCH 14/25] feat(authentication): clean the state and the browser local storage on logout --- app/shared/services.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/shared/services.js b/app/shared/services.js index e1c671e85..b7c74196d 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -240,7 +240,7 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) initAdminUser: { method: 'POST', params: { username: 'admin', action: 'init' } } }); }]) - .factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'LocalStorage', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, LocalStorage) { + .factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'LocalStorage', 'StateManager', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, LocalStorage, StateManager) { 'use strict'; return { init: function() { @@ -263,7 +263,8 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) }); }, logout: function() { - LocalStorage.deleteJWT(); + StateManager.clean(); + LocalStorage.clean(); }, isAuthenticated: function() { var jwt = LocalStorage.getJWT(); @@ -343,6 +344,9 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) }, deleteJWT: function() { localStorageService.remove('JWT'); + }, + clean: function() { + localStorageService.clearAll(); } }; }]) @@ -363,6 +367,9 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize']) } state.loading = false; }, + clean: function() { + state.endpoint = {}; + }, updateEndpointState: function(loading) { var deferred = $q.defer(); if (loading) { From 70933d1056237965ca6bd38e2dc453224af7dbf5 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 24 Jan 2017 09:39:13 +1300 Subject: [PATCH 15/25] style(sidebar): add active class on Docker section (#534) --- app/components/sidebar/sidebar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/sidebar/sidebar.html b/app/components/sidebar/sidebar.html index 044285e71..d8e97c81b 100644 --- a/app/components/sidebar/sidebar.html +++ b/app/components/sidebar/sidebar.html @@ -44,7 +44,7 @@ Swarm