diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index a04fbed72..03b6fb5e9 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -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, } diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index 4359c19a7..22593e95c 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -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) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 8cb5acf07..88aeb555b 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -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}, -} diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index 2a10c7c2d..312113cc8 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -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 != "" { diff --git a/cmd/ipfs/rotate.go b/cmd/ipfs/rotate.go index 335b1bfac..2dd583ab6 100644 --- a/cmd/ipfs/rotate.go +++ b/cmd/ipfs/rotate.go @@ -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) diff --git a/core/commands/active.go b/core/commands/active.go index 9f924e9a9..34a2c9b9c 100644 --- a/core/commands/active.go +++ b/core/commands/active.go @@ -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()) diff --git a/core/commands/cid.go b/core/commands/cid.go index dd01349d6..13c3e83a9 100644 --- a/core/commands/cid.go +++ b/core/commands/cid.go @@ -26,6 +26,7 @@ var CidCmd = &cmds.Command{ "codecs": codecsCmd, "hashes": hashesCmd, }, + Extra: CreateCmdExtras(SetDoesNotUseRepo(true)), } const ( diff --git a/core/commands/commands.go b/core/commands/commands.go index 404230695..b45ac54c6 100644 --- a/core/commands/commands.go +++ b/core/commands/commands.go @@ -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) diff --git a/core/commands/config.go b/core/commands/config.go index 90f5eca82..50af44343 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -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 { diff --git a/core/commands/external.go b/core/commands/external.go index 37105c31e..0eb4de036 100644 --- a/core/commands/external.go +++ b/core/commands/external.go @@ -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) diff --git a/core/commands/extra.go b/core/commands/extra.go new file mode 100644 index 000000000..e70fb029a --- /dev/null +++ b/core/commands/extra.go @@ -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 +} diff --git a/core/commands/keystore.go b/core/commands/keystore.go index a50ee0b4f..639cc5fab 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -144,6 +144,7 @@ path can be specified with '--output=' or '-o='. 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] diff --git a/core/commands/log.go b/core/commands/log.go index ff560ae4b..78ff8a6b5 100644 --- a/core/commands/log.go +++ b/core/commands/log.go @@ -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] diff --git a/core/commands/repo.go b/core/commands/repo.go index d3de85fdd..0a91648a8 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -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"}) }, diff --git a/core/commands/version.go b/core/commands/version.go index ce7b60edd..834326d17 100644 --- a/core/commands/version.go +++ b/core/commands/version.go @@ -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, diff --git a/go.mod b/go.mod index c048b9097..727139ac7 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index e235f313e..3103e45e2 100644 --- a/go.sum +++ b/go.sum @@ -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=