-
Notifications
You must be signed in to change notification settings - Fork 379
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
Put context.Context arguments on almost everything #431
Conversation
625295f
to
adcbd74
Compare
@@ -1,3 +1,5 @@ | |||
github.com/containers/image |
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.
One-minute look, will review in detail later:
- ^^ This seems unexpected.
- Overall having a
context.Context
in most places is definitely valuable. - I’m not sure about passing
types.SystemContext
andcontext.Context
by a single parameter, notably becauseSystemContext
currently intentionally violates the “Do not storeContext
s inside a struct type” rule. Combining them may still be the right thing to do, I’ll need to take a deeper look.
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.
Just replying to the first point; the first line of the trash vendor.conf identifies the package root (e.g.); trash gives me a warning without it (presumably because I use a multiple-GOPATH and it can't identify the canonical parent on its own).
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.
Ah, I had no idea that was a thing. Thanks.
That’s not sufficient; if anything actually tries to use the |
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.
Overall, I don’t think transferring types.SystemContext
as a Value
in context.Context
makes sense; the two should remain separate.
- Looking at how
copy.Image
uses separatetypes.SystemContext
values for the source and destination (copy.Options.{SourceCtx,DestinationCtx}
), it’s clear that the two are not semantically close enough:types.SystemContext
is an options storage mechanism, which is propagated along with the relevant data structures;context.Context
is a control flow management mechanism, which is propagated along the call stacks and goroutine creations. A single value can’t cleanly be both. - As mentioned earlier, the way
SystemContext
is stored inImageSource
/ImageDestination
implementations violates thecontext.Context
rules (though, arguably, so does the cancelation behavior ofhttp.Request
/http.Response
). - (
types.SystemContext
is commonlynil
for programs which don’t need non-default behavior, and supporting that forcontext.Context
values is not cleanly possible, nor even desirable—linters which validate thecontext.Context
usage rules should warn about usingnil
.)
So, I think the two parameters should stay separate, like the current docker.CheckAuth
.
We might consider renaming SystemContext
to make the two easier to distinguish (CIOptions
?), but that’s really a separate discussion and probably a separate PR, if we should do this at all.
Cc: @runcom if he disagrees.
If this PR adds the context.Context
parameters, they should actually be used. This is most important for the blocking <- channel
operations.
Similarly, though perhaps less urgently, I think many of the local-filesystem operations should be cancelable if they work on layers or whole images; copying a multi-gigabyte image may well take long enough that the caller may realize in the middle that the operation is not needed after all. AFAICT having a cancelable io.Copy
helper would make this reasonably easy. OTOH the local disks tend to still be reasonably fast and the most important local storage mechanisms (containers-storage:
) seem to not have implemented cancelation/interruptible operations so far, so this is not completely blocking for me; adding FIXME notes to the relevant places would be nice, though.
https://golang.org/pkg/context/#Background documents context.Background
as “typically used by…tests”; if we are not actually trying to test cancelation, that’s what we should do. context.TODO
is pretty much equivalent to context.Background
, and exists primarily to make missing handling easy to find; cluttering the results with all our tests would not be helpful.
@@ -495,9 +496,9 @@ type diffIDResult struct { | |||
|
|||
// copyLayer copies a layer with srcInfo (with known Digest and possibly known Size) in src to dest, perhaps compressing it if canCompress, | |||
// and returns a complete blobInfo of the copied layer, and a value for LayerDiffIDs if diffIDIsNeeded | |||
func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest.Digest, error) { | |||
func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo) (types.BlobInfo, digest.Digest, 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.
The blocking
diffIDResult = <-diffIDChan
needs to abort if ctx
is canceled.
@@ -548,7 +549,7 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest | |||
// it copies a blob with srcInfo (with known Digest and possibly known Size) from srcStream to dest, | |||
// perhaps compressing the stream if canCompress, | |||
// and returns a complete blobInfo of the copied blob and perhaps a <-chan diffIDResult if diffIDIsNeeded, to be read by the caller. | |||
func (ic *imageCopier) copyLayerFromStream(srcStream io.Reader, srcInfo types.BlobInfo, | |||
func (ic *imageCopier) copyLayerFromStream(ctx context.Context, srcStream io.Reader, srcInfo types.BlobInfo, |
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.
(Non-blocking: It would be nice to have diffIDComputationGoroutine
reliably terminate if ctx
is canceled.
It does terminate, ultimately due to the defer…pipeWriter.CloseWithError
, but that’s a bit convoluted.
E.g. if a cancelable io.Copy
helper were created, using it in the goroutine would be nice.)
@@ -607,7 +608,7 @@ func computeDiffID(stream io.Reader, decompressor compression.DecompressorFunc) | |||
// perhaps sending a copy to an io.Writer if getOriginalLayerCopyWriter != nil, | |||
// perhaps compressing it if canCompress, | |||
// and returns a complete blobInfo of the copied blob. | |||
func (c *copier) copyBlobFromStream(srcStream io.Reader, srcInfo types.BlobInfo, | |||
func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, srcInfo types.BlobInfo, |
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.
(Non-blocking: It would be nice to have compressGoroutine
reliably terminate if ctx
is canceled, just like diffIDComputationGoroutine
.
Same for the
_, err := io.Copy(ioutil.Discard, originalLayerReader)
In all these cases, having the ctx
cancelation abort future reads from the Reader
s should be sufficient, but it’s convoluted.)
doc.go
Outdated
@@ -13,12 +13,13 @@ | |||
// if err != nil { | |||
// panic(err) | |||
// } | |||
// img, err := ref.NewImage(nil) | |||
// ctx := context.Background() | |||
// img, err := ref.NewImage(ctx) |
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.
(Nit: The alignment is inconsistent.)
@@ -13,7 +14,7 @@ type archiveImageSource struct { | |||
|
|||
// newImageSource returns a types.ImageSource for the specified image reference. | |||
// The caller must call .Close() on the returned ImageSource. | |||
func newImageSource(ctx *types.SystemContext, ref archiveReference) (types.ImageSource, error) { | |||
func newImageSource(ctx context.Context, ref archiveReference) (types.ImageSource, 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.
This really should be actually cancelable, in the tarfile.NewSourceFromFile
→NewSourceFromStream
:
_, err := io.Copy(tarCopyFile, inputStream)
part at least, which, although does not do anything over the network, may be copying many gigabytes of data and take several seconds.
@@ -389,7 +390,7 @@ func (d *ostreeImageDestination) PutSignatures(signatures [][]byte) error { | |||
return nil | |||
} | |||
|
|||
func (d *ostreeImageDestination) Commit() error { | |||
func (d *ostreeImageDestination) Commit(ctx context.Context) 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.
importBlob
, fixFiles
, generateTarSplitMetadata
, and maybe more, can handle many-gigabyte files or directory structures and should be cancelable.
@@ -256,13 +256,13 @@ func (s *ostreeImageSource) readSingleFile(commit, path string) (io.ReadCloser, | |||
} | |||
|
|||
// GetBlob returns a stream for the specified blob, and the blob's size. | |||
func (s *ostreeImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { | |||
func (s *ostreeImageSource) GetBlob(ctx context.Context, info types.BlobInfo) (io.ReadCloser, int64, 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.
The goroutine should probably be canceled by ctx
.
storage/storage_image.go
Outdated
@@ -284,7 +284,7 @@ func (s storageImageDestination) ShouldCompressLayers() bool { | |||
|
|||
// PutBlob stores a layer or data blob in our temporary directory, checking that any information | |||
// in the blobinfo matches the incoming data. | |||
func (s *storageImageDestination) PutBlob(stream io.Reader, blobinfo types.BlobInfo) (types.BlobInfo, error) { | |||
func (s *storageImageDestination) PutBlob(ctx context.Context, stream io.Reader, blobinfo types.BlobInfo) (types.BlobInfo, 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.
The
_, err = io.Copy(diffID.Hash(), decompressed)
can copy many gigabytes of data and should be cancelable.
@@ -460,7 +460,7 @@ func (s *storageImageDestination) getConfigBlob(info types.BlobInfo) ([]byte, er | |||
return nil, errors.New("blob not found") | |||
} | |||
|
|||
func (s *storageImageDestination) Commit() error { | |||
func (s *storageImageDestination) Commit(ctx context.Context) 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.
The
layer, _, err := s.imageRef.transport.store.PutLayer(id, lastLayer, nil, "", false, diff)
line can copy many gigabytes of data, and should be cancelable. (I’m not sure that can be done right now.)
tarball/tarball_src.go
Outdated
@@ -34,7 +34,7 @@ type tarballImageSource struct { | |||
manifest []byte | |||
} | |||
|
|||
func (r *tarballReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) { | |||
func (r *tarballReference) NewImageSource(ctx context.Context) (types.ImageSource, 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.
The
n, err := io.Copy(ioutil.Discard, reader)
can copy many gigabytes of data and should be cancelable.
Hm, I'm willing to follow through with changes like splitting SystemContext and Context apart (which would also resolve the nil problem by making it a compile failure), and I can easily use Background instead of TODO in the tests (generally, I use TODO because those tests are the ones that would catch failures to use ctx in the code, but if you prefer Background, that's fine, too). I'm probably not willing to take on the task of fixing the whole library re: context behavior, although there's clear value in that, my PR was basically me scratching my itch along the code paths I needed, and then propagating those changes as needed :) Does that work for you? |
I’m fine with not handling the local FS copies; in the worst case I can add the FIXMEs in there myself. I’d insist on having the two cases of blocking on a channel handled; that should be roughly select {
case <-ctx.Done():
return ctx.Err()
case theVariable <- theChannel:
} but I guess I can trade that: if you are willing to split the
I’m not sure what you mean; looking at the implementation, |
Yeah, SGTM. It'll probably take me a few days, but I'll get to it.
Ah, sorry, I didn't mean the TODOs would, I meant that that they're real TODOs in the sense of indicating the need for future work; the tests they're in would be the things meant to catch failures to use the context. The next step on those TODOs might be to have a test-specific context in the suite that makes sure it cancels the context after each test, so you can't leak threads during the test (for example). But, either way, grepping for |
- Network IO paths should react to cancels now. - File IO paths generally still won't. - `SystemContext` objects have been renamed to `sys` to leave `ctx` available for the stdlib context objects. Signed-off-by: Mike Lundy <mike@fluffypenguin.org>
adcbd74
to
369c442
Compare
I think I've handled all the comments now, except for the file io paths. How's it look? |
@runcom RFC, please at least ACK the general direction and |
👍 Thanks! I have prepared a skopeo PR at containers/skopeo#493 . Note to self: also check a few of the |
skopeo's PR passing, so merging here |
Update for API changes in containers/image#431
This is a pretty huge change, and it breaks api, so consider this first steps :)
After this change:
SystemContext
objects have been renamed tosys
to leavectx
available for the stdlib context objects.