Skip to content

implement ipfs pin update #3846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions core/commands/object/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

cmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
dag "github.com/ipfs/go-ipfs/merkledag"
dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
path "github.com/ipfs/go-ipfs/path"
)
Expand Down Expand Up @@ -86,19 +85,7 @@ Example:
return
}

pbobj_a, ok := obj_a.(*dag.ProtoNode)
if !ok {
res.SetError(dag.ErrNotProtobuf, cmds.ErrNormal)
return
}

pbobj_b, ok := obj_b.(*dag.ProtoNode)
if !ok {
res.SetError(dag.ErrNotProtobuf, cmds.ErrNormal)
return
}

changes, err := dagutils.Diff(ctx, node.DAG, pbobj_a, pbobj_b)
changes, err := dagutils.Diff(ctx, node.DAG, obj_a, obj_b)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
Expand Down
84 changes: 81 additions & 3 deletions core/commands/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ var PinCmd = &cmds.Command{
},

Subcommands: map[string]*cmds.Command{
"add": addPinCmd,
"rm": rmPinCmd,
"ls": listPinCmd,
"add": addPinCmd,
"rm": rmPinCmd,
"ls": listPinCmd,
"update": updatePinCmd,
},
}

Expand Down Expand Up @@ -332,6 +333,83 @@ Example:
},
}

var updatePinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Update a recursive pin",
ShortDescription: `
Updates one pin to another, making sure that all objects in the new pin are
local. Then removes the old pin. This is an optimized version of adding the
new pin and removing the old one.
`,
},

Arguments: []cmds.Argument{
cmds.StringArg("from-path", true, false, "Path to old object."),
cmds.StringArg("to-path", true, false, "Path to new object to be pinned."),
},
Options: []cmds.Option{
cmds.BoolOption("unpin", "Remove the old pin.").Default(true),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case of update with no unpin?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its if you want to use the optimized pinning functionality and still keep the old pin

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, right, because it will piggy bank on the already pinned hashes.

},
Type: PinOutput{},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

unpin, _, err := req.Option("unpin").Bool()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

from, err := path.ParsePath(req.Arguments()[0])
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

to, err := path.ParsePath(req.Arguments()[1])
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

fromc, err := core.ResolveToCid(req.Context(), n, from)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

toc, err := core.ResolveToCid(req.Context(), n, to)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

err = n.Pinning.Update(req.Context(), fromc, toc, unpin)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

res.SetOutput(&PinOutput{Pins: []string{from.String(), to.String()}})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
added, ok := res.Output().(*PinOutput)
if !ok {
return nil, u.ErrCast()
}

buf := new(bytes.Buffer)
fmt.Fprintf(buf, "updated %s to %s\n", added.Pins[0], added.Pins[1])
return buf, nil
},
},
}

type RefKeyObject struct {
Type string
}
Expand Down
2 changes: 1 addition & 1 deletion merkledag/merkledag.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (n *dagService) Remove(nd node.Node) error {
// GetLinksDirect creates a function to get the links for a node, from
// the node, bypassing the LinkService. If the node does not exist
// locally (and can not be retrieved) an error will be returned.
func GetLinksDirect(serv DAGService) GetLinks {
func GetLinksDirect(serv node.NodeGetter) GetLinks {
return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) {
node, err := serv.Get(ctx, c)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions merkledag/utils/diff.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package dagutils

import (
"context"
"fmt"
"path"

dag "github.com/ipfs/go-ipfs/merkledag"

context "context"
cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid"
node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format"
)

const (
Expand Down Expand Up @@ -87,7 +88,8 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs [
return e.Finalize(ds)
}

func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Change, error) {
// Diff returns a set of changes that transform node 'a' into node 'b'
func Diff(ctx context.Context, ds dag.DAGService, a, b node.Node) ([]*Change, error) {
if len(a.Links()) == 0 && len(b.Links()) == 0 {
return []*Change{
&Change{
Expand All @@ -104,7 +106,7 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Chang

// strip out unchanged stuff
for _, lnk := range a.Links() {
l, err := b.GetNodeLink(lnk.Name)
l, _, err := b.ResolveLink([]string{lnk.Name})
if err == nil {
if l.Cid.Equals(lnk.Cid) {
// no change... ignore it
Expand Down
91 changes: 91 additions & 0 deletions merkledag/utils/diffenum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package dagutils

import (
"context"
"fmt"

mdag "github.com/ipfs/go-ipfs/merkledag"

cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid"
node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format"
)

// DiffEnumerate fetches every object in the graph pointed to by 'to' that is
// not in 'from'. This can be used to more efficiently fetch a graph if you can
// guarantee you already have the entirety of 'from'
func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid) error {
fnd, err := dserv.Get(ctx, from)
if err != nil {
return fmt.Errorf("get %s: %s", from, err)
}

tnd, err := dserv.Get(ctx, to)
if err != nil {
return fmt.Errorf("get %s: %s", to, err)
}

diff := getLinkDiff(fnd, tnd)

sset := cid.NewSet()
for _, c := range diff {
// Since we're already assuming we have everything in the 'from' graph,
// add all those cids to our 'already seen' set to avoid potentially
// enumerating them later
if c.bef != nil {
sset.Add(c.bef)
}
}
for _, c := range diff {
if c.bef == nil {
if sset.Has(c.aft) {
continue
}
err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.aft, sset.Visit)
if err != nil {
return err
}
} else {
err := DiffEnumerate(ctx, dserv, c.bef, c.aft)
if err != nil {
return err
}
}
}

return nil
}

// if both bef and aft are not nil, then that signifies bef was replaces with aft.
// if bef is nil and aft is not, that means aft was newly added
// if aft is nil and bef is not, that means bef was deleted
type diffpair struct {
bef, aft *cid.Cid
}

// getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does
// not log deletions as our usecase doesnt call for this.
func getLinkDiff(a, b node.Node) []diffpair {
have := make(map[string]*node.Link)
names := make(map[string]*node.Link)
for _, l := range a.Links() {
have[l.Cid.KeyString()] = l
names[l.Name] = l
}

var out []diffpair

for _, l := range b.Links() {
if have[l.Cid.KeyString()] != nil {
continue
}

match, ok := names[l.Name]
if !ok {
out = append(out, diffpair{aft: l.Cid})
continue
}

out = append(out, diffpair{bef: match.Cid, aft: l.Cid})
}
return out
}
Loading