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
}