@cryptotaxi247 / kubo / commits / c8cfed5c8

coreapi: implement pin api

License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>

Łukasz Magiera committed Jan 10, 2018 at 18:41 UTC c8cfed5c84f1d4c991434184cc9e287d9bdebeff
4 files changed +221 -14
core/coreapi/coreapi.go
+4
@@ -52,6 +52,10 @@ func (api *CoreAPI) Object() coreiface.ObjectAPI {
52 return &ObjectAPI{api, nil}
53 }
54
55 +func (api *CoreAPI) Pin() coreiface.PinAPI {
56 + return &PinAPI{api, nil}
57 +}
58 +
59 // ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the
60 // resolved Node.
61 func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
core/coreapi/interface/interface.go
+22 -3
@@ -67,6 +67,24 @@ type Pin interface {
67 Type() string
68 }
69
70 +// PinStatus holds information about pin health
71 +type PinStatus interface {
72 + // Ok indicates whether the pin has been verified to be correct
73 + Ok() bool
74 +
75 + // BadNodes returns any bad (usually missing) nodes from the pin
76 + BadNodes() []BadPinNode
77 +}
78 +
79 +// BadPinNode is a node that has been marked as bad by Pin.Verify
80 +type BadPinNode interface {
81 + // Path is the path of the node
82 + Path() Path
83 +
84 + // Err is the reason why the node has been marked as bad
85 + Err() error
86 +}
87 +
88 // CoreAPI defines an unified interface to IPFS for Go programs.
89 type CoreAPI interface {
90 // Unixfs returns an implementation of Unixfs API.
@@ -83,6 +101,7 @@ type CoreAPI interface {
101
102 // Key returns an implementation of Key API.
103 Key() KeyAPI
104 + Pin() PinAPI
105
106 // ObjectAPI returns an implementation of Object API
107 Object() ObjectAPI
@@ -342,7 +361,7 @@ type PinAPI interface {
361 WithRecursive(bool) options.PinAddOption
362
363 // Ls returns list of pinned objects on this node
345 - Ls(context.Context) ([]Pin, error)
364 + Ls(context.Context, ...options.PinLsOption) ([]Pin, error)
365
366 // WithType is an option for Ls which allows to specify which pin types should
367 // be returned
@@ -360,10 +379,10 @@ type PinAPI interface {
379
380 // Update changes one pin to another, skipping checks for matching paths in
381 // the old tree
363 - Update(ctx context.Context, from Path, to Path) error
382 + Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error
383
384 // Verify verifies the integrity of pinned objects
366 - Verify(context.Context) error
385 + Verify(context.Context) (<-chan PinStatus, error)
386 }
387
388 var ErrIsDir = errors.New("object is a directory")
core/coreapi/interface/options/pin.go
+27
@@ -8,8 +8,13 @@ type PinLsSettings struct {
8 Type string
9 }
10
11 +type PinUpdateSettings struct {
12 + Unpin bool
13 +}
14 +
15 type PinAddOption func(*PinAddSettings) error
16 type PinLsOption func(settings *PinLsSettings) error
17 +type PinUpdateOption func(*PinUpdateSettings) error
18
19 func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) {
20 options := &PinAddSettings{
@@ -41,6 +46,21 @@ func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) {
46 return options, nil
47 }
48
49 +func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) {
50 + options := &PinUpdateSettings{
51 + Unpin: true,
52 + }
53 +
54 + for _, opt := range opts {
55 + err := opt(options)
56 + if err != nil {
57 + return nil, err
58 + }
59 + }
60 +
61 + return options, nil
62 +}
63 +
64 type PinOptions struct{}
65
66 func (api *PinOptions) WithRecursive(recucsive bool) PinAddOption {
@@ -56,3 +76,10 @@ func (api *PinOptions) WithType(t string) PinLsOption {
76 return nil
77 }
78 }
79 +
80 +func (api *PinOptions) WithUnpin(unpin bool) PinUpdateOption {
81 + return func(settings *PinUpdateSettings) error {
82 + settings.Unpin = unpin
83 + return nil
84 + }
85 +}
core/coreapi/pin.go
+168 -11
@@ -2,10 +2,15 @@ package coreapi
2
3 import (
4 "context"
5 + "fmt"
6
7 coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
8 caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
8 - "github.com/pkg/errors"
9 + corerepo "github.com/ipfs/go-ipfs/core/corerepo"
10 + merkledag "github.com/ipfs/go-ipfs/merkledag"
11 + pin "github.com/ipfs/go-ipfs/pin"
12 +
13 + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid"
14 )
15
16 type PinAPI struct {
@@ -13,22 +18,174 @@ type PinAPI struct {
18 *caopts.PinOptions
19 }
20
16 -func (api *PinAPI) Add(context.Context, coreiface.Path, ...caopts.PinAddOption) error {
17 - return errors.New("TODO")
21 +func (api *PinAPI) Add(ctx context.Context, p coreiface.Path, opts ...caopts.PinAddOption) error {
22 + settings, err := caopts.PinAddOptions(opts...)
23 + if err != nil {
24 + return err
25 + }
26 +
27 + defer api.node.Blockstore.PinLock().Unlock()
28 +
29 + _, err = corerepo.Pin(api.node, ctx, []string{p.String()}, settings.Recursive)
30 + if err != nil {
31 + return err
32 + }
33 +
34 + return nil
35 +}
36 +
37 +func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) ([]coreiface.Pin, error) {
38 + settings, err := caopts.PinLsOptions(opts...)
39 + if err != nil {
40 + return nil, err
41 + }
42 +
43 + switch settings.Type {
44 + case "all", "direct", "indirect", "recursive":
45 + default:
46 + return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
47 + }
48 +
49 + return pinLsAll(settings.Type, ctx, api.node.Pinning, api.node.DAG)
50 +}
51 +
52 +func (api *PinAPI) Rm(ctx context.Context, p coreiface.Path) error {
53 + _, err := corerepo.Unpin(api.node, ctx, []string{p.String()}, true)
54 + if err != nil {
55 + return err
56 + }
57 +
58 + return nil
59 +}
60 +
61 +func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface.Path, opts ...caopts.PinUpdateOption) error {
62 + settings, err := caopts.PinUpdateOptions(opts...)
63 + if err != nil {
64 + return err
65 + }
66 +
67 + return api.node.Pinning.Update(ctx, from.Cid(), to.Cid(), settings.Unpin)
68 +}
69 +
70 +type pinStatus struct {
71 + cid *cid.Cid
72 + ok bool
73 + badNodes []coreiface.BadPinNode
74 +}
75 +
76 +// BadNode is used in PinVerifyRes
77 +type badNode struct {
78 + cid *cid.Cid
79 + err error
80 +}
81 +
82 +func (s *pinStatus) Ok() bool {
83 + return s.Ok()
84 }
85
20 -func (api *PinAPI) Ls(context.Context) ([]coreiface.Pin, error) {
21 - return nil, errors.New("TODO")
86 +func (s *pinStatus) BadNodes() []coreiface.BadPinNode {
87 + return s.badNodes
88 }
89
24 -func (api *PinAPI) Rm(context.Context, coreiface.Path) error {
25 - return errors.New("TODO")
90 +func (n *badNode) Path() coreiface.Path {
91 + return ParseCid(n.cid)
92 }
93
28 -func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface.Path) error {
29 - return errors.New("TODO")
94 +func (n *badNode) Err() error {
95 + return n.err
96 }
97
32 -func (api *PinAPI) Verify(context.Context) error {
33 - return errors.New("TODO")
98 +func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, error) {
99 + visited := make(map[string]*pinStatus)
100 + getLinks := api.node.DAG.GetOfflineLinkService().GetLinks
101 + recPins := api.node.Pinning.RecursiveKeys()
102 +
103 + var checkPin func(root *cid.Cid) *pinStatus
104 + checkPin = func(root *cid.Cid) *pinStatus {
105 + key := root.String()
106 + if status, ok := visited[key]; ok {
107 + return status
108 + }
109 +
110 + links, err := getLinks(ctx, root)
111 + if err != nil {
112 + status := &pinStatus{ok: false, cid: root}
113 + status.badNodes = []coreiface.BadPinNode{&badNode{cid: root, err: err}}
114 + visited[key] = status
115 + return status
116 + }
117 +
118 + status := &pinStatus{ok: true, cid: root}
119 + for _, lnk := range links {
120 + res := checkPin(lnk.Cid)
121 + if !res.ok {
122 + status.ok = false
123 + status.badNodes = append(status.badNodes, res.badNodes...)
124 + }
125 + }
126 +
127 + visited[key] = status
128 + return status
129 + }
130 +
131 + out := make(chan coreiface.PinStatus)
132 + go func() {
133 + defer close(out)
134 + for _, c := range recPins {
135 + out <- checkPin(c)
136 + }
137 + }()
138 +
139 + return out, nil
140 +}
141 +
142 +type pinInfo struct {
143 + pinType string
144 + object *cid.Cid
145 +}
146 +
147 +func (p *pinInfo) Path() coreiface.Path {
148 + return ParseCid(p.object)
149 +}
150 +
151 +func (p *pinInfo) Type() string {
152 + return p.pinType
153 +}
154 +
155 +func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag merkledag.DAGService) ([]coreiface.Pin, error) {
156 +
157 + keys := make(map[string]*pinInfo)
158 +
159 + AddToResultKeys := func(keyList []*cid.Cid, typeStr string) {
160 + for _, c := range keyList {
161 + keys[c.String()] = &pinInfo{
162 + pinType: typeStr,
163 + object: c,
164 + }
165 + }
166 + }
167 +
168 + if typeStr == "direct" || typeStr == "all" {
169 + AddToResultKeys(pinning.DirectKeys(), "direct")
170 + }
171 + if typeStr == "indirect" || typeStr == "all" {
172 + set := cid.NewSet()
173 + for _, k := range pinning.RecursiveKeys() {
174 + err := merkledag.EnumerateChildren(ctx, dag.GetLinks, k, set.Visit)
175 + if err != nil {
176 + return nil, err
177 + }
178 + }
179 + AddToResultKeys(set.Keys(), "indirect")
180 + }
181 + if typeStr == "recursive" || typeStr == "all" {
182 + AddToResultKeys(pinning.RecursiveKeys(), "recursive")
183 + }
184 +
185 + out := make([]coreiface.Pin, 0, len(keys))
186 + for _, v := range keys {
187 + out = append(out, v)
188 + }
189 +
190 + return out, nil
191 }