Closes #1206 This can cause Anubis to have other issues, but at the very least these issues are at the Anubis level, not the level of your target service so it's less bad.
94 lines
2.2 KiB
Go
94 lines
2.2 KiB
Go
package bbolt
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/TecharoHQ/anubis/lib/store"
|
|
"go.etcd.io/bbolt"
|
|
)
|
|
|
|
var (
|
|
ErrMissingPath = errors.New("bbolt: path is missing from config")
|
|
ErrCantWriteToPath = errors.New("bbolt: can't write to path")
|
|
)
|
|
|
|
func init() {
|
|
store.Register("bbolt", Factory{})
|
|
}
|
|
|
|
// Factory builds new instances of the bbolt storage backend according to
|
|
// configuration passed via a json.RawMessage.
|
|
type Factory struct{}
|
|
|
|
// Build parses and validates the bbolt storage backend Config and creates
|
|
// a new instance of it.
|
|
func (Factory) Build(ctx context.Context, data json.RawMessage) (store.Interface, error) {
|
|
var config Config
|
|
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
|
return nil, fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
if err := config.Valid(); err != nil {
|
|
return nil, fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
bdb, err := bbolt.Open(config.Path, 0600, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't open bbolt database %s: %w", config.Path, err)
|
|
}
|
|
|
|
result := &Store{
|
|
bdb: bdb,
|
|
}
|
|
|
|
go result.cleanupThread(ctx)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Valid parses and validates the bbolt store Config or returns
|
|
// an error.
|
|
func (Factory) Valid(data json.RawMessage) error {
|
|
var config Config
|
|
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
|
return fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
if err := config.Valid(); err != nil {
|
|
return fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Config is the bbolt storage backend configuration.
|
|
type Config struct {
|
|
// Path is the filesystem path of the database. The folder must be writable to Anubis.
|
|
Path string `json:"path"`
|
|
}
|
|
|
|
// Valid validates the configuration including checking if its containing folder is writable.
|
|
func (c Config) Valid() error {
|
|
var errs []error
|
|
|
|
if c.Path == "" {
|
|
errs = append(errs, ErrMissingPath)
|
|
} else {
|
|
dir := filepath.Dir(c.Path)
|
|
if err := os.WriteFile(filepath.Join(dir, ".test-file"), []byte(""), 0600); err != nil {
|
|
errs = append(errs, ErrCantWriteToPath)
|
|
}
|
|
os.Remove(filepath.Join(dir, ".test-file"))
|
|
}
|
|
|
|
if len(errs) != 0 {
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
return nil
|
|
}
|