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
|
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 {
|
func fakeContainerMgrMountInt() mount.Interface {
|
||||||
return &fakeMountInterface{
|
return &fakeMountInterface{
|
||||||
[]mount.MountPoint{
|
[]mount.MountPoint{
|
||||||
|
|
|
@ -69,7 +69,6 @@ func (kl *Kubelet) newVolumeMounterFromPlugins(spec *volume.Spec, pod *v1.Pod, o
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err)
|
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)
|
physicalMounter, err := plugin.NewMounter(spec, pod, opts)
|
||||||
if err != nil {
|
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)
|
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/net:all-srcs",
|
||||||
"//pkg/util/netsh:all-srcs",
|
"//pkg/util/netsh:all-srcs",
|
||||||
"//pkg/util/node:all-srcs",
|
"//pkg/util/node:all-srcs",
|
||||||
|
"//pkg/util/nsenter:all-srcs",
|
||||||
"//pkg/util/oom:all-srcs",
|
"//pkg/util/oom:all-srcs",
|
||||||
"//pkg/util/parsers:all-srcs",
|
"//pkg/util/parsers:all-srcs",
|
||||||
"//pkg/util/pointer:all-srcs",
|
"//pkg/util/pointer:all-srcs",
|
||||||
|
|
|
@ -31,6 +31,7 @@ go_library(
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
||||||
"//pkg/util/io:go_default_library",
|
"//pkg/util/io:go_default_library",
|
||||||
|
"//pkg/util/nsenter:go_default_library",
|
||||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets: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 {
|
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) {
|
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 {
|
func (f *FakeMounter) MakeRShared(path string) error {
|
||||||
return nil
|
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"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FileType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Default mount command if mounter path is not specified
|
// Default mount command if mounter path is not specified
|
||||||
defaultMountCommand = "mount"
|
defaultMountCommand = "mount"
|
||||||
MountsInGlobalPDPath = "mounts"
|
MountsInGlobalPDPath = "mounts"
|
||||||
|
FileTypeDirectory FileType = "Directory"
|
||||||
|
FileTypeFile FileType = "File"
|
||||||
|
FileTypeSocket FileType = "Socket"
|
||||||
|
FileTypeCharDev FileType = "CharDevice"
|
||||||
|
FileTypeBlockDev FileType = "BlockDevice"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
|
@ -70,6 +77,18 @@ type Interface interface {
|
||||||
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||||
// propagation. If not, it bind-mounts the path as rshared.
|
// propagation. If not, it bind-mounts the path as rshared.
|
||||||
MakeRShared(path string) error
|
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,
|
// 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
|
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
|
||||||
// to a device.
|
// to a device.
|
||||||
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
|
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) {
|
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 {
|
if err != nil {
|
||||||
return false, fmt.Errorf(
|
return false, fmt.Errorf(
|
||||||
"PathIsDevice failed for path %q: %v",
|
"PathIsDevice failed for path %q: %v",
|
||||||
pathname,
|
pathname,
|
||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
// path refers to a device
|
||||||
|
if finfo.Mode()&os.ModeDevice != 0 {
|
||||||
|
isDevice = true
|
||||||
|
}
|
||||||
|
|
||||||
if !isDevice {
|
if !isDevice {
|
||||||
glog.Errorf("Path %q is not refering to a device.", pathname)
|
glog.Errorf("Path %q is not refering to a device.", pathname)
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -282,23 +294,6 @@ func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
|
||||||
return false, errno
|
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
|
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
|
||||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||||
return getDeviceNameFromMount(mounter, mountPath, pluginDir)
|
return getDeviceNameFromMount(mounter, mountPath, pluginDir)
|
||||||
|
@ -350,9 +345,64 @@ func parseProcMounts(content []byte) ([]MountPoint, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *Mounter) MakeRShared(path string) error {
|
func (mounter *Mounter) MakeRShared(path string) error {
|
||||||
mountCmd := defaultMountCommand
|
return doMakeRShared(path, procMountInfoPath)
|
||||||
mountArgs := []string{}
|
}
|
||||||
return doMakeRShared(path, procMountInfoPath, mountCmd, mountArgs)
|
|
||||||
|
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
|
// 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
|
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
||||||
// mountArgs are expected to contain mount-like command, doMakeRShared will add
|
// mountArgs are expected to contain mount-like command, doMakeRShared will add
|
||||||
// '--bind <path> <path>' and '--make-rshared <path>' to mountArgs.
|
// '--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)
|
shared, err := isShared(path, mountInfoFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -18,6 +18,10 @@ limitations under the License.
|
||||||
|
|
||||||
package mount
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
type Mounter struct {
|
type Mounter struct {
|
||||||
mounterPath string
|
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 {
|
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||||
return nil
|
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"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
@ -167,6 +168,67 @@ func (mounter *Mounter) MakeRShared(path string) error {
|
||||||
return nil
|
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 {
|
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||||
// Try to mount the disk
|
// Try to mount the disk
|
||||||
glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
||||||
|
|
|
@ -25,78 +25,30 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/kubernetes/pkg/util/nsenter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NsenterMounter is part of experimental support for running the kubelet
|
const (
|
||||||
// in a container. Currently, all docker containers receive their own mount
|
// hostProcMountsPath is the default mount path for rootfs
|
||||||
// namespaces. NsenterMounter works by executing nsenter to run commands in
|
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.
|
// 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 {
|
type NsenterMounter struct {
|
||||||
// a map of commands to their paths on the host filesystem
|
ne *nsenter.Nsenter
|
||||||
paths map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNsenterMounter() *NsenterMounter {
|
func NewNsenterMounter() *NsenterMounter {
|
||||||
m := &NsenterMounter{
|
return &NsenterMounter{ne: nsenter.NewNsenter()}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NsenterMounter implements mount.Interface
|
// NsenterMounter implements mount.Interface
|
||||||
var _ = Interface(&NsenterMounter{})
|
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
|
// 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()
|
// 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 {
|
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
|
// doNsenterMount nsenters the host's mount namespace and performs the
|
||||||
// requested mount.
|
// requested mount.
|
||||||
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
|
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)
|
glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
|
||||||
args := n.makeNsenterArgs(source, target, fstype, options)
|
cmd, args := n.makeNsenterArgs(source, target, fstype, options)
|
||||||
|
outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
|
||||||
glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
|
|
||||||
exec := exec.New()
|
|
||||||
outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
|
|
||||||
if len(outputBytes) != 0 {
|
if len(outputBytes) != 0 {
|
||||||
glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
|
glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeNsenterArgs makes a list of argument to nsenter in order to do the
|
// makeNsenterArgs makes a list of argument to nsenter in order to do the
|
||||||
// requested mount.
|
// requested mount.
|
||||||
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
|
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
|
||||||
mountCmd := n.absHostPath("mount")
|
mountCmd := n.ne.AbsHostPath("mount")
|
||||||
mountArgs := makeMountArgs(source, target, fstype, options)
|
mountArgs := makeMountArgs(source, target, fstype, options)
|
||||||
|
|
||||||
if systemdRunPath, hasSystemd := n.paths["systemd-run"]; hasSystemd {
|
if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
|
||||||
// Complete command line:
|
// Complete command line:
|
||||||
// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
|
// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
|
||||||
// Expected flow is:
|
// 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
|
// No code here, mountCmd and mountArgs use /bin/mount
|
||||||
}
|
}
|
||||||
|
|
||||||
nsenterArgs := []string{
|
return mountCmd, mountArgs
|
||||||
"--mount=" + hostMountNamespacePath,
|
|
||||||
"--",
|
|
||||||
mountCmd,
|
|
||||||
}
|
|
||||||
nsenterArgs = append(nsenterArgs, mountArgs...)
|
|
||||||
|
|
||||||
return nsenterArgs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount runs umount(8) in the host's mount namespace.
|
// Unmount runs umount(8) in the host's mount namespace.
|
||||||
func (n *NsenterMounter) Unmount(target string) error {
|
func (n *NsenterMounter) Unmount(target string) error {
|
||||||
args := []string{
|
args := []string{target}
|
||||||
"--mount=" + hostMountNamespacePath,
|
|
||||||
"--",
|
|
||||||
n.absHostPath("umount"),
|
|
||||||
target,
|
|
||||||
}
|
|
||||||
// No need to execute systemd-run here, it's enough that unmount is executed
|
// 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)
|
// in the host's mount namespace. It will finish appropriate fuse daemon(s)
|
||||||
// running in any scope.
|
// running in any scope.
|
||||||
glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
|
glog.V(5).Infof("nsenter unmount args: %v", args)
|
||||||
exec := exec.New()
|
outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
|
||||||
outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
|
|
||||||
if len(outputBytes) != 0 {
|
if len(outputBytes) != 0 {
|
||||||
glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
|
glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +141,7 @@ func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
|
||||||
|
|
||||||
func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||||
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
|
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
|
// 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.
|
// 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
|
// 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
|
// 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}
|
args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
|
||||||
glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)
|
glog.V(5).Infof("nsenter findmnt args: %v", args)
|
||||||
|
out, err := n.ne.Exec("findmnt", args).CombinedOutput()
|
||||||
exec := exec.New()
|
|
||||||
out, err := exec.Command(nsenterPath, args...).CombinedOutput()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
|
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.
|
// 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
|
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
|
||||||
// to a device.
|
// to a device.
|
||||||
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
|
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
|
//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)
|
return getDeviceNameFromMount(n, mountPath, pluginDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NsenterMounter) absHostPath(command string) string {
|
func (n *NsenterMounter) MakeRShared(path string) error {
|
||||||
path, ok := n.paths[command]
|
return doMakeRShared(path, hostProcMountinfoPath)
|
||||||
if !ok {
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NsenterMounter) MakeRShared(path string) error {
|
func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
|
||||||
nsenterCmd := nsenterPath
|
var pathType FileType
|
||||||
nsenterArgs := []string{
|
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
|
||||||
"--mount=" + hostMountNamespacePath,
|
if err != nil {
|
||||||
"--",
|
return pathType, err
|
||||||
n.absHostPath("mount"),
|
|
||||||
}
|
}
|
||||||
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
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
type NsenterMounter struct{}
|
type NsenterMounter struct{}
|
||||||
|
|
||||||
func NewNsenterMounter() *NsenterMounter {
|
func NewNsenterMounter() *NsenterMounter {
|
||||||
|
@ -65,3 +69,19 @@ func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (stri
|
||||||
func (*NsenterMounter) MakeRShared(path string) error {
|
func (*NsenterMounter) MakeRShared(path string) error {
|
||||||
return nil
|
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 {
|
func (mounter *fakeMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||||
return errors.New("not implemented")
|
return errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *fakeMounter) Unmount(target string) error {
|
func (mounter *fakeMounter) Unmount(target string) error {
|
||||||
return errors.New("not implemented")
|
return errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *fakeMounter) List() ([]mount.MountPoint, error) {
|
func (mounter *fakeMounter) List() ([]mount.MountPoint, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter fakeMounter) DeviceOpened(pathname string) (bool, error) {
|
func (mounter fakeMounter) DeviceOpened(pathname string) (bool, error) {
|
||||||
return false, errors.New("not implemented")
|
return false, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *fakeMounter) PathIsDevice(pathname string) (bool, error) {
|
func (mounter *fakeMounter) PathIsDevice(pathname string) (bool, error) {
|
||||||
return false, errors.New("not implemented")
|
return false, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *fakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
func (mounter *fakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||||
return "", errors.New("not implemented")
|
return "", errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mounter *fakeMounter) IsMountPointMatch(mp mount.MountPoint, dir string) bool {
|
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) {
|
func (mounter *fakeMounter) IsNotMountPoint(dir string) (bool, error) {
|
||||||
return mount.IsNotMountPoint(mounter, dir)
|
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) {
|
func (mounter *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
name := path.Base(file)
|
name := path.Base(file)
|
||||||
if strings.HasPrefix(name, "mount") {
|
if strings.HasPrefix(name, "mount") {
|
||||||
|
|
|
@ -11,21 +11,9 @@ go_library(
|
||||||
srcs = [
|
srcs = [
|
||||||
"doc.go",
|
"doc.go",
|
||||||
"host_path.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 = [
|
deps = [
|
||||||
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/util/volumehelper:go_default_library",
|
"//pkg/volume/util/volumehelper:go_default_library",
|
||||||
"//pkg/volume/validation: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/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/types: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/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(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = select({
|
srcs = ["host_path_test.go"],
|
||||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
|
||||||
"host_path_test.go",
|
|
||||||
],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
library = ":go_default_library",
|
library = ":go_default_library",
|
||||||
deps = select({
|
deps = [
|
||||||
"@io_bazel_rules_go//go/platform:linux_amd64": [
|
"//pkg/util/file:go_default_library",
|
||||||
"//pkg/util/file:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/testing:go_default_library",
|
"//pkg/volume/testing:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1: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/api/resource:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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/types:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
],
|
],
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||||
"k8s.io/kubernetes/pkg/volume/validation"
|
"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
|
pathType = hostPathVolumeSource.Type
|
||||||
}
|
}
|
||||||
return &hostPathMounter{
|
return &hostPathMounter{
|
||||||
hostPath: &hostPath{path: path, pathType: pathType, containerized: opts.Containerized},
|
hostPath: &hostPath{path: path, pathType: pathType},
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
|
mounter: plugin.host.GetMounter(plugin.GetPluginName()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,9 +184,8 @@ func newProvisioner(options volume.VolumeOptions, host volume.VolumeHost, plugin
|
||||||
// HostPath volumes represent a bare host file or directory mount.
|
// HostPath volumes represent a bare host file or directory mount.
|
||||||
// The direct at the specified path will be directly exposed to the container.
|
// The direct at the specified path will be directly exposed to the container.
|
||||||
type hostPath struct {
|
type hostPath struct {
|
||||||
path string
|
path string
|
||||||
pathType *v1.HostPathType
|
pathType *v1.HostPathType
|
||||||
containerized bool
|
|
||||||
volume.MetricsNil
|
volume.MetricsNil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +196,7 @@ func (hp *hostPath) GetPath() string {
|
||||||
type hostPathMounter struct {
|
type hostPathMounter struct {
|
||||||
*hostPath
|
*hostPath
|
||||||
readOnly bool
|
readOnly bool
|
||||||
|
mounter mount.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ volume.Mounter = &hostPathMounter{}
|
var _ volume.Mounter = &hostPathMounter{}
|
||||||
|
@ -224,7 +226,7 @@ func (b *hostPathMounter) SetUp(fsGroup *int64) error {
|
||||||
if *b.pathType == v1.HostPathUnset {
|
if *b.pathType == v1.HostPathUnset {
|
||||||
return nil
|
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.
|
// SetUpAt does not make sense for host paths - probably programmer error.
|
||||||
|
@ -340,132 +342,77 @@ type hostPathTypeChecker interface {
|
||||||
GetPath() string
|
GetPath() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileTypeChecker interface {
|
type fileTypeChecker struct {
|
||||||
getFileType(fileInfo os.FileInfo) (v1.HostPathType, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is implemented in per-OS files
|
|
||||||
type defaultFileTypeChecker struct{}
|
|
||||||
|
|
||||||
type osFileTypeChecker struct {
|
|
||||||
path string
|
path string
|
||||||
exists bool
|
exists bool
|
||||||
info os.FileInfo
|
mounter mount.Interface
|
||||||
checker fileTypeChecker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) Exists() bool {
|
func (ftc *fileTypeChecker) Exists() bool {
|
||||||
return ftc.exists
|
return ftc.mounter.ExistsPath(ftc.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) IsFile() bool {
|
func (ftc *fileTypeChecker) IsFile() bool {
|
||||||
if !ftc.Exists() {
|
if !ftc.Exists() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !ftc.info.IsDir()
|
return !ftc.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) MakeFile() error {
|
func (ftc *fileTypeChecker) MakeFile() error {
|
||||||
f, err := os.OpenFile(ftc.path, os.O_CREATE, os.FileMode(0644))
|
return ftc.mounter.MakeFile(ftc.path)
|
||||||
defer f.Close()
|
|
||||||
if err != nil {
|
|
||||||
if !os.IsExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) IsDir() bool {
|
func (ftc *fileTypeChecker) IsDir() bool {
|
||||||
if !ftc.Exists() {
|
if !ftc.Exists() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return ftc.info.IsDir()
|
pathType, err := ftc.mounter.GetFileType(ftc.path)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return blkDevType == v1.HostPathBlockDev
|
return string(pathType) == string(v1.HostPathDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) IsChar() bool {
|
func (ftc *fileTypeChecker) MakeDir() error {
|
||||||
if !ftc.Exists() {
|
return ftc.mounter.MakeDir(ftc.path)
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
charDevType, err := ftc.checker.getFileType(ftc.info)
|
func (ftc *fileTypeChecker) IsBlock() bool {
|
||||||
|
blkDevType, err := ftc.mounter.GetFileType(ftc.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return charDevType == v1.HostPathCharDev
|
return string(blkDevType) == string(v1.HostPathBlockDev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftc *osFileTypeChecker) IsSocket() bool {
|
func (ftc *fileTypeChecker) IsChar() bool {
|
||||||
if !ftc.Exists() {
|
charDevType, err := ftc.mounter.GetFileType(ftc.path)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
socketType, err := ftc.checker.getFileType(ftc.info)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
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
|
return ftc.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOSFileTypeChecker(path string, checker fileTypeChecker) (hostPathTypeChecker, error) {
|
func newFileTypeChecker(path string, mounter mount.Interface) hostPathTypeChecker {
|
||||||
ftc := osFileTypeChecker{path: path, checker: checker}
|
return &fileTypeChecker{path: path, mounter: mounter}
|
||||||
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 checkType(path string, pathType *v1.HostPathType, containerized bool) error {
|
// checkType checks whether the given path is the exact pathType
|
||||||
var ftc hostPathTypeChecker
|
func checkType(path string, pathType *v1.HostPathType, mounter mount.Interface) error {
|
||||||
var err error
|
return checkTypeInternal(newFileTypeChecker(path, mounter), pathType)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkTypeInternal(ftc hostPathTypeChecker, pathType *v1.HostPathType) error {
|
func checkTypeInternal(ftc hostPathTypeChecker, pathType *v1.HostPathType) error {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2014 The Kubernetes Authors.
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
@ -30,6 +28,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||||
|
utilmount "k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
)
|
)
|
||||||
|
@ -323,8 +322,58 @@ type fakeFileTypeChecker struct {
|
||||||
desiredType string
|
desiredType string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fftc *fakeFileTypeChecker) getFileType(_ os.FileInfo) (v1.HostPathType, error) {
|
func (fftc *fakeFileTypeChecker) Mount(source string, target string, fstype string, options []string) error {
|
||||||
return *newHostPathType(fftc.desiredType), nil
|
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 {
|
func setUp() error {
|
||||||
|
@ -363,14 +412,16 @@ func TestOSFileTypeChecker(t *testing.T) {
|
||||||
isChar bool
|
isChar bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Existing Folder",
|
name: "Existing Folder",
|
||||||
path: "/tmp/ExistingFolder",
|
path: "/tmp/ExistingFolder",
|
||||||
isDir: true,
|
desiredType: string(utilmount.FileTypeDirectory),
|
||||||
|
isDir: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Existing File",
|
name: "Existing File",
|
||||||
path: "/tmp/ExistingFolder/foo",
|
path: "/tmp/ExistingFolder/foo",
|
||||||
isFile: true,
|
desiredType: string(utilmount.FileTypeFile),
|
||||||
|
isFile: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Existing Socket File",
|
name: "Existing Socket File",
|
||||||
|
@ -393,11 +444,8 @@ func TestOSFileTypeChecker(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
oftc, err := newOSFileTypeChecker(tc.path,
|
fakeFTC := &fakeFileTypeChecker{desiredType: tc.desiredType}
|
||||||
&fakeFileTypeChecker{desiredType: tc.desiredType})
|
oftc := newFileTypeChecker(tc.path, fakeFTC)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("[%d: %q] expect nil, but got %v", i, tc.name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := oftc.GetPath()
|
path := oftc.GetPath()
|
||||||
if path != tc.path {
|
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
|
CloudTags *map[string]string
|
||||||
// Volume provisioning parameters from StorageClass
|
// Volume provisioning parameters from StorageClass
|
||||||
Parameters map[string]string
|
Parameters map[string]string
|
||||||
// This flag helps identify whether kubelet is running in a container
|
|
||||||
Containerized bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DynamicPluginProber interface {
|
type DynamicPluginProber interface {
|
||||||
|
|
Loading…
Reference in New Issue