Split
This commit is contained in:
parent
3981025fa4
commit
43682fab05
2 changed files with 79 additions and 40 deletions
28
pkg/storage/fs.go
Normal file
28
pkg/storage/fs.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoSuchKey = errors.New("no such key in store")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Item interface {
|
||||||
|
sync.Locker
|
||||||
|
Dirty()
|
||||||
|
IsDirty() bool
|
||||||
|
GetData() interface{}
|
||||||
|
SetData(interface{})
|
||||||
|
ItemKey() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Store interface {
|
||||||
|
GetItem(key string, data interface{}) (Item, error)
|
||||||
|
Get(key string, data interface{}) error
|
||||||
|
Put(key string, version, minorVersion int, secretMode bool, data interface{}) (Item, error)
|
||||||
|
FlushAll() []error
|
||||||
|
Flush(key string) error
|
||||||
|
Shutdown()
|
||||||
|
}
|
|
@ -2,55 +2,43 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IndentStr = strings.Repeat(" ", 4)
|
IndentStr = strings.Repeat(" ", 4)
|
||||||
|
|
||||||
ErrNoSuchKey = errors.New("no such key in store")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SecretMode os.FileMode = 0600
|
SecretMode fs.FileMode = 0600
|
||||||
DefaultMode os.FileMode = 0644
|
DefaultMode fs.FileMode = 0644
|
||||||
)
|
)
|
||||||
|
|
||||||
type Data interface {
|
|
||||||
}
|
|
||||||
|
|
||||||
type item struct {
|
type item struct {
|
||||||
|
sync.Mutex `json:"-"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
MinorVersion *int `json:"minor_version,omitempty"`
|
MinorVersion *int `json:"minor_version,omitempty"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
|
|
||||||
fmode os.FileMode
|
fmode fs.FileMode
|
||||||
dirty bool
|
dirty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item interface {
|
func (i *item) Dirty() { i.Lock(); defer i.Unlock(); i.dirty = true }
|
||||||
Dirty()
|
func (i *item) IsDirty() bool { i.Lock(); defer i.Unlock(); return i.dirty }
|
||||||
IsDirty() bool
|
func (i *item) GetData() interface{} { i.Lock(); defer i.Unlock(); return i.Data }
|
||||||
GetData() interface{}
|
func (i *item) SetData(d interface{}) { i.Lock(); defer i.Unlock(); i.Data = d; i.dirty = true }
|
||||||
SetData(interface{})
|
func (i *item) ItemKey() string { return i.Key /* key is immutable */ }
|
||||||
ItemKey() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *item) Dirty() { i.dirty = true }
|
func (it *item) mode() fs.FileMode {
|
||||||
func (i *item) IsDirty() bool { return i.dirty }
|
|
||||||
func (i *item) GetData() interface{} { return i.Data }
|
|
||||||
func (i *item) SetData(d interface{}) { i.Data = d; i.Dirty() }
|
|
||||||
func (i *item) ItemKey() string { return i.Key }
|
|
||||||
|
|
||||||
func (it *item) mode() os.FileMode {
|
|
||||||
if it.fmode != 0 {
|
if it.fmode != 0 {
|
||||||
return it.fmode
|
return it.fmode
|
||||||
}
|
}
|
||||||
|
@ -59,21 +47,35 @@ func (it *item) mode() os.FileMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
type fsStore struct {
|
type fsStore struct {
|
||||||
fs.FS
|
sync.RWMutex
|
||||||
|
fs fs.FS
|
||||||
storeRoot string
|
storeRoot string
|
||||||
s map[string]*item
|
s map[string]*item
|
||||||
}
|
}
|
||||||
|
|
||||||
type Store interface {
|
func (s *fsStore) get(key string) *item {
|
||||||
GetItem(key string, data interface{}) (Item, error)
|
s.RLock()
|
||||||
Get(key string, data interface{}) error
|
defer s.RUnlock()
|
||||||
Put(key string, version, minorVersion int, secretMode bool, data interface{}) (Item, error)
|
|
||||||
FlushAll() []error
|
i, ok := s.s[key]
|
||||||
Flush(key string) error
|
if !ok {
|
||||||
Shutdown()
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fsStore) put(key string, it *item) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.s[key] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fsStore) persist(it *item) error {
|
func (s *fsStore) persist(it *item) error {
|
||||||
|
it.Lock()
|
||||||
|
defer it.Unlock()
|
||||||
|
|
||||||
f, err := os.OpenFile(path.Join(s.storeRoot, it.Key), os.O_WRONLY|os.O_CREATE, it.mode())
|
f, err := os.OpenFile(path.Join(s.storeRoot, it.Key), os.O_WRONLY|os.O_CREATE, it.mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -93,19 +95,19 @@ func (s *fsStore) persist(it *item) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fsStore) Dirty(key string) error {
|
func (s *fsStore) Dirty(key string) error {
|
||||||
it, has := s.s[key]
|
it := s.get(key)
|
||||||
if !has {
|
if it == nil {
|
||||||
return ErrNoSuchKey
|
return ErrNoSuchKey
|
||||||
}
|
}
|
||||||
|
|
||||||
it.dirty = true
|
it.Dirty()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fsStore) Flush(key string) error {
|
func (s *fsStore) Flush(key string) error {
|
||||||
it, exists := s.s[key]
|
it := s.get(key)
|
||||||
if !exists {
|
if it == nil {
|
||||||
return ErrNoSuchKey
|
return ErrNoSuchKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +115,9 @@ func (s *fsStore) Flush(key string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fsStore) FlushAll() []error {
|
func (s *fsStore) FlushAll() []error {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, it := range s.s {
|
for _, it := range s.s {
|
||||||
err := s.persist(it)
|
err := s.persist(it)
|
||||||
|
@ -167,15 +172,21 @@ func (s *fsStore) Get(key string, data interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fsStore) GetItem(key string, data interface{}) (Item, error) {
|
func (s *fsStore) GetItem(key string, data interface{}) (Item, error) {
|
||||||
f, err := s.Open(key)
|
f, err := s.fs.Open(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
item := &item{
|
item := &item{
|
||||||
Data: data,
|
Data: data,
|
||||||
|
fmode: fi.Mode(),
|
||||||
}
|
}
|
||||||
d := json.NewDecoder(f)
|
d := json.NewDecoder(f)
|
||||||
err = d.Decode(item)
|
err = d.Decode(item)
|
||||||
|
@ -187,7 +198,7 @@ func (s *fsStore) GetItem(key string, data interface{}) (Item, error) {
|
||||||
return nil, fmt.Errorf("key mismatch '%s' != '%s'", item.Key, key)
|
return nil, fmt.Errorf("key mismatch '%s' != '%s'", item.Key, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.s[key] = item
|
s.put(key, item)
|
||||||
|
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
@ -197,7 +208,7 @@ func OpenFileStore(configRoot string) (*fsStore, error) {
|
||||||
stor := os.DirFS(storeRoot)
|
stor := os.DirFS(storeRoot)
|
||||||
|
|
||||||
return &fsStore{
|
return &fsStore{
|
||||||
FS: stor,
|
fs: stor,
|
||||||
storeRoot: storeRoot,
|
storeRoot: storeRoot,
|
||||||
s: make(map[string]*item),
|
s: make(map[string]*item),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
Loading…
Reference in a new issue