safing-portbase/database/registry.go
2018-09-06 19:06:13 +02:00

145 lines
2.7 KiB
Go

package database
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"sync"
"github.com/tevino/abool"
)
// RegisteredDatabase holds information about registered databases
type RegisteredDatabase struct {
Name string
Description string
StorageType string
PrimaryAPI string
}
// Equal returns whether this instance equals another.
func (r *RegisteredDatabase) Equal(o *RegisteredDatabase) bool {
if r.Name != o.Name ||
r.Description != o.Description ||
r.StorageType != o.StorageType ||
r.PrimaryAPI != o.PrimaryAPI {
return false
}
return true
}
const (
registryFileName = "databases.json"
)
var (
initialized = abool.NewBool(false)
registry map[string]*RegisteredDatabase
registryLock sync.Mutex
)
// RegisterDatabase registers a new database.
func RegisterDatabase(new *RegisteredDatabase) error {
if !initialized.IsSet() {
return errors.New("database not initialized")
}
registryLock.Lock()
defer registryLock.Unlock()
registeredDB, ok := registry[new.Name]
if !ok || !new.Equal(registeredDB) {
registry[new.Name] = new
return saveRegistry()
}
return nil
}
// Initialize initialized the database
func Initialize(location string) error {
if initialized.SetToIf(false, true) {
rootDir = location
err := checkRootDir()
if err != nil {
return fmt.Errorf("could not create/open database directory (%s): %s", rootDir, err)
}
err = loadRegistry()
if err != nil {
return fmt.Errorf("could not load database registry (%s): %s", path.Join(rootDir, registryFileName), err)
}
return nil
}
return errors.New("database already initialized")
}
func checkRootDir() error {
// open dir
dir, err := os.Open(rootDir)
if err != nil {
if err == os.ErrNotExist {
return os.MkdirAll(rootDir, 0700)
}
return err
}
defer dir.Close()
fileInfo, err := dir.Stat()
if err != nil {
return err
}
if fileInfo.Mode().Perm() != 0700 {
return dir.Chmod(0700)
}
return nil
}
func loadRegistry() error {
registryLock.Lock()
defer registryLock.Unlock()
// read file
filePath := path.Join(rootDir, registryFileName)
data, err := ioutil.ReadFile(filePath)
if err != nil {
if err == os.ErrNotExist {
registry = make(map[string]*RegisteredDatabase)
return nil
}
return err
}
// parse
new := make(map[string]*RegisteredDatabase)
err = json.Unmarshal(data, new)
if err != nil {
return err
}
// set
registry = new
return nil
}
func saveRegistry() error {
registryLock.Lock()
defer registryLock.Unlock()
// marshal
data, err := json.Marshal(registry)
if err != nil {
return err
}
// write file
filePath := path.Join(rootDir, registryFileName)
return ioutil.WriteFile(filePath, data, 0600)
}