mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
Update to kube v1.17
Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
committed by
mergify[bot]
parent
327fcd1b1b
commit
3af1e26d7c
122
vendor/k8s.io/kubernetes/pkg/util/filesystem/defaultfs.go
generated
vendored
122
vendor/k8s.io/kubernetes/pkg/util/filesystem/defaultfs.go
generated
vendored
@ -1,122 +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 filesystem
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultFs implements Filesystem using same-named functions from "os" and "io/ioutil"
|
||||
type DefaultFs struct{}
|
||||
|
||||
var _ Filesystem = DefaultFs{}
|
||||
|
||||
// Stat via os.Stat
|
||||
func (DefaultFs) Stat(name string) (os.FileInfo, error) {
|
||||
return os.Stat(name)
|
||||
}
|
||||
|
||||
// Create via os.Create
|
||||
func (DefaultFs) Create(name string) (File, error) {
|
||||
file, err := os.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &defaultFile{file}, nil
|
||||
}
|
||||
|
||||
// Rename via os.Rename
|
||||
func (DefaultFs) Rename(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
// MkdirAll via os.MkdirAll
|
||||
func (DefaultFs) MkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// Chtimes via os.Chtimes
|
||||
func (DefaultFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
// RemoveAll via os.RemoveAll
|
||||
func (DefaultFs) RemoveAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// Remove via os.RemoveAll
|
||||
func (DefaultFs) Remove(name string) error {
|
||||
return os.Remove(name)
|
||||
}
|
||||
|
||||
// ReadFile via ioutil.ReadFile
|
||||
func (DefaultFs) ReadFile(filename string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
}
|
||||
|
||||
// TempDir via ioutil.TempDir
|
||||
func (DefaultFs) TempDir(dir, prefix string) (string, error) {
|
||||
return ioutil.TempDir(dir, prefix)
|
||||
}
|
||||
|
||||
// TempFile via ioutil.TempFile
|
||||
func (DefaultFs) TempFile(dir, prefix string) (File, error) {
|
||||
file, err := ioutil.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &defaultFile{file}, nil
|
||||
}
|
||||
|
||||
// ReadDir via ioutil.ReadDir
|
||||
func (DefaultFs) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
return ioutil.ReadDir(dirname)
|
||||
}
|
||||
|
||||
// Walk via filepath.Walk
|
||||
func (DefaultFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return filepath.Walk(root, walkFn)
|
||||
}
|
||||
|
||||
// defaultFile implements File using same-named functions from "os"
|
||||
type defaultFile struct {
|
||||
file *os.File
|
||||
}
|
||||
|
||||
// Name via os.File.Name
|
||||
func (file *defaultFile) Name() string {
|
||||
return file.file.Name()
|
||||
}
|
||||
|
||||
// Write via os.File.Write
|
||||
func (file *defaultFile) Write(b []byte) (n int, err error) {
|
||||
return file.file.Write(b)
|
||||
}
|
||||
|
||||
// Sync via os.File.Sync
|
||||
func (file *defaultFile) Sync() error {
|
||||
return file.file.Sync()
|
||||
}
|
||||
|
||||
// Close via os.File.Close
|
||||
func (file *defaultFile) Close() error {
|
||||
return file.file.Close()
|
||||
}
|
128
vendor/k8s.io/kubernetes/pkg/util/filesystem/fakefs.go
generated
vendored
128
vendor/k8s.io/kubernetes/pkg/util/filesystem/fakefs.go
generated
vendored
@ -1,128 +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 filesystem
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// fakeFs is implemented in terms of afero
|
||||
type fakeFs struct {
|
||||
a afero.Afero
|
||||
}
|
||||
|
||||
// NewFakeFs returns a fake Filesystem that exists in-memory, useful for unit tests
|
||||
func NewFakeFs() Filesystem {
|
||||
return &fakeFs{a: afero.Afero{Fs: afero.NewMemMapFs()}}
|
||||
}
|
||||
|
||||
// Stat via afero.Fs.Stat
|
||||
func (fs *fakeFs) Stat(name string) (os.FileInfo, error) {
|
||||
return fs.a.Fs.Stat(name)
|
||||
}
|
||||
|
||||
// Create via afero.Fs.Create
|
||||
func (fs *fakeFs) Create(name string) (File, error) {
|
||||
file, err := fs.a.Fs.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fakeFile{file}, nil
|
||||
}
|
||||
|
||||
// Rename via afero.Fs.Rename
|
||||
func (fs *fakeFs) Rename(oldpath, newpath string) error {
|
||||
return fs.a.Fs.Rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
// MkdirAll via afero.Fs.MkdirAll
|
||||
func (fs *fakeFs) MkdirAll(path string, perm os.FileMode) error {
|
||||
return fs.a.Fs.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// Chtimes via afero.Fs.Chtimes
|
||||
func (fs *fakeFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return fs.a.Fs.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
// ReadFile via afero.ReadFile
|
||||
func (fs *fakeFs) ReadFile(filename string) ([]byte, error) {
|
||||
return fs.a.ReadFile(filename)
|
||||
}
|
||||
|
||||
// TempDir via afero.TempDir
|
||||
func (fs *fakeFs) TempDir(dir, prefix string) (string, error) {
|
||||
return fs.a.TempDir(dir, prefix)
|
||||
}
|
||||
|
||||
// TempFile via afero.TempFile
|
||||
func (fs *fakeFs) TempFile(dir, prefix string) (File, error) {
|
||||
file, err := fs.a.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fakeFile{file}, nil
|
||||
}
|
||||
|
||||
// ReadDir via afero.ReadDir
|
||||
func (fs *fakeFs) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
return fs.a.ReadDir(dirname)
|
||||
}
|
||||
|
||||
// Walk via afero.Walk
|
||||
func (fs *fakeFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return fs.a.Walk(root, walkFn)
|
||||
}
|
||||
|
||||
// RemoveAll via afero.RemoveAll
|
||||
func (fs *fakeFs) RemoveAll(path string) error {
|
||||
return fs.a.RemoveAll(path)
|
||||
}
|
||||
|
||||
// Remove via afero.RemoveAll
|
||||
func (fs *fakeFs) Remove(name string) error {
|
||||
return fs.a.Remove(name)
|
||||
}
|
||||
|
||||
// fakeFile implements File; for use with fakeFs
|
||||
type fakeFile struct {
|
||||
file afero.File
|
||||
}
|
||||
|
||||
// Name via afero.File.Name
|
||||
func (file *fakeFile) Name() string {
|
||||
return file.file.Name()
|
||||
}
|
||||
|
||||
// Write via afero.File.Write
|
||||
func (file *fakeFile) Write(b []byte) (n int, err error) {
|
||||
return file.file.Write(b)
|
||||
}
|
||||
|
||||
// Sync via afero.File.Sync
|
||||
func (file *fakeFile) Sync() error {
|
||||
return file.file.Sync()
|
||||
}
|
||||
|
||||
// Close via afero.File.Close
|
||||
func (file *fakeFile) Close() error {
|
||||
return file.file.Close()
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/util/filesystem/filesystem.go
generated
vendored
52
vendor/k8s.io/kubernetes/pkg/util/filesystem/filesystem.go
generated
vendored
@ -1,52 +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 filesystem
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Filesystem is an interface that we can use to mock various filesystem operations
|
||||
type Filesystem interface {
|
||||
// from "os"
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
Create(name string) (File, error)
|
||||
Rename(oldpath, newpath string) error
|
||||
MkdirAll(path string, perm os.FileMode) error
|
||||
Chtimes(name string, atime time.Time, mtime time.Time) error
|
||||
RemoveAll(path string) error
|
||||
Remove(name string) error
|
||||
|
||||
// from "io/ioutil"
|
||||
ReadFile(filename string) ([]byte, error)
|
||||
TempDir(dir, prefix string) (string, error)
|
||||
TempFile(dir, prefix string) (File, error)
|
||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
Walk(root string, walkFn filepath.WalkFunc) error
|
||||
}
|
||||
|
||||
// File is an interface that we can use to mock various filesystem operations typically
|
||||
// accessed through the File object from the "os" package
|
||||
type File interface {
|
||||
// for now, the only os.File methods used are those below, add more as necessary
|
||||
Name() string
|
||||
Write(b []byte) (n int, err error)
|
||||
Sync() error
|
||||
Close() error
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/util/filesystem/watcher.go
generated
vendored
89
vendor/k8s.io/kubernetes/pkg/util/filesystem/watcher.go
generated
vendored
@ -1,89 +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 filesystem
|
||||
|
||||
import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
// FSWatcher is a callback-based filesystem watcher abstraction for fsnotify.
|
||||
type FSWatcher interface {
|
||||
// Initializes the watcher with the given watch handlers.
|
||||
// Called before all other methods.
|
||||
Init(FSEventHandler, FSErrorHandler) error
|
||||
|
||||
// Starts listening for events and errors.
|
||||
// When an event or error occurs, the corresponding handler is called.
|
||||
Run()
|
||||
|
||||
// Add a filesystem path to watch
|
||||
AddWatch(path string) error
|
||||
}
|
||||
|
||||
// FSEventHandler is called when a fsnotify event occurs.
|
||||
type FSEventHandler func(event fsnotify.Event)
|
||||
|
||||
// FSErrorHandler is called when a fsnotify error occurs.
|
||||
type FSErrorHandler func(err error)
|
||||
|
||||
type fsnotifyWatcher struct {
|
||||
watcher *fsnotify.Watcher
|
||||
eventHandler FSEventHandler
|
||||
errorHandler FSErrorHandler
|
||||
}
|
||||
|
||||
var _ FSWatcher = &fsnotifyWatcher{}
|
||||
|
||||
// NewFsnotifyWatcher returns an implementation of FSWatcher that continuously listens for
|
||||
// fsnotify events and calls the event handler as soon as an event is received.
|
||||
func NewFsnotifyWatcher() FSWatcher {
|
||||
return &fsnotifyWatcher{}
|
||||
}
|
||||
|
||||
func (w *fsnotifyWatcher) AddWatch(path string) error {
|
||||
return w.watcher.Add(path)
|
||||
}
|
||||
|
||||
func (w *fsnotifyWatcher) Init(eventHandler FSEventHandler, errorHandler FSErrorHandler) error {
|
||||
var err error
|
||||
w.watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.eventHandler = eventHandler
|
||||
w.errorHandler = errorHandler
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *fsnotifyWatcher) Run() {
|
||||
go func() {
|
||||
defer w.watcher.Close()
|
||||
for {
|
||||
select {
|
||||
case event := <-w.watcher.Events:
|
||||
if w.eventHandler != nil {
|
||||
w.eventHandler(event)
|
||||
}
|
||||
case err := <-w.watcher.Errors:
|
||||
if w.errorHandler != nil {
|
||||
w.errorHandler(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
76
vendor/k8s.io/kubernetes/pkg/util/metrics/util.go
generated
vendored
76
vendor/k8s.io/kubernetes/pkg/util/metrics/util.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
metricsLock sync.Mutex
|
||||
rateLimiterMetrics = make(map[string]*rateLimiterMetric)
|
||||
)
|
||||
|
||||
type rateLimiterMetric struct {
|
||||
metric prometheus.Gauge
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func registerRateLimiterMetric(ownerName string) error {
|
||||
metricsLock.Lock()
|
||||
defer metricsLock.Unlock()
|
||||
|
||||
if _, ok := rateLimiterMetrics[ownerName]; ok {
|
||||
// only register once in Prometheus. We happen to see an ownerName reused in parallel integration tests.
|
||||
return nil
|
||||
}
|
||||
metric := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "rate_limiter_use",
|
||||
Subsystem: ownerName,
|
||||
Help: fmt.Sprintf("A metric measuring the saturation of the rate limiter for %v", ownerName),
|
||||
})
|
||||
if err := prometheus.Register(metric); err != nil {
|
||||
return fmt.Errorf("error registering rate limiter usage metric: %v", err)
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
rateLimiterMetrics[ownerName] = &rateLimiterMetric{
|
||||
metric: metric,
|
||||
stopCh: stopCh,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterMetricAndTrackRateLimiterUsage registers a metric ownerName_rate_limiter_use in prometheus to track
|
||||
// how much used rateLimiter is and starts a goroutine that updates this metric every updatePeriod
|
||||
func RegisterMetricAndTrackRateLimiterUsage(ownerName string, rateLimiter flowcontrol.RateLimiter) error {
|
||||
if err := registerRateLimiterMetric(ownerName); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: determine how to track rate limiter saturation
|
||||
// See discussion at https://go-review.googlesource.com/c/time/+/29958#message-4caffc11669cadd90e2da4c05122cfec50ea6a22
|
||||
// go wait.Until(func() {
|
||||
// metricsLock.Lock()
|
||||
// defer metricsLock.Unlock()
|
||||
// rateLimiterMetrics[ownerName].metric.Set()
|
||||
// }, updatePeriod, rateLimiterMetrics[ownerName].stopCh)
|
||||
return nil
|
||||
}
|
18
vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
generated
vendored
18
vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
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"
|
50
vendor/k8s.io/kubernetes/pkg/util/mount/exec.go
generated
vendored
50
vendor/k8s.io/kubernetes/pkg/util/mount/exec.go
generated
vendored
@ -1,50 +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 mount
|
||||
|
||||
import "k8s.io/utils/exec"
|
||||
|
||||
func NewOsExec() Exec {
|
||||
return &osExec{}
|
||||
}
|
||||
|
||||
// Real implementation of Exec interface that uses simple util.Exec
|
||||
type osExec struct{}
|
||||
|
||||
var _ Exec = &osExec{}
|
||||
|
||||
func (e *osExec) Run(cmd string, args ...string) ([]byte, error) {
|
||||
exe := exec.New()
|
||||
return exe.Command(cmd, args...).CombinedOutput()
|
||||
}
|
||||
|
||||
func NewFakeExec(run runHook) *FakeExec {
|
||||
return &FakeExec{runHook: run}
|
||||
}
|
||||
|
||||
// Fake for testing.
|
||||
type FakeExec struct {
|
||||
runHook runHook
|
||||
}
|
||||
type runHook func(cmd string, args ...string) ([]byte, error)
|
||||
|
||||
func (f *FakeExec) Run(cmd string, args ...string) ([]byte, error) {
|
||||
if f.runHook != nil {
|
||||
return f.runHook(cmd, args...)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
238
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
238
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
@ -1,238 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// FakeMounter implements mount.Interface for tests.
|
||||
type FakeMounter struct {
|
||||
MountPoints []MountPoint
|
||||
Log []FakeAction
|
||||
Filesystem map[string]FileType
|
||||
// Error to return for a path when calling IsLikelyNotMountPoint
|
||||
MountCheckErrors map[string]error
|
||||
// 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()
|
||||
|
||||
opts := []string{}
|
||||
|
||||
for _, option := range options {
|
||||
// find 'bind' option
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
// reuse MountPoint.Opts field to mark mount as readonly
|
||||
opts = append(opts, option)
|
||||
}
|
||||
|
||||
// If target is a symlink, get its absolute path
|
||||
absTarget, err := filepath.EvalSymlinks(target)
|
||||
if err != nil {
|
||||
absTarget = target
|
||||
}
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: opts})
|
||||
klog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
|
||||
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) Unmount(target string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
// If target is a symlink, get its absolute path
|
||||
absTarget, err := filepath.EvalSymlinks(target)
|
||||
if err != nil {
|
||||
absTarget = target
|
||||
}
|
||||
|
||||
newMountpoints := []MountPoint{}
|
||||
for _, mp := range f.MountPoints {
|
||||
if mp.Path == absTarget {
|
||||
klog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
|
||||
// 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: absTarget})
|
||||
delete(f.MountCheckErrors, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) List() ([]MountPoint, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
return f.MountPoints, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
return mp.Path == dir
|
||||
}
|
||||
|
||||
func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
err := f.MountCheckErrors[file]
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = os.Stat(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// If file is a symlink, get its absolute path
|
||||
absFile, err := filepath.EvalSymlinks(file)
|
||||
if err != nil {
|
||||
absFile = file
|
||||
}
|
||||
|
||||
for _, mp := range f.MountPoints {
|
||||
if mp.Path == absFile {
|
||||
klog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
klog.V(5).Infof("isLikelyNotMountPoint 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, pluginMountDir string) (string, error) {
|
||||
return getDeviceNameFromMount(f, mountPath, pluginMountDir)
|
||||
}
|
||||
|
||||
func (f *FakeMounter) MakeRShared(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetFileType(pathname string) (FileType, error) {
|
||||
if t, ok := f.Filesystem[pathname]; ok {
|
||||
return t, nil
|
||||
}
|
||||
return FileType("Directory"), 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, error) {
|
||||
if _, ok := f.Filesystem[pathname]; ok {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) EvalHostSymlinks(pathname string) (string, error) {
|
||||
return pathname, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
// Ignore error in FakeMounter, because we actually didn't create files.
|
||||
realpath = pathname
|
||||
}
|
||||
return getMountRefsByDev(f, realpath)
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("GetFSGroup not implemented")
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("GetSELinuxSupport not implemented")
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
376
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
376
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
@ -1,376 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FileType string
|
||||
|
||||
const (
|
||||
// Default mount command if mounter path is not specified
|
||||
defaultMountCommand = "mount"
|
||||
FileTypeDirectory FileType = "Directory"
|
||||
FileTypeFile FileType = "File"
|
||||
FileTypeSocket FileType = "Socket"
|
||||
FileTypeCharDev FileType = "CharDevice"
|
||||
FileTypeBlockDev FileType = "BlockDevice"
|
||||
)
|
||||
|
||||
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)
|
||||
// IsMountPointMatch determines if the mountpoint matches the dir
|
||||
IsMountPointMatch(mp MountPoint, dir string) bool
|
||||
// IsLikelyNotMountPoint uses heuristics to determine if a directory
|
||||
// is a mountpoint.
|
||||
// It should return ErrNotExist when the directory does not exist.
|
||||
// IsLikelyNotMountPoint does NOT properly detect all mountpoint types
|
||||
// most notably linux bind mounts and symbolic link.
|
||||
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 within its plugin directory
|
||||
GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error)
|
||||
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||
// propagation. If not, it bind-mounts the path as rshared.
|
||||
MakeRShared(path string) error
|
||||
// GetFileType checks for file/directory/socket/block/character devices.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container
|
||||
GetFileType(pathname string) (FileType, error)
|
||||
// MakeFile creates an empty file.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container
|
||||
MakeFile(pathname string) error
|
||||
// MakeDir creates a new directory.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container
|
||||
MakeDir(pathname string) error
|
||||
// Will operate in the host mount namespace if kubelet is running in a container.
|
||||
// Error is returned on any other error than "file not found".
|
||||
ExistsPath(pathname string) (bool, error)
|
||||
// EvalHostSymlinks returns the path name after evaluating symlinks.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container.
|
||||
EvalHostSymlinks(pathname string) (string, error)
|
||||
// GetMountRefs finds all mount references to the path, returns a
|
||||
// list of paths. Path could be a mountpoint path, device or a normal
|
||||
// directory (for bind mount).
|
||||
GetMountRefs(pathname string) ([]string, error)
|
||||
// GetFSGroup returns FSGroup of the path.
|
||||
GetFSGroup(pathname string) (int64, error)
|
||||
// GetSELinuxSupport returns true if given path is on a mount that supports
|
||||
// SELinux.
|
||||
GetSELinuxSupport(pathname string) (bool, error)
|
||||
// GetMode returns permissions of the path.
|
||||
GetMode(pathname string) (os.FileMode, error)
|
||||
}
|
||||
|
||||
type Subpath struct {
|
||||
// index of the VolumeMount for this container
|
||||
VolumeMountIndex int
|
||||
// Full path to the subpath directory on the host
|
||||
Path string
|
||||
// name of the volume that is a valid directory name.
|
||||
VolumeName string
|
||||
// Full path to the volume path
|
||||
VolumePath string
|
||||
// Path to the pod's directory, including pod UID
|
||||
PodDir string
|
||||
// Name of the container
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
// Exec executes command where mount utilities are. This can be either the host,
|
||||
// container where kubelet runs or even a remote pod with mount utilities.
|
||||
// Usual k8s.io/utils/exec interface is not used because kubelet.RunInContainer does
|
||||
// not provide stdin/stdout/stderr streams.
|
||||
type Exec interface {
|
||||
// Run executes a command and returns its stdout + stderr combined in one
|
||||
// stream.
|
||||
Run(cmd string, args ...string) ([]byte, 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
|
||||
Exec
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return mounter.formatAndMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// getMountRefsByDev finds all references to the device provided
|
||||
// by mountPath; returns a list of paths.
|
||||
// Note that mountPath should be path after the evaluation of any symblolic links.
|
||||
func getMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
|
||||
mps, err := mounter.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Finding the device mounted to mountPath
|
||||
diskDev := ""
|
||||
for i := range mps {
|
||||
if mountPath == mps[i].Path {
|
||||
diskDev = mps[i].Device
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find all references to the device.
|
||||
var refs []string
|
||||
for i := range mps {
|
||||
if mps[i].Device == diskDev || mps[i].Device == mountPath {
|
||||
if mps[i].Path != mountPath {
|
||||
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
|
||||
}
|
||||
|
||||
// IsNotMountPoint determines if a directory is a mountpoint.
|
||||
// It should return ErrNotExist when the directory does not exist.
|
||||
// IsNotMountPoint is more expensive than IsLikelyNotMountPoint.
|
||||
// IsNotMountPoint detects bind mounts in linux.
|
||||
// IsNotMountPoint enumerates all the mountpoints using List() and
|
||||
// the list of mountpoints may be large, then it uses
|
||||
// IsMountPointMatch to evaluate whether the directory is a mountpoint
|
||||
func IsNotMountPoint(mounter Interface, file string) (bool, error) {
|
||||
// IsLikelyNotMountPoint provides a quick check
|
||||
// to determine whether file IS A mountpoint
|
||||
notMnt, notMntErr := mounter.IsLikelyNotMountPoint(file)
|
||||
if notMntErr != nil && os.IsPermission(notMntErr) {
|
||||
// We were not allowed to do the simple stat() check, e.g. on NFS with
|
||||
// root_squash. Fall back to /proc/mounts check below.
|
||||
notMnt = true
|
||||
notMntErr = nil
|
||||
}
|
||||
if notMntErr != nil {
|
||||
return notMnt, notMntErr
|
||||
}
|
||||
// identified as mountpoint, so return this fact
|
||||
if notMnt == false {
|
||||
return notMnt, nil
|
||||
}
|
||||
|
||||
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts
|
||||
resolvedFile, err := mounter.EvalHostSymlinks(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// check all mountpoints since IsLikelyNotMountPoint
|
||||
// is not reliable for some mountpoint types
|
||||
mountPoints, mountPointsErr := mounter.List()
|
||||
if mountPointsErr != nil {
|
||||
return notMnt, mountPointsErr
|
||||
}
|
||||
for _, mp := range mountPoints {
|
||||
if mounter.IsMountPointMatch(mp, resolvedFile) {
|
||||
notMnt = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return notMnt, nil
|
||||
}
|
||||
|
||||
// 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, []string) {
|
||||
// Because we have an FD opened on the subpath bind mount, the "bind" option
|
||||
// needs to be included, otherwise the mount target will error as busy if you
|
||||
// remount as readonly.
|
||||
//
|
||||
// As a consequence, all read only bind mounts will no longer change the underlying
|
||||
// volume mount to be read only.
|
||||
bindRemountOpts := []string{"bind", "remount"}
|
||||
bind := false
|
||||
bindOpts := []string{"bind"}
|
||||
|
||||
// _netdev is a userspace mount option and does not automatically get added when
|
||||
// bind mount is created and hence we must carry it over.
|
||||
if checkForNetDev(options) {
|
||||
bindOpts = append(bindOpts, "_netdev")
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
switch option {
|
||||
case "bind":
|
||||
bind = true
|
||||
break
|
||||
case "remount":
|
||||
break
|
||||
default:
|
||||
bindRemountOpts = append(bindRemountOpts, option)
|
||||
}
|
||||
}
|
||||
|
||||
return bind, bindOpts, bindRemountOpts
|
||||
}
|
||||
|
||||
func checkForNetDev(options []string) bool {
|
||||
for _, option := range options {
|
||||
if option == "_netdev" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: this is a workaround for the unmount device issue caused by gci mounter.
|
||||
// In GCI cluster, if gci mounter is used for mounting, the container started by mounter
|
||||
// script will cause additional mounts created in the container. Since these mounts are
|
||||
// irrelevant to the original mounts, they should be not considered when checking the
|
||||
// mount references. Current solution is to filter out those mount paths that contain
|
||||
// the string of original mount path.
|
||||
// Plan to work on better approach to solve this issue.
|
||||
|
||||
func HasMountRefs(mountPath string, mountRefs []string) bool {
|
||||
count := 0
|
||||
for _, ref := range mountRefs {
|
||||
if !strings.Contains(ref, mountPath) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// PathWithinBase checks if give path is within given base directory.
|
||||
func PathWithinBase(fullPath, basePath string) bool {
|
||||
rel, err := filepath.Rel(basePath, fullPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if StartsWithBackstep(rel) {
|
||||
// Needed to escape the base path
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// StartsWithBackstep checks if the given path starts with a backstep segment
|
||||
func StartsWithBackstep(rel string) bool {
|
||||
// normalize to / and check for ../
|
||||
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
|
||||
}
|
||||
|
||||
// getFileType checks for file/directory/socket and block/character devices
|
||||
func 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
|
||||
}
|
||||
|
||||
// checks whether the mode is the target mode
|
||||
isSpecificMode := func(mode, targetMode os.FileMode) bool {
|
||||
return mode&targetMode == targetMode
|
||||
}
|
||||
|
||||
mode := info.Mode()
|
||||
if mode.IsDir() {
|
||||
return FileTypeDirectory, nil
|
||||
} else if mode.IsRegular() {
|
||||
return FileTypeFile, nil
|
||||
} else if isSpecificMode(mode, os.ModeSocket) {
|
||||
return FileTypeSocket, nil
|
||||
} else if isSpecificMode(mode, os.ModeDevice) {
|
||||
if isSpecificMode(mode, os.ModeCharDevice) {
|
||||
return FileTypeCharDev, nil
|
||||
}
|
||||
return FileTypeBlockDev, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
}
|
103
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_common.go
generated
vendored
103
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_common.go
generated
vendored
@ -1,103 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
"os"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// CleanupMountPoint unmounts the given path and
|
||||
// deletes the remaining directory if successful.
|
||||
// if extensiveMountPointCheck is true
|
||||
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
|
||||
// IsNotMountPoint is more expensive but properly handles bind mounts within the same fs.
|
||||
func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool) error {
|
||||
// mounter.ExistsPath cannot be used because for containerized kubelet, we need to check
|
||||
// the path in the kubelet container, not on the host.
|
||||
pathExists, pathErr := PathExists(mountPath)
|
||||
if !pathExists {
|
||||
klog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
|
||||
return nil
|
||||
}
|
||||
corruptedMnt := IsCorruptedMnt(pathErr)
|
||||
if pathErr != nil && !corruptedMnt {
|
||||
return fmt.Errorf("Error checking path: %v", pathErr)
|
||||
}
|
||||
return doCleanupMountPoint(mountPath, mounter, extensiveMountPointCheck, corruptedMnt)
|
||||
}
|
||||
|
||||
// doCleanupMountPoint unmounts the given path and
|
||||
// deletes the remaining directory if successful.
|
||||
// if extensiveMountPointCheck is true
|
||||
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
|
||||
// IsNotMountPoint is more expensive but properly handles bind mounts within the same fs.
|
||||
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, and the mount point check
|
||||
// will be skipped
|
||||
func doCleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
|
||||
if !corruptedMnt {
|
||||
var notMnt bool
|
||||
var err error
|
||||
if extensiveMountPointCheck {
|
||||
notMnt, err = IsNotMountPoint(mounter, mountPath)
|
||||
} else {
|
||||
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if notMnt {
|
||||
klog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
|
||||
return os.Remove(mountPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Unmount the mount path
|
||||
klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath)
|
||||
if err := mounter.Unmount(mountPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath)
|
||||
if mntErr != nil {
|
||||
return mntErr
|
||||
}
|
||||
if notMnt {
|
||||
klog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
|
||||
return os.Remove(mountPath)
|
||||
}
|
||||
return fmt.Errorf("Failed to unmount path %v", mountPath)
|
||||
}
|
||||
|
||||
// TODO: clean this up to use pkg/util/file/FileExists
|
||||
// PathExists returns true if the specified path exists.
|
||||
func PathExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
} else if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else if IsCorruptedMnt(err) {
|
||||
return true, err
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
44
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_unix.go
generated
vendored
44
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_unix.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 2019 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"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// IsCorruptedMnt return true if err is about corrupted mount point
|
||||
func IsCorruptedMnt(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var underlyingError error
|
||||
switch pe := err.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case *os.PathError:
|
||||
underlyingError = pe.Err
|
||||
case *os.LinkError:
|
||||
underlyingError = pe.Err
|
||||
case *os.SyscallError:
|
||||
underlyingError = pe.Err
|
||||
}
|
||||
|
||||
return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE || underlyingError == syscall.EIO || underlyingError == syscall.EACCES
|
||||
}
|
68
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_windows.go
generated
vendored
68
vendor/k8s.io/kubernetes/pkg/util/mount/mount_helper_windows.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2019 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"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// following failure codes are from https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1300-1699-
|
||||
// ERROR_BAD_NETPATH = 53
|
||||
// ERROR_NETWORK_BUSY = 54
|
||||
// ERROR_UNEXP_NET_ERR = 59
|
||||
// ERROR_NETNAME_DELETED = 64
|
||||
// ERROR_NETWORK_ACCESS_DENIED = 65
|
||||
// ERROR_BAD_DEV_TYPE = 66
|
||||
// ERROR_BAD_NET_NAME = 67
|
||||
// ERROR_SESSION_CREDENTIAL_CONFLICT = 1219
|
||||
// ERROR_LOGON_FAILURE = 1326
|
||||
var errorNoList = [...]int{53, 54, 59, 64, 65, 66, 67, 1219, 1326}
|
||||
|
||||
// IsCorruptedMnt return true if err is about corrupted mount point
|
||||
func IsCorruptedMnt(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var underlyingError error
|
||||
switch pe := err.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case *os.PathError:
|
||||
underlyingError = pe.Err
|
||||
case *os.LinkError:
|
||||
underlyingError = pe.Err
|
||||
case *os.SyscallError:
|
||||
underlyingError = pe.Err
|
||||
}
|
||||
|
||||
if ee, ok := underlyingError.(syscall.Errno); ok {
|
||||
for _, errno := range errorNoList {
|
||||
if int(ee) == errno {
|
||||
klog.Warningf("IsCorruptedMnt failed with error: %v, error code: %v", err, errno)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
827
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
827
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
@ -1,827 +0,0 @@
|
||||
// +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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"k8s.io/klog"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
utilio "k8s.io/utils/io"
|
||||
utilpath "k8s.io/utils/path"
|
||||
)
|
||||
|
||||
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
|
||||
// At least number of fields per line in /proc/<pid>/mountinfo.
|
||||
expectedAtLeastNumFieldsPerMountInfo = 10
|
||||
// Location of the mount file to use
|
||||
procMountsPath = "/proc/mounts"
|
||||
// Location of the mountinfo file
|
||||
procMountInfoPath = "/proc/self/mountinfo"
|
||||
// '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
|
||||
withSystemd bool
|
||||
}
|
||||
|
||||
// New returns a mount.Interface for the current system.
|
||||
// It provides options to override the default mounter behavior.
|
||||
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||
func New(mounterPath string) Interface {
|
||||
return &Mounter{
|
||||
mounterPath: mounterPath,
|
||||
withSystemd: detectSystemd(),
|
||||
}
|
||||
}
|
||||
|
||||
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
|
||||
// be an empty string in case it's not required, e.g. for remount, or for auto filesystem
|
||||
// type, where kernel handles fstype 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 if containerized mounter is needed. Otherwise, it is set to empty.
|
||||
// All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
|
||||
mounterPath := ""
|
||||
bind, bindOpts, bindRemountOpts := IsBind(options)
|
||||
if bind {
|
||||
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
|
||||
}
|
||||
// The list of filesystems that require containerized mounter on GCI image cluster
|
||||
fsTypesNeedMounter := map[string]struct{}{
|
||||
"nfs": {},
|
||||
"glusterfs": {},
|
||||
"ceph": {},
|
||||
"cifs": {},
|
||||
}
|
||||
if _, ok := fsTypesNeedMounter[fstype]; ok {
|
||||
mounterPath = mounter.mounterPath
|
||||
}
|
||||
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options)
|
||||
}
|
||||
|
||||
// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
|
||||
func (m *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error {
|
||||
mountArgs := MakeMountArgs(source, target, fstype, options)
|
||||
if len(mounterPath) > 0 {
|
||||
mountArgs = append([]string{mountCmd}, mountArgs...)
|
||||
mountCmd = mounterPath
|
||||
}
|
||||
|
||||
if m.withSystemd {
|
||||
// Try to run mount via systemd-run --scope. This will escape the
|
||||
// service where kubelet runs and any fuse daemons will be started in a
|
||||
// specific scope. kubelet service than can be restarted without killing
|
||||
// these fuse daemons.
|
||||
//
|
||||
// Complete command line (when mounterPath is not used):
|
||||
// systemd-run --description=... --scope -- mount -t <type> <what> <where>
|
||||
//
|
||||
// Expected flow:
|
||||
// * systemd-run creates a transient scope (=~ cgroup) and executes its
|
||||
// argument (/bin/mount) there.
|
||||
// * mount does its job, forks a fuse daemon if necessary and finishes.
|
||||
// (systemd-run --scope finishes at this point, returning mount's exit
|
||||
// code and stdout/stderr - thats one of --scope benefits).
|
||||
// * systemd keeps the fuse daemon running in the scope (i.e. in its own
|
||||
// cgroup) until the fuse daemon dies (another --scope benefit).
|
||||
// Kubelet service can be restarted and the fuse daemon survives.
|
||||
// * When the fuse daemon dies (e.g. during unmount) systemd removes the
|
||||
// scope automatically.
|
||||
//
|
||||
// systemd-mount is not used because it's too new for older distros
|
||||
// (CentOS 7, Debian Jessie).
|
||||
mountCmd, mountArgs = AddSystemdScope("systemd-run", target, mountCmd, mountArgs)
|
||||
} else {
|
||||
// No systemd-run on the host (or we failed to check it), assume kubelet
|
||||
// does not run as a systemd service.
|
||||
// No code here, mountCmd and mountArgs are already populated.
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
|
||||
command := exec.Command(mountCmd, mountArgs...)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
args := strings.Join(mountArgs, " ")
|
||||
klog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s\n", err, mountCmd, args, string(output))
|
||||
return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s\n",
|
||||
err, mountCmd, args, string(output))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// detectSystemd returns true if OS runs with systemd as init. When not sure
|
||||
// (permission errors, ...), it returns false.
|
||||
// There may be different ways how to detect systemd, this one makes sure that
|
||||
// systemd-runs (needed by Mount()) works.
|
||||
func detectSystemd() bool {
|
||||
if _, err := exec.LookPath("systemd-run"); err != nil {
|
||||
klog.V(2).Infof("Detected OS without systemd")
|
||||
return false
|
||||
}
|
||||
// Try to run systemd-run --scope /bin/true, that should be enough
|
||||
// to make sure that systemd is really running and not just installed,
|
||||
// which happens when running in a container with a systemd-based image
|
||||
// but with different pid 1.
|
||||
cmd := exec.Command("systemd-run", "--description=Kubernetes systemd probe", "--scope", "true")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Cannot run systemd-run, assuming non-systemd OS")
|
||||
klog.V(4).Infof("systemd-run failed with: %v", err)
|
||||
klog.V(4).Infof("systemd-run output: %s", string(output))
|
||||
return false
|
||||
}
|
||||
klog.V(2).Infof("Detected OS with systemd")
|
||||
return true
|
||||
}
|
||||
|
||||
// MakeMountArgs makes the arguments to the mount(8) command.
|
||||
// Implementation is shared with NsEnterMounter
|
||||
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
|
||||
}
|
||||
|
||||
// AddSystemdScope adds "system-run --scope" to given command line
|
||||
// implementation is shared with NsEnterMounter
|
||||
func AddSystemdScope(systemdRunPath, mountName, command string, args []string) (string, []string) {
|
||||
descriptionArg := fmt.Sprintf("--description=Kubernetes transient mount for %s", mountName)
|
||||
systemdRunArgs := []string{descriptionArg, "--scope", "--", command}
|
||||
return systemdRunPath, append(systemdRunArgs, args...)
|
||||
}
|
||||
|
||||
// Unmount unmounts the target.
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
klog.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)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
|
||||
return ((mp.Path == dir) || (mp.Path == deletedDir))
|
||||
}
|
||||
|
||||
// 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.
|
||||
// It also can not distinguish between mountpoints and symbolic links.
|
||||
// mkdir /tmp/a /tmp/b; mount --bind /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) {
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
rootStat, err := os.Stat(filepath.Dir(strings.TrimSuffix(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) {
|
||||
pathType, err := mounter.GetFileType(pathname)
|
||||
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
|
||||
return isDevice, err
|
||||
}
|
||||
|
||||
// ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
|
||||
func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
|
||||
var isDevice bool
|
||||
finfo, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
isDevice = false
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(
|
||||
"PathIsDevice failed for path %q: %v",
|
||||
pathname,
|
||||
err)
|
||||
}
|
||||
// path refers to a device
|
||||
if finfo.Mode()&os.ModeDevice != 0 {
|
||||
isDevice = true
|
||||
}
|
||||
|
||||
if !isDevice {
|
||||
klog.Errorf("Path %q is not referring to a device.", pathname)
|
||||
return false, nil
|
||||
}
|
||||
fd, errno := unix.Open(pathname, unix.O_RDONLY|unix.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 unix.Close(fd)
|
||||
if errno == nil {
|
||||
// device not in use
|
||||
return false, nil
|
||||
} else if errno == unix.EBUSY {
|
||||
// device is in use
|
||||
return true, nil
|
||||
}
|
||||
// error during call to Open
|
||||
return false, errno
|
||||
}
|
||||
|
||||
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
|
||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
|
||||
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
|
||||
}
|
||||
|
||||
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
|
||||
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
|
||||
}
|
||||
|
||||
// GetDeviceNameFromMountLinux find the device name from /proc/mounts in which
|
||||
// the mount path reference should match the given plugin mount directory. In case no mount path reference
|
||||
// matches, returns the volume name taken from its given mountPath
|
||||
// This implementation is shared with NsEnterMounter
|
||||
func GetDeviceNameFromMountLinux(mounter Interface, mountPath, pluginMountDir string) (string, error) {
|
||||
refs, err := mounter.GetMountRefs(mountPath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
klog.V(4).Infof("Directory %s is not mounted", mountPath)
|
||||
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||
}
|
||||
for _, ref := range refs {
|
||||
if strings.HasPrefix(ref, pluginMountDir) {
|
||||
volumeID, err := filepath.Rel(pluginMountDir, ref)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
return volumeID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return path.Base(mountPath), nil
|
||||
}
|
||||
|
||||
// ListProcMounts is shared with NsEnterMounter
|
||||
func ListProcMounts(mountFilePath string) ([]MountPoint, error) {
|
||||
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseProcMounts(content)
|
||||
}
|
||||
|
||||
func parseProcMounts(content []byte) ([]MountPoint, error) {
|
||||
out := []MountPoint{}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for _, line := range lines {
|
||||
if line == "" {
|
||||
// the last split() item is empty string following the last \n
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != expectedNumFieldsPerLine {
|
||||
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
|
||||
}
|
||||
|
||||
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 nil, err
|
||||
}
|
||||
mp.Freq = freq
|
||||
|
||||
pass, err := strconv.Atoi(fields[5])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp.Pass = pass
|
||||
|
||||
out = append(out, mp)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeRShared(path string) error {
|
||||
return DoMakeRShared(path, procMountInfoPath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
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, error) {
|
||||
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
|
||||
return filepath.EvalSymlinks(pathname)
|
||||
}
|
||||
|
||||
// formatAndMount uses unix utils to format and mount the given disk
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
readOnly := false
|
||||
for _, option := range options {
|
||||
if option == "ro" {
|
||||
readOnly = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
options = append(options, "defaults")
|
||||
|
||||
if !readOnly {
|
||||
// Run fsck on the disk to fix repairable issues, only do this for volumes requested as rw.
|
||||
klog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
|
||||
args := []string{"-a", source}
|
||||
out, err := mounter.Exec.Run("fsck", args...)
|
||||
if err != nil {
|
||||
ee, isExitError := err.(utilexec.ExitError)
|
||||
switch {
|
||||
case err == utilexec.ErrExecutableNotFound:
|
||||
klog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
|
||||
case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
|
||||
klog.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:
|
||||
klog.Infof("`fsck` error %s", string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to mount the disk
|
||||
klog.V(4).Infof("Attempting to mount disk: %s %s %s", fstype, source, target)
|
||||
mountErr := mounter.Interface.Mount(source, target, fstype, options)
|
||||
if mountErr != nil {
|
||||
// Mount failed. This indicates either that the disk is unformatted or
|
||||
// it contains an unexpected filesystem.
|
||||
existingFormat, err := mounter.GetDiskFormat(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingFormat == "" {
|
||||
if readOnly {
|
||||
// Don't attempt to format if mounting as readonly, return an error to reflect this.
|
||||
return errors.New("failed to mount unformatted volume as read only")
|
||||
}
|
||||
|
||||
// Disk is unformatted so format it.
|
||||
args := []string{source}
|
||||
// Use 'ext4' as the default
|
||||
if len(fstype) == 0 {
|
||||
fstype = "ext4"
|
||||
}
|
||||
|
||||
if fstype == "ext4" || fstype == "ext3" {
|
||||
args = []string{
|
||||
"-F", // Force flag
|
||||
"-m0", // Zero blocks reserved for super-user
|
||||
source,
|
||||
}
|
||||
}
|
||||
klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
|
||||
_, err := mounter.Exec.Run("mkfs."+fstype, args...)
|
||||
if err == nil {
|
||||
// the disk has been formatted successfully try to mount it again.
|
||||
klog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
klog.Errorf("format of disk %q failed: type:(%q) target:(%q) options:(%q)error:(%v)", source, fstype, target, options, err)
|
||||
return err
|
||||
} else {
|
||||
// Disk is already formatted and failed to mount
|
||||
if len(fstype) == 0 || fstype == existingFormat {
|
||||
// This is mount error
|
||||
return mountErr
|
||||
} else {
|
||||
// Block device is formatted with unexpected filesystem, let the user know
|
||||
return fmt.Errorf("failed to mount the volume as %q, it already contains %s. Mount error: %v", fstype, existingFormat, mountErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mountErr
|
||||
}
|
||||
|
||||
// GetDiskFormat uses 'blkid' to see if the given disk is unformatted
|
||||
func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
|
||||
args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk}
|
||||
klog.V(4).Infof("Attempting to determine if disk %q is formatted using blkid with args: (%v)", disk, args)
|
||||
dataOut, err := mounter.Exec.Run("blkid", args...)
|
||||
output := string(dataOut)
|
||||
klog.V(4).Infof("Output: %q, err: %v", output, err)
|
||||
|
||||
if err != nil {
|
||||
if exit, ok := err.(utilexec.ExitError); ok {
|
||||
if exit.ExitStatus() == 2 {
|
||||
// Disk device is unformatted.
|
||||
// For `blkid`, if the specified token (TYPE/PTTYPE, etc) was
|
||||
// not found, or no (specified) devices could be identified, an
|
||||
// exit code of 2 is returned.
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
klog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
var fstype, pttype string
|
||||
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, l := range lines {
|
||||
if len(l) <= 0 {
|
||||
// Ignore empty line.
|
||||
continue
|
||||
}
|
||||
cs := strings.Split(l, "=")
|
||||
if len(cs) != 2 {
|
||||
return "", fmt.Errorf("blkid returns invalid output: %s", output)
|
||||
}
|
||||
// TYPE is filesystem type, and PTTYPE is partition table type, according
|
||||
// to https://www.kernel.org/pub/linux/utils/util-linux/v2.21/libblkid-docs/.
|
||||
if cs[0] == "TYPE" {
|
||||
fstype = cs[1]
|
||||
} else if cs[0] == "PTTYPE" {
|
||||
pttype = cs[1]
|
||||
}
|
||||
}
|
||||
|
||||
if len(pttype) > 0 {
|
||||
klog.V(4).Infof("Disk %s detected partition table type: %s", disk, pttype)
|
||||
// Returns a special non-empty string as filesystem type, then kubelet
|
||||
// will not format it.
|
||||
return "unknown data, probably partitions", nil
|
||||
}
|
||||
|
||||
return fstype, nil
|
||||
}
|
||||
|
||||
// isShared returns true, if given path is on a mount point that has shared
|
||||
// mount propagation.
|
||||
func isShared(mount string, mountInfoPath string) (bool, error) {
|
||||
info, err := findMountInfo(mount, mountInfoPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// parse optional parameters
|
||||
for _, opt := range info.optionalFields {
|
||||
if strings.HasPrefix(opt, "shared:") {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// This represents a single line in /proc/<pid>/mountinfo.
|
||||
type mountInfo struct {
|
||||
// Unique ID for the mount (maybe reused after umount).
|
||||
id int
|
||||
// The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
|
||||
parentID int
|
||||
// The value of `st_dev` for files on this filesystem.
|
||||
majorMinor string
|
||||
// The pathname of the directory in the filesystem which forms the root of this mount.
|
||||
root string
|
||||
// Mount source, filesystem-specific information. e.g. device, tmpfs name.
|
||||
source string
|
||||
// Mount point, the pathname of the mount point.
|
||||
mountPoint string
|
||||
// Optional fieds, zero or more fields of the form "tag[:value]".
|
||||
optionalFields []string
|
||||
// The filesystem type in the form "type[.subtype]".
|
||||
fsType string
|
||||
// Per-mount options.
|
||||
mountOptions []string
|
||||
// Per-superblock options.
|
||||
superOptions []string
|
||||
}
|
||||
|
||||
// parseMountInfo parses /proc/xxx/mountinfo.
|
||||
func parseMountInfo(filename string) ([]mountInfo, error) {
|
||||
content, err := utilio.ConsistentRead(filename, maxListTries)
|
||||
if err != nil {
|
||||
return []mountInfo{}, err
|
||||
}
|
||||
contentStr := string(content)
|
||||
infos := []mountInfo{}
|
||||
|
||||
for _, line := range strings.Split(contentStr, "\n") {
|
||||
if line == "" {
|
||||
// the last split() item is empty string following the last \n
|
||||
continue
|
||||
}
|
||||
// See `man proc` for authoritative description of format of the file.
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < expectedAtLeastNumFieldsPerMountInfo {
|
||||
return nil, fmt.Errorf("wrong number of fields in (expected at least %d, got %d): %s", expectedAtLeastNumFieldsPerMountInfo, len(fields), line)
|
||||
}
|
||||
id, err := strconv.Atoi(fields[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentID, err := strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := mountInfo{
|
||||
id: id,
|
||||
parentID: parentID,
|
||||
majorMinor: fields[2],
|
||||
root: fields[3],
|
||||
mountPoint: fields[4],
|
||||
mountOptions: strings.Split(fields[5], ","),
|
||||
}
|
||||
// All fields until "-" are "optional fields".
|
||||
i := 6
|
||||
for ; i < len(fields) && fields[i] != "-"; i++ {
|
||||
info.optionalFields = append(info.optionalFields, fields[i])
|
||||
}
|
||||
// Parse the rest 3 fields.
|
||||
i += 1
|
||||
if len(fields)-i < 3 {
|
||||
return nil, fmt.Errorf("expect 3 fields in %s, got %d", line, len(fields)-i)
|
||||
}
|
||||
info.fsType = fields[i]
|
||||
info.source = fields[i+1]
|
||||
info.superOptions = strings.Split(fields[i+2], ",")
|
||||
infos = append(infos, info)
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func findMountInfo(path, mountInfoPath string) (mountInfo, error) {
|
||||
infos, err := parseMountInfo(mountInfoPath)
|
||||
if err != nil {
|
||||
return mountInfo{}, err
|
||||
}
|
||||
|
||||
// process /proc/xxx/mountinfo in backward order and find the first mount
|
||||
// point that is prefix of 'path' - that's the mount where path resides
|
||||
var info *mountInfo
|
||||
for i := len(infos) - 1; i >= 0; i-- {
|
||||
if PathWithinBase(path, infos[i].mountPoint) {
|
||||
info = &infos[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if info == nil {
|
||||
return mountInfo{}, fmt.Errorf("cannot find mount point for %q", path)
|
||||
}
|
||||
return *info, nil
|
||||
}
|
||||
|
||||
// DoMakeRShared is common implementation of MakeRShared on Linux. It checks if
|
||||
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
||||
// mountArgs are expected to contain mount-like command, DoMakeRShared will add
|
||||
// '--bind <path> <path>' and '--make-rshared <path>' to mountArgs.
|
||||
func DoMakeRShared(path string, mountInfoFilename string) error {
|
||||
shared, err := isShared(path, mountInfoFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if shared {
|
||||
klog.V(4).Infof("Directory %s is already on a shared mount", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.V(2).Infof("Bind-mounting %q with shared mount propagation", path)
|
||||
// mount --bind /var/lib/kubelet /var/lib/kubelet
|
||||
if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_BIND, "" /*data*/); err != nil {
|
||||
return fmt.Errorf("failed to bind-mount %s: %v", path, err)
|
||||
}
|
||||
|
||||
// mount --make-rshared /var/lib/kubelet
|
||||
if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_SHARED|syscall.MS_REC, "" /*data*/); err != nil {
|
||||
return fmt.Errorf("failed to make %s rshared: %v", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSELinux is common implementation of GetSELinuxSupport on Linux.
|
||||
func GetSELinux(path string, mountInfoFilename string) (bool, error) {
|
||||
info, err := findMountInfo(path, mountInfoFilename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// "seclabel" can be both in mount options and super options.
|
||||
for _, opt := range info.superOptions {
|
||||
if opt == "seclabel" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, opt := range info.mountOptions {
|
||||
if opt == "seclabel" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
pathExists, pathErr := PathExists(pathname)
|
||||
if !pathExists {
|
||||
return []string{}, nil
|
||||
} else if IsCorruptedMnt(pathErr) {
|
||||
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
|
||||
return []string{}, nil
|
||||
} else if pathErr != nil {
|
||||
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
|
||||
}
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return SearchMountPoints(realpath, procMountInfoPath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return GetSELinux(pathname, procMountInfoPath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return GetFSGroupLinux(realpath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return GetModeLinux(pathname)
|
||||
}
|
||||
|
||||
// GetFSGroupLinux is shared between Linux and NsEnterMounter
|
||||
// pathname must already be evaluated for symlinks
|
||||
func GetFSGroupLinux(pathname string) (int64, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(info.Sys().(*syscall.Stat_t).Gid), nil
|
||||
}
|
||||
|
||||
// GetModeLinux is shared between Linux and NsEnterMounter
|
||||
func GetModeLinux(pathname string) (os.FileMode, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Mode(), nil
|
||||
}
|
||||
|
||||
// SearchMountPoints finds all mount references to the source, returns a list of
|
||||
// mountpoints.
|
||||
// This function assumes source cannot be device.
|
||||
// Some filesystems may share a source name, e.g. tmpfs. And for bind mounting,
|
||||
// it's possible to mount a non-root path of a filesystem, so we need to use
|
||||
// root path and major:minor to represent mount source uniquely.
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
|
||||
mis, err := parseMountInfo(mountInfoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mountID := 0
|
||||
rootPath := ""
|
||||
majorMinor := ""
|
||||
|
||||
// Finding the underlying root path and major:minor if possible.
|
||||
// We need search in backward order because it's possible for later mounts
|
||||
// to overlap earlier mounts.
|
||||
for i := len(mis) - 1; i >= 0; i-- {
|
||||
if hostSource == mis[i].mountPoint || PathWithinBase(hostSource, mis[i].mountPoint) {
|
||||
// If it's a mount point or path under a mount point.
|
||||
mountID = mis[i].id
|
||||
rootPath = filepath.Join(mis[i].root, strings.TrimPrefix(hostSource, mis[i].mountPoint))
|
||||
majorMinor = mis[i].majorMinor
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rootPath == "" || majorMinor == "" {
|
||||
return nil, fmt.Errorf("failed to get root path and major:minor for %s", hostSource)
|
||||
}
|
||||
|
||||
var refs []string
|
||||
for i := range mis {
|
||||
if mis[i].id == mountID {
|
||||
// Ignore mount entry for mount source itself.
|
||||
continue
|
||||
}
|
||||
if mis[i].root == rootPath && mis[i].majorMinor == majorMinor {
|
||||
refs = append(refs, mis[i].mountPoint)
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
123
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
123
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
@ -1,123 +0,0 @@
|
||||
// +build !linux,!windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Mounter struct {
|
||||
mounterPath string
|
||||
}
|
||||
|
||||
var unsupportedErr = errors.New("util/mount on this platform is not supported")
|
||||
|
||||
// New returns a mount.Interface for the current system.
|
||||
// It provides options to override the default mounter behavior.
|
||||
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||
func New(mounterPath string) Interface {
|
||||
return &Mounter{
|
||||
mounterPath: mounterPath,
|
||||
}
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||
return []MountPoint{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
return (mp.Path == dir)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
|
||||
return "", unsupportedErr
|
||||
}
|
||||
|
||||
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
|
||||
return "", unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeRShared(path string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
return FileType("fake"), unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeDir(pathname string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeFile(pathname string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
|
||||
return true, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
|
||||
return "", unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
414
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
414
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
@ -1,414 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
"k8s.io/utils/keymutex"
|
||||
|
||||
utilpath "k8s.io/utils/path"
|
||||
)
|
||||
|
||||
// Mounter provides the default implementation of mount.Interface
|
||||
// for the windows platform. This implementation assumes that the
|
||||
// kubelet is running in the host's root mount namespace.
|
||||
type Mounter struct {
|
||||
mounterPath string
|
||||
}
|
||||
|
||||
// New returns a mount.Interface for the current system.
|
||||
// It provides options to override the default mounter behavior.
|
||||
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||
func New(mounterPath string) Interface {
|
||||
return &Mounter{
|
||||
mounterPath: mounterPath,
|
||||
}
|
||||
}
|
||||
|
||||
// acquire lock for smb mount
|
||||
var getSMBMountMutex = keymutex.NewHashed(0)
|
||||
|
||||
// Mount : mounts source to target with given options.
|
||||
// currently only supports cifs(smb), bind mount(for disk)
|
||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
target = normalizeWindowsPath(target)
|
||||
|
||||
if source == "tmpfs" {
|
||||
klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, options)
|
||||
return os.MkdirAll(target, 0755)
|
||||
}
|
||||
|
||||
parentDir := filepath.Dir(target)
|
||||
if err := os.MkdirAll(parentDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
|
||||
options, source, target, fstype)
|
||||
bindSource := source
|
||||
|
||||
// tell it's going to mount azure disk or azure file according to options
|
||||
if bind, _, _ := IsBind(options); bind {
|
||||
// mount azure disk
|
||||
bindSource = normalizeWindowsPath(source)
|
||||
} else {
|
||||
if len(options) < 2 {
|
||||
klog.Warningf("mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
|
||||
options, len(options), source, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
// currently only cifs mount is supported
|
||||
if strings.ToLower(fstype) != "cifs" {
|
||||
return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
|
||||
}
|
||||
|
||||
// lock smb mount for the same source
|
||||
getSMBMountMutex.LockKey(source)
|
||||
defer getSMBMountMutex.UnlockKey(source)
|
||||
|
||||
if output, err := newSMBMapping(options[0], options[1], source); err != nil {
|
||||
if isSMBMappingExist(source) {
|
||||
klog.V(2).Infof("SMB Mapping(%s) already exists, now begin to remove and remount", source)
|
||||
if output, err := removeSMBMapping(source); err != nil {
|
||||
return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
|
||||
}
|
||||
if output, err := newSMBMapping(options[0], options[1], source); err != nil {
|
||||
return fmt.Errorf("New-SmbGlobalMapping remount failed: %v, output: %q", err, output)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("New-SmbGlobalMapping failed: %v, output: %q", err, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
|
||||
klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// do the SMB mount with username, password, remotepath
|
||||
// return (output, error)
|
||||
func newSMBMapping(username, password, remotepath string) (string, error) {
|
||||
if username == "" || password == "" || remotepath == "" {
|
||||
return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, password, remotepath)
|
||||
}
|
||||
|
||||
// use PowerShell Environment Variables to store user input string to prevent command line injection
|
||||
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
|
||||
cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
|
||||
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
|
||||
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
|
||||
cmd := exec.Command("powershell", "/c", cmdLine)
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("smbuser=%s", username),
|
||||
fmt.Sprintf("smbpassword=%s", password),
|
||||
fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
return string(output), err
|
||||
}
|
||||
|
||||
// check whether remotepath is already mounted
|
||||
func isSMBMappingExist(remotepath string) bool {
|
||||
cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||
_, err := cmd.CombinedOutput()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// remove SMB mapping
|
||||
func removeSMBMapping(remotepath string) (string, error) {
|
||||
cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||
output, err := cmd.CombinedOutput()
|
||||
return string(output), err
|
||||
}
|
||||
|
||||
// Unmount unmounts the target.
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
klog.V(4).Infof("azureMount: Unmount target (%q)", target)
|
||||
target = normalizeWindowsPath(target)
|
||||
if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
|
||||
klog.Errorf("rmdir failed: %v, output: %q", err, string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns a list of all mounted filesystems. todo
|
||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||
return []MountPoint{}, nil
|
||||
}
|
||||
|
||||
// IsMountPointMatch determines if the mountpoint matches the dir
|
||||
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
return mp.Path == dir
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
stat, err := os.Lstat(file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
// If current file is a symlink, then it is a mountpoint.
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(file)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("readlink error: %v", err)
|
||||
}
|
||||
exists, err := mounter.ExistsPath(target)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return !exists, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetDeviceNameFromMount given a mnt point, find the device
|
||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
|
||||
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
|
||||
}
|
||||
|
||||
// getDeviceNameFromMount find the device(drive) name in which
|
||||
// the mount path reference should match the given plugin mount directory. In case no mount path reference
|
||||
// matches, returns the volume name taken from its given mountPath
|
||||
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
|
||||
refs, err := mounter.GetMountRefs(mountPath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||
}
|
||||
basemountPath := normalizeWindowsPath(pluginMountDir)
|
||||
for _, ref := range refs {
|
||||
if strings.Contains(ref, basemountPath) {
|
||||
volumeID, err := filepath.Rel(normalizeWindowsPath(basemountPath), ref)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
return volumeID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return path.Base(mountPath), nil
|
||||
}
|
||||
|
||||
// DeviceOpened determines if the device is in use elsewhere
|
||||
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// PathIsDevice determines if a path is a device.
|
||||
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||
// propagation. Empty implementation here.
|
||||
func (mounter *Mounter) MakeRShared(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileType checks for sockets/block/character devices
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
// 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, error) {
|
||||
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
|
||||
}
|
||||
|
||||
// EvalHostSymlinks returns the path name after evaluating symlinks
|
||||
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
|
||||
return filepath.EvalSymlinks(pathname)
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
// Try to mount the disk
|
||||
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
||||
|
||||
if err := ValidateDiskNumber(source); err != nil {
|
||||
klog.Errorf("diskMount: formatAndMount failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fstype) == 0 {
|
||||
// Use 'NTFS' as the default
|
||||
fstype = "NTFS"
|
||||
}
|
||||
|
||||
// format disk if it is unformatted(raw)
|
||||
cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+
|
||||
" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
|
||||
if output, err := mounter.Exec.Run("powershell", "/c", cmd); err != nil {
|
||||
return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
|
||||
}
|
||||
klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)
|
||||
|
||||
driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
driverPath := driveLetter + ":"
|
||||
target = normalizeWindowsPath(target)
|
||||
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
|
||||
if output, err := mounter.Exec.Run("cmd", "/c", "mklink", "/D", target, driverPath); err != nil {
|
||||
klog.Errorf("mklink failed: %v, output: %q", err, string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeWindowsPath(path string) string {
|
||||
normalizedPath := strings.Replace(path, "/", "\\", -1)
|
||||
if strings.HasPrefix(normalizedPath, "\\") {
|
||||
normalizedPath = "c:" + normalizedPath
|
||||
}
|
||||
return normalizedPath
|
||||
}
|
||||
|
||||
// ValidateDiskNumber : disk number should be a number in [0, 99]
|
||||
func ValidateDiskNumber(disk string) error {
|
||||
diskNum, err := strconv.Atoi(disk)
|
||||
if err != nil {
|
||||
return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
|
||||
}
|
||||
|
||||
if diskNum < 0 || diskNum > 99 {
|
||||
return fmt.Errorf("disk number out of range: %q", disk)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get drive letter according to windows disk number
|
||||
func getDriveLetterByDiskNumber(diskNum string, exec Exec) (string, error) {
|
||||
cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
|
||||
output, err := exec.Run("powershell", "/c", cmd)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
|
||||
}
|
||||
if len(string(output)) < 1 {
|
||||
return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
|
||||
}
|
||||
return string(output)[:1], nil
|
||||
}
|
||||
|
||||
// getAllParentLinks walks all symbolic links and return all the parent targets recursively
|
||||
func getAllParentLinks(path string) ([]string, error) {
|
||||
const maxIter = 255
|
||||
links := []string{}
|
||||
for {
|
||||
links = append(links, path)
|
||||
if len(links) > maxIter {
|
||||
return links, fmt.Errorf("unexpected length of parent links: %v", links)
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return links, fmt.Errorf("Lstat: %v", err)
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
path, err = os.Readlink(path)
|
||||
if err != nil {
|
||||
return links, fmt.Errorf("Readlink error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return links, nil
|
||||
}
|
||||
|
||||
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
windowsPath := normalizeWindowsPath(pathname)
|
||||
pathExists, pathErr := PathExists(windowsPath)
|
||||
if !pathExists {
|
||||
return []string{}, nil
|
||||
} else if IsCorruptedMnt(pathErr) {
|
||||
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
|
||||
return []string{}, nil
|
||||
} else if pathErr != nil {
|
||||
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
|
||||
}
|
||||
return []string{pathname}, nil
|
||||
}
|
||||
|
||||
// Note that on windows, it always returns 0. We actually don't set FSGroup on
|
||||
// windows platform, see SetVolumeOwnership implementation.
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
// Windows does not support SELinux.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Mode(), nil
|
||||
}
|
183
vendor/k8s.io/kubernetes/pkg/util/node/node.go
generated
vendored
183
vendor/k8s.io/kubernetes/pkg/util/node/node.go
generated
vendored
@ -26,10 +26,12 @@ import (
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
@ -106,50 +108,93 @@ func GetNodeHostIP(node *v1.Node) (net.IP, error) {
|
||||
}
|
||||
|
||||
// GetNodeIP returns the ip of node with the provided hostname
|
||||
// If required, wait for the node to be defined.
|
||||
func GetNodeIP(client clientset.Interface, hostname string) net.IP {
|
||||
var nodeIP net.IP
|
||||
node, err := client.CoreV1().Nodes().Get(hostname, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.Warningf("Failed to retrieve node info: %v", err)
|
||||
return nil
|
||||
backoff := wait.Backoff{
|
||||
Steps: 6,
|
||||
Duration: 1 * time.Second,
|
||||
Factor: 2.0,
|
||||
Jitter: 0.2,
|
||||
}
|
||||
nodeIP, err = GetNodeHostIP(node)
|
||||
if err != nil {
|
||||
klog.Warningf("Failed to retrieve node IP: %v", err)
|
||||
return nil
|
||||
|
||||
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
node, err := client.CoreV1().Nodes().Get(hostname, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to retrieve node info: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
nodeIP, err = GetNodeHostIP(node)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to retrieve node IP: %v", err)
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err == nil {
|
||||
klog.Infof("Successfully retrieved node IP: %v", nodeIP)
|
||||
}
|
||||
return nodeIP
|
||||
}
|
||||
|
||||
// GetZoneKey is a helper function that builds a string identifier that is unique per failure-zone;
|
||||
// it returns empty-string for no zone.
|
||||
// Since there are currently two separate zone keys:
|
||||
// * "failure-domain.beta.kubernetes.io/zone"
|
||||
// * "topology.kubernetes.io/zone"
|
||||
// GetZoneKey will first check failure-domain.beta.kubernetes.io/zone and if not exists, will then check
|
||||
// topology.kubernetes.io/zone
|
||||
func GetZoneKey(node *v1.Node) string {
|
||||
labels := node.Labels
|
||||
if labels == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
region, _ := labels[v1.LabelZoneRegion]
|
||||
failureDomain, _ := labels[v1.LabelZoneFailureDomain]
|
||||
// TODO: prefer stable labels for zone in v1.18
|
||||
zone, ok := labels[v1.LabelZoneFailureDomain]
|
||||
if !ok {
|
||||
zone, _ = labels[v1.LabelZoneFailureDomainStable]
|
||||
}
|
||||
|
||||
if region == "" && failureDomain == "" {
|
||||
// TODO: prefer stable labels for region in v1.18
|
||||
region, ok := labels[v1.LabelZoneRegion]
|
||||
if !ok {
|
||||
region, _ = labels[v1.LabelZoneRegionStable]
|
||||
}
|
||||
|
||||
if region == "" && zone == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// We include the null character just in case region or failureDomain has a colon
|
||||
// (We do assume there's no null characters in a region or failureDomain)
|
||||
// As a nice side-benefit, the null character is not printed by fmt.Print or glog
|
||||
return region + ":\x00:" + failureDomain
|
||||
return region + ":\x00:" + zone
|
||||
}
|
||||
|
||||
type nodeForConditionPatch struct {
|
||||
Status nodeStatusForPatch `json:"status"`
|
||||
}
|
||||
|
||||
type nodeStatusForPatch struct {
|
||||
Conditions []v1.NodeCondition `json:"conditions"`
|
||||
}
|
||||
|
||||
// SetNodeCondition updates specific node condition with patch operation.
|
||||
func SetNodeCondition(c clientset.Interface, node types.NodeName, condition v1.NodeCondition) error {
|
||||
generatePatch := func(condition v1.NodeCondition) ([]byte, error) {
|
||||
raw, err := json.Marshal(&[]v1.NodeCondition{condition})
|
||||
patch := nodeForConditionPatch{
|
||||
Status: nodeStatusForPatch{
|
||||
Conditions: []v1.NodeCondition{
|
||||
condition,
|
||||
},
|
||||
},
|
||||
}
|
||||
patchBytes, err := json.Marshal(&patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(fmt.Sprintf(`{"status":{"conditions":%s}}`, raw)), nil
|
||||
return patchBytes, nil
|
||||
}
|
||||
condition.LastHeartbeatTime = metav1.NewTime(time.Now())
|
||||
patch, err := generatePatch(condition)
|
||||
@ -160,15 +205,48 @@ func SetNodeCondition(c clientset.Interface, node types.NodeName, condition v1.N
|
||||
return err
|
||||
}
|
||||
|
||||
type nodeForCIDRMergePatch struct {
|
||||
Spec nodeSpecForMergePatch `json:"spec"`
|
||||
}
|
||||
|
||||
type nodeSpecForMergePatch struct {
|
||||
PodCIDR string `json:"podCIDR"`
|
||||
PodCIDRs []string `json:"podCIDRs,omitempty"`
|
||||
}
|
||||
|
||||
// PatchNodeCIDR patches the specified node's CIDR to the given value.
|
||||
func PatchNodeCIDR(c clientset.Interface, node types.NodeName, cidr string) error {
|
||||
raw, err := json.Marshal(cidr)
|
||||
patch := nodeForCIDRMergePatch{
|
||||
Spec: nodeSpecForMergePatch{
|
||||
PodCIDR: cidr,
|
||||
},
|
||||
}
|
||||
patchBytes, err := json.Marshal(&patch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json.Marshal CIDR: %v", err)
|
||||
}
|
||||
|
||||
patchBytes := []byte(fmt.Sprintf(`{"spec":{"podCIDR":%s}}`, raw))
|
||||
if _, err := c.CoreV1().Nodes().Patch(string(node), types.StrategicMergePatchType, patchBytes); err != nil {
|
||||
return fmt.Errorf("failed to patch node CIDR: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PatchNodeCIDRs patches the specified node.CIDR=cidrs[0] and node.CIDRs to the given value.
|
||||
func PatchNodeCIDRs(c clientset.Interface, node types.NodeName, cidrs []string) error {
|
||||
// set the pod cidrs list and set the old pod cidr field
|
||||
patch := nodeForCIDRMergePatch{
|
||||
Spec: nodeSpecForMergePatch{
|
||||
PodCIDR: cidrs[0],
|
||||
PodCIDRs: cidrs,
|
||||
},
|
||||
}
|
||||
|
||||
patchBytes, err := json.Marshal(&patch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json.Marshal CIDR: %v", err)
|
||||
}
|
||||
klog.V(4).Infof("cidrs patch bytes are:%s", string(patchBytes))
|
||||
if _, err := c.CoreV1().Nodes().Patch(string(node), types.StrategicMergePatchType, patchBytes); err != nil {
|
||||
return fmt.Errorf("failed to patch node CIDR: %v", err)
|
||||
}
|
||||
@ -195,12 +273,21 @@ func preparePatchBytesforNodeStatus(nodeName types.NodeName, oldNode *v1.Node, n
|
||||
return nil, fmt.Errorf("failed to Marshal oldData for node %q: %v", nodeName, err)
|
||||
}
|
||||
|
||||
// NodeStatus.Addresses is incorrectly annotated as patchStrategy=merge, which
|
||||
// will cause strategicpatch.CreateTwoWayMergePatch to create an incorrect patch
|
||||
// if it changed.
|
||||
manuallyPatchAddresses := (len(oldNode.Status.Addresses) > 0) && !equality.Semantic.DeepEqual(oldNode.Status.Addresses, newNode.Status.Addresses)
|
||||
|
||||
// Reset spec to make sure only patch for Status or ObjectMeta is generated.
|
||||
// Note that we don't reset ObjectMeta here, because:
|
||||
// 1. This aligns with Nodes().UpdateStatus().
|
||||
// 2. Some component does use this to update node annotations.
|
||||
newNode.Spec = oldNode.Spec
|
||||
newData, err := json.Marshal(newNode)
|
||||
diffNode := newNode.DeepCopy()
|
||||
diffNode.Spec = oldNode.Spec
|
||||
if manuallyPatchAddresses {
|
||||
diffNode.Status.Addresses = oldNode.Status.Addresses
|
||||
}
|
||||
newData, err := json.Marshal(diffNode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to Marshal newData for node %q: %v", nodeName, err)
|
||||
}
|
||||
@ -209,5 +296,63 @@ func preparePatchBytesforNodeStatus(nodeName types.NodeName, oldNode *v1.Node, n
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for node %q: %v", nodeName, err)
|
||||
}
|
||||
if manuallyPatchAddresses {
|
||||
patchBytes, err = fixupPatchForNodeStatusAddresses(patchBytes, newNode.Status.Addresses)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fix up NodeAddresses in patch for node %q: %v", nodeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return patchBytes, nil
|
||||
}
|
||||
|
||||
// fixupPatchForNodeStatusAddresses adds a replace-strategy patch for Status.Addresses to
|
||||
// the existing patch
|
||||
func fixupPatchForNodeStatusAddresses(patchBytes []byte, addresses []v1.NodeAddress) ([]byte, error) {
|
||||
// Given patchBytes='{"status": {"conditions": [ ... ], "phase": ...}}' and
|
||||
// addresses=[{"type": "InternalIP", "address": "10.0.0.1"}], we need to generate:
|
||||
//
|
||||
// {
|
||||
// "status": {
|
||||
// "conditions": [ ... ],
|
||||
// "phase": ...,
|
||||
// "addresses": [
|
||||
// {
|
||||
// "type": "InternalIP",
|
||||
// "address": "10.0.0.1"
|
||||
// },
|
||||
// {
|
||||
// "$patch": "replace"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
|
||||
var patchMap map[string]interface{}
|
||||
if err := json.Unmarshal(patchBytes, &patchMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrBytes, err := json.Marshal(addresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var addrArray []interface{}
|
||||
if err := json.Unmarshal(addrBytes, &addrArray); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrArray = append(addrArray, map[string]interface{}{"$patch": "replace"})
|
||||
|
||||
status := patchMap["status"]
|
||||
if status == nil {
|
||||
status = map[string]interface{}{}
|
||||
patchMap["status"] = status
|
||||
}
|
||||
statusMap, ok := status.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected data in patch")
|
||||
}
|
||||
statusMap["addresses"] = addrArray
|
||||
|
||||
return json.Marshal(patchMap)
|
||||
}
|
||||
|
1
vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
generated
vendored
1
vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
generated
vendored
@ -27,6 +27,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultImageTag is the default tag for docker image.
|
||||
DefaultImageTag = "latest"
|
||||
)
|
||||
|
||||
|
6
vendor/k8s.io/kubernetes/pkg/util/resizefs/resizefs_linux.go
generated
vendored
6
vendor/k8s.io/kubernetes/pkg/util/resizefs/resizefs_linux.go
generated
vendored
@ -22,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/utils/mount"
|
||||
)
|
||||
|
||||
// ResizeFs Provides support for resizing file systems
|
||||
@ -61,7 +61,7 @@ func (resizefs *ResizeFs) Resize(devicePath string, deviceMountPath string) (boo
|
||||
}
|
||||
|
||||
func (resizefs *ResizeFs) extResize(devicePath string) (bool, error) {
|
||||
output, err := resizefs.mounter.Exec.Run("resize2fs", devicePath)
|
||||
output, err := resizefs.mounter.Exec.Command("resize2fs", devicePath).CombinedOutput()
|
||||
if err == nil {
|
||||
klog.V(2).Infof("Device %s resized successfully", devicePath)
|
||||
return true, nil
|
||||
@ -74,7 +74,7 @@ func (resizefs *ResizeFs) extResize(devicePath string) (bool, error) {
|
||||
|
||||
func (resizefs *ResizeFs) xfsResize(deviceMountPath string) (bool, error) {
|
||||
args := []string{"-d", deviceMountPath}
|
||||
output, err := resizefs.mounter.Exec.Run("xfs_growfs", args...)
|
||||
output, err := resizefs.mounter.Exec.Command("xfs_growfs", args...).CombinedOutput()
|
||||
|
||||
if err == nil {
|
||||
klog.V(2).Infof("Device %s resized successfully", deviceMountPath)
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/util/resizefs/resizefs_unsupported.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/util/resizefs/resizefs_unsupported.go
generated
vendored
@ -21,7 +21,7 @@ package resizefs
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/utils/mount"
|
||||
)
|
||||
|
||||
// ResizeFs Provides support for resizing file systems
|
||||
|
91
vendor/k8s.io/kubernetes/pkg/util/slice/slice.go
generated
vendored
91
vendor/k8s.io/kubernetes/pkg/util/slice/slice.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
/*
|
||||
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 slice provides utility methods for common operations on slices.
|
||||
package slice
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||
)
|
||||
|
||||
// CopyStrings copies the contents of the specified string slice
|
||||
// into a new slice.
|
||||
func CopyStrings(s []string) []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]string, len(s))
|
||||
copy(c, s)
|
||||
return c
|
||||
}
|
||||
|
||||
// SortStrings sorts the specified string slice in place. It returns the same
|
||||
// slice that was provided in order to facilitate method chaining.
|
||||
func SortStrings(s []string) []string {
|
||||
sort.Strings(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// ShuffleStrings copies strings from the specified slice into a copy in random
|
||||
// order. It returns a new slice.
|
||||
func ShuffleStrings(s []string) []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
shuffled := make([]string, len(s))
|
||||
perm := utilrand.Perm(len(s))
|
||||
for i, j := range perm {
|
||||
shuffled[j] = s[i]
|
||||
}
|
||||
return shuffled
|
||||
}
|
||||
|
||||
// ContainsString checks if a given slice of strings contains the provided string.
|
||||
// If a modifier func is provided, it is called with the slice item before the comparation.
|
||||
func ContainsString(slice []string, s string, modifier func(s string) string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
if modifier != nil && modifier(item) == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveString returns a newly created []string that contains all items from slice that
|
||||
// are not equal to s and modifier(s) in case modifier func is provided.
|
||||
func RemoveString(slice []string, s string, modifier func(s string) string) []string {
|
||||
newSlice := make([]string, 0)
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
continue
|
||||
}
|
||||
if modifier != nil && modifier(item) == s {
|
||||
continue
|
||||
}
|
||||
newSlice = append(newSlice, item)
|
||||
}
|
||||
if len(newSlice) == 0 {
|
||||
// Sanitize for unit tests so we don't need to distinguish empty array
|
||||
// and nil.
|
||||
newSlice = nil
|
||||
}
|
||||
return newSlice
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/util/system/system_utils.go
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/util/system/system_utils.go
generated
vendored
@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 system
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO: find a better way of figuring out if given node is a registered master.
|
||||
func IsMasterNode(nodeName string) bool {
|
||||
// We are trying to capture "master(-...)?$" regexp.
|
||||
// However, using regexp.MatchString() results even in more than 35%
|
||||
// of all space allocations in ControllerManager spent in this function.
|
||||
// That's why we are trying to be a bit smarter.
|
||||
if strings.HasSuffix(nodeName, "master") {
|
||||
return true
|
||||
}
|
||||
if len(nodeName) >= 10 {
|
||||
return strings.HasSuffix(nodeName[:len(nodeName)-3], "master-")
|
||||
}
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user