Skip to content

Commit 6883d7e

Browse files
authored
First support for locks
Only for local FS for now
1 parent 944108c commit 6883d7e

19 files changed

+203
-2
lines changed

cmd/add.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"path/filepath"
2424
"strings"
25+
"time"
2526

2627
"github.com/ItalyPaleAle/prvt/fs"
2728
"github.com/ItalyPaleAle/prvt/fs/fsindex"
@@ -82,6 +83,15 @@ You must specify a destination, which is a folder inside the repository where yo
8283
return NewExecError(ErrorUser, "Could not initialize store", err)
8384
}
8485

86+
// Acquire a lock
87+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
88+
err = store.AcquireLock(ctx)
89+
cancel()
90+
if err != nil {
91+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
92+
}
93+
defer store.ReleaseLock()
94+
8595
// Request the info file
8696
info, err := store.GetInfoFile()
8797
if err != nil {
@@ -121,7 +131,6 @@ You must specify a destination, which is a folder inside the repository where yo
121131
}
122132

123133
// Iterate through the args and add them all
124-
ctx := context.Background()
125134
res := make(chan repository.PathResultMessage)
126135
go func() {
127136
var err error
@@ -140,7 +149,7 @@ You must specify a destination, which is a folder inside the repository where yo
140149
folder := filepath.Dir(expanded)
141150
target := filepath.Base(expanded)
142151

143-
repo.AddPath(ctx, folder, target, flagDestination, flagForce, res)
152+
repo.AddPath(context.Background(), folder, target, flagDestination, flagForce, res)
144153
}
145154

146155
close(res)

cmd/ls.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
2223
"sort"
2324
"strings"
25+
"time"
2426

2527
"github.com/ItalyPaleAle/prvt/fs"
2628
"github.com/ItalyPaleAle/prvt/fs/fsindex"
@@ -58,6 +60,15 @@ Shows the list of all files and folders contained in the repository at a given p
5860
return NewExecError(ErrorUser, "Could not initialize store", err)
5961
}
6062

