feat: wrap commands to send info (#612)

Wrap commands in a better way to pass storage
This commit is contained in:
Henrique Dias 2019-01-07 20:24:23 +00:00 committed by GitHub
parent f396602084
commit 01ff03e426
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 170 additions and 212 deletions

View File

@ -15,18 +15,13 @@ var cmdsAddCmd = &cobra.Command{
Short: "Add a command to run on a specific event", Short: "Add a command to run on a specific event",
Long: `Add a command to run on a specific event.`, Long: `Add a command to run on a specific event.`,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
command := strings.Join(args[1:], " ") command := strings.Join(args[1:], " ")
s.Commands[args[0]] = append(s.Commands[args[0]], command) s.Commands[args[0]] = append(s.Commands[args[0]], command)
err = st.Settings.Save(s) err = d.store.Settings.Save(s)
checkErr(err) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, }, pythonConfig{}),
} }

View File

@ -14,11 +14,8 @@ var cmdsLsCmd = &cobra.Command{
Short: "List all commands for each event", Short: "List all commands for each event",
Long: `List all commands for each event.`, Long: `List all commands for each event.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
evt := mustGetString(cmd, "event") evt := mustGetString(cmd, "event")
@ -30,5 +27,5 @@ var cmdsLsCmd = &cobra.Command{
show["after_"+evt] = s.Commands["after_"+evt] show["after_"+evt] = s.Commands["after_"+evt]
printEvents(show) printEvents(show)
} }
}, }, pythonConfig{}),
} }

View File

@ -27,11 +27,8 @@ var cmdsRmCmd = &cobra.Command{
return nil return nil
}, },
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
evt := args[0] evt := args[0]
@ -44,8 +41,8 @@ var cmdsRmCmd = &cobra.Command{
} }
s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...) s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...)
err = st.Settings.Save(s) err = d.store.Settings.Save(s)
checkErr(err) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, }, pythonConfig{}),
} }

View File

@ -13,14 +13,11 @@ var configCatCmd = &cobra.Command{
Short: "Prints the configuration", Short: "Prints the configuration",
Long: `Prints the configuration.`, Long: `Prints the configuration.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
auther, err := st.Auth.Get(s.AuthMethod) auther, err := d.store.Auth.Get(s.AuthMethod)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, }, pythonConfig{}),
} }

View File

@ -1,15 +1,11 @@
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
v "github.com/spf13/viper"
) )
func init() { func init() {
@ -26,20 +22,11 @@ this options can be changed in the future with the command
to the defaults when creating new users and you don't to the defaults when creating new users and you don't
override the options.`, override the options.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
databasePath := v.GetString("database")
if _, err := os.Stat(databasePath); err == nil {
panic(errors.New(databasePath + " already exists"))
}
defaults := settings.UserDefaults{} defaults := settings.UserDefaults{}
getUserDefaults(cmd, &defaults, true) getUserDefaults(cmd, &defaults, true)
authMethod, auther := getAuthentication(cmd) authMethod, auther := getAuthentication(cmd)
db, err := storm.Open(databasePath)
checkErr(err)
defer db.Close()
st := getStorage(db)
s := &settings.Settings{ s := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit Key: generateRandomBytes(64), // 256 bit
Signup: mustGetBool(cmd, "signup"), Signup: mustGetBool(cmd, "signup"),
@ -53,9 +40,9 @@ override the options.`,
}, },
} }
err = st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
err = st.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
fmt.Printf(` fmt.Printf(`
@ -64,5 +51,5 @@ Now add your first user via 'filebrowser users new' and then you just
need to call the main command to boot up the server. need to call the main command to boot up the server.
`) `)
printSettings(s, auther) printSettings(s, auther)
}, }, pythonConfig{noDB: true}),
} }

View File

@ -19,12 +19,8 @@ var configSetCmd = &cobra.Command{
Long: `Updates the configuration. Set the flags for the options Long: `Updates the configuration. Set the flags for the options
you want to change.`, you want to change.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
hasAuth := false hasAuth := false
@ -50,15 +46,15 @@ you want to change.`,
var auther auth.Auther var auther auth.Auther
if hasAuth { if hasAuth {
s.AuthMethod, auther = getAuthentication(cmd) s.AuthMethod, auther = getAuthentication(cmd)
err = st.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
} else { } else {
auther, err = st.Auth.Get(s.AuthMethod) auther, err = d.store.Auth.Get(s.AuthMethod)
checkErr(err) checkErr(err)
} }
err = st.Settings.Save(s) err = d.store.Settings.Save(s)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, }, pythonConfig{}),
} }

