2023-08-28 16:37:36 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-09-20 03:48:39 +00:00
|
|
|
"context"
|
2023-09-27 03:06:00 +00:00
|
|
|
"database/sql"
|
2023-09-20 03:48:39 +00:00
|
|
|
"errors"
|
2023-08-28 16:37:36 +00:00
|
|
|
"fmt"
|
2023-09-20 03:48:39 +00:00
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
2023-08-28 16:37:36 +00:00
|
|
|
|
2023-09-20 03:48:39 +00:00
|
|
|
"github.com/BurntSushi/toml"
|
2023-08-28 16:37:36 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
2023-09-20 17:58:56 +00:00
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
|
|
|
|
"github.com/filecoin-project/lotus/node/config"
|
2023-08-28 16:37:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var configCmd = &cli.Command{
|
|
|
|
Name: "config",
|
2023-09-20 03:48:39 +00:00
|
|
|
Usage: "Manage node config by layers. The layer 'base' will always be applied. ",
|
2023-08-28 16:37:36 +00:00
|
|
|
Subcommands: []*cli.Command{
|
|
|
|
configDefaultCmd,
|
|
|
|
configSetCmd,
|
|
|
|
configGetCmd,
|
2023-09-20 03:48:39 +00:00
|
|
|
configListCmd,
|
|
|
|
configViewCmd,
|
2023-09-29 16:56:10 +00:00
|
|
|
configRmCmd,
|
2023-08-28 16:37:36 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configDefaultCmd = &cli.Command{
|
|
|
|
Name: "default",
|
2023-09-20 03:48:39 +00:00
|
|
|
Usage: "Print default node config",
|
2023-08-28 16:37:36 +00:00
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "no-comment",
|
|
|
|
Usage: "don't comment default values",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
2023-09-20 03:48:39 +00:00
|
|
|
c := config.DefaultLotusProvider()
|
|
|
|
|
2023-09-20 21:17:51 +00:00
|
|
|
cb, err := config.ConfigUpdate(c, nil, config.Commented(!cctx.Bool("no-comment")), config.DefaultKeepUncommented(), config.NoEnv())
|
2023-09-20 03:48:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-20 21:17:51 +00:00
|
|
|
fmt.Print(string(cb))
|
2023-09-20 03:48:39 +00:00
|
|
|
|
2023-08-28 16:37:36 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configSetCmd = &cli.Command{
|
2023-09-20 03:48:39 +00:00
|
|
|
Name: "set",
|
|
|
|
Usage: "Set a config layer or the base",
|
|
|
|
ArgsUsage: "a layer's name",
|
2023-08-28 16:37:36 +00:00
|
|
|
Action: func(cctx *cli.Context) error {
|
2023-09-20 03:48:39 +00:00
|
|
|
args := cctx.Args()
|
|
|
|
if args.Len() != 1 {
|
2023-09-20 17:58:56 +00:00
|
|
|
return errors.New("must have exactly 1 arg for the file name")
|
2023-09-20 03:48:39 +00:00
|
|
|
}
|
|
|
|
db, err := makeDB(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fn := args.First()
|
|
|
|
bytes, err := os.ReadFile(fn)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot read file %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
lp := config.DefaultLotusProvider() // ensure it's toml
|
2023-09-20 17:58:56 +00:00
|
|
|
_, err = toml.Decode(string(bytes), lp)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot decode file: %w", err)
|
|
|
|
}
|
2023-09-20 03:48:39 +00:00
|
|
|
_ = lp
|
|
|
|
|
|
|
|
name := strings.Split(fn, ".")[0]
|
|
|
|
_, err = db.Exec(context.Background(),
|
2023-09-27 03:06:00 +00:00
|
|
|
`INSERT INTO harmony_config (title, config) VALUES ($1, $2)
|
2023-09-20 03:48:39 +00:00
|
|
|
ON CONFLICT (title) DO UPDATE SET config = excluded.config`, name, string(bytes))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to save config layer: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("Layer " + name + " created/updated")
|
2023-08-28 16:37:36 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var configGetCmd = &cli.Command{
|
2023-09-20 03:48:39 +00:00
|
|
|
Name: "get",
|
|
|
|
Usage: "Get a config layer by name. You may want to pipe the output to a file, or use 'less'",
|
|
|
|
ArgsUsage: "layer name",
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
args := cctx.Args()
|
|
|
|
if args.Len() != 1 {
|
2023-09-20 17:58:56 +00:00
|
|
|
return fmt.Errorf("want 1 layer arg, got %d", args.Len())
|
2023-09-20 03:48:39 +00:00
|
|
|
}
|
|
|
|
db, err := makeDB(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var cfg string
|
2023-09-27 03:06:00 +00:00
|
|
|
err = db.QueryRow(context.Background(), `SELECT config FROM harmony_config WHERE title=$1`, args.First()).Scan(&cfg)
|
2023-09-20 03:48:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Println(cfg)
|
|
|
|
|
|
|
|
return nil
|
2023-08-28 16:37:36 +00:00
|
|
|
},
|
2023-09-20 03:48:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var configListCmd = &cli.Command{
|
|
|
|
Name: "list",
|
|
|
|
Usage: "List config layers you can get.",
|
|
|
|
Flags: []cli.Flag{},
|
2023-08-28 16:37:36 +00:00
|
|
|
Action: func(cctx *cli.Context) error {
|
2023-09-20 03:48:39 +00:00
|
|
|
db, err := makeDB(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var res []string
|
2023-09-27 03:06:00 +00:00
|
|
|
err = db.Select(context.Background(), &res, `SELECT title FROM harmony_config ORDER BY title`)
|
2023-09-20 17:58:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to read from db: %w", err)
|
|
|
|
}
|
2023-09-20 03:48:39 +00:00
|
|
|
for _, r := range res {
|
|
|
|
fmt.Println(r)
|
|
|
|
}
|
|
|
|
|
2023-08-28 16:37:36 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2023-09-20 03:48:39 +00:00
|
|
|
|
2023-09-29 16:56:10 +00:00
|
|
|
var configRmCmd = &cli.Command{
|
|
|
|
Name: "rm",
|
|
|
|
Usage: "Remvoe a named config layer.",
|
|
|
|
Flags: []cli.Flag{},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
args := cctx.Args()
|
|
|
|
if args.Len() != 1 {
|
|
|
|
return errors.New("must have exactly 1 arg for the layer name")
|
|
|
|
}
|
|
|
|
db, err := makeDB(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ct, err := db.Exec(context.Background(), `DELETE FROM harmony_config WHERE title=$1`, args.First())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to read from db: %w", err)
|
|
|
|
}
|
|
|
|
if ct == 0 {
|
|
|
|
return fmt.Errorf("no layer named %s", args.First())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2023-09-20 03:48:39 +00:00
|
|
|
var configViewCmd = &cli.Command{
|
|
|
|
Name: "view",
|
2023-09-27 03:06:00 +00:00
|
|
|
Usage: "View stacked config layers as it will be interpreted by this version of lotus-provider.",
|
2023-09-20 03:48:39 +00:00
|
|
|
ArgsUsage: "a list of layers to be interpreted as the final config",
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
db, err := makeDB(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
lp, err := getConfig(cctx, db)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:06:00 +00:00
|
|
|
e := toml.NewEncoder(os.Stdout)
|
|
|
|
e.Indent = " "
|
|
|
|
return e.Encode(lp)
|
2023-09-20 03:48:39 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func getConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) {
|
|
|
|
lp := config.DefaultLotusProvider()
|
|
|
|
have := []string{}
|
|
|
|
for _, layer := range regexp.MustCompile("[ |,]").Split(cctx.String("layers"), -1) {
|
|
|
|
text := ""
|
2023-09-27 03:06:00 +00:00
|
|
|
err := db.QueryRow(cctx.Context, `SELECT config FROM harmony_config WHERE title=$1`, layer).Scan(&text)
|
2023-09-20 03:48:39 +00:00
|
|
|
if err != nil {
|
2023-09-27 03:06:00 +00:00
|
|
|
if strings.Contains(err.Error(), sql.ErrNoRows.Error()) {
|
|
|
|
return nil, fmt.Errorf("missing layer '%s' ", layer)
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("could not read layer '%s': %w", layer, err)
|
2023-09-20 03:48:39 +00:00
|
|
|
}
|
|
|
|
meta, err := toml.Decode(text, &lp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err)
|
|
|
|
}
|
|
|
|
for _, k := range meta.Keys() {
|
|
|
|
have = append(have, strings.Join(k, " "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ = have // TODO: verify that required fields are here.
|
|
|
|
return lp, nil
|
|
|
|
}
|