From 7275d23e4b82b924f903c05f6de3b942813eb204 Mon Sep 17 00:00:00 2001 From: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com> Date: Mon, 4 Jul 2022 11:39:03 +1200 Subject: [PATCH] feat(stack/swarm): add prune option for swarm stack redeployment [EE-2678] (#7025) --- .../test_data/output_24_to_latest.json | 3 +++ api/go.mod | 2 +- api/go.sum | 4 ++-- api/http/handler/stacks/stack_update_git.go | 7 ++++++ .../stacks/stack_update_git_redeploy.go | 12 +++++++++- api/portainer.go | 8 +++++++ .../stack-redeploy-git-form.controller.js | 24 ++++++++++++++++++- .../stack-redeploy-git-form.html | 2 +- .../components/option-panel/index.js | 13 ++++++++++ .../option-panel/option-panel.controller.js | 10 ++++++++ .../components/option-panel/option-panel.html | 15 ++++++++++++ app/portainer/models/stack.js | 2 ++ app/portainer/services/api/stackService.js | 1 + 13 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 app/portainer/components/option-panel/index.js create mode 100644 app/portainer/components/option-panel/option-panel.controller.js create mode 100644 app/portainer/components/option-panel/option-panel.html diff --git a/api/datastore/test_data/output_24_to_latest.json b/api/datastore/test_data/output_24_to_latest.json index 575924ca5..c4bf5e520 100644 --- a/api/datastore/test_data/output_24_to_latest.json +++ b/api/datastore/test_data/output_24_to_latest.json @@ -790,6 +790,7 @@ "IsComposeFormat": false, "Name": "alpine", "Namespace": "", + "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/2", "ResourceControl": null, "Status": 1, @@ -812,6 +813,7 @@ "IsComposeFormat": false, "Name": "redis", "Namespace": "", + "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/5", "ResourceControl": null, "Status": 1, @@ -834,6 +836,7 @@ "IsComposeFormat": false, "Name": "nginx", "Namespace": "", + "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/6", "ResourceControl": null, "Status": 1, diff --git a/api/go.mod b/api/go.mod index 0757f5ed6..59d14de08 100644 --- a/api/go.mod +++ b/api/go.mod @@ -11,7 +11,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 github.com/docker/cli v20.10.9+incompatible - github.com/docker/docker v20.10.9+incompatible + github.com/docker/docker v20.10.16+incompatible github.com/fvbommel/sortorder v1.0.2 github.com/fxamacker/cbor/v2 v2.3.0 github.com/g07cha/defender v0.0.0-20180505193036-5665c627c814 diff --git a/api/go.sum b/api/go.sum index da4624f2c..9cb1ed1ee 100644 --- a/api/go.sum +++ b/api/go.sum @@ -332,6 +332,8 @@ github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k= github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= +github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -807,8 +809,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/portainer/docker-compose-wrapper v0.0.0-20220526210722-e1574867298e h1:gW1Ooaj7RZ9YkwHxesnNEyOB5nUD71FlZ7cdb5h63vw= -github.com/portainer/docker-compose-wrapper v0.0.0-20220526210722-e1574867298e/go.mod h1:WxDlJWZxCnicdLCPnLNEv7/gRhjeIVuCGmsv+iOPH3c= github.com/portainer/docker-compose-wrapper v0.0.0-20220531190153-c597b853e410 h1:LjxLd8UGR8ae73ov/vLrt/0jedj/nh98XnONkr8DJj8= github.com/portainer/docker-compose-wrapper v0.0.0-20220531190153-c597b853e410/go.mod h1:WxDlJWZxCnicdLCPnLNEv7/gRhjeIVuCGmsv+iOPH3c= github.com/portainer/libcrypto v0.0.0-20210422035235-c652195c5c3a h1:qY8TbocN75n5PDl16o0uVr5MevtM5IhdwSelXEd4nFM= diff --git a/api/http/handler/stacks/stack_update_git.go b/api/http/handler/stacks/stack_update_git.go index d4a1c3485..3de663e22 100644 --- a/api/http/handler/stacks/stack_update_git.go +++ b/api/http/handler/stacks/stack_update_git.go @@ -18,6 +18,7 @@ import ( type stackGitUpdatePayload struct { AutoUpdate *portainer.StackAutoUpdate Env []portainer.Pair + Prune bool RepositoryReferenceName string RepositoryAuthentication bool RepositoryUsername string @@ -131,6 +132,12 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) * stack.UpdatedBy = user.Username stack.UpdateDate = time.Now().Unix() + if stack.Type == portainer.DockerSwarmStack { + stack.Option = &portainer.StackOption{ + Prune: payload.Prune, + } + } + if payload.RepositoryAuthentication { password := payload.RepositoryPassword if password == "" && stack.GitConfig != nil && stack.GitConfig.Authentication != nil { diff --git a/api/http/handler/stacks/stack_update_git_redeploy.go b/api/http/handler/stacks/stack_update_git_redeploy.go index 74c7ae283..6173b203e 100644 --- a/api/http/handler/stacks/stack_update_git_redeploy.go +++ b/api/http/handler/stacks/stack_update_git_redeploy.go @@ -24,6 +24,7 @@ type stackGitRedployPayload struct { RepositoryUsername string RepositoryPassword string Env []portainer.Pair + Prune bool } func (payload *stackGitRedployPayload) Validate(r *http.Request) error { @@ -118,6 +119,11 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request) stack.GitConfig.ReferenceName = payload.RepositoryReferenceName stack.Env = payload.Env + if stack.Type == portainer.DockerSwarmStack { + stack.Option = &portainer.StackOption{ + Prune: payload.Prune, + } + } backupProjectPath := fmt.Sprintf("%s-old", stack.ProjectPath) err = filesystem.MoveDirectory(stack.ProjectPath, backupProjectPath) @@ -187,7 +193,11 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request) func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint) *httperror.HandlerError { switch stack.Type { case portainer.DockerSwarmStack: - config, httpErr := handler.createSwarmDeployConfig(r, stack, endpoint, false) + prune := false + if stack.Option != nil { + prune = stack.Option.Prune + } + config, httpErr := handler.createSwarmDeployConfig(r, stack, endpoint, prune) if httpErr != nil { return httpErr } diff --git a/api/portainer.go b/api/portainer.go index 786b4a834..77e3b9e7f 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -922,6 +922,8 @@ type ( AdditionalFiles []string `json:"AdditionalFiles"` // The auto update settings of a git stack AutoUpdate *StackAutoUpdate `json:"AutoUpdate"` + // The stack deployment option + Option *StackOption `json:"Option"` // The git config of this stack GitConfig *gittypes.RepoConfig // Whether the stack is from a app template @@ -942,6 +944,12 @@ type ( JobID string `example:"15"` } + // StackOption represents the options for stack deployment + StackOption struct { + // Prune services that are no longer referenced + Prune bool `example:"false"` + } + // StackID represents a stack identifier (it must be composed of Name + "_" + SwarmID to create a unique identifier) StackID int diff --git a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js index 1a3092b7c..7f0fb059c 100644 --- a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js +++ b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js @@ -28,6 +28,9 @@ class StackRedeployGitFormController { RepositoryUsername: '', RepositoryPassword: '', Env: [], + Option: { + Prune: false, + }, // auto update AutoUpdate: { RepositoryAutomaticUpdates: false, @@ -41,6 +44,7 @@ class StackRedeployGitFormController { this.onChangeRef = this.onChangeRef.bind(this); this.onChangeAutoUpdate = this.onChangeAutoUpdate.bind(this); this.onChangeEnvVar = this.onChangeEnvVar.bind(this); + this.onChangeOption = this.onChangeOption.bind(this); } buildAnalyticsProperties() { @@ -88,6 +92,15 @@ class StackRedeployGitFormController { this.onChange({ Env: value }); } + onChangeOption(values) { + this.onChange({ + Option: { + ...this.formValues.Option, + ...values, + }, + }); + } + async submit() { const tplCrop = '
Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption.
' + @@ -101,7 +114,13 @@ class StackRedeployGitFormController { } try { this.state.redeployInProgress = true; - await this.StackService.updateGit(this.stack.Id, this.stack.EndpointId, this.FormHelper.removeInvalidEnvVars(this.formValues.Env), false, this.formValues); + await this.StackService.updateGit( + this.stack.Id, + this.stack.EndpointId, + this.FormHelper.removeInvalidEnvVars(this.formValues.Env), + this.formValues.Option.Prune, + this.formValues + ); this.Notifications.success('Pulled and redeployed stack successfully'); this.$state.reload(); } catch (err) { @@ -148,6 +167,9 @@ class StackRedeployGitFormController { $onInit() { this.formValues.RefName = this.model.ReferenceName; this.formValues.Env = this.stack.Env; + if (this.stack.Option) { + this.formValues.Option = this.stack.Option; + } // Init auto update if (this.stack.AutoUpdate && (this.stack.AutoUpdate.Interval || this.stack.AutoUpdate.Webhook)) { diff --git a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html index fce764ba2..abb70eb6c 100644 --- a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html +++ b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.html @@ -36,7 +36,7 @@ explanation="These values will be used as substitutions in the stack file" on-change="($ctrl.onChangeEnvVar)" > - +