63+
// Acquire a lock
64+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
65+
err = store.AcquireLock(ctx)
66+
cancel()
67+
if err != nil {
68+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
69+
}
70+
defer store.ReleaseLock()
71+
6172
// Request the info file
6273
info, err := store.GetInfoFile()
6374
if err != nil {

cmd/repo-info.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426
"github.com/ItalyPaleAle/prvt/fs/fsindex"
@@ -53,6 +55,15 @@ func NewRepoInfoCmd() *cobra.Command {
5355
return NewExecError(ErrorUser, "Could not initialize store", err)
5456
}
5557

58+
// Acquire a lock
59+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
60+
err = store.AcquireLock(ctx)
61+
cancel()
62+
if err != nil {
63+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
64+
}
65+
defer store.ReleaseLock()
66+
5667
// Request the info file
5768
info, err := store.GetInfoFile()
5869
if err != nil {

cmd/repo-init.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"errors"
2223
"fmt"
24+
"time"
2325

2426
"github.com/ItalyPaleAle/prvt/fs"
2527

@@ -58,6 +60,15 @@ In order to use GPG keys, you need to have GPG version 2 installed separately. Y
5860
return NewExecError(ErrorUser, "Could not initialize repository", err)
5961
}
6062

63+
// Acquire a lock
64+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
65+
err = store.AcquireLock(ctx)
66+
cancel()
67+
if err != nil {
68+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
69+
}
70+
defer store.ReleaseLock()
71+
6172
// Create the info file after generating a new master key
6273
info, errMessage, err := NewInfoFile(flagGPGKey)
6374
if err != nil {

cmd/repo-key-add.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426

@@ -55,6 +57,15 @@ In order to use GPG keys, you need to have GPG version 2 installed separately. Y
5557
return NewExecError(ErrorUser, "Could not initialize store", err)
5658
}
5759

60+
// Acquire a lock
61+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
62+
err = store.AcquireLock(ctx)
63+
cancel()
64+
if err != nil {
65+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
66+
}
67+
defer store.ReleaseLock()
68+
5869
// Request the info file
5970
info, err := store.GetInfoFile()
6071
if err != nil {

cmd/repo-key-ls.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426
"github.com/ItalyPaleAle/prvt/keys"
@@ -50,6 +52,15 @@ Usage: "prvt repo key ls --store <string>"
5052
return NewExecError(ErrorUser, "Could not initialize store", err)
5153
}
5254

55+
// Acquire a lock
56+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
57+
err = store.AcquireLock(ctx)
58+
cancel()
59+
if err != nil {
60+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
61+
}
62+
defer store.ReleaseLock()
63+
5364
// Request the info file
5465
info, err := store.GetInfoFile()
5566
if err != nil {

cmd/repo-key-rm.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"errors"
2223
"fmt"
24+
"time"
2325

2426
"github.com/ItalyPaleAle/prvt/fs"
2527

@@ -58,6 +60,15 @@ To identify a passphrase or a GPG key among those authorized, you can use the "p
5860
return NewExecError(ErrorUser, "Could not initialize store", err)
5961
}
6062

63+
// Acquire a lock
64+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
65+
err = store.AcquireLock(ctx)
66+
cancel()
67+
if err != nil {
68+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
69+
}
70+
defer store.ReleaseLock()
71+
6172
// Request the info file
6273
info, err := store.GetInfoFile()
6374
if err != nil {

cmd/repo-key-test.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426

@@ -51,6 +53,15 @@ This command is particularly useful to determine the ID of a key that you want t
5153
return NewExecError(ErrorUser, "Could not initialize store", err)
5254
}
5355

56+
// Acquire a lock
57+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
58+
err = store.AcquireLock(ctx)
59+
cancel()
60+
if err != nil {
61+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
62+
}
63+
defer store.ReleaseLock()
64+
5465
// Request the info file
5566
info, err := store.GetInfoFile()
5667
if err != nil {

cmd/repo-upgrade.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"fmt"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426

@@ -48,6 +50,15 @@ Usage: "prvt repo upgrade --store <string>"
4850
return NewExecError(ErrorUser, "Could not initialize store", err)
4951
}
5052

53+
// Acquire a lock
54+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
55+
err = store.AcquireLock(ctx)
56+
cancel()
57+
if err != nil {
58+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
59+
}
60+
defer store.ReleaseLock()
61+
5162
// Request the info file
5263
info, err := store.GetInfoFile()
5364
if err != nil {

cmd/rm.go

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package cmd
2020
import (
2121
"context"
2222
"fmt"
23+
"time"
2324

2425
"github.com/ItalyPaleAle/prvt/fs"
2526
"github.com/ItalyPaleAle/prvt/fs/fsindex"
@@ -60,6 +61,15 @@ To remove a file, specify its exact path. To remove a folder recursively, specif
6061
return NewExecError(ErrorUser, "Could not initialize store", err)
6162
}
6263

64+
// Acquire a lock
65+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
66+
err = store.AcquireLock(ctx)
67+
cancel()
68+
if err != nil {
69+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
70+
}
71+
defer store.ReleaseLock()
72+
6373
// Request the info file
6474
info, err := store.GetInfoFile()
6575
if err != nil {

cmd/serve.go

+11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
package cmd
1919

2020
import (
21+
"context"
2122
"errors"
23+
"time"
2224

2325
"github.com/ItalyPaleAle/prvt/fs"
2426
"github.com/ItalyPaleAle/prvt/fs/fsindex"
@@ -93,6 +95,15 @@ You can use the optional "--address" and "--port" flags to control what address
9395
return NewExecError(ErrorUser, "Could not initialize store", err)
9496
}
9597

98+
// Acquire a lock
99+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
100+
err = store.AcquireLock(ctx)
101+
cancel()
102+
if err != nil {
103+
return NewExecError(ErrorApp, "Could not acquire a lock. Please make sure that no other instance of prvt is running with the same repo.", err)
104+
}
105+
defer store.ReleaseLock()
106+
96107
// Request the info file
97108
info, err = store.GetInfoFile()
98109
if err != nil {

fs/fs-azure-storage.go

+8
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,14 @@ func (f *AzureStorage) Delete(ctx context.Context, name string, tag interface{})
601601
return
602602
}
603603

604+
func (f *AzureStorage) AcquireLock(ctx context.Context) (err error) {
605+
return nil
606+
}
607+
608+
func (f *AzureStorage) ReleaseLock() (err error) {
609+
return nil
610+
}
611+
604612
// Internal function that returns the URL object for a blob
605613
func (f *AzureStorage) blobUrl(name string) (blockBlobURL azblob.BlockBlobURL, err error) {
606614
if name == "" {

fs/fs-local.go

+30
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ import (
2929
"reflect"
3030
"strings"
3131
"sync"
32+
"time"
3233

3334
"github.com/ItalyPaleAle/prvt/crypto"
3435
"github.com/ItalyPaleAle/prvt/fs/fsutils"
3536
"github.com/ItalyPaleAle/prvt/infofile"
3637
"github.com/ItalyPaleAle/prvt/utils"
3738

39+
"github.com/gofrs/flock"
3840
homedir "github.com/mitchellh/go-homedir"
3941
)
4042

@@ -53,6 +55,7 @@ type Local struct {
5355
basePath string
5456
cache *fsutils.MetadataCache
5557
mux sync.Mutex
58+
lock *flock.Flock
5659
}
5760

5861
func (f *Local) OptionsList() *FsOptionsList {
@@ -492,6 +495,33 @@ func (f *Local) Delete(ctx context.Context, name string, tag interface{}) (err e
492495
return
493496
}
494497

498+
func (f *Local) AcquireLock(ctx context.Context) (err error) {
499+
// For the Local implementation, locks are created on a local file using flock and contain no text
500+
f.lock = flock.New(f.basePath + "_lock")
501+
502+
// Gain an exclusive lock
503+
locked, err := f.lock.TryLockContext(ctx, 400*time.Millisecond)
504+
if err != nil {
505+
return err
506+
}
507+
if !locked {
508+
return errors.New("could not acquire lock")
509+
}
510+
511+
return nil
512+
}
513+
514+
func (f *Local) ReleaseLock() (err error) {
515+
// Silently short-circuit
516+
if f.lock == nil || !f.lock.Locked() {
517+
return nil
518+
}
519+
520+
// Release the lock
521+
// We won't try to delete the file to avoid race conditions with another instance trying to acquire a lock
522+
return f.lock.Unlock()
523+
}
524+
495525
// Internal function that returns the path to the file on disk
496526
// The second argument should be true when we're getting a path for a new file: in that case, the function will check that the folder(s) exist on disk
497527
func (f *Local) filePath(name string, isCreate bool) (path string, err error) {

fs/fs-s3.go

+8
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,14 @@ func (f *S3) Delete(ctx context.Context, name string, tag interface{}) (err erro
495495
return
496496
}
497497

498+
func (f *S3) AcquireLock(ctx context.Context) (err error) {
499+
return nil
500+
}
501+
502+
func (f *S3) ReleaseLock() (err error) {
503+
return nil
504+
}
505+
498506
// Internal function that returns the path to the file on storage
499507
func (f *S3) filePath(name string) (path string, err error) {
500508
if name == "" {

0 commit comments

Comments
 (0)