Add bolt store
This commit is contained in:
parent
6d904c724b
commit
bf9f8d80cd
@ -18,7 +18,6 @@ import (
|
|||||||
"gitea.benny.dog/torjus/ezshare/config"
|
"gitea.benny.dog/torjus/ezshare/config"
|
||||||
"gitea.benny.dog/torjus/ezshare/pb"
|
"gitea.benny.dog/torjus/ezshare/pb"
|
||||||
"gitea.benny.dog/torjus/ezshare/server"
|
"gitea.benny.dog/torjus/ezshare/server"
|
||||||
"gitea.benny.dog/torjus/ezshare/store"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
@ -137,7 +136,12 @@ func ActionServe(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStore := store.NewMemoryFileStore()
|
// Setup store
|
||||||
|
s, closeFunc, err := cfg.Server.StoreConfig.GetStore()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to initialize store: %w", err)
|
||||||
|
}
|
||||||
|
defer closeFunc()
|
||||||
// Setup shutdown-handling
|
// Setup shutdown-handling
|
||||||
rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
defer rootCancel()
|
defer rootCancel()
|
||||||
@ -157,7 +161,7 @@ func ActionServe(c *cli.Context) error {
|
|||||||
grpcAddr = c.String("grpc-addr")
|
grpcAddr = c.String("grpc-addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
grpcFileServer := server.NewGRPCFileServiceServer(fileStore)
|
grpcFileServer := server.NewGRPCFileServiceServer(s)
|
||||||
if c.IsSet("hostname") {
|
if c.IsSet("hostname") {
|
||||||
grpcFileServer.Hostname = c.String("hostname")
|
grpcFileServer.Hostname = c.String("hostname")
|
||||||
}
|
}
|
||||||
@ -215,7 +219,7 @@ func ActionServe(c *cli.Context) error {
|
|||||||
if c.IsSet("http-addr") {
|
if c.IsSet("http-addr") {
|
||||||
httpAddr = c.String("http-addr")
|
httpAddr = c.String("http-addr")
|
||||||
}
|
}
|
||||||
httpServer := server.NewHTTPSever(fileStore)
|
httpServer := server.NewHTTPSever(s)
|
||||||
httpServer.Addr = httpAddr
|
httpServer.Addr = httpAddr
|
||||||
|
|
||||||
// wait for cancel
|
// wait for cancel
|
||||||
|
@ -10,7 +10,9 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.benny.dog/torjus/ezshare/store"
|
||||||
"github.com/pelletier/go-toml"
|
"github.com/pelletier/go-toml"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
@ -26,23 +28,33 @@ type CertificatePaths struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
LogLevel string `toml:"LogLevel"`
|
LogLevel string `toml:"LogLevel"`
|
||||||
Hostname string `toml:"Hostname"`
|
Hostname string `toml:"Hostname"`
|
||||||
GRPC *ServerGRPCConfig `toml:"GRPC"`
|
StoreConfig *ServerStoreConfig `toml:"Store"`
|
||||||
HTTP *ServerHTTPConfig `toml:"HTTP"`
|
GRPC *ServerGRPCConfig `toml:"GRPC"`
|
||||||
|
HTTP *ServerHTTPConfig `toml:"HTTP"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerStoreConfig struct {
|
type ServerStoreConfig struct {
|
||||||
Type string `toml:"Type"`
|
Type string `toml:"Type"`
|
||||||
FSStoreConfig *FSStoreConfig `toml:"Filesystem"`
|
FSStoreConfig *FSStoreConfig `toml:"Filesystem"`
|
||||||
|
BoltStoreConfig *BoltStoreConfig `toml:"Bolt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BoltStoreConfig struct {
|
||||||
|
Path string `toml:"Path"`
|
||||||
|
}
|
||||||
|
|
||||||
type FSStoreConfig struct {
|
type FSStoreConfig struct {
|
||||||
Dir string `toml:"Dir"`
|
Dir string `toml:"Dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerGRPCConfig struct {
|
type ServerGRPCConfig struct {
|
||||||
ListenAddr string `toml:"ListenAddr"`
|
ListenAddr string `toml:"ListenAddr"`
|
||||||
CACerts *CertificatePaths `toml:"CACerts"`
|
CACerts *CertificatePaths `toml:"CACerts"`
|
||||||
Certs *CertificatePaths `toml:"Certs"`
|
Certs *CertificatePaths `toml:"Certs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerHTTPConfig struct {
|
type ServerHTTPConfig struct {
|
||||||
ListenAddr string `toml:"ListenAddr"`
|
ListenAddr string `toml:"ListenAddr"`
|
||||||
}
|
}
|
||||||
@ -213,3 +225,24 @@ func (c *Config) ToDefaultFile() error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("config-file already exists")
|
return fmt.Errorf("config-file already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *ServerStoreConfig) GetStore() (store.FileStore, func() error, error) {
|
||||||
|
nopCloseFunc := func() error { return nil }
|
||||||
|
if strings.EqualFold(sc.Type, "bolt") {
|
||||||
|
s, err := store.NewBoltStore(sc.BoltStoreConfig.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return s, s.Close, err
|
||||||
|
|
||||||
|
}
|
||||||
|
if strings.EqualFold(sc.Type, "filesystem") {
|
||||||
|
s := store.NewFileSystemStore(sc.FSStoreConfig.Dir)
|
||||||
|
return s, nopCloseFunc, nil
|
||||||
|
}
|
||||||
|
if strings.EqualFold(sc.Type, "memory") {
|
||||||
|
return store.NewMemoryFileStore(), nopCloseFunc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("invalid store config")
|
||||||
|
}
|
||||||
|
@ -15,10 +15,15 @@ Hostname = "localhost"
|
|||||||
# Storage configuration
|
# Storage configuration
|
||||||
[Server.Store]
|
[Server.Store]
|
||||||
# How server stores file
|
# How server stores file
|
||||||
# Must be one of: filesystem, memory
|
# Must be one of: filesystem, memory, bolt
|
||||||
# Required
|
# Required
|
||||||
Type = "filesystem"
|
Type = "filesystem"
|
||||||
|
|
||||||
|
[Server.Store.Bolt]
|
||||||
|
# Where the bolt-db is stored
|
||||||
|
# Required if store-type is bolt
|
||||||
|
Path = ""
|
||||||
|
|
||||||
[Server.Store.Filesystem]
|
[Server.Store.Filesystem]
|
||||||
# Where files are stored
|
# Where files are stored
|
||||||
# Required if store-type is filesystem
|
# Required if store-type is filesystem
|
||||||
|
1
go.mod
1
go.mod
@ -7,6 +7,7 @@ require (
|
|||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/pelletier/go-toml v1.9.4
|
github.com/pelletier/go-toml v1.9.4
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
|
go.etcd.io/bbolt v1.3.6
|
||||||
google.golang.org/grpc v1.42.0
|
google.golang.org/grpc v1.42.0
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
)
|
)
|
||||||
|
3
go.sum
3
go.sum
@ -70,6 +70,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
|
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||||
|
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
@ -97,6 +99,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
90
store/bolt.go
Normal file
90
store/bolt.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.benny.dog/torjus/ezshare/pb"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
bolt "go.etcd.io/bbolt"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BoltStore struct {
|
||||||
|
db *bolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
var bktKey = []byte("files")
|
||||||
|
|
||||||
|
func NewBoltStore(path string) (*BoltStore, error) {
|
||||||
|
s := &BoltStore{}
|
||||||
|
db, err := bolt.Open(path, 0660, &bolt.Options{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open store: %w", err)
|
||||||
|
}
|
||||||
|
s.db = db
|
||||||
|
err = db.Update(func(t *bolt.Tx) error {
|
||||||
|
if _, err := t.CreateBucketIfNotExists(bktKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create bucket: %w", err)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) Close() error {
|
||||||
|
return s.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) GetFile(id string) (*pb.File, error) {
|
||||||
|
var file pb.File
|
||||||
|
var data []byte
|
||||||
|
if err := s.db.View(func(t *bolt.Tx) error {
|
||||||
|
bkt := t.Bucket(bktKey)
|
||||||
|
data = bkt.Get([]byte(id))
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting file: %w", err)
|
||||||
|
}
|
||||||
|
if data == nil {
|
||||||
|
return nil, ErrNoSuchFile
|
||||||
|
}
|
||||||
|
|
||||||
|
err := proto.Unmarshal(data, &file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) StoreFile(file *pb.File) (string, error) {
|
||||||
|
file.FileId = uuid.Must(uuid.NewRandom()).String()
|
||||||
|
data, err := proto.Marshal(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to marshal file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.db.Update(func(t *bolt.Tx) error {
|
||||||
|
bkt := t.Bucket(bktKey)
|
||||||
|
return bkt.Put([]byte(file.FileId), data)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to store file: %w", err)
|
||||||
|
}
|
||||||
|
return file.FileId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) DeleteFile(id string) error {
|
||||||
|
return s.db.Update(func(t *bolt.Tx) error {
|
||||||
|
bkt := t.Bucket(bktKey)
|
||||||
|
data := bkt.Get([]byte(id))
|
||||||
|
if data == nil {
|
||||||
|
return ErrNoSuchFile
|
||||||
|
}
|
||||||
|
return bkt.Delete([]byte(id))
|
||||||
|
})
|
||||||
|
}
|
18
store/bolt_test.go
Normal file
18
store/bolt_test.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.benny.dog/torjus/ezshare/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBoltStore(t *testing.T) {
|
||||||
|
path := filepath.Join(t.TempDir(), "boltstore.db")
|
||||||
|
s, err := store.NewBoltStore(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error opening store: %s", err)
|
||||||
|
}
|
||||||
|
doFileStoreTest(s, t)
|
||||||
|
s.Close()
|
||||||
|
}
|
@ -1,21 +1,13 @@
|
|||||||
package store_test
|
package store_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.benny.dog/torjus/ezshare/store"
|
"gitea.benny.dog/torjus/ezshare/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFileStore(t *testing.T) {
|
func TestFileStore(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir(os.TempDir(), "ezshare-test")
|
dir := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create temp-dir")
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = os.RemoveAll(dir)
|
|
||||||
}()
|
|
||||||
|
|
||||||
s := store.NewFileSystemStore(dir)
|
s := store.NewFileSystemStore(dir)
|
||||||
doFileStoreTest(s, t)
|
doFileStoreTest(s, t)
|
||||||
|
Loading…
Reference in New Issue
Block a user