diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index 44058042d50..8315f7ea377 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -94,15 +94,6 @@ func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, return mounter.formatAndMount(source, target, fstype, options) } -// New returns a mount.Interface for the current system. -// It provides options to override the default mounter behavior. -// mounterPath allows using an alternative to `/bin/mount` for mounting. -func New(mounterPath string) Interface { - return &Mounter{ - mounterPath: mounterPath, - } -} - // GetMountRefs finds all other references to the device referenced // by mountPath; returns a list of paths. func GetMountRefs(mounter Interface, mountPath string) ([]string, error) { diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index ae8a96cba3b..e9167facfc5 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -55,6 +55,17 @@ const ( // kubelet is running in the host's root mount namespace. type Mounter struct { mounterPath string + withSystemd bool +} + +// New returns a mount.Interface for the current system. +// It provides options to override the default mounter behavior. +// mounterPath allows using an alternative to `/bin/mount` for mounting. +func New(mounterPath string) Interface { + return &Mounter{ + mounterPath: mounterPath, + withSystemd: detectSystemd(), + } } // Mount mounts source to target as fstype with given options. 'source' and 'fstype' must @@ -68,18 +79,18 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio mounterPath := "" bind, bindRemountOpts := isBind(options) if bind { - err := doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"}) + err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"}) if err != nil { return err } - return doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts) + return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts) } // The list of filesystems that require containerized mounter on GCI image cluster fsTypesNeedMounter := sets.NewString("nfs", "glusterfs", "ceph", "cifs") if fsTypesNeedMounter.Has(fstype) { mounterPath = mounter.mounterPath } - return doMount(mounterPath, defaultMountCommand, source, target, fstype, options) + return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options) } // isBind detects whether a bind mount is being requested and makes the remount options to @@ -108,14 +119,14 @@ func isBind(options []string) (bool, []string) { } // doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used. -func doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error { +func (m *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error { mountArgs := makeMountArgs(source, target, fstype, options) if len(mounterPath) > 0 { mountArgs = append([]string{mountCmd}, mountArgs...) mountCmd = mounterPath } - if systemdRunPath, err := exec.LookPath("systemd-run"); err == nil { + if m.withSystemd { // Try to run mount via systemd-run --scope. This will escape the // service where kubelet runs and any fuse daemons will be started in a // specific scope. kubelet service than can be restarted without killing @@ -138,7 +149,7 @@ func doMount(mounterPath string, mountCmd string, source string, target string, // // systemd-mount is not used because it's too new for older distros // (CentOS 7, Debian Jessie). - mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs) + mountCmd, mountArgs = addSystemdScope("systemd-run", target, mountCmd, mountArgs) } else { // No systemd-run on the host (or we failed to check it), assume kubelet // does not run as a systemd service. @@ -157,6 +168,31 @@ func doMount(mounterPath string, mountCmd string, source string, target string, return err } +// detectSystemd returns true if OS runs with systemd as init. When not sure +// (permission errors, ...), it returns false. +// There may be different ways how to detect systemd, this one makes sure that +// systemd-runs (needed by Mount()) works. +func detectSystemd() bool { + if _, err := exec.LookPath("systemd-run"); err != nil { + glog.V(2).Infof("Detected OS without systemd") + return false + } + // Try to run systemd-run --scope /bin/true, that should be enough + // to make sure that systemd is really running and not just installed, + // which happens when running in a container with a systemd-based image + // but with different pid 1. + cmd := exec.Command("systemd-run", "--description=Kubernetes systemd probe", "--scope", "true") + output, err := cmd.CombinedOutput() + if err != nil { + glog.V(2).Infof("Cannot run systemd-run, assuming non-systemd OS") + glog.V(4).Infof("systemd-run failed with: %v", err) + glog.V(4).Infof("systemd-run output: %s", string(output)) + return false + } + glog.V(2).Infof("Detected OS with systemd") + return true +} + // makeMountArgs makes the arguments to the mount(8) command. func makeMountArgs(source, target, fstype string, options []string) []string { // Build mount command as follows: diff --git a/pkg/util/mount/mount_unsupported.go b/pkg/util/mount/mount_unsupported.go index f9abab813dc..2119b1a0ad6 100644 --- a/pkg/util/mount/mount_unsupported.go +++ b/pkg/util/mount/mount_unsupported.go @@ -22,6 +22,15 @@ type Mounter struct { mounterPath string } +// New returns a mount.Interface for the current system. +// It provides options to override the default mounter behavior. +// mounterPath allows using an alternative to `/bin/mount` for mounting. +func New(mounterPath string) Interface { + return &Mounter{ + mounterPath: mounterPath, + } +} + func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error { return nil }