提交 f338520a 作者: Kevin Atkinson

gc: add option to stream errors

License: MIT
Signed-off-by: 's avatarKevin Atkinson <k@kevina.org>
上级 401d1565
...@@ -40,6 +40,11 @@ var RepoCmd = &cmds.Command{ ...@@ -40,6 +40,11 @@ var RepoCmd = &cmds.Command{
}, },
} }
type GcResult struct {
Key *cid.Cid
Error string `json:",omitempty"`
}
var repoGcCmd = &cmds.Command{ var repoGcCmd = &cmds.Command{
Helptext: cmds.HelpText{ Helptext: cmds.HelpText{
Tagline: "Perform a garbage collection sweep on the repo.", Tagline: "Perform a garbage collection sweep on the repo.",
...@@ -51,6 +56,7 @@ order to reclaim hard disk space. ...@@ -51,6 +56,7 @@ order to reclaim hard disk space.
}, },
Options: []cmds.Option{ Options: []cmds.Option{
cmds.BoolOption("quiet", "q", "Write minimal output.").Default(false), cmds.BoolOption("quiet", "q", "Write minimal output.").Default(false),
cmds.BoolOption("stream-errors", "Stream errors.").Default(false),
}, },
Run: func(req cmds.Request, res cmds.Response) { Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode() n, err := req.InvocContext().GetNode()
...@@ -59,22 +65,41 @@ order to reclaim hard disk space. ...@@ -59,22 +65,41 @@ order to reclaim hard disk space.
return return
} }
streamErrors, _, _ := res.Request().Option("stream-errors").Bool()
gcOutChan := corerepo.GarbageCollectAsync(n, req.Context()) gcOutChan := corerepo.GarbageCollectAsync(n, req.Context())
outChan := make(chan interface{}, len(gcOutChan)) outChan := make(chan interface{}, cap(gcOutChan))
res.SetOutput((<-chan interface{})(outChan)) res.SetOutput((<-chan interface{})(outChan))
go func() { go func() {
defer close(outChan) defer close(outChan)
err := corerepo.CollectResult(req.Context(), gcOutChan, func(k *cid.Cid) { unreportedError := false
outChan <- &corerepo.KeyRemoved{k} var lastErr error
if streamErrors {
for res := range gcOutChan {
if unreportedError {
outChan <- &GcResult{Error: lastErr.Error()}
unreportedError = false
}
if res.Error != nil {
lastErr = res.Error
unreportedError = true
} else {
outChan <- &GcResult{Key: res.KeyRemoved}
}
}
} else {
lastErr = corerepo.CollectResult(req.Context(), gcOutChan, func(k *cid.Cid) {
outChan <- &GcResult{Key: k}
}) })
if err != nil { }
res.SetError(err, cmds.ErrNormal) if lastErr != nil {
res.SetError(lastErr, cmds.ErrNormal)
} }
}() }()
}, },
Type: corerepo.KeyRemoved{}, Type: GcResult{},
Marshalers: cmds.MarshalerMap{ Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) { cmds.Text: func(res cmds.Response) (io.Reader, error) {
outChan, ok := res.Output().(<-chan interface{}) outChan, ok := res.Output().(<-chan interface{})
...@@ -87,26 +112,29 @@ order to reclaim hard disk space. ...@@ -87,26 +112,29 @@ order to reclaim hard disk space.
return nil, err return nil, err
} }
marshal := func(v interface{}) (io.Reader, error) { for v := range outChan {
obj, ok := v.(*corerepo.KeyRemoved) obj, ok := v.(*GcResult)
if !ok { if !ok {
return nil, u.ErrCast() return nil, u.ErrCast()
} }
buf := new(bytes.Buffer) if obj.Error != "" {
fmt.Fprintf(res.Stderr(), "Error: %s\n", obj.Error)
continue
}
if quiet { if quiet {
buf = bytes.NewBufferString(obj.Key.String() + "\n") fmt.Fprintf(res.Stdout(), "%s\n", obj.Key.String())
} else { } else {
buf = bytes.NewBufferString(fmt.Sprintf("removed %s\n", obj.Key)) fmt.Fprintf(res.Stdout(), "removed %s\n", obj.Key.String())
} }
return buf, nil
} }
return &cmds.ChannelMarshaler{ if res.Error() != nil {
Channel: outChan, return nil, res.Error()
Marshaler: marshal, }
Res: res,
}, nil return nil, nil
}, },
}, },
} }
......
...@@ -20,10 +20,6 @@ var log = logging.Logger("corerepo") ...@@ -20,10 +20,6 @@ var log = logging.Logger("corerepo")
var ErrMaxStorageExceeded = errors.New("Maximum storage limit exceeded. Maybe unpin some files?") var ErrMaxStorageExceeded = errors.New("Maximum storage limit exceeded. Maybe unpin some files?")
type KeyRemoved struct {
Key *cid.Cid
}
type GC struct { type GC struct {
Node *core.IpfsNode Node *core.IpfsNode
Repo repo.Repo Repo repo.Repo
......
...@@ -134,6 +134,13 @@ test_gc_robust_part2() { ...@@ -134,6 +134,13 @@ test_gc_robust_part2() {
grep -q "aborted" repo_gc_out grep -q "aborted" repo_gc_out
' '
test_expect_success "'ipfs repo gc --stream-errors' should abort and report each error separately" '
test_must_fail ipfs repo gc --stream-errors 2>&1 | tee repo_gc_out &&
grep -q "Error: could not retrieve links for $LEAF1" repo_gc_out &&
grep -q "Error: could not retrieve links for $LEAF2" repo_gc_out &&
grep -q "Error: garbage collection aborted" repo_gc_out
'
test_expect_success "unpin 1MB file" ' test_expect_success "unpin 1MB file" '
ipfs pin rm $HASH2 ipfs pin rm $HASH2
' '
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论