Conversation
pkg/mount/mount.go
Outdated
There was a problem hiding this comment.
|
LGTM but a small nit. Could you port this to containerd, because we are going to deprecate docker/pkg/mount pkg and use containerd |
Yes, and I was about to start working on that... but there's much more to be ported (mountinfo parser, for one thing). |
|
Should be fixed now. |
|
Failures look legit (at least, same failures on janky, power, and z) |
|
I have now carefully audited all users of |
|
Failure in experimental CI is flaky test, Windows failure is unrelated |
|
z failure is known flaky test, TestSwarmClusterRotateUnlockKey, see #33041 |
There was a problem hiding this comment.
Same here; perhaps s/mount failed/failed to mount/ ?
There was a problem hiding this comment.
Looks like this if is redundant now, and we can just return umountErr (perhaps you find the explicit if more clear though, so up to you 😄)
daemon/graphdriver/zfs/zfs.go
Outdated
There was a problem hiding this comment.
Do you think we'd want to keep zfs in the error message (to more easily find where to look for the issue?) or is this already logged with storagedriver=zfs ? 🤔 (in that case; ignore 😂)
There was a problem hiding this comment.
I think it will be clear from the context -- after all, we only support one graph driver at the time, and its name is logged.
There was a problem hiding this comment.
I think we should keep zfs in there (same with other graphdriver's error messages as well).
"error creating zfs mount" is much easier to search and find in code or to find other people reporting a similar error.
There was a problem hiding this comment.
In general, though, I don't think having unique error strings to aid developers in bug report analysis is a good strategy. It would be much better to provide some context in error messages (like file name and line number where the error happens). This is the way I was doing it back in my C days (using preprocessor macros). Alas, logrus doesn't do it. Perhaps it will get this functionality one day? Could be possible with debug.Stack() or smth.
There was a problem hiding this comment.
"errors.WithStack" from "github.com/pkg/errors"... but still a good error message that signals where the error came from is important.
pkg/mount/mount.go
Outdated
There was a problem hiding this comment.
Looks like we always want to ignore the EINVAL case (a similar check is in RecursiveUnmount); perhaps move this to unmount() and return nil if the error is due to sys call.EINVAL;
diff --git a/pkg/mount/mount.go b/pkg/mount/mount.go
index 12f968cbb8..3edd031acf 100644
--- a/pkg/mount/mount.go
+++ b/pkg/mount/mount.go
@@ -4,9 +4,7 @@ import (
"sort"
"strconv"
"strings"
- "syscall"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -125,12 +123,7 @@ func ForceMount(device, target, mType, options string) error {
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
// does a normal unmount.
func Unmount(target string) error {
- err := unmount(target, mntDetach)
- if err != nil && errors.Cause(err) == syscall.EINVAL {
- // ignore "not mounted" error
- err = nil
- }
- return err
+ return unmount(target, mntDetach)
}
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
@@ -150,16 +143,6 @@ func RecursiveUnmount(target string) error {
logrus.Debugf("Trying to unmount %s", m.Mountpoint)
err = unmount(m.Mountpoint, mntDetach)
if err != nil {
- // If the error is EINVAL either this whole package is wrong (invalid flags passed to unmount(2)) or this is
- // not a mountpoint (which is ok in this case).
- // Meanwhile calling `Mounted()` is very expensive.
- //
- // We've purposefully used `syscall.EINVAL` here instead of `unix.EINVAL` to avoid platform branching
- // Since `EINVAL` is defined for both Windows and Linux in the `syscall` package (and other platforms),
- // this is nicer than defining a custom value that we can refer to in each platform file.
- if errors.Cause(err) == syscall.EINVAL {
- continue
- }
if i == len(mounts)-1 {
if mounted, e := Mounted(m.Mountpoint); e != nil || mounted {
return err
diff --git a/pkg/mount/mounter_linux.go b/pkg/mount/mounter_linux.go
index bfa87a05ee..6f289e06d2 100644
--- a/pkg/mount/mounter_linux.go
+++ b/pkg/mount/mounter_linux.go
@@ -1,6 +1,9 @@
package mount // import "github.com/docker/docker/pkg/mount"
import (
+ "syscall"
+
+ "github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -77,6 +80,16 @@ func unmount(target string, flag int) error {
if err == nil {
return nil
}
+ // If the error is EINVAL either this whole package is wrong (invalid flags passed to unmount(2)) or this is
+ // not a mountpoint (which is ok in this case).
+ // Meanwhile calling `Mounted()` is very expensive.
+ //
+ // We've purposefully used `syscall.EINVAL` here instead of `unix.EINVAL` to avoid platform branching
+ // Since `EINVAL` is defined for both Windows and Linux in the `syscall` package (and other platforms),
+ // this is nicer than defining a custom value that we can refer to in each platform file.
+ if errors.Cause(err) == syscall.EINVAL {
+ return nil
+ }
return &Error{
op: "umount",
target: target,There was a problem hiding this comment.
sure; it'll be cleaner this way
plugin/manager_linux.go
Outdated
There was a problem hiding this comment.
Should this be "Failed to unmount" ? Ah, I see; the .Error() returns the "op" as part of the error, so this will include unmount.
Perhaps we should prefix that error with "failed to <op>" otherwise we'll have to add that everywhere 🤔
There was a problem hiding this comment.
I am following the style of errors from os package (and the general unix error style) which is in the form <op>: <error>. In fact it makes sense here as well, something that would look like:
... level=warning msg="umount <dir>: device busy"
which gives no less info than
... level=warning msg="Failed to umount <dir>: device busy"
so I'll drop the "failed to" from this place.
pkg/mount/mount.go
Outdated
There was a problem hiding this comment.
Should we have the error start with "failed to " (e.g.)?
There was a problem hiding this comment.
Well, all the error messages (say from os package) are in the form op: msg, for example
open: permission denied
or
readlink: no such file or directory
I think we should follow the pattern. Brevity is the soul of wit 😸, besides, "failed to" is redundant here -- it's an error, so it is implied that something has failed.
Codecov Report
@@ Coverage Diff @@
## master #38068 +/- ##
=========================================
Coverage ? 36.1%
=========================================
Files ? 610
Lines ? 45296
Branches ? 0
=========================================
Hits ? 16352
Misses ? 26706
Partials ? 2238 |
|
Seeing one more failure; |
I forgot a hunk :( should be fixed now |
dbfa09b to
8571150
Compare
aaedc36 to
f4d618f
Compare
|
|
PPC failure is #36903 |
syscall.Stat (and Lstat), unlike functions from os pkg,
return "raw" errors (like EPERM or EINVAL), and those are
propagated up the function call stack unchanged, and gets
logged and/or returned to the user as is.
Wrap those into os.PathError{} so the error message will
at least have function name and file name.
Note we use Capitalized function names to distinguish
between functions in os and ours.
Signed-off-by: Kir Kolyshkin <[email protected]>
As standard mount.Unmount does what we need, let's use it. In addition, this adds ignoring "not mounted" condition, which was previously implemented (see PR#33329, commit cfa2591) via a very expensive call to mount.Mounted(). Signed-off-by: Kir Kolyshkin <[email protected]>
The function is not needed as it's just a shallow wrapper around unix.Mount(). Signed-off-by: Kir Kolyshkin <[email protected]>
It has been pointed out that we're ignoring EINVAL from umount(2) everywhere, so let's move it to a lower-level function. Also, its implementation should be the same for any UNIX incarnation, so let's consolidate it. Signed-off-by: Kir Kolyshkin <[email protected]>
The errors returned from Mount and Unmount functions are raw syscall.Errno errors (like EPERM or EINVAL), which provides no context about what has happened and why. Similar to os.PathError type, introduce mount.Error type with some context. The error messages will now look like this: > mount /tmp/mount-tests/source:/tmp/mount-tests/target, flags: 0x1001: operation not permitted or > mount tmpfs:/tmp/mount-test-source-516297835: operation not permitted Before this patch, it was just > operation not permitted [v2: add Cause()] [v3: rename MountError to Error, document Cause()] [v4: fixes; audited all users] [v5: make Error type private; changes after @cpuguy83 reviews] Signed-off-by: Kir Kolyshkin <[email protected]>
|
Rebased; conflicts resolved. I believe this one is ready to be merged and is very helpful is diagnosing bugs (issue of the day this PR could greatly help with is #38252) |
|
@cpuguy83 PTAL; I know you wanted volume mount errors to be wrapped as well but I think it could be done in a separate PR -- this one is more about system mounts (as in |
Some functions that moby uses return "raw" errors from the kernel. Wrap those to provide more error context to the caller. Please see individual commit descriptions for details.