diff options
author | Daniel J Walsh <dwalsh@redhat.com> | 2018-03-26 18:26:55 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-03-27 18:09:12 +0000 |
commit | af64e10400f8533a0c48ecdf5ab9b7fbf329e14e (patch) | |
tree | 59160e3841b440dd35189c724bbb4375a7be173b /vendor/k8s.io/kubernetes/pkg/volume/util | |
parent | 26d7e3c7b85e28c4e42998c90fdcc14079f13eef (diff) | |
download | podman-af64e10400f8533a0c48ecdf5ab9b7fbf329e14e.tar.gz podman-af64e10400f8533a0c48ecdf5ab9b7fbf329e14e.tar.bz2 podman-af64e10400f8533a0c48ecdf5ab9b7fbf329e14e.zip |
Vendor in lots of kubernetes stuff to shrink image size
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Closes: #554
Approved by: mheon
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/volume/util')
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go | 462 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/device_util.go | 31 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux.go | 61 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/device_util_unsupported.go | 24 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/doc.go | 18 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go (renamed from vendor/k8s.io/kubernetes/pkg/volume/util/fs.go) | 9 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go (renamed from vendor/k8s.io/kubernetes/pkg/volume/util/fs_unsupported.go) | 2 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/io_util.go | 47 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go | 252 | ||||
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/volume/util/util.go | 213 |
10 files changed, 258 insertions, 861 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go b/vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go deleted file mode 100644 index 5eef55b45..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go +++ /dev/null @@ -1,462 +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 util - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "time" - - "github.com/golang/glog" - - "k8s.io/apimachinery/pkg/util/sets" -) - -const ( - maxFileNameLength = 255 - maxPathLength = 4096 -) - -// AtomicWriter handles atomically projecting content for a set of files into -// a target directory. -// -// Note: -// -// 1. AtomicWriter reserves the set of pathnames starting with `..`. -// 2. AtomicWriter offers no concurrency guarantees and must be synchronized -// by the caller. -// -// The visible files in this volume are symlinks to files in the writer's data -// directory. Actual files are stored in a hidden timestamped directory which -// is symlinked to by the data directory. The timestamped directory and -// data directory symlink are created in the writer's target dir. This scheme -// allows the files to be atomically updated by changing the target of the -// data directory symlink. -// -// Consumers of the target directory can monitor the ..data symlink using -// inotify or fanotify to receive events when the content in the volume is -// updated. -type AtomicWriter struct { - targetDir string - logContext string -} - -type FileProjection struct { - Data []byte - Mode int32 -} - -// NewAtomicWriter creates a new AtomicWriter configured to write to the given -// target directory, or returns an error if the target directory does not exist. -func NewAtomicWriter(targetDir string, logContext string) (*AtomicWriter, error) { - _, err := os.Stat(targetDir) - if os.IsNotExist(err) { - return nil, err - } - - return &AtomicWriter{targetDir: targetDir, logContext: logContext}, nil -} - -const ( - dataDirName = "..data" - newDataDirName = "..data_tmp" -) - -// Write does an atomic projection of the given payload into the writer's target -// directory. Input paths must not begin with '..'. -// -// The Write algorithm is: -// -// 1. The payload is validated; if the payload is invalid, the function returns -// 2. The user-visible portion of the volume is walked to determine whether any -// portion of the payload was deleted and is still present on disk. -// If the payload is already present on disk and there are no deleted files, -// the function returns -// 3. A check is made to determine whether data present in the payload has changed -// 4. A new timestamped dir is created -// 5. The payload is written to the new timestamped directory -// 6. Symlinks and directory for new user-visible files are created (if needed). -// -// For example, consider the files: -// <target-dir>/podName -// <target-dir>/user/labels -// <target-dir>/k8s/annotations -// -// The user visible files are symbolic links into the internal data directory: -// <target-dir>/podName -> ..data/podName -// <target-dir>/usr/labels -> ../..data/usr/labels -// <target-dir>/k8s/annotations -> ../..data/k8s/annotations -// -// Relative links are created into the data directory for files in subdirectories. -// -// The data directory itself is a link to a timestamped directory with -// the real data: -// <target-dir>/..data -> ..2016_02_01_15_04_05.12345678/ -// 7. The current timestamped directory is detected by reading the data directory -// symlink -// 8. A symlink to the new timestamped directory ..data_tmp is created that will -// become the new data directory -// 9. The new data directory symlink is renamed to the data directory; rename is atomic -// 10. Old paths are removed from the user-visible portion of the target directory -// 11. The previous timestamped directory is removed, if it exists -func (w *AtomicWriter) Write(payload map[string]FileProjection) error { - // (1) - cleanPayload, err := validatePayload(payload) - if err != nil { - glog.Errorf("%s: invalid payload: %v", w.logContext, err) - return err - } - - // (2) - pathsToRemove, err := w.pathsToRemove(cleanPayload) - if err != nil { - glog.Errorf("%s: error determining user-visible files to remove: %v", w.logContext, err) - return err - } - - // (3) - if should, err := w.shouldWritePayload(cleanPayload); err != nil { - glog.Errorf("%s: error determining whether payload should be written to disk: %v", w.logContext, err) - return err - } else if !should && len(pathsToRemove) == 0 { - glog.V(4).Infof("%s: no update required for target directory %v", w.logContext, w.targetDir) - return nil - } else { - glog.V(4).Infof("%s: write required for target directory %v", w.logContext, w.targetDir) - } - - // (4) - tsDir, err := w.newTimestampDir() - if err != nil { - glog.V(4).Infof("%s: error creating new ts data directory: %v", w.logContext, err) - return err - } - - // (5) - if err = w.writePayloadToDir(cleanPayload, tsDir); err != nil { - glog.Errorf("%s: error writing payload to ts data directory %s: %v", w.logContext, tsDir, err) - return err - } else { - glog.V(4).Infof("%s: performed write of new data to ts data directory: %s", w.logContext, tsDir) - } - - // (6) - if err = w.createUserVisibleFiles(cleanPayload); err != nil { - glog.Errorf("%s: error creating visible symlinks in %s: %v", w.logContext, w.targetDir, err) - return err - } - - // (7) - _, tsDirName := filepath.Split(tsDir) - dataDirPath := path.Join(w.targetDir, dataDirName) - oldTsDir, err := os.Readlink(dataDirPath) - if err != nil && !os.IsNotExist(err) { - glog.Errorf("%s: error reading link for data directory: %v", w.logContext, err) - return err - } - - // (8) - newDataDirPath := path.Join(w.targetDir, newDataDirName) - if err = os.Symlink(tsDirName, newDataDirPath); err != nil { - os.RemoveAll(tsDir) - glog.Errorf("%s: error creating symbolic link for atomic update: %v", w.logContext, err) - return err - } - - // (9) - if runtime.GOOS == "windows" { - os.Remove(dataDirPath) - err = os.Symlink(tsDirName, dataDirPath) - os.Remove(newDataDirPath) - } else { - err = os.Rename(newDataDirPath, dataDirPath) - } - if err != nil { - os.Remove(newDataDirPath) - os.RemoveAll(tsDir) - glog.Errorf("%s: error renaming symbolic link for data directory %s: %v", w.logContext, newDataDirPath, err) - return err - } - - // (10) - if err = w.removeUserVisiblePaths(pathsToRemove); err != nil { - glog.Errorf("%s: error removing old visible symlinks: %v", w.logContext, err) - return err - } - - // (11) - if len(oldTsDir) > 0 { - if err = os.RemoveAll(path.Join(w.targetDir, oldTsDir)); err != nil { - glog.Errorf("%s: error removing old data directory %s: %v", w.logContext, oldTsDir, err) - return err - } - } - - return nil -} - -// validatePayload returns an error if any path in the payload returns a copy of the payload with the paths cleaned. -func validatePayload(payload map[string]FileProjection) (map[string]FileProjection, error) { - cleanPayload := make(map[string]FileProjection) - for k, content := range payload { - if err := validatePath(k); err != nil { - return nil, err - } - - cleanPayload[path.Clean(k)] = content - } - - return cleanPayload, nil -} - -// validatePath validates a single path, returning an error if the path is -// invalid. paths may not: -// -// 1. be absolute -// 2. contain '..' as an element -// 3. start with '..' -// 4. contain filenames larger than 255 characters -// 5. be longer than 4096 characters -func validatePath(targetPath string) error { - // TODO: somehow unify this with the similar api validation, - // validateVolumeSourcePath; the error semantics are just different enough - // from this that it was time-prohibitive trying to find the right - // refactoring to re-use. - if targetPath == "" { - return fmt.Errorf("invalid path: must not be empty: %q", targetPath) - } - if path.IsAbs(targetPath) { - return fmt.Errorf("invalid path: must be relative path: %s", targetPath) - } - - if len(targetPath) > maxPathLength { - return fmt.Errorf("invalid path: must be less than %d characters", maxPathLength) - } - - items := strings.Split(targetPath, string(os.PathSeparator)) - for _, item := range items { - if item == ".." { - return fmt.Errorf("invalid path: must not contain '..': %s", targetPath) - } - if len(item) > maxFileNameLength { - return fmt.Errorf("invalid path: filenames must be less than %d characters", maxFileNameLength) - } - } - if strings.HasPrefix(items[0], "..") && len(items[0]) > 2 { - return fmt.Errorf("invalid path: must not start with '..': %s", targetPath) - } - - return nil -} - -// shouldWritePayload returns whether the payload should be written to disk. -func (w *AtomicWriter) shouldWritePayload(payload map[string]FileProjection) (bool, error) { - for userVisiblePath, fileProjection := range payload { - shouldWrite, err := w.shouldWriteFile(path.Join(w.targetDir, userVisiblePath), fileProjection.Data) - if err != nil { - return false, err - } - - if shouldWrite { - return true, nil - } - } - - return false, nil -} - -// shouldWriteFile returns whether a new version of a file should be written to disk. -func (w *AtomicWriter) shouldWriteFile(path string, content []byte) (bool, error) { - _, err := os.Lstat(path) - if os.IsNotExist(err) { - return true, nil - } - - contentOnFs, err := ioutil.ReadFile(path) - if err != nil { - return false, err - } - - return (bytes.Compare(content, contentOnFs) != 0), nil -} - -// pathsToRemove walks the user-visible portion of the target directory and -// determines which paths should be removed (if any) after the payload is -// written to the target directory. -func (w *AtomicWriter) pathsToRemove(payload map[string]FileProjection) (sets.String, error) { - paths := sets.NewString() - visitor := func(path string, info os.FileInfo, err error) error { - if path == w.targetDir { - return nil - } - - relativePath := strings.TrimPrefix(path, w.targetDir) - if runtime.GOOS == "windows" { - relativePath = strings.TrimPrefix(relativePath, "\\") - } else { - relativePath = strings.TrimPrefix(relativePath, "/") - } - if strings.HasPrefix(relativePath, "..") { - return nil - } - - paths.Insert(relativePath) - return nil - } - - err := filepath.Walk(w.targetDir, visitor) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - glog.V(5).Infof("%s: current paths: %+v", w.targetDir, paths.List()) - - newPaths := sets.NewString() - for file := range payload { - // add all subpaths for the payload to the set of new paths - // to avoid attempting to remove non-empty dirs - for subPath := file; subPath != ""; { - newPaths.Insert(subPath) - subPath, _ = filepath.Split(subPath) - subPath = strings.TrimSuffix(subPath, "/") - } - } - glog.V(5).Infof("%s: new paths: %+v", w.targetDir, newPaths.List()) - - result := paths.Difference(newPaths) - glog.V(5).Infof("%s: paths to remove: %+v", w.targetDir, result) - - return result, nil -} - -// newTimestampDir creates a new timestamp directory -func (w *AtomicWriter) newTimestampDir() (string, error) { - tsDir, err := ioutil.TempDir(w.targetDir, fmt.Sprintf("..%s.", time.Now().Format("1981_02_01_15_04_05"))) - if err != nil { - glog.Errorf("%s: unable to create new temp directory: %v", w.logContext, err) - return "", err - } - - // 0755 permissions are needed to allow 'group' and 'other' to recurse the - // directory tree. do a chmod here to ensure that permissions are set correctly - // regardless of the process' umask. - err = os.Chmod(tsDir, 0755) - if err != nil { - glog.Errorf("%s: unable to set mode on new temp directory: %v", w.logContext, err) - return "", err - } - - return tsDir, nil -} - -// writePayloadToDir writes the given payload to the given directory. The -// directory must exist. -func (w *AtomicWriter) writePayloadToDir(payload map[string]FileProjection, dir string) error { - for userVisiblePath, fileProjection := range payload { - content := fileProjection.Data - mode := os.FileMode(fileProjection.Mode) - fullPath := path.Join(dir, userVisiblePath) - baseDir, _ := filepath.Split(fullPath) - - err := os.MkdirAll(baseDir, os.ModePerm) - if err != nil { - glog.Errorf("%s: unable to create directory %s: %v", w.logContext, baseDir, err) - return err - } - - err = ioutil.WriteFile(fullPath, content, mode) - if err != nil { - glog.Errorf("%s: unable to write file %s with mode %v: %v", w.logContext, fullPath, mode, err) - return err - } - // Chmod is needed because ioutil.WriteFile() ends up calling - // open(2) to create the file, so the final mode used is "mode & - // ~umask". But we want to make sure the specified mode is used - // in the file no matter what the umask is. - err = os.Chmod(fullPath, mode) - if err != nil { - glog.Errorf("%s: unable to write file %s with mode %v: %v", w.logContext, fullPath, mode, err) - } - } - - return nil -} - -// createUserVisibleFiles creates the relative symlinks for all the -// files configured in the payload. If the directory in a file path does not -// exist, it is created. -// -// Viz: -// For files: "bar", "foo/bar", "baz/bar", "foo/baz/blah" -// the following symlinks and subdirectories are created: -// bar -> ..data/bar -// foo/bar -> ../..data/foo/bar -// baz/bar -> ../..data/baz/bar -// foo/baz/blah -> ../../..data/foo/baz/blah -func (w *AtomicWriter) createUserVisibleFiles(payload map[string]FileProjection) error { - for userVisiblePath := range payload { - dir, _ := filepath.Split(userVisiblePath) - subDirs := 0 - if len(dir) > 0 { - // If dir is not empty, the projection path contains at least one - // subdirectory (example: userVisiblePath := "foo/bar"). - // Since filepath.Split leaves a trailing path separator, in this - // example, dir = "foo/". In order to calculate the number of - // subdirectories, we must subtract 1 from the number returned by split. - subDirs = len(strings.Split(dir, "/")) - 1 - err := os.MkdirAll(path.Join(w.targetDir, dir), os.ModePerm) - if err != nil { - return err - } - } - _, err := os.Readlink(path.Join(w.targetDir, userVisiblePath)) - if err != nil && os.IsNotExist(err) { - // The link into the data directory for this path doesn't exist; create it, - // respecting the number of subdirectories necessary to link - // correctly back into the data directory. - visibleFile := path.Join(w.targetDir, userVisiblePath) - dataDirFile := path.Join(strings.Repeat("../", subDirs), dataDirName, userVisiblePath) - - err = os.Symlink(dataDirFile, visibleFile) - if err != nil { - return err - } - } - } - return nil -} - -// removeUserVisiblePaths removes the set of paths from the user-visible -// portion of the writer's target directory. -func (w *AtomicWriter) removeUserVisiblePaths(paths sets.String) error { - orderedPaths := paths.List() - for ii := len(orderedPaths) - 1; ii >= 0; ii-- { - if err := os.Remove(path.Join(w.targetDir, orderedPaths[ii])); err != nil { - glog.Errorf("%s: error pruning old user-visible path %s: %v", w.logContext, orderedPaths[ii], err) - return err - } - } - - return nil -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util.go b/vendor/k8s.io/kubernetes/pkg/volume/util/device_util.go deleted file mode 100644 index 9098d7b85..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util.go +++ /dev/null @@ -1,31 +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 util - -//DeviceUtil is a util for common device methods -type DeviceUtil interface { - FindMultipathDeviceForDevice(disk string) string -} - -type deviceHandler struct { - get_io IoUtil -} - -//NewDeviceHandler Create a new IoHandler implementation -func NewDeviceHandler(io IoUtil) DeviceUtil { - return &deviceHandler{get_io: io} -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux.go b/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux.go deleted file mode 100644 index 0d9851140..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build linux - -/* -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 util - -import ( - "errors" - "strings" -) - -// FindMultipathDeviceForDevice given a device name like /dev/sdx, find the devicemapper parent -func (handler *deviceHandler) FindMultipathDeviceForDevice(device string) string { - io := handler.get_io - disk, err := findDeviceForPath(device, io) - if err != nil { - return "" - } - sysPath := "/sys/block/" - if dirs, err := io.ReadDir(sysPath); err == nil { - for _, f := range dirs { - name := f.Name() - if strings.HasPrefix(name, "dm-") { - if _, err1 := io.Lstat(sysPath + name + "/slaves/" + disk); err1 == nil { - return "/dev/" + name - } - } - } - } - return "" -} - -// findDeviceForPath Find the underlaying disk for a linked path such as /dev/disk/by-path/XXXX or /dev/mapper/XXXX -// will return sdX or hdX etc, if /dev/sdX is passed in then sdX will be returned -func findDeviceForPath(path string, io IoUtil) (string, error) { - devicePath, err := io.EvalSymlinks(path) - if err != nil { - return "", err - } - // if path /dev/hdX split into "", "dev", "hdX" then we will - // return just the last part - parts := strings.Split(devicePath, "/") - if len(parts) == 3 && strings.HasPrefix(parts[1], "dev") { - return parts[2], nil - } - return "", errors.New("Illegal path for device " + devicePath) -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_unsupported.go b/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_unsupported.go deleted file mode 100644 index 6afb1f139..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/device_util_unsupported.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !linux - -/* -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 util - -// FindMultipathDeviceForDevice unsupported returns "" -func (handler *deviceHandler) FindMultipathDeviceForDevice(device string) string { - return "" -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/doc.go b/vendor/k8s.io/kubernetes/pkg/volume/util/doc.go deleted file mode 100644 index 620add69d..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/doc.go +++ /dev/null @@ -1,18 +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. -*/ - -// Contains utility code for use by volume plugins. -package util // import "k8s.io/kubernetes/pkg/volume/util" diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/fs.go b/vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go index cfa7e30b4..bbb4b0105 100644 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/fs.go +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go @@ -16,14 +16,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package fs import ( "bytes" "fmt" "os/exec" "strings" - "syscall" + + "golang.org/x/sys/unix" "k8s.io/apimachinery/pkg/api/resource" ) @@ -31,8 +32,8 @@ import ( // FSInfo linux returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error) // for the filesystem that path resides upon. func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) { - statfs := &syscall.Statfs_t{} - err := syscall.Statfs(path, statfs) + statfs := &unix.Statfs_t{} + err := unix.Statfs(path, statfs) if err != nil { return 0, 0, 0, 0, 0, 0, err } diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/fs_unsupported.go b/vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go index 8d35d5dae..da41fc8ee 100644 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/fs_unsupported.go +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package fs import ( "fmt" diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/io_util.go b/vendor/k8s.io/kubernetes/pkg/volume/util/io_util.go deleted file mode 100644 index e1f30f5c3..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/io_util.go +++ /dev/null @@ -1,47 +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 util - -import ( - "io/ioutil" - "os" - "path/filepath" -) - -// IoUtil is a mockable util for common IO operations -type IoUtil interface { - ReadDir(dirname string) ([]os.FileInfo, error) - Lstat(name string) (os.FileInfo, error) - EvalSymlinks(path string) (string, error) -} - -type osIOHandler struct{} - -//NewIOHandler Create a new IoHandler implementation -func NewIOHandler() IoUtil { - return &osIOHandler{} -} - -func (handler *osIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) { - return ioutil.ReadDir(dirname) -} -func (handler *osIOHandler) Lstat(name string) (os.FileInfo, error) { - return os.Lstat(name) -} -func (handler *osIOHandler) EvalSymlinks(path string) (string, error) { - return filepath.EvalSymlinks(path) -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go b/vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go new file mode 100644 index 000000000..1af6465c6 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go @@ -0,0 +1,252 @@ +/* +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 recyclerclient + +import ( + "fmt" + + "github.com/golang/glog" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/watch" + clientset "k8s.io/client-go/kubernetes" +) + +type RecycleEventRecorder func(eventtype, message string) + +// RecycleVolumeByWatchingPodUntilCompletion is intended for use with volume +// Recyclers. This function will save the given Pod to the API and watch it +// until it completes, fails, or the pod's ActiveDeadlineSeconds is exceeded, +// whichever comes first. An attempt to delete a recycler pod is always +// attempted before returning. +// +// In case there is a pod with the same namespace+name already running, this +// function deletes it as it is not able to judge if it is an old recycler +// or user has forged a fake recycler to block Kubernetes from recycling.// +// +// pod - the pod designed by a volume plugin to recycle the volume. pod.Name +// will be overwritten with unique name based on PV.Name. +// client - kube client for API operations. +func RecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *v1.Pod, kubeClient clientset.Interface, recorder RecycleEventRecorder) error { + return internalRecycleVolumeByWatchingPodUntilCompletion(pvName, pod, newRecyclerClient(kubeClient, recorder)) +} + +// same as above func comments, except 'recyclerClient' is a narrower pod API +// interface to ease testing +func internalRecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *v1.Pod, recyclerClient recyclerClient) error { + glog.V(5).Infof("creating recycler pod for volume %s\n", pod.Name) + + // Generate unique name for the recycler pod - we need to get "already + // exists" error when a previous controller has already started recycling + // the volume. Here we assume that pv.Name is already unique. + pod.Name = "recycler-for-" + pvName + pod.GenerateName = "" + + stopChannel := make(chan struct{}) + defer close(stopChannel) + podCh, err := recyclerClient.WatchPod(pod.Name, pod.Namespace, stopChannel) + if err != nil { + glog.V(4).Infof("cannot start watcher for pod %s/%s: %v", pod.Namespace, pod.Name, err) + return err + } + + // Start the pod + _, err = recyclerClient.CreatePod(pod) + if err != nil { + if errors.IsAlreadyExists(err) { + deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace) + if deleteErr != nil { + return fmt.Errorf("failed to delete old recycler pod %s/%s: %s", pod.Namespace, pod.Name, deleteErr) + } + // Recycler will try again and the old pod will be hopefully deleted + // at that time. + return fmt.Errorf("old recycler pod found, will retry later") + } + return fmt.Errorf("unexpected error creating recycler pod: %+v", err) + } + err = waitForPod(pod, recyclerClient, podCh) + + // In all cases delete the recycler pod and log its result. + glog.V(2).Infof("deleting recycler pod %s/%s", pod.Namespace, pod.Name) + deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace) + if deleteErr != nil { + glog.Errorf("failed to delete recycler pod %s/%s: %v", pod.Namespace, pod.Name, err) + } + + // Returning recycler error is preferred, the pod will be deleted again on + // the next retry. + if err != nil { + return fmt.Errorf("failed to recycle volume: %s", err) + } + + // Recycle succeeded but we failed to delete the recycler pod. Report it, + // the controller will re-try recycling the PV again shortly. + if deleteErr != nil { + return fmt.Errorf("failed to delete recycler pod: %s", deleteErr) + } + + return nil +} + +// waitForPod watches the pod it until it finishes and send all events on the +// pod to the PV. +func waitForPod(pod *v1.Pod, recyclerClient recyclerClient, podCh <-chan watch.Event) error { + for { + event, ok := <-podCh + if !ok { + return fmt.Errorf("recycler pod %q watch channel had been closed", pod.Name) + } + switch event.Object.(type) { + case *v1.Pod: + // POD changed + pod := event.Object.(*v1.Pod) + glog.V(4).Infof("recycler pod update received: %s %s/%s %s", event.Type, pod.Namespace, pod.Name, pod.Status.Phase) + switch event.Type { + case watch.Added, watch.Modified: + if pod.Status.Phase == v1.PodSucceeded { + // Recycle succeeded. + return nil + } + if pod.Status.Phase == v1.PodFailed { + if pod.Status.Message != "" { + return fmt.Errorf(pod.Status.Message) + } else { + return fmt.Errorf("pod failed, pod.Status.Message unknown.") + } + } + + case watch.Deleted: + return fmt.Errorf("recycler pod was deleted") + + case watch.Error: + return fmt.Errorf("recycler pod watcher failed") + } + + case *v1.Event: + // Event received + podEvent := event.Object.(*v1.Event) + glog.V(4).Infof("recycler event received: %s %s/%s %s/%s %s", event.Type, podEvent.Namespace, podEvent.Name, podEvent.InvolvedObject.Namespace, podEvent.InvolvedObject.Name, podEvent.Message) + if event.Type == watch.Added { + recyclerClient.Event(podEvent.Type, podEvent.Message) + } + } + } +} + +// recyclerClient abstracts access to a Pod by providing a narrower interface. +// This makes it easier to mock a client for testing. +type recyclerClient interface { + CreatePod(pod *v1.Pod) (*v1.Pod, error) + GetPod(name, namespace string) (*v1.Pod, error) + DeletePod(name, namespace string) error + // WatchPod returns a ListWatch for watching a pod. The stopChannel is used + // to close the reflector backing the watch. The caller is responsible for + // derring a close on the channel to stop the reflector. + WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error) + // Event sends an event to the volume that is being recycled. + Event(eventtype, message string) +} + +func newRecyclerClient(client clientset.Interface, recorder RecycleEventRecorder) recyclerClient { + return &realRecyclerClient{ + client, + recorder, + } +} + +type realRecyclerClient struct { + client clientset.Interface + recorder RecycleEventRecorder +} + +func (c *realRecyclerClient) CreatePod(pod *v1.Pod) (*v1.Pod, error) { + return c.client.CoreV1().Pods(pod.Namespace).Create(pod) +} + +func (c *realRecyclerClient) GetPod(name, namespace string) (*v1.Pod, error) { + return c.client.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{}) +} + +func (c *realRecyclerClient) DeletePod(name, namespace string) error { + return c.client.CoreV1().Pods(namespace).Delete(name, nil) +} + +func (c *realRecyclerClient) Event(eventtype, message string) { + c.recorder(eventtype, message) +} + +func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error) { + podSelector, err := fields.ParseSelector("metadata.name=" + name) + if err != nil { + return nil, err + } + options := metav1.ListOptions{ + FieldSelector: podSelector.String(), + Watch: true, + } + + podWatch, err := c.client.CoreV1().Pods(namespace).Watch(options) + if err != nil { + return nil, err + } + + eventSelector, _ := fields.ParseSelector("involvedObject.name=" + name) + eventWatch, err := c.client.CoreV1().Events(namespace).Watch(metav1.ListOptions{ + FieldSelector: eventSelector.String(), + Watch: true, + }) + if err != nil { + podWatch.Stop() + return nil, err + } + + eventCh := make(chan watch.Event, 30) + + go func() { + defer eventWatch.Stop() + defer podWatch.Stop() + defer close(eventCh) + var podWatchChannelClosed bool + var eventWatchChannelClosed bool + for { + select { + case _ = <-stopChannel: + return + + case podEvent, ok := <-podWatch.ResultChan(): + if !ok { + podWatchChannelClosed = true + } else { + eventCh <- podEvent + } + case eventEvent, ok := <-eventWatch.ResultChan(): + if !ok { + eventWatchChannelClosed = true + } else { + eventCh <- eventEvent + } + } + if podWatchChannelClosed && eventWatchChannelClosed { + break + } + } + }() + + return eventCh, nil +} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/util.go b/vendor/k8s.io/kubernetes/pkg/volume/util/util.go deleted file mode 100644 index 660c3c9db..000000000 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/util.go +++ /dev/null @@ -1,213 +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 util - -import ( - "fmt" - "os" - "path" - - "github.com/golang/glog" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/kubernetes/pkg/api/v1" - v1helper "k8s.io/kubernetes/pkg/api/v1/helper" - storage "k8s.io/kubernetes/pkg/apis/storage/v1" - "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" - "k8s.io/kubernetes/pkg/util/mount" -) - -const readyFileName = "ready" - -// IsReady checks for the existence of a regular file -// called 'ready' in the given directory and returns -// true if that file exists. -func IsReady(dir string) bool { - readyFile := path.Join(dir, readyFileName) - s, err := os.Stat(readyFile) - if err != nil { - return false - } - - if !s.Mode().IsRegular() { - glog.Errorf("ready-file is not a file: %s", readyFile) - return false - } - - return true -} - -// SetReady creates a file called 'ready' in the given -// directory. It logs an error if the file cannot be -// created. -func SetReady(dir string) { - if err := os.MkdirAll(dir, 0750); err != nil && !os.IsExist(err) { - glog.Errorf("Can't mkdir %s: %v", dir, err) - return - } - - readyFile := path.Join(dir, readyFileName) - file, err := os.Create(readyFile) - if err != nil { - glog.Errorf("Can't touch %s: %v", readyFile, err) - return - } - file.Close() -} - -// UnmountPath is a common unmount routine that unmounts the given path and -// deletes the remaining directory if successful. -func UnmountPath(mountPath string, mounter mount.Interface) error { - return UnmountMountPoint(mountPath, mounter, false /* extensiveMountPointCheck */) -} - -// UnmountMountPoint is a common unmount routine that 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. -func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool) error { - if pathExists, pathErr := PathExists(mountPath); pathErr != nil { - return fmt.Errorf("Error checking if path exists: %v", pathErr) - } else if !pathExists { - glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath) - return nil - } - - var notMnt bool - var err error - - if extensiveMountPointCheck { - notMnt, err = mount.IsNotMountPoint(mounter, mountPath) - } else { - notMnt, err = mounter.IsLikelyNotMountPoint(mountPath) - } - - if err != nil { - return err - } - - if notMnt { - glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath) - return os.Remove(mountPath) - } - - // Unmount the mount path - glog.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 err - } - if notMnt { - glog.V(4).Infof("%q is unmounted, deleting the directory", mountPath) - return os.Remove(mountPath) - } - return fmt.Errorf("Failed to unmount path %v", mountPath) -} - -// 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 { - return false, err - } -} - -// GetSecretForPod locates secret by name in the pod's namespace and returns secret map -func GetSecretForPod(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (map[string]string, error) { - secret := make(map[string]string) - if kubeClient == nil { - return secret, fmt.Errorf("Cannot get kube client") - } - secrets, err := kubeClient.Core().Secrets(pod.Namespace).Get(secretName, metav1.GetOptions{}) - if err != nil { - return secret, err - } - for name, data := range secrets.Data { - secret[name] = string(data) - } - return secret, nil -} - -// GetSecretForPV locates secret by name and namespace, verifies the secret type, and returns secret map -func GetSecretForPV(secretNamespace, secretName, volumePluginName string, kubeClient clientset.Interface) (map[string]string, error) { - secret := make(map[string]string) - if kubeClient == nil { - return secret, fmt.Errorf("Cannot get kube client") - } - secrets, err := kubeClient.Core().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{}) - if err != nil { - return secret, err - } - if secrets.Type != v1.SecretType(volumePluginName) { - return secret, fmt.Errorf("Cannot get secret of type %s", volumePluginName) - } - for name, data := range secrets.Data { - secret[name] = string(data) - } - return secret, nil -} - -func GetClassForVolume(kubeClient clientset.Interface, pv *v1.PersistentVolume) (*storage.StorageClass, error) { - if kubeClient == nil { - return nil, fmt.Errorf("Cannot get kube client") - } - className := v1helper.GetPersistentVolumeClass(pv) - if className == "" { - return nil, fmt.Errorf("Volume has no storage class") - } - - class, err := kubeClient.StorageV1().StorageClasses().Get(className, metav1.GetOptions{}) - if err != nil { - return nil, err - } - return class, nil -} - -// CheckNodeAffinity looks at the PV node affinity, and checks if the node has the same corresponding labels -// This ensures that we don't mount a volume that doesn't belong to this node -func CheckNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]string) error { - affinity, err := v1helper.GetStorageNodeAffinityFromAnnotation(pv.Annotations) - if err != nil { - return fmt.Errorf("Error getting storage node affinity: %v", err) - } - if affinity == nil { - return nil - } - - if affinity.RequiredDuringSchedulingIgnoredDuringExecution != nil { - terms := affinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms - glog.V(10).Infof("Match for RequiredDuringSchedulingIgnoredDuringExecution node selector terms %+v", terms) - for _, term := range terms { - selector, err := v1helper.NodeSelectorRequirementsAsSelector(term.MatchExpressions) - if err != nil { - return fmt.Errorf("Failed to parse MatchExpressions: %v", err) - } - if !selector.Matches(labels.Set(nodeLabels)) { - return fmt.Errorf("NodeSelectorTerm %+v does not match node labels", term.MatchExpressions) - } - } - } - return nil -} |