lotus/cli/cmd.go

343 lines
7.7 KiB
Go
Raw Normal View History

package cli
import (
"context"
"fmt"
2019-07-23 18:49:09 +00:00
"net/http"
2020-10-18 11:59:38 +00:00
"net/url"
"os"
"os/signal"
"strings"
"syscall"
logging "github.com/ipfs/go-log/v2"
2019-11-21 16:10:04 +00:00
"github.com/mitchellh/go-homedir"
"github.com/urfave/cli/v2"
2020-06-05 22:59:01 +00:00
"golang.org/x/xerrors"
2019-07-10 17:28:49 +00:00
2020-05-20 17:43:22 +00:00
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
2020-10-09 00:57:41 +00:00
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/node/repo"
)
2019-07-23 18:49:09 +00:00
var log = logging.Logger("cli")
const (
2020-08-16 02:20:32 +00:00
metadataTraceContext = "traceContext"
)
// custom CLI error
type ErrCmdFailed struct {
msg string
}
func (e *ErrCmdFailed) Error() string {
return e.msg
}
func NewCliError(s string) error {
return &ErrCmdFailed{s}
}
// ApiConnector returns API instance
2019-07-24 00:09:34 +00:00
type ApiConnector func() api.FullNode
2019-07-08 19:07:16 +00:00
2020-08-13 20:37:09 +00:00
// The flag passed on the command line with the listen address of the API
// server (only used by the tests)
func flagForAPI(t repo.RepoType) string {
switch t {
case repo.FullNode:
return "api-url"
2020-08-13 20:37:09 +00:00
case repo.StorageMiner:
return "miner-api-url"
2020-08-30 18:28:58 +00:00
case repo.Worker:
return "worker-api-url"
2020-08-13 20:37:09 +00:00
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
}
func flagForRepo(t repo.RepoType) string {
switch t {
case repo.FullNode:
return "repo"
case repo.StorageMiner:
2020-07-08 10:38:59 +00:00
return "miner-repo"
2020-08-30 18:28:58 +00:00
case repo.Worker:
return "worker-repo"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
}
func envForRepo(t repo.RepoType) string {
switch t {
case repo.FullNode:
return "FULLNODE_API_INFO"
case repo.StorageMiner:
return "MINER_API_INFO"
2020-08-30 18:28:58 +00:00
case repo.Worker:
return "WORKER_API_INFO"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
}
2020-07-10 12:18:09 +00:00
// TODO remove after deprecation period
func envForRepoDeprecation(t repo.RepoType) string {
switch t {
case repo.FullNode:
return "FULLNODE_API_INFO"
case repo.StorageMiner:
return "STORAGE_API_INFO"
2020-08-30 18:28:58 +00:00
case repo.Worker:
return "WORKER_API_INFO"
2020-07-10 12:18:09 +00:00
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
}
2020-10-09 00:57:41 +00:00
func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (cliutil.APIInfo, error) {
2020-08-13 20:37:09 +00:00
// Check if there was a flag passed with the listen address of the API
// server (only used by the tests)
apiFlag := flagForAPI(t)
if ctx.IsSet(apiFlag) {
strma := ctx.String(apiFlag)
strma = strings.TrimSpace(strma)
2020-10-09 00:57:41 +00:00
return cliutil.APIInfo{Addr: strma}, nil
2020-08-13 20:37:09 +00:00
}
2020-07-10 12:18:09 +00:00
envKey := envForRepo(t)
env, ok := os.LookupEnv(envKey)
if !ok {
// TODO remove after deprecation period
envKey = envForRepoDeprecation(t)
env, ok = os.LookupEnv(envKey)
if ok {
log.Warnf("Use deprecation env(%s) value, please use env(%s) instead.", envKey, envForRepo(t))
}
}
if ok {
2020-10-09 00:57:41 +00:00
return cliutil.ParseApiInfo(env), nil
}
repoFlag := flagForRepo(t)
2019-11-21 16:10:04 +00:00
p, err := homedir.Expand(ctx.String(repoFlag))
2019-07-10 17:28:49 +00:00
if err != nil {
2020-10-09 00:57:41 +00:00
return cliutil.APIInfo{}, xerrors.Errorf("could not expand home dir (%s): %w", repoFlag, err)
2019-11-21 16:10:04 +00:00
}
r, err := repo.NewFS(p)
if err != nil {
2020-10-09 00:57:41 +00:00
return cliutil.APIInfo{}, xerrors.Errorf("could not open repo at path: %s; %w", p, err)
2019-07-10 17:28:49 +00:00
}
ma, err := r.APIEndpoint()
if err != nil {
2020-10-09 00:57:41 +00:00
return cliutil.APIInfo{}, xerrors.Errorf("could not get api endpoint: %w", err)
2019-07-10 17:28:49 +00:00
}
token, err := r.APIToken()
2019-11-21 16:10:04 +00:00
if err != nil {
log.Warnf("Couldn't load CLI token, capabilities may be limited: %v", err)
2019-11-21 16:10:04 +00:00
}
2020-10-09 00:57:41 +00:00
return cliutil.APIInfo{
Addr: ma.String(),
Token: token,
}, nil
2019-11-21 16:10:04 +00:00
}
func GetRawAPI(ctx *cli.Context, t repo.RepoType) (string, http.Header, error) {
ainfo, err := GetAPIInfo(ctx, t)
2019-07-11 11:52:07 +00:00
if err != nil {
return "", nil, xerrors.Errorf("could not get API info: %w", err)
2019-07-11 11:52:07 +00:00
}
2019-11-21 16:10:04 +00:00
addr, err := ainfo.DialArgs()
2019-07-23 18:49:09 +00:00
if err != nil {
return "", nil, xerrors.Errorf("could not get DialArgs: %w", err)
2019-07-23 18:49:09 +00:00
}
return addr, ainfo.AuthHeader(), nil
}
2019-10-03 18:12:30 +00:00
func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
ti, ok := ctx.App.Metadata["repoType"]
if !ok {
log.Errorf("unknown repo type, are you sure you want to use GetAPI?")
ti = repo.FullNode
}
t, ok := ti.(repo.RepoType)
if !ok {
log.Errorf("repoType type does not match the type of repo.RepoType")
2019-08-02 16:18:44 +00:00
}
2020-10-05 12:14:43 +00:00
if tn, ok := ctx.App.Metadata["testnode-storage"]; ok {
return tn.(api.StorageMiner), func() {}, nil
}
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
return tn.(api.FullNode), func() {}, nil
}
addr, headers, err := GetRawAPI(ctx, t)
2019-08-02 16:18:44 +00:00
if err != nil {
2019-10-03 18:12:30 +00:00
return nil, nil, err
2019-08-02 16:18:44 +00:00
}
return client.NewCommonRPC(ctx.Context, addr, headers)
2019-08-02 16:18:44 +00:00
}
2019-10-03 18:12:30 +00:00
func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error) {
2020-10-05 12:14:43 +00:00
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
return tn.(api.FullNode), func() {}, nil
}
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
if err != nil {
2019-10-03 18:12:30 +00:00
return nil, nil, err
}
return client.NewFullNodeRPC(ctx.Context, addr, headers)
}
func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error) {
if tn, ok := ctx.App.Metadata["test-services"]; ok {
return tn.(ServicesAPI), nil
}
api, c, err := GetFullNodeAPI(ctx)
if err != nil {
return nil, err
}
return &ServicesImpl{api: api, closer: c}, nil
}
2020-10-18 11:59:38 +00:00
type GetStorageMinerOptions struct {
PreferHttp bool
}
type GetStorageMinerOption func(*GetStorageMinerOptions)
func StorageMinerUseHttp(opts *GetStorageMinerOptions) {
opts.PreferHttp = true
}
func GetStorageMinerAPI(ctx *cli.Context, opts ...GetStorageMinerOption) (api.StorageMiner, jsonrpc.ClientCloser, error) {
var options GetStorageMinerOptions
for _, opt := range opts {
opt(&options)
}
2020-10-05 12:14:43 +00:00
if tn, ok := ctx.App.Metadata["testnode-storage"]; ok {
return tn.(api.StorageMiner), func() {}, nil
}
addr, headers, err := GetRawAPI(ctx, repo.StorageMiner)
if err != nil {
2019-10-03 18:12:30 +00:00
return nil, nil, err
}
2020-10-18 11:59:38 +00:00
if options.PreferHttp {
u, err := url.Parse(addr)
if err != nil {
return nil, nil, xerrors.Errorf("parsing miner api URL: %w", err)
}
switch u.Scheme {
case "ws":
u.Scheme = "http"
case "wss":
u.Scheme = "https"
}
addr = u.String()
}
return client.NewStorageMinerRPC(ctx.Context, addr, headers)
}
2020-08-30 18:28:58 +00:00
func GetWorkerAPI(ctx *cli.Context) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
addr, headers, err := GetRawAPI(ctx, repo.Worker)
if err != nil {
return nil, nil, err
}
return client.NewWorkerRPC(ctx.Context, addr, headers)
}
func GetGatewayAPI(ctx *cli.Context) (api.GatewayAPI, jsonrpc.ClientCloser, error) {
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
if err != nil {
return nil, nil, err
}
return client.NewGatewayRPC(ctx.Context, addr, headers)
}
2019-09-17 18:36:06 +00:00
func DaemonContext(cctx *cli.Context) context.Context {
2020-08-16 02:20:32 +00:00
if mtCtx, ok := cctx.App.Metadata[metadataTraceContext]; ok {
2019-09-17 18:36:06 +00:00
return mtCtx.(context.Context)
}
return context.Background()
}
2019-07-18 23:16:23 +00:00
// ReqContext returns context for cli execution. Calling it for the first time
// installs SIGTERM handler that will close returned context.
// Not safe for concurrent execution.
2019-07-18 23:16:23 +00:00
func ReqContext(cctx *cli.Context) context.Context {
2019-09-17 18:36:06 +00:00
tCtx := DaemonContext(cctx)
ctx, done := context.WithCancel(tCtx)
sigChan := make(chan os.Signal, 2)
go func() {
<-sigChan
done()
}()
2020-03-09 22:03:33 +00:00
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
return ctx
2019-07-08 19:07:16 +00:00
}
2020-03-23 12:29:24 +00:00
var CommonCommands = []*cli.Command{
netCmd,
2020-05-13 11:08:59 +00:00
authCmd,
2020-03-23 12:29:24 +00:00
logCmd,
waitApiCmd,
2020-05-13 11:08:59 +00:00
fetchParamCmd,
2020-08-17 09:48:12 +00:00
pprofCmd,
2020-08-04 18:57:40 +00:00
VersionCmd,
2020-03-23 12:29:24 +00:00
}
var Commands = []*cli.Command{
2020-07-21 17:19:47 +00:00
WithCategory("basic", sendCmd),
WithCategory("basic", walletCmd),
WithCategory("basic", clientCmd),
WithCategory("basic", multisigCmd),
WithCategory("basic", paychCmd),
WithCategory("developer", authCmd),
WithCategory("developer", mpoolCmd),
WithCategory("developer", stateCmd),
WithCategory("developer", chainCmd),
WithCategory("developer", logCmd),
WithCategory("developer", waitApiCmd),
WithCategory("developer", fetchParamCmd),
WithCategory("network", netCmd),
WithCategory("network", syncCmd),
2020-08-17 09:48:12 +00:00
pprofCmd,
2020-08-04 18:57:40 +00:00
VersionCmd,
2020-05-13 11:08:59 +00:00
}
2020-07-21 17:19:47 +00:00
func WithCategory(cat string, cmd *cli.Command) *cli.Command {
2021-02-10 13:38:42 +00:00
cmd.Category = strings.ToUpper(cat)
2020-05-13 11:08:59 +00:00
return cmd
}