From 85ae70583319bf3da19042b8abe188a6d5cbd5de Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:41:48 -0300 Subject: [PATCH] fix(gitops): add singleflight behavior to RedeployWhenChanged calls EE-6377 (#10734) --- api/stacks/deployments/deploy.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/api/stacks/deployments/deploy.go b/api/stacks/deployments/deploy.go index b5111de02..58ef025a9 100644 --- a/api/stacks/deployments/deploy.go +++ b/api/stacks/deployments/deploy.go @@ -3,6 +3,7 @@ package deployments import ( "crypto/tls" "fmt" + "strconv" "time" portainer "github.com/portainer/portainer/api" @@ -16,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" + "golang.org/x/sync/singleflight" ) type StackAuthorMissingErr struct { @@ -27,11 +29,11 @@ func (e *StackAuthorMissingErr) Error() string { return fmt.Sprintf("stack's %v author %s is missing", e.stackID, e.authorName) } +var singleflightGroup = &singleflight.Group{} + // RedeployWhenChanged pull and redeploy the stack when git repo changed // Stack will always be redeployed if force deployment is set to true func RedeployWhenChanged(stackID portainer.StackID, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) error { - log.Debug().Int("stack_id", int(stackID)).Msg("redeploying stack") - stack, err := datastore.Stack().Read(stackID) if dataservices.IsErrObjectNotFound(err) { return scheduler.NewPermanentError(errors.WithMessagef(err, "failed to get the stack %v", stackID)) @@ -39,6 +41,24 @@ func RedeployWhenChanged(stackID portainer.StackID, deployer StackDeployer, data return errors.WithMessagef(err, "failed to get the stack %v", stackID) } + // Webhook + if stack.AutoUpdate != nil && stack.AutoUpdate.Webhook != "" { + return redeployWhenChanged(stack, deployer, datastore, gitService) + } + + // Polling + _, err, _ = singleflightGroup.Do(strconv.Itoa(int(stackID)), func() (any, error) { + return nil, redeployWhenChanged(stack, deployer, datastore, gitService) + }) + + return err +} + +func redeployWhenChanged(stack *portainer.Stack, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) error { + stackID := stack.ID + + log.Debug().Int("stack_id", int(stackID)).Msg("redeploying stack") + if stack.GitConfig == nil { return nil // do nothing if it isn't a git-based stack }