Skip to content

Commit

Permalink
Merge pull request #6478 from ipfs/fix/stale-api
Browse files Browse the repository at this point in the history
ignore stale API files and deprecate ipfs repo fsck
  • Loading branch information
Stebalien committed Jul 3, 2019
2 parents eca93bc + 2b61dbb commit eb4da3c
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 362 deletions.
213 changes: 82 additions & 131 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
repo "github.com/ipfs/go-ipfs/repo"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"

osh "github.com/Kubuxu/go-os-helper"
"github.com/ipfs/go-ipfs-cmds"
"github.com/ipfs/go-ipfs-cmds/cli"
"github.com/ipfs/go-ipfs-cmds/http"
Expand Down Expand Up @@ -195,21 +194,90 @@ func checkDebug(req *cmds.Request) {
}
}

func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) {
apiAddrStr, apiSpecified := req.Options[corecmds.ApiOption].(string)
if !apiSpecified {
return nil, nil
}
return ma.NewMultiaddr(apiAddrStr)
}

func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
exe := cmds.NewExecutor(req.Root)
cctx := env.(*oldcmds.Context)
details := commandDetails(req.Path)
client, err := commandShouldRunOnDaemon(*details, req, env.(*oldcmds.Context))

// Check if the command is disabled.
if details.cannotRunOnClient && details.cannotRunOnDaemon {
return nil, fmt.Errorf("command disabled: %v", req.Path)
}

// Can we just run this locally?
if !details.cannotRunOnClient && details.doesNotUseRepo {
return exe, nil
}

// Get the API option from the commandline.
apiAddr, err := apiAddrOption(req)
if err != nil {
return nil, err
}

var exctr cmds.Executor
if client != nil && !req.Command.External {
exctr = client.(cmds.Executor)
} else {
exctr = cmds.NewExecutor(req.Root)
// Require that the command be run on the daemon when the API flag is
// passed (unless we're trying to _run_ the daemon).
daemonRequested := apiAddr != nil && req.Command != daemonCmd

// Run this on the client if required.
if details.cannotRunOnDaemon || req.Command.External {
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.
return nil, errors.New("api flag specified but command cannot be run on the daemon")
}
return exe, nil
}

// Finally, look in the repo for an API file.
if apiAddr == nil {
var err error
apiAddr, err = fsrepo.APIAddr(cctx.ConfigRoot)
switch err {
case nil, repo.ErrApiNotRunning:
default:
return nil, err
}
}

return exctr, nil
// Still no api specified? Run it on the client or fail.
if apiAddr == nil {
if details.cannotRunOnClient {
return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path)
}
return exe, nil
}

// Resolve the API addr.
apiAddr, err = resolveAddr(req.Context, apiAddr)
if err != nil {
return nil, err
}
_, host, err := manet.DialArgs(apiAddr)
if err != nil {
return nil, err
}

// Construct the executor.
opts := []http.ClientOpt{
http.ClientWithAPIPrefix(corehttp.APIPath),
}

// Fallback on a local executor if we (a) have a repo and (b) aren't
// forcing a daemon.
if !daemonRequested && fsrepo.IsInitialized(cctx.ConfigRoot) {
opts = append(opts, http.ClientWithFallback(exe))
}

return http.NewClient(host, opts...), nil
}

func checkPermissions(path string) (bool, error) {
Expand All @@ -227,75 +295,19 @@ func checkPermissions(path string) (bool, error) {
}

// commandDetails returns a command's details for the command given by |path|.
func commandDetails(path []string) *cmdDetails {
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
}

// commandShouldRunOnDaemon determines, from command details, whether a
// command ought to be executed on an ipfs daemon.
//
// It returns a client if the command should be executed on a daemon and nil if
// it should be executed on a client. It returns an error if the command must
// NOT be executed on either.
func commandShouldRunOnDaemon(details cmdDetails, req *cmds.Request, cctx *oldcmds.Context) (http.Client, error) {
path := req.Path
// root command.
if len(path) < 1 {
return nil, nil
}

if details.cannotRunOnClient && details.cannotRunOnDaemon {
return nil, fmt.Errorf("command disabled: %s", path[0])
}

if details.doesNotUseRepo && details.canRunOnClient() {
return nil, nil
}

// at this point need to know whether api is running. we defer
// to this point so that we don't check unnecessarily

// did user specify an api to use for this command?
apiAddrStr, _ := req.Options[corecmds.ApiOption].(string)

client, err := getAPIClient(req.Context, cctx.ConfigRoot, apiAddrStr)
if err == repo.ErrApiNotRunning {
if apiAddrStr != "" && req.Command != daemonCmd {
// if user SPECIFIED an api, and this cmd is not daemon
// we MUST use it. so error out.
return nil, err
}

// ok for api not to be running
} else if err != nil { // some other api error
return nil, err
}

if client != nil {
if details.cannotRunOnDaemon {
// check if daemon locked. legacy error text, for now.
log.Debugf("Command cannot run on daemon. Checking if daemon is locked")
if daemonLocked, _ := fsrepo.LockedByOtherProcess(cctx.ConfigRoot); daemonLocked {
return nil, cmds.ClientError("ipfs daemon is running. please stop it to run this command")
}
return nil, nil
}

return client, nil
}

if details.cannotRunOnClient {
return nil, cmds.ClientError("must run on the ipfs daemon")
}

return nil, nil
return details
}

func getRepoPath(req *cmds.Request) (string, error) {
Expand Down Expand Up @@ -366,67 +378,6 @@ func profileIfEnabled() (func(), error) {
return func() {}, nil
}

var apiFileErrorFmt string = `Failed to parse '%[1]s/api' file.
error: %[2]s
If you're sure go-ipfs isn't running, you can just delete it.
`
var checkIPFSUnixFmt = "Otherwise check:\n\tps aux | grep ipfs"
var checkIPFSWinFmt = "Otherwise check:\n\ttasklist | findstr ipfs"

// getAPIClient checks the repo, and the given options, checking for
// a running API service. if there is one, it returns a client.
// otherwise, it returns errApiNotRunning, or another error.
func getAPIClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client, error) {
var apiErrorFmt string
switch {
case osh.IsUnix():
apiErrorFmt = apiFileErrorFmt + checkIPFSUnixFmt
case osh.IsWindows():
apiErrorFmt = apiFileErrorFmt + checkIPFSWinFmt
default:
apiErrorFmt = apiFileErrorFmt
}

var addr ma.Multiaddr
var err error
if len(apiAddrStr) != 0 {
addr, err = ma.NewMultiaddr(apiAddrStr)
if err != nil {
return nil, err
}
if len(addr.Protocols()) == 0 {
return nil, fmt.Errorf("multiaddr doesn't provide any protocols")
}
} else {
addr, err = fsrepo.APIAddr(repoPath)
if err == repo.ErrApiNotRunning {
return nil, err
}

if err != nil {
return nil, fmt.Errorf(apiErrorFmt, repoPath, err.Error())
}
}
if len(addr.Protocols()) == 0 {
return nil, fmt.Errorf(apiErrorFmt, repoPath, "multiaddr doesn't provide any protocols")
}
return apiClientForAddr(ctx, addr)
}

func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) {
addr, err := resolveAddr(ctx, addr)
if err != nil {
return nil, err
}

_, host, err := manet.DialArgs(addr)
if err != nil {
return nil, err
}

return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
}

