vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

86
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/BUILD generated vendored Normal file
View File

@ -0,0 +1,86 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"attacher.go",
"attacher-defaults.go",
"detacher.go",
"detacher-defaults.go",
"driver-call.go",
"fake_watcher.go",
"mounter.go",
"mounter-defaults.go",
"plugin.go",
"plugin-defaults.go",
"probe.go",
"unmounter.go",
"unmounter-defaults.go",
"util.go",
"volume.go",
],
importpath = "k8s.io/kubernetes/pkg/volume/flexvolume",
deps = [
"//pkg/util/filesystem:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"attacher_test.go",
"common_test.go",
"detacher_test.go",
"driver-call_test.go",
"flexvolume_test.go",
"mounter_test.go",
"plugin_test.go",
"probe_test.go",
"unmounter_test.go",
],
importpath = "k8s.io/kubernetes/pkg/volume/flexvolume",
library = ":go_default_library",
deps = [
"//pkg/util/filesystem:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/testing:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

15
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/OWNERS generated vendored Normal file
View File

@ -0,0 +1,15 @@
approvers:
- chakri-nelluri
- saad-ali
- MikaelCluseau
reviewers:
- ivan4th
- rata
- sjenning
- saad-ali
- jsafrane
- rootfs
- jingxu97
- msau42
- chakri-nelluri
- MikaelCluseau

View File

@ -0,0 +1,64 @@
/*
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 flexvolume
import (
"time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
)
type attacherDefaults flexVolumeAttacher
// Attach is part of the volume.Attacher interface
func (a *attacherDefaults) Attach(spec *volume.Spec, hostName types.NodeName) (string, error) {
glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default Attach for volume ", spec.Name, ", host ", hostName)
return "", nil
}
// WaitForAttach is part of the volume.Attacher interface
func (a *attacherDefaults) WaitForAttach(spec *volume.Spec, devicePath string, timeout time.Duration) (string, error) {
glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default WaitForAttach for volume ", spec.Name, ", device ", devicePath)
return devicePath, nil
}
// GetDeviceMountPath is part of the volume.Attacher interface
func (a *attacherDefaults) GetDeviceMountPath(spec *volume.Spec, mountsDir string) (string, error) {
return a.plugin.getDeviceMountPath(spec)
}
// MountDevice is part of the volume.Attacher interface
func (a *attacherDefaults) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, mounter mount.Interface) error {
glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default MountDevice for volume ", spec.Name, ", device ", devicePath, ", deviceMountPath ", deviceMountPath)
volSource, readOnly := getVolumeSource(spec)
options := make([]string, 0)
if readOnly {
options = append(options, "ro")
} else {
options = append(options, "rw")
}
diskMounter := &mount.SafeFormatAndMount{Interface: mounter, Exec: a.plugin.host.GetExec(a.plugin.GetPluginName())}
return diskMounter.FormatAndMount(devicePath, deviceMountPath, volSource.FSType, options)
}

View File

@ -0,0 +1,121 @@
/*
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 flexvolume
import (
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume"
)
type flexVolumeAttacher struct {
plugin *flexVolumeAttachablePlugin
}
var _ volume.Attacher = &flexVolumeAttacher{}
// Attach is part of the volume.Attacher interface
func (a *flexVolumeAttacher) Attach(spec *volume.Spec, hostName types.NodeName) (string, error) {
call := a.plugin.NewDriverCall(attachCmd)
call.AppendSpec(spec, a.plugin.host, nil)
call.Append(string(hostName))
status, err := call.Run()
if isCmdNotSupportedErr(err) {
return (*attacherDefaults)(a).Attach(spec, hostName)
} else if err != nil {
return "", err
}
return status.DevicePath, err
}
// WaitForAttach is part of the volume.Attacher interface
func (a *flexVolumeAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod, timeout time.Duration) (string, error) {
call := a.plugin.NewDriverCallWithTimeout(waitForAttachCmd, timeout)
call.Append(devicePath)
call.AppendSpec(spec, a.plugin.host, nil)
status, err := call.Run()
if isCmdNotSupportedErr(err) {
return (*attacherDefaults)(a).WaitForAttach(spec, devicePath, timeout)
} else if err != nil {
return "", err
}
return status.DevicePath, nil
}
// GetDeviceMountPath is part of the volume.Attacher interface
func (a *flexVolumeAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
return a.plugin.getDeviceMountPath(spec)
}
// MountDevice is part of the volume.Attacher interface
func (a *flexVolumeAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
// Mount only once.
alreadyMounted, err := prepareForMount(a.plugin.host.GetMounter(a.plugin.GetPluginName()), deviceMountPath)
if err != nil {
return err
}
if alreadyMounted {
return nil
}
call := a.plugin.NewDriverCall(mountDeviceCmd)
call.Append(deviceMountPath)
call.Append(devicePath)
call.AppendSpec(spec, a.plugin.host, nil)
_, err = call.Run()
if isCmdNotSupportedErr(err) {
// Devicepath is empty if the plugin does not support attach calls. Ignore mountDevice calls if the
// plugin does not implement attach interface.
if devicePath != "" {
return (*attacherDefaults)(a).MountDevice(spec, devicePath, deviceMountPath, a.plugin.host.GetMounter(a.plugin.GetPluginName()))
} else {
return nil
}
}
return err
}
func (a *flexVolumeAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
volumesAttachedCheck := make(map[*volume.Spec]bool)
for _, spec := range specs {
volumesAttachedCheck[spec] = true
call := a.plugin.NewDriverCall(isAttached)
call.AppendSpec(spec, a.plugin.host, nil)
call.Append(string(nodeName))
status, err := call.Run()
if isCmdNotSupportedErr(err) {
return nil, nil
} else if err == nil {
if !status.Attached {
volumesAttachedCheck[spec] = false
glog.V(2).Infof("VolumesAreAttached: check volume (%q) is no longer attached", spec.Name())
}
} else {
return nil, err
}
}
return volumesAttachedCheck, nil
}

View File

@ -0,0 +1,76 @@
/*
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 flexvolume
import (
"testing"
"time"
"k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/volume"
)
func TestAttach(t *testing.T) {
spec := fakeVolumeSpec()
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), attachCmd,
specJson(plugin, spec, nil), "localhost"),
)
a, _ := plugin.NewAttacher()
a.Attach(spec, "localhost")
}
func TestWaitForAttach(t *testing.T) {
spec := fakeVolumeSpec()
var pod *v1.Pod
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), waitForAttachCmd, "/dev/sdx",
specJson(plugin, spec, nil)),
)
a, _ := plugin.NewAttacher()
a.WaitForAttach(spec, "/dev/sdx", pod, 1*time.Second)
}
func TestMountDevice(t *testing.T) {
spec := fakeVolumeSpec()
plugin, rootDir := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), mountDeviceCmd, rootDir+"/mount-dir", "/dev/sdx",
specJson(plugin, spec, nil)),
)
a, _ := plugin.NewAttacher()
a.MountDevice(spec, "/dev/sdx", rootDir+"/mount-dir")
}
func TestIsVolumeAttached(t *testing.T) {
spec := fakeVolumeSpec()
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), isAttached, specJson(plugin, spec, nil), "localhost"),
)
a, _ := plugin.NewAttacher()
specs := []*volume.Spec{spec}
a.VolumesAreAttached(specs, "localhost")
}

View File

@ -0,0 +1,142 @@
/*
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 flexvolume
import (
"encoding/json"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/volume"
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
func testPlugin() (*flexVolumeAttachablePlugin, string) {
rootDir, err := utiltesting.MkTmpdir("flexvolume_test")
if err != nil {
panic("error creating temp dir: " + err.Error())
}
return &flexVolumeAttachablePlugin{
flexVolumePlugin: &flexVolumePlugin{
driverName: "test",
execPath: "/plugin",
host: volumetesting.NewFakeVolumeHost(rootDir, nil, nil),
unsupportedCommands: []string{},
},
}, rootDir
}
func assertDriverCall(t *testing.T, output fakeexec.FakeCombinedOutputAction, expectedCommand string, expectedArgs ...string) fakeexec.FakeCommandAction {
return func(cmd string, args ...string) exec.Cmd {
if cmd != "/plugin/test" {
t.Errorf("Wrong executable called: got %v, expected %v", cmd, "/plugin/test")
}
if args[0] != expectedCommand {
t.Errorf("Wrong command called: got %v, expected %v", args[0], expectedCommand)
}
cmdArgs := args[1:]
if !sameArgs(cmdArgs, expectedArgs) {
t.Errorf("Wrong args for %s: got %v, expected %v", args[0], cmdArgs, expectedArgs)
}
return &fakeexec.FakeCmd{
Argv: args,
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{output},
}
}
}
func fakeRunner(fakeCommands ...fakeexec.FakeCommandAction) exec.Interface {
return &fakeexec.FakeExec{
CommandScript: fakeCommands,
}
}
func fakeResultOutput(result interface{}) fakeexec.FakeCombinedOutputAction {
return func() ([]byte, error) {
bytes, err := json.Marshal(result)
if err != nil {
panic("Unable to marshal result: " + err.Error())
}
return bytes, nil
}
}
func successOutput() fakeexec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil})
}
func notSupportedOutput() fakeexec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil})
}
func sameArgs(args, expectedArgs []string) bool {
if len(args) != len(expectedArgs) {
return false
}
for i, v := range args {
if v != expectedArgs[i] {
return false
}
}
return true
}
func fakeVolumeSpec() *volume.Spec {
vol := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
FlexVolume: &v1.FlexVolumeSource{
Driver: "kubernetes.io/fakeAttacher",
ReadOnly: false,
},
},
}
return volume.NewSpecFromVolume(vol)
}
func fakePersistentVolumeSpec() *volume.Spec {
vol := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol1",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
FlexVolume: &v1.FlexVolumeSource{
Driver: "kubernetes.io/fakeAttacher",
ReadOnly: false,
},
},
},
}
return volume.NewSpecFromPersistentVolume(vol, false)
}
func specJson(plugin *flexVolumeAttachablePlugin, spec *volume.Spec, extraOptions map[string]string) string {
o, err := NewOptionsForDriver(spec, plugin.host, extraOptions)
if err != nil {
panic("Failed to convert spec: " + err.Error())
}
bytes, err := json.Marshal(o)
if err != nil {
panic("Unable to marshal result: " + err.Error())
}
return string(bytes)
}

View File

@ -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 flexvolume
import (
"time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume/util"
)
type detacherDefaults flexVolumeDetacher
// Detach is part of the volume.Detacher interface.
func (d *detacherDefaults) Detach(volumeName string, hostName types.NodeName) error {
glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default Detach for volume ", volumeName, ", host ", hostName)
return nil
}
// WaitForDetach is part of the volume.Detacher interface.
func (d *detacherDefaults) WaitForDetach(devicePath string, timeout time.Duration) error {
glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default WaitForDetach for device ", devicePath)
return nil
}
// UnmountDevice is part of the volume.Detacher interface.
func (d *detacherDefaults) UnmountDevice(deviceMountPath string) error {
glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default UnmountDevice for device mount path ", deviceMountPath)
return util.UnmountPath(deviceMountPath, d.plugin.host.GetMounter(d.plugin.GetPluginName()))
}

View File

@ -0,0 +1,98 @@
/*
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 flexvolume
import (
"fmt"
"os"
"time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)
type flexVolumeDetacher struct {
plugin *flexVolumeAttachablePlugin
}
var _ volume.Detacher = &flexVolumeDetacher{}
// Detach is part of the volume.Detacher interface.
func (d *flexVolumeDetacher) Detach(volumeName string, hostName types.NodeName) error {
call := d.plugin.NewDriverCall(detachCmd)
call.Append(volumeName)
call.Append(string(hostName))
_, err := call.Run()
if isCmdNotSupportedErr(err) {
return (*detacherDefaults)(d).Detach(volumeName, hostName)
}
return err
}
// WaitForDetach is part of the volume.Detacher interface.
func (d *flexVolumeDetacher) WaitForDetach(devicePath string, timeout time.Duration) error {
call := d.plugin.NewDriverCallWithTimeout(waitForDetachCmd, timeout)
call.Append(devicePath)
_, err := call.Run()
if isCmdNotSupportedErr(err) {
return (*detacherDefaults)(d).WaitForDetach(devicePath, timeout)
}
return err
}
// UnmountDevice is part of the volume.Detacher interface.
func (d *flexVolumeDetacher) UnmountDevice(deviceMountPath string) error {
if pathExists, pathErr := util.PathExists(deviceMountPath); pathErr != nil {
return fmt.Errorf("Error checking if path exists: %v", pathErr)
} else if !pathExists {
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", deviceMountPath)
return nil
}
notmnt, err := isNotMounted(d.plugin.host.GetMounter(d.plugin.GetPluginName()), deviceMountPath)
if err != nil {
return err
}
if notmnt {
glog.Warningf("Warning: Path: %v already unmounted", deviceMountPath)
} else {
call := d.plugin.NewDriverCall(unmountDeviceCmd)
call.Append(deviceMountPath)
_, err := call.Run()
if isCmdNotSupportedErr(err) {
err = (*detacherDefaults)(d).UnmountDevice(deviceMountPath)
}
if err != nil {
return err
}
}
// Flexvolume driver may remove the directory. Ignore if it does.
if pathExists, pathErr := util.PathExists(deviceMountPath); pathErr != nil {
return fmt.Errorf("Error checking if path exists: %v", pathErr)
} else if !pathExists {
return nil
}
return os.Remove(deviceMountPath)
}

View File

@ -0,0 +1,43 @@
/*
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 flexvolume
import (
"testing"
)
func TestDetach(t *testing.T) {
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), detachCmd,
"sdx", "localhost"),
)
d, _ := plugin.NewDetacher()
d.Detach("sdx", "localhost")
}
func TestUnmountDevice(t *testing.T) {
plugin, rootDir := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), unmountDeviceCmd,
rootDir+"/mount-dir"),
)
d, _ := plugin.NewDetacher()
d.UnmountDevice(rootDir + "/mount-dir")
}

View File

@ -0,0 +1,249 @@
/*
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 flexvolume
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume"
)
const (
// Driver calls
initCmd = "init"
getVolumeNameCmd = "getvolumename"
isAttached = "isattached"
attachCmd = "attach"
waitForAttachCmd = "waitforattach"
mountDeviceCmd = "mountdevice"
detachCmd = "detach"
waitForDetachCmd = "waitfordetach"
unmountDeviceCmd = "unmountdevice"
mountCmd = "mount"
unmountCmd = "unmount"
// Option keys
optionFSType = "kubernetes.io/fsType"
optionReadWrite = "kubernetes.io/readwrite"
optionKeySecret = "kubernetes.io/secret"
optionFSGroup = "kubernetes.io/fsGroup"
optionMountsDir = "kubernetes.io/mountsDir"
optionPVorVolumeName = "kubernetes.io/pvOrVolumeName"
optionKeyPodName = "kubernetes.io/pod.name"
optionKeyPodNamespace = "kubernetes.io/pod.namespace"
optionKeyPodUID = "kubernetes.io/pod.uid"
optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name"
)
const (
// StatusSuccess represents the successful completion of command.
StatusSuccess = "Success"
// StatusNotSupported represents that the command is not supported.
StatusNotSupported = "Not supported"
)
var (
TimeoutError = fmt.Errorf("Timeout")
)
// DriverCall implements the basic contract between FlexVolume and its driver.
// The caller is responsible for providing the required args.
type DriverCall struct {
Command string
Timeout time.Duration
plugin *flexVolumePlugin
args []string
}
func (plugin *flexVolumePlugin) NewDriverCall(command string) *DriverCall {
return plugin.NewDriverCallWithTimeout(command, 0)
}
func (plugin *flexVolumePlugin) NewDriverCallWithTimeout(command string, timeout time.Duration) *DriverCall {
return &DriverCall{
Command: command,
Timeout: timeout,
plugin: plugin,
args: []string{command},
}
}
func (dc *DriverCall) Append(arg string) {
dc.args = append(dc.args, arg)
}
func (dc *DriverCall) AppendSpec(spec *volume.Spec, host volume.VolumeHost, extraOptions map[string]string) error {
optionsForDriver, err := NewOptionsForDriver(spec, host, extraOptions)
if err != nil {
return err
}
jsonBytes, err := json.Marshal(optionsForDriver)
if err != nil {
return fmt.Errorf("Failed to marshal spec, error: %s", err.Error())
}
dc.Append(string(jsonBytes))
return nil
}
func (dc *DriverCall) Run() (*DriverStatus, error) {
if dc.plugin.isUnsupported(dc.Command) {
return nil, errors.New(StatusNotSupported)
}
execPath := dc.plugin.getExecutable()
cmd := dc.plugin.runner.Command(execPath, dc.args...)
timeout := false
if dc.Timeout > 0 {
timer := time.AfterFunc(dc.Timeout, func() {
timeout = true
cmd.Stop()
})
defer timer.Stop()
}
output, execErr := cmd.CombinedOutput()
if execErr != nil {
if timeout {
return nil, TimeoutError
}
_, err := handleCmdResponse(dc.Command, output)
if err == nil {
glog.Errorf("FlexVolume: driver bug: %s: exec error (%s) but no error in response.", execPath, execErr)
return nil, execErr
}
if isCmdNotSupportedErr(err) {
dc.plugin.unsupported(dc.Command)
} else {
glog.Warningf("FlexVolume: driver call failed: executable: %s, args: %s, error: %s, output: %q", execPath, dc.args, execErr.Error(), output)
}
return nil, err
}
status, err := handleCmdResponse(dc.Command, output)
if err != nil {
if isCmdNotSupportedErr(err) {
dc.plugin.unsupported(dc.Command)
}
return nil, err
}
return status, nil
}
// OptionsForDriver represents the spec given to the driver.
type OptionsForDriver map[string]string
func NewOptionsForDriver(spec *volume.Spec, host volume.VolumeHost, extraOptions map[string]string) (OptionsForDriver, error) {
volSource, readOnly := getVolumeSource(spec)
options := map[string]string{}
options[optionFSType] = volSource.FSType
if readOnly {
options[optionReadWrite] = "ro"
} else {
options[optionReadWrite] = "rw"
}
options[optionPVorVolumeName] = spec.Name()
for key, value := range extraOptions {
options[key] = value
}
for key, value := range volSource.Options {
options[key] = value
}
return OptionsForDriver(options), nil
}
// DriverStatus represents the return value of the driver callout.
type DriverStatus struct {
// Status of the callout. One of "Success", "Failure" or "Not supported".
Status string `json:"status"`
// Reason for success/failure.
Message string `json:"message,omitempty"`
// Path to the device attached. This field is valid only for attach calls.
// ie: /dev/sdx
DevicePath string `json:"device,omitempty"`
// Cluster wide unique name of the volume.
VolumeName string `json:"volumeName,omitempty"`
// Represents volume is attached on the node
Attached bool `json:"attached,omitempty"`
// Returns capabilities of the driver.
// By default we assume all the capabilities are supported.
// If the plugin does not support a capability, it can return false for that capability.
Capabilities *DriverCapabilities `json:",omitempty"`
}
type DriverCapabilities struct {
Attach bool `json:"attach"`
SELinuxRelabel bool `json:"selinuxRelabel"`
}
func defaultCapabilities() *DriverCapabilities {
return &DriverCapabilities{
Attach: true,
SELinuxRelabel: true,
}
}
// isCmdNotSupportedErr checks if the error corresponds to command not supported by
// driver.
func isCmdNotSupportedErr(err error) bool {
if err != nil && err.Error() == StatusNotSupported {
return true
}
return false
}
// handleCmdResponse processes the command output and returns the appropriate
// error code or message.
func handleCmdResponse(cmd string, output []byte) (*DriverStatus, error) {
status := DriverStatus{
Capabilities: defaultCapabilities(),
}
if err := json.Unmarshal(output, &status); err != nil {
glog.Errorf("Failed to unmarshal output for command: %s, output: %q, error: %s", cmd, string(output), err.Error())
return nil, err
} else if status.Status == StatusNotSupported {
glog.V(5).Infof("%s command is not supported by the driver", cmd)
return nil, errors.New(status.Status)
} else if status.Status != StatusSuccess {
errMsg := fmt.Sprintf("%s command failed, status: %s, reason: %s", cmd, status.Status, status.Message)
glog.Errorf(errMsg)
return nil, fmt.Errorf("%s", errMsg)
}
return &status, nil
}

View File

@ -0,0 +1,32 @@
/*
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 flexvolume
import (
"testing"
)
func TestHandleResponseDefaults(t *testing.T) {
ds, err := handleCmdResponse("test", []byte(`{"status": "Success"}`))
if err != nil {
t.Error("error: ", err)
}
if *ds.Capabilities != *defaultCapabilities() {
t.Error("wrong default capabilities: ", *ds.Capabilities)
}
}

View File

@ -0,0 +1,53 @@
/*
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 flexvolume
import (
"github.com/fsnotify/fsnotify"
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)
// Mock filesystem watcher
type fakeWatcher struct {
watches []string // List of watches added by the prober, ordered from least recent to most recent.
eventHandler utilfs.FSEventHandler
}
var _ utilfs.FSWatcher = &fakeWatcher{}
func NewFakeWatcher() *fakeWatcher {
return &fakeWatcher{
watches: nil,
}
}
func (w *fakeWatcher) Init(eventHandler utilfs.FSEventHandler, _ utilfs.FSErrorHandler) error {
w.eventHandler = eventHandler
return nil
}
func (w *fakeWatcher) Run() { /* no-op */ }
func (w *fakeWatcher) AddWatch(path string) error {
w.watches = append(w.watches, path)
return nil
}
// Triggers a mock filesystem event.
func (w *fakeWatcher) TriggerEvent(op fsnotify.Op, filename string) {
w.eventHandler(fsnotify.Event{Op: op, Name: filename})
}

