init
This commit is contained in:
12
go.mod
Normal file
12
go.mod
Normal file
@@ -0,0 +1,12 @@
|
||||
module code.uint32.ru/tiny/objstore
|
||||
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/nats-io/nats.go v1.41.2
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
)
|
22
go.sum
Normal file
22
go.sum
Normal file
@@ -0,0 +1,22 @@
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
|
||||
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nats.go v1.41.2 h1:5UkfLAtu/036s99AhFRlyNDI1Ieylb36qbGjJzHixos=
|
||||
github.com/nats-io/nats.go v1.41.2/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
71
internal/filesystem/fs_storage.go
Normal file
71
internal/filesystem/fs_storage.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func Open(path string) (*Storage, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil, fmt.Errorf("provided path %s is not a directory", path)
|
||||
}
|
||||
|
||||
abs, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not tarnslate %s to absolute path", path)
|
||||
}
|
||||
|
||||
return &Storage{prefix: abs}, nil
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (s *Storage) Save(key string, data []byte) error {
|
||||
path := s.toAbs(key)
|
||||
|
||||
if err := os.WriteFile(path, data, 0664); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) Load(key string) ([]byte, error) {
|
||||
path := s.toAbs(key)
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Storage) Delete(key string) error {
|
||||
path := s.toAbs(key)
|
||||
|
||||
err := os.Remove(path)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) toAbs(path string) string {
|
||||
return filepath.Join(s.prefix, path)
|
||||
}
|
45
internal/filesystem/fs_storage_test.go
Normal file
45
internal/filesystem/fs_storage_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStorageMethods(t *testing.T) {
|
||||
st, err := Open("./testdata")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
name := "mytestfile"
|
||||
data := []byte("contents of my test file")
|
||||
|
||||
defer os.Remove(name) // just in case
|
||||
|
||||
if err := st.Save(name, data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := st.Save(name, data); err != nil {
|
||||
t.Errorf("rewrite operatoin failed: %v", err)
|
||||
}
|
||||
|
||||
b, err := st.Load(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(data, b) {
|
||||
t.Error("loaded file differs from original")
|
||||
}
|
||||
|
||||
if err := st.Delete(name); err != nil {
|
||||
t.Errorf("delete failed: %v", err)
|
||||
}
|
||||
|
||||
if err := st.Delete(name); err != nil {
|
||||
t.Errorf("delete of non-existent failed: %v", err)
|
||||
}
|
||||
|
||||
}
|
69
internal/natsobj/nats_store.go
Normal file
69
internal/natsobj/nats_store.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package natsobj
|
||||
|
||||
import (
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
store nats.ObjectStore
|
||||
conn *nats.Conn
|
||||
}
|
||||
|
||||
func Open(bucket, url string) (*Storage, error) {
|
||||
nc, err := nats.Connect(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js, err := nc.JetStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &nats.ObjectStoreConfig{
|
||||
Bucket: bucket,
|
||||
Description: "microkv bucket",
|
||||
MaxBytes: -1,
|
||||
Storage: nats.FileStorage,
|
||||
Compression: true,
|
||||
}
|
||||
|
||||
store, err := js.CreateObjectStore(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st := &Storage{store: store, conn: nc}
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (n *Storage) Save(key string, data []byte) error {
|
||||
if _, err := n.store.PutBytes(key, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Storage) Load(key string) ([]byte, error) {
|
||||
b, err := n.store.GetBytes(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (n *Storage) Delete(key string) error {
|
||||
if err := n.store.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Storage) Close() error {
|
||||
n.conn.Close()
|
||||
|
||||
return nil
|
||||
}
|
38
storage.go
Normal file
38
storage.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package microkv
|
||||
|
||||
import (
|
||||
"code.uint32.ru/tiny/objstore/internal/filesystem"
|
||||
"code.uint32.ru/tiny/objstore/internal/natsobj"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Storage = (*natsobj.Storage)(nil)
|
||||
_ Storage = (*filesystem.Storage)(nil)
|
||||
)
|
||||
|
||||
// Storage is a very basic object store.
|
||||
type Storage interface {
|
||||
// Save puts file with name 'key' into the store. If a file with such name
|
||||
// already exists it gets overwritten.
|
||||
Save(key string, data []byte) error
|
||||
// Load returns contents of file named 'key'.
|
||||
Load(key string) ([]byte, error)
|
||||
// Delete removes file named 'key' from the store.
|
||||
// If such file does not exist Delete returns nil.
|
||||
Delete(key string) error
|
||||
// Close must be called when you're done working with Storage.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// NewNats connects to NATS messaging system and tries to create
|
||||
// a new object storage with name 'bucket'. The returned Storage
|
||||
// uses the created bucket as underlying physical store.
|
||||
func NewNats(bucket string, url string) (Storage, error) {
|
||||
return natsobj.Open(bucket, url)
|
||||
}
|
||||
|
||||
// NewFS established a key/value within the directory 'path'
|
||||
// and uses is as underlying physical store.
|
||||
func NewFS(path string) (Storage, error) {
|
||||
return filesystem.Open(path)
|
||||
}
|
Reference in New Issue
Block a user