diff --git a/api/http/handler/endpoints/endpoint_snapshot.go b/api/http/handler/endpoints/endpoint_snapshot.go
new file mode 100644
index 000000000..51a18293e
--- /dev/null
+++ b/api/http/handler/endpoints/endpoint_snapshot.go
@@ -0,0 +1,42 @@
+package endpoints
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/portainer/portainer"
+ httperror "github.com/portainer/portainer/http/error"
+ "github.com/portainer/portainer/http/response"
+)
+
+// POST request on /api/endpoints/snapshot
+func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+ endpoints, err := handler.EndpointService.Endpoints()
+ if err != nil {
+ return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
+ }
+
+ for _, endpoint := range endpoints {
+ if endpoint.Type == portainer.AzureEnvironment {
+ continue
+ }
+
+ snapshot, err := handler.Snapshotter.CreateSnapshot(&endpoint)
+ endpoint.Status = portainer.EndpointStatusUp
+ if err != nil {
+ log.Printf("http error: endpoint snapshot error (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
+ endpoint.Status = portainer.EndpointStatusDown
+ }
+
+ if snapshot != nil {
+ endpoint.Snapshots = []portainer.Snapshot{*snapshot}
+ }
+
+ err = handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
+ if err != nil {
+ return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
+ }
+ }
+
+ return response.Empty(w)
+}
diff --git a/api/http/handler/endpoints/handler.go b/api/http/handler/endpoints/handler.go
index 1c6e79cc4..fc1bc1e6c 100644
--- a/api/http/handler/endpoints/handler.go
+++ b/api/http/handler/endpoints/handler.go
@@ -43,6 +43,8 @@ func NewHandler(bouncer *security.RequestBouncer, authorizeEndpointManagement bo
h.Handle("/endpoints",
bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointCreate))).Methods(http.MethodPost)
+ h.Handle("/endpoints/snapshot",
+ bouncer.AdministratorAccess(httperror.LoggerHandler(h.endpointSnapshot))).Methods(http.MethodPost)
h.Handle("/endpoints",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointList))).Methods(http.MethodGet)
h.Handle("/endpoints/{id}",
diff --git a/app/portainer/components/endpoint-list/endpoint-list.js b/app/portainer/components/endpoint-list/endpoint-list.js
index 9d551591f..319d6ef08 100644
--- a/app/portainer/components/endpoint-list/endpoint-list.js
+++ b/app/portainer/components/endpoint-list/endpoint-list.js
@@ -11,6 +11,8 @@ angular.module('portainer.app').component('endpointList', {
titleText: '@',
titleIcon: '@',
endpoints: '<',
- dashboardAction: '<'
+ dashboardAction: '<',
+ snapshotAction: '<',
+ showSnapshotAction: '<'
}
});
diff --git a/app/portainer/components/endpoint-list/endpointList.html b/app/portainer/components/endpoint-list/endpointList.html
index f6b904408..9110703f9 100644
--- a/app/portainer/components/endpoint-list/endpointList.html
+++ b/app/portainer/components/endpoint-list/endpointList.html
@@ -8,6 +8,12 @@
+
+
+
+
diff --git a/app/portainer/rest/endpoint.js b/app/portainer/rest/endpoint.js
index 5969212a0..455a27448 100644
--- a/app/portainer/rest/endpoint.js
+++ b/app/portainer/rest/endpoint.js
@@ -6,6 +6,7 @@ angular.module('portainer.app')
get: { method: 'GET', params: { id: '@id' } },
update: { method: 'PUT', params: { id: '@id' } },
updateAccess: { method: 'PUT', params: { id: '@id', action: 'access' } },
- remove: { method: 'DELETE', params: { id: '@id'} }
+ remove: { method: 'DELETE', params: { id: '@id'} },
+ snapshot: { method: 'POST', params: { id: 'snapshot' }}
});
}]);
diff --git a/app/portainer/services/api/endpointService.js b/app/portainer/services/api/endpointService.js
index 003674240..aa3d57bb6 100644
--- a/app/portainer/services/api/endpointService.js
+++ b/app/portainer/services/api/endpointService.js
@@ -12,6 +12,10 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) {
return Endpoints.query({}).$promise;
};
+ service.snapshot = function() {
+ return Endpoints.snapshot({}, {}).$promise;
+ };
+
service.endpointsByGroup = function(groupId) {
var deferred = $q.defer();
diff --git a/app/portainer/services/modalService.js b/app/portainer/services/modalService.js
index 0adb6fc30..74260e1fc 100644
--- a/app/portainer/services/modalService.js
+++ b/app/portainer/services/modalService.js
@@ -142,14 +142,14 @@ angular.module('portainer.app')
}, false);
};
- service.confirmExperimentalFeature = function(callback) {
+ service.confirmEndpointSnapshot = function(callback) {
service.confirm({
- title: 'Experimental feature',
- message: 'This feature is currently experimental, please use with caution.',
+ title: 'Are you sure?',
+ message: 'Triggering a manual refresh will poll each endpoint to retrieve its information, this may take a few moments.',
buttons: {
confirm: {
label: 'Continue',
- className: 'btn-danger'
+ className: 'btn-primary'
}
},
callback: callback
diff --git a/app/portainer/views/home/home.html b/app/portainer/views/home/home.html
index a1396f7ab..d7190ef63 100644
--- a/app/portainer/views/home/home.html
+++ b/app/portainer/views/home/home.html
@@ -30,6 +30,8 @@
title-text="Endpoints" title-icon="fa-plug"
endpoints="endpoints"
dashboard-action="goToDashboard"
+ show-snapshot-action="isAdmin"
+ snapshot-action="triggerSnapshot"
>
diff --git a/app/portainer/views/home/homeController.js b/app/portainer/views/home/homeController.js
index f9d3bfe5f..88bee800b 100644
--- a/app/portainer/views/home/homeController.js
+++ b/app/portainer/views/home/homeController.js
@@ -1,6 +1,6 @@
angular.module('portainer.app')
-.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager',
-function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager) {
+.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService',
+function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService) {
$scope.goToDashboard = function(endpoint) {
EndpointProvider.setEndpointID(endpoint.Id);
@@ -12,6 +12,24 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
}
};
+ function triggerSnapshot() {
+ EndpointService.snapshot()
+ .then(function success(data) {
+ Notifications.success('Success', 'Endpoints updated');
+ $state.reload();
+ })
+ .catch(function error(err) {
+ Notifications.error('Failure', err, 'An error occured during endpoint snapshot');
+ });
+ }
+
+ $scope.triggerSnapshot = function() {
+ ModalService.confirmEndpointSnapshot(function (result) {
+ if(!result) { return; }
+ triggerSnapshot();
+ });
+ };
+
function switchToAzureEndpoint(endpoint) {
StateManager.updateEndpointState(endpoint.Name, endpoint.Type, [])
.then(function success() {