forked from cerc-io/ipld-eth-server
249 lines
6.3 KiB
Go
249 lines
6.3 KiB
Go
package fsrepo
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/ipfs/go-ipfs/repo"
|
|
|
|
ds "github.com/ipfs/go-datastore"
|
|
"github.com/ipfs/go-datastore/mount"
|
|
dssync "github.com/ipfs/go-datastore/sync"
|
|
"github.com/ipfs/go-ds-measure"
|
|
)
|
|
|
|
// ConfigFromMap creates a new datastore config from a map
|
|
type ConfigFromMap func(map[string]interface{}) (DatastoreConfig, error)
|
|
|
|
// DatastoreConfig is an abstraction of a datastore config. A "spec"
|
|
// is first converted to a DatastoreConfig and then Create() is called
|
|
// to instantiate a new datastore
|
|
type DatastoreConfig interface {
|
|
// DiskSpec returns a minimal configuration of the datastore
|
|
// represting what is stored on disk. Run time values are
|
|
// excluded.
|
|
DiskSpec() DiskSpec
|
|
|
|
// Create instantiate a new datastore from this config
|
|
Create(path string) (repo.Datastore, error)
|
|
}
|
|
|
|
// DiskSpec is a minimal representation of the characteristic values of the
|
|
// datastore. If two diskspecs are the same, the loader assumes that they refer
|
|
// to exactly the same datastore. If they differ at all, it is assumed they are
|
|
// completely different datastores and a migration will be performed. Runtime
|
|
// values such as cache options or concurrency options should not be added
|
|
// here.
|
|
type DiskSpec map[string]interface{}
|
|
|
|
// Bytes returns a minimal JSON encoding of the DiskSpec
|
|
func (spec DiskSpec) Bytes() []byte {
|
|
b, err := json.Marshal(spec)
|
|
if err != nil {
|
|
// should not happen
|
|
panic(err)
|
|
}
|
|
return bytes.TrimSpace(b)
|
|
}
|
|
|
|
// String returns a minimal JSON encoding of the DiskSpec
|
|
func (spec DiskSpec) String() string {
|
|
return string(spec.Bytes())
|
|
}
|
|
|
|
var datastores map[string]ConfigFromMap
|
|
|
|
func init() {
|
|
datastores = map[string]ConfigFromMap{
|
|
"mount": MountDatastoreConfig,
|
|
"mem": MemDatastoreConfig,
|
|
"log": LogDatastoreConfig,
|
|
"measure": MeasureDatastoreConfig,
|
|
}
|
|
}
|
|
|
|
func AddDatastoreConfigHandler(name string, dsc ConfigFromMap) error {
|
|
_, ok := datastores[name]
|
|
if ok {
|
|
return fmt.Errorf("already have a datastore named %q", name)
|
|
}
|
|
|
|
datastores[name] = dsc
|
|
return nil
|
|
}
|
|
|
|
// AnyDatastoreConfig returns a DatastoreConfig from a spec based on
|
|
// the "type" parameter
|
|
func AnyDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) {
|
|
which, ok := params["type"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("'type' field missing or not a string")
|
|
}
|
|
fun, ok := datastores[which]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown datastore type: %s", which)
|
|
}
|
|
return fun(params)
|
|
}
|
|
|
|
type mountDatastoreConfig struct {
|
|
mounts []premount
|
|
}
|
|
|
|
type premount struct {
|
|
ds DatastoreConfig
|
|
prefix ds.Key
|
|
}
|
|
|
|
// MountDatastoreConfig returns a mount DatastoreConfig from a spec
|
|
func MountDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) {
|
|
var res mountDatastoreConfig
|
|
mounts, ok := params["mounts"].([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("'mounts' field is missing or not an array")
|
|
}
|
|
for _, iface := range mounts {
|
|
cfg, ok := iface.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected map for mountpoint")
|
|
}
|
|
|
|
child, err := AnyDatastoreConfig(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prefix, found := cfg["mountpoint"]
|
|
if !found {
|
|
return nil, fmt.Errorf("no 'mountpoint' on mount")
|
|
}
|
|
|
|
res.mounts = append(res.mounts, premount{
|
|
ds: child,
|
|
prefix: ds.NewKey(prefix.(string)),
|
|
})
|
|
}
|
|
sort.Slice(res.mounts,
|
|
func(i, j int) bool {
|
|
return res.mounts[i].prefix.String() > res.mounts[j].prefix.String()
|
|
})
|
|
|
|
return &res, nil
|
|
}
|
|
|
|
func (c *mountDatastoreConfig) DiskSpec() DiskSpec {
|
|
cfg := map[string]interface{}{"type": "mount"}
|
|
mounts := make([]interface{}, len(c.mounts))
|
|
for i, m := range c.mounts {
|
|
c := m.ds.DiskSpec()
|
|
if c == nil {
|
|
c = make(map[string]interface{})
|
|
}
|
|
c["mountpoint"] = m.prefix.String()
|
|
mounts[i] = c
|
|
}
|
|
cfg["mounts"] = mounts
|
|
return cfg
|
|
}
|
|
|
|
func (c *mountDatastoreConfig) Create(path string) (repo.Datastore, error) {
|
|
mounts := make([]mount.Mount, len(c.mounts))
|
|
for i, m := range c.mounts {
|
|
ds, err := m.ds.Create(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mounts[i].Datastore = ds
|
|
mounts[i].Prefix = m.prefix
|
|
}
|
|
return mount.New(mounts), nil
|
|
}
|
|
|
|
type memDatastoreConfig struct {
|
|
cfg map[string]interface{}
|
|
}
|
|
|
|
// MemDatastoreConfig returns a memory DatastoreConfig from a spec
|
|
func MemDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) {
|
|
return &memDatastoreConfig{params}, nil
|
|
}
|
|
|
|
func (c *memDatastoreConfig) DiskSpec() DiskSpec {
|
|
return nil
|
|
}
|
|
|
|
func (c *memDatastoreConfig) Create(string) (repo.Datastore, error) {
|
|
return dssync.MutexWrap(ds.NewMapDatastore()), nil
|
|
}
|
|
|
|
type logDatastoreConfig struct {
|
|
child DatastoreConfig
|
|
name string
|
|
}
|
|
|
|
// LogDatastoreConfig returns a log DatastoreConfig from a spec
|
|
func LogDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) {
|
|
childField, ok := params["child"].(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("'child' field is missing or not a map")
|
|
}
|
|
child, err := AnyDatastoreConfig(childField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name, ok := params["name"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("'name' field was missing or not a string")
|
|
}
|
|
return &logDatastoreConfig{child, name}, nil
|
|
|
|
}
|
|
|
|
func (c *logDatastoreConfig) Create(path string) (repo.Datastore, error) {
|
|
child, err := c.child.Create(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ds.NewLogDatastore(child, c.name), nil
|
|
}
|
|
|
|
func (c *logDatastoreConfig) DiskSpec() DiskSpec {
|
|
return c.child.DiskSpec()
|
|
}
|
|
|
|
type measureDatastoreConfig struct {
|
|
child DatastoreConfig
|
|
prefix string
|
|
}
|
|
|
|
// MeasureDatastoreConfig returns a measure DatastoreConfig from a spec
|
|
func MeasureDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) {
|
|
childField, ok := params["child"].(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("'child' field is missing or not a map")
|
|
}
|
|
child, err := AnyDatastoreConfig(childField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prefix, ok := params["prefix"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("'prefix' field was missing or not a string")
|
|
}
|
|
return &measureDatastoreConfig{child, prefix}, nil
|
|
}
|
|
|
|
func (c *measureDatastoreConfig) DiskSpec() DiskSpec {
|
|
return c.child.DiskSpec()
|
|
}
|
|
|
|
func (c measureDatastoreConfig) Create(path string) (repo.Datastore, error) {
|
|
child, err := c.child.Create(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return measure.New(c.prefix, child), nil
|
|
}
|