View File

@ -12,7 +12,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
fbhttp "github.com/filebrowser/filebrowser/v2/http" fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
@ -91,81 +90,71 @@ set FB_DATABASE equals to the path.
Also, if the database path doesn't exist, File Browser will enter into Also, if the database path doesn't exist, File Browser will enter into
the quick setup mode and a new database will be bootstraped and a new the quick setup mode and a new database will be bootstraped and a new
user created with the credentials from options "username" and "password".`, user created with the credentials from options "username" and "password".`,
Run: serveAndListen, Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
switch logMethod := v.GetString("log"); logMethod {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: logMethod,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
if !d.hadDB {
quickSetup(d)
}
port := v.GetInt("port")
address := v.GetString("address")
cert := v.GetString("cert")
key := v.GetString("key")
scope := v.GetString("scope")
scope, err := filepath.Abs(scope)
checkErr(err)
settings, err := d.store.Settings.Get()
checkErr(err)
// Despite Base URL and Scope being "server" type of
// variables, we persist them to the database because
// they are needed during the execution and not only
// to start up the server.
settings.BaseURL = v.GetString("baseurl")
settings.Scope = scope
err = d.store.Settings.Save(settings)
checkErr(err)
handler, err := fbhttp.NewHandler(d.store)
checkErr(err)
var listener net.Listener
if key != "" && cert != "" {
cer, err := tls.LoadX509KeyPair(cert, key)
checkErr(err)
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config)
checkErr(err)
} else {
listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port))
checkErr(err)
}
log.Println("Listening on", listener.Addr().String())
if err := http.Serve(listener, handler); err != nil {
log.Fatal(err)
}
}, pythonConfig{noDB: true}),
} }
func serveAndListen(cmd *cobra.Command, args []string) { func quickSetup(d pythonData) {
switch logMethod := v.GetString("log"); logMethod {
case "stdout":
log.SetOutput(os.Stdout)
case "stderr":
log.SetOutput(os.Stderr)
case "":
log.SetOutput(ioutil.Discard)
default:
log.SetOutput(&lumberjack.Logger{
Filename: logMethod,
MaxSize: 100,
MaxAge: 14,
MaxBackups: 10,
})
}
if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) {
quickSetup(cmd)
}
db := getDB()
defer db.Close()
st := getStorage(db)
port := v.GetInt("port")
address := v.GetString("address")
cert := v.GetString("cert")
key := v.GetString("key")
scope := v.GetString("scope")
scope, err := filepath.Abs(scope)
checkErr(err)
settings, err := st.Settings.Get()
checkErr(err)
// Despite Base URL and Scope being "server" type of
// variables, we persist them to the database because
// they are needed during the execution and not only
// to start up the server.
settings.BaseURL = v.GetString("baseurl")
settings.Scope = scope
err = st.Settings.Save(settings)
checkErr(err)
handler, err := fbhttp.NewHandler(st)
checkErr(err)
var listener net.Listener
if key != "" && cert != "" {
cer, err := tls.LoadX509KeyPair(cert, key)
checkErr(err)
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config)
checkErr(err)
} else {
listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port))
checkErr(err)
}
log.Println("Listening on", listener.Addr().String())
if err := http.Serve(listener, handler); err != nil {
log.Fatal(err)
}
}
func quickSetup(cmd *cobra.Command) {
db, err := storm.Open(v.GetString("database"))
checkErr(err)
defer db.Close()
set := &settings.Settings{ set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit Key: generateRandomBytes(64), // 256 bit
BaseURL: v.GetString("baseurl"), BaseURL: v.GetString("baseurl"),
@ -187,12 +176,10 @@ func quickSetup(cmd *cobra.Command) {
}, },
} }
st := getStorage(db) err := d.store.Settings.Save(set)
err = st.Settings.Save(set)
checkErr(err) checkErr(err)
err = st.Auth.Save(&auth.JSONAuth{}) err = d.store.Auth.Save(&auth.JSONAuth{})
checkErr(err) checkErr(err)
username := v.GetString("username") username := v.GetString("username")
@ -216,7 +203,7 @@ func quickSetup(cmd *cobra.Command) {
set.Defaults.Apply(user) set.Defaults.Apply(user)
user.Perm.Admin = true user.Perm.Admin = true
err = st.Users.Save(user) err = d.store.Users.Save(user)
checkErr(err) checkErr(err)
} }

View File

