diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 75258464f84..762af30b5a4 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -2,18 +2,21 @@ package commands import ( "errors" + "fmt" "io" "strings" "time" - cmds "github.com/ipfs/go-ipfs/commands" - "github.com/ipfs/go-ipfs/core" + cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" e "github.com/ipfs/go-ipfs/core/commands/e" ncmd "github.com/ipfs/go-ipfs/core/commands/name" + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ns "github.com/ipfs/go-ipfs/namesys" nsopts "github.com/ipfs/go-ipfs/namesys/opts" path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + "gx/ipfs/QmPTfgFTo9PFr1PvPKyKoeMgBvYPh6cX3aDP7DHKVbnCbi/go-ipfs-cmds" "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit" ) @@ -62,12 +65,17 @@ Resolve the value of an IPFS DAG path: }, Options: []cmdkit.Option{ cmdkit.BoolOption("recursive", "r", "Resolve until the result is an IPFS name."), - cmdkit.UintOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."), + cmdkit.IntOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."), cmdkit.StringOption("dht-timeout", "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."), }, - Run: func(req cmds.Request, res cmds.Response) { + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) { + api, err := cmdenv.GetApi(env) + if err != nil { + res.SetError(err, cmdkit.ErrNormal) + return + } - n, err := req.InvocContext().GetNode() + n, err := cmdenv.GetNode(env) if err != nil { res.SetError(err, cmdkit.ErrNormal) return @@ -81,16 +89,19 @@ Resolve the value of an IPFS DAG path: } } - name := req.Arguments()[0] - recursive, _, _ := req.Option("recursive").Bool() + name := req.Arguments[0] + recursive, _ := req.Options["recursive"].(bool) // the case when ipns is resolved step by step if strings.HasPrefix(name, "/ipns/") && !recursive { - rc, rcok, _ := req.Option("dht-record-count").Int() - dhtt, dhttok, _ := req.Option("dht-timeout").String() - ropts := []nsopts.ResolveOpt{nsopts.Depth(1)} + rc, rcok := req.Options["dht-record-count"].(uint) + dhtt, dhttok := req.Options["dht-timeout"].(string) + ropts := []options.NameResolveOption{ + options.Name.ResolveOption(nsopts.Depth(1)), + } + if rcok { - ropts = append(ropts, nsopts.DhtRecordCount(uint(rc))) + ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtRecordCount(rc))) } if dhttok { d, err := time.ParseDuration(dhtt) @@ -102,48 +113,45 @@ Resolve the value of an IPFS DAG path: res.SetError(errors.New("DHT timeout value must be >= 0"), cmdkit.ErrNormal) return } - ropts = append(ropts, nsopts.DhtTimeout(d)) + ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtTimeout(d))) } - p, err := n.Namesys.Resolve(req.Context(), name, ropts...) + p, err := api.Name().Resolve(req.Context, name, ropts...) // ErrResolveRecursion is fine if err != nil && err != ns.ErrResolveRecursion { res.SetError(err, cmdkit.ErrNormal) return } - res.SetOutput(&ncmd.ResolvedPath{Path: p}) + cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: path.Path(p.String())}) return } // else, ipfs path or ipns with recursive flag - p, err := path.ParsePath(name) + p, err := coreiface.ParsePath(name) if err != nil { res.SetError(err, cmdkit.ErrNormal) return } - node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, p) + rp, err := api.ResolvePath(req.Context, p) if err != nil { res.SetError(err, cmdkit.ErrNormal) return } - c := node.Cid() + c := rp.Cid() - res.SetOutput(&ncmd.ResolvedPath{Path: path.FromCid(c)}) + cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: path.FromCid(c)}) }, - Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) (io.Reader, error) { - v, err := unwrapOutput(res.Output()) - if err != nil { - return nil, err - } - + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error { output, ok := v.(*ncmd.ResolvedPath) if !ok { - return nil, e.TypeErr(output, v) + return e.TypeErr(output, v) } - return strings.NewReader(output.Path.String() + "\n"), nil - }, + + fmt.Fprintln(w, output.Path.String()) + return nil + }), }, Type: ncmd.ResolvedPath{}, } diff --git a/core/commands/root.go b/core/commands/root.go index 9ed34a7f55a..54bfe5965fd 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -135,7 +135,7 @@ var rootSubcommands = map[string]*cmds.Command{ "ping": lgc.NewCommand(PingCmd), "p2p": lgc.NewCommand(P2PCmd), "refs": lgc.NewCommand(RefsCmd), - "resolve": lgc.NewCommand(ResolveCmd), + "resolve": ResolveCmd, "swarm": lgc.NewCommand(SwarmCmd), "tar": lgc.NewCommand(TarCmd), "file": lgc.NewCommand(unixfs.UnixFSCmd), @@ -183,7 +183,7 @@ var rootROSubcommands = map[string]*cmds.Command{ "resolve": dag.DagResolveCmd, }, }), - "resolve": lgc.NewCommand(ResolveCmd), + "resolve": ResolveCmd, "version": lgc.NewCommand(VersionCmd), } diff --git a/core/coreapi/interface/options/name.go b/core/coreapi/interface/options/name.go index 48aecf18ba8..ba3691b03cd 100644 --- a/core/coreapi/interface/options/name.go +++ b/core/coreapi/interface/options/name.go @@ -2,6 +2,8 @@ package options import ( "time" + + ropts "github.com/ipfs/go-ipfs/namesys/opts" ) const ( @@ -14,9 +16,10 @@ type NamePublishSettings struct { } type NameResolveSettings struct { - Recursive bool - Local bool - Cache bool + Local bool + Cache bool + + ResolveOpts []ropts.ResolveOpt } type NamePublishOption func(*NamePublishSettings) error @@ -40,9 +43,8 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { options := &NameResolveSettings{ - Recursive: false, - Local: false, - Cache: true, + Local: false, + Cache: true, } for _, opt := range opts { @@ -80,15 +82,6 @@ func (nameOpts) Key(key string) NamePublishOption { } } -// Recursive is an option for Name.Resolve which specifies whether to perform a -// recursive lookup. Default value is false -func (nameOpts) Recursive(recursive bool) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.Recursive = recursive - return nil - } -} - // Local is an option for Name.Resolve which specifies if the lookup should be // offline. Default value is false func (nameOpts) Local(local bool) NameResolveOption { @@ -106,3 +99,11 @@ func (nameOpts) Cache(cache bool) NameResolveOption { return nil } } + +// +func (nameOpts) ResolveOption(opt ropts.ResolveOpt) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.ResolveOpts = append(settings.ResolveOpts, opt) + return nil + } +} diff --git a/core/coreapi/name.go b/core/coreapi/name.go index 79b785f1112..da1249271a4 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -7,17 +7,16 @@ import ( "strings" "time" - core "github.com/ipfs/go-ipfs/core" + "github.com/ipfs/go-ipfs/core" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - keystore "github.com/ipfs/go-ipfs/keystore" - namesys "github.com/ipfs/go-ipfs/namesys" - nsopts "github.com/ipfs/go-ipfs/namesys/opts" + "github.com/ipfs/go-ipfs/keystore" + "github.com/ipfs/go-ipfs/namesys" ipath "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" - crypto "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - offline "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" + "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" ) type NameAPI CoreAPI @@ -119,12 +118,7 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam name = "/ipns/" + name } - var ropts []nsopts.ResolveOpt - if !options.Recursive { - ropts = append(ropts, nsopts.Depth(1)) - } - - output, err := resolver.Resolve(ctx, name, ropts...) + output, err := resolver.Resolve(ctx, name, options.ResolveOpts...) if err != nil { return nil, err } diff --git a/core/coreapi/path.go b/core/coreapi/path.go index cba68aeeac2..7d7e1487fa4 100644 --- a/core/coreapi/path.go +++ b/core/coreapi/path.go @@ -1,53 +1,44 @@ package coreapi import ( - context "context" - fmt "fmt" + "context" + "fmt" gopath "path" - core "github.com/ipfs/go-ipfs/core" + "github.com/ipfs/go-ipfs/core" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - namesys "github.com/ipfs/go-ipfs/namesys" uio "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs/io" ipfspath "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" - resolver "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path/resolver" + "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path/resolver" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the // resolved Node. func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (ipld.Node, error) { - return resolveNode(ctx, api.node.DAG, api.node.Namesys, p) -} - -// ResolvePath resolves the path `p` using Unixfs resolver, returns the -// resolved path. -func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.ResolvedPath, error) { - return resolvePath(ctx, api.node.DAG, api.node.Namesys, p) -} - -func resolveNode(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (ipld.Node, error) { - rp, err := resolvePath(ctx, ng, nsys, p) + rp, err := api.ResolvePath(ctx, p) if err != nil { return nil, err } - node, err := ng.Get(ctx, rp.Cid()) + node, err := api.node.DAG.Get(ctx, rp.Cid()) if err != nil { return nil, err } return node, nil } -func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (coreiface.ResolvedPath, error) { +// ResolvePath resolves the path `p` using Unixfs resolver, returns the +// resolved path. +func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.ResolvedPath, error) { if _, ok := p.(coreiface.ResolvedPath); ok { return p.(coreiface.ResolvedPath), nil } ipath := ipfspath.Path(p.String()) - ipath, err := core.ResolveIPNS(ctx, nsys, ipath) + ipath, err := core.ResolveIPNS(ctx, api.node.Namesys, ipath) if err == core.ErrNoNamesys { return nil, coreiface.ErrOffline } else if err != nil { @@ -66,7 +57,7 @@ func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSyste } r := &resolver.Resolver{ - DAG: ng, + DAG: api.node.DAG, ResolveOnce: resolveOnce, } diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go index 1798336dbdb..eb76e24ffef 100644 --- a/core/coreapi/unixfs.go +++ b/core/coreapi/unixfs.go @@ -32,7 +32,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.ResolvedP func (api *UnixfsAPI) Cat(ctx context.Context, p coreiface.Path) (coreiface.Reader, error) { dget := api.node.DAG // TODO: use a session here once routing perf issues are resolved - dagnode, err := resolveNode(ctx, dget, api.node.Namesys, p) + dagnode, err := api.core().ResolveNode(ctx, p) if err != nil { return nil, err } diff --git a/test/sharness/t0260-sharding.sh b/test/sharness/t0260-sharding.sh index 01fc447f43b..6fb3cf657d9 100755 --- a/test/sharness/t0260-sharding.sh +++ b/test/sharness/t0260-sharding.sh @@ -84,6 +84,12 @@ test_expect_success "gateway can resolve sharded dirs" ' test_cmp expected actual ' +test_expect_success "'ipfs resolve' can resolve sharded dirs" ' + echo /ipfs/QmZ3RfWk1u5LEGYLHA633B5TNJy3Du27K6Fny9wcxpowGS > expected && + ipfs resolve "/ipfs/$SHARDED/file100" > actual && + test_cmp expected actual +' + test_kill_ipfs_daemon test_add_large_dir_v1() {