pomerium/internal/autocert/storage_locker.go
Caleb Doxsey 8d61575ada
autocert: add support for storage in gcs (#3794)
* autocert: add support for storage in s3

* go mod tidy

* skip on mac

* autocert: add support for storage in gcs
2022-12-09 08:22:32 -07:00

74 lines
1.4 KiB
Go

package autocert
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/fs"
"time"
"github.com/google/uuid"
)
const (
lockDuration = time.Second * 30
lockPollInterval = time.Second
)
type lockState struct {
ID string
Expires time.Time
}
type locker struct {
store func(ctx context.Context, key string, value []byte) error
load func(ctx context.Context, key string) ([]byte, error)
delete func(ctx context.Context, key string) error
}
func (l *locker) Lock(ctx context.Context, name string) error {
key := fmt.Sprintf("locks/%s", name)
lockID := uuid.NewString()
for {
data, err := l.load(ctx, key)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
var ls lockState
if json.Unmarshal(data, &ls) == nil {
if ls.ID == lockID {
return nil
} else if ls.Expires.Before(time.Now()) {
// ignore the existing lock and take it ourselves
} else {
// wait
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(lockPollInterval):
}
continue
}
}
ls.ID = lockID
ls.Expires = time.Now().Add(lockDuration)
data, err = json.Marshal(ls)
if err != nil {
return err
}
err = l.store(ctx, key, data)
if err != nil {
return err
}
}
}
func (l *locker) Unlock(ctx context.Context, name string) error {
key := fmt.Sprintf("locks/%s", name)
return l.delete(ctx, key)
}