refactor: get rid of cmdDetails awkwardness
Adin Schmahmann committed
Jul 20, 2020 at 16:38 UTC
692e9f59ff985b7771df6cd63dbde16ff8078ee1
17 files changed
+92
-85
cmd/ipfs/daemon.go
+2
@@ -182,6 +182,8 @@ Headers.
182
// cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
183
},
184
Subcommands: map[string]*cmds.Command{},
185
+ NoRemote: true,
186
+ Extra: commands.CreateCmdExtras(commands.SetDoesNotUseConfigAsInput(true)),
187
Run: daemonFunc,
188
}
189
cmd/ipfs/init.go
+3
@@ -13,6 +13,7 @@ import (
13
assets "github.com/ipfs/go-ipfs/assets"
14
oldcmds "github.com/ipfs/go-ipfs/commands"
15
core "github.com/ipfs/go-ipfs/core"
16
+ "github.com/ipfs/go-ipfs/core/commands"
17
namesys "github.com/ipfs/go-ipfs/namesys"
18
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
19
@@ -66,6 +67,8 @@ environment variable:
67
// name of the file?
68
// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs."),
69
},
70
+ NoRemote: true,
71
+ Extra: commands.CreateCmdExtras(commands.SetDoesNotUseRepo(true), commands.SetDoesNotUseConfigAsInput(true)),
72
PreRun: func(req *cmds.Request, env cmds.Environment) error {
73
cctx := env.(*oldcmds.Context)
74
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
cmd/ipfs/ipfs.go
-58
@@ -1,8 +1,6 @@
1
package main
2
3
import (
4
- "fmt"
5
-
4
commands "github.com/ipfs/go-ipfs/core/commands"
5
6
cmds "github.com/ipfs/go-ipfs-cmds"
@@ -39,59 +37,3 @@ func init() {
37
}
38
}
39
}
42
-
43
-// NB: when necessary, properties are described using negatives in order to
44
-// provide desirable defaults
45
-type cmdDetails struct {
46
- cannotRunOnClient bool
47
- cannotRunOnDaemon bool
48
- doesNotUseRepo bool
49
-
50
- // doesNotUseConfigAsInput describes commands that do not use the config as
51
- // input. These commands either initialize the config or perform operations
52
- // that don't require access to the config.
53
- //
54
- // pre-command hooks that require configs must not be run before these
55
- // commands.
56
- doesNotUseConfigAsInput bool
57
-
58
- // preemptsAutoUpdate describes commands that must be executed without the
59
- // auto-update pre-command hook
60
- preemptsAutoUpdate bool
61
-}
62
-
63
-func (d *cmdDetails) String() string {
64
- return fmt.Sprintf("on client? %t, on daemon? %t, uses repo? %t",
65
- d.canRunOnClient(), d.canRunOnDaemon(), d.usesRepo())
66
-}
67
-
68
-func (d *cmdDetails) Loggable() map[string]interface{} {
69
- return map[string]interface{}{
70
- "canRunOnClient": d.canRunOnClient(),
71
- "canRunOnDaemon": d.canRunOnDaemon(),
72
- "preemptsAutoUpdate": d.preemptsAutoUpdate,
73
- "usesConfigAsInput": d.usesConfigAsInput(),
74
- "usesRepo": d.usesRepo(),
75
- }
76
-}
77
-
78
-func (d *cmdDetails) usesConfigAsInput() bool { return !d.doesNotUseConfigAsInput }
79
-func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient }
80
-func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon }
81
-func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo }
82
-
83
-// "What is this madness!?" you ask. Our commands have the unfortunate problem of
84
-// not being able to run on all the same contexts. This map describes these
85
-// properties so that other code can make decisions about whether to invoke a
86
-// command or return an error to the user.
87
-var cmdDetailsMap = map[string]cmdDetails{
88
- "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true},
89
- "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true},
90
- "commands": {doesNotUseRepo: true},
91
- "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init
92
- "log": {cannotRunOnClient: true},
93
- "diag/cmds": {cannotRunOnClient: true},
94
- "repo/fsck": {cannotRunOnDaemon: true},
95
- "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true},
96
- "cid": {doesNotUseRepo: true},
97
-}
cmd/ipfs/main.go
+7
-23
@@ -10,7 +10,6 @@ import (
10
"net/http"
11
"os"
12
"runtime/pprof"
13
- "strings"
13
"time"
14
15
util "github.com/ipfs/go-ipfs/cmd/ipfs/util"
@@ -202,16 +201,17 @@ func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) {
201
func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
202
exe := cmds.NewExecutor(req.Root)
203
cctx := env.(*oldcmds.Context)
205
- details := commandDetails(req.Path)
204
205
// Check if the command is disabled.
208
- if details.cannotRunOnClient && details.cannotRunOnDaemon {
206
+ if req.Command.NoLocal && req.Command.NoRemote {
207
return nil, fmt.Errorf("command disabled: %v", req.Path)
208
}
209
210
// Can we just run this locally?
213
- if !details.cannotRunOnClient && details.doesNotUseRepo {
214
- return exe, nil
211
+ if !req.Command.NoLocal {
212
+ if doesNotUseRepo, ok := corecmds.GetDoesNotUseRepo(req.Command.Extra); doesNotUseRepo && ok {
213
+ return exe, nil
214
+ }
215
}
216
217
// Get the API option from the commandline.
@@ -225,7 +225,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
225
daemonRequested := apiAddr != nil && req.Command != daemonCmd
226
227
// Run this on the client if required.
228
- if details.cannotRunOnDaemon || req.Command.External {
228
+ if req.Command.NoRemote {
229
if daemonRequested {
230
// User requested that the command be run on the daemon but we can't.
231
// NOTE: We drop this check for the `ipfs daemon` command.
@@ -247,7 +247,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
247
248
// Still no api specified? Run it on the client or fail.
249
if apiAddr == nil {
250
- if details.cannotRunOnClient {
250
+ if req.Command.NoLocal {
251
return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path)
252
}
253
return exe, nil
@@ -293,22 +293,6 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
293
return cmdhttp.NewClient(host, opts...), nil
294
}
295
296
-// commandDetails returns a command's details for the command given by |path|.
297
-func commandDetails(path []string) cmdDetails {
298
- if len(path) == 0 {
299
- // special case root command
300
- return cmdDetails{doesNotUseRepo: true}
301
- }
302
- var details cmdDetails
303
- // find the last command in path that has a cmdDetailsMap entry
304
- for i := range path {
305
- if cmdDetails, found := cmdDetailsMap[strings.Join(path[:i+1], "/")]; found {
306
- details = cmdDetails
307
- }
308
- }
309
- return details
310
-}
311
-
296
func getRepoPath(req *cmds.Request) (string, error) {
297
repoOpt, found := req.Options["config"].(string)
298
if found && repoOpt != "" {
cmd/ipfs/rotate.go
+1
@@ -36,6 +36,7 @@ environment variable:
36
cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault),
37
cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."),
38
},
39
+ NoRemote: true,
40
PreRun: func(req *cmds.Request, env cmds.Environment) error {
41
cctx := env.(*oldcmds.Context)
42
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
core/commands/active.go
+1
@@ -23,6 +23,7 @@ var ActiveReqsCmd = &cmds.Command{
23
Lists running and recently run commands.
24
`,
25
},
26
+ NoLocal: true,
27
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
28
ctx := env.(*oldcmds.Context)
29
return cmds.EmitOnce(res, ctx.ReqLog.Report())
core/commands/cid.go
+1
@@ -26,6 +26,7 @@ var CidCmd = &cmds.Command{
26
"codecs": codecsCmd,
27
"hashes": hashesCmd,
28
},
29
+ Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
30
}
31
32
const (
core/commands/commands.go
+1
@@ -66,6 +66,7 @@ func CommandsCmd(root *cmds.Command) *cmds.Command {
66
Options: []cmds.Option{
67
cmds.BoolOption(flagsOptionName, "f", "Show command flags"),
68
},
69
+ Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
70
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
71
rootCmd := cmd2outputCmd("ipfs", root)
72
rootCmd.showOpts, _ = req.Options[flagsOptionName].(bool)
core/commands/config.go
+2
-1
@@ -239,7 +239,8 @@ To use 'ipfs config edit', you must have the $EDITOR environment
239
variable set to your preferred text editor.
240
`,
241
},
242
-
242
+ NoRemote: true,
243
+ Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
244
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
245
cfgRoot, err := cmdenv.GetConfigRoot(env)
246
if err != nil {
core/commands/external.go
+1
@@ -17,6 +17,7 @@ func ExternalBinary(instructions string) *cmds.Command {
17
cmds.StringArg("args", false, true, "Arguments for subcommand."),
18
},
19
External: true,
20
+ NoRemote: true,
21
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
22
binname := strings.Join(append([]string{"ipfs"}, req.Path...), "-")
23
_, err := exec.LookPath(binname)
core/commands/extra.go
new
+65
@@ -0,0 +1,65 @@
1
+package commands
2
+
3
+import cmds "github.com/ipfs/go-ipfs-cmds"
4
+
5
+func CreateCmdExtras(opts ...func(e *cmds.Extra)) *cmds.Extra {
6
+ e := new(cmds.Extra)
7
+ for _, o := range opts {
8
+ o(e)
9
+ }
10
+ return e
11
+}
12
+
13
+type doesNotUseRepo struct{}
14
+
15
+func SetDoesNotUseRepo(val bool) func(e *cmds.Extra) {
16
+ return func(e *cmds.Extra) {
17
+ e.SetValue(doesNotUseRepo{}, val)
18
+ }
19
+}
20
+
21
+func GetDoesNotUseRepo(e *cmds.Extra) (val bool, found bool) {
22
+ return getBoolFlag(e, doesNotUseRepo{})
23
+}
24
+
25
+// doesNotUseConfigAsInput describes commands that do not use the config as
26
+// input. These commands either initialize the config or perform operations
27
+// that don't require access to the config.
28
+//
29
+// pre-command hooks that require configs must not be run before these
30
+// commands.
31
+type doesNotUseConfigAsInput struct{}
32
+
33
+func SetDoesNotUseConfigAsInput(val bool) func(e *cmds.Extra) {
34
+ return func(e *cmds.Extra) {
35
+ e.SetValue(doesNotUseConfigAsInput{}, val)
36
+ }
37
+}
38
+
39
+func GetDoesNotUseConfigAsInput(e *cmds.Extra) (val bool, found bool) {
40
+ return getBoolFlag(e, doesNotUseConfigAsInput{})
41
+}
42
+
43
+// preemptsAutoUpdate describes commands that must be executed without the
44
+// auto-update pre-command hook
45
+type preemptsAutoUpdate struct{}
46
+
47
+func SetPreemptsAutoUpdate(val bool) func(e *cmds.Extra) {
48
+ return func(e *cmds.Extra) {
49
+ e.SetValue(preemptsAutoUpdate{}, val)
50
+ }
51
+}
52
+
53
+func GetPreemptsAutoUpdate(e *cmds.Extra) (val bool, found bool) {
54
+ return getBoolFlag(e, preemptsAutoUpdate{})
55
+}
56
+
57
+func getBoolFlag(e *cmds.Extra, key interface{}) (val bool, found bool) {
58
+ var ival interface{}
59
+ ival, found = e.GetValue(key)
60
+ if !found {
61
+ return false, false
62
+ }
63
+ val = ival.(bool)
64
+ return val, found
65
+}
core/commands/keystore.go
+1
@@ -144,6 +144,7 @@ path can be specified with '--output=<path>' or '-o=<path>'.
144
Options: []cmds.Option{
145
cmds.StringOption(outputOptionName, "o", "The path where the output should be stored."),
146
},
147
+ NoRemote: true,
148
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
149
name := req.Arguments[0]
150
core/commands/log.go
+1
@@ -55,6 +55,7 @@ the event log.
55
One of: debug, info, warn, error, dpanic, panic, fatal.
56
`),
57
},
58
+ NoLocal: true,
59
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
60
args := req.Arguments
61
subsystem, level := args[0], args[1]
core/commands/repo.go
+1
@@ -217,6 +217,7 @@ var repoFsckCmd = &cmds.Command{
217
'ipfs repo fsck' is now a no-op.
218
`,
219
},
220
+ NoRemote: true,
221
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
222
return cmds.EmitOnce(res, &MessageOutput{"`ipfs repo fsck` is deprecated and does nothing.\n"})
223
},
core/commands/version.go
+2
@@ -43,6 +43,8 @@ var VersionCmd = &cmds.Command{
43
cmds.BoolOption(versionRepoOptionName, "Show repo version."),
44
cmds.BoolOption(versionAllOptionName, "Show all version information"),
45
},
46
+ // must be permitted to run before init
47
+ Extra: CreateCmdExtras(SetDoesNotUseRepo(true), SetDoesNotUseConfigAsInput(true)),
48
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
49
return cmds.EmitOnce(res, &VersionOutput{
50
Version: version.CurrentVersionNumber,
go.mod
+1
-1
@@ -31,7 +31,7 @@ require (
31
github.com/ipfs/go-graphsync v0.0.5
32
github.com/ipfs/go-ipfs-blockstore v0.1.4
33
github.com/ipfs/go-ipfs-chunker v0.0.5
34
- github.com/ipfs/go-ipfs-cmds v0.2.9
34
+ github.com/ipfs/go-ipfs-cmds v0.3.0
35
github.com/ipfs/go-ipfs-config v0.9.0
36
github.com/ipfs/go-ipfs-ds-help v0.1.1
37
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
go.sum
+2
-2
@@ -340,8 +340,8 @@ github.com/ipfs/go-ipfs-chunker v0.0.1 h1:cHUUxKFQ99pozdahi+uSC/3Y6HeRpi9oTeUHbE
340
github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
341
github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
342
github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
343
-github.com/ipfs/go-ipfs-cmds v0.2.9 h1:zQTENe9UJrtCb2bOtRoDGjtuo3rQjmuPdPnVlqoBV/M=
344
-github.com/ipfs/go-ipfs-cmds v0.2.9/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk=
343
+github.com/ipfs/go-ipfs-cmds v0.3.0 h1:mi9oYrSCox5aBhutqAYqw6/9crlyGbw4E/aJtwS4zI4=
344
+github.com/ipfs/go-ipfs-cmds v0.3.0/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk=
345
github.com/ipfs/go-ipfs-config v0.9.0 h1:qTXJ9CyOyQv1LFJUMysxz8fi6RxxnP9QqcmiobuANvw=
346
github.com/ipfs/go-ipfs-config v0.9.0/go.mod h1:GQUxqb0NfkZmEU92PxqqqLVVFTLpoGGUlBaTyDaAqrE=
347
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=