func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) {
ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second)
defer cancelFunc()
Expand Down
39 changes: 2 additions & 37 deletions core/commands/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
Expand All @@ -20,7 +19,6 @@ import (
cid "github.com/ipfs/go-cid"
bstore "github.com/ipfs/go-ipfs-blockstore"
cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/go-ipfs-config"
)

type RepoVersion struct {
Expand Down Expand Up @@ -216,44 +214,11 @@ var repoFsckCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Remove repo lockfiles.",
ShortDescription: `
'ipfs repo fsck' is a plumbing command that will remove repo and level db
lockfiles, as well as the api file. This command can only run when no ipfs
daemons are running.
'ipfs repo fsck' is now a no-op.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
configRoot, err := cmdenv.GetConfigRoot(env)
if err != nil {
return err
}

dsPath, err := config.DataStorePath(configRoot)
if err != nil {
return err
}

dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
repoLockFile := filepath.Join(configRoot, fsrepo.LockFile)
apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically

log.Infof("Removing repo lockfile: %s", repoLockFile)
log.Infof("Removing datastore lockfile: %s", dsLockFile)
log.Infof("Removing api file: %s", apiFile)

err = os.Remove(repoLockFile)
if err != nil && !os.IsNotExist(err) {
return err
}
err = os.Remove(dsLockFile)
if err != nil && !os.IsNotExist(err) {
return err
}
err = os.Remove(apiFile)
if err != nil && !os.IsNotExist(err) {
return err
}

return cmds.EmitOnce(res, &MessageOutput{"Lockfiles have been removed.\n"})
return cmds.EmitOnce(res, &MessageOutput{"`ipfs repo fsck` is deprecated and does nothing.\n"})
},
Type: MessageOutput{},
Encoders: cmds.EncoderMap{
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module github.com/ipfs/go-ipfs

require (
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
github.com/Kubuxu/go-os-helper v0.0.1
github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd
github.com/blang/semver v3.5.1+incompatible
github.com/bren2010/proquint v0.0.0-20160323162903-38337c27106d
Expand Down Expand Up @@ -32,7 +31,7 @@ require (
github.com/ipfs/go-ipfs-blockstore v0.0.1
github.com/ipfs/go-ipfs-blocksutil v0.0.1
github.com/ipfs/go-ipfs-chunker v0.0.1
github.com/ipfs/go-ipfs-cmds v0.0.10
github.com/ipfs/go-ipfs-cmds v0.1.0
github.com/ipfs/go-ipfs-config v0.0.6
github.com/ipfs/go-ipfs-ds-help v0.0.1
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IW
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
github.com/ipfs/go-ipfs-chunker v0.0.1 h1:cHUUxKFQ99pozdahi+uSC/3Y6HeRpi9oTeUHbE27SEw=
github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
github.com/ipfs/go-ipfs-cmds v0.0.10 h1:4kA3E94HbDrLb4RZTkX3yXyUjKv50RfPz0Pv9xkP2cA=
github.com/ipfs/go-ipfs-cmds v0.0.10/go.mod h1:TiK4e7/V31tuEb8YWDF8lN3qrnDH+BS7ZqWIeYJlAs8=
github.com/ipfs/go-ipfs-cmds v0.1.0 h1:0CEde9EcxByej8+L6d1PST57J4ambRPyCTjLG5Ymou8=
github.com/ipfs/go-ipfs-cmds v0.1.0/go.mod h1:TiK4e7/V31tuEb8YWDF8lN3qrnDH+BS7ZqWIeYJlAs8=
github.com/ipfs/go-ipfs-config v0.0.5 h1:D9ek19anOzm8iYPvezeeamSg5mzwqKPb2jyAyJZT/4A=
github.com/ipfs/go-ipfs-config v0.0.5/go.mod h1:IGkVTacurWv9WFKc7IBPjHGM/7hi6+PEClqUb/l2BIM=
github.com/ipfs/go-ipfs-config v0.0.6 h1:jzK9Tl8S0oWBir3F5ObtGgnHRPdqQ0MYiCmwXtV3Ps4=
Expand Down

0 comments on commit eb4da3c

Please sign in to comment.