-
Notifications
You must be signed in to change notification settings - Fork 1
Cross Platform lock + windows encryption #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
180b593
187858e
93a1abd
43149eb
cbd3859
b231fb4
54f1c22
15852ec
f973d88
e32eddf
d139ff0
c825fca
9c21596
666b417
357fd53
53249aa
92fe187
d4c10f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,3 +13,4 @@ | |
|
|
||
| # Dependency directories (remove the comment below to include it) | ||
| # vendor/ | ||
| .vscode/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package lock | ||
|
|
||
| import ( | ||
| "errors" | ||
| "io/fs" | ||
| "os" | ||
| "sync" | ||
| "time" | ||
|
|
||
| "github.com/AzureAD/microsoft-authentication-extensions-for-go/flock" | ||
| ) | ||
|
|
||
| type Lock struct { | ||
| retries int | ||
| retryDelay time.Duration | ||
|
|
||
| fLock *flock.Flock | ||
| mu sync.Mutex | ||
| } | ||
|
|
||
| type Option func(l *Lock) | ||
|
|
||
| func WithRetries(n int) Option { | ||
| return func(l *Lock) { | ||
| l.retries = n | ||
| } | ||
| } | ||
| func WithRetryDelay(t time.Duration) Option { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Public requires a comment. I'd put: // WithRetryDelay changes the default delay from [delay] to t. |
||
| return func(l *Lock) { | ||
| l.retryDelay = t | ||
| } | ||
| } | ||
|
|
||
| func New(lockFileName string, options ...Option) (*Lock, error) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lockFileName can be just "p". // New creates a Lock for a lock file at "p". If "p" doesn't exist, it will be created when using. Lock(). |
||
| l := &Lock{} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to add default value for retries and retryDelay. |
||
| for _, o := range options { | ||
| o(l) | ||
| } | ||
| l.fLock = flock.New(lockFileName) | ||
| return l, nil | ||
| } | ||
|
|
||
| func (l *Lock) Lock() error { | ||
| l.mu.Lock() | ||
| defer l.mu.Unlock() | ||
| for tryCount := 0; tryCount < l.retries; tryCount++ { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for i := 0; i < l.retries; i++ { |
||
| locked, err := l.fLock.TryLock() | ||
| if err != nil || !locked { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If locked { |
||
| time.Sleep(l.retryDelay * time.Millisecond) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be: |
||
| continue | ||
| } else if locked { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove "else if locked" section |
||
| // commenting this till we have flock merged in and ready to use with changes | ||
| // otherwise it would not compile | ||
| //l.fLock.Fh.WriteString(fmt.Sprintf("{%d} {%s}", os.Getpid(), os.Args[0])) | ||
| //l.fLock.Fh.Sync() | ||
| return nil | ||
| } | ||
| } | ||
| return errors.New("failed to acquire lock") | ||
| } | ||
|
|
||
| func (l *Lock) UnLock() error { | ||
| if l.fLock != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the "if l.fLock != nil", doesn't provide value. |
||
| if err := l.fLock.Unlock(); err != nil { | ||
| return err | ||
| } | ||
| err := os.Remove(l.fLock.Path()) | ||
| if err != nil { | ||
| errVal := err.(*fs.PathError) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if errors.Is(err, fs. ErrNotExist) || errors.Is(err, fs. ErrPermission) { |
||
| if errVal != fs.ErrNotExist || errVal != fs.ErrPermission { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| package lock | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "errors" | ||
| "fmt" | ||
| "io/ioutil" | ||
| "log" | ||
| "os" | ||
| "strings" | ||
| "sync" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/AzureAD/microsoft-authentication-extensions-for-go/internal" | ||
| ) | ||
|
|
||
| const cacheFile = "cache.txt" | ||
|
|
||
| func TestLocking(t *testing.T) { | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove blank line |
||
| tests := []struct { | ||
| desc string | ||
| concurrency int | ||
| sleepInterval time.Duration | ||
| cacheFile string | ||
| }{ | ||
| {"Normal", 4, 50 * time.Millisecond, "cache_normal"}, | ||
| {"High", 40, 50 * time.Millisecond, "cache_high"}, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| tmpfile, err := ioutil.TempFile("", test.cacheFile) | ||
| defer os.Remove(tmpfile.Name()) | ||
| if err != nil { | ||
| t.Fatalf("TestLocking(%s): Could not create cache file", test.desc) | ||
| } | ||
| err = spin(test.concurrency, time.Duration(test.sleepInterval), tmpfile.Name()) | ||
| if err != nil { | ||
| t.Fatalf("TestLocking(%s): %s", test.desc, err) | ||
| } | ||
| } | ||
|
|
||
| } | ||
| func acquire(threadNo int, sleepInterval time.Duration, cacheFile string) { | ||
| cacheAccessor := internal.NewFileAccessor(cacheFile) | ||
| lockfileName := cacheFile + ".lockfile" | ||
| l, err := New(lockfileName, WithRetries(60), WithRetryDelay(100)) | ||
| if err := l.Lock(); err != nil { | ||
| log.Println("Couldn't acquire lock", err.Error()) | ||
| return | ||
| } | ||
| defer l.UnLock() | ||
| data, err := cacheAccessor.Read() | ||
| if err != nil { | ||
| log.Println(err) | ||
| } | ||
| var buffer bytes.Buffer | ||
| buffer.Write(data) | ||
| buffer.WriteString(fmt.Sprintf("< %d \n", threadNo)) | ||
| time.Sleep(sleepInterval) | ||
| buffer.WriteString(fmt.Sprintf("> %d \n", threadNo)) | ||
| cacheAccessor.Write(buffer.Bytes()) | ||
| } | ||
|
|
||
| func spin(concurrency int, sleepInterval time.Duration, cacheFile string) error { | ||
| var wg sync.WaitGroup | ||
| wg.Add(concurrency) | ||
| for i := 0; i < concurrency; i++ { | ||
| go func(i int) { | ||
| defer wg.Done() | ||
| acquire(i, sleepInterval, cacheFile) | ||
| }(i) | ||
| } | ||
| wg.Wait() | ||
| i, err := validate(cacheFile) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if i != concurrency*2 { | ||
| return fmt.Errorf("should have seen %d line entries, found %d", concurrency*2, i) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func validate(cacheFile string) (int, error) { | ||
| var ( | ||
| count int | ||
| prevProc, tag, proc string | ||
| ) | ||
| data, err := os.ReadFile(cacheFile) | ||
| if err != nil { | ||
| log.Println(err) | ||
| } | ||
| temp := strings.Split(string(data), "\n") | ||
|
|
||
| for _, s := range temp { | ||
| if strings.TrimSpace(s) == "" { | ||
| continue | ||
| } | ||
| count += 1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. count++ |
||
| split := strings.Split(s, " ") | ||
| tag = split[0] | ||
| proc = split[1] | ||
| if prevProc == "" { | ||
| if tag != "<" { | ||
| return 0, errors.New("opening bracket not found") | ||
| } | ||
| prevProc = proc | ||
| continue | ||
| } | ||
| if proc != prevProc { | ||
| return 0, errors.New("process overlap found") | ||
| } | ||
| if tag != ">" { | ||
| return 0, errors.New("process overlap found") | ||
| } | ||
| prevProc = "" | ||
| } | ||
| return count, nil | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add two tests: One tests all the conditions of Lock().
You can do this by providing an interface: type flocker interface{ type fakeFlock struct { func (f fakeFlock) TryLock() error { func (f fakeFlock) Unlock() error { |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| module github.com/AzureAD/microsoft-authentication-extensions-for-go | ||
|
|
||
| go 1.14 | ||
|
|
||
| require ( | ||
| github.com/AzureAD/microsoft-authentication-library-for-go v0.3.0 | ||
| github.com/billgraziano/dpapi v0.4.0 | ||
| github.com/gofrs/flock v0.8.1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This flock line shouldn't. be here. So either a path change is needed or you need to run go tidy |
||
|
|
||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public requires a comment. I'd put:
// WithRetries changes the default number of retries from [retries] to n. Negative values panic()
Also, add panic on negative values.