1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-22 01:17:11 +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)"), // cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
}, },
Subcommands: map[string]*cmds.Command{}, Subcommands: map[string]*cmds.Command{},
NoRemote: true,
Extra: commands.CreateCmdExtras(commands.SetDoesNotUseConfigAsInput(true)),
Run: daemonFunc, Run: daemonFunc,
} }

View File

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

View File

@ -1,8 +1,6 @@
package main package main
import ( import (
"fmt"
commands "github.com/ipfs/go-ipfs/core/commands" commands "github.com/ipfs/go-ipfs/core/commands"
cmds "github.com/ipfs/go-ipfs-cmds" 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" "net/http"
"os" "os"
"runtime/pprof" "runtime/pprof"
"strings"
"time" "time"
util "github.com/ipfs/go-ipfs/cmd/ipfs/util" util "github.com/ipfs/go-ipfs/cmd/ipfs/util"
@ -202,17 +201,18 @@ func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) {
func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) { func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
exe := cmds.NewExecutor(req.Root) exe := cmds.NewExecutor(req.Root)
cctx := env.(*oldcmds.Context) cctx := env.(*oldcmds.Context)
details := commandDetails(req.Path)
// Check if the command is disabled. // 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) return nil, fmt.Errorf("command disabled: %v", req.Path)
} }
// Can we just run this locally? // Can we just run this locally?
if !details.cannotRunOnClient && details.doesNotUseRepo { if !req.Command.NoLocal {
if doesNotUseRepo, ok := corecmds.GetDoesNotUseRepo(req.Command.Extra); doesNotUseRepo && ok {
return exe, nil return exe, nil
} }
}
// Get the API option from the commandline. // Get the API option from the commandline.
apiAddr, err := apiAddrOption(req) apiAddr, err := apiAddrOption(req)
@ -225,7 +225,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
daemonRequested := apiAddr != nil && req.Command != daemonCmd daemonRequested := apiAddr != nil && req.Command != daemonCmd
// Run this on the client if required. // Run this on the client if required.
if details.cannotRunOnDaemon || req.Command.External { if req.Command.NoRemote {
if daemonRequested { if daemonRequested {
// User requested that the command be run on the daemon but we can't. // User requested that the command be run on the daemon but we can't.
// NOTE: We drop this check for the `ipfs daemon` command. // 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. // Still no api specified? Run it on the client or fail.
if apiAddr == nil { 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 nil, fmt.Errorf("command must be run on the daemon: %v", req.Path)
} }
return exe, nil return exe, nil
@ -293,22 +293,6 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
return cmdhttp.NewClient(host, opts...), nil 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) { func getRepoPath(req *cmds.Request) (string, error) {
repoOpt, found := req.Options["config"].(string) repoOpt, found := req.Options["config"].(string)
if found && repoOpt != "" { 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.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."), 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 { PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context) cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot) daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)

View File

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

View File

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

View File

@ -66,6 +66,7 @@ func CommandsCmd(root *cmds.Command) *cmds.Command {
Options: []cmds.Option{ Options: []cmds.Option{
cmds.BoolOption(flagsOptionName, "f", "Show command flags"), cmds.BoolOption(flagsOptionName, "f", "Show command flags"),
}, },
Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
rootCmd := cmd2outputCmd("ipfs", root) rootCmd := cmd2outputCmd("ipfs", root)
rootCmd.showOpts, _ = req.Options[flagsOptionName].(bool) 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. 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 { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cfgRoot, err := cmdenv.GetConfigRoot(env) cfgRoot, err := cmdenv.GetConfigRoot(env)
if err != nil { if err != nil {

View File

@ -17,6 +17,7 @@ func ExternalBinary(instructions string) *cmds.Command {
cmds.StringArg("args", false, true, "Arguments for subcommand."), cmds.StringArg("args", false, true, "Arguments for subcommand."),
}, },
External: true, External: true,
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
binname := strings.Join(append([]string{"ipfs"}, req.Path...), "-") binname := strings.Join(append([]string{"ipfs"}, req.Path...), "-")
_, err := exec.LookPath(binname) _, 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{ Options: []cmds.Option{
cmds.StringOption(outputOptionName, "o", "The path where the output should be stored."), 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 { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
name := req.Arguments[0] name := req.Arguments[0]

View File

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

View File

@ -217,6 +217,7 @@ var repoFsckCmd = &cmds.Command{
'ipfs repo fsck' is now a no-op. 'ipfs repo fsck' is now a no-op.
`, `,
}, },
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { 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"}) 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(versionRepoOptionName, "Show repo version."),
cmds.BoolOption(versionAllOptionName, "Show all version information"), 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 { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
return cmds.EmitOnce(res, &VersionOutput{ return cmds.EmitOnce(res, &VersionOutput{
Version: version.CurrentVersionNumber, 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-graphsync v0.0.5
github.com/ipfs/go-ipfs-blockstore v0.1.4 github.com/ipfs/go-ipfs-blockstore v0.1.4
github.com/ipfs/go-ipfs-chunker v0.0.5 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-config v0.9.0
github.com/ipfs/go-ipfs-ds-help v0.1.1 github.com/ipfs/go-ipfs-ds-help v0.1.1
github.com/ipfs/go-ipfs-exchange-interface v0.0.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.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 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= 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.3.0 h1:mi9oYrSCox5aBhutqAYqw6/9crlyGbw4E/aJtwS4zI4=
github.com/ipfs/go-ipfs-cmds v0.2.9/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk= 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 h1:qTXJ9CyOyQv1LFJUMysxz8fi6RxxnP9QqcmiobuANvw=
github.com/ipfs/go-ipfs-config v0.9.0/go.mod h1:GQUxqb0NfkZmEU92PxqqqLVVFTLpoGGUlBaTyDaAqrE= 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= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=