feat(home): add the ability to refresh endpoint information (#2080)

* feat(home): add the ability to refresh endpoint information

* style(home): update refresh confirmation message
pull/2073/head
Anthony Lapenna 2018-07-25 21:52:17 +02:00 committed by GitHub
parent 52f71b0813
commit ab77f149fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 8 deletions

View File

@ -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)
}

View File

@ -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}",

View File

@ -11,6 +11,8 @@ angular.module('portainer.app').component('endpointList', {
titleText: '@',
titleIcon: '@',
endpoints: '<',
dashboardAction: '<'
dashboardAction: '<',
snapshotAction: '<',
showSnapshotAction: '<'
}
});

View File

@ -8,6 +8,12 @@
</div>
</div>
<div class="actionBar" ng-if="$ctrl.showSnapshotAction">
<button type="button" class="btn btn-sm btn-primary" ng-click="$ctrl.snapshotAction()">
<i class="fa fa-sync space-right" aria-hidden="true"></i>Refresh
</button>
</div>
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input type="text" class="searchInput" ng-model="$ctrl.state.textFilter" placeholder="Search by name, group, tag..." auto-focus>

View File

@ -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' }}
});
}]);

View File

@ -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();

View File

@ -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

View File

@ -30,6 +30,8 @@
title-text="Endpoints" title-icon="fa-plug"
endpoints="endpoints"
dashboard-action="goToDashboard"
show-snapshot-action="isAdmin"
snapshot-action="triggerSnapshot"
></endpoint-list>
</div>
</div>

View File

@ -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() {