Remove fuse

This commit is contained in:
Ben Johnson
2020-12-17 15:15:01 -07:00
parent bbcdb30cb3
commit b00095ccf5
13 changed files with 187 additions and 1116 deletions

View File

@@ -1,6 +1,7 @@
package main
import (
"context"
"encoding/json"
"errors"
"flag"
@@ -8,137 +9,116 @@ import (
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"bazil.org/fuse"
"bazil.org/fuse/fs"
"github.com/benbjohnson/litestream"
)
func main() {
// Setup signal handler.
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() { <-c; cancel() }()
// Initialize program and read flags/config.
m := NewMain()
if err := m.Run(os.Args[1:]); err == flag.ErrHelp {
if err := m.ParseFlags(os.Args[1:]); err == flag.ErrHelp {
os.Exit(1)
} else if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// Start monitoring databases.
if err := m.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// Wait for signal to stop program.
<-ctx.Done()
signal.Reset()
// Gracefully close
if err := m.Close(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
type Main struct {
logger *log.Logger
TargetPath string
Path string
ConfigPath string
Config Config
}
func NewMain() *Main {
return &Main{
logger: log.New(ioutil.Discard, "", log.LstdFlags),
}
return &Main{}
}
func (m *Main) Run(args []string) (err error) {
flagSet := flag.NewFlagSet("litestream", flag.ContinueOnError)
flagSet.StringVar(&m.TargetPath, "target", "", "target directory")
verbose := flagSet.Bool("v", false, "verbose")
flagSet.Usage = m.usage
if err := flagSet.Parse(args); err != nil {
// ParseFlags parses the flag set from args & loads the configuration.
func (m *Main) ParseFlags(ctx context.Context, args []string) (err error) {
fs := flag.NewFlagSet("litestream", flag.ContinueOnError)
fs.StringVar(&m.ConfigPath, "config", "", "configuration path")
fs.Usage = m.usage
if err := fs.Parse(args); err != nil {
return err
}
// Ensure mount path is specified.
if flagSet.NArg() > 1 {
return errors.New("too many arguments, only specify mount path")
} else if m.Path = flagSet.Arg(0); m.Path == "" {
return errors.New("mount path required")
}
// Ensure mount path exists & is a directory.
if fi, err := os.Stat(m.Path); err != nil {
// Load configuration.
if m.ConfigPath == "" {
return errors.New("-config required")
} else if m.Config, err = ReadConfigFile(m.ConfigPath); err != nil {
return err
} else if !fi.IsDir() {
return fmt.Errorf("mount path must be a directory")
}
// If no target is specified, default to a hidden directory based on the mount path.
if m.TargetPath == "" {
m.TargetPath = filepath.Join(filepath.Dir(m.Path), "."+filepath.Base(m.Path))
return nil
}
if err := m.ensureTargetPath(); err != nil {
// Run loads all databases specified in the configuration.
func (m *Main) Run(ctx context.Context) (err error) {
if len(m.Config.DBs) == 0 {
return errors.New("configuration must specify at least one database")
}
for _, dbc := range m.Config.DBs {
db := litestream.NewDB()
db.Path = dbc.Path
if err := db.Open(); err != nil {
return err
}
m.DBs = append(m.DBs, db)
}
// Setup logging, if verbose specified.
var config fs.Config
if *verbose {
config.Debug = debug
m.logger = log.New(os.Stderr, "", log.LstdFlags)
}
// Mount FUSE filesystem.
conn, err := fuse.Mount(m.Path)
if err != nil {
return err
}
defer fuse.Unmount(m.Path)
defer conn.Close()
m.logger.Printf("mounted %s; target=%s", m.Path, m.TargetPath)
fileSystem := litestream.NewFileSystem(m.TargetPath)
if err := fileSystem.Open(); err != nil {
return err
}
defer fileSystem.Close()
s := fs.New(conn, &config)
return s.Serve(fileSystem)
return nil
}
func (m *Main) ensureTargetPath() error {
// Check if target path exists, exit if it does.
if _, err := os.Stat(m.TargetPath); err == nil {
return nil
} else if err != nil && !os.IsNotExist(err) {
return err
// Close closes all open databases.
func (m *Main) Close() (err error) {
for _, db := range m.DBs {
if e := db.Close(); e != nil {
log.Printf("error closing db: path=%s err=%s", db.Path, e)
if err == nil {
err = e
}
}
}
// Create target path with the same permissions as the mount path.
fi, err := os.Stat(m.Path)
if err != nil {
return err
}
return os.Mkdir(m.TargetPath, fi.Mode())
return err
}
func (m *Main) usage() {
func (m *Main) Usage() {
fmt.Println(`
Litestream is a FUSE file system that replicates SQLite databases.
Litestream is a daemon for replicating SQLite databases.
Usage:
litestream [arguments] PATH
litestream [arguments]
Arguments:
-target PATH
Specifies the directory to store data.
Defaults to a hidden directory next to PATH.
-v
Enable verbose logging.
-config PATH
Specifies the configuration file. Required.
`[1:])
}
// debug is a function that can be used for fs.Config.Debug.
// It marshals the msg to JSON and prints to the log.
func debug(msg interface{}) {
buf, err := json.Marshal(msg)
if err != nil {
println("debug: marshal error: %v", err)
return
}
log.Print("DEBUG ", string(buf))
}