diff --git a/api/http/handler/stacks/stack_migrate.go b/api/http/handler/stacks/stack_migrate.go index 33d410b7b..beb579a34 100644 --- a/api/http/handler/stacks/stack_migrate.go +++ b/api/http/handler/stacks/stack_migrate.go @@ -23,7 +23,7 @@ func (payload *stackMigratePayload) Validate(r *http.Request) error { return nil } -// POST request on /api/stacks/:id/migrate +// POST request on /api/stacks/:id/migrate?endpointId= func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { stackID, err := request.RetrieveNumericRouteVariableValue(r, "id") if err != nil { @@ -59,6 +59,17 @@ func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *ht } } + // TODO: this is a work-around for stacks created with Portainer version >= 1.17.1 + // The EndpointID property is not available for these stacks, this API endpoint + // can use the optional EndpointID query parameter to associate a valid endpoint identifier to the stack. + endpointID, err := request.RetrieveNumericQueryParameter(r, "endpointId", true) + if err != nil { + return &httperror.HandlerError{http.StatusBadRequest, "Invalid query parameter: endpointId", err} + } + if endpointID != int(stack.EndpointID) { + stack.EndpointID = portainer.EndpointID(endpointID) + } + endpoint, err := handler.EndpointService.Endpoint(stack.EndpointID) if err == portainer.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} diff --git a/app/portainer/rest/stack.js b/app/portainer/rest/stack.js index d1de2da1e..6d8807791 100644 --- a/app/portainer/rest/stack.js +++ b/app/portainer/rest/stack.js @@ -9,6 +9,6 @@ angular.module('portainer.app') update: { method: 'PUT', params: { id: '@id' }, ignoreLoadingBar: true }, remove: { method: 'DELETE', params: { id: '@id', external: '@external', endpointId: '@endpointId' } }, getStackFile: { method: 'GET', params: { id : '@id', action: 'file' } }, - migrate: { method: 'POST', params: { id : '@id', action: 'migrate' }, ignoreLoadingBar: true } + migrate: { method: 'POST', params: { id : '@id', action: 'migrate', endpointId: '@endpointId' }, ignoreLoadingBar: true } }); }]); diff --git a/app/portainer/services/api/stackService.js b/app/portainer/services/api/stackService.js index 8a70bfc0c..c32f6bec2 100644 --- a/app/portainer/services/api/stackService.js +++ b/app/portainer/services/api/stackService.js @@ -46,7 +46,7 @@ function StackServiceFactory($q, Stack, ResourceControlService, FileUploadServic return; } - return Stack.migrate({ id: stack.Id }, { EndpointID: targetEndpointId, SwarmID: swarm.Id }).$promise; + return Stack.migrate({ id: stack.Id, endpointId: stack.EndpointId }, { EndpointID: targetEndpointId, SwarmID: swarm.Id }).$promise; }) .then(function success(data) { deferred.resolve(); @@ -66,7 +66,7 @@ function StackServiceFactory($q, Stack, ResourceControlService, FileUploadServic EndpointProvider.setEndpointID(targetEndpointId); - Stack.migrate({ id: stack.Id }, { EndpointID: targetEndpointId }).$promise + Stack.migrate({ id: stack.Id, endpointId: stack.EndpointId }, { EndpointID: targetEndpointId }).$promise .then(function success(data) { deferred.resolve(); }) diff --git a/app/portainer/views/stacks/edit/stackController.js b/app/portainer/views/stacks/edit/stackController.js index c7816d41b..57356dbe7 100644 --- a/app/portainer/views/stacks/edit/stackController.js +++ b/app/portainer/views/stacks/edit/stackController.js @@ -54,6 +54,15 @@ function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceSe migrateRequest = StackService.migrateComposeStack; } + // TODO: this is a work-around for stacks created with Portainer version >= 1.17.1 + // The EndpointID property is not available for these stacks, we can pass + // the current endpoint identifier as a part of the migrate request. It will be used if + // the EndpointID property is not defined on the stack. + var endpointId = EndpointProvider.endpointID(); + if (stack.EndpointId === 0) { + stack.EndpointId = endpointId; + } + $scope.state.migrationInProgress = true; migrateRequest(stack, targetEndpointId) .then(function success(data) {