summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/kubernetes/pkg/util
diff options
context:
space:
mode:
authorDaniel J Walsh <dwalsh@redhat.com>2018-03-26 18:26:55 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-03-27 18:09:12 +0000
commitaf64e10400f8533a0c48ecdf5ab9b7fbf329e14e (patch)
tree59160e3841b440dd35189c724bbb4375a7be173b /vendor/k8s.io/kubernetes/pkg/util
parent26d7e3c7b85e28c4e42998c90fdcc14079f13eef (diff)
downloadpodman-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/util')
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/doc.go20
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/exec.go200
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go145
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/file/file.go57
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/io/consistentread.go45
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/io/io.go61
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/io/writer.go40
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/exec.go50
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go140
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go87
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/fake.go28
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount.go159
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go578
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go47
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go346
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go225
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go24
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go10
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter.go124
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter_unsupported.go50
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go6
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/pointer/pointer.go68
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/taints/taints.go342
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/template.go48
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/umask.go27
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/umask_windows.go27
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/util.go140
28 files changed, 2081 insertions, 1031 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/util/doc.go b/vendor/k8s.io/kubernetes/pkg/util/doc.go
deleted file mode 100644
index f7e214f31..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/doc.go
+++ /dev/null
@@ -1,20 +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 util implements various utility functions used in both testing and implementation
-// of Kubernetes. Package util may not depend on any other package in the Kubernetes
-// package tree.
-package util // import "k8s.io/kubernetes/pkg/util"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go b/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
deleted file mode 100644
index de7301c8d..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
+++ /dev/null
@@ -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 exec provides an injectable interface and implementations for running commands.
-package exec // import "k8s.io/kubernetes/pkg/util/exec"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go b/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
deleted file mode 100644
index f43bfa7a1..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
+++ /dev/null
@@ -1,200 +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 exec
-
-import (
- "io"
- osexec "os/exec"
- "syscall"
- "time"
-)
-
-// ErrExecutableNotFound is returned if the executable is not found.
-var ErrExecutableNotFound = osexec.ErrNotFound
-
-// Interface is an interface that presents a subset of the os/exec API. Use this
-// when you want to inject fakeable/mockable exec behavior.
-type Interface interface {
- // Command returns a Cmd instance which can be used to run a single command.
- // This follows the pattern of package os/exec.
- Command(cmd string, args ...string) Cmd
-
- // LookPath wraps os/exec.LookPath
- LookPath(file string) (string, error)
-}
-
-// Cmd is an interface that presents an API that is very similar to Cmd from os/exec.
-// As more functionality is needed, this can grow. Since Cmd is a struct, we will have
-// to replace fields with get/set method pairs.
-type Cmd interface {
- // Run runs the command to the completion.
- Run() error
- // CombinedOutput runs the command and returns its combined standard output
- // and standard error. This follows the pattern of package os/exec.
- CombinedOutput() ([]byte, error)
- // Output runs the command and returns standard output, but not standard err
- Output() ([]byte, error)
- SetDir(dir string)
- SetStdin(in io.Reader)
- SetStdout(out io.Writer)
- SetStderr(out io.Writer)
- // Stops the command by sending SIGTERM. It is not guaranteed the
- // process will stop before this function returns. If the process is not
- // responding, an internal timer function will send a SIGKILL to force
- // terminate after 10 seconds.
- Stop()
-}
-
-// ExitError is an interface that presents an API similar to os.ProcessState, which is
-// what ExitError from os/exec is. This is designed to make testing a bit easier and
-// probably loses some of the cross-platform properties of the underlying library.
-type ExitError interface {
- String() string
- Error() string
- Exited() bool
- ExitStatus() int
-}
-
-// Implements Interface in terms of really exec()ing.
-type executor struct{}
-
-// New returns a new Interface which will os/exec to run commands.
-func New() Interface {
- return &executor{}
-}
-
-// Command is part of the Interface interface.
-func (executor *executor) Command(cmd string, args ...string) Cmd {
- return (*cmdWrapper)(osexec.Command(cmd, args...))
-}
-
-// LookPath is part of the Interface interface
-func (executor *executor) LookPath(file string) (string, error) {
- return osexec.LookPath(file)
-}
-
-// Wraps exec.Cmd so we can capture errors.
-type cmdWrapper osexec.Cmd
-
-func (cmd *cmdWrapper) SetDir(dir string) {
- cmd.Dir = dir
-}
-
-func (cmd *cmdWrapper) SetStdin(in io.Reader) {
- cmd.Stdin = in
-}
-
-func (cmd *cmdWrapper) SetStdout(out io.Writer) {
- cmd.Stdout = out
-}
-
-func (cmd *cmdWrapper) SetStderr(out io.Writer) {
- cmd.Stderr = out
-}
-
-// Run is part of the Cmd interface.
-func (cmd *cmdWrapper) Run() error {
- return (*osexec.Cmd)(cmd).Run()
-}
-
-// CombinedOutput is part of the Cmd interface.
-func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
- out, err := (*osexec.Cmd)(cmd).CombinedOutput()
- if err != nil {
- return out, handleError(err)
- }
- return out, nil
-}
-
-func (cmd *cmdWrapper) Output() ([]byte, error) {
- out, err := (*osexec.Cmd)(cmd).Output()
- if err != nil {
- return out, handleError(err)
- }
- return out, nil
-}
-
-// Stop is part of the Cmd interface.
-func (cmd *cmdWrapper) Stop() {
- c := (*osexec.Cmd)(cmd)
- if c.ProcessState.Exited() {
- return
- }
- c.Process.Signal(syscall.SIGTERM)
- time.AfterFunc(10*time.Second, func() {
- if c.ProcessState.Exited() {
- return
- }
- c.Process.Signal(syscall.SIGKILL)
- })
-}
-
-func handleError(err error) error {
- if ee, ok := err.(*osexec.ExitError); ok {
- // Force a compile fail if exitErrorWrapper can't convert to ExitError.
- var x ExitError = &ExitErrorWrapper{ee}
- return x
- }
- if ee, ok := err.(*osexec.Error); ok {
- if ee.Err == osexec.ErrNotFound {
- return ErrExecutableNotFound
- }
- }
- return err
-}
-
-// ExitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError.
-// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited().
-type ExitErrorWrapper struct {
- *osexec.ExitError
-}
-
-var _ ExitError = ExitErrorWrapper{}
-
-// ExitStatus is part of the ExitError interface.
-func (eew ExitErrorWrapper) ExitStatus() int {
- ws, ok := eew.Sys().(syscall.WaitStatus)
- if !ok {
- panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper")
- }
- return ws.ExitStatus()
-}
-
-// CodeExitError is an implementation of ExitError consisting of an error object
-// and an exit code (the upper bits of os.exec.ExitStatus).
-type CodeExitError struct {
- Err error
- Code int
-}
-
-var _ ExitError = CodeExitError{}
-
-func (e CodeExitError) Error() string {
- return e.Err.Error()
-}
-
-func (e CodeExitError) String() string {
- return e.Err.Error()
-}
-
-func (e CodeExitError) Exited() bool {
- return true
-}
-
-func (e CodeExitError) ExitStatus() int {
- return e.Code
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go b/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
deleted file mode 100644
index c7fcd6cec..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
+++ /dev/null
@@ -1,145 +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 exec
-
-import (
- "fmt"
- "io"
-)
-
-// A simple scripted Interface type.
-type FakeExec struct {
- CommandScript []FakeCommandAction
- CommandCalls int
- LookPathFunc func(string) (string, error)
-}
-
-type FakeCommandAction func(cmd string, args ...string) Cmd
-
-func (fake *FakeExec) Command(cmd string, args ...string) Cmd {
- if fake.CommandCalls > len(fake.CommandScript)-1 {
- panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
- }
- i := fake.CommandCalls
- fake.CommandCalls++
- return fake.CommandScript[i](cmd, args...)
-}
-
-func (fake *FakeExec) LookPath(file string) (string, error) {
- return fake.LookPathFunc(file)
-}
-
-// A simple scripted Cmd type.
-type FakeCmd struct {
- Argv []string
- CombinedOutputScript []FakeCombinedOutputAction
- CombinedOutputCalls int
- CombinedOutputLog [][]string
- RunScript []FakeRunAction
- RunCalls int
- RunLog [][]string
- Dirs []string
- Stdin io.Reader
- Stdout io.Writer
- Stderr io.Writer
-}
-
-func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd {
- fake.Argv = append([]string{cmd}, args...)
- return fake
-}
-
-type FakeCombinedOutputAction func() ([]byte, error)
-type FakeRunAction func() ([]byte, []byte, error)
-
-func (fake *FakeCmd) SetDir(dir string) {
- fake.Dirs = append(fake.Dirs, dir)
-}
-
-func (fake *FakeCmd) SetStdin(in io.Reader) {
- fake.Stdin = in
-}
-
-func (fake *FakeCmd) SetStdout(out io.Writer) {
- fake.Stdout = out
-}
-
-func (fake *FakeCmd) SetStderr(out io.Writer) {
- fake.Stderr = out
-}
-
-func (fake *FakeCmd) Run() error {
- if fake.RunCalls > len(fake.RunScript)-1 {
- panic("ran out of Run() actions")
- }
- if fake.RunLog == nil {
- fake.RunLog = [][]string{}
- }
- i := fake.RunCalls
- fake.RunLog = append(fake.RunLog, append([]string{}, fake.Argv...))
- fake.RunCalls++
- stdout, stderr, err := fake.RunScript[i]()
- if stdout != nil {
- fake.Stdout.Write(stdout)
- }
- if stderr != nil {
- fake.Stderr.Write(stderr)
- }
- return err
-}
-
-func (fake *FakeCmd) CombinedOutput() ([]byte, error) {
- if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 {
- panic("ran out of CombinedOutput() actions")
- }
- if fake.CombinedOutputLog == nil {
- fake.CombinedOutputLog = [][]string{}
- }
- i := fake.CombinedOutputCalls
- fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...))
- fake.CombinedOutputCalls++
- return fake.CombinedOutputScript[i]()
-}
-
-func (fake *FakeCmd) Output() ([]byte, error) {
- return nil, fmt.Errorf("unimplemented")
-}
-
-func (fake *FakeCmd) Stop() {
- // no-op
-}
-
-// A simple fake ExitError type.
-type FakeExitError struct {
- Status int
-}
-
-func (fake *FakeExitError) String() string {
- return fmt.Sprintf("exit %d", fake.Status)
-}
-
-func (fake *FakeExitError) Error() string {
- return fake.String()
-}
-
-func (fake *FakeExitError) Exited() bool {
- return true
-}
-
-func (fake *FakeExitError) ExitStatus() int {
- return fake.Status
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/file/file.go b/vendor/k8s.io/kubernetes/pkg/util/file/file.go
new file mode 100644
index 000000000..70d26c4ef
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/file/file.go
@@ -0,0 +1,57 @@
+/*
+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 file
+
+import (
+ "os"
+)
+
+// FileExists checks if specified file exists.
+func FileExists(filename string) (bool, error) {
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// FileOrSymlinkExists checks if specified file or symlink exists.
+func FileOrSymlinkExists(filename string) (bool, error) {
+ if _, err := os.Lstat(filename); os.IsNotExist(err) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// ReadDirNoStat returns a string of files/directories contained
+// in dirname without calling lstat on them.
+func ReadDirNoStat(dirname string) ([]string, error) {
+ if dirname == "" {
+ dirname = "."
+ }
+
+ f, err := os.Open(dirname)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return f.Readdirnames(-1)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/io/consistentread.go b/vendor/k8s.io/kubernetes/pkg/util/io/consistentread.go
new file mode 100644
index 000000000..6e1f17b09
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/io/consistentread.go
@@ -0,0 +1,45 @@
+/*
+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 io
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+)
+
+// ConsistentRead repeatedly reads a file until it gets the same content twice.
+// This is useful when reading files in /proc that are larger than page size
+// and kernel may modify them between individual read() syscalls.
+func ConsistentRead(filename string, attempts int) ([]byte, error) {
+ oldContent, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ for i := 0; i < attempts; i++ {
+ newContent, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ if bytes.Compare(oldContent, newContent) == 0 {
+ return newContent, nil
+ }
+ // Files are different, continue reading
+ oldContent = newContent
+ }
+ return nil, fmt.Errorf("could not get consistent content of %s after %d attempts", filename, attempts)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/io/io.go b/vendor/k8s.io/kubernetes/pkg/util/io/io.go
deleted file mode 100644
index 0be8e1272..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/io/io.go
+++ /dev/null
@@ -1,61 +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 io
-
-import (
- "fmt"
- "io/ioutil"
- "os"
-
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/api/v1"
-)
-
-// LoadPodFromFile will read, decode, and return a Pod from a file.
-func LoadPodFromFile(filePath string) (*v1.Pod, error) {
- if filePath == "" {
- return nil, fmt.Errorf("file path not specified")
- }
- podDef, err := ioutil.ReadFile(filePath)
- if err != nil {
- return nil, fmt.Errorf("failed to read file path %s: %+v", filePath, err)
- }
- if len(podDef) == 0 {
- return nil, fmt.Errorf("file was empty: %s", filePath)
- }
- pod := &v1.Pod{}
-
- codec := api.Codecs.LegacyCodec(api.Registry.GroupOrDie(v1.GroupName).GroupVersion)
- if err := runtime.DecodeInto(codec, podDef, pod); err != nil {
- return nil, fmt.Errorf("failed decoding file: %v", err)
- }
- return pod, nil
-}
-
-// SavePodToFile will encode and save a pod to a given path & permissions
-func SavePodToFile(pod *v1.Pod, filePath string, perm os.FileMode) error {
- if filePath == "" {
- return fmt.Errorf("file path not specified")
- }
- codec := api.Codecs.LegacyCodec(api.Registry.GroupOrDie(v1.GroupName).GroupVersion)
- data, err := runtime.Encode(codec, pod)
- if err != nil {
- return fmt.Errorf("failed encoding pod: %v", err)
- }
- return ioutil.WriteFile(filePath, data, perm)
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/io/writer.go b/vendor/k8s.io/kubernetes/pkg/util/io/writer.go
index 086508c90..8d1d9964e 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/io/writer.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/io/writer.go
@@ -21,13 +21,15 @@ import (
"fmt"
"io/ioutil"
"os"
- "os/exec"
+
+ "k8s.io/kubernetes/pkg/util/nsenter"
"github.com/golang/glog"
)
// Writer is an interface which allows to write data to a file.
type Writer interface {
+ // WriteFile mimics ioutil.WriteFile.
WriteFile(filename string, data []byte, perm os.FileMode) error
}
@@ -36,41 +38,37 @@ type Writer interface {
type StdWriter struct {
}
+// WriteFile directly calls ioutil.WriteFile.
func (writer *StdWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
return ioutil.WriteFile(filename, data, perm)
}
-// Alternative implementation of Writer interface that allows writing data to file
-// using nsenter command.
+// NsenterWriter is implementation of Writer interface that allows writing data
+// to file using nsenter command.
// If a program (e.g. kubelet) runs in a container it may want to write data to
// a mounted device. Since in Docker, mount propagation mode is set to private,
// it will not see the mounted device in its own namespace. To work around this
-// limitaion one has to first enter hosts namespace (by using 'nsenter') and only
-// then write data.
-type NsenterWriter struct {
-}
+// limitation one has to first enter hosts namespace (by using 'nsenter') and
+// only then write data.
+type NsenterWriter struct{}
-// TODO: should take a writer, not []byte
+// WriteFile calls 'nsenter cat - > <the file>' and 'nsenter chmod' to create a
+// file on the host.
func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
- cmd := "nsenter"
- base_args := []string{
- "--mount=/rootfs/proc/1/ns/mnt",
- "--",
- }
-
- echo_args := append(base_args, "sh", "-c", fmt.Sprintf("cat > %s", filename))
- glog.V(5).Infof("Command to write data to file: %v %v", cmd, echo_args)
- command := exec.Command(cmd, echo_args...)
- command.Stdin = bytes.NewBuffer(data)
+ ne := nsenter.NewNsenter()
+ echoArgs := []string{"-c", fmt.Sprintf("cat > %s", filename)}
+ glog.V(5).Infof("nsenter: write data to file %s by nsenter", filename)
+ command := ne.Exec("sh", echoArgs)
+ command.SetStdin(bytes.NewBuffer(data))
outputBytes, err := command.CombinedOutput()
if err != nil {
glog.Errorf("Output from writing to %q: %v", filename, string(outputBytes))
return err
}
- chmod_args := append(base_args, "chmod", fmt.Sprintf("%o", perm), filename)
- glog.V(5).Infof("Command to change permissions to file: %v %v", cmd, chmod_args)
- outputBytes, err = exec.Command(cmd, chmod_args...).CombinedOutput()
+ chmodArgs := []string{fmt.Sprintf("%o", perm), filename}
+ glog.V(5).Infof("nsenter: change permissions of file %s to %s", filename, chmodArgs[0])
+ outputBytes, err = ne.Exec("chmod", chmodArgs).CombinedOutput()
if err != nil {
glog.Errorf("Output from chmod command: %v", string(outputBytes))
return err
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/exec.go b/vendor/k8s.io/kubernetes/pkg/util/mount/exec.go
new file mode 100644
index 000000000..716cda0a0
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/exec.go
@@ -0,0 +1,50 @@
+/*
+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
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go
new file mode 100644
index 000000000..b12a2be38
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go
@@ -0,0 +1,140 @@
+// +build linux
+
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+import (
+ "fmt"
+
+ "github.com/golang/glog"
+)
+
+// ExecMounter is a mounter that uses provided Exec interface to mount and
+// unmount a filesystem. For all other calls it uses a wrapped mounter.
+type execMounter struct {
+ wrappedMounter Interface
+ exec Exec
+}
+
+func NewExecMounter(exec Exec, wrapped Interface) Interface {
+ return &execMounter{
+ wrappedMounter: wrapped,
+ exec: exec,
+ }
+}
+
+// execMounter implements mount.Interface
+var _ Interface = &execMounter{}
+
+// Mount runs mount(8) using given exec interface.
+func (m *execMounter) Mount(source string, target string, fstype string, options []string) error {
+ bind, bindRemountOpts := isBind(options)
+
+ if bind {
+ err := m.doExecMount(source, target, fstype, []string{"bind"})
+ if err != nil {
+ return err
+ }
+ return m.doExecMount(source, target, fstype, bindRemountOpts)
+ }
+
+ return m.doExecMount(source, target, fstype, options)
+}
+
+// doExecMount calls exec(mount <what> <where>) using given exec interface.
+func (m *execMounter) doExecMount(source, target, fstype string, options []string) error {
+ glog.V(5).Infof("Exec Mounting %s %s %s %v", source, target, fstype, options)
+ mountArgs := makeMountArgs(source, target, fstype, options)
+ output, err := m.exec.Run("mount", mountArgs...)
+ glog.V(5).Infof("Exec mounted %v: %v: %s", mountArgs, err, string(output))
+ if err != nil {
+ return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n",
+ err, "mount", source, target, fstype, options, string(output))
+ }
+
+ return err
+}
+
+// Unmount runs umount(8) using given exec interface.
+func (m *execMounter) Unmount(target string) error {
+ outputBytes, err := m.exec.Run("umount", target)
+ if err == nil {
+ glog.V(5).Infof("Exec unmounted %s: %s", target, string(outputBytes))
+ } else {
+ glog.V(5).Infof("Failed to exec unmount %s: err: %q, umount output: %s", target, err, string(outputBytes))
+ }
+
+ return err
+}
+
+// List returns a list of all mounted filesystems.
+func (m *execMounter) List() ([]MountPoint, error) {
+ return m.wrappedMounter.List()
+}
+
+// IsLikelyNotMountPoint determines whether a path is a mountpoint.
+func (m *execMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return m.wrappedMounter.IsLikelyNotMountPoint(file)
+}
+
+// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
+// Returns true if open returns errno EBUSY, and false if errno is nil.
+// Returns an error if errno is any error other than EBUSY.
+// Returns with error if pathname is not a device.
+func (m *execMounter) DeviceOpened(pathname string) (bool, error) {
+ return m.wrappedMounter.DeviceOpened(pathname)
+}
+
+// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
+// to a device.
+func (m *execMounter) PathIsDevice(pathname string) (bool, error) {
+ return m.wrappedMounter.PathIsDevice(pathname)
+}
+
+//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
+func (m *execMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return m.wrappedMounter.GetDeviceNameFromMount(mountPath, pluginDir)
+}
+
+func (m *execMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return m.wrappedMounter.IsMountPointMatch(mp, dir)
+}
+
+func (m *execMounter) IsNotMountPoint(dir string) (bool, error) {
+ return m.wrappedMounter.IsNotMountPoint(dir)
+}
+
+func (m *execMounter) MakeRShared(path string) error {
+ return m.wrappedMounter.MakeRShared(path)
+}
+
+func (m *execMounter) GetFileType(pathname string) (FileType, error) {
+ return m.wrappedMounter.GetFileType(pathname)
+}
+
+func (m *execMounter) MakeFile(pathname string) error {
+ return m.wrappedMounter.MakeFile(pathname)
+}
+
+func (m *execMounter) MakeDir(pathname string) error {
+ return m.wrappedMounter.MakeDir(pathname)
+}
+
+func (m *execMounter) ExistsPath(pathname string) bool {
+ return m.wrappedMounter.ExistsPath(pathname)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go
new file mode 100644
index 000000000..136704b23
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go
@@ -0,0 +1,87 @@
+// +build !linux
+
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+import (
+ "errors"
+)
+
+type execMounter struct{}
+
+// ExecMounter is a mounter that uses provided Exec interface to mount and
+// unmount a filesystem. For all other calls it uses a wrapped mounter.
+func NewExecMounter(exec Exec, wrapped Interface) Interface {
+ return &execMounter{}
+}
+
+func (mounter *execMounter) Mount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (mounter *execMounter) Unmount(target string) error {
+ return nil
+}
+
+func (mounter *execMounter) List() ([]MountPoint, error) {
+ return []MountPoint{}, nil
+}
+
+func (mounter *execMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (mounter *execMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, dir)
+}
+
+func (mounter *execMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *execMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return "", nil
+}
+
+func (mounter *execMounter) DeviceOpened(pathname string) (bool, error) {
+ return false, nil
+}
+
+func (mounter *execMounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *execMounter) MakeRShared(path string) error {
+ return nil
+}
+
+func (mounter *execMounter) GetFileType(pathname string) (FileType, error) {
+ return FileType("fake"), errors.New("not implemented")
+}
+
+func (mounter *execMounter) MakeDir(pathname string) error {
+ return nil
+}
+
+func (mounter *execMounter) MakeFile(pathname string) error {
+ return nil
+}
+
+func (mounter *execMounter) ExistsPath(pathname string) bool {
+ return true
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
index 2b71fa0a7..f4e2e411d 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
@@ -17,6 +17,7 @@ limitations under the License.
package mount
import (
+ "os"
"path/filepath"
"sync"
@@ -125,7 +126,7 @@ func (f *FakeMounter) List() ([]MountPoint, error) {
}
func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
- return (mp.Path == dir)
+ return mp.Path == dir
}
func (f *FakeMounter) IsNotMountPoint(dir string) (bool, error) {
@@ -136,6 +137,11 @@ func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
+ _, 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 {
@@ -171,3 +177,23 @@ func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
return getDeviceNameFromMount(f, mountPath, pluginDir)
}
+
+func (f *FakeMounter) MakeRShared(path string) error {
+ return nil
+}
+
+func (f *FakeMounter) GetFileType(pathname string) (FileType, error) {
+ return FileType("fake"), nil
+}
+
+func (f *FakeMounter) MakeDir(pathname string) error {
+ return nil
+}
+
+func (f *FakeMounter) MakeFile(pathname string) error {
+ return nil
+}
+
+func (f *FakeMounter) ExistsPath(pathname string) bool {
+ return false
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
index 0c458d64b..0f9bac03f 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
@@ -19,19 +19,22 @@ limitations under the License.
package mount
import (
- "fmt"
- "path"
+ "os"
"path/filepath"
"strings"
-
- "github.com/golang/glog"
- "k8s.io/kubernetes/pkg/util/exec"
)
+type FileType string
+
const (
// Default mount command if mounter path is not specified
- defaultMountCommand = "mount"
- MountsInGlobalPDPath = "mounts"
+ defaultMountCommand = "mount"
+ MountsInGlobalPDPath = "mounts"
+ FileTypeDirectory FileType = "Directory"
+ FileTypeFile FileType = "File"
+ FileTypeSocket FileType = "Socket"
+ FileTypeCharDev FileType = "CharDevice"
+ FileTypeBlockDev FileType = "BlockDevice"
)
type Interface interface {
@@ -68,6 +71,31 @@ type Interface interface {
// GetDeviceNameFromMount finds the device name by checking the mount path
// to get the global mount path which matches its plugin directory
GetDeviceNameFromMount(mountPath, pluginDir string) (string, error)
+ // MakeRShared checks that given path is on a mount with 'rshared' mount
+ // propagation. If not, it bind-mounts the path as rshared.
+ MakeRShared(path string) error
+ // GetFileType checks for file/directory/socket/block/character devices.
+ // Will operate in the host mount namespace if kubelet is running in a container
+ GetFileType(pathname string) (FileType, error)
+ // MakeFile creates an empty file.
+ // Will operate in the host mount namespace if kubelet is running in a container
+ MakeFile(pathname string) error
+ // MakeDir creates a new directory.
+ // Will operate in the host mount namespace if kubelet is running in a container
+ MakeDir(pathname string) error
+ // ExistsPath checks whether the path exists.
+ // Will operate in the host mount namespace if kubelet is running in a container
+ ExistsPath(pathname string) bool
+}
+
+// Exec executes command where mount utilities are. This can be either the host,
+// container where kubelet runs or even a remote pod with mount utilities.
+// Usual pkg/util/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
@@ -89,7 +117,7 @@ type MountPoint struct {
// mounts it otherwise the device is formatted first then mounted.
type SafeFormatAndMount struct {
Interface
- Runner exec.Interface
+ Exec
}
// FormatAndMount formats the given disk, if needed, and mounts it.
@@ -98,53 +126,35 @@ type SafeFormatAndMount struct {
// disk is already formatted or it is being mounted as read-only, it
// will be mounted without formatting.
func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
- // Don't attempt to format if mounting as readonly. Go straight to mounting.
- for _, option := range options {
- if option == "ro" {
- return mounter.Interface.Mount(source, target, fstype, options)
- }
- }
return mounter.formatAndMount(source, target, fstype, options)
}
-// New returns a mount.Interface for the current system.
-// It provides options to override the default mounter behavior.
-// mounterPath allows using an alternative to `/bin/mount` for mounting.
-func New(mounterPath string) Interface {
- return &Mounter{
- mounterPath: mounterPath,
- }
-}
-
-// GetMountRefs finds all other references to the device referenced
+// GetMountRefsByDev finds all references to the device provided
// by mountPath; returns a list of paths.
-func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+func GetMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
mps, err := mounter.List()
if err != nil {
return nil, err
}
-
- // Find the device name.
- deviceName := ""
- // If mountPath is symlink, need get its target path.
slTarget, err := filepath.EvalSymlinks(mountPath)
if err != nil {
slTarget = mountPath
}
+
+ // Finding the device mounted to mountPath
+ diskDev := ""
for i := range mps {
- if mps[i].Path == slTarget {
- deviceName = mps[i].Device
+ if slTarget == mps[i].Path {
+ diskDev = mps[i].Device
break
}
}
// Find all references to the device.
var refs []string
- if deviceName == "" {
- glog.Warningf("could not determine device for path: %q", mountPath)
- } else {
- for i := range mps {
- if mps[i].Device == deviceName && mps[i].Path != slTarget {
+ for i := range mps {
+ if mps[i].Device == diskDev || mps[i].Device == slTarget {
+ if mps[i].Path != slTarget {
refs = append(refs, mps[i].Path)
}
}
@@ -185,34 +195,6 @@ func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, e
return device, refCount, nil
}
-// getDeviceNameFromMount find the device name from /proc/mounts in which
-// the mount path reference should match the given plugin directory. In case no mount path reference
-// matches, returns the volume name taken from its given mountPath
-func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
- refs, err := GetMountRefs(mounter, mountPath)
- if err != nil {
- glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
- return "", err
- }
- if len(refs) == 0 {
- glog.V(4).Infof("Directory %s is not mounted", mountPath)
- return "", fmt.Errorf("directory %s is not mounted", mountPath)
- }
- basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
- for _, ref := range refs {
- if strings.HasPrefix(ref, basemountPath) {
- volumeID, err := filepath.Rel(basemountPath, ref)
- if err != nil {
- glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
- return "", err
- }
- return volumeID, nil
- }
- }
-
- return path.Base(mountPath), nil
-}
-
// IsNotMountPoint determines if a directory is a mountpoint.
// It should return ErrNotExist when the directory does not exist.
// This method uses the List() of all mountpoints
@@ -222,6 +204,12 @@ 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
}
@@ -243,3 +231,46 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
}
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) {
+ bindRemountOpts := []string{"remount"}
+ bind := false
+
+ if len(options) != 0 {
+ for _, option := range options {
+ switch option {
+ case "bind":
+ bind = true
+ break
+ case "remount":
+ break
+ default:
+ bindRemountOpts = append(bindRemountOpts, option)
+ }
+ }
+ }
+
+ return bind, bindRemountOpts
+}
+
+// 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
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
index 4c141ad5b..11835432d 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
@@ -19,19 +19,21 @@ limitations under the License.
package mount
import (
- "bufio"
+ "errors"
"fmt"
- "hash/fnv"
- "io"
"os"
"os/exec"
+ "path"
+ "path/filepath"
"strconv"
"strings"
"syscall"
"github.com/golang/glog"
+ "golang.org/x/sys/unix"
"k8s.io/apimachinery/pkg/util/sets"
- utilexec "k8s.io/kubernetes/pkg/util/exec"
+ utilio "k8s.io/kubernetes/pkg/util/io"
+ utilexec "k8s.io/utils/exec"
)
const (
@@ -41,9 +43,8 @@ const (
expectedNumFieldsPerLine = 6
// Location of the mount file to use
procMountsPath = "/proc/mounts"
-)
-
-const (
+ // 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
@@ -55,77 +56,154 @@ const (
// 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 emtpy string in case it's not required, e.g. for remount, or for auto filesystem
-// type, where kernel handles fs type for you. The mount 'options' is a list of options,
+// 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 an support bind mounts.
+ // All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
mounterPath := ""
bind, bindRemountOpts := isBind(options)
if bind {
- err := doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"})
+ err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"})
if err != nil {
return err
}
- return doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
+ return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
}
// The list of filesystems that require containerized mounter on GCI image cluster
fsTypesNeedMounter := sets.NewString("nfs", "glusterfs", "ceph", "cifs")
if fsTypesNeedMounter.Has(fstype) {
mounterPath = mounter.mounterPath
}
- return doMount(mounterPath, defaultMountCommand, source, target, fstype, options)
-}
-
-// isBind detects whether a bind mount is being requested and makes the remount options to
-// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
-// The list equals:
-// options - 'bind' + 'remount' (no duplicate)
-func isBind(options []string) (bool, []string) {
- bindRemountOpts := []string{"remount"}
- bind := false
-
- if len(options) != 0 {
- for _, option := range options {
- switch option {
- case "bind":
- bind = true
- break
- case "remount":
- break
- default:
- bindRemountOpts = append(bindRemountOpts, option)
- }
- }
- }
-
- return bind, bindRemountOpts
+ 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 doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error {
+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.
+ }
+
glog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
command := exec.Command(mountCmd, mountArgs...)
output, err := command.CombinedOutput()
if err != nil {
- glog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n", err, mountCmd, source, target, fstype, options, string(output))
- return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n",
- err, mountCmd, source, target, fstype, options, string(output))
+ args := strings.Join(mountArgs, " ")
+ glog.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
}
+// GetMountRefs finds all other references to the device referenced
+// by mountPath; returns a list of paths.
+func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+ mps, err := mounter.List()
+ if err != nil {
+ return nil, err
+ }
+ // Find the device name.
+ deviceName := ""
+ // If mountPath is symlink, need get its target path.
+ slTarget, err := filepath.EvalSymlinks(mountPath)
+ if err != nil {
+ slTarget = mountPath
+ }
+ for i := range mps {
+ if mps[i].Path == slTarget {
+ deviceName = mps[i].Device
+ break
+ }
+ }
+
+ // Find all references to the device.
+ var refs []string
+ if deviceName == "" {
+ glog.Warningf("could not determine device for path: %q", mountPath)
+ } else {
+ for i := range mps {
+ if mps[i].Device == deviceName && mps[i].Path != slTarget {
+ refs = append(refs, mps[i].Path)
+ }
+ }
+ }
+ return refs, nil
+}
+
+// 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 {
+ glog.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 {
+ glog.V(2).Infof("Cannot run systemd-run, assuming non-systemd OS")
+ glog.V(4).Infof("systemd-run failed with: %v", err)
+ glog.V(4).Infof("systemd-run output: %s", string(output))
+ return false
+ }
+ glog.V(2).Infof("Detected OS with systemd")
+ return true
+}
+
// makeMountArgs makes the arguments to the mount(8) command.
func makeMountArgs(source, target, fstype string, options []string) []string {
// Build mount command as follows:
@@ -145,6 +223,13 @@ func makeMountArgs(source, target, fstype string, options []string) []string {
return mountArgs
}
+// addSystemdScope adds "system-run --scope" to given command line
+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 {
glog.V(4).Infof("Unmounting %s", target)
@@ -181,7 +266,7 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
if err != nil {
return true, err
}
- rootStat, err := os.Lstat(file + "/..")
+ rootStat, err := os.Lstat(filepath.Dir(strings.TrimSuffix(file, "/")))
if err != nil {
return true, err
}
@@ -205,29 +290,41 @@ func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
- return pathIsDevice(pathname)
+ pathType, err := mounter.GetFileType(pathname)
+ isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
+ return isDevice, err
}
func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
- isDevice, err := pathIsDevice(pathname)
+ var isDevice bool
+ finfo, err := os.Stat(pathname)
+ if os.IsNotExist(err) {
+ isDevice = false
+ }
+ // err in call to os.Stat
if err != nil {
return false, fmt.Errorf(
"PathIsDevice failed for path %q: %v",
pathname,
err)
}
+ // path refers to a device
+ if finfo.Mode()&os.ModeDevice != 0 {
+ isDevice = true
+ }
+
if !isDevice {
- glog.Errorf("Path %q is not refering to a device.", pathname)
+ glog.Errorf("Path %q is not referring to a device.", pathname)
return false, nil
}
- fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
+ 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 syscall.Close(fd)
+ defer unix.Close(fd)
if errno == nil {
// device not in use
return false, nil
- } else if errno == syscall.EBUSY {
+ } else if errno == unix.EBUSY {
// device is in use
return true, nil
}
@@ -235,121 +332,174 @@ func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
return false, errno
}
-func pathIsDevice(pathname string) (bool, error) {
- finfo, err := os.Stat(pathname)
- if os.IsNotExist(err) {
- return false, nil
- }
- // err in call to os.Stat
- if err != nil {
- return false, err
- }
- // path refers to a device
- if finfo.Mode()&os.ModeDevice != 0 {
- return true, nil
- }
- // path does not refer to device
- return false, nil
-}
-
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginDir)
}
-func listProcMounts(mountFilePath string) ([]MountPoint, error) {
- hash1, err := readProcMounts(mountFilePath, nil)
+// getDeviceNameFromMount find the device name from /proc/mounts in which
+// the mount path reference should match the given plugin directory. In case no mount path reference
+// matches, returns the volume name taken from its given mountPath
+func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
+ refs, err := GetMountRefs(mounter, mountPath)
if err != nil {
- return nil, err
+ glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
+ return "", err
}
-
- for i := 0; i < maxListTries; i++ {
- mps := []MountPoint{}
- hash2, err := readProcMounts(mountFilePath, &mps)
- if err != nil {
- return nil, err
- }
- if hash1 == hash2 {
- // Success
- return mps, nil
+ if len(refs) == 0 {
+ glog.V(4).Infof("Directory %s is not mounted", mountPath)
+ return "", fmt.Errorf("directory %s is not mounted", mountPath)
+ }
+ basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
+ for _, ref := range refs {
+ if strings.HasPrefix(ref, basemountPath) {
+ volumeID, err := filepath.Rel(basemountPath, ref)
+ if err != nil {
+ glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
+ return "", err
+ }
+ return volumeID, nil
}
- hash1 = hash2
}
- return nil, fmt.Errorf("failed to get a consistent snapshot of %v after %d tries", mountFilePath, maxListTries)
+
+ return path.Base(mountPath), nil
}
-// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash
-// of the contents. If the out argument is not nil, this fills it with MountPoint structs.
-func readProcMounts(mountFilePath string, out *[]MountPoint) (uint32, error) {
- file, err := os.Open(mountFilePath)
+func listProcMounts(mountFilePath string) ([]MountPoint, error) {
+ content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
if err != nil {
- return 0, err
+ return nil, err
}
- defer file.Close()
- return readProcMountsFrom(file, out)
+ return parseProcMounts(content)
}
-func readProcMountsFrom(file io.Reader, out *[]MountPoint) (uint32, error) {
- hash := fnv.New32a()
- scanner := bufio.NewReader(file)
- for {
- line, err := scanner.ReadString('\n')
- if err == io.EOF {
- break
+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 0, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
+ return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
}
- fmt.Fprintf(hash, "%s", line)
+ mp := MountPoint{
+ Device: fields[0],
+ Path: fields[1],
+ Type: fields[2],
+ Opts: strings.Split(fields[3], ","),
+ }
- if out != nil {
- mp := MountPoint{
- Device: fields[0],
- Path: fields[1],
- Type: fields[2],
- Opts: strings.Split(fields[3], ","),
- }
+ freq, err := strconv.Atoi(fields[4])
+ if err != nil {
+ return nil, err
+ }
+ mp.Freq = freq
- freq, err := strconv.Atoi(fields[4])
- if err != nil {
- return 0, err
- }
- mp.Freq = freq
+ pass, err := strconv.Atoi(fields[5])
+ if err != nil {
+ return nil, err
+ }
+ mp.Pass = pass
- pass, err := strconv.Atoi(fields[5])
- if err != nil {
- return 0, 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) {
+ var pathType FileType
+ finfo, err := os.Stat(pathname)
+ if os.IsNotExist(err) {
+ return pathType, fmt.Errorf("path %q does not exist", pathname)
+ }
+ // err in call to os.Stat
+ if err != nil {
+ return pathType, err
+ }
+
+ mode := finfo.Sys().(*syscall.Stat_t).Mode
+ switch mode & syscall.S_IFMT {
+ case syscall.S_IFSOCK:
+ return FileTypeSocket, nil
+ case syscall.S_IFBLK:
+ return FileTypeBlockDev, nil
+ case syscall.S_IFCHR:
+ return FileTypeCharDev, nil
+ case syscall.S_IFDIR:
+ return FileTypeDirectory, nil
+ case syscall.S_IFREG:
+ return FileTypeFile, nil
+ }
+
+ return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
+}
+
+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
+}
- *out = append(*out, mp)
+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 hash.Sum32(), nil
+ return nil
+}
+
+func (mounter *Mounter) ExistsPath(pathname string) bool {
+ _, err := os.Stat(pathname)
+ if err != nil {
+ return false
+ }
+ return true
}
// formatAndMount uses unix utils to format and mount the given disk
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")
- // Run fsck on the disk to fix repairable issues
- glog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
- args := []string{"-a", source}
- cmd := mounter.Runner.Command("fsck", args...)
- out, err := cmd.CombinedOutput()
- if err != nil {
- ee, isExitError := err.(utilexec.ExitError)
- switch {
- case err == utilexec.ErrExecutableNotFound:
- glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
- case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
- glog.Infof("Device %s has errors which were corrected by fsck.", source)
- case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
- return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out))
- case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
- glog.Infof("`fsck` error %s", string(out))
+ if !readOnly {
+ // Run fsck on the disk to fix repairable issues, only do this for volumes requested as rw.
+ glog.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:
+ glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
+ case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
+ glog.Infof("Device %s has errors which were corrected by fsck.", source)
+ case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
+ return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out))
+ case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
+ glog.Infof("`fsck` error %s", string(out))
+ }
}
}
@@ -359,13 +509,18 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
if mountErr != nil {
// Mount failed. This indicates either that the disk is unformatted or
// it contains an unexpected filesystem.
- existingFormat, err := mounter.getDiskFormat(source)
+ 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}
+ args := []string{source}
// Use 'ext4' as the default
if len(fstype) == 0 {
fstype = "ext4"
@@ -375,8 +530,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
args = []string{"-F", source}
}
glog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
- cmd := mounter.Runner.Command("mkfs."+fstype, args...)
- _, err := cmd.CombinedOutput()
+ _, err := mounter.Exec.Run("mkfs."+fstype, args...)
if err == nil {
// the disk has been formatted successfully try to mount it again.
glog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
@@ -398,35 +552,149 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
return mountErr
}
-// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated
-func (mounter *SafeFormatAndMount) getDiskFormat(disk string) (string, error) {
- args := []string{"-n", "-o", "FSTYPE", disk}
- cmd := mounter.Runner.Command("lsblk", args...)
- glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
- dataOut, err := cmd.CombinedOutput()
+// GetDiskFormat uses 'blkid' to see if the given disk is unformated
+func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
+ args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk}
+ glog.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)
- glog.V(4).Infof("Output: %q", output)
+ glog.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
+ }
+ }
glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
return "", err
}
- // Split lsblk output into lines. Unformatted devices should contain only
- // "\n". Beware of "\n\n", that's a device with one empty partition.
- output = strings.TrimSuffix(output, "\n") // Avoid last empty line
+ var fstype, pttype string
+
lines := strings.Split(output, "\n")
- if lines[0] != "" {
- // The device is formatted
- return lines[0], nil
+ 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(lines) == 1 {
- // The device is unformatted and has no dependent devices
- return "", nil
+ if len(pttype) > 0 {
+ glog.V(4).Infof("Disk %s detected partition table type: %s", pttype)
+ // Returns a special non-empty string as filesystem type, then kubelet
+ // will not format it.
+ return "unknown data, probably partitions", nil
}
- // The device has dependent devices, most probably partitions (LVM, LUKS
- // and MD RAID are reported as FSTYPE and caught above).
- 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(path string, filename string) (bool, error) {
+ infos, err := parseMountInfo(filename)
+ if err != nil {
+ return false, 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 strings.HasPrefix(path, infos[i].mountPoint) {
+ info = &infos[i]
+ break
+ }
+ }
+ if info == nil {
+ return false, fmt.Errorf("cannot find mount point for %q", path)
+ }
+
+ // parse optional parameters
+ for _, opt := range info.optional {
+ if strings.HasPrefix(opt, "shared:") {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+type mountInfo struct {
+ mountPoint string
+ // list of "optional parameters", mount propagation is one of them
+ optional []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
+ }
+ fields := strings.Fields(line)
+ if len(fields) < 7 {
+ return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 8, len(fields), line)
+ }
+ info := mountInfo{
+ mountPoint: fields[4],
+ optional: []string{},
+ }
+ for i := 6; i < len(fields) && fields[i] != "-"; i++ {
+ info.optional = append(info.optional, fields[i])
+ }
+ infos = append(infos, info)
+ }
+ return infos, 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 {
+ glog.V(4).Infof("Directory %s is already on a shared mount", path)
+ return nil
+ }
+
+ glog.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
}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
index 632ad0606..87d1e3748 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
@@ -1,4 +1,4 @@
-// +build !linux
+// +build !linux,!windows
/*
Copyright 2014 The Kubernetes Authors.
@@ -18,10 +18,23 @@ limitations under the License.
package mount
+import (
+ "errors"
+)
+
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,
+ }
+}
+
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
return nil
}
@@ -30,6 +43,12 @@ func (mounter *Mounter) Unmount(target string) error {
return nil
}
+// GetMountRefs finds all other references to the device referenced
+// by mountPath; returns a list of paths.
+func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+ return []string{}, nil
+}
+
func (mounter *Mounter) List() ([]MountPoint, error) {
return []MountPoint{}, nil
}
@@ -50,6 +69,10 @@ func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (str
return "", nil
}
+func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
+ return "", nil
+}
+
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
@@ -58,10 +81,30 @@ func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
-func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+func (mounter *Mounter) MakeRShared(path string) error {
return nil
}
+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, nil
}
+
+func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
+ return FileType("fake"), errors.New("not implemented")
+}
+
+func (mounter *Mounter) MakeDir(pathname string) error {
+ return nil
+}
+
+func (mounter *Mounter) MakeFile(pathname string) error {
+ return nil
+}
+
+func (mounter *Mounter) ExistsPath(pathname string) bool {
+ return true
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
new file mode 100644
index 000000000..0c63626ea
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
@@ -0,0 +1,346 @@
+// +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"
+ "syscall"
+
+ "github.com/golang/glog"
+)
+
+// 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,
+ }
+}
+
+// Mount : mounts source to target as NTFS with given options.
+func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
+ target = normalizeWindowsPath(target)
+
+ if source == "tmpfs" {
+ glog.V(3).Infof("azureMount: 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
+ }
+
+ glog.V(4).Infof("azureMount: mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
+ options, source, target, fstype)
+ bindSource := ""
+
+ // 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 {
+ glog.Warningf("azureMount: 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("azureMount: only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
+ }
+
+ cmdLine := fmt.Sprintf(`$User = "%s";$PWord = ConvertTo-SecureString -String "%s" -AsPlainText -Force;`+
+ `$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord`,
+ options[0], options[1])
+
+ bindSource = source
+ cmdLine += fmt.Sprintf(";New-SmbGlobalMapping -RemotePath %s -Credential $Credential", source)
+
+ if output, err := exec.Command("powershell", "/c", cmdLine).CombinedOutput(); err != nil {
+ return fmt.Errorf("azureMount: SmbGlobalMapping failed: %v, only SMB mount is supported now, output: %q", err, string(output))
+ }
+ }
+
+ if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
+ glog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
+ return err
+ }
+
+ return nil
+}
+
+// Unmount unmounts the target.
+func (mounter *Mounter) Unmount(target string) error {
+ glog.V(4).Infof("azureMount: Unmount target (%q)", target)
+ target = normalizeWindowsPath(target)
+ if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
+ glog.Errorf("rmdir failed: %v, output: %q", err, string(output))
+ return err
+ }
+ return nil
+}
+
+// GetMountRefs finds all other references to the device(drive) referenced
+// by mountPath; returns a list of paths.
+func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+ refs, err := getAllParentLinks(normalizeWindowsPath(mountPath))
+ if err != nil {
+ return nil, err
+ }
+ return refs, 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
+}
+
+// IsNotMountPoint determines if a directory is a mountpoint.
+func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, 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 {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// GetDeviceNameFromMount given a mnt point, find the device
+func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(mounter, mountPath, pluginDir)
+}
+
+// getDeviceNameFromMount find the device(drive) name in which
+// the mount path reference should match the given plugin directory. In case no mount path reference
+// matches, returns the volume name taken from its given mountPath
+func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
+ refs, err := GetMountRefs(mounter, mountPath)
+ if err != nil {
+ glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
+ return "", err
+ }
+ if len(refs) == 0 {
+ return "", fmt.Errorf("directory %s is not mounted", mountPath)
+ }
+ basemountPath := normalizeWindowsPath(path.Join(pluginDir, MountsInGlobalPDPath))
+ for _, ref := range refs {
+ if strings.Contains(ref, basemountPath) {
+ volumeID, err := filepath.Rel(normalizeWindowsPath(basemountPath), ref)
+ if err != nil {
+ glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
+ return "", err
+ }
+ return volumeID, nil
+ }
+ }
+
+ return path.Base(mountPath), nil
+}
+
+// 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) {
+ var pathType FileType
+ info, err := os.Stat(pathname)
+ if os.IsNotExist(err) {
+ return pathType, fmt.Errorf("path %q does not exist", pathname)
+ }
+ // err in call to os.Stat
+ if err != nil {
+ return pathType, err
+ }
+
+ mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
+ switch mode & syscall.S_IFMT {
+ case syscall.S_IFSOCK:
+ return FileTypeSocket, nil
+ case syscall.S_IFBLK:
+ return FileTypeBlockDev, nil
+ case syscall.S_IFCHR:
+ return FileTypeCharDev, nil
+ case syscall.S_IFDIR:
+ return FileTypeDirectory, nil
+ case syscall.S_IFREG:
+ return FileTypeFile, nil
+ }
+
+ return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
+}
+
+// MakeFile creates a new directory
+func (mounter *Mounter) MakeDir(pathname string) error {
+ err := os.MkdirAll(pathname, os.FileMode(0755))
+ if err != nil {
+ if !os.IsExist(err) {
+ return err
+ }
+ }
+ return nil
+}
+
+// MakeFile creates an empty file
+func (mounter *Mounter) MakeFile(pathname string) error {
+ f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
+ defer f.Close()
+ if err != nil {
+ if !os.IsExist(err) {
+ return err
+ }
+ }
+ return nil
+}
+
+// ExistsPath checks whether the path exists
+func (mounter *Mounter) ExistsPath(pathname string) bool {
+ _, err := os.Stat(pathname)
+ if err != nil {
+ return false
+ }
+ return true
+}
+
+func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+ // Try to mount the disk
+ glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
+
+ if err := ValidateDiskNumber(source); err != nil {
+ glog.Errorf("azureMount: formatAndMount failed, err: %v\n", err)
+ return err
+ }
+
+ driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
+ if err != nil {
+ return err
+ }
+ driverPath := driveLetter + ":"
+ target = normalizeWindowsPath(target)
+ glog.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 {
+ glog.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
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
index 4af8ef0d8..99e81837f 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
@@ -25,74 +25,30 @@ import (
"strings"
"github.com/golang/glog"
- "k8s.io/kubernetes/pkg/util/exec"
+ "k8s.io/kubernetes/pkg/util/nsenter"
)
-// NsenterMounter is part of experimental support for running the kubelet
-// in a container. Currently, all docker containers receive their own mount
-// namespaces. NsenterMounter works by executing nsenter to run commands in
+const (
+ // hostProcMountsPath is the default mount path for rootfs
+ hostProcMountsPath = "/rootfs/proc/1/mounts"
+ // hostProcMountinfoPath is the default mount info path for rootfs
+ hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
+)
+
+// Currently, all docker containers receive their own mount namespaces.
+// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
-//
-// NsenterMounter requires:
-//
-// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
-// of the bind-mount of the kubelet root directory in the container.
-// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
-// performed in the host's mount namespace do not propagate out to the
-// bind-mount in this docker version.
-// 2. The host's root filesystem must be available at /rootfs
-// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
-// filesystem.
-// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
-// the present, this effectively means that the kubelet is running in a
-// privileged container.
-// 5. The volume path used by the Kubelet must be the same inside and outside
-// the container and be writable by the container (to initialize volume)
-// contents. TODO: remove this requirement.
-// 6. The host image must have mount, findmnt, and umount binaries in /bin,
-// /usr/sbin, or /usr/bin
-//
-// For more information about mount propagation modes, see:
-// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
type NsenterMounter struct {
- // a map of commands to their paths on the host filesystem
- paths map[string]string
+ ne *nsenter.Nsenter
}
func NewNsenterMounter() *NsenterMounter {
- m := &NsenterMounter{
- paths: map[string]string{
- "mount": "",
- "findmnt": "",
- "umount": "",
- },
- }
- // search for the mount command in other locations besides /usr/bin
- for binary := range m.paths {
- // default to root
- m.paths[binary] = filepath.Join("/", binary)
- for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
- binPath := filepath.Join(path, binary)
- if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
- continue
- }
- m.paths[binary] = binPath
- break
- }
- // TODO: error, so that the kubelet can stop if the mounts don't exist
- }
- return m
+ return &NsenterMounter{ne: nsenter.NewNsenter()}
}
// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})
-const (
- hostRootFsPath = "/rootfs"
- hostProcMountsPath = "/rootfs/proc/1/mounts"
- nsenterPath = "nsenter"
-)
-
// Mount runs mount(8) in the host's root mount namespace. Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
@@ -112,49 +68,65 @@ func (n *NsenterMounter) Mount(source string, target string, fstype string, opti
// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
- glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options)
- args := n.makeNsenterArgs(source, target, fstype, options)
-
- glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
- exec := exec.New()
- outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
+ cmd, args := n.makeNsenterArgs(source, target, fstype, options)
+ outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
if len(outputBytes) != 0 {
glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
}
-
return err
}
// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
-func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
- nsenterArgs := []string{
- "--mount=/rootfs/proc/1/ns/mnt",
- "--",
- n.absHostPath("mount"),
+func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
+ mountCmd := n.ne.AbsHostPath("mount")
+ mountArgs := makeMountArgs(source, target, fstype, options)
+
+ if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
+ // Complete command line:
+ // nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
+ // Expected flow is:
+ // * nsenter breaks out of container's mount namespace and executes
+ // host's systemd-run.
+ // * 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 container can be restarted and the fuse daemon survives.
+ // * When the daemon dies (e.g. during unmount) systemd removes the
+ // scope automatically.
+ mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs)
+ } else {
+ // Fall back to simple mount when the host has no systemd.
+ // Complete command line:
+ // nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/mount -t <type> <what> <where>
+ // Expected flow is:
+ // * nsenter breaks out of container's mount namespace and executes host's /bin/mount.
+ // * mount does its job, forks a fuse daemon if necessary and finishes.
+ // * Any fuse daemon runs in cgroup of kubelet docker container,
+ // restart of kubelet container will kill it!
+
+ // No code here, mountCmd and mountArgs use /bin/mount
}
- args := makeMountArgs(source, target, fstype, options)
-
- return append(nsenterArgs, args...)
+ return mountCmd, mountArgs
}
// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
- args := []string{
- "--mount=/rootfs/proc/1/ns/mnt",
- "--",
- n.absHostPath("umount"),
- target,
- }
-
- glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
- exec := exec.New()
- outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ args := []string{target}
+ // No need to execute systemd-run here, it's enough that unmount is executed
+ // in the host's mount namespace. It will finish appropriate fuse daemon(s)
+ // running in any scope.
+ glog.V(5).Infof("nsenter unmount args: %v", args)
+ outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
if len(outputBytes) != 0 {
glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
}
-
return err
}
@@ -169,7 +141,7 @@ func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
- return ((mp.Path == dir) || (mp.Path == deletedDir))
+ return (mp.Path == dir) || (mp.Path == deletedDir)
}
// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
@@ -189,20 +161,21 @@ func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
// the first of multiple possible mountpoints using --first-only.
// Also add fstype output to make sure that the output of target file will give the full path
// TODO: Need more refactoring for this function. Track the solution with issue #26996
- args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
- glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)
-
- exec := exec.New()
- out, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
+ glog.V(5).Infof("nsenter findmnt args: %v", args)
+ out, err := n.ne.Exec("findmnt", args).CombinedOutput()
if err != nil {
- glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
+ glog.V(2).Infof("Failed findmnt command for path %s: %s %v", file, out, err)
// Different operating systems behave differently for paths which are not mount points.
// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
// It's safer to assume that it's not a mount point.
return true, nil
}
- mountTarget := strings.Split(string(out), " ")[0]
- mountTarget = strings.TrimSuffix(mountTarget, "\n")
+ mountTarget, err := parseFindMnt(string(out))
+ if err != nil {
+ return false, err
+ }
+
glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)
if mountTarget == file {
@@ -213,6 +186,18 @@ func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
+// parse output of "findmnt -o target,fstype" and return just the target
+func parseFindMnt(out string) (string, error) {
+ // cut trailing newline
+ out = strings.TrimSuffix(out, "\n")
+ // cut everything after the last space - it's the filesystem type
+ i := strings.LastIndex(out, " ")
+ if i == -1 {
+ return "", fmt.Errorf("error parsing findmnt output, expected at least one space: %q", out)
+ }
+ return out[:i], nil
+}
+
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
@@ -224,7 +209,9 @@ func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
- return pathIsDevice(pathname)
+ pathType, err := n.GetFileType(pathname)
+ isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
+ return isDevice, err
}
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
@@ -232,10 +219,54 @@ func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (st
return getDeviceNameFromMount(n, mountPath, pluginDir)
}
-func (n *NsenterMounter) absHostPath(command string) string {
- path, ok := n.paths[command]
- if !ok {
- return command
+func (n *NsenterMounter) MakeRShared(path string) error {
+ return doMakeRShared(path, hostProcMountinfoPath)
+}
+
+func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
+ var pathType FileType
+ outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
+ if err != nil {
+ return pathType, err
+ }
+
+ switch string(outputBytes) {
+ case "socket":
+ return FileTypeSocket, nil
+ case "character special file":
+ return FileTypeCharDev, nil
+ case "block special file":
+ return FileTypeBlockDev, nil
+ case "directory":
+ return FileTypeDirectory, nil
+ case "regular file":
+ return FileTypeFile, nil
+ }
+
+ return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
+}
+
+func (mounter *NsenterMounter) MakeDir(pathname string) error {
+ args := []string{"-p", pathname}
+ if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (mounter *NsenterMounter) MakeFile(pathname string) error {
+ args := []string{pathname}
+ if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
+ args := []string{pathname}
+ _, err := mounter.ne.Exec("ls", args).CombinedOutput()
+ if err == nil {
+ return true
}
- return path
+ return false
}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
index e955e1b78..f4eb692f9 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
@@ -18,6 +18,10 @@ limitations under the License.
package mount
+import (
+ "errors"
+)
+
type NsenterMounter struct{}
func NewNsenterMounter() *NsenterMounter {
@@ -61,3 +65,23 @@ func (*NsenterMounter) PathIsDevice(pathname string) (bool, error) {
func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
return "", nil
}
+
+func (*NsenterMounter) MakeRShared(path string) error {
+ return nil
+}
+
+func (*NsenterMounter) GetFileType(_ string) (FileType, error) {
+ return FileType("fake"), errors.New("not implemented")
+}
+
+func (*NsenterMounter) MakeDir(pathname string) error {
+ return nil
+}
+
+func (*NsenterMounter) MakeFile(pathname string) error {
+ return nil
+}
+
+func (*NsenterMounter) ExistsPath(pathname string) bool {
+ return true
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go b/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go
index 5b6fe933f..90ad58c63 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go
@@ -21,8 +21,10 @@ import (
"strings"
)
+// IPNet maps string to net.IPNet.
type IPNet map[string]*net.IPNet
+// ParseIPNets parses string slice to IPNet.
func ParseIPNets(specs ...string) (IPNet, error) {
ipnetset := make(IPNet)
for _, spec := range specs {
@@ -96,9 +98,9 @@ func (s IPNet) StringSlice() []string {
}
// IsSuperset returns true if and only if s1 is a superset of s2.
-func (s1 IPNet) IsSuperset(s2 IPNet) bool {
+func (s IPNet) IsSuperset(s2 IPNet) bool {
for k := range s2 {
- _, found := s1[k]
+ _, found := s[k]
if !found {
return false
}
@@ -109,8 +111,8 @@ func (s1 IPNet) IsSuperset(s2 IPNet) bool {
// Equal returns true if and only if s1 is equal (as a set) to s2.
// Two sets are equal if their membership is identical.
// (In practice, this means same elements, order doesn't matter)
-func (s1 IPNet) Equal(s2 IPNet) bool {
- return len(s1) == len(s2) && s1.IsSuperset(s2)
+func (s IPNet) Equal(s2 IPNet) bool {
+ return len(s) == len(s2) && s.IsSuperset(s2)
}
// Len returns the size of the set.
diff --git a/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter.go b/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter.go
new file mode 100644
index 000000000..32fbc0848
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter.go
@@ -0,0 +1,124 @@
+// +build linux
+
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nsenter
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "k8s.io/utils/exec"
+
+ "github.com/golang/glog"
+)
+
+const (
+ hostRootFsPath = "/rootfs"
+ // hostProcMountNsPath is the default mount namespace for rootfs
+ hostProcMountNsPath = "/rootfs/proc/1/ns/mnt"
+ // nsenterPath is the default nsenter command
+ nsenterPath = "nsenter"
+)
+
+// Nsenter is part of experimental support for running the kubelet
+// in a container.
+//
+// Nsenter requires:
+//
+// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
+// of the bind-mount of the kubelet root directory in the container.
+// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
+// performed in the host's mount namespace do not propagate out to the
+// bind-mount in this docker version.
+// 2. The host's root filesystem must be available at /rootfs
+// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
+// filesystem.
+// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
+// the present, this effectively means that the kubelet is running in a
+// privileged container.
+// 5. The volume path used by the Kubelet must be the same inside and outside
+// the container and be writable by the container (to initialize volume)
+// contents. TODO: remove this requirement.
+// 6. The host image must have "mount", "findmnt", "umount", "stat", "touch",
+// "mkdir", "ls", "sh" and "chmod" binaries in /bin, /usr/sbin, or /usr/bin
+// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin
+// For more information about mount propagation modes, see:
+// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+type Nsenter struct {
+ // a map of commands to their paths on the host filesystem
+ paths map[string]string
+}
+
+// NewNsenter constructs a new instance of Nsenter
+func NewNsenter() *Nsenter {
+ ne := &Nsenter{
+ paths: map[string]string{
+ "mount": "",
+ "findmnt": "",
+ "umount": "",
+ "systemd-run": "",
+ "stat": "",
+ "touch": "",
+ "mkdir": "",
+ "ls": "",
+ "sh": "",
+ "chmod": "",
+ },
+ }
+ // search for the required commands in other locations besides /usr/bin
+ for binary := range ne.paths {
+ // default to root
+ ne.paths[binary] = filepath.Join("/", binary)
+ for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
+ binPath := filepath.Join(path, binary)
+ if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
+ continue
+ }
+ ne.paths[binary] = binPath
+ break
+ }
+ // TODO: error, so that the kubelet can stop if the paths don't exist
+ // (don't forget that systemd-run is optional)
+ }
+ return ne
+}
+
+// Exec executes nsenter commands in hostProcMountNsPath mount namespace
+func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd {
+ fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
+ append([]string{ne.AbsHostPath(cmd)}, args...)...)
+ glog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
+ exec := exec.New()
+ return exec.Command(nsenterPath, fullArgs...)
+}
+
+// AbsHostPath returns the absolute runnable path for a specified command
+func (ne *Nsenter) AbsHostPath(command string) string {
+ path, ok := ne.paths[command]
+ if !ok {
+ return command
+ }
+ return path
+}
+
+// SupportsSystemd checks whether command systemd-run exists
+func (ne *Nsenter) SupportsSystemd() (string, bool) {
+ systemdRunPath, hasSystemd := ne.paths["systemd-run"]
+ return systemdRunPath, hasSystemd
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter_unsupported.go
new file mode 100644
index 000000000..9c2130cb6
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter_unsupported.go
@@ -0,0 +1,50 @@
+// +build !linux
+
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package nsenter
+
+import (
+ "k8s.io/utils/exec"
+)
+
+// Nsenter is part of experimental support for running the kubelet
+// in a container.
+type Nsenter struct {
+ // a map of commands to their paths on the host filesystem
+ Paths map[string]string
+}
+
+// NewNsenter constructs a new instance of Nsenter
+func NewNsenter() *Nsenter {
+ return &Nsenter{}
+}
+
+// Exec executes nsenter commands in hostProcMountNsPath mount namespace
+func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd {
+ return nil
+}
+
+// AbsHostPath returns the absolute runnable path for a specified command
+func (ne *Nsenter) AbsHostPath(command string) string {
+ return ""
+}
+
+// SupportsSystemd checks whether command systemd-run exists
+func (ne *Nsenter) SupportsSystemd() (string, bool) {
+ return "", false
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go b/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
index 4e70cc682..be35da7cc 100644
--- a/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
+++ b/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
@@ -18,6 +18,10 @@ package parsers
import (
"fmt"
+ // Import the crypto sha256 algorithm for the docker image parser to work
+ _ "crypto/sha256"
+ // Import the crypto/sha512 algorithm for the docker image parser to work with 384 and 512 sha hashes
+ _ "crypto/sha512"
dockerref "github.com/docker/distribution/reference"
)
@@ -29,7 +33,7 @@ const (
// ParseImageName parses a docker image string into three parts: repo, tag and digest.
// If both tag and digest are empty, a default image tag will be returned.
func ParseImageName(image string) (string, string, string, error) {
- named, err := dockerref.ParseNamed(image)
+ named, err := dockerref.ParseNormalizedNamed(image)
if err != nil {
return "", "", "", fmt.Errorf("couldn't parse image name: %v", err)
}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/pointer/pointer.go b/vendor/k8s.io/kubernetes/pkg/util/pointer/pointer.go
new file mode 100644
index 000000000..a970bf7f5
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/pointer/pointer.go
@@ -0,0 +1,68 @@
+/*
+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 pointer
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when,
+// for example, an API struct is handled by plugins which need to distinguish
+// "no plugin accepted this spec" from "this spec is empty".
+//
+// This function is only valid for structs and pointers to structs. Any other
+// type will cause a panic. Passing a typed nil pointer will return true.
+func AllPtrFieldsNil(obj interface{}) bool {
+ v := reflect.ValueOf(obj)
+ if !v.IsValid() {
+ panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
+ }
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return true
+ }
+ v = v.Elem()
+ }
+ for i := 0; i < v.NumField(); i++ {
+ if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
+ return false
+ }
+ }
+ return true
+}
+
+// Int32Ptr returns a pointer to an int32
+func Int32Ptr(i int32) *int32 {
+ o := i
+ return &o
+}
+
+// Int32PtrDerefOr dereference the int32 ptr and returns it i not nil,
+// else returns def.
+func Int32PtrDerefOr(ptr *int32, def int32) int32 {
+ if ptr != nil {
+ return *ptr
+ }
+ return def
+}
+
+// BoolPtr returns a pointer to a bool
+func BoolPtr(b bool) *bool {
+ o := b
+ return &o
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/taints/taints.go b/vendor/k8s.io/kubernetes/pkg/util/taints/taints.go
new file mode 100644
index 000000000..76e4bb866
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/taints/taints.go
@@ -0,0 +1,342 @@
+/*
+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 taints implements utilites for working with taints
+package taints
+
+import (
+ "fmt"
+ "strings"
+
+ "k8s.io/api/core/v1"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/validation"
+ api "k8s.io/kubernetes/pkg/apis/core"
+ "k8s.io/kubernetes/pkg/apis/core/helper"
+)
+
+const (
+ MODIFIED = "modified"
+ TAINTED = "tainted"
+ UNTAINTED = "untainted"
+)
+
+// parseTaint parses a taint from a string. Taint must be of the format '<key>=<value>:<effect>'.
+func parseTaint(st string) (v1.Taint, error) {
+ var taint v1.Taint
+ parts := strings.Split(st, "=")
+ if len(parts) != 2 || len(parts[1]) == 0 || len(validation.IsQualifiedName(parts[0])) > 0 {
+ return taint, fmt.Errorf("invalid taint spec: %v", st)
+ }
+
+ parts2 := strings.Split(parts[1], ":")
+
+ errs := validation.IsValidLabelValue(parts2[0])
+ if len(parts2) != 2 || len(errs) != 0 {
+ return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
+ }
+
+ effect := v1.TaintEffect(parts2[1])
+ if err := validateTaintEffect(effect); err != nil {
+ return taint, err
+ }
+
+ taint.Key = parts[0]
+ taint.Value = parts2[0]
+ taint.Effect = effect
+
+ return taint, nil
+}
+
+func validateTaintEffect(effect v1.TaintEffect) error {
+ if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute {
+ return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect)
+ }
+
+ return nil
+}
+
+// NewTaintsVar wraps []api.Taint in a struct that implements flag.Value to allow taints to be
+// bound to command line flags.
+func NewTaintsVar(ptr *[]api.Taint) taintsVar {
+ return taintsVar{
+ ptr: ptr,
+ }
+}
+
+type taintsVar struct {
+ ptr *[]api.Taint
+}
+
+func (t taintsVar) Set(s string) error {
+ if len(s) == 0 {
+ *t.ptr = nil
+ return nil
+ }
+ sts := strings.Split(s, ",")
+ var taints []api.Taint
+ for _, st := range sts {
+ taint, err := parseTaint(st)
+ if err != nil {
+ return err
+ }
+ taints = append(taints, api.Taint{Key: taint.Key, Value: taint.Value, Effect: api.TaintEffect(taint.Effect)})
+ }
+ *t.ptr = taints
+ return nil
+}
+
+func (t taintsVar) String() string {
+ if len(*t.ptr) == 0 {
+ return ""
+ }
+ var taints []string
+ for _, taint := range *t.ptr {
+ taints = append(taints, fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect))
+ }
+ return strings.Join(taints, ",")
+}
+
+func (t taintsVar) Type() string {
+ return "[]api.Taint"
+}
+
+// ParseTaints takes a spec which is an array and creates slices for new taints to be added, taints to be deleted.
+func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) {
+ var taints, taintsToRemove []v1.Taint
+ uniqueTaints := map[v1.TaintEffect]sets.String{}
+
+ for _, taintSpec := range spec {
+ if strings.Index(taintSpec, "=") != -1 && strings.Index(taintSpec, ":") != -1 {
+ newTaint, err := parseTaint(taintSpec)
+ if err != nil {
+ return nil, nil, err
+ }
+ // validate if taint is unique by <key, effect>
+ if len(uniqueTaints[newTaint.Effect]) > 0 && uniqueTaints[newTaint.Effect].Has(newTaint.Key) {
+ return nil, nil, fmt.Errorf("duplicated taints with the same key and effect: %v", newTaint)
+ }
+ // add taint to existingTaints for uniqueness check
+ if len(uniqueTaints[newTaint.Effect]) == 0 {
+ uniqueTaints[newTaint.Effect] = sets.String{}
+ }
+ uniqueTaints[newTaint.Effect].Insert(newTaint.Key)
+
+ taints = append(taints, newTaint)
+ } else if strings.HasSuffix(taintSpec, "-") {
+ taintKey := taintSpec[:len(taintSpec)-1]
+ var effect v1.TaintEffect
+ if strings.Index(taintKey, ":") != -1 {
+ parts := strings.Split(taintKey, ":")
+ taintKey = parts[0]
+ effect = v1.TaintEffect(parts[1])
+ }
+
+ // If effect is specified, need to validate it.
+ if len(effect) > 0 {
+ err := validateTaintEffect(effect)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintKey, Effect: effect})
+ } else {
+ return nil, nil, fmt.Errorf("unknown taint spec: %v", taintSpec)
+ }
+ }
+ return taints, taintsToRemove, nil
+}
+
+// ReorganizeTaints returns the updated set of taints, taking into account old taints that were not updated,
+// old taints that were updated, old taints that were deleted, and new taints.
+func ReorganizeTaints(node *v1.Node, overwrite bool, taintsToAdd []v1.Taint, taintsToRemove []v1.Taint) (string, []v1.Taint, error) {
+ newTaints := append([]v1.Taint{}, taintsToAdd...)
+ oldTaints := node.Spec.Taints
+ // add taints that already existing but not updated to newTaints
+ added := addTaints(oldTaints, &newTaints)
+ allErrs, deleted := deleteTaints(taintsToRemove, &newTaints)
+ if (added && deleted) || overwrite {
+ return MODIFIED, newTaints, utilerrors.NewAggregate(allErrs)
+ } else if added {
+ return TAINTED, newTaints, utilerrors.NewAggregate(allErrs)
+ }
+ return UNTAINTED, newTaints, utilerrors.NewAggregate(allErrs)
+}
+
+// deleteTaints deletes the given taints from the node's taintlist.
+func deleteTaints(taintsToRemove []v1.Taint, newTaints *[]v1.Taint) ([]error, bool) {
+ allErrs := []error{}
+ var removed bool
+ for _, taintToRemove := range taintsToRemove {
+ removed = false
+ if len(taintToRemove.Effect) > 0 {
+ *newTaints, removed = DeleteTaint(*newTaints, &taintToRemove)
+ } else {
+ *newTaints, removed = DeleteTaintsByKey(*newTaints, taintToRemove.Key)
+ }
+ if !removed {
+ allErrs = append(allErrs, fmt.Errorf("taint %q not found", taintToRemove.ToString()))
+ }
+ }
+ return allErrs, removed
+}
+
+// addTaints adds the newTaints list to existing ones and updates the newTaints List.
+// TODO: This needs a rewrite to take only the new values instead of appended newTaints list to be consistent.
+func addTaints(oldTaints []v1.Taint, newTaints *[]v1.Taint) bool {
+ for _, oldTaint := range oldTaints {
+ existsInNew := false
+ for _, taint := range *newTaints {
+ if taint.MatchTaint(&oldTaint) {
+ existsInNew = true
+ break
+ }
+ }
+ if !existsInNew {
+ *newTaints = append(*newTaints, oldTaint)
+ }
+ }
+ return len(oldTaints) != len(*newTaints)
+}
+
+// CheckIfTaintsAlreadyExists checks if the node already has taints that we want to add and returns a string with taint keys.
+func CheckIfTaintsAlreadyExists(oldTaints []v1.Taint, taints []v1.Taint) string {
+ var existingTaintList = make([]string, 0)
+ for _, taint := range taints {
+ for _, oldTaint := range oldTaints {
+ if taint.Key == oldTaint.Key && taint.Effect == oldTaint.Effect {
+ existingTaintList = append(existingTaintList, taint.Key)
+ }
+ }
+ }
+ return strings.Join(existingTaintList, ",")
+}
+
+// DeleteTaintsByKey removes all the taints that have the same key to given taintKey
+func DeleteTaintsByKey(taints []v1.Taint, taintKey string) ([]v1.Taint, bool) {
+ newTaints := []v1.Taint{}
+ deleted := false
+ for i := range taints {
+ if taintKey == taints[i].Key {
+ deleted = true
+ continue
+ }
+ newTaints = append(newTaints, taints[i])
+ }
+ return newTaints, deleted
+}
+
+// DeleteTaint removes all the the taints that have the same key and effect to given taintToDelete.
+func DeleteTaint(taints []v1.Taint, taintToDelete *v1.Taint) ([]v1.Taint, bool) {
+ newTaints := []v1.Taint{}
+ deleted := false
+ for i := range taints {
+ if taintToDelete.MatchTaint(&taints[i]) {
+ deleted = true
+ continue
+ }
+ newTaints = append(newTaints, taints[i])
+ }
+ return newTaints, deleted
+}
+
+// RemoveTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
+// false otherwise.
+func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
+ newNode := node.DeepCopy()
+ nodeTaints := newNode.Spec.Taints
+ if len(nodeTaints) == 0 {
+ return newNode, false, nil
+ }
+
+ if !TaintExists(nodeTaints, taint) {
+ return newNode, false, nil
+ }
+
+ newTaints, _ := DeleteTaint(nodeTaints, taint)
+ newNode.Spec.Taints = newTaints
+ return newNode, true, nil
+}
+
+// AddOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
+// false otherwise.
+func AddOrUpdateTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
+ newNode := node.DeepCopy()
+ nodeTaints := newNode.Spec.Taints
+
+ var newTaints []v1.Taint
+ updated := false
+ for i := range nodeTaints {
+ if taint.MatchTaint(&nodeTaints[i]) {
+ if helper.Semantic.DeepEqual(*taint, nodeTaints[i]) {
+ return newNode, false, nil
+ }
+ newTaints = append(newTaints, *taint)
+ updated = true
+ continue
+ }
+
+ newTaints = append(newTaints, nodeTaints[i])
+ }
+
+ if !updated {
+ newTaints = append(newTaints, *taint)
+ }
+
+ newNode.Spec.Taints = newTaints
+ return newNode, true, nil
+}
+
+// TaintExists checks if the given taint exists in list of taints. Returns true if exists false otherwise.
+func TaintExists(taints []v1.Taint, taintToFind *v1.Taint) bool {
+ for _, taint := range taints {
+ if taint.MatchTaint(taintToFind) {
+ return true
+ }
+ }
+ return false
+}
+
+func TaintSetDiff(t1, t2 []v1.Taint) (taintsToAdd []*v1.Taint, taintsToRemove []*v1.Taint) {
+ for _, taint := range t1 {
+ if !TaintExists(t2, &taint) {
+ t := taint
+ taintsToAdd = append(taintsToAdd, &t)
+ }
+ }
+
+ for _, taint := range t2 {
+ if !TaintExists(t1, &taint) {
+ t := taint
+ taintsToRemove = append(taintsToRemove, &t)
+ }
+ }
+
+ return
+}
+
+func TaintSetFilter(taints []v1.Taint, fn func(*v1.Taint) bool) []v1.Taint {
+ res := []v1.Taint{}
+
+ for _, taint := range taints {
+ if fn(&taint) {
+ res = append(res, taint)
+ }
+ }
+
+ return res
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/template.go b/vendor/k8s.io/kubernetes/pkg/util/template.go
deleted file mode 100644
index d09d7dc86..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/template.go
+++ /dev/null
@@ -1,48 +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 (
- "bytes"
- "go/doc"
- "io"
- "strings"
- "text/template"
-)
-
-func wrap(indent string, s string) string {
- var buf bytes.Buffer
- doc.ToText(&buf, s, indent, indent+" ", 80-len(indent))
- return buf.String()
-}
-
-// ExecuteTemplate executes templateText with data and output written to w.
-func ExecuteTemplate(w io.Writer, templateText string, data interface{}) error {
- t := template.New("top")
- t.Funcs(template.FuncMap{
- "trim": strings.TrimSpace,
- "wrap": wrap,
- })
- template.Must(t.Parse(templateText))
- return t.Execute(w, data)
-}
-
-func ExecuteTemplateToString(templateText string, data interface{}) (string, error) {
- b := bytes.Buffer{}
- err := ExecuteTemplate(&b, templateText, data)
- return b.String(), err
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/umask.go b/vendor/k8s.io/kubernetes/pkg/util/umask.go
deleted file mode 100644
index 35ccce50b..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/umask.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// +build !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 util
-
-import (
- "syscall"
-)
-
-func Umask(mask int) (old int, err error) {
- return syscall.Umask(mask), nil
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go b/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go
deleted file mode 100644
index 7a1ba1538..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// +build 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 util
-
-import (
- "errors"
-)
-
-func Umask(mask int) (int, error) {
- return 0, errors.New("platform and architecture is not supported")
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/util.go b/vendor/k8s.io/kubernetes/pkg/util/util.go
deleted file mode 100644
index 389e145e8..000000000
--- a/vendor/k8s.io/kubernetes/pkg/util/util.go
+++ /dev/null
@@ -1,140 +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 util
-
-import (
- "fmt"
- "os"
- "reflect"
- "regexp"
-)
-
-// Takes a list of strings and compiles them into a list of regular expressions
-func CompileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
- regexps := []*regexp.Regexp{}
- for _, regexpStr := range regexpStrings {
- r, err := regexp.Compile(regexpStr)
- if err != nil {
- return []*regexp.Regexp{}, err
- }
- regexps = append(regexps, r)
- }
- return regexps, nil
-}
-
-// Detects if using systemd as the init system
-// Please note that simply reading /proc/1/cmdline can be misleading because
-// some installation of various init programs can automatically make /sbin/init
-// a symlink or even a renamed version of their main program.
-// TODO(dchen1107): realiably detects the init system using on the system:
-// systemd, upstart, initd, etc.
-func UsingSystemdInitSystem() bool {
- if _, err := os.Stat("/run/systemd/system"); err == nil {
- return true
- }
-
- return false
-}
-
-// Tests whether all pointer fields in a struct are nil. This is useful when,
-// for example, an API struct is handled by plugins which need to distinguish
-// "no plugin accepted this spec" from "this spec is empty".
-//
-// This function is only valid for structs and pointers to structs. Any other
-// type will cause a panic. Passing a typed nil pointer will return true.
-func AllPtrFieldsNil(obj interface{}) bool {
- v := reflect.ValueOf(obj)
- if !v.IsValid() {
- panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
- }
- if v.Kind() == reflect.Ptr {
- if v.IsNil() {
- return true
- }
- v = v.Elem()
- }
- for i := 0; i < v.NumField(); i++ {
- if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
- return false
- }
- }
- return true
-}
-
-func FileExists(filename string) (bool, error) {
- if _, err := os.Stat(filename); os.IsNotExist(err) {
- return false, nil
- } else if err != nil {
- return false, err
- }
- return true, nil
-}
-
-func FileOrSymlinkExists(filename string) (bool, error) {
- if _, err := os.Lstat(filename); os.IsNotExist(err) {
- return false, nil
- } else if err != nil {
- return false, err
- }
- return true, nil
-}
-
-// ReadDirNoStat returns a string of files/directories contained
-// in dirname without calling lstat on them.
-func ReadDirNoStat(dirname string) ([]string, error) {
- if dirname == "" {
- dirname = "."
- }
-
- f, err := os.Open(dirname)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- return f.Readdirnames(-1)
-}
-
-// IntPtr returns a pointer to an int
-func IntPtr(i int) *int {
- o := i
- return &o
-}
-
-// Int32Ptr returns a pointer to an int32
-func Int32Ptr(i int32) *int32 {
- o := i
- return &o
-}
-
-// IntPtrDerefOr dereference the int ptr and returns it i not nil,
-// else returns def.
-func IntPtrDerefOr(ptr *int, def int) int {
- if ptr != nil {
- return *ptr
- }
- return def
-}
-
-// Int32PtrDerefOr dereference the int32 ptr and returns it i not nil,
-// else returns def.
-func Int32PtrDerefOr(ptr *int32, def int32) int32 {
- if ptr != nil {
- return *ptr
- }
- return def
-}