Skip to content

Commit f8b0191

Browse files
committed
implement ipfs pin update
License: MIT Signed-off-by: Jeromy <[email protected]>
1 parent 987ff1a commit f8b0191

File tree

8 files changed

+372
-22
lines changed

8 files changed

+372
-22
lines changed

core/commands/object/diff.go

+1-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
cmds "github.com/ipfs/go-ipfs/commands"
99
core "github.com/ipfs/go-ipfs/core"
10-
dag "github.com/ipfs/go-ipfs/merkledag"
1110
dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
1211
path "github.com/ipfs/go-ipfs/path"
1312
)
@@ -86,19 +85,7 @@ Example:
8685
return
8786
}
8887

89-
pbobj_a, ok := obj_a.(*dag.ProtoNode)
90-
if !ok {
91-
res.SetError(dag.ErrNotProtobuf, cmds.ErrNormal)
92-
return
93-
}
94-
95-
pbobj_b, ok := obj_b.(*dag.ProtoNode)
96-
if !ok {
97-
res.SetError(dag.ErrNotProtobuf, cmds.ErrNormal)
98-
return
99-
}
100-
101-
changes, err := dagutils.Diff(ctx, node.DAG, pbobj_a, pbobj_b)
88+
changes, err := dagutils.Diff(ctx, node.DAG, obj_a, obj_b)
10289
if err != nil {
10390
res.SetError(err, cmds.ErrNormal)
10491
return

core/commands/pin.go

+81-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ var PinCmd = &cmds.Command{
2424
},
2525

2626
Subcommands: map[string]*cmds.Command{
27-
"add": addPinCmd,
28-
"rm": rmPinCmd,
29-
"ls": listPinCmd,
27+
"add": addPinCmd,
28+
"rm": rmPinCmd,
29+
"ls": listPinCmd,
30+
"update": updatePinCmd,
3031
},
3132
}
3233

@@ -332,6 +333,83 @@ Example:
332333
},
333334
}
334335

336+
var updatePinCmd = &cmds.Command{
337+
Helptext: cmds.HelpText{
338+
Tagline: "Update a recursive pin",
339+
ShortDescription: `
340+
Updates one pin to another, making sure that all objects in the new pin are
341+
local. Then removes the old pin. This is an optimized version of adding the
342+
new pin and removing the old one.
343+
`,
344+
},
345+
346+
Arguments: []cmds.Argument{
347+
cmds.StringArg("from-path", true, false, "Path to old object."),
348+
cmds.StringArg("to-path", true, false, "Path to new object to be pinned."),
349+
},
350+
Options: []cmds.Option{
351+
cmds.BoolOption("unpin", "Remove the old pin.").Default(true),
352+
},
353+
Type: PinOutput{},
354+
Run: func(req cmds.Request, res cmds.Response) {
355+
n, err := req.InvocContext().GetNode()
356+
if err != nil {
357+
res.SetError(err, cmds.ErrNormal)
358+
return
359+
}
360+
361+
unpin, _, err := req.Option("unpin").Bool()
362+
if err != nil {
363+
res.SetError(err, cmds.ErrNormal)
364+
return
365+
}
366+
367+
from, err := path.ParsePath(req.Arguments()[0])
368+
if err != nil {
369+
res.SetError(err, cmds.ErrNormal)
370+
return
371+
}
372+
373+
to, err := path.ParsePath(req.Arguments()[1])
374+
if err != nil {
375+
res.SetError(err, cmds.ErrNormal)
376+
return
377+
}
378+
379+
fromc, err := core.ResolveToCid(req.Context(), n, from)
380+
if err != nil {
381+
res.SetError(err, cmds.ErrNormal)
382+
return
383+
}
384+
385+
toc, err := core.ResolveToCid(req.Context(), n, to)
386+
if err != nil {
387+
res.SetError(err, cmds.ErrNormal)
388+
return
389+
}
390+
391+
err = n.Pinning.Update(req.Context(), fromc, toc, unpin)
392+
if err != nil {
393+
res.SetError(err, cmds.ErrNormal)
394+
return
395+
}
396+
397+
res.SetOutput(&PinOutput{Pins: []string{from.String(), to.String()}})
398+
},
399+
Marshalers: cmds.MarshalerMap{
400+
cmds.Text: func(res cmds.Response) (io.Reader, error) {
401+
added, ok := res.Output().(*PinOutput)
402+
if !ok {
403+
return nil, u.ErrCast()
404+
}
405+
406+
buf := new(bytes.Buffer)
407+
fmt.Fprintf(buf, "updated %s to %s\n", added.Pins[0], added.Pins[1])
408+
return buf, nil
409+
},
410+
},
411+
}
412+
335413
type RefKeyObject struct {
336414
Type string
337415
}

