-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
cli/command: ctx cancel should not print or produce a non zero exit code #5666
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5666 +/- ##
==========================================
- Coverage 59.53% 59.14% -0.39%
==========================================
Files 346 343 -3
Lines 29356 29347 -9
==========================================
- Hits 17477 17358 -119
- Misses 10909 11017 +108
- Partials 970 972 +2 |
The user might kill the CLI through a SIGINT/SIGTERM which cancels the main context we pass around. Currently the context cancel error is printed alongside any other wrapped error with a generic exit code (125). This patch improves on this behavior and prevents any error from being printed when they match `context.Cancelled`. The `cli.StatusError` error would wrap errors but not provide a way to unwrap them. This would lead to situations where `errors.Is` would not match the underlying error. Signed-off-by: Alano Terblanche <[email protected]>
b6c8353
to
d7c81a3
Compare
@@ -6,16 +6,22 @@ import ( | |||
|
|||
// StatusError reports an unsuccessful exit by a command. | |||
type StatusError struct { | |||
Status string | |||
Status 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.
If we're changing this, it would make more sense to do
Status error | |
StatusError error |
or
Status error | |
Cause error |
However, we need to use some caution changing this. It's widely used, so we could never include this in a minor release, and even in a major we need to be careful.
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.
It might be better to fix this without changing cli.StatusError
first, and do this change separately, unless we absolutely need it to fix 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'll think a bit about a solution for this, but I don't think we should concern ourselves too much with externals importing this particular package.
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.
but I don't think we should concern ourselves too much with externals importing this particular package.
For any reason in particular?
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.
AFAIK we have no SLA stating that internal packages in the CLI should never break due to external usage. I'd take the side of our duties are to Docker and Docker's users before a 3rd party using our code.
@@ -30,7 +30,7 @@ import ( | |||
|
|||
func main() { | |||
err := dockerMain(context.Background()) | |||
if err != nil && !errdefs.IsCancelled(err) { | |||
if err != nil && !errdefs.IsCancelled(err) && !errdefs.IsContext(err) { |
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 the !errdefs.IsContext(err)
? IMO I'd like to see a message telling me what happened if a request timed out or some such.
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.
true, right now I don't want to print any exit status or error message when the context is cancelled. I think it might be possible to determine if this is a sigint/sigterm vs a timeout for example.
@@ -103,13 +103,13 @@ func runRun(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, ro | |||
// just in case the parse does not exit | |||
if err != nil { | |||
return cli.StatusError{ | |||
Status: withHelp(err, "run").Error(), | |||
Status: withHelp(err, "run"), |
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.
Perhaps as an intermediate (non-breaking) step, we could;
- change
withHelp
to return a string (instead of error) - make
withHelp
"context-aware" and discard the error-message if it's a context error
(Haven't looked in-depth if that's an option, so take that with a grain of salt 😅)
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 could not try to handle this all magically, and instead handle context cancellation errors at each place where we think they occur. That would have the benefit of not hiding unexpected context cancelled errors if they're coming from somewhere a bug/an unexpected interaction. DRI is fine, but too much generalization can also be bad.
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.
make withHelp "context-aware" and discard the error-message if it's a context error
we could, but this would mean inconsistent behavior, some places would print context cancelled
and others nothing.
// Unwrap allows wrapped errors stored in StatusError to be checked | ||
// using `errors.Is`. |
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.
Unwrap
doesn't allow wrapped errors to be checked using errors.Is
– it allows StatusError
to be checked with errors.Is
(and exposes the underlying error).
// Unwrap allows wrapped errors stored in StatusError to be checked | |
// using `errors.Is`. | |
// Unwrap returns the wrapped error. | |
// | |
// This allows StatusError to be checked with errors.Is. |
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.
True, what I tried to say was: errors.Is
checks for errors down the error chain. Since we wrap errors using fmt.Errorf("%w", err)
and store that inside StatusError
we need to check the underlying error instead of just err == StatusError
.
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.
Right, it's just a matter of wording. IMO the GoDoc for Unwrap
should explain what (from the perspective of StatusError
) what Unwrap
does, which specifically is "return the wrapped error – which allows checking StatusErr with errors.Is
".
allows wrapped errors stored in StatusError to be checked using errors.Is.
is less clear to me since it could also be interpreted as "allows you to do errors.Is
on the underlying error" which isn't the case.
The user might kill the CLI through a SIGINT/SIGTERM
which cancels the main context we pass around.
Currently the context cancel error is printed
alongside any other wrapped error with a generic
exit code (125).
This patch improves on this behavior and prevents
any error from being printed when they match
context.Cancelled
.The
cli.StatusError
error would wrap errors butnot provide a way to unwrap them. This would lead
to situations where
errors.Is
would not match the underlying error.Closes #5659
- What I did
- How I did it
- How to verify it
- Description for the changelog
- A picture of a cute animal (not mandatory but encouraged)