mirror of https://github.com/k3s-io/k3s
Merge pull request #51771 from dixudx/refactor_nsenter
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Refactor nsenter **What this PR does / why we need it**: **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #51273 **Special notes for your reviewer**: /assign @jsafrane **Release note**: ```release-note None ```pull/6/head
commit
85b252d47e
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
@ -350,9 +345,64 @@ 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)
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -523,7 +573,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 <path> <path>' and '--make-rshared <path>' 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <type> <what> <where>
|
||||
// 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.
|
||||
|
@ -277,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
|
||||
|
@ -285,20 +219,54 @@ 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 {
|
||||
return doMakeRShared(path, hostProcMountinfoPath)
|
||||
}
|
||||
|
||||
func (n *NsenterMounter) MakeRShared(path string) error {
|
||||
nsenterCmd := nsenterPath
|
||||
nsenterArgs := []string{
|
||||
"--mount=" + hostMountNamespacePath,
|
||||
"--",
|
||||
n.absHostPath("mount"),
|
||||
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
|
||||
}
|
||||
return doMakeRShared(path, hostProcMountinfoPath, nsenterCmd, nsenterArgs)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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") {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue