1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-21 08:56:37 +08:00

refactor: get rid of cmdDetails awkwardness

This commit is contained in:
Adin Schmahmann
2020-07-20 16:38:54 -04:00
parent 18a27564cb
commit 692e9f59ff
17 changed files with 92 additions and 85 deletions

View File

@ -182,6 +182,8 @@ Headers.
// cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
},
Subcommands: map[string]*cmds.Command{},
NoRemote: true,
Extra: commands.CreateCmdExtras(commands.SetDoesNotUseConfigAsInput(true)),
Run: daemonFunc,
}

View File

@ -13,6 +13,7 @@ import (
assets "github.com/ipfs/go-ipfs/assets"
oldcmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/core/commands"
namesys "github.com/ipfs/go-ipfs/namesys"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
@ -66,6 +67,8 @@ environment variable:
// name of the file?
// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs."),
},
NoRemote: true,
Extra: commands.CreateCmdExtras(commands.SetDoesNotUseRepo(true), commands.SetDoesNotUseConfigAsInput(true)),
PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)

View File

@ -1,8 +1,6 @@
package main
import (
"fmt"
commands "github.com/ipfs/go-ipfs/core/commands"
cmds "github.com/ipfs/go-ipfs-cmds"
@ -39,59 +37,3 @@ func init() {
}
}
}
// NB: when necessary, properties are described using negatives in order to
// provide desirable defaults
type cmdDetails struct {
cannotRunOnClient bool
cannotRunOnDaemon bool
doesNotUseRepo bool
// doesNotUseConfigAsInput describes commands that do not use the config as
// input. These commands either initialize the config or perform operations
// that don't require access to the config.
//
// pre-command hooks that require configs must not be run before these
// commands.
doesNotUseConfigAsInput bool
// preemptsAutoUpdate describes commands that must be executed without the
// auto-update pre-command hook
preemptsAutoUpdate bool
}
func (d *cmdDetails) String() string {
return fmt.Sprintf("on client? %t, on daemon? %t, uses repo? %t",
d.canRunOnClient(), d.canRunOnDaemon(), d.usesRepo())
}
func (d *cmdDetails) Loggable() map[string]interface{} {
return map[string]interface{}{
"canRunOnClient": d.canRunOnClient(),
"canRunOnDaemon": d.canRunOnDaemon(),
"preemptsAutoUpdate": d.preemptsAutoUpdate,
"usesConfigAsInput": d.usesConfigAsInput(),
"usesRepo": d.usesRepo(),
}
}
func (d *cmdDetails) usesConfigAsInput() bool { return !d.doesNotUseConfigAsInput }
func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient }
func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon }
func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo }
// "What is this madness!?" you ask. Our commands have the unfortunate problem of
// not being able to run on all the same contexts. This map describes these
// properties so that other code can make decisions about whether to invoke a
// command or return an error to the user.
var cmdDetailsMap = map[string]cmdDetails{
"init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true},
"daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true},
"commands": {doesNotUseRepo: true},
"version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init
"log": {cannotRunOnClient: true},
"diag/cmds": {cannotRunOnClient: true},
"repo/fsck": {cannotRunOnDaemon: true},
"config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true},
"cid": {doesNotUseRepo: true},
}

View File

@ -10,7 +10,6 @@ import (
"net/http"
"os"
"runtime/pprof"
"strings"
"time"
util "github.com/ipfs/go-ipfs/cmd/ipfs/util"
@ -202,16 +201,17 @@ func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) {
func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
exe := cmds.NewExecutor(req.Root)
cctx := env.(*oldcmds.Context)
details := commandDetails(req.Path)
// Check if the command is disabled.
if details.cannotRunOnClient && details.cannotRunOnDaemon {
if req.Command.NoLocal && req.Command.NoRemote {
return nil, fmt.Errorf("command disabled: %v", req.Path)
}
// Can we just run this locally?
if !details.cannotRunOnClient && details.doesNotUseRepo {
return exe, nil
if !req.Command.NoLocal {
if doesNotUseRepo, ok := corecmds.GetDoesNotUseRepo(req.Command.Extra); doesNotUseRepo && ok {
return exe, nil
}
}
// Get the API option from the commandline.
@ -225,7 +225,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
daemonRequested := apiAddr != nil && req.Command != daemonCmd
// Run this on the client if required.
if details.cannotRunOnDaemon || req.Command.External {
if req.Command.NoRemote {
if daemonRequested {
// User requested that the command be run on the daemon but we can't.
// NOTE: We drop this check for the `ipfs daemon` command.
@ -247,7 +247,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
// Still no api specified? Run it on the client or fail.
if apiAddr == nil {
if details.cannotRunOnClient {
if req.Command.NoLocal {
return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path)
}
return exe, nil
@ -293,22 +293,6 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
return cmdhttp.NewClient(host, opts...), nil
}
// commandDetails returns a command's details for the command given by |path|.
func commandDetails(path []string) cmdDetails {
if len(path) == 0 {
// special case root command
return cmdDetails{doesNotUseRepo: true}
}
var details cmdDetails
// find the last command in path that has a cmdDetailsMap entry
for i := range path {
if cmdDetails, found := cmdDetailsMap[strings.Join(path[:i+1], "/")]; found {
details = cmdDetails
}
}
return details
}
func getRepoPath(req *cmds.Request) (string, error) {
repoOpt, found := req.Options["config"].(string)
if found && repoOpt != "" {

View File

@ -36,6 +36,7 @@ environment variable:
cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault),
cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."),
},
NoRemote: true,
PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)

View File

@ -23,6 +23,7 @@ var ActiveReqsCmd = &cmds.Command{
Lists running and recently run commands.
`,
},
NoLocal: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
ctx := env.(*oldcmds.Context)
return cmds.EmitOnce(res, ctx.ReqLog.Report())

View File

@ -26,6 +26,7 @@ var CidCmd = &cmds.Command{
"codecs": codecsCmd,
"hashes": hashesCmd,
},
Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
}
const (

View File

@ -66,6 +66,7 @@ func CommandsCmd(root *cmds.Command) *cmds.Command {
Options: []cmds.Option{
cmds.BoolOption(flagsOptionName, "f", "Show command flags"),
},
Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
rootCmd := cmd2outputCmd("ipfs", root)
rootCmd.showOpts, _ = req.Options[flagsOptionName].(bool)

View File

@ -239,7 +239,8 @@ To use 'ipfs config edit', you must have the $EDITOR environment
variable set to your preferred text editor.
`,
},
NoRemote: true,
Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cfgRoot, err := cmdenv.GetConfigRoot(env)
if err != nil {

View File

@ -17,6 +17,7 @@ func ExternalBinary(instructions string) *cmds.Command {
cmds.StringArg("args", false, true, "Arguments for subcommand."),
},
External: true,
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
binname := strings.Join(append([]string{"ipfs"}, req.Path...), "-")
_, err := exec.LookPath(binname)

65
core/commands/extra.go Normal file
View File

@ -0,0 +1,65 @@
package commands
import cmds "github.com/ipfs/go-ipfs-cmds"
func CreateCmdExtras(opts ...func(e *cmds.Extra)) *cmds.Extra {
e := new(cmds.Extra)
for _, o := range opts {
o(e)
}
return e
}
type doesNotUseRepo struct{}
func SetDoesNotUseRepo(val bool) func(e *cmds.Extra) {
return func(e *cmds.Extra) {
e.SetValue(doesNotUseRepo{}, val)
}
}
func GetDoesNotUseRepo(e *cmds.Extra) (val bool, found bool) {
return getBoolFlag(e, doesNotUseRepo{})
}
// doesNotUseConfigAsInput describes commands that do not use the config as
// input. These commands either initialize the config or perform operations
// that don't require access to the config.
//
// pre-command hooks that require configs must not be run before these
// commands.
type doesNotUseConfigAsInput struct{}
func SetDoesNotUseConfigAsInput(val bool) func(e *cmds.Extra) {
return func(e *cmds.Extra) {
e.SetValue(doesNotUseConfigAsInput{}, val)
}
}
func GetDoesNotUseConfigAsInput(e *cmds.Extra) (val bool, found bool) {
return getBoolFlag(e, doesNotUseConfigAsInput{})
}
// preemptsAutoUpdate describes commands that must be executed without the
// auto-update pre-command hook
type preemptsAutoUpdate struct{}
func SetPreemptsAutoUpdate(val bool) func(e *cmds.Extra) {
return func(e *cmds.Extra) {
e.SetValue(preemptsAutoUpdate{}, val)
}
}
func GetPreemptsAutoUpdate(e *cmds.Extra) (val bool, found bool) {
return getBoolFlag(e, preemptsAutoUpdate{})
}
func getBoolFlag(e *cmds.Extra, key interface{}) (val bool, found bool) {
var ival interface{}
ival, found = e.GetValue(key)
if !found {
return false, false
}
val = ival.(bool)
return val, found
}

View File

@ -144,6 +144,7 @@ path can be specified with '--output=<path>' or '-o=<path>'.
Options: []cmds.Option{
cmds.StringOption(outputOptionName, "o", "The path where the output should be stored."),
},
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
name := req.Arguments[0]

View File

@ -55,6 +55,7 @@ the event log.
One of: debug, info, warn, error, dpanic, panic, fatal.
`),
},
NoLocal: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
args := req.Arguments
subsystem, level := args[0], args[1]

View File

@ -217,6 +217,7 @@ var repoFsckCmd = &cmds.Command{
'ipfs repo fsck' is now a no-op.
`,
},
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
return cmds.EmitOnce(res, &MessageOutput{"`ipfs repo fsck` is deprecated and does nothing.\n"})
},

View File

@ -43,6 +43,8 @@ var VersionCmd = &cmds.Command{
cmds.BoolOption(versionRepoOptionName, "Show repo version."),
cmds.BoolOption(versionAllOptionName, "Show all version information"),
},
// must be permitted to run before init
Extra: CreateCmdExtras(SetDoesNotUseRepo(true), SetDoesNotUseConfigAsInput(true)),
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
return cmds.EmitOnce(res, &VersionOutput{
Version: version.CurrentVersionNumber,

2
go.mod
View File

@ -31,7 +31,7 @@ require (
github.com/ipfs/go-graphsync v0.0.5
github.com/ipfs/go-ipfs-blockstore v0.1.4
github.com/ipfs/go-ipfs-chunker v0.0.5
github.com/ipfs/go-ipfs-cmds v0.2.9
github.com/ipfs/go-ipfs-cmds v0.3.0
github.com/ipfs/go-ipfs-config v0.9.0
github.com/ipfs/go-ipfs-ds-help v0.1.1
github.com/ipfs/go-ipfs-exchange-interface v0.0.1

4
go.sum
View File

@ -340,8 +340,8 @@ github.com/ipfs/go-ipfs-chunker v0.0.1 h1:cHUUxKFQ99pozdahi+uSC/3Y6HeRpi9oTeUHbE
github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
github.com/ipfs/go-ipfs-cmds v0.2.9 h1:zQTENe9UJrtCb2bOtRoDGjtuo3rQjmuPdPnVlqoBV/M=
github.com/ipfs/go-ipfs-cmds v0.2.9/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk=
github.com/ipfs/go-ipfs-cmds v0.3.0 h1:mi9oYrSCox5aBhutqAYqw6/9crlyGbw4E/aJtwS4zI4=
github.com/ipfs/go-ipfs-cmds v0.3.0/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk=
github.com/ipfs/go-ipfs-config v0.9.0 h1:qTXJ9CyOyQv1LFJUMysxz8fi6RxxnP9QqcmiobuANvw=
github.com/ipfs/go-ipfs-config v0.9.0/go.mod h1:GQUxqb0NfkZmEU92PxqqqLVVFTLpoGGUlBaTyDaAqrE=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=