-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract and rework commands package #3856
Conversation
core/commands/active.go
Outdated
ctx := req.InvocContext() | ||
log.Debug("active reqs: ctx=", ctx) | ||
reqLog := ctx.ReqLog | ||
log.Debug("active reqs: reqlog=", reqLog) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you can remove all the debug logging here once youre done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course!
core/commands/add.go
Outdated
@@ -17,7 +20,7 @@ import ( | |||
mfs "github.com/ipfs/go-ipfs/mfs" | |||
ft "github.com/ipfs/go-ipfs/unixfs" | |||
|
|||
u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" | |||
//u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no longer importing this package? Thats wonderful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make an effort to get rid of it in other places as well if you want.
core/commands/add.go
Outdated
} | ||
res.SetOutput(nil) | ||
}, | ||
PostRun: map[cmds.EncodingType]func(cmds.Request, cmds.ResponseEmitter) cmds.ResponseEmitter{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be type-defed to something like cmds.EmitterMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
core/commands/add.go
Outdated
} | ||
|
||
// TODO why does err != io.EOF return true? | ||
if err.Error() != "EOF" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
odd. Is this still the case? Maybe we recreated an error somewhere like errors.New(err.Error())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll check
core/commands/bootstrap.go
Outdated
) | ||
|
||
if ch, ok = res.Output().(<-chan interface{}); !ok { | ||
if ch, ok = res.Output().(chan interface{}); !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably ensure that its only ever a <-chan interface{}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I think I tried that due to the weird shimming layer, but I'll take another look
core/commands/dht.go
Outdated
return nil, u.ErrCast() | ||
} | ||
|
||
cmds.Text: func() func(cmds.Response) (io.Reader, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should have a type defined for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I couldn't come up with a good name :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uhm.... hrm. Maybe ResponseGenFunc or something?
core/commands/filestore.go
Outdated
return | ||
} | ||
args := req.Arguments() | ||
if len(args) > 0 { | ||
out := perKeyActionToChan(args, func(c *cid.Cid) *filestore.ListRes { | ||
return filestore.List(fs, c) | ||
}, req.Context()) | ||
res.SetOutput(out) | ||
|
||
for v := range out { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for this sort of thing, we should do a for-select with the request context to allow for cancellation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we rather need to pass ctx
to Emit
core/commands/filestore.go
Outdated
out := listResToChan(next, req.Context()) | ||
res.SetOutput(out) | ||
for v := range out { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe since we do this often, we should have some sort of re.PipeOutput(ch)
method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually some of the ResponseEmitters already copy on Emit. Will make sure they all behave that way
core/commands/filestore.go
Outdated
errors bool | ||
) | ||
|
||
for err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd use a switch here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how a switch would help here but I made a change that removes the condition from the loop head
core/commands/get.go
Outdated
|
||
v, err := res.Next() | ||
if err != nil { | ||
//TODO XXX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
core/commands/id.go
Outdated
ch, ok := res.Output().(chan interface{}) | ||
if !ok { | ||
log.Debugf("received type %T", res.Output()) | ||
return nil, u.ErrCast() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should have a new error thing that prints out the expected and actual types. Instead of the ugly ErrCast
nonsense (no need to do this now though)
core/commands/object/object.go
Outdated
if !ok { | ||
ch, ok = res.Output().(<-chan interface{}) | ||
if !ok { | ||
panic("object.get marshaler: res.Output() is not chan interface{}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably don't want to panic. returning an error is preferable here
core/commands/object/object.go
Outdated
} | ||
} | ||
|
||
node := (<-ch).(*Node) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sketchy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I don't check the type? I guess I need to find a general answer for this...ErrCast is also stupid
core/commands/object/object.go
Outdated
if ch_, ok := res.Output().(chan interface{}); ok { | ||
ch = ch_ | ||
} else { | ||
ch = res.Output().(chan interface{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"else, fuck it, do it anyways"
core/commands/pin.go
Outdated
if out.Pins != nil { | ||
added = out.Pins | ||
} else { | ||
fmt.Fprintf(res.Stderr(), "Fetched/Processed %d nodes\r", out.Progress) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should only be done when the progress option is enabled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is implicit because out.Pins
is non-nil if the progress option is disabled
core/commands/repo.go
Outdated
out <- &VerifyProgress{Message: "verify complete, some blocks were corrupt."} | ||
//out <- &VerifyProgress{Message: "verify complete, some blocks were corrupt."} | ||
log.Debugf("Run.go.else: %T.SetError", res) | ||
res.SetError(fmt.Errorf("verify complete, some blocks were corrupt"), cmdsutil.ErrNormal) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to make SetError accept just a string optionally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two thoughts. First, this is the old Response
and I didn't change it too much. Here we still have to pass error, ErrorType
. With the new ones it is interface{}, ErrorType
. Second, making it accept only a string (or interface{}
for that matter) seems kind of sketchy because the only way to do it is to make it variadic and thus accept more than one ErrorType
which is also weird:
func (re *ResponsEmitter) SetError(msg interface{}, codes ...ErrorType)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with not changing the old stuff, but theres no need to make it variadic. Just change the signature to:
func (res Response) SetError(msg interface{}, code ErrorType)
Then in the function you would type switch msg to either an error or a string, and just fmt.Sprint
anything else that wasnt one of those.
But, like it said, don't bother spending time changing the legacy stuff :)
@@ -13,6 +13,7 @@ test_expect_success "current dir is writable" ' | |||
' | |||
|
|||
test_expect_success "ipfs version succeeds" ' | |||
ipfs version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed?
test/sharness/t0020-init.sh
Outdated
@@ -128,7 +128,7 @@ test_expect_success "clean up ipfs dir" ' | |||
|
|||
test_init_ipfs | |||
|
|||
test_launch_ipfs_daemon | |||
test_launch_ipfs_daemon -D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the -D for?
test/sharness/t0030-mount.sh
Outdated
@@ -58,6 +58,7 @@ test_expect_success "mount directories cannot be removed while active" ' | |||
' | |||
|
|||
test_expect_success "unmount directories" ' | |||
sleep 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed?
test/sharness/t0051-object.sh
Outdated
@@ -89,6 +89,10 @@ test_object_cmd() { | |||
' | |||
|
|||
test_expect_success "'ipfs object put broken.hxml' output looks good" ' | |||
# TODO remove this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking really good. I left a few comments, mostly will want all the excess debug statements and commented out things removed before merge. Also i'm very skeptical of any changes to the sharness tests.
Also go-ipfs-cmds has to be gxify'ed, Codeclimate and CI should pass. |
Thanks! Yes, sharness changes are temporary so I see why the test fails and to skip those that don't work on my machine
Of course! Once it is gxified CI should be at least able to test it. And I'll stir those commits again anyway, in the end there will be a clean signed off and licensed set of commits. Maybe I'll even squash it down to one commit so there are no commits that don't pass the tests. What is your preference on this? |
If there are commits that do not pass, better squash them. |
34245d5
to
2f8df94
Compare
@keks could you either sign those commits off or squash them? The gitcop notifications are murdering me |
Yeah sorry. I wanted to squash later and going through all of them is also a hassle. I'll just squash early. Btw it seems I surfaced the race condition problem. Not sure yet how to tackle that one when it only fires on CI but not locally. |
Me and @whyrusleeping were inspecting and exploring options on how to proceed. tl;dr; Here is the type of responses + respective headers we need
This is a breaking http-api change but it might not be breaking for every API client as JS and Go were the primary users of x-chunked vs x-stream that we no longer need. |
(note: 'catdog' is just a placeholder so we didnt confuse ourselves with 'Stream' and 'Chunk' because they have meanings other than the meaning that is implied by the header) |
We could also be explicit about it: ContentType: |
d05110d
to
2625c2c
Compare
Whoops. Wrong button. This now passes js-ipfs-api, go test and sharness. I made a tiny change to one sharness test, I hope that's fine. I'm happy to change the HTTP header semantics, but please let's not do it in this PR as it is not actually within scope. |
@keks i think you need to update your go-ipfs-cmds dep. I'm getting some weird preamble thing printed out in my daemon |
6ac1c1e
to
7d2039a
Compare
7d2039a
to
434ee5c
Compare
License: MIT Signed-off-by: keks <keks@cryptoscope.co>
License: MIT Signed-off-by: keks <keks@cryptoscope.co>
License: MIT Signed-off-by: keks <keks@cryptoscope.co>
434ee5c
to
d95a87c
Compare
off we go! |
Extract and rework commands package
Extract and rework commands package
This PR rips out
commands/
, changes a lot of stuff and puts it in go-ipfs-cmds. This PR is WIP and primarily here for reviewing.see #3524 and go-ipfs-cmds
Some remarks:
Backwards compatibility
We don't want to rewrite all of
core/commands
at once, so I built a shimming layer that allows us to use the current go-ipfs/commands.Command structs (with some limitations). All the shimming code is ingo-ipfs-cmds/legacy.go
. That is a lot of code and in the medium term it would be great to dump it. For that we need to use the new commands library in all of core/commands. That is not a short-term project though.Shared code
There is quite a lot of shared code between go-ipfs/commands and go-ipfs-cmds. To reduce the number of type conversions, I moved most of it to go-ipfs-cmds/cmdsutil. I don't think this is a very good name though and it might change in the future.
$pkg/${pkg}util
usually contains code that operates on types in$pkg
and here it's the other way around: cmdsutil contains a lot of basic tools (e.g. an error type) that is used by both go-ipfs-cmds and go-ipfs/commands. Maybego-ipfs-cmds/core
is better?Basic model
Most changes I made affected the type
Response
, which I split up intoResponse
andResponseEmitter
. Basically a producer is given aResponseEmitter
on which it can callEmit(v)
a few times (basically it's a channel write) - or just once ifv
is an io.Reader. These values can be received by a consumer which has the correspondingResponse
by callingv, err := res.Next()
. These can be chained at will - which actually happens when there is aPostRun
.Marshalers, Encoders, PostRun
When I started this project I complained about PostRun. I still think it's a bit weird, but it fulfills an important use case.
In go-ipfs/commands, the
Marshal
function takes the value passed toSetOutput
and do it's magic to build a bytestream from that. In go-ipfs-cmds there is no singular value, instead we operate on streams.Encode()
is very similar, but instead it operates on a single emitted value. It is called once per call toEmit
. This means that no state is kept between emitted values. If you want to e.g. not abort on errors and print an error digest after everything completed, you need to usePostRun
.PostRun
takes a request and the actual ResponseEmitter and returns a new ResponseEmitter. Usually the first thing that happens in a PostRun is to create aResponseEmitter
/Response
pair backed by a channel. In a goroutine we read from thatResponse
, do our thing, and send it to the underlying ResponseEmitter. After calling the goroutine we return the channel-backedResponseEmitter
. Usually the call to PostRun looks like this:That way the returned ResponseEmitter will be used by the Run function and all emitted values will pass through PostRun before ending up at the final destination (usually a
cmds/cli.ResponseEmitter
). This allows building flexible pipelines.Changes to the sharness tests
I changed some small stuff in the sharness tests. I don't intend to keep these changes, but they are currently handy. The changes basically include a lot of debug output and a few disabled tests, because my computer doesn't like tests that use
nc
. I don't know why, but they also fail for master. Maybe you can try to run them? That is t0060, t0061 and t0235. Remove thetest_done
from the test's preamble.I also wasn't able to test fuse and docker.
That was a lot of explanation. Let me know what you think!