From edadce359c47b1e199bbbb3f898fb89881008d56 Mon Sep 17 00:00:00 2001 From: "Miguel A. C" <30386061+doncicuto@users.noreply.github.com> Date: Sat, 20 Jan 2018 18:05:01 +0100 Subject: [PATCH] feat(stack-details): add stack deploy prune option (#1567) * feat(stack-details): add stack deploy prune option * fix go fmt issues * add changes proposed by reviewer * refactor deployStack as suggested by codeclimate --- api/exec/stack_manager.go | 9 ++++- api/http/handler/stack.go | 53 +++++++++++++++++++++---- api/portainer.go | 2 +- api/swagger.yaml | 6 ++- app/components/stack/stack.html | 16 ++++++++ app/components/stack/stackController.js | 8 +++- app/services/api/stackService.js | 4 +- 7 files changed, 83 insertions(+), 15 deletions(-) diff --git a/api/exec/stack_manager.go b/api/exec/stack_manager.go index 7e418d70f..8ba6b357e 100644 --- a/api/exec/stack_manager.go +++ b/api/exec/stack_manager.go @@ -54,10 +54,15 @@ func (manager *StackManager) Logout(endpoint *portainer.Endpoint) error { } // Deploy executes the docker stack deploy command. -func (manager *StackManager) Deploy(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (manager *StackManager) Deploy(stack *portainer.Stack, prune bool, endpoint *portainer.Endpoint) error { stackFilePath := path.Join(stack.ProjectPath, stack.EntryPoint) command, args := prepareDockerCommandAndArgs(manager.binaryPath, endpoint) - args = append(args, "stack", "deploy", "--with-registry-auth", "--compose-file", stackFilePath, stack.Name) + + if prune { + args = append(args, "stack", "deploy", "--prune", "--with-registry-auth", "--compose-file", stackFilePath, stack.Name) + } else { + args = append(args, "stack", "deploy", "--with-registry-auth", "--compose-file", stackFilePath, stack.Name) + } env := make([]string, 0) for _, envvar := range stack.Env { diff --git a/api/http/handler/stack.go b/api/http/handler/stack.go index ed54c500f..3ad0ce7ae 100644 --- a/api/http/handler/stack.go +++ b/api/http/handler/stack.go @@ -37,6 +37,14 @@ type StackHandler struct { StackManager portainer.StackManager } +type stackDeploymentConfig struct { + endpoint *portainer.Endpoint + stack *portainer.Stack + prune bool + dockerhub *portainer.DockerHub + registries []portainer.Registry +} + // NewStackHandler returns a new instance of StackHandler. func NewStackHandler(bouncer *security.RequestBouncer) *StackHandler { h := &StackHandler{ @@ -78,6 +86,7 @@ type ( putStackRequest struct { StackFileContent string `valid:"required"` Env []portainer.Pair `valid:""` + Prune bool `valid:"-"` } ) @@ -207,7 +216,14 @@ func (handler *StackHandler) handlePostStacksStringMethod(w http.ResponseWriter, return } - err = handler.deployStack(endpoint, stack, dockerhub, filteredRegistries) + config := stackDeploymentConfig{ + stack: stack, + endpoint: endpoint, + dockerhub: dockerhub, + registries: filteredRegistries, + prune: false, + } + err = handler.deployStack(&config) if err != nil { httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger) return @@ -334,7 +350,14 @@ func (handler *StackHandler) handlePostStacksRepositoryMethod(w http.ResponseWri return } - err = handler.deployStack(endpoint, stack, dockerhub, filteredRegistries) + config := stackDeploymentConfig{ + stack: stack, + endpoint: endpoint, + dockerhub: dockerhub, + registries: filteredRegistries, + prune: false, + } + err = handler.deployStack(&config) if err != nil { httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger) return @@ -445,7 +468,14 @@ func (handler *StackHandler) handlePostStacksFileMethod(w http.ResponseWriter, r return } - err = handler.deployStack(endpoint, stack, dockerhub, filteredRegistries) + config := stackDeploymentConfig{ + stack: stack, + endpoint: endpoint, + dockerhub: dockerhub, + registries: filteredRegistries, + prune: false, + } + err = handler.deployStack(&config) if err != nil { httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger) return @@ -637,7 +667,14 @@ func (handler *StackHandler) handlePutStack(w http.ResponseWriter, r *http.Reque return } - err = handler.deployStack(endpoint, stack, dockerhub, filteredRegistries) + config := stackDeploymentConfig{ + stack: stack, + endpoint: endpoint, + dockerhub: dockerhub, + registries: filteredRegistries, + prune: req.Prune, + } + err = handler.deployStack(&config) if err != nil { httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger) return @@ -732,22 +769,22 @@ func (handler *StackHandler) handleDeleteStack(w http.ResponseWriter, r *http.Re } } -func (handler *StackHandler) deployStack(endpoint *portainer.Endpoint, stack *portainer.Stack, dockerhub *portainer.DockerHub, registries []portainer.Registry) error { +func (handler *StackHandler) deployStack(config *stackDeploymentConfig) error { handler.stackCreationMutex.Lock() - err := handler.StackManager.Login(dockerhub, registries, endpoint) + err := handler.StackManager.Login(config.dockerhub, config.registries, config.endpoint) if err != nil { handler.stackCreationMutex.Unlock() return err } - err = handler.StackManager.Deploy(stack, endpoint) + err = handler.StackManager.Deploy(config.stack, config.prune, config.endpoint) if err != nil { handler.stackCreationMutex.Unlock() return err } - err = handler.StackManager.Logout(endpoint) + err = handler.StackManager.Logout(config.endpoint) if err != nil { handler.stackCreationMutex.Unlock() return err diff --git a/api/portainer.go b/api/portainer.go index 8b250009b..82a3bc475 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -384,7 +384,7 @@ type ( StackManager interface { Login(dockerhub *DockerHub, registries []Registry, endpoint *Endpoint) error Logout(endpoint *Endpoint) error - Deploy(stack *Stack, endpoint *Endpoint) error + Deploy(stack *Stack, prune bool, endpoint *Endpoint) error Remove(stack *Stack, endpoint *Endpoint) error } ) diff --git a/api/swagger.yaml b/api/swagger.yaml index 5f4b9308b..5a5d2d008 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -2976,10 +2976,14 @@ definitions: description: "A list of environment variables used during stack deployment" items: $ref: "#/definitions/Stack_Env" + Prune: + type: "boolean" + example: false + description: "Prune services that are no longer referenced" StackFileInspectResponse: type: "object" properties: StackFileContent: type: "string" example: "version: 3\n services:\n web:\n image:nginx" - description: "Content of the Stack file." + description: "Content of the Stack file." diff --git a/app/components/stack/stack.html b/app/components/stack/stack.html index 970f8c294..37f3785ec 100644 --- a/app/components/stack/stack.html +++ b/app/components/stack/stack.html @@ -90,6 +90,22 @@ + +
+ Options +
+
+
+ + +
+
+
Actions
diff --git a/app/components/stack/stackController.js b/app/components/stack/stackController.js index 976b473b2..91137d33e 100644 --- a/app/components/stack/stackController.js +++ b/app/components/stack/stackController.js @@ -7,14 +7,19 @@ function ($q, $scope, $state, $transition$, $document, StackService, NodeService publicURL: EndpointProvider.endpointPublicURL() }; + $scope.formValues = { + Prune: false + }; + $scope.deployStack = function () { // The codemirror editor does not work with ng-model so we need to retrieve // the value directly from the editor. var stackFile = $scope.editor.getValue(); var env = FormHelper.removeInvalidEnvVars($scope.stack.Env); + var prune = $scope.formValues.Prune; $scope.state.actionInProgress = true; - StackService.updateStack($scope.stack.Id, stackFile, env) + StackService.updateStack($scope.stack.Id, stackFile, env, prune) .then(function success(data) { Notifications.success('Stack successfully deployed'); $state.reload(); @@ -37,6 +42,7 @@ function ($q, $scope, $state, $transition$, $document, StackService, NodeService function initView() { var stackId = $transition$.params().id; + var apiVersion = $scope.applicationState.endpoint.apiVersion; StackService.stack(stackId) .then(function success(data) { diff --git a/app/services/api/stackService.js b/app/services/api/stackService.js index e206b620d..64b99ec01 100644 --- a/app/services/api/stackService.js +++ b/app/services/api/stackService.js @@ -167,8 +167,8 @@ function StackServiceFactory($q, Stack, ResourceControlService, FileUploadServic return deferred.promise; }; - service.updateStack = function(id, stackFile, env) { - return Stack.update({ id: id, StackFileContent: stackFile, Env: env }).$promise; + service.updateStack = function(id, stackFile, env, prune) { + return Stack.update({ id: id, StackFileContent: stackFile, Env: env, Prune: prune}).$promise; }; return service;