View File

@ -0,0 +1,214 @@
/*
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 flexvolume
import (
"bytes"
"fmt"
"os"
"path"
"testing"
"text/template"
"k8s.io/api/core/v1"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
)
const execScriptTempl1 = `#!/bin/bash
if [ "$1" == "init" -a $# -eq 1 ]; then
echo -n '{
"status": "Success"
}'
exit 0
fi
PATH=$2
if [ "$1" == "attach" -a $# -eq 2 ]; then
echo -n '{
"device": "{{.DevicePath}}",
"status": "Success"
}'
exit 0
elif [ "$1" == "detach" -a $# -eq 2 ]; then
echo -n '{
"status": "Success"
}'
exit 0
elif [ "$1" == "getvolumename" -a $# -eq 4 ]; then
echo -n '{
"status": "Success",
"volume": "fakevolume"
}'
exit 0
elif [ "$1" == "isattached" -a $# -eq 2 ]; then
echo -n '{
"status": "Success",
"attached": true
}'
exit 0
fi
echo -n '{
"status": "Not supported"
}'
exit 1
# Direct the arguments to a file to be tested against later
echo -n $@ &> {{.OutputFile}}
`
const execScriptTempl2 = `#!/bin/bash
if [ "$1" == "init" -a $# -eq 1 ]; then
echo -n '{
"status": "Success"
}'
exit 0
fi
if [ "$1" == "getvolumename" -a $# -eq 2 ]; then
echo -n '{
"status": "Success",
"volumeName": "fakevolume"
}'
exit 0
elif [ "$1" == "mount" -a $# -eq 4 ]; then
PATH=$2
/bin/mkdir -p $PATH
if [ $? -ne 0 ]; then
echo -n '{
"status": "Failure",
"reason": "Failed to create $PATH"
}'
exit 1
fi
echo -n '{
"status": "Success"
}'
exit 0
elif [ "$1" == "unmount" -a $# -eq 2 ]; then
PATH=$2
/bin/rm -r $PATH
if [ $? -ne 0 ]; then
echo -n '{
"status": "Failure",
"reason": "Failed to cleanup $PATH"
}'
exit 1
fi
echo -n '{
"status": "Success"
}'
exit 0
fi
echo -n '{
"status": "Not Supported"
}'
exit 1
# Direct the arguments to a file to be tested against later
echo -n $@ &> {{.OutputFile}}
`
func installPluginUnderTest(t *testing.T, vendorName, plugName, tmpDir string, execScriptTempl string, execTemplateData *map[string]interface{}) {
vendoredName := plugName
if vendorName != "" {
vendoredName = fmt.Sprintf("%s~%s", vendorName, plugName)
}
pluginDir := path.Join(tmpDir, vendoredName)
err := os.MkdirAll(pluginDir, 0777)
if err != nil {
t.Errorf("Failed to create plugin: %v", err)
}
pluginExec := path.Join(pluginDir, plugName)
f, err := os.Create(pluginExec)
if err != nil {
t.Errorf("Failed to install plugin")
}
err = f.Chmod(0777)
if err != nil {
t.Errorf("Failed to set exec perms on plugin")
}
if execTemplateData == nil {
execTemplateData = &map[string]interface{}{
"DevicePath": "/dev/sdx",
"OutputFile": path.Join(pluginDir, plugName+".out"),
}
}
tObj := template.Must(template.New("test").Parse(execScriptTempl))
buf := &bytes.Buffer{}
if err := tObj.Execute(buf, *execTemplateData); err != nil {
t.Errorf("Error in executing script template - %v", err)
}
execScript := buf.String()
_, err = f.WriteString(execScript)
if err != nil {
t.Errorf("Failed to write plugin exec")
}
f.Close()
}
func TestCanSupport(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("flexvolume_test")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil)
plugMgr.InitPlugins(nil, GetDynamicPluginProber(tmpDir), volumetest.NewFakeVolumeHost("fake", nil, nil))
plugin, err := plugMgr.FindPluginByName("flexvolume-kubernetes.io/fakeAttacher")
if err != nil {
t.Fatalf("Can't find the plugin by name")
}
if plugin.GetPluginName() != "flexvolume-kubernetes.io/fakeAttacher" {
t.Errorf("Wrong name: %s", plugin.GetPluginName())
}
if !plugin.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{FlexVolume: &v1.FlexVolumeSource{Driver: "kubernetes.io/fakeAttacher"}}}}) {
t.Errorf("Expected true")
}
if !plugin.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{FlexVolume: &v1.FlexVolumeSource{Driver: "kubernetes.io/fakeAttacher"}}}}}) {
t.Errorf("Expected true")
}
if plugin.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{}}}) {
t.Errorf("Expected false")
}
}
func TestGetAccessModes(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("flexvolume_test")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil)
plugMgr.InitPlugins(nil, GetDynamicPluginProber(tmpDir), volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plugin, err := plugMgr.FindPersistentPluginByName("flexvolume-kubernetes.io/fakeAttacher")
if err != nil {
t.Fatalf("Can't find the plugin by name")
}
if !volumetest.ContainsAccessMode(plugin.GetAccessModes(), v1.ReadWriteOnce) || !volumetest.ContainsAccessMode(plugin.GetAccessModes(), v1.ReadOnlyMany) {
t.Errorf("Expected two AccessModeTypes: %s and %s", v1.ReadWriteOnce, v1.ReadOnlyMany)
}
}

View File

@ -0,0 +1,56 @@
/*
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 flexvolume
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume"
)
type mounterDefaults flexVolumeMounter
// SetUpAt is part of the volume.Mounter interface.
// This implementation relies on the attacher's device mount path and does a bind mount to dir.
func (f *mounterDefaults) SetUpAt(dir string, fsGroup *int64) error {
glog.Warning(logPrefix(f.plugin), "using default SetUpAt to ", dir)
src, err := f.plugin.getDeviceMountPath(f.spec)
if err != nil {
return err
}
if err := doMount(f.mounter, src, dir, "auto", []string{"bind"}); err != nil {
return err
}
return nil
}
// Returns the default volume attributes.
func (f *mounterDefaults) GetAttributes() volume.Attributes {
glog.V(5).Infof(logPrefix(f.plugin), "using default GetAttributes")
return volume.Attributes{
ReadOnly: f.readOnly,
Managed: !f.readOnly,
SupportsSELinux: f.flexVolume.plugin.capabilities.SELinuxRelabel,
}
}
func (f *mounterDefaults) CanMount() error {
return nil
}

View File

@ -0,0 +1,110 @@
/*
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 flexvolume
import (
"os"
"strconv"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/utils/exec"
)
// FlexVolumeMounter is the disk that will be exposed by this plugin.
type flexVolumeMounter struct {
*flexVolume
// Runner used to setup the volume.
runner exec.Interface
// the considered volume spec
spec *volume.Spec
readOnly bool
volume.MetricsNil
}
var _ volume.Mounter = &flexVolumeMounter{}
// Mounter interface
// SetUp creates new directory.
func (f *flexVolumeMounter) SetUp(fsGroup *int64) error {
return f.SetUpAt(f.GetPath(), fsGroup)
}
// SetUpAt creates new directory.
func (f *flexVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
// Mount only once.
alreadyMounted, err := prepareForMount(f.mounter, dir)
if err != nil {
return err
}
if alreadyMounted {
return nil
}
call := f.plugin.NewDriverCall(mountCmd)
// Interface parameters
call.Append(dir)
extraOptions := make(map[string]string)
// pod metadata
extraOptions[optionKeyPodName] = f.podName
extraOptions[optionKeyPodNamespace] = f.podNamespace
extraOptions[optionKeyPodUID] = string(f.podUID)
// service account metadata
extraOptions[optionKeyServiceAccountName] = f.podServiceAccountName
// Extract secret and pass it as options.
if err := addSecretsToOptions(extraOptions, f.spec, f.podNamespace, f.driverName, f.plugin.host); err != nil {
os.Remove(dir)
return err
}
// Implicit parameters
if fsGroup != nil {
extraOptions[optionFSGroup] = strconv.FormatInt(int64(*fsGroup), 10)
}
call.AppendSpec(f.spec, f.plugin.host, extraOptions)
_, err = call.Run()
if isCmdNotSupportedErr(err) {
err = (*mounterDefaults)(f).SetUpAt(dir, fsGroup)
}
if err != nil {
os.Remove(dir)
return err
}
if !f.readOnly {
volume.SetVolumeOwnership(f, fsGroup)
}
return nil
}
// GetAttributes get the flex volume attributes. The attributes will be queried
// using plugin callout after we finalize the callout syntax.
func (f *flexVolumeMounter) GetAttributes() volume.Attributes {
return (*mounterDefaults)(f).GetAttributes()
}
func (f *flexVolumeMounter) CanMount() error {
return nil
}

View File

@ -0,0 +1,72 @@
/*
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 flexvolume
import (
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
)
func TestSetUpAt(t *testing.T) {
spec := fakeVolumeSpec()
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "my-pod",
Namespace: "my-ns",
UID: types.UID("my-uid"),
},
Spec: v1.PodSpec{
ServiceAccountName: "my-sa",
},
}
mounter := &mount.FakeMounter{}
plugin, rootDir := testPlugin()
plugin.unsupportedCommands = []string{"unsupportedCmd"}
plugin.runner = fakeRunner(
// first call without fsGroup
assertDriverCall(t, successOutput(), mountCmd, rootDir+"/mount-dir",
specJson(plugin, spec, map[string]string{
optionKeyPodName: "my-pod",
optionKeyPodNamespace: "my-ns",
optionKeyPodUID: "my-uid",
optionKeyServiceAccountName: "my-sa",
})),
// second test has fsGroup
assertDriverCall(t, notSupportedOutput(), mountCmd, rootDir+"/mount-dir",
specJson(plugin, spec, map[string]string{
optionFSGroup: "42",
optionKeyPodName: "my-pod",
optionKeyPodNamespace: "my-ns",
optionKeyPodUID: "my-uid",
optionKeyServiceAccountName: "my-sa",
})),
assertDriverCall(t, fakeVolumeNameOutput("sdx"), getVolumeNameCmd,
specJson(plugin, spec, nil)),
)
m, _ := plugin.newMounterInternal(spec, pod, mounter, plugin.runner)
m.SetUpAt(rootDir+"/mount-dir", nil)
fsGroup := int64(42)
m.SetUpAt(rootDir+"/mount-dir", &fsGroup)
}

View File

@ -0,0 +1,34 @@
/*
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 flexvolume
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume"
)
type pluginDefaults flexVolumePlugin
func logPrefix(plugin *flexVolumePlugin) string {
return "flexVolume driver " + plugin.driverName + ": "
}
func (plugin *pluginDefaults) GetVolumeName(spec *volume.Spec) (string, error) {
glog.Warning(logPrefix((*flexVolumePlugin)(plugin)), "using default GetVolumeName for volume ", spec.Name())
return spec.Name(), nil
}

View File

@ -0,0 +1,266 @@
/*
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 flexvolume
import (
"fmt"
"path"
"runtime"
"strings"
"sync"
"github.com/golang/glog"
api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/utils/exec"
)
const (
flexVolumePluginName = "kubernetes.io/flexvolume"
flexVolumePluginNamePrefix = "flexvolume-"
)
// FlexVolumePlugin object.
type flexVolumePlugin struct {
driverName string
execPath string
host volume.VolumeHost
runner exec.Interface
sync.Mutex
unsupportedCommands []string
capabilities DriverCapabilities
}
type flexVolumeAttachablePlugin struct {
*flexVolumePlugin
}
var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{}
var _ volume.PersistentVolumePlugin = &flexVolumePlugin{}
type PluginFactory interface {
NewFlexVolumePlugin(pluginDir, driverName string) (volume.VolumePlugin, error)
}
type pluginFactory struct{}
func (pluginFactory) NewFlexVolumePlugin(pluginDir, name string) (volume.VolumePlugin, error) {
execPath := path.Join(pluginDir, name)
driverName := utilstrings.UnescapePluginName(name)
flexPlugin := &flexVolumePlugin{
driverName: driverName,
execPath: execPath,
runner: exec.New(),
unsupportedCommands: []string{},
}
// Initialize the plugin and probe the capabilities
call := flexPlugin.NewDriverCall(initCmd)
ds, err := call.Run()
if err != nil {
return nil, err
}
flexPlugin.capabilities = *ds.Capabilities
if flexPlugin.capabilities.Attach {
// Plugin supports attach/detach, so return flexVolumeAttachablePlugin
return &flexVolumeAttachablePlugin{flexVolumePlugin: flexPlugin}, nil
} else {
return flexPlugin, nil
}
}
// Init is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error {
plugin.host = host
// Hardwired 'success' as any errors from calling init() will be caught by NewFlexVolumePlugin()
return nil
}
func (plugin *flexVolumePlugin) getExecutable() string {
parts := strings.Split(plugin.driverName, "/")
execName := parts[len(parts)-1]
execPath := path.Join(plugin.execPath, execName)
if runtime.GOOS == "windows" {
execPath = volume.GetWindowsPath(execPath)
}
return execPath
}
// Name is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) GetPluginName() string {
return flexVolumePluginNamePrefix + plugin.driverName
}
// GetVolumeName is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
call := plugin.NewDriverCall(getVolumeNameCmd)
call.AppendSpec(spec, plugin.host, nil)
_, err := call.Run()
if isCmdNotSupportedErr(err) {
return (*pluginDefaults)(plugin).GetVolumeName(spec)
} else if err != nil {
return "", err
}
name, err := (*pluginDefaults)(plugin).GetVolumeName(spec)
if err != nil {
return "", err
}
glog.Warning(logPrefix(plugin), "GetVolumeName is not supported yet. Defaulting to PV or volume name: ", name)
return name, nil
}
// CanSupport is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) CanSupport(spec *volume.Spec) bool {
source, _ := getVolumeSource(spec)
return (source != nil) && (source.Driver == plugin.driverName)
}
// RequiresRemount is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) RequiresRemount() bool {
return false
}
// GetAccessModes gets the allowed access modes for this plugin.
func (plugin *flexVolumePlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
return []api.PersistentVolumeAccessMode{
api.ReadWriteOnce,
api.ReadOnlyMany,
}
}
// NewMounter is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
return plugin.newMounterInternal(spec, pod, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner)
}
// newMounterInternal is the internal mounter routine to build the volume.
func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod, mounter mount.Interface, runner exec.Interface) (volume.Mounter, error) {
source, readOnly := getVolumeSource(spec)
return &flexVolumeMounter{
flexVolume: &flexVolume{
driverName: source.Driver,
execPath: plugin.getExecutable(),
mounter: mounter,
plugin: plugin,
podName: pod.Name,
podUID: pod.UID,
podNamespace: pod.Namespace,
podServiceAccountName: pod.Spec.ServiceAccountName,
volName: spec.Name(),
},
runner: runner,
spec: spec,
readOnly: readOnly,
}, nil
}
// NewUnmounter is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner)
}
// newUnmounterInternal is the internal unmounter routine to clean the volume.
func (plugin *flexVolumePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface, runner exec.Interface) (volume.Unmounter, error) {
return &flexVolumeUnmounter{
flexVolume: &flexVolume{
driverName: plugin.driverName,
execPath: plugin.getExecutable(),
mounter: mounter,
plugin: plugin,
podUID: podUID,
volName: volName,
},
runner: runner,
}, nil
}
// NewAttacher is part of the volume.AttachableVolumePlugin interface.
func (plugin *flexVolumeAttachablePlugin) NewAttacher() (volume.Attacher, error) {
return &flexVolumeAttacher{plugin}, nil
}
// NewDetacher is part of the volume.AttachableVolumePlugin interface.
func (plugin *flexVolumeAttachablePlugin) NewDetacher() (volume.Detacher, error) {
return &flexVolumeDetacher{plugin}, nil
}
// ConstructVolumeSpec is part of the volume.AttachableVolumePlugin interface.
func (plugin *flexVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
flexVolume := &api.Volume{
Name: volumeName,
VolumeSource: api.VolumeSource{
FlexVolume: &api.FlexVolumeSource{
Driver: plugin.driverName,
},
},
}
return volume.NewSpecFromVolume(flexVolume), nil
}
func (plugin *flexVolumePlugin) SupportsMountOption() bool {
return false
}
// Mark the given commands as unsupported.
func (plugin *flexVolumePlugin) unsupported(commands ...string) {
plugin.Lock()
defer plugin.Unlock()
plugin.unsupportedCommands = append(plugin.unsupportedCommands, commands...)
}
func (plugin *flexVolumePlugin) SupportsBulkVolumeVerification() bool {
return false
}
// Returns true iff the given command is known to be unsupported.
func (plugin *flexVolumePlugin) isUnsupported(command string) bool {
plugin.Lock()
defer plugin.Unlock()
for _, unsupportedCommand := range plugin.unsupportedCommands {
if command == unsupportedCommand {
return true
}
}
return false
}
func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
return mount.GetMountRefs(mounter, deviceMountPath)
}
func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, error) {
volumeName, err := plugin.GetVolumeName(spec)
if err != nil {
return "", fmt.Errorf("GetVolumeName failed from getDeviceMountPath: %s", err)
}
mountsDir := path.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts")
return path.Join(mountsDir, volumeName), nil
}

View File

@ -0,0 +1,56 @@
/*
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 flexvolume
import (
"testing"
exec "k8s.io/utils/exec/testing"
)
func TestInit(t *testing.T) {
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, successOutput(), "init"),
)
plugin.Init(plugin.host)
}
func fakeVolumeNameOutput(name string) exec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{
Status: StatusSuccess,
VolumeName: name,
})
}
func TestGetVolumeName(t *testing.T) {
spec := fakeVolumeSpec()
plugin, _ := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, fakeVolumeNameOutput(spec.Name()), getVolumeNameCmd,
specJson(plugin, spec, nil)),
)
name, err := plugin.GetVolumeName(spec)
if err != nil {
t.Errorf("GetVolumeName() failed: %v", err)
}
expectedName := spec.Name()
if name != expectedName {
t.Errorf("GetVolumeName() returned %v instead of %v", name, expectedName)
}
}

234
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/probe.go generated vendored Normal file
View File

@ -0,0 +1,234 @@
/*
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 flexvolume
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume"
"os"
"fmt"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"k8s.io/apimachinery/pkg/util/errors"
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)
type flexVolumeProber struct {
mutex sync.Mutex
pluginDir string // Flexvolume driver directory
watcher utilfs.FSWatcher
probeNeeded bool // Must only read and write this through testAndSetProbeNeeded.
lastUpdated time.Time // Last time probeNeeded was updated.
watchEventCount int
factory PluginFactory
fs utilfs.Filesystem
}
const (
// TODO (cxing) Tune these params based on test results.
// watchEventLimit is the max allowable number of processed watches within watchEventInterval.
watchEventInterval = 5 * time.Second
watchEventLimit = 20
)
func GetDynamicPluginProber(pluginDir string) volume.DynamicPluginProber {
return &flexVolumeProber{
pluginDir: pluginDir,
watcher: utilfs.NewFsnotifyWatcher(),
factory: pluginFactory{},
fs: &utilfs.DefaultFs{},
}
}
func (prober *flexVolumeProber) Init() error {
prober.testAndSetProbeNeeded(true)
prober.lastUpdated = time.Now()
if err := prober.createPluginDir(); err != nil {
return err
}
if err := prober.initWatcher(); err != nil {
return err
}
return nil
}
// Probes for Flexvolume drivers.
// If a filesystem update has occurred since the last probe, updated = true
// and the list of probed plugins is returned.
// Otherwise, update = false and probedPlugins = nil.
//
// If an error occurs, updated and plugins are set arbitrarily.
func (prober *flexVolumeProber) Probe() (updated bool, plugins []volume.VolumePlugin, err error) {
probeNeeded := prober.testAndSetProbeNeeded(false)
if !probeNeeded {
return false, nil, nil
}
files, err := prober.fs.ReadDir(prober.pluginDir)
if err != nil {
return false, nil, fmt.Errorf("Error reading the Flexvolume directory: %s", err)
}
plugins = []volume.VolumePlugin{}
allErrs := []error{}
for _, f := range files {
// only directories with names that do not begin with '.' are counted as plugins
// and pluginDir/dirname/dirname should be an executable
// unless dirname contains '~' for escaping namespace
// e.g. dirname = vendor~cifs
// then, executable will be pluginDir/dirname/cifs
if f.IsDir() && filepath.Base(f.Name())[0] != '.' {
plugin, pluginErr := prober.factory.NewFlexVolumePlugin(prober.pluginDir, f.Name())
if pluginErr != nil {
pluginErr = fmt.Errorf(
"Error creating Flexvolume plugin from directory %s, skipping. Error: %s",
f.Name(), pluginErr)
allErrs = append(allErrs, pluginErr)
continue
}
plugins = append(plugins, plugin)
}
}
return true, plugins, errors.NewAggregate(allErrs)
}
func (prober *flexVolumeProber) handleWatchEvent(event fsnotify.Event) error {
// event.Name is the watched path.
if filepath.Base(event.Name)[0] == '.' {
// Ignore files beginning with '.'
return nil
}
eventPathAbs, err := filepath.Abs(event.Name)
if err != nil {
return err
}
pluginDirAbs, err := filepath.Abs(prober.pluginDir)
if err != nil {
return err
}
// If the Flexvolume plugin directory is removed, need to recreate it
// in order to keep it under watch.
if eventOpIs(event, fsnotify.Remove) && eventPathAbs == pluginDirAbs {
if err := prober.createPluginDir(); err != nil {
return err
}
if err := prober.addWatchRecursive(pluginDirAbs); err != nil {
return err
}
} else if eventOpIs(event, fsnotify.Create) {
if err := prober.addWatchRecursive(eventPathAbs); err != nil {
return err
}
}
prober.updateProbeNeeded()
return nil
}
func (prober *flexVolumeProber) updateProbeNeeded() {
// Within 'watchEventInterval' seconds, a max of 'watchEventLimit' watch events is processed.
// The watch event will not be registered if the limit is reached.
// This prevents increased disk usage from Probe() being triggered too frequently (either
// accidentally or maliciously).
if time.Since(prober.lastUpdated) > watchEventInterval {
// Update, then reset the timer and watch count.
prober.testAndSetProbeNeeded(true)
prober.lastUpdated = time.Now()
prober.watchEventCount = 1
} else if prober.watchEventCount < watchEventLimit {
prober.testAndSetProbeNeeded(true)
prober.watchEventCount++
}
}
// Recursively adds to watch all directories inside and including the file specified by the given filename.
// If the file is a symlink to a directory, it will watch the symlink but not any of the subdirectories.
//
// Each file or directory change triggers two events: one from the watch on itself, another from the watch
// on its parent directory.
func (prober *flexVolumeProber) addWatchRecursive(filename string) error {
addWatch := func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
if err := prober.watcher.AddWatch(path); err != nil {
glog.Errorf("Error recursively adding watch: %v", err)
}
}
return nil
}
return prober.fs.Walk(filename, addWatch)
}
// Creates a new filesystem watcher and adds watches for the plugin directory
// and all of its subdirectories.
func (prober *flexVolumeProber) initWatcher() error {
err := prober.watcher.Init(func(event fsnotify.Event) {
if err := prober.handleWatchEvent(event); err != nil {
glog.Errorf("Flexvolume prober watch: %s", err)
}
}, func(err error) {
glog.Errorf("Received an error from watcher: %s", err)
})
if err != nil {
return fmt.Errorf("Error initializing watcher: %s", err)
}
if err := prober.addWatchRecursive(prober.pluginDir); err != nil {
return fmt.Errorf("Error adding watch on Flexvolume directory: %s", err)
}
prober.watcher.Run()
return nil
}
// Creates the plugin directory, if it doesn't already exist.
func (prober *flexVolumeProber) createPluginDir() error {
if _, err := prober.fs.Stat(prober.pluginDir); os.IsNotExist(err) {
glog.Warningf("Flexvolume plugin directory at %s does not exist. Recreating.", prober.pluginDir)
err := prober.fs.MkdirAll(prober.pluginDir, 0755)
if err != nil {
return fmt.Errorf("Error (re-)creating driver directory: %s", err)
}
}
return nil
}
func (prober *flexVolumeProber) testAndSetProbeNeeded(newval bool) (oldval bool) {
prober.mutex.Lock()
defer prober.mutex.Unlock()
oldval, prober.probeNeeded = prober.probeNeeded, newval
return
}
func eventOpIs(event fsnotify.Event, op fsnotify.Op) bool {
return event.Op&op == op
}

View File

@ -0,0 +1,274 @@
/*
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 flexvolume
import (
"fmt"
"path"
"testing"
"github.com/fsnotify/fsnotify"
"github.com/stretchr/testify/assert"
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
"k8s.io/kubernetes/pkg/volume"
)
const (
pluginDir = "/flexvolume"
driverName = "fake-driver"
)
// Probes a driver installed before prober initialization.
func TestProberExistingDriverBeforeInit(t *testing.T) {
// Arrange
driverPath, _, watcher, prober := initTestEnvironment(t)
// Act
updated, plugins, err := prober.Probe()
// Assert
// Probe occurs, 1 plugin should be returned, and 2 watches (pluginDir and all its
// current subdirectories) registered.
assert.True(t, updated)
assert.Equal(t, 1, len(plugins))
assert.Equal(t, pluginDir, watcher.watches[0])
assert.Equal(t, driverPath, watcher.watches[1])
assert.NoError(t, err)
// Should no longer probe.
// Act
updated, plugins, err = prober.Probe()
// Assert
assert.False(t, updated)
assert.Equal(t, 0, len(plugins))
assert.NoError(t, err)
}
// Probes newly added drivers after prober is running.
func TestProberAddDriver(t *testing.T) {
// Arrange
_, fs, watcher, prober := initTestEnvironment(t)
prober.Probe()
updated, _, _ := prober.Probe()
assert.False(t, updated)
// Call probe after a file is added. Should return true.
// Arrange
const driverName2 = "fake-driver2"
driverPath := path.Join(pluginDir, driverName2)
installDriver(driverName2, fs)
watcher.TriggerEvent(fsnotify.Create, driverPath)
watcher.TriggerEvent(fsnotify.Create, path.Join(driverPath, driverName2))
// Act
updated, plugins, err := prober.Probe()
// Assert
assert.True(t, updated)
assert.Equal(t, 2, len(plugins)) // 1 existing, 1 newly added
assert.Equal(t, driverPath, watcher.watches[len(watcher.watches)-1]) // Checks most recent watch
assert.NoError(t, err)
// Call probe again, should return false.
// Act
updated, _, err = prober.Probe()
// Assert
assert.False(t, updated)
assert.NoError(t, err)
// Call probe after a non-driver file is added in a subdirectory. Should return true.
// Arrange
fp := path.Join(driverPath, "dummyfile")
fs.Create(fp)
watcher.TriggerEvent(fsnotify.Create, fp)
// Act
updated, plugins, err = prober.Probe()
// Assert
assert.True(t, updated)
assert.Equal(t, 2, len(plugins)) // Number of plugins should not change.
assert.NoError(t, err)
// Call probe again, should return false.
// Act
updated, _, err = prober.Probe()
// Assert
assert.False(t, updated)
assert.NoError(t, err)
}
// Tests the behavior when no drivers exist in the plugin directory.
func TestEmptyPluginDir(t *testing.T) {
// Arrange
fs := utilfs.NewFakeFs()
watcher := NewFakeWatcher()
prober := &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{error: false},
}
prober.Init()
// Act
updated, plugins, err := prober.Probe()
// Assert
assert.True(t, updated)
assert.Equal(t, 0, len(plugins))
assert.NoError(t, err)
}
// Issue an event to remove plugindir. New directory should still be watched.
func TestRemovePluginDir(t *testing.T) {
// Arrange
driverPath, fs, watcher, _ := initTestEnvironment(t)
fs.RemoveAll(pluginDir)
watcher.TriggerEvent(fsnotify.Remove, path.Join(driverPath, driverName))
watcher.TriggerEvent(fsnotify.Remove, driverPath)
watcher.TriggerEvent(fsnotify.Remove, pluginDir)
// Act: The handler triggered by the above events should have already handled the event appropriately.
// Assert
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
assert.Equal(t, pluginDir, watcher.watches[len(watcher.watches)-1])
}
// Issue multiple events and probe multiple times. Should give true, false, false...
func TestProberMultipleEvents(t *testing.T) {
const iterations = 5
// Arrange
_, fs, watcher, prober := initTestEnvironment(t)
for i := 0; i < iterations; i++ {
newDriver := fmt.Sprintf("multi-event-driver%d", 1)
installDriver(newDriver, fs)
driverPath := path.Join(pluginDir, newDriver)
watcher.TriggerEvent(fsnotify.Create, driverPath)
watcher.TriggerEvent(fsnotify.Create, path.Join(driverPath, newDriver))
}
// Act
updated, _, err := prober.Probe()
// Assert
assert.True(t, updated)
assert.NoError(t, err)
for i := 0; i < iterations-1; i++ {
updated, _, err = prober.Probe()
assert.False(t, updated)
assert.NoError(t, err)
}
}
// When many events are triggered quickly in succession, events should stop triggering a probe update
// after a certain limit.
func TestProberRateLimit(t *testing.T) {
// Arrange
driverPath, _, watcher, prober := initTestEnvironment(t)
for i := 0; i < watchEventLimit; i++ {
watcher.TriggerEvent(fsnotify.Write, path.Join(driverPath, driverName))
}
// Act
updated, plugins, err := prober.Probe()
// Assert
// The probe results should not be different from what it would be if none of the events
// are triggered.
assert.True(t, updated)
assert.Equal(t, 1, len(plugins))
assert.NoError(t, err)
// Arrange
watcher.TriggerEvent(fsnotify.Write, path.Join(driverPath, driverName))
// Act
updated, _, err = prober.Probe()
// Assert
// The last event is outside the event limit. Should not trigger a probe.
assert.False(t, updated)
assert.NoError(t, err)
}
func TestProberError(t *testing.T) {
fs := utilfs.NewFakeFs()
watcher := NewFakeWatcher()
prober := &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{error: true},
}
installDriver(driverName, fs)
prober.Init()
_, _, err := prober.Probe()
assert.Error(t, err)
}
// Installs a mock driver (an empty file) in the mock fs.
func installDriver(driverName string, fs utilfs.Filesystem) {
driverPath := path.Join(pluginDir, driverName)
fs.MkdirAll(driverPath, 0666)
fs.Create(path.Join(driverPath, driverName))
}
// Initializes mocks, installs a single driver in the mock fs, then initializes prober.
func initTestEnvironment(t *testing.T) (
driverPath string,
fs utilfs.Filesystem,
watcher *fakeWatcher,
prober volume.DynamicPluginProber) {
fs = utilfs.NewFakeFs()
watcher = NewFakeWatcher()
prober = &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{error: false},
}
driverPath = path.Join(pluginDir, driverName)
installDriver(driverName, fs)
prober.Init()
assert.NotNilf(t, watcher.eventHandler,
"Expect watch event handler to be registered after prober init, but is not.")
return
}
// Fake Flexvolume plugin
type fakePluginFactory struct {
error bool // Indicates whether an error should be returned.
}
var _ PluginFactory = fakePluginFactory{}
func (m fakePluginFactory) NewFlexVolumePlugin(_, driverName string) (volume.VolumePlugin, error) {
if m.error {
return nil, fmt.Errorf("Flexvolume plugin error")
}
// Dummy Flexvolume plugin. Prober never interacts with the plugin.
return &flexVolumePlugin{driverName: driverName}, nil
}

View File

@ -0,0 +1,29 @@
/*
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 flexvolume
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume/util"
)
type unmounterDefaults flexVolumeUnmounter
func (f *unmounterDefaults) TearDownAt(dir string) error {
glog.Warning(logPrefix(f.plugin), "using default TearDownAt for ", dir)
return util.UnmountPath(dir, f.mounter)
}

View File

@ -0,0 +1,71 @@
/*
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 flexvolume
import (
"fmt"
"os"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
"k8s.io/utils/exec"
)
// FlexVolumeUnmounter is the disk that will be cleaned by this plugin.
type flexVolumeUnmounter struct {
*flexVolume
// Runner used to teardown the volume.
runner exec.Interface
volume.MetricsNil
}
var _ volume.Unmounter = &flexVolumeUnmounter{}
// Unmounter interface
func (f *flexVolumeUnmounter) TearDown() error {
path := f.GetPath()
return f.TearDownAt(path)
}
func (f *flexVolumeUnmounter) TearDownAt(dir string) error {
if pathExists, pathErr := util.PathExists(dir); pathErr != nil {
return fmt.Errorf("Error checking if path exists: %v", pathErr)
} else if !pathExists {
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", dir)
return nil
}
call := f.plugin.NewDriverCall(unmountCmd)
call.Append(dir)
_, err := call.Run()
if isCmdNotSupportedErr(err) {
err = (*unmounterDefaults)(f).TearDownAt(dir)
}
if err != nil {
return err
}
// Flexvolume driver may remove the directory. Ignore if it does.
if pathExists, pathErr := util.PathExists(dir); pathErr != nil {
return fmt.Errorf("Error checking if path exists: %v", pathErr)
} else if !pathExists {
return nil
}
return os.Remove(dir)
}

View File

@ -0,0 +1,37 @@
/*
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 flexvolume
import (
"testing"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
)
func TestTearDownAt(t *testing.T) {
mounter := &mount.FakeMounter{}
plugin, rootDir := testPlugin()
plugin.runner = fakeRunner(
assertDriverCall(t, notSupportedOutput(), unmountCmd,
rootDir+"/mount-dir"),
)
u, _ := plugin.newUnmounterInternal("volName", types.UID("poduid"), mounter, plugin.runner)
u.TearDownAt(rootDir + "/mount-dir")
}

101
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/util.go generated vendored Normal file
View File

@ -0,0 +1,101 @@
/*
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 flexvolume
import (
"encoding/base64"
"fmt"
"os"
"github.com/golang/glog"
api "k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)
func addSecretsToOptions(options map[string]string, spec *volume.Spec, namespace string, driverName string, host volume.VolumeHost) error {
fv, _ := getVolumeSource(spec)
if fv.SecretRef == nil {
return nil
}
kubeClient := host.GetKubeClient()
if kubeClient == nil {
return fmt.Errorf("Cannot get kube client")
}
secrets, err := util.GetSecretForPV(namespace, fv.SecretRef.Name, driverName, host.GetKubeClient())
if err != nil {
err = fmt.Errorf("Couldn't get secret %v/%v err: %v", namespace, fv.SecretRef.Name, err)
return err
}
for name, data := range secrets {
options[optionKeySecret+"/"+name] = base64.StdEncoding.EncodeToString([]byte(data))
glog.V(1).Infof("found flex volume secret info: %s", name)
}
return nil
}
func getVolumeSource(spec *volume.Spec) (volumeSource *api.FlexVolumeSource, readOnly bool) {
if spec.Volume != nil && spec.Volume.FlexVolume != nil {
volumeSource = spec.Volume.FlexVolume
readOnly = volumeSource.ReadOnly
} else if spec.PersistentVolume != nil {
volumeSource = spec.PersistentVolume.Spec.FlexVolume
readOnly = spec.ReadOnly
}
return
}
func prepareForMount(mounter mount.Interface, deviceMountPath string) (bool, error) {
notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
return false, err
}
notMnt = true
} else {
return false, err
}
}
return !notMnt, nil
}
// Mounts the device at the given path.
// It is expected that prepareForMount has been called before.
func doMount(mounter mount.Interface, devicePath, deviceMountPath, fsType string, options []string) error {
err := mounter.Mount(devicePath, deviceMountPath, fsType, options)
if err != nil {
glog.Errorf("Failed to mount the volume at %s, device: %s, error: %s", deviceMountPath, devicePath, err.Error())
return err
}
return nil
}
func isNotMounted(mounter mount.Interface, deviceMountPath string) (bool, error) {
notmnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
if err != nil {
glog.Errorf("Error checking mount point %s, error: %v", deviceMountPath, err)
return false, err
}
return notmnt, nil
}

View File

@ -0,0 +1,52 @@
/*
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 flexvolume
import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
)
type flexVolume struct {
// driverName is the name of the plugin driverName.
driverName string
// Driver executable used to setup the volume.
execPath string
// mounter provides the interface that is used to mount the actual
// block device.
mounter mount.Interface
// podName is the name of the pod, if available.
podName string
// podUID is the UID of the pod.
podUID types.UID
// podNamespace is the namespace of the pod, if available.
podNamespace string
// podServiceAccountName is the service account name of the pod, if available.
podServiceAccountName string
// volName is the name of the pod's volume.
volName string
// the underlying plugin
plugin *flexVolumePlugin
}
// volume.Volume interface
func (f *flexVolume) GetPath() string {
name := f.driverName
return f.plugin.host.GetPodVolumeDir(f.podUID, utilstrings.EscapeQualifiedNameForDisk(name), f.volName)
}