Skip to content

Commit

Permalink
feat: add 'berty sql dump' command
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Aug 21, 2018
1 parent e4d39ce commit 0df13b0
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 15 deletions.
31 changes: 16 additions & 15 deletions core/cmd/berty/daemon.go
Expand Up @@ -13,6 +13,7 @@ import (
reuse "github.com/libp2p/go-reuseport"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
Expand All @@ -31,10 +32,10 @@ import (
)

type daemonOptions struct {
sql sqlOptions

bind string
hideBanner bool
sqlPath string
sqlKey string
bootstrap []string
dropDatabase bool
initOnly bool
Expand All @@ -44,28 +45,28 @@ type daemonOptions struct {
logP2PSubsytem []string
}

func newDaemonCommand() *cobra.Command {
opts := &daemonOptions{}
cmd := &cobra.Command{
Use: "daemon",
RunE: func(cmd *cobra.Command, args []string) error {
return daemon(opts)
},
}

flags := cmd.Flags()
func daemonSetupFlags(flags *pflag.FlagSet, opts *daemonOptions) {
flags.BoolVar(&opts.dropDatabase, "drop-database", false, "drop database to force a reinitialization")
flags.BoolVar(&opts.hideBanner, "hide-banner", false, "hide banner")
flags.BoolVar(&opts.initOnly, "init-only", false, "stop after node initialization (useful for integration tests")
flags.BoolVar(&opts.noP2P, "no-p2p", false, "Disable p2p Drier")
flags.StringVarP(&opts.bind, "bind", "b", ":1337", "gRPC listening address")
flags.StringVarP(&opts.sqlKey, "sql-key", "", "s3cur3", "sqlcipher database encryption key")
flags.StringVarP(&opts.sqlPath, "sql-path", "", "/tmp/berty.db", "sqlcipher database path")
flags.StringSliceVarP(&opts.bootstrap, "bootstrap", "", []string{}, "boostrap peers")
flags.StringSliceVarP(&opts.bindP2P, "bind-p2p", "", []string{"/ip4/0.0.0.0/tcp/0"}, "p2p listening address")
flags.StringVarP(&opts.logP2PLevel, "log-p2p-level", "", "", "Enable log on libp2p (can be 'critical', 'error', 'warning', 'notice', 'info', 'debug')")
flags.StringSliceVarP(&opts.logP2PSubsytem, "log-p2p-subsystem", "", []string{"*"}, "log libp2p specific subsystem")
}

func newDaemonCommand() *cobra.Command {
opts := &daemonOptions{}
cmd := &cobra.Command{
Use: "daemon",
RunE: func(cmd *cobra.Command, args []string) error {
return daemon(opts)
},
}
sqlSetupFlags(cmd.Flags(), &opts.sql)
daemonSetupFlags(cmd.Flags(), opts)
return cmd
}

Expand All @@ -92,7 +93,7 @@ func daemon(opts *daemonOptions) error {
}

// initialize sql
db, err := sqlcipher.Open(opts.sqlPath, []byte(opts.sqlKey))
db, err := sqlcipher.Open(opts.sql.path, []byte(opts.sql.key))
if err != nil {
return errors.Wrap(err, "failed to open sqlcipher")
}
Expand Down
1 change: 1 addition & 0 deletions core/cmd/berty/root.go
Expand Up @@ -23,6 +23,7 @@ func newRootCommand() *cobra.Command {
cmd.AddCommand(
newDaemonCommand(),
newClientCommand(),
newSQLCommand(),
)
return cmd
}
Expand Down
25 changes: 25 additions & 0 deletions core/cmd/berty/sql.go
@@ -0,0 +1,25 @@
package main

import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

type sqlOptions struct {
path string
key string
}

func sqlSetupFlags(flags *pflag.FlagSet, opts *sqlOptions) {
flags.StringVarP(&opts.key, "sql-key", "", "s3cur3", "sqlcipher database encryption key")
flags.StringVarP(&opts.path, "sql-path", "", "/tmp/berty.db", "sqlcipher database path")
}

func newSQLCommand() *cobra.Command {
// sql (root)
cmd := &cobra.Command{
Use: "sql",
}
cmd.AddCommand(newSQLDumpCommand())
return cmd
}
96 changes: 96 additions & 0 deletions core/cmd/berty/sql_dump.go
@@ -0,0 +1,96 @@
package main

import (
"encoding/json"
"fmt"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/berty/berty/core/api/p2p"
"github.com/berty/berty/core/entity"
"github.com/berty/berty/core/sql"
"github.com/berty/berty/core/sql/sqlcipher"
)

type sqlDumpOptions struct {
sql sqlOptions
autoPreload bool
}

func sqlDumpSetupFlags(flags *pflag.FlagSet, opts *sqlDumpOptions) {
flags.BoolVar(&opts.autoPreload, "auto-preload", false, "preload associations")
}

func newSQLDumpCommand() *cobra.Command {
// sql dump
opts := &sqlDumpOptions{}
cmd := &cobra.Command{
Use: "dump",
RunE: func(cmd *cobra.Command, args []string) error {
return sqlDump(opts)
},
}
sqlDumpSetupFlags(cmd.Flags(), opts)
sqlSetupFlags(cmd.Flags(), &opts.sql)
return cmd
}

func sqlDump(opts *sqlDumpOptions) error {
// initialize sql
db, err := sqlcipher.Open(opts.sql.path, []byte(opts.sql.key))
if err != nil {
return errors.Wrap(err, "failed to open sqlcipher")
}
defer db.Close()
if db, err = sql.Init(db); err != nil {
return errors.Wrap(err, "failed to initialize sql")
}

db = db.Set("gorm:auto_preload", opts.autoPreload)

dump := struct {
Config entity.Config `json:"config"`
Contacts []entity.Contact `json:"contacts"`
Events []p2p.Event `json:"events"`
Conversations []entity.Conversation `json:"conversations"`
ConversationMembers []entity.ConversationMember `json:"conversation_members"`
Devices []entity.Device `json:"devices"`
}{
Contacts: []entity.Contact{},
Events: []p2p.Event{},
Conversations: []entity.Conversation{},
ConversationMembers: []entity.ConversationMember{},
Devices: []entity.Device{},
}

// fetch entities
if err := db.Set("gorm:auto_preload", false).Find(&dump.Config).Error; err != nil {
return err
}
if err := db.Find(&dump.Contacts).Error; err != nil {
return err
}
if err := db.Find(&dump.Conversations).Error; err != nil {
return err
}
if err := db.Find(&dump.ConversationMembers).Error; err != nil {
return err
}
if err := db.Find(&dump.Devices).Error; err != nil {
return err
}
if err := db.Find(&dump.Events).Error; err != nil {
return err
}

// rendering
out, err := json.MarshalIndent(dump, "", " ")
if err != nil {
return errors.Wrap(err, "failed to marshal the dump")
}
fmt.Println(string(out))

return nil
}

0 comments on commit 0df13b0

Please sign in to comment.