mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-28 08:47:42 +08:00
coreapi: implement pin api
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
This commit is contained in:
@ -52,6 +52,10 @@ func (api *CoreAPI) Object() coreiface.ObjectAPI {
|
||||
return &ObjectAPI{api, nil}
|
||||
}
|
||||
|
||||
func (api *CoreAPI) Pin() coreiface.PinAPI {
|
||||
return &PinAPI{api, nil}
|
||||
}
|
||||
|
||||
// ResolveNode resolves the path `p` using Unixfx resolver, gets and returns the
|
||||
// resolved Node.
|
||||
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
|
||||
|
@ -67,6 +67,24 @@ type Pin interface {
|
||||
Type() string
|
||||
}
|
||||
|
||||
// PinStatus holds information about pin health
|
||||
type PinStatus interface {
|
||||
// Ok indicates whether the pin has been verified to be correct
|
||||
Ok() bool
|
||||
|
||||
// BadNodes returns any bad (usually missing) nodes from the pin
|
||||
BadNodes() []BadPinNode
|
||||
}
|
||||
|
||||
// BadPinNode is a node that has been marked as bad by Pin.Verify
|
||||
type BadPinNode interface {
|
||||
// Path is the path of the node
|
||||
Path() Path
|
||||
|
||||
// Err is the reason why the node has been marked as bad
|
||||
Err() error
|
||||
}
|
||||
|
||||
// CoreAPI defines an unified interface to IPFS for Go programs.
|
||||
type CoreAPI interface {
|
||||
// Unixfs returns an implementation of Unixfs API.
|
||||
@ -83,6 +101,7 @@ type CoreAPI interface {
|
||||
|
||||
// Key returns an implementation of Key API.
|
||||
Key() KeyAPI
|
||||
Pin() PinAPI
|
||||
|
||||
// ObjectAPI returns an implementation of Object API
|
||||
Object() ObjectAPI
|
||||
@ -342,7 +361,7 @@ type PinAPI interface {
|
||||
WithRecursive(bool) options.PinAddOption
|
||||
|
||||
// Ls returns list of pinned objects on this node
|
||||
Ls(context.Context) ([]Pin, error)
|
||||
Ls(context.Context, ...options.PinLsOption) ([]Pin, error)
|
||||
|
||||
// WithType is an option for Ls which allows to specify which pin types should
|
||||
// be returned
|
||||
@ -360,10 +379,10 @@ type PinAPI interface {
|
||||
|
||||
// Update changes one pin to another, skipping checks for matching paths in
|
||||
// the old tree
|
||||
Update(ctx context.Context, from Path, to Path) error
|
||||
Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error
|
||||
|
||||
// Verify verifies the integrity of pinned objects
|
||||
Verify(context.Context) error
|
||||
Verify(context.Context) (<-chan PinStatus, error)
|
||||
}
|
||||
|
||||
var ErrIsDir = errors.New("object is a directory")
|
||||
|
@ -8,8 +8,13 @@ type PinLsSettings struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
type PinUpdateSettings struct {
|
||||
Unpin bool
|
||||
}
|
||||
|
||||
type PinAddOption func(*PinAddSettings) error
|
||||
type PinLsOption func(settings *PinLsSettings) error
|
||||
type PinUpdateOption func(*PinUpdateSettings) error
|
||||
|
||||
func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) {
|
||||
options := &PinAddSettings{
|
||||
@ -41,6 +46,21 @@ func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) {
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) {
|
||||
options := &PinUpdateSettings{
|
||||
Unpin: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type PinOptions struct{}
|
||||
|
||||
func (api *PinOptions) WithRecursive(recucsive bool) PinAddOption {
|
||||
@ -56,3 +76,10 @@ func (api *PinOptions) WithType(t string) PinLsOption {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (api *PinOptions) WithUnpin(unpin bool) PinUpdateOption {
|
||||
return func(settings *PinUpdateSettings) error {
|
||||
settings.Unpin = unpin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,15 @@ package coreapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
|
||||
caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
|
||||
"github.com/pkg/errors"
|
||||
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
||||
merkledag "github.com/ipfs/go-ipfs/merkledag"
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid"
|
||||
)
|
||||
|
||||
type PinAPI struct {
|
||||
@ -13,22 +18,174 @@ type PinAPI struct {
|
||||
*caopts.PinOptions
|
||||
}
|
||||
|
||||
func (api *PinAPI) Add(context.Context, coreiface.Path, ...caopts.PinAddOption) error {
|
||||
return errors.New("TODO")
|
||||
func (api *PinAPI) Add(ctx context.Context, p coreiface.Path, opts ...caopts.PinAddOption) error {
|
||||
settings, err := caopts.PinAddOptions(opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer api.node.Blockstore.PinLock().Unlock()
|
||||
|
||||
_, err = corerepo.Pin(api.node, ctx, []string{p.String()}, settings.Recursive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *PinAPI) Ls(context.Context) ([]coreiface.Pin, error) {
|
||||
return nil, errors.New("TODO")
|
||||
func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) ([]coreiface.Pin, error) {
|
||||
settings, err := caopts.PinLsOptions(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch settings.Type {
|
||||
case "all", "direct", "indirect", "recursive":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
|
||||
}
|
||||
|
||||
return pinLsAll(settings.Type, ctx, api.node.Pinning, api.node.DAG)
|
||||
}
|
||||
|
||||
func (api *PinAPI) Rm(context.Context, coreiface.Path) error {
|
||||
return errors.New("TODO")
|
||||
func (api *PinAPI) Rm(ctx context.Context, p coreiface.Path) error {
|
||||
_, err := corerepo.Unpin(api.node, ctx, []string{p.String()}, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface.Path) error {
|
||||
return errors.New("TODO")
|
||||
func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface.Path, opts ...caopts.PinUpdateOption) error {
|
||||
settings, err := caopts.PinUpdateOptions(opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.node.Pinning.Update(ctx, from.Cid(), to.Cid(), settings.Unpin)
|
||||
}
|
||||
|
||||
func (api *PinAPI) Verify(context.Context) error {
|
||||
return errors.New("TODO")
|
||||
type pinStatus struct {
|
||||
cid *cid.Cid
|
||||
ok bool
|
||||
badNodes []coreiface.BadPinNode
|
||||
}
|
||||
|
||||
// BadNode is used in PinVerifyRes
|
||||
type badNode struct {
|
||||
cid *cid.Cid
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *pinStatus) Ok() bool {
|
||||
return s.Ok()
|
||||
}
|
||||
|
||||
func (s *pinStatus) BadNodes() []coreiface.BadPinNode {
|
||||
return s.badNodes
|
||||
}
|
||||
|
||||
func (n *badNode) Path() coreiface.Path {
|
||||
return ParseCid(n.cid)
|
||||
}
|
||||
|
||||
func (n *badNode) Err() error {
|
||||
return n.err
|
||||
}
|
||||
|
||||
func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, error) {
|
||||
visited := make(map[string]*pinStatus)
|
||||
getLinks := api.node.DAG.GetOfflineLinkService().GetLinks
|
||||
recPins := api.node.Pinning.RecursiveKeys()
|
||||
|
||||
var checkPin func(root *cid.Cid) *pinStatus
|
||||
checkPin = func(root *cid.Cid) *pinStatus {
|
||||
key := root.String()
|
||||
if status, ok := visited[key]; ok {
|
||||
return status
|
||||
}
|
||||
|
||||
links, err := getLinks(ctx, root)
|
||||
if err != nil {
|
||||
status := &pinStatus{ok: false, cid: root}
|
||||
status.badNodes = []coreiface.BadPinNode{&badNode{cid: root, err: err}}
|
||||
visited[key] = status
|
||||
return status
|
||||
}
|
||||
|
||||
status := &pinStatus{ok: true, cid: root}
|
||||
for _, lnk := range links {
|
||||
res := checkPin(lnk.Cid)
|
||||
if !res.ok {
|
||||
status.ok = false
|
||||
status.badNodes = append(status.badNodes, res.badNodes...)
|
||||
}
|
||||
}
|
||||
|
||||
visited[key] = status
|
||||
return status
|
||||
}
|
||||
|
||||
out := make(chan coreiface.PinStatus)
|
||||
go func() {
|
||||
defer close(out)
|
||||
for _, c := range recPins {
|
||||
out <- checkPin(c)
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type pinInfo struct {
|
||||
pinType string
|
||||
object *cid.Cid
|
||||
}
|
||||
|
||||
func (p *pinInfo) Path() coreiface.Path {
|
||||
return ParseCid(p.object)
|
||||
}
|
||||
|
||||
func (p *pinInfo) Type() string {
|
||||
return p.pinType
|
||||
}
|
||||
|
||||
func pinLsAll(typeStr string, ctx context.Context, pinning pin.Pinner, dag merkledag.DAGService) ([]coreiface.Pin, error) {
|
||||
|
||||
keys := make(map[string]*pinInfo)
|
||||
|
||||
AddToResultKeys := func(keyList []*cid.Cid, typeStr string) {
|
||||
for _, c := range keyList {
|
||||
keys[c.String()] = &pinInfo{
|
||||
pinType: typeStr,
|
||||
object: c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if typeStr == "direct" || typeStr == "all" {
|
||||
AddToResultKeys(pinning.DirectKeys(), "direct")
|
||||
}
|
||||
if typeStr == "indirect" || typeStr == "all" {
|
||||
set := cid.NewSet()
|
||||
for _, k := range pinning.RecursiveKeys() {
|
||||
err := merkledag.EnumerateChildren(ctx, dag.GetLinks, k, set.Visit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
AddToResultKeys(set.Keys(), "indirect")
|
||||
}
|
||||
if typeStr == "recursive" || typeStr == "all" {
|
||||
AddToResultKeys(pinning.RecursiveKeys(), "recursive")
|
||||
}
|
||||
|
||||
out := make([]coreiface.Pin, 0, len(keys))
|
||||
for _, v := range keys {
|
||||
out = append(out, v)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user