Switch to github.com/golang/dep for vendoring
Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
parent
d6ab91be27
commit
8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions
50
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"fake.go",
|
||||
"mount.go",
|
||||
"mount_linux.go",
|
||||
"nsenter_mount.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/sets",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"mount_linux_test.go",
|
||||
"safe_format_and_mount_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/exec:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
18
vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
Copyright 2014 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 mount defines an interface to mounting filesystems.
|
||||
package mount // import "k8s.io/kubernetes/pkg/util/mount"
|
146
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
Normal file
146
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Copyright 2015 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 mount
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// FakeMounter implements mount.Interface for tests.
|
||||
type FakeMounter struct {
|
||||
MountPoints []MountPoint
|
||||
Log []FakeAction
|
||||
// Some tests run things in parallel, make sure the mounter does not produce
|
||||
// any golang's DATA RACE warnings.
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
var _ Interface = &FakeMounter{}
|
||||
|
||||
// Values for FakeAction.Action
|
||||
const FakeActionMount = "mount"
|
||||
const FakeActionUnmount = "unmount"
|
||||
|
||||
// FakeAction objects are logged every time a fake mount or unmount is called.
|
||||
type FakeAction struct {
|
||||
Action string // "mount" or "unmount"
|
||||
Target string // applies to both mount and unmount actions
|
||||
Source string // applies only to "mount" actions
|
||||
FSType string // applies only to "mount" actions
|
||||
}
|
||||
|
||||
func (f *FakeMounter) ResetLog() {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
f.Log = []FakeAction{}
|
||||
}
|
||||
|
||||
func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
// find 'bind' option
|
||||
for _, option := range options {
|
||||
if option == "bind" {
|
||||
// This is a bind-mount. In order to mimic linux behaviour, we must
|
||||
// use the original device of the bind-mount as the real source.
|
||||
// E.g. when mounted /dev/sda like this:
|
||||
// $ mount /dev/sda /mnt/test
|
||||
// $ mount -o bind /mnt/test /mnt/bound
|
||||
// then /proc/mount contains:
|
||||
// /dev/sda /mnt/test
|
||||
// /dev/sda /mnt/bound
|
||||
// (and not /mnt/test /mnt/bound)
|
||||
// I.e. we must use /dev/sda as source instead of /mnt/test in the
|
||||
// bind mount.
|
||||
for _, mnt := range f.MountPoints {
|
||||
if source == mnt.Path {
|
||||
source = mnt.Device
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype})
|
||||
glog.V(5).Infof("Fake mounter: mounted %s to %s", source, target)
|
||||
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: target, Source: source, FSType: fstype})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) Unmount(target string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
newMountpoints := []MountPoint{}
|
||||
for _, mp := range f.MountPoints {
|
||||
if mp.Path == target {
|
||||
glog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, target)
|
||||
// Don't copy it to newMountpoints
|
||||
continue
|
||||
}
|
||||
newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
|
||||
}
|
||||
f.MountPoints = newMountpoints
|
||||
f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: target})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) List() ([]MountPoint, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
return f.MountPoints, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
for _, mp := range f.MountPoints {
|
||||
if mp.Path == file {
|
||||
glog.V(5).Infof("isLikelyMountPoint for %s: mounted %s, false", file, mp.Path)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
glog.V(5).Infof("isLikelyMountPoint for %s: true", file)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) DeviceOpened(pathname string) (bool, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
for _, mp := range f.MountPoints {
|
||||
if mp.Device == pathname {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return getDeviceNameFromMount(f, mountPath, pluginDir)
|
||||
}
|
206
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
Normal file
206
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
Copyright 2014 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.
|
||||
*/
|
||||
|
||||
// TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have
|
||||
// an alternate platform, we will need to abstract further.
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default mount command if mounter path is not specified
|
||||
defaultMountCommand = "mount"
|
||||
MountsInGlobalPDPath = "mounts"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
// Mount mounts source to target as fstype with given options.
|
||||
Mount(source string, target string, fstype string, options []string) error
|
||||
// Unmount unmounts given target.
|
||||
Unmount(target string) error
|
||||
// List returns a list of all mounted filesystems. This can be large.
|
||||
// On some platforms, reading mounts is not guaranteed consistent (i.e.
|
||||
// it could change between chunked reads). This is guaranteed to be
|
||||
// consistent.
|
||||
List() ([]MountPoint, error)
|
||||
// IsLikelyNotMountPoint determines if a directory is a mountpoint.
|
||||
// It should return ErrNotExist when the directory does not exist.
|
||||
IsLikelyNotMountPoint(file string) (bool, error)
|
||||
// DeviceOpened determines if the device is in use elsewhere
|
||||
// on the system, i.e. still mounted.
|
||||
DeviceOpened(pathname string) (bool, error)
|
||||
// PathIsDevice determines if a path is a device.
|
||||
PathIsDevice(pathname string) (bool, error)
|
||||
// GetDeviceNameFromMount finds the device name by checking the mount path
|
||||
// to get the global mount path which matches its plugin directory
|
||||
GetDeviceNameFromMount(mountPath, pluginDir string) (string, error)
|
||||
}
|
||||
|
||||
// Compile-time check to ensure all Mounter implementations satisfy
|
||||
// the mount interface
|
||||
var _ Interface = &Mounter{}
|
||||
|
||||
// This represents a single line in /proc/mounts or /etc/fstab.
|
||||
type MountPoint struct {
|
||||
Device string
|
||||
Path string
|
||||
Type string
|
||||
Opts []string
|
||||
Freq int
|
||||
Pass int
|
||||
}
|
||||
|
||||
// SafeFormatAndMount probes a device to see if it is formatted.
|
||||
// Namely it checks to see if a file system is present. If so it
|
||||
// mounts it otherwise the device is formatted first then mounted.
|
||||
type SafeFormatAndMount struct {
|
||||
Interface
|
||||
Runner exec.Interface
|
||||
}
|
||||
|
||||
// FormatAndMount formats the given disk, if needed, and mounts it.
|
||||
// That is if the disk is not formatted and it is not being mounted as
|
||||
// read-only it will format it first then mount it. Otherwise, if the
|
||||
// disk is already formatted or it is being mounted as read-only, it
|
||||
// will be mounted without formatting.
|
||||
func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
|
||||
// Don't attempt to format if mounting as readonly. Go straight to mounting.
|
||||
for _, option := range options {
|
||||
if option == "ro" {
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
}
|
||||
return mounter.formatAndMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// New returns a mount.Interface for the current system.
|
||||
// It provides options to override the default mounter behavior.
|
||||
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||
func New(mounterPath string) Interface {
|
||||
// If mounter-path flag is not set, use default mount path
|
||||
if mounterPath == "" {
|
||||
mounterPath = defaultMountCommand
|
||||
}
|
||||
|
||||
return &Mounter{
|
||||
mounterPath: mounterPath,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMountRefs finds all other references to the device referenced
|
||||
// by mountPath; returns a list of paths.
|
||||
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
|
||||
mps, err := mounter.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the device name.
|
||||
deviceName := ""
|
||||
// If mountPath is symlink, need get its target path.
|
||||
slTarget, err := filepath.EvalSymlinks(mountPath)
|
||||
if err != nil {
|
||||
slTarget = mountPath
|
||||
}
|
||||
for i := range mps {
|
||||
if mps[i].Path == slTarget {
|
||||
deviceName = mps[i].Device
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find all references to the device.
|
||||
var refs []string
|
||||
if deviceName == "" {
|
||||
glog.Warningf("could not determine device for path: %q", mountPath)
|
||||
} else {
|
||||
for i := range mps {
|
||||
if mps[i].Device == deviceName && mps[i].Path != slTarget {
|
||||
refs = append(refs, mps[i].Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// GetDeviceNameFromMount: given a mnt point, find the device from /proc/mounts
|
||||
// returns the device name, reference count, and error code
|
||||
func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) {
|
||||
mps, err := mounter.List()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
// Find the device name.
|
||||
// FIXME if multiple devices mounted on the same mount path, only the first one is returned
|
||||
device := ""
|
||||
// If mountPath is symlink, need get its target path.
|
||||
slTarget, err := filepath.EvalSymlinks(mountPath)
|
||||
if err != nil {
|
||||
slTarget = mountPath
|
||||
}
|
||||
for i := range mps {
|
||||
if mps[i].Path == slTarget {
|
||||
device = mps[i].Device
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find all references to the device.
|
||||
refCount := 0
|
||||
for i := range mps {
|
||||
if mps[i].Device == device {
|
||||
refCount++
|
||||
}
|
||||
}
|
||||
return device, refCount, nil
|
||||
}
|
||||
|
||||
// getDeviceNameFromMount find the device name from /proc/mounts in which
|
||||
// the mount path reference should match the given plugin directory. In case no mount path reference
|
||||
// matches, returns the volume name taken from its given mountPath
|
||||
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
|
||||
refs, err := GetMountRefs(mounter, mountPath)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
glog.V(4).Infof("Directory %s is not mounted", mountPath)
|
||||
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||
}
|
||||
basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
|
||||
for _, ref := range refs {
|
||||
if strings.HasPrefix(ref, basemountPath) {
|
||||
volumeID, err := filepath.Rel(basemountPath, ref)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
return volumeID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return path.Base(mountPath), nil
|
||||
}
|
396
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
Normal file
396
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,396 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
// How many times to retry for a consistent read of /proc/mounts.
|
||||
maxListTries = 3
|
||||
// Number of fields per line in /proc/mounts as per the fstab man page.
|
||||
expectedNumFieldsPerLine = 6
|
||||
// Location of the mount file to use
|
||||
procMountsPath = "/proc/mounts"
|
||||
)
|
||||
|
||||
const (
|
||||
// 'fsck' found errors and corrected them
|
||||
fsckErrorsCorrected = 1
|
||||
// 'fsck' found errors but exited without correcting them
|
||||
fsckErrorsUncorrected = 4
|
||||
)
|
||||
|
||||
// Mounter provides the default implementation of mount.Interface
|
||||
// for the linux platform. This implementation assumes that the
|
||||
// kubelet is running in the host's root mount namespace.
|
||||
type Mounter struct {
|
||||
mounterPath string
|
||||
}
|
||||
|
||||
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
|
||||
// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
|
||||
// type, where kernel handles fs type for you. The mount 'options' is a list of options,
|
||||
// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
|
||||
// required, call Mount with an empty string list or nil.
|
||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
// Path to mounter binary. Set to mount accessible via $PATH by default.
|
||||
// All Linux distros are expected to be shipped with a mount utility that an support bind mounts.
|
||||
mounterPath := defaultMountCommand
|
||||
bind, bindRemountOpts := isBind(options)
|
||||
if bind {
|
||||
err := doMount(mounterPath, source, target, fstype, []string{"bind"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return doMount(mounterPath, source, target, fstype, bindRemountOpts)
|
||||
}
|
||||
// The list of filesystems that require containerized mounter on GCI image cluster
|
||||
fsTypesNeedMounter := sets.NewString("nfs", "glusterfs")
|
||||
if fsTypesNeedMounter.Has(fstype) {
|
||||
mounterPath = mounter.mounterPath
|
||||
}
|
||||
return doMount(mounterPath, source, target, fstype, options)
|
||||
}
|
||||
|
||||
// isBind detects whether a bind mount is being requested and makes the remount options to
|
||||
// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
|
||||
// The list equals:
|
||||
// options - 'bind' + 'remount' (no duplicate)
|
||||
func isBind(options []string) (bool, []string) {
|
||||
bindRemountOpts := []string{"remount"}
|
||||
bind := false
|
||||
|
||||
if len(options) != 0 {
|
||||
for _, option := range options {
|
||||
switch option {
|
||||
case "bind":
|
||||
bind = true
|
||||
break
|
||||
case "remount":
|
||||
break
|
||||
default:
|
||||
bindRemountOpts = append(bindRemountOpts, option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bind, bindRemountOpts
|
||||
}
|
||||
|
||||
// doMount runs the mount command.
|
||||
func doMount(mountCmd string, source string, target string, fstype string, options []string) error {
|
||||
glog.V(4).Infof("Mounting %s %s %s %v with command: %q", source, target, fstype, options, mountCmd)
|
||||
mountArgs := makeMountArgs(source, target, fstype, options)
|
||||
|
||||
glog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
|
||||
command := exec.Command(mountCmd, mountArgs...)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
glog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n", err, mountCmd, source, target, fstype, options, string(output))
|
||||
return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n",
|
||||
err, mountCmd, source, target, fstype, options, string(output))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// makeMountArgs makes the arguments to the mount(8) command.
|
||||
func makeMountArgs(source, target, fstype string, options []string) []string {
|
||||
// Build mount command as follows:
|
||||
// mount [-t $fstype] [-o $options] [$source] $target
|
||||
mountArgs := []string{}
|
||||
if len(fstype) > 0 {
|
||||
mountArgs = append(mountArgs, "-t", fstype)
|
||||
}
|
||||
if len(options) > 0 {
|
||||
mountArgs = append(mountArgs, "-o", strings.Join(options, ","))
|
||||
}
|
||||
if len(source) > 0 {
|
||||
mountArgs = append(mountArgs, source)
|
||||
}
|
||||
mountArgs = append(mountArgs, target)
|
||||
|
||||
return mountArgs
|
||||
}
|
||||
|
||||
// Unmount unmounts the target.
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
glog.V(4).Infof("Unmounting %s", target)
|
||||
command := exec.Command("umount", target)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns a list of all mounted filesystems.
|
||||
func (*Mounter) List() ([]MountPoint, error) {
|
||||
return listProcMounts(procMountsPath)
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
||||
// It is fast but not necessarily ALWAYS correct. If the path is in fact
|
||||
// a bind mount from one part of a mount to another it will not be detected.
|
||||
// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
|
||||
// will return true. When in fact /tmp/b is a mount point. If this situation
|
||||
// if of interest to you, don't use this function...
|
||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return IsNotMountPoint(file)
|
||||
}
|
||||
|
||||
func IsNotMountPoint(file string) (bool, error) {
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
rootStat, err := os.Lstat(file + "/..")
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
// If the directory has a different device as parent, then it is a mountpoint.
|
||||
if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
|
||||
// If pathname is not a device, log and return false with nil error.
|
||||
// If open returns errno EBUSY, return true with nil error.
|
||||
// If open returns nil, return false with nil error.
|
||||
// Otherwise, return false with error
|
||||
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return exclusiveOpenFailsOnDevice(pathname)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
|
||||
isDevice, err := pathIsDevice(pathname)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(
|
||||
"PathIsDevice failed for path %q: %v",
|
||||
pathname,
|
||||
err)
|
||||
}
|
||||
if !isDevice {
|
||||
glog.Errorf("Path %q is not refering to a device.", pathname)
|
||||
return false, nil
|
||||
}
|
||||
fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
|
||||
// If the device is in use, open will return an invalid fd.
|
||||
// When this happens, it is expected that Close will fail and throw an error.
|
||||
defer syscall.Close(fd)
|
||||
if errno == nil {
|
||||
// device not in use
|
||||
return false, nil
|
||||
} else if errno == syscall.EBUSY {
|
||||
// device is in use
|
||||
return true, nil
|
||||
}
|
||||
// error during call to Open
|
||||
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)
|
||||
}
|
||||
|
||||
func listProcMounts(mountFilePath string) ([]MountPoint, error) {
|
||||
hash1, err := readProcMounts(mountFilePath, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < maxListTries; i++ {
|
||||
mps := []MountPoint{}
|
||||
hash2, err := readProcMounts(mountFilePath, &mps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hash1 == hash2 {
|
||||
// Success
|
||||
return mps, nil
|
||||
}
|
||||
hash1 = hash2
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get a consistent snapshot of %v after %d tries", mountFilePath, maxListTries)
|
||||
}
|
||||
|
||||
// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash
|
||||
// of the contents. If the out argument is not nil, this fills it with MountPoint structs.
|
||||
func readProcMounts(mountFilePath string, out *[]MountPoint) (uint32, error) {
|
||||
file, err := os.Open(mountFilePath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
return readProcMountsFrom(file, out)
|
||||
}
|
||||
|
||||
func readProcMountsFrom(file io.Reader, out *[]MountPoint) (uint32, error) {
|
||||
hash := adler32.New()
|
||||
scanner := bufio.NewReader(file)
|
||||
for {
|
||||
line, err := scanner.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != expectedNumFieldsPerLine {
|
||||
return 0, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
|
||||
}
|
||||
|
||||
fmt.Fprintf(hash, "%s", line)
|
||||
|
||||
if out != nil {
|
||||
mp := MountPoint{
|
||||
Device: fields[0],
|
||||
Path: fields[1],
|
||||
Type: fields[2],
|
||||
Opts: strings.Split(fields[3], ","),
|
||||
}
|
||||
|
||||
freq, err := strconv.Atoi(fields[4])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mp.Freq = freq
|
||||
|
||||
pass, err := strconv.Atoi(fields[5])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mp.Pass = pass
|
||||
|
||||
*out = append(*out, mp)
|
||||
}
|
||||
}
|
||||
return hash.Sum32(), nil
|
||||
}
|
||||
|
||||
// formatAndMount uses unix utils to format and mount the given disk
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
options = append(options, "defaults")
|
||||
|
||||
// Run fsck on the disk to fix repairable issues
|
||||
glog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
|
||||
args := []string{"-a", source}
|
||||
cmd := mounter.Runner.Command("fsck", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
ee, isExitError := err.(utilexec.ExitError)
|
||||
switch {
|
||||
case err == utilexec.ErrExecutableNotFound:
|
||||
glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
|
||||
case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
|
||||
glog.Infof("Device %s has errors which were corrected by fsck.", source)
|
||||
case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
|
||||
return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out))
|
||||
case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
|
||||
glog.Infof("`fsck` error %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
// Try to mount the disk
|
||||
glog.V(4).Infof("Attempting to mount disk: %s %s %s", fstype, source, target)
|
||||
err = mounter.Interface.Mount(source, target, fstype, options)
|
||||
if err != nil {
|
||||
// It is possible that this disk is not formatted. Double check using diskLooksUnformatted
|
||||
notFormatted, err := mounter.diskLooksUnformatted(source)
|
||||
if err == nil && notFormatted {
|
||||
args = []string{source}
|
||||
// Disk is unformatted so format it.
|
||||
// Use 'ext4' as the default
|
||||
if len(fstype) == 0 {
|
||||
fstype = "ext4"
|
||||
}
|
||||
if fstype == "ext4" || fstype == "ext3" {
|
||||
args = []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", source}
|
||||
}
|
||||
glog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
|
||||
cmd := mounter.Runner.Command("mkfs."+fstype, args...)
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
// the disk has been formatted successfully try to mount it again.
|
||||
glog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
glog.Errorf("format of disk %q failed: type:(%q) target:(%q) options:(%q)error:(%v)", source, fstype, target, options, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated
|
||||
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
||||
args := []string{"-nd", "-o", "FSTYPE", disk}
|
||||
cmd := mounter.Runner.Command("lsblk", args...)
|
||||
glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
|
||||
dataOut, err := cmd.CombinedOutput()
|
||||
output := strings.TrimSpace(string(dataOut))
|
||||
|
||||
// TODO (#13212): check if this disk has partitions and return false, and
|
||||
// an error if so.
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return output == "", nil
|
||||
}
|
182
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
Normal file
182
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadProcMountsFrom(t *testing.T) {
|
||||
successCase :=
|
||||
`/dev/0 /path/to/0 type0 flags 0 0
|
||||
/dev/1 /path/to/1 type1 flags 1 1
|
||||
/dev/2 /path/to/2 type2 flags,1,2=3 2 2
|
||||
`
|
||||
hash, err := readProcMountsFrom(strings.NewReader(successCase), nil)
|
||||
if err != nil {
|
||||
t.Errorf("expected success")
|
||||
}
|
||||
if hash != 0xa3522051 {
|
||||
t.Errorf("expected 0xa3522051, got %#x", hash)
|
||||
}
|
||||
mounts := []MountPoint{}
|
||||
hash, err = readProcMountsFrom(strings.NewReader(successCase), &mounts)
|
||||
if err != nil {
|
||||
t.Errorf("expected success")
|
||||
}
|
||||
if hash != 0xa3522051 {
|
||||
t.Errorf("expected 0xa3522051, got %#x", hash)
|
||||
}
|
||||
if len(mounts) != 3 {
|
||||
t.Fatalf("expected 3 mounts, got %d", len(mounts))
|
||||
}
|
||||
mp := MountPoint{"/dev/0", "/path/to/0", "type0", []string{"flags"}, 0, 0}
|
||||
if !mountPointsEqual(&mounts[0], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[0]: %#v", mounts[0])
|
||||
}
|
||||
mp = MountPoint{"/dev/1", "/path/to/1", "type1", []string{"flags"}, 1, 1}
|
||||
if !mountPointsEqual(&mounts[1], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[1]: %#v", mounts[1])
|
||||
}
|
||||
mp = MountPoint{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, 2, 2}
|
||||
if !mountPointsEqual(&mounts[2], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[2]: %#v", mounts[2])
|
||||
}
|
||||
|
||||
errorCases := []string{
|
||||
"/dev/0 /path/to/mount\n",
|
||||
"/dev/1 /path/to/mount type flags a 0\n",
|
||||
"/dev/2 /path/to/mount type flags 0 b\n",
|
||||
}
|
||||
for _, ec := range errorCases {
|
||||
_, err := readProcMountsFrom(strings.NewReader(ec), &mounts)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mountPointsEqual(a, b *MountPoint) bool {
|
||||
if a.Device != b.Device || a.Path != b.Path || a.Type != b.Type || !slicesEqual(a.Opts, b.Opts) || a.Pass != b.Pass || a.Freq != b.Freq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func slicesEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestGetMountRefs(t *testing.T) {
|
||||
fm := &FakeMounter{
|
||||
MountPoints: []MountPoint{
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"},
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedRefs []string
|
||||
}{
|
||||
{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod",
|
||||
[]string{
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd",
|
||||
},
|
||||
},
|
||||
{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1",
|
||||
[]string{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2",
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if refs, err := GetMountRefs(fm, test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) {
|
||||
t.Errorf("%d. getMountRefs(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setEquivalent(set1, set2 []string) bool {
|
||||
map1 := make(map[string]bool)
|
||||
map2 := make(map[string]bool)
|
||||
for _, s := range set1 {
|
||||
map1[s] = true
|
||||
}
|
||||
for _, s := range set2 {
|
||||
map2[s] = true
|
||||
}
|
||||
|
||||
for s := range map1 {
|
||||
if !map2[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for s := range map2 {
|
||||
if !map1[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestGetDeviceNameFromMount(t *testing.T) {
|
||||
fm := &FakeMounter{
|
||||
MountPoints: []MountPoint{
|
||||
{Device: "/dev/disk/by-path/prefix-lun-1",
|
||||
Path: "/mnt/111"},
|
||||
{Device: "/dev/disk/by-path/prefix-lun-1",
|
||||
Path: "/mnt/222"},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedDevice string
|
||||
expectedRefs int
|
||||
}{
|
||||
{
|
||||
"/mnt/222",
|
||||
"/dev/disk/by-path/prefix-lun-1",
|
||||
2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if device, refs, err := GetDeviceNameFromMount(fm, test.mountPath); err != nil || test.expectedRefs != refs || test.expectedDevice != device {
|
||||
t.Errorf("%d. GetDeviceNameFromMount(%s) = (%s, %d), %v; expected (%s,%d), nil", i, test.mountPath, device, refs, err, test.expectedDevice, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
63
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
type Mounter struct {
|
||||
mounterPath string
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||
return []MountPoint{}, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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 IsNotMountPoint(file string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
231
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
Normal file
231
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
func NewNsenterMounter() *NsenterMounter {
|
||||
m := &NsenterMounter{
|
||||
paths: map[string]string{
|
||||
"mount": "",
|
||||
"findmnt": "",
|
||||
"umount": "",
|
||||
},
|
||||
}
|
||||
// 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
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NsenterMounter implements mount.Interface
|
||||
var _ = Interface(&NsenterMounter{})
|
||||
|
||||
const (
|
||||
hostRootFsPath = "/rootfs"
|
||||
hostProcMountsPath = "/rootfs/proc/1/mounts"
|
||||
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 {
|
||||
bind, bindRemountOpts := isBind(options)
|
||||
|
||||
if bind {
|
||||
err := n.doNsenterMount(source, target, fstype, []string{"bind"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.doNsenterMount(source, target, fstype, bindRemountOpts)
|
||||
}
|
||||
|
||||
return n.doNsenterMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// 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()
|
||||
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 {
|
||||
nsenterArgs := []string{
|
||||
"--mount=/rootfs/proc/1/ns/mnt",
|
||||
"--",
|
||||
n.absHostPath("mount"),
|
||||
}
|
||||
|
||||
args := makeMountArgs(source, target, fstype, options)
|
||||
|
||||
return append(nsenterArgs, args...)
|
||||
}
|
||||
|
||||
// Unmount runs umount(8) in the host's mount namespace.
|
||||
func (n *NsenterMounter) Unmount(target string) error {
|
||||
args := []string{
|
||||
"--mount=/rootfs/proc/1/ns/mnt",
|
||||
"--",
|
||||
n.absHostPath("umount"),
|
||||
target,
|
||||
}
|
||||
|
||||
glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
|
||||
exec := exec.New()
|
||||
outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
|
||||
if len(outputBytes) != 0 {
|
||||
glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// List returns a list of all mounted filesystems in the host's mount namespace.
|
||||
func (*NsenterMounter) List() ([]MountPoint, error) {
|
||||
return listProcMounts(hostProcMountsPath)
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
|
||||
// in the host's root mount namespace.
|
||||
func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
file, err := filepath.Abs(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// Check the directory exists
|
||||
if _, err = os.Stat(file); os.IsNotExist(err) {
|
||||
glog.V(5).Infof("findmnt: directory %s does not exist", file)
|
||||
return true, err
|
||||
}
|
||||
// Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get 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
|
||||
// TODO: Need more refactoring for this function. Track the solution with issue #26996
|
||||
args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", 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()
|
||||
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.
|
||||
// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
|
||||
// It's safer to assume that it's not a mount point.
|
||||
return true, nil
|
||||
}
|
||||
mountTarget := strings.Split(string(out), " ")[0]
|
||||
mountTarget = strings.TrimSuffix(mountTarget, "\n")
|
||||
glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)
|
||||
|
||||
if mountTarget == file {
|
||||
glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
|
||||
return false, nil
|
||||
}
|
||||
glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
|
||||
// Returns true if open returns errno EBUSY, and false if errno is nil.
|
||||
// Returns an error if errno is any error other than EBUSY.
|
||||
// Returns with error if pathname is not a device.
|
||||
func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return exclusiveOpenFailsOnDevice(pathname)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
|
||||
func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return getDeviceNameFromMount(n, mountPath, pluginDir)
|
||||
}
|
||||
|
||||
func (n *NsenterMounter) absHostPath(command string) string {
|
||||
path, ok := n.paths[command]
|
||||
if !ok {
|
||||
return command
|
||||
}
|
||||
return path
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
type NsenterMounter struct{}
|
||||
|
||||
func NewNsenterMounter() *NsenterMounter {
|
||||
return &NsenterMounter{}
|
||||
}
|
||||
|
||||
var _ = Interface(&NsenterMounter{})
|
||||
|
||||
func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) Unmount(target string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) List() ([]MountPoint, error) {
|
||||
return []MountPoint{}, nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
}
|
224
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
Normal file
224
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
type ErrorMounter struct {
|
||||
*FakeMounter
|
||||
errIndex int
|
||||
err []error
|
||||
}
|
||||
|
||||
func (mounter *ErrorMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
i := mounter.errIndex
|
||||
mounter.errIndex++
|
||||
if mounter.err != nil && mounter.err[i] != nil {
|
||||
return mounter.err[i]
|
||||
}
|
||||
return mounter.FakeMounter.Mount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
type ExecArgs struct {
|
||||
command string
|
||||
args []string
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
func TestSafeFormatAndMount(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
||||
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
|
||||
}
|
||||
tests := []struct {
|
||||
description string
|
||||
fstype string
|
||||
mountOptions []string
|
||||
execScripts []ExecArgs
|
||||
mountErrs []error
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "Test a read only mount",
|
||||
fstype: "ext4",
|
||||
mountOptions: []string{"ro"},
|
||||
},
|
||||
{
|
||||
description: "Test a normal mount",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 4",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 4}},
|
||||
},
|
||||
expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them: ."),
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 1 (errors found and corrected)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 8}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test that 'lsblk' is called and fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "ext4", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("unknown filesystem type '(null)'"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'lsblk' is called and confirms unformatted disk, format fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
|
||||
},
|
||||
expectedError: fmt.Errorf("formatting failed"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("Still cannot mount"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes with ext3",
|
||||
fstype: "ext3",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext3", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "test that none ext4 fs does not get called with ext4 options.",
|
||||
fstype: "xfs",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil},
|
||||
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
commandScripts := []exec.FakeCommandAction{}
|
||||
for _, expected := range test.execScripts {
|
||||
ecmd := expected.command
|
||||
eargs := expected.args
|
||||
output := expected.output
|
||||
err := expected.err
|
||||
commandScript := func(cmd string, args ...string) exec.Cmd {
|
||||
if cmd != ecmd {
|
||||
t.Errorf("Unexpected command %s. Expecting %s", cmd, ecmd)
|
||||
}
|
||||
|
||||
for j := range args {
|
||||
if args[j] != eargs[j] {
|
||||
t.Errorf("Unexpected args %v. Expecting %v", args, eargs)
|
||||
}
|
||||
}
|
||||
fake := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(output), err },
|
||||
},
|
||||
}
|
||||
return exec.InitFakeCmd(&fake, cmd, args...)
|
||||
}
|
||||
commandScripts = append(commandScripts, commandScript)
|
||||
}
|
||||
|
||||
fake := exec.FakeExec{
|
||||
CommandScript: commandScripts,
|
||||
}
|
||||
|
||||
fakeMounter := ErrorMounter{&FakeMounter{}, 0, test.mountErrs}
|
||||
mounter := SafeFormatAndMount{
|
||||
Interface: &fakeMounter,
|
||||
Runner: &fake,
|
||||
}
|
||||
|
||||
device := "/dev/foo"
|
||||
dest := "/mnt/bar"
|
||||
err := mounter.FormatAndMount(device, dest, test.fstype, test.mountOptions)
|
||||
if test.expectedError == nil {
|
||||
if err != nil {
|
||||
t.Errorf("test \"%s\" unexpected non-error: %v", test.description, err)
|
||||
}
|
||||
|
||||
// Check that something was mounted on the directory
|
||||
isNotMountPoint, err := fakeMounter.IsLikelyNotMountPoint(dest)
|
||||
if err != nil || isNotMountPoint {
|
||||
t.Errorf("test \"%s\" the directory was not mounted", test.description)
|
||||
}
|
||||
|
||||
//check that the correct device was mounted
|
||||
mountedDevice, _, err := GetDeviceNameFromMount(fakeMounter.FakeMounter, dest)
|
||||
if err != nil || mountedDevice != device {
|
||||
t.Errorf("test \"%s\" the correct device was not mounted", test.description)
|
||||
}
|
||||
} else {
|
||||
if err == nil || test.expectedError.Error() != err.Error() {
|
||||
t.Errorf("test \"%s\" unexpected error: \n [%v]. \nExpecting [%v]", test.description, err, test.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue