Skip to content

Commit

Permalink
coreapi: implement pin api
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
  • Loading branch information
magik6k committed Jan 10, 2018
1 parent 140e7bd commit 9daaa01
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 14 deletions.
4 changes: 4 additions & 0 deletions core/coreapi/coreapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func (api *CoreAPI) Key() coreiface.KeyAPI {
return &KeyAPI{api, nil}
}

func (api *CoreAPI) Pin() coreiface.PinAPI {
return &PinAPI{api, nil}
}

func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
p, err := api.ResolvePath(ctx, p)
if err != nil {
Expand Down
25 changes: 22 additions & 3 deletions core/coreapi/interface/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,32 @@ 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
Unixfs() UnixfsAPI
Dag() DagAPI
Name() NameAPI
Key() KeyAPI
Pin() PinAPI

// ResolvePath resolves the path using Unixfs resolver
ResolvePath(context.Context, Path) (Path, error)
Expand Down Expand Up @@ -220,7 +239,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
Expand All @@ -238,10 +257,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")
Expand Down
27 changes: 27 additions & 0 deletions core/coreapi/interface/options/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
}
179 changes: 168 additions & 11 deletions core/coreapi/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,190 @@ 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 {
*CoreAPI
*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(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(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, 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)
}

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 (api *PinAPI) Ls(context.Context) ([]coreiface.Pin, error) {
return nil, errors.New("TODO")
func (s *pinStatus) BadNodes() []coreiface.BadPinNode {
return s.badNodes
}

func (api *PinAPI) Rm(context.Context, coreiface.Path) error {
return errors.New("TODO")
func (n *badNode) Path() coreiface.Path {
return ParseCid(n.cid)
}

func (api *PinAPI) Update(ctx context.Context, from coreiface.Path, to coreiface.Path) error {
return errors.New("TODO")
func (n *badNode) Err() error {
return n.err
}

func (api *PinAPI) Verify(context.Context) error {
return errors.New("TODO")
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
}

0 comments on commit 9daaa01

Please sign in to comment.