1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-23 15:29:42 +02:00

feat(services): add the ability to pull latest image when updating a … (#1984)

* feat(services): add the ability to pull latest image when updating a service

* feat(services): update version header value

* refactor(services): remove TODO

* feat(services): rollback version header value to 1.29
This commit is contained in:
Anthony Lapenna 2018-06-20 15:53:58 +02:00 committed by GitHub
parent 0da9e564b9
commit 61c74e22f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 24 deletions

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker') angular.module('portainer.docker')
.controller('ServicesDatatableActionsController', ['$state', 'ServiceService', 'ServiceHelper', 'Notifications', 'ModalService', .controller('ServicesDatatableActionsController', ['$state', 'ServiceService', 'ServiceHelper', 'Notifications', 'ModalService', 'ImageHelper',
function ($state, ServiceService, ServiceHelper, Notifications, ModalService) { function ($state, ServiceService, ServiceHelper, Notifications, ModalService, ImageHelper) {
this.scaleAction = function scaleService(service) { this.scaleAction = function scaleService(service) {
var config = ServiceHelper.serviceToConfig(service.Model); var config = ServiceHelper.serviceToConfig(service.Model);
@ -17,16 +17,6 @@ function ($state, ServiceService, ServiceHelper, Notifications, ModalService) {
}); });
}; };
this.updateAction = 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);
}
);
};
this.removeAction = function(selectedItems) { this.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.',
@ -37,10 +27,28 @@ function ($state, ServiceService, ServiceHelper, Notifications, ModalService) {
); );
}; };
function forceUpdateServices(services) { this.updateAction = function(selectedItems) {
ModalService.confirmServiceForceUpdate(
'Do you want to force an update of the selected service(s)? All the tasks associated to the selected service(s) will be recreated.',
function (result) {
if(!result) { return; }
var pullImage = false;
if (result[0]) {
pullImage = true;
}
forceUpdateServices(selectedItems, pullImage);
}
);
};
function forceUpdateServices(services, pullImage) {
var actionCount = services.length; var actionCount = services.length;
angular.forEach(services, function (service) { angular.forEach(services, function (service) {
var config = ServiceHelper.serviceToConfig(service.Model); var config = ServiceHelper.serviceToConfig(service.Model);
if (pullImage) {
config.TaskTemplate.ContainerSpec.Image = ImageHelper.removeDigestFromRepository(config.TaskTemplate.ContainerSpec.Image);
}
// As explained in https://github.com/docker/swarmkit/issues/2364 ForceUpdate can accept a random // 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. // value or an increment of the counter value to force an update.
config.TaskTemplate.ForceUpdate++; config.TaskTemplate.ForceUpdate++;

View file

@ -55,5 +55,9 @@ angular.module('portainer.docker')
}; };
}; };
helper.removeDigestFromRepository = function(repository) {
return repository.split('@sha')[0];
};
return helper; return helper;
}]); }]);

View file

@ -49,7 +49,7 @@ function ServiceViewModel(data, runningTasks, allTasks, nodes) {
this.LogDriverName = ''; this.LogDriverName = '';
this.LogDriverOpts = []; this.LogDriverOpts = [];
} }
this.Constraints = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Constraints || [] : []; this.Constraints = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Constraints || [] : [];
this.Preferences = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Preferences || [] : []; this.Preferences = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Preferences || [] : [];
this.Platforms = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Platforms || [] : []; this.Platforms = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Platforms || [] : [];

View file

@ -10,10 +10,24 @@ function ServiceFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, Htt
query: { method: 'GET', isArray: true, params: {filters: '@filters'} }, query: { method: 'GET', isArray: true, params: {filters: '@filters'} },
create: { create: {
method: 'POST', params: {action: 'create'}, method: 'POST', params: {action: 'create'},
headers: { 'X-Registry-Auth': HttpRequestHelper.registryAuthenticationHeader }, headers: {
'X-Registry-Auth': HttpRequestHelper.registryAuthenticationHeader,
// TODO: This is a temporary work-around that allows us to leverage digest pinning on
// the Docker daemon side. It has been moved client-side since Docker API version > 1.29.
// We should introduce digest pinning in Portainer as well.
'version': '1.29'
},
ignoreLoadingBar: true ignoreLoadingBar: true
}, },
update: { method: 'POST', params: {id: '@id', action: 'update', version: '@version'} }, update: {
method: 'POST', params: { id: '@id', action: 'update', version: '@version' },
headers: {
// TODO: This is a temporary work-around that allows us to leverage digest pinning on
// the Docker daemon side. It has been moved client-side since Docker API version > 1.29.
// We should introduce digest pinning in Portainer as well.
'version': '1.29'
}
},
remove: { method: 'DELETE', params: {id: '@id'} }, remove: { method: 'DELETE', params: {id: '@id'} },
logs: { logs: {
method: 'GET', params: { id: '@id', action: 'logs' }, method: 'GET', params: { id: '@id', action: 'logs' },

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker') angular.module('portainer.docker')
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'ContainerService', 'TaskHelper', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService', .controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'ContainerService', 'TaskHelper', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService', 'ImageHelper',
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, ContainerService, TaskHelper, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService) { function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, ContainerService, TaskHelper, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService, ImageHelper) {
$scope.state = { $scope.state = {
updateInProgress: false, updateInProgress: false,
@ -354,16 +354,24 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.forceUpdateService = function(service) { $scope.forceUpdateService = function(service) {
ModalService.confirmServiceForceUpdate( ModalService.confirmServiceForceUpdate(
'Do you want to force update this service? All the tasks associated to the selected service(s) will be recreated.', 'Do you want to force an update of the service? All the tasks associated to the service will be recreated.',
function onConfirm(confirmed) { function (result) {
if(!confirmed) { return; } if(!result) { return; }
forceUpdateService(service); var pullImage = false;
if (result[0]) {
pullImage = true;
}
forceUpdateService(service, pullImage);
} }
); );
}; };
function forceUpdateService(service) { function forceUpdateService(service, pullImage) {
var config = ServiceHelper.serviceToConfig(service.Model); var config = ServiceHelper.serviceToConfig(service.Model);
if (pullImage) {
config.TaskTemplate.ContainerSpec.Image = config.TaskTemplate.ContainerSpec.Image = ImageHelper.removeDigestFromRepository(config.TaskTemplate.ContainerSpec.Image);
}
// As explained in https://github.com/docker/swarmkit/issues/2364 ForceUpdate can accept a random // 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. // value or an increment of the counter value to force an update.
config.TaskTemplate.ForceUpdate++; config.TaskTemplate.ForceUpdate++;

View file

@ -157,9 +157,16 @@ angular.module('portainer.app')
}; };
service.confirmServiceForceUpdate = function(message, callback) { service.confirmServiceForceUpdate = function(message, callback) {
service.confirm({ service.customPrompt({
title: 'Are you sure ?', title: 'Are you sure ?',
message: message, message: message,
inputType: 'checkbox',
inputOptions: [
{
text: 'Pull latest image version<i></i>',
value: '1'
}
],
buttons: { buttons: {
confirm: { confirm: {
label: 'Update', label: 'Update',