feat(service): add force update in service list/detail (#1536)

pull/1558/head
Miguel A. C 2018-01-08 22:06:56 +01:00 committed by Anthony Lapenna
parent 35892525ff
commit 0e28aebd65
7 changed files with 97 additions and 15 deletions

View File

@ -38,6 +38,7 @@
<td>ID</td> <td>ID</td>
<td> <td>
{{ service.Id }} {{ service.Id }}
<button class="btn btn-xs btn-primary" ng-click="forceUpdateService(service)"><i class="fa fa-refresh space-right" aria-hidden="true" ng-disabled="isUpdating" ng-if="applicationState.endpoint.apiVersion >= 1.25"></i>Force update this service</button>
<button class="btn btn-xs btn-danger" ng-click="removeService()"><i class="fa fa-trash space-right" aria-hidden="true" ng-disabled="isUpdating"></i>Delete this service</button> <button class="btn btn-xs btn-danger" ng-click="removeService()"><i class="fa fa-trash space-right" aria-hidden="true" ng-disabled="isUpdating"></i>Delete this service</button>
</td> </td>
</tr> </tr>

View File

@ -168,7 +168,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
} }
}; };
$scope.addLogDriverOpt = function addLogDriverOpt(service) { $scope.addLogDriverOpt = function addLogDriverOpt(service) {
service.LogDriverOpts.push({ key: '', value: '', originalValue: '' }); service.LogDriverOpts.push({ key: '', value: '', originalValue: '' });
updateServiceArray(service, 'LogDriverOpts', service.LogDriverOpts); updateServiceArray(service, 'LogDriverOpts', service.LogDriverOpts);
}; };
@ -182,16 +182,16 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
if (variable.value !== variable.originalValue || variable.key !== variable.originalKey) { if (variable.value !== variable.originalValue || variable.key !== variable.originalKey) {
updateServiceArray(service, 'LogDriverOpts', service.LogDriverOpts); updateServiceArray(service, 'LogDriverOpts', service.LogDriverOpts);
} }
}; };
$scope.updateLogDriverName = function updateLogDriverName(service) { $scope.updateLogDriverName = function updateLogDriverName(service) {
updateServiceArray(service, 'LogDriverName', service.LogDriverName); updateServiceArray(service, 'LogDriverName', service.LogDriverName);
}; };
$scope.addHostsEntry = function (service) { $scope.addHostsEntry = function (service) {
if (!service.Hosts) { if (!service.Hosts) {
service.Hosts = []; service.Hosts = [];
} }
service.Hosts.push({ hostname: '', ip: '' }); service.Hosts.push({ hostname: '', ip: '' });
}; };
$scope.removeHostsEntry = function(service, index) { $scope.removeHostsEntry = function(service, index) {
var removedElement = service.Hosts.splice(index, 1); var removedElement = service.Hosts.splice(index, 1);
@ -199,9 +199,9 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
updateServiceArray(service, 'Hosts', service.Hosts); updateServiceArray(service, 'Hosts', service.Hosts);
} }
}; };
$scope.updateHostsEntry = function(service, entry) { $scope.updateHostsEntry = function(service, entry) {
updateServiceArray(service, 'Hosts', service.Hosts); updateServiceArray(service, 'Hosts', service.Hosts);
}; };
$scope.cancelChanges = function cancelChanges(service, keys) { $scope.cancelChanges = function cancelChanges(service, keys) {
if (keys) { // clean out the keys only from the list of modified keys if (keys) { // clean out the keys only from the list of modified keys
@ -239,7 +239,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets ? service.ServiceSecrets.map(SecretHelper.secretConfig) : []; config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets ? service.ServiceSecrets.map(SecretHelper.secretConfig) : [];
config.TaskTemplate.ContainerSpec.Configs = service.ServiceConfigs ? service.ServiceConfigs.map(ConfigHelper.configConfig) : []; config.TaskTemplate.ContainerSpec.Configs = service.ServiceConfigs ? service.ServiceConfigs.map(ConfigHelper.configConfig) : [];
config.TaskTemplate.ContainerSpec.Hosts = service.Hosts ? ServiceHelper.translateHostnameIPToHostsEntries(service.Hosts) : []; config.TaskTemplate.ContainerSpec.Hosts = service.Hosts ? ServiceHelper.translateHostnameIPToHostsEntries(service.Hosts) : [];
if (service.Mode === 'replicated') { if (service.Mode === 'replicated') {
config.Mode.Replicated.Replicas = service.Replicas; config.Mode.Replicated.Replicas = service.Replicas;
} }
@ -279,17 +279,17 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
MaxAttempts: service.RestartMaxAttempts, MaxAttempts: service.RestartMaxAttempts,
Window: ServiceHelper.translateHumanDurationToNanos(service.RestartWindow) || 0 Window: ServiceHelper.translateHumanDurationToNanos(service.RestartWindow) || 0
}; };
config.TaskTemplate.LogDriver = null; config.TaskTemplate.LogDriver = null;
if (service.LogDriverName) { if (service.LogDriverName) {
config.TaskTemplate.LogDriver = { Name: service.LogDriverName }; config.TaskTemplate.LogDriver = { Name: service.LogDriverName };
if (service.LogDriverName !== 'none') { if (service.LogDriverName !== 'none') {
var logOpts = ServiceHelper.translateKeyValueToLogDriverOpts(service.LogDriverOpts); var logOpts = ServiceHelper.translateKeyValueToLogDriverOpts(service.LogDriverOpts);
if (Object.keys(logOpts).length !== 0 && logOpts.constructor === Object) { if (Object.keys(logOpts).length !== 0 && logOpts.constructor === Object) {
config.TaskTemplate.LogDriver.Options = logOpts; config.TaskTemplate.LogDriver.Options = logOpts;
} }
} }
} }
if (service.Ports) { if (service.Ports) {
service.Ports.forEach(function (binding) { service.Ports.forEach(function (binding) {
@ -338,6 +338,32 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
}); });
} }
$scope.forceUpdateService = function(service) {
ModalService.confirmServiceForceUpdate(
'Do you want to force update this service? All the tasks associated to the selected service(s) will be recreated.',
function onConfirm(confirmed) {
if(!confirmed) { return; }
forceUpdateService(service);
}
);
};
function forceUpdateService(service) {
var config = ServiceHelper.serviceToConfig(service.Model);
// As explained in https://github.com/docker/swarmkit/issues/2364 ForceUpdate can accept a random
// value or an increment of the counter value to force an update.
config.TaskTemplate.ForceUpdate++;
ServiceService.update(service, config)
.then(function success(data) {
Notifications.success('Service successfully updated', service.Name);
$scope.cancelChanges({});
initView();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to force update service', service.Name);
});
}
function translateServiceArrays(service) { function translateServiceArrays(service) {
service.ServiceSecrets = service.Secrets ? service.Secrets.map(SecretHelper.flattenSecret) : []; service.ServiceSecrets = service.Secrets ? service.Secrets.map(SecretHelper.flattenSecret) : [];
service.ServiceConfigs = service.Configs ? service.Configs.map(ConfigHelper.flattenConfig) : []; service.ServiceConfigs = service.Configs ? service.Configs.map(ConfigHelper.flattenConfig) : [];
@ -365,7 +391,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
} }
function initView() { function initView() {
var apiVersion = $scope.applicationState.endpoint.apiVersion; var apiVersion = $scope.applicationState.endpoint.apiVersion;
ServiceService.service($transition$.params().id) ServiceService.service($transition$.params().id)
.then(function success(data) { .then(function success(data) {

View File

@ -16,7 +16,9 @@
show-ownership-column="applicationState.application.authentication" show-ownership-column="applicationState.application.authentication"
remove-action="removeAction" remove-action="removeAction"
scale-action="scaleAction" scale-action="scaleAction"
force-update-action="forceUpdateAction"
swarm-manager-ip="swarmManagerIP" swarm-manager-ip="swarmManagerIP"
show-force-update-button="applicationState.endpoint.apiVersion >= 1.25"
></services-datatable> ></services-datatable>
</div> </div>
</div> </div>

View File

@ -17,6 +17,39 @@ function ($q, $scope, $state, Service, ServiceService, ServiceHelper, Notificati
}); });
}; };
$scope.forceUpdateAction = function(selectedItems) {
ModalService.confirmServiceForceUpdate(
'Do you want to force update of selected service(s)? All the tasks associated to the selected service(s) will be recreated.',
function onConfirm(confirmed) {
if(!confirmed) { return; }
forceUpdateServices(selectedItems);
}
);
};
function forceUpdateServices(services) {
var actionCount = services.length;
angular.forEach(services, function (service) {
var config = ServiceHelper.serviceToConfig(service.Model);
// As explained in https://github.com/docker/swarmkit/issues/2364 ForceUpdate can accept a random
// value or an increment of the counter value to force an update.
config.TaskTemplate.ForceUpdate++;
ServiceService.update(service, config)
.then(function success(data) {
Notifications.success('Service successfully updated', service.Name);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to force update service', service.Name);
})
.finally(function final() {
--actionCount;
if (actionCount === 0) {
$state.reload();
}
});
});
}
$scope.removeAction = function(selectedItems) { $scope.removeAction = function(selectedItems) {
ModalService.confirmDeletion( ModalService.confirmDeletion(
'Do you want to remove the selected service(s)? All the containers associated to the selected service(s) will be removed too.', 'Do you want to remove the selected service(s)? All the containers associated to the selected service(s) will be removed too.',

View File

@ -16,6 +16,10 @@
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)"> ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
<i class="fa fa-trash space-right" aria-hidden="true"></i>Remove <i class="fa fa-trash space-right" aria-hidden="true"></i>Remove
</button> </button>
<button ng-if="$ctrl.showForceUpdateButton" type="button" class="btn btn-sm btn-primary"
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.forceUpdateAction($ctrl.state.selectedItems)">
<i class="fa fa-refresh space-right" aria-hidden="true"></i>Force update
</button>
<button type="button" class="btn btn-sm btn-primary" ui-sref="actions.create.service"> <button type="button" class="btn btn-sm btn-primary" ui-sref="actions.create.service">
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add service <i class="fa fa-plus space-right" aria-hidden="true"></i>Add service
</button> </button>

View File

@ -12,6 +12,8 @@ angular.module('ui').component('servicesDatatable', {
showOwnershipColumn: '<', showOwnershipColumn: '<',
removeAction: '<', removeAction: '<',
scaleAction: '<', scaleAction: '<',
swarmManagerIp: '<' swarmManagerIp: '<',
forceUpdateAction: '<',
showForceUpdateButton: '<'
} }
}); });

View File

@ -156,5 +156,19 @@ angular.module('portainer.services')
}); });
}; };
service.confirmServiceForceUpdate = function(message, callback) {
service.confirm({
title: 'Are you sure ?',
message: message,
buttons: {
confirm: {
label: 'Update',
className: 'btn-primary'
}
},
callback: callback
});
};
return service; return service;
}]); }]);