merkledag/merkledag.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func (n *dagService) Remove(nd node.Node) error {
153153
// GetLinksDirect creates a function to get the links for a node, from
154154
// the node, bypassing the LinkService. If the node does not exist
155155
// locally (and can not be retrieved) an error will be returned.
156-
func GetLinksDirect(serv DAGService) GetLinks {
156+
func GetLinksDirect(serv node.NodeGetter) GetLinks {
157157
return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) {
158158
node, err := serv.Get(ctx, c)
159159
if err != nil {

merkledag/utils/diff.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package dagutils
22

33
import (
4+
"context"
45
"fmt"
56
"path"
67

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

9-
context "context"
1010
cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
11+
node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
1112
)
1213

1314
const (
@@ -87,7 +88,7 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs [
8788
return e.Finalize(ds)
8889
}
8990

90-
func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Change, error) {
91+
func Diff(ctx context.Context, ds dag.DAGService, a, b node.Node) ([]*Change, error) {
9192
if len(a.Links()) == 0 && len(b.Links()) == 0 {
9293
return []*Change{
9394
&Change{
@@ -104,7 +105,7 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Chang
104105

105106
// strip out unchanged stuff
106107
for _, lnk := range a.Links() {
107-
l, err := b.GetNodeLink(lnk.Name)
108+
l, _, err := b.ResolveLink([]string{lnk.Name})
108109
if err == nil {
109110
if l.Cid.Equals(lnk.Cid) {
110111
// no change... ignore it

merkledag/utils/diffenum.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package dagutils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
mdag "github.com/ipfs/go-ipfs/merkledag"
8+
9+
cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
10+
node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
11+
)
12+
13+
// DiffEnumerate fetches every object in the graph pointed to by 'to' that is
14+
// not in 'from'. This can be used to more efficiently fetch a graph if you can
15+
// guarantee you already have the entirety of 'from'
16+
func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid) error {
17+
fnd, err := dserv.Get(ctx, from)
18+
if err != nil {
19+
return fmt.Errorf("get %s: %s", from, err)
20+
}
21+
22+
tnd, err := dserv.Get(ctx, to)
23+
if err != nil {
24+
return fmt.Errorf("get %s: %s", to, err)
25+
}
26+
27+
diff := getLinkDiff(fnd, tnd)
28+
29+
sset := cid.NewSet()
30+
for _, c := range diff {
31+
if c.a == nil {
32+
err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.b, sset.Visit)
33+
if err != nil {
34+
return err
35+
}
36+
} else {
37+
err := DiffEnumerate(ctx, dserv, c.a, c.b)
38+
if err != nil {
39+
return err
40+
}
41+
}
42+
}
43+
44+
return nil
45+
}
46+
47+
type diffpair struct {
48+
a, b *cid.Cid
49+
}
50+
51+
func getLinkDiff(a, b node.Node) []diffpair {
52+
have := make(map[string]*node.Link)
53+
names := make(map[string]*node.Link)
54+
for _, l := range a.Links() {
55+
have[l.Cid.KeyString()] = l
56+
names[l.Name] = l
57+
}
58+
59+
var out []diffpair
60+
61+
for _, l := range b.Links() {
62+
if have[l.Cid.KeyString()] != nil {
63+
continue
64+
}
65+
66+
match, ok := names[l.Name]
67+
if !ok {
68+
out = append(out, diffpair{b: l.Cid})
69+
continue
70+
}
71+
72+
out = append(out, diffpair{a: match.Cid, b: l.Cid})
73+
}
74+
return out
75+
}

merkledag/utils/diffenum_test.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package dagutils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
dag "github.com/ipfs/go-ipfs/merkledag"
9+
mdtest "github.com/ipfs/go-ipfs/merkledag/test"
10+
11+
cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
12+
node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
13+
)
14+
15+
func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node {
16+
this := desc[name]
17+
nd := new(dag.ProtoNode)
18+
nd.SetData([]byte(name))
19+
for k, v := range this {
20+
child, ok := out[v]
21+
if !ok {
22+
child = buildNode(v, desc, out)
23+
out[v] = child
24+
}
25+
26+
if err := nd.AddNodeLink(k, child); err != nil {
27+
panic(err)
28+
}
29+
}
30+
31+
return nd
32+
}
33+
34+
type ndesc map[string]string
35+
36+
func mkGraph(desc map[string]ndesc) map[string]node.Node {
37+
out := make(map[string]node.Node)
38+
for name, _ := range desc {
39+
if _, ok := out[name]; ok {
40+
continue
41+
}
42+
43+
out[name] = buildNode(name, desc, out)
44+
}
45+
return out
46+
}
47+
48+
var tg1 = map[string]ndesc{
49+
"a1": ndesc{
50+
"foo": "b",
51+
},
52+
"b": ndesc{},
53+
"a2": ndesc{
54+
"foo": "b",
55+
"bar": "c",
56+
},
57+
"c": ndesc{},
58+
}
59+
60+
var tg2 = map[string]ndesc{
61+
"a1": ndesc{
62+
"foo": "b",
63+
},
64+
"b": ndesc{},
65+
"a2": ndesc{
66+
"foo": "b",
67+
"bar": "c",
68+
},
69+
"c": ndesc{"baz": "d"},
70+
"d": ndesc{},
71+
}
72+
73+
func TestDiffEnumBasic(t *testing.T) {
74+
ctx, cancel := context.WithCancel(context.Background())
75+
defer cancel()
76+
nds := mkGraph(tg1)
77+
78+
ds := mdtest.Mock()
79+
lgds := &getLogger{ds: ds}
80+
81+
for _, nd := range nds {
82+
_, err := ds.Add(nd)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
}
87+
88+
err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid())
89+
if err != nil {
90+
t.Fatal(err)
91+
}
92+
93+
err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()})
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
}
98+
99+
type getLogger struct {
100+
ds node.NodeGetter
101+
log []*cid.Cid
102+
}
103+
104+
func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (node.Node, error) {
105+
nd, err := gl.ds.Get(ctx, c)
106+
if err != nil {
107+
return nil, err
108+
}
109+
gl.log = append(gl.log, c)
110+
return nd, nil
111+
}
112+
113+
func assertCidList(a, b []*cid.Cid) error {
114+
if len(a) != len(b) {
115+
return fmt.Errorf("got different number of cids than expected")
116+
}
117+
for i, c := range a {
118+
if !c.Equals(b[i]) {
119+
return fmt.Errorf("expected %s, got %s", c, b[i])
120+
}
121+
}
122+
return nil
123+
}
124+
func TestDiffEnumFail(t *testing.T) {
125+
ctx, cancel := context.WithCancel(context.Background())
126+
defer cancel()
127+
nds := mkGraph(tg2)
128+
129+
ds := mdtest.Mock()
130+
lgds := &getLogger{ds: ds}
131+
132+
for _, s := range []string{"a1", "a2", "b", "c"} {
133+
_, err := ds.Add(nds[s])
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
}
138+
139+
err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid())
140+
if err != dag.ErrNotFound {
141+
t.Fatal("expected err not found")
142+
}
143+
144+
err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()})
145+
if err != nil {
146+
t.Fatal(err)
147+
}
148+
149+
}

0 commit comments

Comments
 (0)