Skip to content
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

Close with error #236

Merged
merged 1 commit into from
Feb 25, 2017
Merged

Close with error #236

merged 1 commit into from
Feb 25, 2017

Conversation

erikh
Copy link
Contributor

@erikh erikh commented Feb 6, 2017

This patch augments the interfaces' close methods with an error return which is
only actually useful in one or two spots for now. However, it can be utilized
later as things fill in.

Fixes #82

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

skopeo should be similarly updated to handle the errors.

I am still not sold on this being truly worthwhile (all this only to report a failure in daemonImageSource when we fail to remove a temporary file which will be very likely cleaned up by systemd-tmpfiles in the worst case?), but if we are doing this, let’s actually do it right.

if !d.committed {
logrus.Debugf("docker-daemon: Closing tar stream to abort loading")
err = errors.New("Aborting upload, daemonImageDestination closed without a previous .Commit()")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an error in .Close(); if the caller decides to abort without a .Commit(), and the upload is successfully aborted, .Close() should indicate success.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still outstanding.

@@ -139,7 +139,7 @@ type ImageDestination interface {
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
Reference() ImageReference
// Close removes resources associated with an initialized ImageDestination, if any.
Close()
Close() error
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caller in copy.Image should be updated to handle the error.

@@ -185,7 +185,7 @@ type UnparsedImage interface {
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
Reference() ImageReference
// Close removes resources associated with an initialized UnparsedImage, if any.
Close()
Close() error
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callers in copy.Image (both for UnparseImage and Image) should be updated to handle the error.

And perhaps doc.go as well?

@mtrmac
Copy link
Collaborator

mtrmac commented Feb 6, 2017

To link the two, this fixes #82.

@erikh
Copy link
Contributor Author

erikh commented Feb 6, 2017

Will make adjustments, and file a PR on skopeo in the next few days.

@erikh erikh force-pushed the close-with-error branch 2 times, most recently from 484c771 to 2682f2f Compare February 9, 2017 02:58
@erikh
Copy link
Contributor Author

erikh commented Feb 9, 2017

Ok I updated this to handle close errors, I also changed the return convention (read the comments at the inner-top of copy.Image) to use output variables. All is explained in comments, but if this feels too cumbersome for the benefits we can talk about alternatives.

copy/copy.go Outdated
// source image admissibility.
func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageReference, options *Options) (retErr error) {
// XXX this function uses an output parameter for the error return value.
// Setting this and returning is the ideal way to return an error.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at https://golang.org/ref/spec#Return_statements , even with a named return value it would be perfectly fine to just return somerrorvalue AFAICS.

(Also, doesn’t XXX mean “this is bad”? Perhaps just NOTE: or something like that.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I typically use it to say "WARNING" or something of the like: "pay attention" basically.

copy/copy.go Outdated
defer dest.Close()
defer func() {
if err := dest.Close(); err != nil {
retErr = errors.Wrapf(retErr, "dest: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should only happen if retErr == nil; the original value of retErr was the primary cause we are failing; that dest.Close() has failed may be completely unrelated, or it may be a side effect of the original retErr failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok sure, I was just thinking that maybe it'd be nice to have all the error context in the event of a copy failure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, I wasn’t paying enough attention to notice that this preserves both. Let me check…

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

… something like this would be nice if we can make that human-readable. See how the other errors.Wrapf calls in here try to tell the user what is going on. I am not sure that this is workable here:

errors.Wrapf(retErr, "Error closing destination: %v, after primary failure", err)

is bordering on incomprehensible, especially if err were long / created by several errors.Wrapf calls. (We can make the underlying data type situation better by using something like aggregateErr from openshift-copies.go, but that is probably even worse for readability.)

If we can’t make that human-readable, I think preserving a non-nil retErr is more important than the failure from .Close.

@runcom ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I get the concern.

What if I did something like this:

errors.Wrapf(retErr, "(destination error %v) ", err)

That way each indepednent error would be wrapped by parens. I could also add newlines.

I think the errors will be valuable context in the face of other, higher level errors.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s certainly better than my attempt.

The code as is (making daemonImageSource.Close fail using an invalid path):

$ sudo ./x docker-daemon:busybox:latest dir:d
FATA[0000] Error copying: source: remove makethisfail//var/tmp/docker-daemon-tar266436759: no such file or directory: Error writing blob: open d/dir-put-blob135662858: no such file or directory 

quite misleading about the cause/effect relationships

“primary error” wording:

FATA[0000] Error copying: Error closing source: remove makethisfail//var/tmp/docker-daemon-tar088311212: no such file or directory, after primary failure: Error writing blob: open d/dir-put-blob812853531: no such file or directory 

Still focusing on the wrong thing (closing failure)

With the parentheses:

FATA[0000] Error copying: (source error remove makethisfail//var/tmp/docker-daemon-tar460112488: no such file or directory): Error writing blob: open d/dir-put-blob983238823: no such file or directory 

Certainly better. Good enough?

Newlines are attractive (they would even allow us to build a bulleted list :) ) but risky, the caller is fairly likely not expecting them.

At this point I am really not sure, I’ll let @runcom weigh in.

copy/copy.go Outdated
@@ -118,7 +131,9 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
unparsedImage := image.UnparsedFromSource(rawSource)
defer func() {
if unparsedImage != nil {
unparsedImage.Close()
if err := unparsedImage.Close(); err != nil {
retErr = errors.Wrapf(retErr, "unparsed: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

copy/copy.go Outdated
defer src.Close()
defer func() {
if err := src.Close(); err != nil {
retErr = errors.Wrapf(retErr, "source: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

if !d.committed {
logrus.Debugf("docker-daemon: Closing tar stream to abort loading")
err = errors.New("Aborting upload, daemonImageDestination closed without a previous .Commit()")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still outstanding.

copy/copy.go Outdated
@@ -106,9 +113,15 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe

dest, err := destRef.NewImageDestination(options.DestinationCtx)
if err != nil {
return errors.Wrapf(err, "Error initializing destination %s", transports.ImageName(destRef))
retErr = errors.Wrapf(err, "Error initializing destination %s", transports.ImageName(destRef))
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not necessary. return X will set retErr = x implicitly.

@erikh
Copy link
Contributor Author

erikh commented Feb 20, 2017

PTAL

changes:

  • using parens for inner errors now
  • retErr code was adjusted based on return semantics.

@erikh
Copy link
Contributor Author

erikh commented Feb 21, 2017

#240 is blocking this.

@mtrmac
Copy link
Collaborator

mtrmac commented Feb 21, 2017

LGTM pending tests. (Shouldn't test-skopeo work even after the API change? If not, the way we deal with API interdependencies is usually to

  • Temporarily modify the corresponding skopeo PR to vendor from the proposed branch, show that that passes tests
  • Then merge the containers/image PR, ignoring the test failure
  • Update the skopeo PR to vendor from the updated containers/image master again, and merge it.

Approved with PullApprove

@mtrmac
Copy link
Collaborator

mtrmac commented Feb 21, 2017

👍

@erikh
Copy link
Contributor Author

erikh commented Feb 25, 2017

rebased

@runcom
Copy link
Member

runcom commented Feb 25, 2017

@erikh sorry needs one last rebase before I merge this

This allows us to provide in the image interfaces a method of providing
an error at close time. This is only currently used in a few situations.

Signed-off-by: Erik Hollensbe <github@hollensbe.org>
@erikh
Copy link
Contributor Author

erikh commented Feb 25, 2017 via email

@runcom
Copy link
Member

runcom commented Feb 25, 2017

👍

Approved with PullApprove

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Close() should be Close() error
4 participants