@ -4,7 +4,6 @@ import (
"strconv" "strconv"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -32,7 +31,7 @@ var rulesRmCommand = &cobra.Command{
return nil return nil
}, },
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
i, err := strconv.Atoi(args[0]) i, err := strconv.Atoi(args[0])
checkErr(err) checkErr(err)
f := i f := i
@ -41,18 +40,18 @@ var rulesRmCommand = &cobra.Command{
checkErr(err) checkErr(err)
} }
user := func(u *users.User, st *storage.Storage) { user := func(u *users.User) {
u.Rules = append(u.Rules[:i], u.Rules[f+1:]...) u.Rules = append(u.Rules[:i], u.Rules[f+1:]...)
err := st.Users.Save(u) err := d.store.Users.Save(u)
checkErr(err) checkErr(err)
} }
global := func(s *settings.Settings, st *storage.Storage) { global := func(s *settings.Settings) {
s.Rules = append(s.Rules[:i], s.Rules[f+1:]...) s.Rules = append(s.Rules[:i], s.Rules[f+1:]...)
err := st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
} }
runRules(cmd, user, global) runRules(d.store, cmd, user, global)
}, }, pythonConfig{}),
} }

View File

@ -32,18 +32,14 @@ rules.`,
}, },
} }
func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), global func(*settings.Settings, *storage.Storage)) { func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User), global func(*settings.Settings)) {
db := getDB()
defer db.Close()
st := getStorage(db)
id := getUserIdentifier(cmd) id := getUserIdentifier(cmd)
if id != nil { if id != nil {
user, err := st.Users.Get("", id) user, err := st.Users.Get("", id)
checkErr(err) checkErr(err)
if users != nil { if users != nil {
users(user, st) users(user)
} }
printRules(user.Rules, id) printRules(user.Rules, id)
@ -54,7 +50,7 @@ func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), glo
checkErr(err) checkErr(err)
if global != nil { if global != nil {
global(settings, st) global(settings)
} }
printRules(settings.Rules, id) printRules(settings.Rules, id)

View File

@ -5,7 +5,6 @@ import (
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -21,7 +20,7 @@ var rulesAddCmd = &cobra.Command{
Short: "Add a global rule or user rule", Short: "Add a global rule or user rule",
Long: `Add a global rule or user rule.`, Long: `Add a global rule or user rule.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
allow := mustGetBool(cmd, "allow") allow := mustGetBool(cmd, "allow")
regex := mustGetBool(cmd, "regex") regex := mustGetBool(cmd, "regex")
exp := args[0] exp := args[0]
@ -41,18 +40,18 @@ var rulesAddCmd = &cobra.Command{
rule.Path = exp rule.Path = exp
} }
user := func(u *users.User, st *storage.Storage) { user := func(u *users.User) {
u.Rules = append(u.Rules, rule) u.Rules = append(u.Rules, rule)
err := st.Users.Save(u) err := d.store.Users.Save(u)
checkErr(err) checkErr(err)
} }
global := func(s *settings.Settings, st *storage.Storage) { global := func(s *settings.Settings) {
s.Rules = append(s.Rules, rule) s.Rules = append(s.Rules, rule)
err := st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
} }
runRules(cmd, user, global) runRules(d.store, cmd, user, global)
}, }, pythonConfig{}),
} }

View File

