Full node metadata backup
This commit is contained in:
parent
5c33982f72
commit
e444977891
@ -475,6 +475,12 @@ type FullNode interface {
|
|||||||
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||||
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
||||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error)
|
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error)
|
||||||
|
|
||||||
|
// CreateBackup creates node backup onder the specified file name. The
|
||||||
|
// method requires that the lotus daemon is running with the
|
||||||
|
// LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that
|
||||||
|
// the path specified when calling CreateBackup is within the base path
|
||||||
|
CreateBackup(ctx context.Context, fpath string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileRef struct {
|
type FileRef struct {
|
||||||
|
@ -239,6 +239,8 @@ type FullNodeStruct struct {
|
|||||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*api.VoucherCreateResult, error) `perm:"sign"`
|
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*api.VoucherCreateResult, error) `perm:"sign"`
|
||||||
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
||||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
|
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
|
||||||
|
|
||||||
|
CreateBackup func(ctx context.Context, fpath string) error `perm:"admin"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,6 +1029,10 @@ func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Addr
|
|||||||
return c.Internal.PaychVoucherSubmit(ctx, ch, sv, secret, proof)
|
return c.Internal.PaychVoucherSubmit(ctx, ch, sv, secret, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FullNodeStruct) CreateBackup(ctx context.Context, fpath string) error {
|
||||||
|
return c.Internal.CreateBackup(ctx, fpath)
|
||||||
|
}
|
||||||
|
|
||||||
// StorageMinerStruct
|
// StorageMinerStruct
|
||||||
|
|
||||||
func (c *StorageMinerStruct) ActorAddress(ctx context.Context) (address.Address, error) {
|
func (c *StorageMinerStruct) ActorAddress(ctx context.Context) (address.Address, error) {
|
||||||
|
125
cli/backup.go
Normal file
125
cli/backup.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/lib/backupds"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BackupAPI interface {
|
||||||
|
CreateBackup(ctx context.Context, fpath string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BackupApiFn func(ctx *cli.Context) (BackupAPI, jsonrpc.ClientCloser, error)
|
||||||
|
|
||||||
|
func BackupCmd(repoFlag string, rt repo.RepoType, getApi BackupApiFn) *cli.Command {
|
||||||
|
var offlineBackup = func(cctx *cli.Context) error {
|
||||||
|
logging.SetLogLevel("badger", "ERROR") // nolint:errcheck
|
||||||
|
|
||||||
|
repoPath := cctx.String(repoFlag)
|
||||||
|
r, err := repo.NewFS(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := r.Exists()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return xerrors.Errorf("repo at '%s' is not initialized", cctx.String(repoFlag))
|
||||||
|
}
|
||||||
|
|
||||||
|
lr, err := r.LockRO(rt)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("locking repo: %w", err)
|
||||||
|
}
|
||||||
|
defer lr.Close() // nolint:errcheck
|
||||||
|
|
||||||
|
mds, err := lr.Datastore("/metadata")
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting metadata datastore: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bds := backupds.Wrap(mds)
|
||||||
|
|
||||||
|
fpath, err := homedir.Expand(cctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("expanding file path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("opening backup file %s: %w", fpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bds.Backup(out); err != nil {
|
||||||
|
if cerr := out.Close(); cerr != nil {
|
||||||
|
log.Errorw("error closing backup file while handling backup error", "closeErr", cerr, "backupErr", err)
|
||||||
|
}
|
||||||
|
return xerrors.Errorf("backup error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := out.Close(); err != nil {
|
||||||
|
return xerrors.Errorf("closing backup file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineBackup = func(cctx *cli.Context) error {
|
||||||
|
api, closer, err := getApi(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting api: %w (if the node isn't running you can use the --offline flag)", err)
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
err = api.CreateBackup(ReqContext(cctx), cctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Success")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "backup",
|
||||||
|
Usage: "Create node metadata backup",
|
||||||
|
Description: `The backup command writes a copy of node metadata under the specified path
|
||||||
|
|
||||||
|
Online backups:
|
||||||
|
For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set
|
||||||
|
to a path where backup files are supposed to be saved, and the path specified in
|
||||||
|
this command must be within this base path`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "offline",
|
||||||
|
Usage: "create backup without the node running",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ArgsUsage: "[backup file path]",
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
if cctx.Args().Len() != 1 {
|
||||||
|
return xerrors.Errorf("expected 1 argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cctx.Bool("offline") {
|
||||||
|
return offlineBackup(cctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return onlineBackup(cctx)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,115 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
logging "github.com/ipfs/go-log/v2"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"golang.org/x/xerrors"
|
|
||||||
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
|
|
||||||
lcli "github.com/filecoin-project/lotus/cli"
|
lcli "github.com/filecoin-project/lotus/cli"
|
||||||
"github.com/filecoin-project/lotus/lib/backupds"
|
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var backupCmd = &cli.Command{
|
var backupCmd = lcli.BackupCmd(FlagMinerRepo, repo.StorageMiner, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
|
||||||
Name: "backup",
|
return lcli.GetStorageMinerAPI(cctx)
|
||||||
Usage: "Create node metadata backup",
|
})
|
||||||
Description: `The backup command writes a copy of node metadata under the specified path
|
|
||||||
|
|
||||||
Online backups:
|
|
||||||
For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set
|
|
||||||
to a path where backup files are supposed to be saved, and the path specified in
|
|
||||||
this command must be within this base path`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "offline",
|
|
||||||
Usage: "create backup without the node running",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ArgsUsage: "[backup file path]",
|
|
||||||
Action: func(cctx *cli.Context) error {
|
|
||||||
if cctx.Args().Len() != 1 {
|
|
||||||
return xerrors.Errorf("expected 1 argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cctx.Bool("offline") {
|
|
||||||
return offlineBackup(cctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return onlineBackup(cctx)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func offlineBackup(cctx *cli.Context) error {
|
|
||||||
logging.SetLogLevel("badger", "ERROR") // nolint:errcheck
|
|
||||||
|
|
||||||
repoPath := cctx.String(FlagMinerRepo)
|
|
||||||
r, err := repo.NewFS(repoPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err := r.Exists()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return xerrors.Errorf("repo at '%s' is not initialized", cctx.String(FlagMinerRepo))
|
|
||||||
}
|
|
||||||
|
|
||||||
lr, err := r.LockRO(repo.StorageMiner)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("locking repo: %w", err)
|
|
||||||
}
|
|
||||||
defer lr.Close() // nolint:errcheck
|
|
||||||
|
|
||||||
mds, err := lr.Datastore("/metadata")
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("getting metadata datastore: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bds := backupds.Wrap(mds)
|
|
||||||
|
|
||||||
fpath, err := homedir.Expand(cctx.Args().First())
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("expanding file path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("opening backup file %s: %w", fpath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bds.Backup(out); err != nil {
|
|
||||||
if cerr := out.Close(); cerr != nil {
|
|
||||||
log.Errorw("error closing backup file while handling backup error", "closeErr", cerr, "backupErr", err)
|
|
||||||
}
|
|
||||||
return xerrors.Errorf("backup error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := out.Close(); err != nil {
|
|
||||||
return xerrors.Errorf("closing backup file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func onlineBackup(cctx *cli.Context) error {
|
|
||||||
api, closer, err := lcli.GetStorageMinerAPI(cctx)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("getting api: %w (if the node isn't running you can use the --offline flag)", err)
|
|
||||||
}
|
|
||||||
defer closer()
|
|
||||||
|
|
||||||
err = api.CreateBackup(lcli.ReqContext(cctx), cctx.Args().First())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Success")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
14
cmd/lotus/backup.go
Normal file
14
cmd/lotus/backup.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
|
|
||||||
|
lcli "github.com/filecoin-project/lotus/cli"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var backupCmd = lcli.BackupCmd("repo", repo.FullNode, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
|
||||||
|
return lcli.GetFullNodeAPI(cctx)
|
||||||
|
})
|
@ -22,6 +22,7 @@ func main() {
|
|||||||
|
|
||||||
local := []*cli.Command{
|
local := []*cli.Command{
|
||||||
DaemonCmd,
|
DaemonCmd,
|
||||||
|
backupCmd,
|
||||||
}
|
}
|
||||||
if AdvanceBlockCmd != nil {
|
if AdvanceBlockCmd != nil {
|
||||||
local = append(local, AdvanceBlockCmd)
|
local = append(local, AdvanceBlockCmd)
|
||||||
|
67
node/impl/backup.go
Normal file
67
node/impl/backup.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/lib/backupds"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func backup(mds dtypes.MetadataDS, fpath string) error {
|
||||||
|
bb, ok := os.LookupEnv("LOTUS_BACKUP_BASE_PATH")
|
||||||
|
if !ok {
|
||||||
|
return xerrors.Errorf("LOTUS_BACKUP_BASE_PATH env var not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
bds, ok := mds.(*backupds.Datastore)
|
||||||
|
if !ok {
|
||||||
|
return xerrors.Errorf("expected a backup datastore")
|
||||||
|
}
|
||||||
|
|
||||||
|
bb, err := homedir.Expand(bb)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("expanding base path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bb, err = filepath.Abs(bb)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting absolute base path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fpath, err = homedir.Expand(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("expanding file path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fpath, err = filepath.Abs(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting absolute file path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(fpath, bb) {
|
||||||
|
return xerrors.Errorf("backup file name (%s) must be inside base path (%s)", fpath, bb)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("open %s: %w", fpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bds.Backup(out); err != nil {
|
||||||
|
if cerr := out.Close(); cerr != nil {
|
||||||
|
log.Errorw("error closing backup file while handling backup error", "closeErr", cerr, "backupErr", err)
|
||||||
|
}
|
||||||
|
return xerrors.Errorf("backup error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := out.Close(); err != nil {
|
||||||
|
return xerrors.Errorf("closing backup file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package impl
|
package impl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
logging "github.com/ipfs/go-log/v2"
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
@ -9,6 +11,7 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/node/impl/full"
|
"github.com/filecoin-project/lotus/node/impl/full"
|
||||||
"github.com/filecoin-project/lotus/node/impl/market"
|
"github.com/filecoin-project/lotus/node/impl/market"
|
||||||
"github.com/filecoin-project/lotus/node/impl/paych"
|
"github.com/filecoin-project/lotus/node/impl/paych"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.Logger("node")
|
var log = logging.Logger("node")
|
||||||
@ -26,6 +29,12 @@ type FullNodeAPI struct {
|
|||||||
full.WalletAPI
|
full.WalletAPI
|
||||||
full.SyncAPI
|
full.SyncAPI
|
||||||
full.BeaconAPI
|
full.BeaconAPI
|
||||||
|
|
||||||
|
DS dtypes.MetadataDS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *FullNodeAPI) CreateBackup(ctx context.Context, fpath string) error {
|
||||||
|
return backup(n.DS, fpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ api.FullNode = &FullNodeAPI{}
|
var _ api.FullNode = &FullNodeAPI{}
|
||||||
|
@ -5,14 +5,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/libp2p/go-libp2p-core/host"
|
"github.com/libp2p/go-libp2p-core/host"
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
@ -34,11 +31,9 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/api/apistruct"
|
"github.com/filecoin-project/lotus/api/apistruct"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/lib/backupds"
|
|
||||||
"github.com/filecoin-project/lotus/miner"
|
"github.com/filecoin-project/lotus/miner"
|
||||||
"github.com/filecoin-project/lotus/node/impl/common"
|
"github.com/filecoin-project/lotus/node/impl/common"
|
||||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
|
||||||
"github.com/filecoin-project/lotus/storage"
|
"github.com/filecoin-project/lotus/storage"
|
||||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||||
)
|
)
|
||||||
@ -61,8 +56,7 @@ type StorageMinerAPI struct {
|
|||||||
DataTransfer dtypes.ProviderDataTransfer
|
DataTransfer dtypes.ProviderDataTransfer
|
||||||
Host host.Host
|
Host host.Host
|
||||||
|
|
||||||
DS dtypes.MetadataDS
|
DS dtypes.MetadataDS
|
||||||
Repo repo.LockedRepo
|
|
||||||
|
|
||||||
ConsiderOnlineStorageDealsConfigFunc dtypes.ConsiderOnlineStorageDealsConfigFunc
|
ConsiderOnlineStorageDealsConfigFunc dtypes.ConsiderOnlineStorageDealsConfigFunc
|
||||||
SetConsiderOnlineStorageDealsConfigFunc dtypes.SetConsiderOnlineStorageDealsConfigFunc
|
SetConsiderOnlineStorageDealsConfigFunc dtypes.SetConsiderOnlineStorageDealsConfigFunc
|
||||||
@ -525,58 +519,7 @@ func (sm *StorageMinerAPI) PiecesGetCIDInfo(ctx context.Context, payloadCid cid.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StorageMinerAPI) CreateBackup(ctx context.Context, fpath string) error {
|
func (sm *StorageMinerAPI) CreateBackup(ctx context.Context, fpath string) error {
|
||||||
// TODO: Config
|
return backup(sm.DS, fpath)
|
||||||
bb, ok := os.LookupEnv("LOTUS_BACKUP_BASE_PATH")
|
|
||||||
if !ok {
|
|
||||||
return xerrors.Errorf("LOTUS_BACKUP_BASE_PATH env var not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
bds, ok := sm.DS.(*backupds.Datastore)
|
|
||||||
if !ok {
|
|
||||||
return xerrors.Errorf("expected a backup datastore")
|
|
||||||
}
|
|
||||||
|
|
||||||
bb, err := homedir.Expand(bb)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("expanding base path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bb, err = filepath.Abs(bb)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("getting absolute base path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fpath, err = homedir.Expand(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("expanding file path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fpath, err = filepath.Abs(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("getting absolute file path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(fpath, bb) {
|
|
||||||
return xerrors.Errorf("backup file name (%s) must be inside base path (%s)", fpath, bb)
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("open %s: %w", fpath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bds.Backup(out); err != nil {
|
|
||||||
if cerr := out.Close(); cerr != nil {
|
|
||||||
log.Errorw("error closing backup file while handling backup error", "closeErr", cerr, "backupErr", err)
|
|
||||||
}
|
|
||||||
return xerrors.Errorf("backup error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := out.Close(); err != nil {
|
|
||||||
return xerrors.Errorf("closing backup file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ api.StorageMiner = &StorageMinerAPI{}
|
var _ api.StorageMiner = &StorageMinerAPI{}
|
||||||
|
Loading…
Reference in New Issue
Block a user