mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor files
This commit is contained in:
86
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/BUILD
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/BUILD
generated
vendored
Normal 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
15
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/OWNERS
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
approvers:
|
||||
- chakri-nelluri
|
||||
- saad-ali
|
||||
- MikaelCluseau
|
||||
reviewers:
|
||||
- ivan4th
|
||||
- rata
|
||||
- sjenning
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
- rootfs
|
||||
- jingxu97
|
||||
- msau42
|
||||
- chakri-nelluri
|
||||
- MikaelCluseau
|
64
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher-defaults.go
generated
vendored
Normal file
64
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher-defaults.go
generated
vendored
Normal 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)
|
||||
}
|
121
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher.go
generated
vendored
Normal file
121
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher.go
generated
vendored
Normal 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
|
||||
}
|
76
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher_test.go
generated
vendored
Normal file
76
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/attacher_test.go
generated
vendored
Normal 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")
|
||||
}
|
142
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/common_test.go
generated
vendored
Normal file
142
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/common_test.go
generated
vendored
Normal 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)
|
||||
}
|
45
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher-defaults.go
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher-defaults.go
generated
vendored
Normal 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()))
|
||||
}
|
98
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher.go
generated
vendored
Normal file
98
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher.go
generated
vendored
Normal 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)
|
||||
}
|
43
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher_test.go
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/detacher_test.go
generated
vendored
Normal 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")
|
||||
}
|
249
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/driver-call.go
generated
vendored
Normal file
249
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/driver-call.go
generated
vendored
Normal 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
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/driver-call_test.go
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/driver-call_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
53
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/fake_watcher.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/fake_watcher.go
generated
vendored
Normal 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})
|
||||
}
|
214
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/flexvolume_test.go
generated
vendored
Normal file
214
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/flexvolume_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter-defaults.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter-defaults.go
generated
vendored
Normal 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
|
||||
}
|
110
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter.go
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter.go
generated
vendored
Normal 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
|
||||
}
|
72
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter_test.go
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/mounter_test.go
generated
vendored
Normal 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)
|
||||
}
|
34
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin-defaults.go
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin-defaults.go
generated
vendored
Normal 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
|
||||
}
|
266
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin.go
generated
vendored
Normal file
266
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin.go
generated
vendored
Normal 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
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin_test.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/plugin_test.go
generated
vendored
Normal 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
234
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/probe.go
generated
vendored
Normal 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
|
||||
}
|
274
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/probe_test.go
generated
vendored
Normal file
274
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/probe_test.go
generated
vendored
Normal 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
|
||||
}
|
29
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter-defaults.go
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter-defaults.go
generated
vendored
Normal 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)
|
||||
}
|
71
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter.go
generated
vendored
Normal file
71
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter.go
generated
vendored
Normal 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)
|
||||
}
|
37
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter_test.go
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/unmounter_test.go
generated
vendored
Normal 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
101
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/util.go
generated
vendored
Normal 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
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/volume.go
generated
vendored
Normal file
52
vendor/k8s.io/kubernetes/pkg/volume/flexvolume/volume.go
generated
vendored
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user