From 9f546ee0f35afe0e7d7d64d15a92d715bd3301b2 Mon Sep 17 00:00:00 2001 From: Moshe Immerman Date: Sun, 26 Nov 2023 20:19:29 +0200 Subject: [PATCH] fix: lock git checkout dirs --- checks/exec.go | 16 +++++++++++++--- pkg/utils/utils.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/checks/exec.go b/checks/exec.go index d6139ecd6..d6ef7bee1 100644 --- a/checks/exec.go +++ b/checks/exec.go @@ -9,17 +9,21 @@ import ( "path/filepath" "runtime" "strings" + "time" "github.com/flanksource/canary-checker/api/context" "github.com/flanksource/canary-checker/api/external" v1 "github.com/flanksource/canary-checker/api/v1" "github.com/flanksource/canary-checker/pkg" + "github.com/flanksource/canary-checker/pkg/utils" "github.com/flanksource/commons/files" "github.com/flanksource/commons/hash" "github.com/flanksource/duty/models" "github.com/hashicorp/go-getter" ) +var checkoutLocks = utils.NamedLock{} + type ExecChecker struct { } @@ -91,11 +95,17 @@ func (c *ExecChecker) prepareEnvironment(ctx *context.Context, check v1.ExecChec result.mountPoint = check.Checkout.Destination if result.mountPoint == "" { - pwd, _ := os.Getwd() - result.mountPoint = filepath.Join(pwd, ".downloads", hash.Sha256Hex(goGetterURL)) + result.mountPoint = filepath.Join("exec-checkout", hash.Sha256Hex(goGetterURL)) + } + // We allow multiple checks to use the same checkout location, for disk space and performance reasons + // however git does not allow multiple operations to be performed, so we need to lock it + lock := checkoutLocks.TryLock(result.mountPoint, 5*time.Minute) + if lock == nil { + return nil, fmt.Errorf("failed to acquire checkout lock for %s", result.mountPoint) } + defer lock.Release() - if err := checkout(ctx, goGetterURL, result.mountPoint); err != nil { + if err := checkout(ctx, goGetterURL, filepath.Join(os.TempDir(), result.mountPoint)); err != nil { return nil, fmt.Errorf("error checking out: %w", err) } } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index b60bea400..d73622ba4 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,16 +1,48 @@ package utils import ( + "context" "crypto/md5" "encoding/hex" "encoding/json" "fmt" + "sync" "time" + "golang.org/x/sync/semaphore" + "github.com/google/uuid" "k8s.io/apimachinery/pkg/util/duration" ) +type NamedLock struct { + locks sync.Map +} + +type Unlocker interface { + Release() +} + +type unlocker struct { + lock *semaphore.Weighted +} + +func (u *unlocker) Release() { + u.lock.Release(1) +} + +func (n *NamedLock) TryLock(name string, timeout time.Duration) Unlocker { + o, _ := n.locks.LoadOrStore(name, semaphore.NewWeighted(1)) + lock := o.(*semaphore.Weighted) + + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) + defer cancel() + if err := lock.Acquire(ctx, 1); err != nil { + return nil + } + return &unlocker{lock} +} + func Age(d time.Duration) string { if d.Milliseconds() == 0 { return "0ms"