Skip to content

Commit

Permalink
Add backup command
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkovicmilos committed Oct 5, 2023
1 parent f4847b1 commit 3dadf2c
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 0 deletions.
12 changes: 12 additions & 0 deletions backup/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package backup

import "github.com/gnolang/tx-archive/log"

type Option func(s *Service)

Check failure on line 5 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service

Check failure on line 5 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service

Check failure on line 5 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service

// WithLogger specifies the logger for the backup service
func WithLogger(l log.Logger) Option {
return func(s *Service) {

Check failure on line 9 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service (typecheck)

Check failure on line 9 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service (typecheck)

Check failure on line 9 in backup/options.go

View workflow job for this annotation

GitHub Actions / Go Linter / lint

undefined: Service (typecheck)
s.logger = l
}
}
Binary file added build/archive
Binary file not shown.
178 changes: 178 additions & 0 deletions cmd/backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package main

import (
"context"
"errors"
"flag"
"fmt"
"os"

"github.com/gnolang/tx-archive/backup"
"github.com/gnolang/tx-archive/backup/client/http"
"github.com/peterbourgon/ff/v3/ffcli"

Check failure on line 12 in cmd/backup.go

View workflow job for this annotation

GitHub Actions / Go Test / test-with-race

missing go.sum entry for module providing package github.com/peterbourgon/ff/v3/ffcli (imported by github.com/gnolang/tx-archive/cmd); to add:

Check failure on line 12 in cmd/backup.go

View workflow job for this annotation

GitHub Actions / Go Test / test

missing go.sum entry for module providing package github.com/peterbourgon/ff/v3/ffcli (imported by github.com/gnolang/tx-archive/cmd); to add:
"go.uber.org/zap"

Check failure on line 13 in cmd/backup.go

View workflow job for this annotation

GitHub Actions / Go Test / test-with-race

no required module provides package go.uber.org/zap; to add it:

Check failure on line 13 in cmd/backup.go

View workflow job for this annotation

GitHub Actions / Go Test / test

no required module provides package go.uber.org/zap; to add it:
)

const (
defaultOutputPath = "./backup.jsonl"
defaultFromBlock = 1
defaultToBlock = -1 // no limit

defaultRemoteAddress = "http://127.0.0.1:26657"
)

var (
errInvalidOutputLocation = errors.New("invalid output location")
errOutputFileExists = errors.New("output file exists")
errInvalidRemote = errors.New("invalid remote address")
)

// backupCfg is the backup command configuration
type backupCfg struct {
outputPath string
remote string

toBlock int64 // < 0 means there is no right bound
fromBlock uint64

overwrite bool
}

// newBackupCmd creates the backup command
func newBackupCmd() *ffcli.Command {
cfg := &backupCfg{}

fs := flag.NewFlagSet("backup", flag.ExitOnError)
cfg.registerFlags(fs)

return &ffcli.Command{
Name: "backup",
ShortUsage: "backup [flags]",
LongHelp: "Runs the chain backup service",
FlagSet: fs,
Exec: cfg.exec,
}
}

// registerFlags registers the backup command flags
func (c *backupCfg) registerFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.outputPath,
"output-path",
defaultOutputPath,
"the output path for the JSONL chain data",
)

fs.StringVar(
&c.remote,
"remote",
defaultRemoteAddress,
"the JSON-RPC URL of the chain to be backed up",
)

fs.Int64Var(
&c.toBlock,
"to-block",
defaultToBlock,
"the end block number for the backup (inclusive). If <0, latest chain height is used",
)

fs.Uint64Var(
&c.fromBlock,
"from-block",
defaultFromBlock,
"the starting block number for the backup (inclusive)",
)

fs.BoolVar(
&c.overwrite,
"overwrite",
false,
"flag indicating if the output file should be overwritten during backup",
)
}

// exec executes the backup command
func (c *backupCfg) exec(ctx context.Context, _ []string) error {
// Make sure the remote address is set
if c.remote == "" {
return errInvalidRemote
}

// Make sure the output file path is valid
if c.outputPath == "" {
return errInvalidOutputLocation
}

// Make sure the output file can be overwritten, if it exists
if _, err := os.Stat(c.outputPath); err == nil && !c.overwrite {
// File already exists, and the overwrite flag is not set
return errOutputFileExists
}

// Set up the config
cfg := backup.DefaultConfig()
cfg.FromBlock = c.fromBlock

if c.toBlock >= 0 {
to64 := uint64(c.toBlock)
cfg.ToBlock = &to64
}

// Set up the client
client := http.NewClient(c.remote)

// Set up the logger
zapLogger, loggerErr := zap.NewDevelopment()
if loggerErr != nil {
return fmt.Errorf("unable to create logger, %w", loggerErr)
}

logger := newCommandLogger(zapLogger)

// Set up the writer (file)
// Open the file for writing
outputFile, openErr := os.OpenFile(
c.outputPath,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0o755,
)
if openErr != nil {
return fmt.Errorf("unable to open file %s, %w", c.outputPath, openErr)
}

closeFile := func() error {
if err := outputFile.Close(); err != nil {
logger.Error("unable to close output file", "err", err.Error())

return err
}

return nil
}

teardown := func() {
if err := closeFile(); err != nil {
if removeErr := os.Remove(outputFile.Name()); removeErr != nil {
logger.Error("unable to remove file", "err", err.Error())
}
}
}

// Set up the teardown
defer teardown()

// Create the backup service
service := backup.NewService(
client,
outputFile,
backup.WithLogger(logger),
)

// Run the backup service
if backupErr := service.ExecuteBackup(ctx, cfg); backupErr != nil {
return fmt.Errorf("unable to execute backup, %w", backupErr)
}

return nil
}
43 changes: 43 additions & 0 deletions cmd/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import "go.uber.org/zap"

type cmdLogger struct {
logger *zap.Logger
}

func newCommandLogger(logger *zap.Logger) *cmdLogger {
return &cmdLogger{
logger: logger,
}
}

func (c *cmdLogger) Info(msg string, args ...interface{}) {
if len(args) == 0 {
c.logger.Info(msg)

return
}

c.logger.Info(msg, zap.Any("args", args))
}

func (c *cmdLogger) Debug(msg string, args ...interface{}) {
if len(args) == 0 {
c.logger.Debug(msg)

return
}

c.logger.Debug(msg, zap.Any("args", args))
}

func (c *cmdLogger) Error(msg string, args ...interface{}) {
if len(args) == 0 {
c.logger.Error(msg)

return
}

c.logger.Error(msg, zap.Any("args", args))
}
36 changes: 36 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"context"
"flag"
"fmt"
"os"

"github.com/peterbourgon/ff/v3/ffcli"
)

func main() {
fs := flag.NewFlagSet("root", flag.ExitOnError)

// Create the root command
cmd := &ffcli.Command{
ShortUsage: "<subcommand> [flags] [<arg>...]",
LongHelp: "The archive command for Gno / TM2 chains",
FlagSet: fs,
Exec: func(_ context.Context, _ []string) error {
return flag.ErrHelp
},
}

// Add the subcommands
cmd.Subcommands = []*ffcli.Command{
newBackupCmd(),
// newRestoreCmd(),
}

if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%+v", err)

os.Exit(1)
}
}

0 comments on commit 3dadf2c

Please sign in to comment.