"pin verify": basic implementation
License: MIT Signed-off-by: Kevin Atkinson <k@kevina.org>
Kevin Atkinson committed
Mar 29, 2017 at 17:02 UTC
325e1c083943b94f2a4ef2d77af00ae41a43f3a2
1 file changed
+107
core/commands/pin.go
+107
@@ -27,6 +27,7 @@ var PinCmd = &cmds.Command{
27
"add": addPinCmd,
28
"rm": rmPinCmd,
29
"ls": listPinCmd,
30
+ "verify": verifyPinCmd,
31
"update": updatePinCmd,
32
},
33
}
@@ -410,6 +411,27 @@ new pin and removing the old one.
411
},
412
}
413
414
+var verifyPinCmd = &cmds.Command{
415
+ Helptext: cmds.HelpText{
416
+ Tagline: "Verify that recursive pins are complete.",
417
+ },
418
+ Run: func(req cmds.Request, res cmds.Response) {
419
+ n, _ := req.InvocContext().GetNode()
420
+
421
+ rdr, wtr := io.Pipe()
422
+ out := pinVerify(req.Context(), n)
423
+
424
+ go func() {
425
+ defer wtr.Close()
426
+ for r := range out {
427
+ r.Format(wtr)
428
+ }
429
+ }()
430
+
431
+ res.SetOutput(rdr)
432
+ },
433
+}
434
+
435
type RefKeyObject struct {
436
Type string
437
}
@@ -492,6 +514,91 @@ func pinLsAll(typeStr string, ctx context.Context, n *core.IpfsNode) (map[string
514
return keys, nil
515
}
516
517
+type pinStatus struct {
518
+ // use pointer to array slice to reduce memory usage
519
+ // the value should not be nil unless uninitialized
520
+ badNodes *[]badNode
521
+}
522
+
523
+type badNode struct {
524
+ cid *cid.Cid
525
+ err error
526
+}
527
+
528
+type pinVerifyRes struct {
529
+ cid *cid.Cid
530
+ pinStatus
531
+}
532
+
533
+func pinVerify(ctx context.Context, n *core.IpfsNode) <-chan pinVerifyRes {
534
+ visited := make(map[string]pinStatus)
535
+ getLinks := n.DAG.GetOfflineLinkService().GetLinks
536
+ emptySlice := &[]badNode{}
537
+ recPins := n.Pinning.RecursiveKeys()
538
+
539
+ var checkPin func(root *cid.Cid) pinStatus
540
+ checkPin = func(root *cid.Cid) pinStatus {
541
+ key := root.String()
542
+ if status, ok := visited[key]; ok {
543
+ if status.badNodes == nil {
544
+ return pinStatus{&[]badNode{badNode{
545
+ cid: root,
546
+ err: fmt.Errorf("Cycle Detected.")}}}
547
+ }
548
+ return status
549
+ }
550
+
551
+ links, err := getLinks(ctx, root)
552
+ if err != nil {
553
+ status := pinStatus{&[]badNode{badNode{cid: root, err: err}}}
554
+ visited[key] = status
555
+ return status
556
+ }
557
+
558
+ status := pinStatus{}
559
+ visited[key] = status // paranoid mode cycle detection
560
+ for _, lnk := range links {
561
+ res := checkPin(lnk.Cid)
562
+ if len(*res.badNodes) > 0 {
563
+ if status.badNodes == nil {
564
+ status.badNodes = res.badNodes
565
+ } else {
566
+ slice := append(*status.badNodes, *res.badNodes...)
567
+ status.badNodes = &slice
568
+ }
569
+ }
570
+ }
571
+ if status.badNodes == nil {
572
+ status.badNodes = emptySlice // prevent special cases
573
+ }
574
+
575
+ visited[key] = status
576
+ return status
577
+ }
578
+
579
+ out := make(chan pinVerifyRes)
580
+ go func() {
581
+ defer close(out)
582
+ for _, cid := range recPins {
583
+ pinStatus := checkPin(cid)
584
+ out <- pinVerifyRes{cid, pinStatus}
585
+ }
586
+ }()
587
+
588
+ return out
589
+}
590
+
591
+func (r pinVerifyRes) Format(out io.Writer) {
592
+ if len(*r.badNodes) == 0 {
593
+ fmt.Fprintf(out, "%s ok\n", r.cid)
594
+ } else {
595
+ fmt.Fprintf(out, "%s broken\n", r.cid)
596
+ for _, e := range *r.badNodes {
597
+ fmt.Fprintf(out, " %s: %s\n", e.cid, e.err)
598
+ }
599
+ }
600
+}
601
+
602
func cidsToStrings(cs []*cid.Cid) []string {
603
out := make([]string, 0, len(cs))
604
for _, c := range cs {