From 46b0b3491f3163118e4cba9e0b799765588f4103 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 1 Sep 2017 09:53:16 +0800 Subject: [PATCH 1/3] refactor nsenter to new pkg/util --- pkg/util/mount/mount_linux.go | 6 +- pkg/util/mount/nsenter_mount.go | 136 +++++------------------- pkg/util/nsenter/nsenter.go | 124 +++++++++++++++++++++ pkg/util/nsenter/nsenter_unsupported.go | 50 +++++++++ 4 files changed, 203 insertions(+), 113 deletions(-) create mode 100644 pkg/util/nsenter/nsenter.go create mode 100644 pkg/util/nsenter/nsenter_unsupported.go diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 2845861cd31..0303eb7dc08 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -350,9 +350,7 @@ func parseProcMounts(content []byte) ([]MountPoint, error) { } func (mounter *Mounter) MakeRShared(path string) error { - mountCmd := defaultMountCommand - mountArgs := []string{} - return doMakeRShared(path, procMountInfoPath, mountCmd, mountArgs) + return doMakeRShared(path, procMountInfoPath) } // formatAndMount uses unix utils to format and mount the given disk @@ -523,7 +521,7 @@ func parseMountInfo(filename string) ([]mountInfo, error) { // path is shared and bind-mounts it as rshared if needed. mountCmd and // mountArgs are expected to contain mount-like command, doMakeRShared will add // '--bind ' and '--make-rshared ' to mountArgs. -func doMakeRShared(path string, mountInfoFilename string, mountCmd string, mountArgs []string) error { +func doMakeRShared(path string, mountInfoFilename string) error { shared, err := isShared(path, mountInfoFilename) if err != nil { return err diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 24a095a6c39..f1f69d24d32 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -25,78 +25,30 @@ import ( "strings" "github.com/golang/glog" - "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/util/nsenter" ) -// NsenterMounter is part of experimental support for running the kubelet -// in a container. Currently, all docker containers receive their own mount -// namespaces. NsenterMounter works by executing nsenter to run commands in +const ( + // hostProcMountsPath is the default mount path for rootfs + hostProcMountsPath = "/rootfs/proc/1/mounts" + // hostProcMountinfoPath is the default mount info path for rootfs + hostProcMountinfoPath = "/rootfs/proc/1/mountinfo" +) + +// Currently, all docker containers receive their own mount namespaces. +// NsenterMounter works by executing nsenter to run commands in // the host's mount namespace. -// -// NsenterMounter requires: -// -// 1. Docker >= 1.6 due to the dependency on the slave propagation mode -// of the bind-mount of the kubelet root directory in the container. -// Docker 1.5 used a private propagation mode for bind-mounts, so mounts -// performed in the host's mount namespace do not propagate out to the -// bind-mount in this docker version. -// 2. The host's root filesystem must be available at /rootfs -// 3. The nsenter binary must be on the Kubelet process' PATH in the container's -// filesystem. -// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at -// the present, this effectively means that the kubelet is running in a -// privileged container. -// 5. The volume path used by the Kubelet must be the same inside and outside -// the container and be writable by the container (to initialize volume) -// contents. TODO: remove this requirement. -// 6. The host image must have mount, findmnt, and umount binaries in /bin, -// /usr/sbin, or /usr/bin -// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin -// For more information about mount propagation modes, see: -// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt type NsenterMounter struct { - // a map of commands to their paths on the host filesystem - paths map[string]string + ne *nsenter.Nsenter } func NewNsenterMounter() *NsenterMounter { - m := &NsenterMounter{ - paths: map[string]string{ - "mount": "", - "findmnt": "", - "umount": "", - "systemd-run": "", - }, - } - // search for the mount command in other locations besides /usr/bin - for binary := range m.paths { - // default to root - m.paths[binary] = filepath.Join("/", binary) - for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} { - binPath := filepath.Join(path, binary) - if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil { - continue - } - m.paths[binary] = binPath - break - } - // TODO: error, so that the kubelet can stop if the mounts don't exist - // (don't forget that systemd-run is optional) - } - return m + return &NsenterMounter{ne: nsenter.NewNsenter()} } // NsenterMounter implements mount.Interface var _ = Interface(&NsenterMounter{}) -const ( - hostRootFsPath = "/rootfs" - hostProcMountsPath = "/rootfs/proc/1/mounts" - hostProcMountinfoPath = "/rootfs/proc/1/mountinfo" - hostMountNamespacePath = "/rootfs/proc/1/ns/mnt" - nsenterPath = "nsenter" -) - // Mount runs mount(8) in the host's root mount namespace. Aside from this // aspect, Mount has the same semantics as the mounter returned by mount.New() func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error { @@ -116,26 +68,22 @@ func (n *NsenterMounter) Mount(source string, target string, fstype string, opti // doNsenterMount nsenters the host's mount namespace and performs the // requested mount. func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error { - glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options) - args := n.makeNsenterArgs(source, target, fstype, options) - - glog.V(5).Infof("Mount command: %v %v", nsenterPath, args) - exec := exec.New() - outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput() + glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options) + cmd, args := n.makeNsenterArgs(source, target, fstype, options) + outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput() if len(outputBytes) != 0 { glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes)) } - return err } // makeNsenterArgs makes a list of argument to nsenter in order to do the // requested mount. -func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string { - mountCmd := n.absHostPath("mount") +func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) { + mountCmd := n.ne.AbsHostPath("mount") mountArgs := makeMountArgs(source, target, fstype, options) - if systemdRunPath, hasSystemd := n.paths["systemd-run"]; hasSystemd { + if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd { // Complete command line: // nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t // Expected flow is: @@ -165,34 +113,20 @@ func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options // No code here, mountCmd and mountArgs use /bin/mount } - nsenterArgs := []string{ - "--mount=" + hostMountNamespacePath, - "--", - mountCmd, - } - nsenterArgs = append(nsenterArgs, mountArgs...) - - return nsenterArgs + return mountCmd, mountArgs } // Unmount runs umount(8) in the host's mount namespace. func (n *NsenterMounter) Unmount(target string) error { - args := []string{ - "--mount=" + hostMountNamespacePath, - "--", - n.absHostPath("umount"), - target, - } + args := []string{target} // No need to execute systemd-run here, it's enough that unmount is executed // in the host's mount namespace. It will finish appropriate fuse daemon(s) // running in any scope. - glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args) - exec := exec.New() - outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput() + glog.V(5).Infof("nsenter unmount args: %v", args) + outputBytes, err := n.ne.Exec("umount", args).CombinedOutput() if len(outputBytes) != 0 { glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes)) } - return err } @@ -207,7 +141,7 @@ func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) { func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool { deletedDir := fmt.Sprintf("%s\\040(deleted)", dir) - return ((mp.Path == dir) || (mp.Path == deletedDir)) + return (mp.Path == dir) || (mp.Path == deletedDir) } // IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt @@ -227,11 +161,9 @@ func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) { // the first of multiple possible mountpoints using --first-only. // Also add fstype output to make sure that the output of target file will give the full path // TODO: Need more refactoring for this function. Track the solution with issue #26996 - args := []string{"--mount=" + hostMountNamespacePath, "--", n.absHostPath("findmnt"), "-o", "target,fstype", "--noheadings", "--first-only", "--target", file} - glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args) - - exec := exec.New() - out, err := exec.Command(nsenterPath, args...).CombinedOutput() + args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file} + glog.V(5).Infof("nsenter findmnt args: %v", args) + out, err := n.ne.Exec("findmnt", args).CombinedOutput() if err != nil { glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err) // Different operating systems behave differently for paths which are not mount points. @@ -285,20 +217,6 @@ func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (st return getDeviceNameFromMount(n, mountPath, pluginDir) } -func (n *NsenterMounter) absHostPath(command string) string { - path, ok := n.paths[command] - if !ok { - return command - } - return path -} - func (n *NsenterMounter) MakeRShared(path string) error { - nsenterCmd := nsenterPath - nsenterArgs := []string{ - "--mount=" + hostMountNamespacePath, - "--", - n.absHostPath("mount"), - } - return doMakeRShared(path, hostProcMountinfoPath, nsenterCmd, nsenterArgs) + return doMakeRShared(path, hostProcMountinfoPath) } diff --git a/pkg/util/nsenter/nsenter.go b/pkg/util/nsenter/nsenter.go new file mode 100644 index 00000000000..32fbc08487e --- /dev/null +++ b/pkg/util/nsenter/nsenter.go @@ -0,0 +1,124 @@ +// +build linux + +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nsenter + +import ( + "fmt" + "os" + "path/filepath" + + "k8s.io/utils/exec" + + "github.com/golang/glog" +) + +const ( + hostRootFsPath = "/rootfs" + // hostProcMountNsPath is the default mount namespace for rootfs + hostProcMountNsPath = "/rootfs/proc/1/ns/mnt" + // nsenterPath is the default nsenter command + nsenterPath = "nsenter" +) + +// Nsenter is part of experimental support for running the kubelet +// in a container. +// +// Nsenter requires: +// +// 1. Docker >= 1.6 due to the dependency on the slave propagation mode +// of the bind-mount of the kubelet root directory in the container. +// Docker 1.5 used a private propagation mode for bind-mounts, so mounts +// performed in the host's mount namespace do not propagate out to the +// bind-mount in this docker version. +// 2. The host's root filesystem must be available at /rootfs +// 3. The nsenter binary must be on the Kubelet process' PATH in the container's +// filesystem. +// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at +// the present, this effectively means that the kubelet is running in a +// privileged container. +// 5. The volume path used by the Kubelet must be the same inside and outside +// the container and be writable by the container (to initialize volume) +// contents. TODO: remove this requirement. +// 6. The host image must have "mount", "findmnt", "umount", "stat", "touch", +// "mkdir", "ls", "sh" and "chmod" binaries in /bin, /usr/sbin, or /usr/bin +// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin +// For more information about mount propagation modes, see: +// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt +type Nsenter struct { + // a map of commands to their paths on the host filesystem + paths map[string]string +} + +// NewNsenter constructs a new instance of Nsenter +func NewNsenter() *Nsenter { + ne := &Nsenter{ + paths: map[string]string{ + "mount": "", + "findmnt": "", + "umount": "", + "systemd-run": "", + "stat": "", + "touch": "", + "mkdir": "", + "ls": "", + "sh": "", + "chmod": "", + }, + } + // search for the required commands in other locations besides /usr/bin + for binary := range ne.paths { + // default to root + ne.paths[binary] = filepath.Join("/", binary) + for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} { + binPath := filepath.Join(path, binary) + if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil { + continue + } + ne.paths[binary] = binPath + break + } + // TODO: error, so that the kubelet can stop if the paths don't exist + // (don't forget that systemd-run is optional) + } + return ne +} + +// Exec executes nsenter commands in hostProcMountNsPath mount namespace +func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd { + fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"}, + append([]string{ne.AbsHostPath(cmd)}, args...)...) + glog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs) + exec := exec.New() + return exec.Command(nsenterPath, fullArgs...) +} + +// AbsHostPath returns the absolute runnable path for a specified command +func (ne *Nsenter) AbsHostPath(command string) string { + path, ok := ne.paths[command] + if !ok { + return command + } + return path +} + +// SupportsSystemd checks whether command systemd-run exists +func (ne *Nsenter) SupportsSystemd() (string, bool) { + systemdRunPath, hasSystemd := ne.paths["systemd-run"] + return systemdRunPath, hasSystemd +} diff --git a/pkg/util/nsenter/nsenter_unsupported.go b/pkg/util/nsenter/nsenter_unsupported.go new file mode 100644 index 00000000000..17b3b5722d3 --- /dev/null +++ b/pkg/util/nsenter/nsenter_unsupported.go @@ -0,0 +1,50 @@ +// +build !linux + +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nsenter + +import ( + "k8s.io/utils/exec" +) + +// Nsenter is part of experimental support for running the kubelet +// in a container. +type Nsenter struct { + // a map of commands to their paths on the host filesystem + Paths map[string]string +} + +// NewNsenter constructs a new instance of Nsenter +func NewNsenter() *Nsenter { + return &Nsenter{} +} + +// Exec executes nsenter commands in hostProcMountNsPath mount namespace +func (ne *Nsenter) Exec(args ...string) exec.Cmd { + return nil +} + +// AbsHostPath returns the absolute runnable path for a specified command +func (ne *Nsenter) AbsHostPath(command string) string { + return "" +} + +// SupportsSystemd checks whether command systemd-run exists +func (ne *Nsenter) SupportsSystemd() (string, bool) { + return "", false +} From 57ead4898b850037c9544e034a5e5cc8420990ad Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 8 Sep 2017 18:15:03 +0800 Subject: [PATCH 2/3] use GetFileType per mount.Interface to check hostpath type --- .../cm/container_manager_linux_test.go | 16 ++ pkg/kubelet/kubelet_volumes.go | 1 - pkg/util/mount/fake.go | 18 ++- pkg/util/mount/mount.go | 23 ++- pkg/util/mount/mount_linux.go | 90 ++++++++--- pkg/util/mount/mount_unsupported.go | 24 +++ pkg/util/mount/mount_windows.go | 62 ++++++++ pkg/util/mount/nsenter_mount.go | 52 +++++- pkg/util/mount/nsenter_mount_unsupported.go | 20 +++ pkg/util/removeall/removeall_test.go | 26 ++- pkg/volume/host_path/host_path.go | 135 +++++----------- pkg/volume/host_path/host_path_test.go | 78 +++++++-- pkg/volume/host_path/host_path_unix.go | 40 ----- pkg/volume/host_path/host_path_windows.go | 38 ----- pkg/volume/host_path/nsenter.go | 150 ------------------ pkg/volume/host_path/nsenter_unsupported.go | 66 -------- pkg/volume/plugins.go | 2 - 17 files changed, 411 insertions(+), 430 deletions(-) delete mode 100644 pkg/volume/host_path/host_path_unix.go delete mode 100644 pkg/volume/host_path/host_path_windows.go delete mode 100644 pkg/volume/host_path/nsenter.go delete mode 100644 pkg/volume/host_path/nsenter_unsupported.go diff --git a/pkg/kubelet/cm/container_manager_linux_test.go b/pkg/kubelet/cm/container_manager_linux_test.go index 19a9482b985..c255967d73f 100644 --- a/pkg/kubelet/cm/container_manager_linux_test.go +++ b/pkg/kubelet/cm/container_manager_linux_test.go @@ -79,6 +79,22 @@ func (mi *fakeMountInterface) MakeRShared(path string) error { return nil } +func (mi *fakeMountInterface) GetFileType(pathname string) (mount.FileType, error) { + return mount.FileType("fake"), nil +} + +func (mi *fakeMountInterface) MakeDir(pathname string) error { + return nil +} + +func (mi *fakeMountInterface) MakeFile(pathname string) error { + return nil +} + +func (mi *fakeMountInterface) ExistsPath(pathname string) bool { + return true +} + func fakeContainerMgrMountInt() mount.Interface { return &fakeMountInterface{ []mount.MountPoint{ diff --git a/pkg/kubelet/kubelet_volumes.go b/pkg/kubelet/kubelet_volumes.go index 6620cc07e2e..79b0fe3ea3a 100644 --- a/pkg/kubelet/kubelet_volumes.go +++ b/pkg/kubelet/kubelet_volumes.go @@ -69,7 +69,6 @@ func (kl *Kubelet) newVolumeMounterFromPlugins(spec *volume.Spec, pod *v1.Pod, o if err != nil { return nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err) } - opts.Containerized = kl.kubeletConfiguration.Containerized physicalMounter, err := plugin.NewMounter(spec, pod, opts) if err != nil { return nil, fmt.Errorf("failed to instantiate mounter for volume: %s using plugin: %s with a root cause: %v", spec.Name(), plugin.GetPluginName(), err) diff --git a/pkg/util/mount/fake.go b/pkg/util/mount/fake.go index 480d15b9a3e..d34510ae2a8 100644 --- a/pkg/util/mount/fake.go +++ b/pkg/util/mount/fake.go @@ -125,7 +125,7 @@ func (f *FakeMounter) List() ([]MountPoint, error) { } func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool { - return (mp.Path == dir) + return mp.Path == dir } func (f *FakeMounter) IsNotMountPoint(dir string) (bool, error) { @@ -175,3 +175,19 @@ func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (strin func (f *FakeMounter) MakeRShared(path string) error { return nil } + +func (f *FakeMounter) GetFileType(pathname string) (FileType, error) { + return FileType("fake"), nil +} + +func (f *FakeMounter) MakeDir(pathname string) error { + return nil +} + +func (f *FakeMounter) MakeFile(pathname string) error { + return nil +} + +func (f *FakeMounter) ExistsPath(pathname string) bool { + return false +} diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index 5be3ca40af5..1c09e157f8e 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -27,10 +27,17 @@ import ( "github.com/golang/glog" ) +type FileType string + const ( // Default mount command if mounter path is not specified - defaultMountCommand = "mount" - MountsInGlobalPDPath = "mounts" + defaultMountCommand = "mount" + MountsInGlobalPDPath = "mounts" + FileTypeDirectory FileType = "Directory" + FileTypeFile FileType = "File" + FileTypeSocket FileType = "Socket" + FileTypeCharDev FileType = "CharDevice" + FileTypeBlockDev FileType = "BlockDevice" ) type Interface interface { @@ -70,6 +77,18 @@ type Interface interface { // MakeRShared checks that given path is on a mount with 'rshared' mount // propagation. If not, it bind-mounts the path as rshared. MakeRShared(path string) error + // GetFileType checks for file/directory/socket/block/character devices. + // Will operate in the host mount namespace if kubelet is running in a container + GetFileType(pathname string) (FileType, error) + // MakeFile creates an empty file. + // Will operate in the host mount namespace if kubelet is running in a container + MakeFile(pathname string) error + // MakeDir creates a new directory. + // Will operate in the host mount namespace if kubelet is running in a container + MakeDir(pathname string) error + // ExistsPath checks whether the path exists. + // Will operate in the host mount namespace if kubelet is running in a container + ExistsPath(pathname string) bool } // Exec executes command where mount utilities are. This can be either the host, diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 0303eb7dc08..8976bd25542 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -252,17 +252,29 @@ func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) { // PathIsDevice uses FileInfo returned from os.Stat to check if path refers // to a device. func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) { - return pathIsDevice(pathname) + pathType, err := mounter.GetFileType(pathname) + isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev + return isDevice, err } func exclusiveOpenFailsOnDevice(pathname string) (bool, error) { - isDevice, err := pathIsDevice(pathname) + var isDevice bool + finfo, err := os.Stat(pathname) + if os.IsNotExist(err) { + isDevice = false + } + // err in call to os.Stat if err != nil { return false, fmt.Errorf( "PathIsDevice failed for path %q: %v", pathname, err) } + // path refers to a device + if finfo.Mode()&os.ModeDevice != 0 { + isDevice = true + } + if !isDevice { glog.Errorf("Path %q is not refering to a device.", pathname) return false, nil @@ -282,23 +294,6 @@ func exclusiveOpenFailsOnDevice(pathname string) (bool, error) { return false, errno } -func pathIsDevice(pathname string) (bool, error) { - finfo, err := os.Stat(pathname) - if os.IsNotExist(err) { - return false, nil - } - // err in call to os.Stat - if err != nil { - return false, err - } - // path refers to a device - if finfo.Mode()&os.ModeDevice != 0 { - return true, nil - } - // path does not refer to device - return false, nil -} - //GetDeviceNameFromMount: given a mount point, find the device name from its global mount point func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) { return getDeviceNameFromMount(mounter, mountPath, pluginDir) @@ -353,6 +348,63 @@ func (mounter *Mounter) MakeRShared(path string) error { return doMakeRShared(path, procMountInfoPath) } +func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { + var pathType FileType + finfo, err := os.Stat(pathname) + if os.IsNotExist(err) { + return pathType, fmt.Errorf("path %q does not exist", pathname) + } + // err in call to os.Stat + if err != nil { + return pathType, err + } + + mode := finfo.Sys().(*syscall.Stat_t).Mode + switch mode & syscall.S_IFMT { + case syscall.S_IFSOCK: + return FileTypeSocket, nil + case syscall.S_IFBLK: + return FileTypeBlockDev, nil + case syscall.S_IFCHR: + return FileTypeBlockDev, nil + case syscall.S_IFDIR: + return FileTypeDirectory, nil + case syscall.S_IFREG: + return FileTypeFile, nil + } + + return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device") +} + +func (mounter *Mounter) MakeDir(pathname string) error { + err := os.MkdirAll(pathname, os.FileMode(0755)) + if err != nil { + if !os.IsExist(err) { + return err + } + } + return nil +} + +func (mounter *Mounter) MakeFile(pathname string) error { + f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644)) + defer f.Close() + if err != nil { + if !os.IsExist(err) { + return err + } + } + return nil +} + +func (mounter *Mounter) ExistsPath(pathname string) bool { + _, err := os.Stat(pathname) + if err != nil { + return false + } + return true +} + // formatAndMount uses unix utils to format and mount the given disk func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { options = append(options, "defaults") diff --git a/pkg/util/mount/mount_unsupported.go b/pkg/util/mount/mount_unsupported.go index e0618949d81..865d53d09e5 100644 --- a/pkg/util/mount/mount_unsupported.go +++ b/pkg/util/mount/mount_unsupported.go @@ -18,6 +18,10 @@ limitations under the License. package mount +import ( + "errors" +) + type Mounter struct { mounterPath string } @@ -74,3 +78,23 @@ func (mounter *Mounter) MakeRShared(path string) error { func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { return nil } + +func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) { + return true, nil +} + +func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { + return FileType("fake"), errors.New("not implemented") +} + +func (mounter *Mounter) MakeDir(pathname string) error { + return nil +} + +func (mounter *Mounter) MakeFile(pathname string) error { + return nil +} + +func (mounter *Mounter) ExistsPath(pathname string) bool { + return true +} diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 2cb890f1b03..50c95caa528 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -25,6 +25,7 @@ import ( "path/filepath" "strconv" "strings" + "syscall" "github.com/golang/glog" ) @@ -167,6 +168,67 @@ func (mounter *Mounter) MakeRShared(path string) error { return nil } +// GetFileType checks for sockets/block/character devices +func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { + var pathType FileType + info, err := os.Stat(pathname) + if os.IsNotExist(err) { + return pathType, fmt.Errorf("path %q does not exist", pathname) + } + // err in call to os.Stat + if err != nil { + return pathType, err + } + + mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes + switch mode & syscall.S_IFMT { + case syscall.S_IFSOCK: + return FileTypeSocket, nil + case syscall.S_IFBLK: + return FileTypeBlockDev, nil + case syscall.S_IFCHR: + return FileTypeCharDev, nil + case syscall.S_IFDIR: + return FileTypeDirectory, nil + case syscall.S_IFREG: + return FileTypeFile, nil + } + + return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device") +} + +// MakeFile creates a new directory +func (mounter *Mounter) MakeDir(pathname string) error { + err := os.MkdirAll(pathname, os.FileMode(0755)) + if err != nil { + if !os.IsExist(err) { + return err + } + } + return nil +} + +// MakeFile creates an empty file +func (mounter *Mounter) MakeFile(pathname string) error { + f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644)) + defer f.Close() + if err != nil { + if !os.IsExist(err) { + return err + } + } + return nil +} + +// ExistsPath checks whether the path exists +func (mounter *Mounter) ExistsPath(pathname string) bool { + _, err := os.Stat(pathname) + if err != nil { + return false + } + return true +} + func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { // Try to mount the disk glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index f1f69d24d32..a6c7869b0d8 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -209,7 +209,9 @@ func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) { // PathIsDevice uses FileInfo returned from os.Stat to check if path refers // to a device. func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) { - return pathIsDevice(pathname) + pathType, err := n.GetFileType(pathname) + isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev + return isDevice, err } //GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts @@ -220,3 +222,51 @@ func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (st func (n *NsenterMounter) MakeRShared(path string) error { return doMakeRShared(path, hostProcMountinfoPath) } + +func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) { + var pathType FileType + outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput() + if err != nil { + return pathType, err + } + + switch string(outputBytes) { + case "socket": + return FileTypeSocket, nil + case "character special file": + return FileTypeCharDev, nil + case "block special file": + return FileTypeBlockDev, nil + case "directory": + return FileTypeDirectory, nil + case "regular file": + return FileTypeFile, nil + } + + return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device") +} + +func (mounter *NsenterMounter) MakeDir(pathname string) error { + args := []string{"-p", pathname} + if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil { + return err + } + return nil +} + +func (mounter *NsenterMounter) MakeFile(pathname string) error { + args := []string{pathname} + if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil { + return err + } + return nil +} + +func (mounter *NsenterMounter) ExistsPath(pathname string) bool { + args := []string{pathname} + _, err := mounter.ne.Exec("ls", args).CombinedOutput() + if err == nil { + return true + } + return false +} diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index f881c508711..f4eb692f958 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -18,6 +18,10 @@ limitations under the License. package mount +import ( + "errors" +) + type NsenterMounter struct{} func NewNsenterMounter() *NsenterMounter { @@ -65,3 +69,19 @@ func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (stri func (*NsenterMounter) MakeRShared(path string) error { return nil } + +func (*NsenterMounter) GetFileType(_ string) (FileType, error) { + return FileType("fake"), errors.New("not implemented") +} + +func (*NsenterMounter) MakeDir(pathname string) error { + return nil +} + +func (*NsenterMounter) MakeFile(pathname string) error { + return nil +} + +func (*NsenterMounter) ExistsPath(pathname string) bool { + return true +} diff --git a/pkg/util/removeall/removeall_test.go b/pkg/util/removeall/removeall_test.go index df04e0cbf76..22bdd092ed3 100644 --- a/pkg/util/removeall/removeall_test.go +++ b/pkg/util/removeall/removeall_test.go @@ -34,27 +34,51 @@ var _ mount.Interface = &fakeMounter{} func (mounter *fakeMounter) Mount(source string, target string, fstype string, options []string) error { return errors.New("not implemented") } + func (mounter *fakeMounter) Unmount(target string) error { return errors.New("not implemented") } + func (mounter *fakeMounter) List() ([]mount.MountPoint, error) { return nil, errors.New("not implemented") } + func (mounter fakeMounter) DeviceOpened(pathname string) (bool, error) { return false, errors.New("not implemented") } + func (mounter *fakeMounter) PathIsDevice(pathname string) (bool, error) { return false, errors.New("not implemented") } + func (mounter *fakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) { return "", errors.New("not implemented") } + func (mounter *fakeMounter) IsMountPointMatch(mp mount.MountPoint, dir string) bool { - return (mp.Path == dir) + return mp.Path == dir } + func (mounter *fakeMounter) IsNotMountPoint(dir string) (bool, error) { return mount.IsNotMountPoint(mounter, dir) } + +func (mounter *fakeMounter) GetFileType(pathname string) (mount.FileType, error) { + return mount.FileType("fake"), errors.New("not implemented") +} + +func (mounter *fakeMounter) MakeDir(pathname string) error { + return nil +} + +func (mounter *fakeMounter) MakeFile(pathname string) error { + return nil +} + +func (mounter *fakeMounter) ExistsPath(pathname string) bool { + return true +} + func (mounter *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) { name := path.Base(file) if strings.HasPrefix(name, "mount") { diff --git a/pkg/volume/host_path/host_path.go b/pkg/volume/host_path/host_path.go index 0ca43cc8585..97e3fa5db55 100644 --- a/pkg/volume/host_path/host_path.go +++ b/pkg/volume/host_path/host_path.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util/volumehelper" "k8s.io/kubernetes/pkg/volume/validation" @@ -113,8 +114,9 @@ func (plugin *hostPathPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts vo pathType = hostPathVolumeSource.Type } return &hostPathMounter{ - hostPath: &hostPath{path: path, pathType: pathType, containerized: opts.Containerized}, + hostPath: &hostPath{path: path, pathType: pathType}, readOnly: readOnly, + mounter: plugin.host.GetMounter(plugin.GetPluginName()), }, nil } @@ -182,9 +184,8 @@ func newProvisioner(options volume.VolumeOptions, host volume.VolumeHost, plugin // HostPath volumes represent a bare host file or directory mount. // The direct at the specified path will be directly exposed to the container. type hostPath struct { - path string - pathType *v1.HostPathType - containerized bool + path string + pathType *v1.HostPathType volume.MetricsNil } @@ -195,6 +196,7 @@ func (hp *hostPath) GetPath() string { type hostPathMounter struct { *hostPath readOnly bool + mounter mount.Interface } var _ volume.Mounter = &hostPathMounter{} @@ -224,7 +226,7 @@ func (b *hostPathMounter) SetUp(fsGroup *int64) error { if *b.pathType == v1.HostPathUnset { return nil } - return checkType(b.GetPath(), b.pathType, b.containerized) + return checkType(b.GetPath(), b.pathType, b.mounter) } // SetUpAt does not make sense for host paths - probably programmer error. @@ -340,132 +342,77 @@ type hostPathTypeChecker interface { GetPath() string } -type fileTypeChecker interface { - getFileType(fileInfo os.FileInfo) (v1.HostPathType, error) -} - -// this is implemented in per-OS files -type defaultFileTypeChecker struct{} - -type osFileTypeChecker struct { +type fileTypeChecker struct { path string exists bool - info os.FileInfo - checker fileTypeChecker + mounter mount.Interface } -func (ftc *osFileTypeChecker) Exists() bool { - return ftc.exists +func (ftc *fileTypeChecker) Exists() bool { + return ftc.mounter.ExistsPath(ftc.path) } -func (ftc *osFileTypeChecker) IsFile() bool { +func (ftc *fileTypeChecker) IsFile() bool { if !ftc.Exists() { return false } - return !ftc.info.IsDir() + return !ftc.IsDir() } -func (ftc *osFileTypeChecker) MakeFile() error { - f, err := os.OpenFile(ftc.path, os.O_CREATE, os.FileMode(0644)) - defer f.Close() - if err != nil { - if !os.IsExist(err) { - return err - } - } - return nil +func (ftc *fileTypeChecker) MakeFile() error { + return ftc.mounter.MakeFile(ftc.path) } -func (ftc *osFileTypeChecker) IsDir() bool { +func (ftc *fileTypeChecker) IsDir() bool { if !ftc.Exists() { return false } - return ftc.info.IsDir() -} - -func (ftc *osFileTypeChecker) MakeDir() error { - err := os.MkdirAll(ftc.path, os.FileMode(0755)) - if err != nil { - if !os.IsExist(err) { - return err - } - } - return nil -} - -func (ftc *osFileTypeChecker) IsBlock() bool { - if !ftc.Exists() { - return false - } - - blkDevType, err := ftc.checker.getFileType(ftc.info) + pathType, err := ftc.mounter.GetFileType(ftc.path) if err != nil { return false } - return blkDevType == v1.HostPathBlockDev + return string(pathType) == string(v1.HostPathDirectory) } -func (ftc *osFileTypeChecker) IsChar() bool { - if !ftc.Exists() { - return false - } +func (ftc *fileTypeChecker) MakeDir() error { + return ftc.mounter.MakeDir(ftc.path) +} - charDevType, err := ftc.checker.getFileType(ftc.info) +func (ftc *fileTypeChecker) IsBlock() bool { + blkDevType, err := ftc.mounter.GetFileType(ftc.path) if err != nil { return false } - return charDevType == v1.HostPathCharDev + return string(blkDevType) == string(v1.HostPathBlockDev) } -func (ftc *osFileTypeChecker) IsSocket() bool { - if !ftc.Exists() { - return false - } - - socketType, err := ftc.checker.getFileType(ftc.info) +func (ftc *fileTypeChecker) IsChar() bool { + charDevType, err := ftc.mounter.GetFileType(ftc.path) if err != nil { return false } - return socketType == v1.HostPathSocket + return string(charDevType) == string(v1.HostPathCharDev) } -func (ftc *osFileTypeChecker) GetPath() string { +func (ftc *fileTypeChecker) IsSocket() bool { + socketType, err := ftc.mounter.GetFileType(ftc.path) + if err != nil { + return false + } + return string(socketType) == string(v1.HostPathSocket) +} + +func (ftc *fileTypeChecker) GetPath() string { return ftc.path } -func newOSFileTypeChecker(path string, checker fileTypeChecker) (hostPathTypeChecker, error) { - ftc := osFileTypeChecker{path: path, checker: checker} - info, err := os.Stat(path) - if err != nil { - ftc.exists = false - if !os.IsNotExist(err) { - return nil, err - } - } else { - ftc.info = info - ftc.exists = true - } - return &ftc, nil +func newFileTypeChecker(path string, mounter mount.Interface) hostPathTypeChecker { + return &fileTypeChecker{path: path, mounter: mounter} } -func checkType(path string, pathType *v1.HostPathType, containerized bool) error { - var ftc hostPathTypeChecker - var err error - if containerized { - // For a containerized kubelet, use nsenter to run commands in - // the host's mount namespace. - // TODO(dixudx): setns into docker's mount namespace, and then run the exact same go code for checks/setup - ftc, err = newNsenterFileTypeChecker(path) - if err != nil { - return err - } - } else { - ftc, err = newOSFileTypeChecker(path, &defaultFileTypeChecker{}) - if err != nil { - return err - } - } - return checkTypeInternal(ftc, pathType) +// checkType checks whether the given path is the exact pathType +func checkType(path string, pathType *v1.HostPathType, mounter mount.Interface) error { + return checkTypeInternal(newFileTypeChecker(path, mounter), pathType) } func checkTypeInternal(ftc hostPathTypeChecker, pathType *v1.HostPathType) error { diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index a421ab731ff..664d0e6b9fb 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -1,5 +1,3 @@ -// +build linux - /* Copyright 2014 The Kubernetes Authors. @@ -30,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/client-go/kubernetes/fake" utilfile "k8s.io/kubernetes/pkg/util/file" + utilmount "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" ) @@ -323,8 +322,58 @@ type fakeFileTypeChecker struct { desiredType string } -func (fftc *fakeFileTypeChecker) getFileType(_ os.FileInfo) (v1.HostPathType, error) { - return *newHostPathType(fftc.desiredType), nil +func (fftc *fakeFileTypeChecker) Mount(source string, target string, fstype string, options []string) error { + return nil +} + +func (fftc *fakeFileTypeChecker) Unmount(target string) error { + return nil +} + +func (fftc *fakeFileTypeChecker) List() ([]utilmount.MountPoint, error) { + return nil, nil +} +func (fftc *fakeFileTypeChecker) IsMountPointMatch(mp utilmount.MountPoint, dir string) bool { + return false +} + +func (fftc *fakeFileTypeChecker) IsNotMountPoint(file string) (bool, error) { + return false, nil +} + +func (fftc *fakeFileTypeChecker) IsLikelyNotMountPoint(file string) (bool, error) { + return false, nil +} + +func (fftc *fakeFileTypeChecker) DeviceOpened(pathname string) (bool, error) { + return false, nil +} +func (fftc *fakeFileTypeChecker) PathIsDevice(pathname string) (bool, error) { + return false, nil +} + +func (fftc *fakeFileTypeChecker) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) { + return "fake", nil +} + +func (fftc *fakeFileTypeChecker) MakeRShared(path string) error { + return nil +} + +func (fftc *fakeFileTypeChecker) MakeFile(pathname string) error { + return nil +} + +func (fftc *fakeFileTypeChecker) MakeDir(pathname string) error { + return nil +} + +func (fftc *fakeFileTypeChecker) ExistsPath(pathname string) bool { + return true +} + +func (fftc *fakeFileTypeChecker) GetFileType(_ string) (utilmount.FileType, error) { + return utilmount.FileType(fftc.desiredType), nil } func setUp() error { @@ -363,14 +412,16 @@ func TestOSFileTypeChecker(t *testing.T) { isChar bool }{ { - name: "Existing Folder", - path: "/tmp/ExistingFolder", - isDir: true, + name: "Existing Folder", + path: "/tmp/ExistingFolder", + desiredType: string(utilmount.FileTypeDirectory), + isDir: true, }, { - name: "Existing File", - path: "/tmp/ExistingFolder/foo", - isFile: true, + name: "Existing File", + path: "/tmp/ExistingFolder/foo", + desiredType: string(utilmount.FileTypeFile), + isFile: true, }, { name: "Existing Socket File", @@ -393,11 +444,8 @@ func TestOSFileTypeChecker(t *testing.T) { } for i, tc := range testCases { - oftc, err := newOSFileTypeChecker(tc.path, - &fakeFileTypeChecker{desiredType: tc.desiredType}) - if err != nil { - t.Errorf("[%d: %q] expect nil, but got %v", i, tc.name, err) - } + fakeFTC := &fakeFileTypeChecker{desiredType: tc.desiredType} + oftc := newFileTypeChecker(tc.path, fakeFTC) path := oftc.GetPath() if path != tc.path { diff --git a/pkg/volume/host_path/host_path_unix.go b/pkg/volume/host_path/host_path_unix.go deleted file mode 100644 index 05deaaed5b2..00000000000 --- a/pkg/volume/host_path/host_path_unix.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build linux darwin - -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package host_path - -import ( - "fmt" - "os" - "syscall" - - "k8s.io/api/core/v1" -) - -func (dftc *defaultFileTypeChecker) getFileType(info os.FileInfo) (v1.HostPathType, error) { - mode := info.Sys().(*syscall.Stat_t).Mode - switch mode & syscall.S_IFMT { - case syscall.S_IFSOCK: - return v1.HostPathSocket, nil - case syscall.S_IFBLK: - return v1.HostPathBlockDev, nil - case syscall.S_IFCHR: - return v1.HostPathCharDev, nil - } - return "", fmt.Errorf("only recognise socket, block device and character device") -} diff --git a/pkg/volume/host_path/host_path_windows.go b/pkg/volume/host_path/host_path_windows.go deleted file mode 100644 index ff27e49371f..00000000000 --- a/pkg/volume/host_path/host_path_windows.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package host_path - -import ( - "fmt" - "os" - "syscall" - - "k8s.io/api/core/v1" -) - -func (dftc *defaultFileTypeChecker) getFileType(info os.FileInfo) (v1.HostPathType, error) { - mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes - switch mode & syscall.S_IFMT { - case syscall.S_IFSOCK: - return v1.HostPathSocket, nil - case syscall.S_IFBLK: - return v1.HostPathBlockDev, nil - case syscall.S_IFCHR: - return v1.HostPathCharDev, nil - } - return "", fmt.Errorf("only recognise socket, block device and character device") -} diff --git a/pkg/volume/host_path/nsenter.go b/pkg/volume/host_path/nsenter.go deleted file mode 100644 index c3c3cfece57..00000000000 --- a/pkg/volume/host_path/nsenter.go +++ /dev/null @@ -1,150 +0,0 @@ -// +build linux - -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package host_path - -import ( - "fmt" - - "k8s.io/utils/exec" -) - -const ( - hostProcMountsNamespace = "/rootfs/proc/1/ns/mnt" - nsenterCmd = "nsenter" - statCmd = "stat" - touchCmd = "touch" - mkdirCmd = "mkdir" -) - -// nsenterFileTypeChecker is part of experimental support for running the kubelet -// in a container. nsenterFileTypeChecker works by executing "nsenter" to run commands in -// the host's mount namespace. -// -// nsenterFileTypeChecker requires: -// -// 1. The host's root filesystem must be available at "/rootfs"; -// 2. The "nsenter" binary must be on the Kubelet process' PATH in the container's -// filesystem; -// 3. The Kubelet process must have CAP_SYS_ADMIN (required by "nsenter"); at -// the present, this effectively means that the kubelet is running in a -// privileged container; -// 4. The host image must have "stat", "touch", "mkdir" binaries in "/bin", "/usr/sbin", or "/usr/bin"; - -type nsenterFileTypeChecker struct { - path string - exists bool -} - -func newNsenterFileTypeChecker(path string) (hostPathTypeChecker, error) { - ftc := &nsenterFileTypeChecker{path: path} - ftc.Exists() - return ftc, nil -} - -func (ftc *nsenterFileTypeChecker) Exists() bool { - args := []string{ - fmt.Sprintf("--mount=%s", hostProcMountsNamespace), - "--", - "ls", - ftc.path, - } - exec := exec.New() - _, err := exec.Command(nsenterCmd, args...).CombinedOutput() - if err == nil { - ftc.exists = true - } - return ftc.exists -} - -func (ftc *nsenterFileTypeChecker) IsFile() bool { - if !ftc.Exists() { - return false - } - return !ftc.IsDir() -} - -func (ftc *nsenterFileTypeChecker) MakeFile() error { - args := []string{ - fmt.Sprintf("--mount=%s", hostProcMountsNamespace), - "--", - touchCmd, - ftc.path, - } - exec := exec.New() - if _, err := exec.Command(nsenterCmd, args...).CombinedOutput(); err != nil { - return err - } - return nil -} - -func (ftc *nsenterFileTypeChecker) IsDir() bool { - return ftc.checkMimetype("directory") -} - -func (ftc *nsenterFileTypeChecker) MakeDir() error { - args := []string{ - fmt.Sprintf("--mount=%s", hostProcMountsNamespace), - "--", - mkdirCmd, - "-p", - ftc.path, - } - exec := exec.New() - if _, err := exec.Command(nsenterCmd, args...).CombinedOutput(); err != nil { - return err - } - return nil -} - -func (ftc *nsenterFileTypeChecker) IsBlock() bool { - return ftc.checkMimetype("block special file") -} - -func (ftc *nsenterFileTypeChecker) IsChar() bool { - return ftc.checkMimetype("character special file") -} - -func (ftc *nsenterFileTypeChecker) IsSocket() bool { - return ftc.checkMimetype("socket") -} - -func (ftc *nsenterFileTypeChecker) GetPath() string { - return ftc.path -} - -func (ftc *nsenterFileTypeChecker) checkMimetype(checkedType string) bool { - if !ftc.Exists() { - return false - } - - args := []string{ - fmt.Sprintf("--mount=%s", hostProcMountsNamespace), - "--", - statCmd, - "-L", - `--printf "%F"`, - ftc.path, - } - exec := exec.New() - outputBytes, err := exec.Command(nsenterCmd, args...).CombinedOutput() - if err != nil { - return false - } - return string(outputBytes) == checkedType -} diff --git a/pkg/volume/host_path/nsenter_unsupported.go b/pkg/volume/host_path/nsenter_unsupported.go deleted file mode 100644 index 166ef9f0a39..00000000000 --- a/pkg/volume/host_path/nsenter_unsupported.go +++ /dev/null @@ -1,66 +0,0 @@ -// +build !linux - -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package host_path - -type nsenterFileTypeChecker struct { - path string - exists bool -} - -func newNsenterFileTypeChecker(path string) (hostPathTypeChecker, error) { - ftc := &nsenterFileTypeChecker{path: path} - ftc.Exists() - return ftc, nil -} - -func (ftc *nsenterFileTypeChecker) Exists() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) IsFile() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) MakeFile() error { - return nil -} - -func (ftc *nsenterFileTypeChecker) IsDir() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) MakeDir() error { - return nil -} - -func (ftc *nsenterFileTypeChecker) IsBlock() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) IsChar() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) IsSocket() bool { - return false -} - -func (ftc *nsenterFileTypeChecker) GetPath() string { - return ftc.path -} diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go index eb2f8149ac4..334578b4d1c 100644 --- a/pkg/volume/plugins.go +++ b/pkg/volume/plugins.go @@ -69,8 +69,6 @@ type VolumeOptions struct { CloudTags *map[string]string // Volume provisioning parameters from StorageClass Parameters map[string]string - // This flag helps identify whether kubelet is running in a container - Containerized bool } type DynamicPluginProber interface { From 542bd17e36a199d977f374f6f574f95f50d0b744 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 20 Sep 2017 15:14:03 +0800 Subject: [PATCH 3/3] auto-gen --- pkg/util/BUILD | 1 + pkg/util/mount/BUILD | 1 + pkg/util/nsenter/BUILD | 36 ++++++++++++++++++++++++ pkg/volume/host_path/BUILD | 56 +++++++++++--------------------------- 4 files changed, 54 insertions(+), 40 deletions(-) create mode 100644 pkg/util/nsenter/BUILD diff --git a/pkg/util/BUILD b/pkg/util/BUILD index 88bd34633cf..a206466463d 100644 --- a/pkg/util/BUILD +++ b/pkg/util/BUILD @@ -38,6 +38,7 @@ filegroup( "//pkg/util/net:all-srcs", "//pkg/util/netsh:all-srcs", "//pkg/util/node:all-srcs", + "//pkg/util/nsenter:all-srcs", "//pkg/util/oom:all-srcs", "//pkg/util/parsers:all-srcs", "//pkg/util/pointer:all-srcs", diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 4c87dbd2a4a..4c3f64f9d5a 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -31,6 +31,7 @@ go_library( ] + select({ "@io_bazel_rules_go//go/platform:linux_amd64": [ "//pkg/util/io:go_default_library", + "//pkg/util/nsenter:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", ], diff --git a/pkg/util/nsenter/BUILD b/pkg/util/nsenter/BUILD new file mode 100644 index 00000000000..7eb0ec5e74c --- /dev/null +++ b/pkg/util/nsenter/BUILD @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "nsenter_unsupported.go", + ] + select({ + "@io_bazel_rules_go//go/platform:linux_amd64": [ + "nsenter.go", + ], + "//conditions:default": [], + }), + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/utils/exec:go_default_library", + ] + select({ + "@io_bazel_rules_go//go/platform:linux_amd64": [ + "//vendor/github.com/golang/glog:go_default_library", + ], + "//conditions:default": [], + }), +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/volume/host_path/BUILD b/pkg/volume/host_path/BUILD index c209bd63696..6d953232757 100644 --- a/pkg/volume/host_path/BUILD +++ b/pkg/volume/host_path/BUILD @@ -11,21 +11,9 @@ go_library( srcs = [ "doc.go", "host_path.go", - "nsenter_unsupported.go", - ] + select({ - "@io_bazel_rules_go//go/platform:darwin_amd64": [ - "host_path_unix.go", - ], - "@io_bazel_rules_go//go/platform:linux_amd64": [ - "host_path_unix.go", - "nsenter.go", - ], - "@io_bazel_rules_go//go/platform:windows_amd64": [ - "host_path_windows.go", - ], - "//conditions:default": [], - }), + ], deps = [ + "//pkg/util/mount:go_default_library", "//pkg/volume:go_default_library", "//pkg/volume/util/volumehelper:go_default_library", "//pkg/volume/validation:go_default_library", @@ -33,37 +21,25 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", - ] + select({ - "@io_bazel_rules_go//go/platform:linux_amd64": [ - "//vendor/k8s.io/utils/exec:go_default_library", - ], - "//conditions:default": [], - }), + ], ) go_test( name = "go_default_test", - srcs = select({ - "@io_bazel_rules_go//go/platform:linux_amd64": [ - "host_path_test.go", - ], - "//conditions:default": [], - }), + srcs = ["host_path_test.go"], library = ":go_default_library", - deps = select({ - "@io_bazel_rules_go//go/platform:linux_amd64": [ - "//pkg/util/file:go_default_library", - "//pkg/volume:go_default_library", - "//pkg/volume/testing:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", - "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", - ], - "//conditions:default": [], - }), + deps = [ + "//pkg/util/file:go_default_library", + "//pkg/util/mount:go_default_library", + "//pkg/volume:go_default_library", + "//pkg/volume/testing:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", + ], ) filegroup(