lotus/cmd/lotus-provider/migrate.go

223 lines
6.1 KiB
Go
Raw Normal View History

2023-11-07 04:49:42 +00:00
package main
import (
"bytes"
"context"
2023-11-09 00:01:44 +00:00
"encoding/base64"
"errors"
2023-11-07 04:49:42 +00:00
"fmt"
2023-11-07 05:13:12 +00:00
"os"
"path"
2023-11-07 04:49:42 +00:00
"strings"
"github.com/BurntSushi/toml"
2023-11-15 12:50:31 +00:00
"github.com/ipfs/go-datastore"
2023-11-09 00:01:44 +00:00
"github.com/samber/lo"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
2023-11-15 12:50:31 +00:00
"github.com/filecoin-project/go-address"
2023-11-07 04:49:42 +00:00
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
"github.com/filecoin-project/lotus/node/config"
2023-11-09 00:01:44 +00:00
"github.com/filecoin-project/lotus/node/modules"
2023-11-07 04:49:42 +00:00
"github.com/filecoin-project/lotus/node/repo"
)
2023-11-09 00:01:44 +00:00
var configMigrateCmd = &cli.Command{
Name: "from-miner",
Description: "Express a database config (for lotus-provider) from an existing miner.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: FlagMinerRepo,
Aliases: []string{FlagMinerRepoDeprecation},
EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"},
Value: "~/.lotusminer",
Usage: fmt.Sprintf("Specify miner repo path. flag(%s) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON", FlagMinerRepoDeprecation),
},
&cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Hidden: true,
Value: "~/.lotus",
},
2023-11-09 00:01:44 +00:00
&cli.StringFlag{
Name: "to-layer",
Aliases: []string{"t"},
2023-11-09 00:01:44 +00:00
Usage: "The layer name for this data push. 'base' is recommended for single-miner setup.",
},
&cli.BoolFlag{
Name: "replace",
Aliases: []string{"r"},
Usage: "Use this with --to-layer to replace an existing layer",
2023-11-07 04:49:42 +00:00
},
},
2023-11-09 00:01:44 +00:00
Action: fromMiner,
2023-11-07 04:49:42 +00:00
}
const (
FlagMinerRepo = "miner-repo"
)
const FlagMinerRepoDeprecation = "storagerepo"
2023-11-09 00:01:44 +00:00
func fromMiner(cctx *cli.Context) (err error) {
2023-11-07 04:49:42 +00:00
ctx := context.Background()
r, err := repo.NewFS(cctx.String(FlagMinerRepo))
if err != nil {
return err
}
ok, err := r.Exists()
if err != nil {
return err
}
if !ok {
return fmt.Errorf("repo not initialized")
}
lr, err := r.LockRO(repo.StorageMiner)
if err != nil {
return fmt.Errorf("locking repo: %w", err)
}
defer func() { _ = lr.Close() }()
cfgNode, err := lr.Config()
if err != nil {
return fmt.Errorf("getting node config: %w", err)
}
smCfg := cfgNode.(*config.StorageMiner)
db, err := harmonydb.NewFromConfig(smCfg.HarmonyDB)
if err != nil {
2023-11-07 05:13:12 +00:00
return fmt.Errorf("could not reach the database. Ensure the Miner config toml's HarmonyDB entry"+
" is setup to reach Yugabyte correctly: %w", err)
2023-11-07 04:49:42 +00:00
}
var titles []string
2023-11-09 00:01:44 +00:00
err = db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`)
2023-11-07 04:49:42 +00:00
if err != nil {
return fmt.Errorf("miner cannot reach the db. Ensure the config toml's HarmonyDB entry"+
" is setup to reach Yugabyte correctly: %s", err.Error())
}
name := cctx.String("to-layer")
2023-11-07 04:49:42 +00:00
if name == "" {
name = fmt.Sprintf("mig%d", len(titles))
2023-11-09 00:01:44 +00:00
} else {
if lo.Contains(titles, name) && !cctx.Bool("overwrite") {
return errors.New("the overwrite flag is needed to replace existing layer: " + name)
}
2023-11-07 04:49:42 +00:00
}
msg := "Layer " + name + ` created. `
2023-11-07 05:13:12 +00:00
// Copy over identical settings:
buf, err := os.ReadFile(path.Join(lr.Path(), "config.toml"))
if err != nil {
return fmt.Errorf("could not read config.toml: %w", err)
2023-11-07 04:49:42 +00:00
}
var lpCfg config.LotusProviderConfig
2023-11-07 05:13:12 +00:00
_, err = toml.Decode(string(buf), &lpCfg)
2023-11-07 04:49:42 +00:00
if err != nil {
return fmt.Errorf("could not decode toml: %w", err)
}
// Populate Miner Address
mmeta, err := lr.Datastore(ctx, "/metadata")
2023-11-07 04:49:42 +00:00
if err != nil {
return xerrors.Errorf("opening miner metadata datastore: %w", err)
2023-11-07 04:49:42 +00:00
}
defer mmeta.Close()
maddrBytes, err := mmeta.Get(ctx, datastore.NewKey("miner-address"))
if err != nil {
return xerrors.Errorf("getting miner address datastore entry: %w", err)
}
addr, err := address.NewFromBytes(maddrBytes)
2023-11-07 04:49:42 +00:00
if err != nil {
return xerrors.Errorf("parsing miner actor address: %w", err)
2023-11-07 04:49:42 +00:00
}
lpCfg.Addresses.MinerAddresses = []string{addr.String()}
2023-11-09 00:01:44 +00:00
ks, err := lr.KeyStore()
if err != nil {
return xerrors.Errorf("keystore err: %w", err)
}
js, err := ks.Get(modules.JWTSecretName)
if err != nil {
return xerrors.Errorf("error getting JWTSecretName: %w", err)
}
2023-11-14 21:33:08 +00:00
lpCfg.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(js.PrivateKey)
2023-11-07 04:49:42 +00:00
// Populate API Key
_, header, err := cliutil.GetRawAPI(cctx, repo.FullNode, "v0")
if err != nil {
2023-11-07 05:13:12 +00:00
return fmt.Errorf("cannot read API: %w", err)
2023-11-07 04:49:42 +00:00
}
2023-11-14 21:33:08 +00:00
ainfo, err := cliutil.GetAPIInfo(&cli.Context{}, repo.FullNode)
if err != nil {
return xerrors.Errorf("could not get API info for FullNode: %w", err)
}
lpCfg.Apis.ChainApiInfo = []string{header.Get("Authorization")[7:] + ":" + ainfo.Addr}
2023-11-07 04:49:42 +00:00
2023-11-07 05:13:12 +00:00
// Enable WindowPoSt
2023-11-07 04:49:42 +00:00
lpCfg.Subsystems.EnableWindowPost = true
2023-11-09 00:01:44 +00:00
msg += `\nBefore running lotus-provider, ensure any miner/worker answering of WindowPost is disabled by
(on Miner) DisableBuiltinWindowPoSt=true and (on Workers) not enabling windowpost on CLI or via
environment variable LOTUS_WORKER_WINDOWPOST.
`
2023-11-07 04:49:42 +00:00
2023-11-07 05:13:12 +00:00
// Express as configTOML
2023-11-07 04:49:42 +00:00
configTOML := &bytes.Buffer{}
2023-11-07 05:13:12 +00:00
if err = toml.NewEncoder(configTOML).Encode(lpCfg); err != nil {
2023-11-07 04:49:42 +00:00
return err
}
if !lo.Contains(titles, "base") {
_, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ('base', '')")
2023-11-07 04:49:42 +00:00
if err != nil {
return err
}
}
2023-11-09 00:01:44 +00:00
2023-11-07 04:49:42 +00:00
_, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", name, configTOML.String())
if err != nil {
return err
}
dbSettings := ""
def := config.DefaultStorageMiner().HarmonyDB
if def.Hosts[0] != smCfg.HarmonyDB.Hosts[0] {
dbSettings += ` --db-host="` + strings.Join(smCfg.HarmonyDB.Hosts, ",") + `"`
}
if def.Port != smCfg.HarmonyDB.Port {
dbSettings += " --db-port=" + smCfg.HarmonyDB.Port
}
if def.Username != smCfg.HarmonyDB.Username {
dbSettings += ` --db-user="` + smCfg.HarmonyDB.Username + `"`
}
if def.Password != smCfg.HarmonyDB.Password {
dbSettings += ` --db-password="` + smCfg.HarmonyDB.Password + `"`
}
if def.Database != smCfg.HarmonyDB.Database {
dbSettings += ` --db-name="` + smCfg.HarmonyDB.Database + `"`
}
msg += `
To work with the config:
./lotus-provider ` + dbSettings + ` config help `
msg += `
To run Lotus Provider: in its own machine or cgroup without other files, use the command:
./lotus-provider ` + dbSettings + ` run --layers="` + name + `"
`
fmt.Println(msg)
return nil
}