@ -13,7 +13,7 @@ var rulesLsCommand = &cobra.Command{
Short: "List global rules or user specific rules", Short: "List global rules or user specific rules",
Long: `List global rules or user specific rules.`, Long: `List global rules or user specific rules.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
runRules(cmd, nil, nil) runRules(d.store, cmd, nil, nil)
}, }, pythonConfig{}),
} }

View File

@ -15,12 +15,8 @@ var usersAddCmd = &cobra.Command{
Short: "Create a new user", Short: "Create a new user",
Long: `Create a new user and add it to the database.`, Long: `Create a new user and add it to the database.`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() s, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
s, err := st.Settings.Get()
checkErr(err) checkErr(err)
getUserDefaults(cmd, &s.Defaults, false) getUserDefaults(cmd, &s.Defaults, false)
@ -34,8 +30,8 @@ var usersAddCmd = &cobra.Command{
} }
s.Defaults.Apply(user) s.Defaults.Apply(user)
err = st.Users.Save(user) err = d.store.Users.Save(user)
checkErr(err) checkErr(err)
printUsers([]*users.User{user}) printUsers([]*users.User{user})
}, }, pythonConfig{}),
} }

View File

@ -25,11 +25,7 @@ var usersLsCmd = &cobra.Command{
Run: findUsers, Run: findUsers,
} }
var findUsers = func(cmd *cobra.Command, args []string) { var findUsers = python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB()
defer db.Close()
st := getStorage(db)
var ( var (
list []*users.User list []*users.User
user *users.User user *users.User
@ -39,16 +35,16 @@ var findUsers = func(cmd *cobra.Command, args []string) {
if len(args) == 1 { if len(args) == 1 {
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
if username != "" { if username != "" {
user, err = st.Users.Get("", username) user, err = d.store.Users.Get("", username)
} else { } else {
user, err = st.Users.Get("", id) user, err = d.store.Users.Get("", id)
} }
list = []*users.User{user} list = []*users.User{user}
} else { } else {
list, err = st.Users.Gets("") list, err = d.store.Users.Gets("")
} }
checkErr(err) checkErr(err)
printUsers(list) printUsers(list)
} }, pythonConfig{})

View File

@ -15,21 +15,17 @@ var usersRmCmd = &cobra.Command{
Short: "Delete a user by username or id", Short: "Delete a user by username or id",
Long: `Delete a user by username or id`, Long: `Delete a user by username or id`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB()
defer db.Close()
st := getStorage(db)
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
var err error var err error
if username != "" { if username != "" {
err = st.Users.Delete(username) err = d.store.Users.Delete(username)
} else { } else {
err = st.Users.Delete(id) err = d.store.Users.Delete(id)
} }
checkErr(err) checkErr(err)
fmt.Println("user deleted successfully") fmt.Println("user deleted successfully")
}, }, pythonConfig{}),
} }

View File

@ -20,12 +20,8 @@ var usersUpdateCmd = &cobra.Command{
Long: `Updates an existing user. Set the flags for the Long: `Updates an existing user. Set the flags for the
options you want to change.`, options you want to change.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
db := getDB() set, err := d.store.Settings.Get()
defer db.Close()
st := getStorage(db)
set, err := st.Settings.Get()
checkErr(err) checkErr(err)
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
@ -35,9 +31,9 @@ options you want to change.`,
var user *users.User var user *users.User
if id != 0 { if id != 0 {
user, err = st.Users.Get(set.Scope, id) user, err = d.store.Users.Get(set.Scope, id)
} else { } else {
user, err = st.Users.Get(set.Scope, username) user, err = d.store.Users.Get(set.Scope, username)
} }
checkErr(err) checkErr(err)
@ -68,8 +64,8 @@ options you want to change.`,
checkErr(err) checkErr(err)
} }
err = st.Users.Update(user) err = d.store.Users.Update(user)
checkErr(err) checkErr(err)
printUsers([]*users.User{user}) printUsers([]*users.User{user})
}, }, pythonConfig{}),
} }

View File

@ -2,7 +2,7 @@ package cmd
import ( import (
"crypto/rand" "crypto/rand"
"errors" "log"
"os" "os"
"github.com/asdine/storm" "github.com/asdine/storm"
@ -61,21 +61,6 @@ func mustGetUint(cmd *cobra.Command, flag string) uint {
return b return b
} }
func getDB() *storm.DB {
databasePath := v.GetString("database")
if _, err := os.Stat(databasePath); err != nil {
panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first."))
}
db, err := storm.Open(databasePath)
checkErr(err)
return db
}
func getStorage(db *storm.DB) *storage.Storage {
return bolt.NewStorage(db)
}
func generateRandomBytes(n int) []byte { func generateRandomBytes(n int) []byte {
b := make([]byte, n) b := make([]byte, n)
_, err := rand.Read(b) _, err := rand.Read(b)
@ -83,3 +68,42 @@ func generateRandomBytes(n int) []byte {
// Note that err == nil only if we read len(b) bytes. // Note that err == nil only if we read len(b) bytes.
return b return b
} }
type cobraFunc func(cmd *cobra.Command, args []string)
type pythonFunc func(cmd *cobra.Command, args []string, data pythonData)
type pythonConfig struct {
noDB bool
}
type pythonData struct {
hadDB bool
store *storage.Storage
}
func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return func(cmd *cobra.Command, args []string) {
data := pythonData{hadDB: true}
path := v.GetString("database")
_, err := os.Stat(path)
if os.IsNotExist(err) {
data.hadDB = false
if !cfg.noDB {
log.Fatal(path + " does not exid.store. Please run 'filebrowser config init' fird.store.")
}
} else if err != nil {
panic(err)
} else if err == nil && cfg.noDB {
log.Fatal(path + " already exists")
}
db, err := storm.Open(path)
checkErr(err)
defer db.Close()
data.store = bolt.NewStorage(db)
fn(cmd, args, data)
}
}