mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor update for CSI 0.3.0
This commit is contained in:
3
vendor/k8s.io/kubernetes/pkg/volume/util/BUILD
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/volume/util/BUILD
generated
vendored
@ -4,6 +4,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"atomic_writer.go",
|
||||
"attach_limit.go",
|
||||
"device_util.go",
|
||||
"doc.go",
|
||||
"error.go",
|
||||
@ -59,6 +60,7 @@ go_library(
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//pkg/volume/util/volumepathhandler:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
@ -91,7 +93,6 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/core/install:go_default_library",
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go
generated
vendored
@ -230,7 +230,7 @@ func validatePayload(payload map[string]FileProjection) (map[string]FileProjecti
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cleanPayload[path.Clean(k)] = content
|
||||
cleanPayload[filepath.Clean(k)] = content
|
||||
}
|
||||
|
||||
return cleanPayload, nil
|
||||
|
175
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer_test.go
generated
vendored
175
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer_test.go
generated
vendored
@ -804,10 +804,183 @@ func checkVolumeContents(targetDir, tcName string, payload map[string]FileProjec
|
||||
|
||||
cleanPathPayload := make(map[string]FileProjection, len(payload))
|
||||
for k, v := range payload {
|
||||
cleanPathPayload[path.Clean(k)] = v
|
||||
cleanPathPayload[filepath.Clean(k)] = v
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cleanPathPayload, observedPayload) {
|
||||
t.Errorf("%v: payload and observed payload do not match.", tcName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePayload(t *testing.T) {
|
||||
maxPath := strings.Repeat("a", maxPathLength+1)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
payload map[string]FileProjection
|
||||
expected sets.String
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "valid payload",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {},
|
||||
"bar": {},
|
||||
},
|
||||
valid: true,
|
||||
expected: sets.NewString("foo", "bar"),
|
||||
},
|
||||
{
|
||||
name: "payload with path length > 4096 is invalid",
|
||||
payload: map[string]FileProjection{
|
||||
maxPath: {},
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "payload with absolute path is invalid",
|
||||
payload: map[string]FileProjection{
|
||||
"/dev/null": {},
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "payload with reserved path is invalid",
|
||||
payload: map[string]FileProjection{
|
||||
"..sneaky.txt": {},
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "payload with doubledot path is invalid",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/../etc/password": {},
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "payload with empty path is invalid",
|
||||
payload: map[string]FileProjection{
|
||||
"": {},
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "payload with unclean path should be cleaned",
|
||||
payload: map[string]FileProjection{
|
||||
"foo////bar": {},
|
||||
},
|
||||
valid: true,
|
||||
expected: sets.NewString("foo/bar"),
|
||||
},
|
||||
}
|
||||
getPayloadPaths := func(payload map[string]FileProjection) sets.String {
|
||||
paths := sets.NewString()
|
||||
for path := range payload {
|
||||
paths.Insert(path)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
real, err := validatePayload(tc.payload)
|
||||
if !tc.valid && err == nil {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
}
|
||||
|
||||
if tc.valid {
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected failure: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
realPaths := getPayloadPaths(real)
|
||||
if !realPaths.Equal(tc.expected) {
|
||||
t.Errorf("%v: unexpected payload paths: %v is not equal to %v", tc.name, realPaths, tc.expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateUserVisibleFiles(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
payload map[string]FileProjection
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "simple path",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {},
|
||||
"bar": {},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"foo": "..data/foo",
|
||||
"bar": "..data/bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple nested path",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/bar": {},
|
||||
"foo/bar/txt": {},
|
||||
"bar/txt": {},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"foo": "..data/foo",
|
||||
"bar": "..data/bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unclean nested path",
|
||||
payload: map[string]FileProjection{
|
||||
"./bar": {},
|
||||
"foo///bar": {},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"bar": "..data/bar",
|
||||
"foo": "..data/foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error creating tmp dir: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
dataDirPath := path.Join(targetDir, dataDirName)
|
||||
err = os.MkdirAll(dataDirPath, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("%v: unexpected error creating data path: %v", tc.name, err)
|
||||
}
|
||||
|
||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||
payload, err := validatePayload(tc.payload)
|
||||
if err != nil {
|
||||
t.Fatalf("%v: unexpected error validating payload: %v", tc.name, err)
|
||||
}
|
||||
err = writer.createUserVisibleFiles(payload)
|
||||
if err != nil {
|
||||
t.Fatalf("%v: unexpected error creating visible files: %v", tc.name, err)
|
||||
}
|
||||
|
||||
for subpath, expectedDest := range tc.expected {
|
||||
visiblePath := path.Join(targetDir, subpath)
|
||||
destination, err := os.Readlink(visiblePath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
t.Fatalf("%v: visible symlink does not exist: %v", tc.name, visiblePath)
|
||||
} else if err != nil {
|
||||
t.Fatalf("%v: unable to read symlink %v: %v", tc.name, dataDirPath, err)
|
||||
}
|
||||
|
||||
if expectedDest != destination {
|
||||
t.Fatalf("%v: symlink destination %q not same with expected data dir %q", tc.name, destination, expectedDest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
vendor/k8s.io/kubernetes/pkg/volume/util/attach_limit.go
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/pkg/volume/util/attach_limit.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
// This file is a common place holder for volume limit utility constants
|
||||
// shared between volume package and scheduler
|
||||
|
||||
const (
|
||||
// EBSVolumeLimitKey resource name that will store volume limits for EBS
|
||||
EBSVolumeLimitKey = "attachable-volumes-aws-ebs"
|
||||
// AzureVolumeLimitKey stores resource name that will store volume limits for Azure
|
||||
AzureVolumeLimitKey = "attachable-volumes-azure-disk"
|
||||
// GCEVolumeLimitKey stores resource name that will store volume limits for GCE node
|
||||
GCEVolumeLimitKey = "attachable-volumes-gce-pd"
|
||||
)
|
3
vendor/k8s.io/kubernetes/pkg/volume/util/fs/BUILD
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/volume/util/fs/BUILD
generated
vendored
@ -34,7 +34,7 @@ go_library(
|
||||
"fs_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"fs_unsupported.go",
|
||||
"fs_windows.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
@ -74,6 +74,7 @@ go_library(
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/golang.org/x/sys/windows:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go
generated
vendored
@ -54,7 +54,8 @@ func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||
return available, capacity, usage, inodes, inodesFree, inodesUsed, nil
|
||||
}
|
||||
|
||||
func Du(path string) (*resource.Quantity, error) {
|
||||
// DiskUsage gets disk usage of specified path.
|
||||
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||
// Uses the same niceness level as cadvisor.fs does when running du
|
||||
// Uses -B 1 to always scale to a blocksize of 1 byte
|
||||
out, err := exec.Command("nice", "-n", "19", "du", "-s", "-B", "1", path).CombinedOutput()
|
||||
|
5
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go
generated
vendored
5
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// +build !linux,!darwin
|
||||
// +build !linux,!darwin,!windows
|
||||
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
@ -29,7 +29,8 @@ func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||
return 0, 0, 0, 0, 0, 0, fmt.Errorf("FsInfo not supported for this build.")
|
||||
}
|
||||
|
||||
func Du(path string) (*resource.Quantity, error) {
|
||||
// DiskUsage gets disk usage of specified path.
|
||||
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||
return nil, fmt.Errorf("Du not supported for this build.")
|
||||
}
|
||||
|
||||
|
77
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_windows.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_windows.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
procGetDiskFreeSpaceEx = modkernel32.NewProc("GetDiskFreeSpaceExW")
|
||||
)
|
||||
|
||||
// FSInfo returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error)
|
||||
// for the filesystem that path resides upon.
|
||||
func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||
var freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes int64
|
||||
var err error
|
||||
|
||||
ret, _, err := syscall.Syscall6(
|
||||
procGetDiskFreeSpaceEx.Addr(),
|
||||
4,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
|
||||
uintptr(unsafe.Pointer(&freeBytesAvailable)),
|
||||
uintptr(unsafe.Pointer(&totalNumberOfBytes)),
|
||||
uintptr(unsafe.Pointer(&totalNumberOfFreeBytes)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret == 0 {
|
||||
return 0, 0, 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
return freeBytesAvailable, totalNumberOfBytes, totalNumberOfBytes - freeBytesAvailable, 0, 0, 0, nil
|
||||
}
|
||||
|
||||
// DiskUsage gets disk usage of specified path.
|
||||
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||
_, _, usage, _, _, _, err := FsInfo(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
used, err := resource.ParseQuantity(fmt.Sprintf("%d", usage))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse fs usage %d due to %v", usage, err)
|
||||
}
|
||||
used.Format = resource.BinarySI
|
||||
return &used, nil
|
||||
}
|
||||
|
||||
// Always return zero since inodes is not supported on Windows.
|
||||
func Find(path string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
22
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go
generated
vendored
22
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go
generated
vendored
@ -112,7 +112,7 @@ type OperationExecutor interface {
|
||||
// For 'Block' volumeMode, this method unmaps symbolic link to the volume
|
||||
// from both the pod device map path in volumeToUnmount and global map path.
|
||||
// And then, updates the actual state of the world to reflect that.
|
||||
UnmountVolume(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
|
||||
UnmountVolume(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, podsDir string) error
|
||||
|
||||
// If a volume has 'Filesystem' volumeMode, UnmountDevice unmounts the
|
||||
// volumes global mount path from the device (for attachable volumes only,
|
||||
@ -142,6 +142,8 @@ type OperationExecutor interface {
|
||||
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool
|
||||
// Expand Volume will grow size available to PVC
|
||||
ExpandVolume(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) error
|
||||
// ExpandVolumeFSWithoutUnmounting will resize volume's file system to expected size without unmounting the volume.
|
||||
ExpandVolumeFSWithoutUnmounting(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
|
||||
// ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
|
||||
ReconstructVolumeOperation(volumeMode v1.PersistentVolumeMode, plugin volume.VolumePlugin, mapperPlugin volume.BlockVolumePlugin, uid types.UID, podName volumetypes.UniquePodName, volumeSpecName string, mountPath string, pluginName string) (*volume.Spec, error)
|
||||
// CheckVolumeExistenceOperation checks volume existence
|
||||
@ -163,7 +165,7 @@ func NewOperationExecutor(
|
||||
// state of the world cache after successful mount/unmount.
|
||||
type ActualStateOfWorldMounterUpdater interface {
|
||||
// Marks the specified volume as mounted to the specified pod
|
||||
MarkVolumeAsMounted(podName volumetypes.UniquePodName, podUID types.UID, volumeName v1.UniqueVolumeName, mounter volume.Mounter, blockVolumeMapper volume.BlockVolumeMapper, outerVolumeSpecName string, volumeGidValue string) error
|
||||
MarkVolumeAsMounted(podName volumetypes.UniquePodName, podUID types.UID, volumeName v1.UniqueVolumeName, mounter volume.Mounter, blockVolumeMapper volume.BlockVolumeMapper, outerVolumeSpecName string, volumeGidValue string, volumeSpec *volume.Spec) error
|
||||
|
||||
// Marks the specified volume as unmounted from the specified pod
|
||||
MarkVolumeAsUnmounted(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error
|
||||
@ -173,6 +175,9 @@ type ActualStateOfWorldMounterUpdater interface {
|
||||
|
||||
// Marks the specified volume as having its global mount unmounted.
|
||||
MarkDeviceAsUnmounted(volumeName v1.UniqueVolumeName) error
|
||||
|
||||
// Marks the specified volume's file system resize request is finished.
|
||||
MarkVolumeAsResized(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error
|
||||
}
|
||||
|
||||
// ActualStateOfWorldAttacherUpdater defines a set of operations updating the
|
||||
@ -746,7 +751,8 @@ func (oe *operationExecutor) MountVolume(
|
||||
|
||||
func (oe *operationExecutor) UnmountVolume(
|
||||
volumeToUnmount MountedVolume,
|
||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) error {
|
||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||
podsDir string) error {
|
||||
fsVolume, err := util.CheckVolumeModeFilesystem(volumeToUnmount.VolumeSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -756,7 +762,7 @@ func (oe *operationExecutor) UnmountVolume(
|
||||
// Filesystem volume case
|
||||
// Unmount a volume if a volume is mounted
|
||||
generatedOperations, err = oe.operationGenerator.GenerateUnmountVolumeFunc(
|
||||
volumeToUnmount, actualStateOfWorld)
|
||||
volumeToUnmount, actualStateOfWorld, podsDir)
|
||||
} else {
|
||||
// Block volume case
|
||||
// Unmap a volume if a volume is mapped
|
||||
@ -816,6 +822,14 @@ func (oe *operationExecutor) ExpandVolume(pvcWithResizeRequest *expandcache.PVCW
|
||||
return oe.pendingOperations.Run(uniqueVolumeKey, "", generatedOperations)
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) ExpandVolumeFSWithoutUnmounting(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error {
|
||||
generatedOperations, err := oe.operationGenerator.GenerateExpandVolumeFSWithoutUnmountingFunc(volumeToMount, actualStateOfWorld)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return oe.pendingOperations.Run(volumeToMount.VolumeName, "", generatedOperations)
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) VerifyControllerAttachedVolume(
|
||||
volumeToMount VolumeToMount,
|
||||
nodeName types.NodeName,
|
||||
|
16
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor_test.go
generated
vendored
16
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor_test.go
generated
vendored
@ -124,7 +124,7 @@ func TestOperationExecutor_UnmountVolume_ConcurrentUnmountForAllPlugins(t *testi
|
||||
PodUID: pod.UID,
|
||||
}
|
||||
}
|
||||
oe.UnmountVolume(volumesToUnmount[i], nil /* actualStateOfWorldMounterUpdater */)
|
||||
oe.UnmountVolume(volumesToUnmount[i], nil /* actualStateOfWorldMounterUpdater */, "" /*podsDir*/)
|
||||
}
|
||||
|
||||
// Assert
|
||||
@ -318,7 +318,7 @@ func TestOperationExecutor_UnmountVolume_ConcurrentUnmountForAllPlugins_VolumeMo
|
||||
VolumeSpec: tmpSpec,
|
||||
}
|
||||
}
|
||||
oe.UnmountVolume(volumesToUnmount[i], nil /* actualStateOfWorldMounterUpdater */)
|
||||
oe.UnmountVolume(volumesToUnmount[i], nil /* actualStateOfWorldMounterUpdater */, "" /* podsDir */)
|
||||
}
|
||||
|
||||
// Assert
|
||||
@ -372,7 +372,7 @@ func (fopg *fakeOperationGenerator) GenerateMountVolumeFunc(waitForAttachTimeout
|
||||
OperationFunc: opFunc,
|
||||
}, nil
|
||||
}
|
||||
func (fopg *fakeOperationGenerator) GenerateUnmountVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||
func (fopg *fakeOperationGenerator) GenerateUnmountVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, podsDir string) (volumetypes.GeneratedOperations, error) {
|
||||
opFunc := func() (error, error) {
|
||||
startOperationAndBlock(fopg.ch, fopg.quit)
|
||||
return nil, nil
|
||||
@ -438,6 +438,16 @@ func (fopg *fakeOperationGenerator) GenerateExpandVolumeFunc(pvcWithResizeReques
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fopg *fakeOperationGenerator) GenerateExpandVolumeFSWithoutUnmountingFunc(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||
opFunc := func() (error, error) {
|
||||
startOperationAndBlock(fopg.ch, fopg.quit)
|
||||
return nil, nil
|
||||
}
|
||||
return volumetypes.GeneratedOperations{
|
||||
OperationFunc: opFunc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fopg *fakeOperationGenerator) GenerateBulkVolumeVerifyFunc(
|
||||
pluginNodeVolumes map[types.NodeName][]*volume.Spec,
|
||||
pluginNane string,
|
||||
|
148
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go
generated
vendored
148
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go
generated
vendored
@ -18,6 +18,7 @@ package operationexecutor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -85,7 +86,7 @@ type OperationGenerator interface {
|
||||
GenerateMountVolumeFunc(waitForAttachTimeout time.Duration, volumeToMount VolumeToMount, actualStateOfWorldMounterUpdater ActualStateOfWorldMounterUpdater, isRemount bool) (volumetypes.GeneratedOperations, error)
|
||||
|
||||
// Generates the UnmountVolume function needed to perform the unmount of a volume plugin
|
||||
GenerateUnmountVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
|
||||
GenerateUnmountVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, podsDir string) (volumetypes.GeneratedOperations, error)
|
||||
|
||||
// Generates the AttachVolume function needed to perform attach of a volume plugin
|
||||
GenerateAttachVolumeFunc(volumeToAttach VolumeToAttach, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
|
||||
@ -120,6 +121,9 @@ type OperationGenerator interface {
|
||||
map[*volume.Spec]v1.UniqueVolumeName, ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
|
||||
|
||||
GenerateExpandVolumeFunc(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) (volumetypes.GeneratedOperations, error)
|
||||
|
||||
// Generates the volume file system resize function, which can resize volume's file system to expected size without unmounting the volume.
|
||||
GenerateExpandVolumeFSWithoutUnmountingFunc(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
|
||||
}
|
||||
|
||||
func (og *operationGenerator) GenerateVolumesAreAttachedFunc(
|
||||
@ -546,12 +550,10 @@ func (og *operationGenerator) GenerateMountVolumeFunc(
|
||||
return volumeToMount.GenerateError("MountVolume.SetUp failed", mountErr)
|
||||
}
|
||||
|
||||
simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.SetUp succeeded", "")
|
||||
_, detailedMsg := volumeToMount.GenerateMsg("MountVolume.SetUp succeeded", "")
|
||||
verbosity := glog.Level(1)
|
||||
if isRemount {
|
||||
verbosity = glog.Level(4)
|
||||
} else {
|
||||
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg)
|
||||
}
|
||||
glog.V(verbosity).Infof(detailedMsg)
|
||||
|
||||
@ -563,7 +565,8 @@ func (og *operationGenerator) GenerateMountVolumeFunc(
|
||||
volumeMounter,
|
||||
nil,
|
||||
volumeToMount.OuterVolumeSpecName,
|
||||
volumeToMount.VolumeGidValue)
|
||||
volumeToMount.VolumeGidValue,
|
||||
volumeToMount.VolumeSpec)
|
||||
if markVolMountedErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MountVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
||||
@ -651,7 +654,8 @@ func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, devi
|
||||
|
||||
func (og *operationGenerator) GenerateUnmountVolumeFunc(
|
||||
volumeToUnmount MountedVolume,
|
||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||
podsDir string) (volumetypes.GeneratedOperations, error) {
|
||||
// Get mountable plugin
|
||||
volumePlugin, err :=
|
||||
og.volumePluginMgr.FindPluginByName(volumeToUnmount.PluginName)
|
||||
@ -666,6 +670,14 @@ func (og *operationGenerator) GenerateUnmountVolumeFunc(
|
||||
}
|
||||
|
||||
unmountVolumeFunc := func() (error, error) {
|
||||
mounter := og.volumePluginMgr.Host.GetMounter(volumeToUnmount.PluginName)
|
||||
|
||||
// Remove all bind-mounts for subPaths
|
||||
podDir := path.Join(podsDir, string(volumeToUnmount.PodUID))
|
||||
if err := mounter.CleanSubPaths(podDir, volumeToUnmount.InnerVolumeSpecName); err != nil {
|
||||
return volumeToUnmount.GenerateError("error cleaning subPath mounts", err)
|
||||
}
|
||||
|
||||
// Execute unmount
|
||||
unmountErr := volumeUnmounter.TearDown()
|
||||
if unmountErr != nil {
|
||||
@ -844,37 +856,19 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.SetUp failed", mapErr)
|
||||
}
|
||||
// Update devicePath for none attachable plugin case
|
||||
|
||||
// if pluginDevicePath is provided, assume attacher may not provide device
|
||||
// or attachment flow uses SetupDevice to get device path
|
||||
if len(pluginDevicePath) != 0 {
|
||||
devicePath = pluginDevicePath
|
||||
}
|
||||
if len(devicePath) == 0 {
|
||||
if len(pluginDevicePath) != 0 {
|
||||
devicePath = pluginDevicePath
|
||||
} else {
|
||||
return volumeToMount.GenerateError("MapVolume failed", fmt.Errorf("Device path of the volume is empty"))
|
||||
}
|
||||
}
|
||||
// Update actual state of world to reflect volume is globally mounted
|
||||
markDeviceMappedErr := actualStateOfWorld.MarkDeviceAsMounted(
|
||||
volumeToMount.VolumeName, devicePath, globalMapPath)
|
||||
if markDeviceMappedErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.MarkDeviceAsMounted failed", markDeviceMappedErr)
|
||||
return volumeToMount.GenerateError("MapVolume failed", fmt.Errorf("Device path of the volume is empty"))
|
||||
}
|
||||
|
||||
mapErr = og.blkUtil.MapDevice(devicePath, globalMapPath, string(volumeToMount.Pod.UID))
|
||||
if mapErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.MapDevice failed", mapErr)
|
||||
}
|
||||
|
||||
// Device mapping for global map path succeeded
|
||||
simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MapVolume.MapDevice succeeded", fmt.Sprintf("globalMapPath %q", globalMapPath))
|
||||
verbosity := glog.Level(4)
|
||||
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg)
|
||||
glog.V(verbosity).Infof(detailedMsg)
|
||||
|
||||
// Map device to pod device map path under the given pod directory using symbolic link
|
||||
// Map device to global and pod device map path
|
||||
volumeMapPath, volName := blockVolumeMapper.GetPodDeviceMapPath()
|
||||
mapErr = og.blkUtil.MapDevice(devicePath, volumeMapPath, volName)
|
||||
mapErr = blockVolumeMapper.MapDevice(devicePath, globalMapPath, volumeMapPath, volName, volumeToMount.Pod.UID)
|
||||
if mapErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.MapDevice failed", mapErr)
|
||||
@ -889,6 +883,20 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
|
||||
return volumeToMount.GenerateError("MapVolume.AttachFileDevice failed", err)
|
||||
}
|
||||
|
||||
// Update actual state of world to reflect volume is globally mounted
|
||||
markDeviceMappedErr := actualStateOfWorld.MarkDeviceAsMounted(
|
||||
volumeToMount.VolumeName, devicePath, globalMapPath)
|
||||
if markDeviceMappedErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.MarkDeviceAsMounted failed", markDeviceMappedErr)
|
||||
}
|
||||
|
||||
// Device mapping for global map path succeeded
|
||||
simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MapVolume.MapDevice succeeded", fmt.Sprintf("globalMapPath %q", globalMapPath))
|
||||
verbosity := glog.Level(4)
|
||||
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg)
|
||||
glog.V(verbosity).Infof(detailedMsg)
|
||||
|
||||
// Device mapping for pod device map path succeeded
|
||||
simpleMsg, detailedMsg = volumeToMount.GenerateMsg("MapVolume.MapDevice succeeded", fmt.Sprintf("volumeMapPath %q", volumeMapPath))
|
||||
verbosity = glog.Level(1)
|
||||
@ -903,7 +911,8 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
|
||||
nil,
|
||||
blockVolumeMapper,
|
||||
volumeToMount.OuterVolumeSpecName,
|
||||
volumeToMount.VolumeGidValue)
|
||||
volumeToMount.VolumeGidValue,
|
||||
volumeToMount.VolumeSpec)
|
||||
if markVolMountedErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
||||
@ -1058,11 +1067,18 @@ func (og *operationGenerator) GenerateUnmapDeviceFunc(
|
||||
glog.V(4).Infof("UnmapDevice: deviceToDetach.DevicePath: %v", deviceToDetach.DevicePath)
|
||||
loopPath, err := og.blkUtil.GetLoopDevice(deviceToDetach.DevicePath)
|
||||
if err != nil {
|
||||
glog.Warningf(deviceToDetach.GenerateMsgDetailed("UnmapDevice: Couldn't find loopback device which takes file descriptor lock", fmt.Sprintf("device path: %q", deviceToDetach.DevicePath)))
|
||||
if err.Error() == volumepathhandler.ErrDeviceNotFound {
|
||||
glog.Warningf(deviceToDetach.GenerateMsgDetailed("UnmapDevice: Couldn't find loopback device which takes file descriptor lock", fmt.Sprintf("device path: %q", deviceToDetach.DevicePath)))
|
||||
} else {
|
||||
errInfo := "UnmapDevice.GetLoopDevice failed to get loopback device, " + fmt.Sprintf("device path: %q", deviceToDetach.DevicePath)
|
||||
return deviceToDetach.GenerateError(errInfo, err)
|
||||
}
|
||||
} else {
|
||||
err = og.blkUtil.RemoveLoopDevice(loopPath)
|
||||
if err != nil {
|
||||
return deviceToDetach.GenerateError("UnmapDevice.AttachFileDevice failed", err)
|
||||
if len(loopPath) != 0 {
|
||||
err = og.blkUtil.RemoveLoopDevice(loopPath)
|
||||
if err != nil {
|
||||
return deviceToDetach.GenerateError("UnmapDevice.RemoveLoopDevice failed", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1289,6 +1305,62 @@ func (og *operationGenerator) GenerateExpandVolumeFunc(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (og *operationGenerator) GenerateExpandVolumeFSWithoutUnmountingFunc(
|
||||
volumeToMount VolumeToMount,
|
||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||
volumePlugin, err :=
|
||||
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
||||
if err != nil || volumePlugin == nil {
|
||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VolumeFSResize.FindPluginBySpec failed", err)
|
||||
}
|
||||
|
||||
attachableVolumePlugin, err :=
|
||||
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToMount.VolumeSpec)
|
||||
if err != nil || attachableVolumePlugin == nil {
|
||||
if attachableVolumePlugin == nil {
|
||||
err = fmt.Errorf("AttachableVolumePlugin is nil")
|
||||
}
|
||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VolumeFSResize.FindAttachablePluginBySpec failed", err)
|
||||
}
|
||||
|
||||
volumeAttacher, err := attachableVolumePlugin.NewAttacher()
|
||||
if err != nil || volumeAttacher == nil {
|
||||
if volumeAttacher == nil {
|
||||
err = fmt.Errorf("VolumeAttacher is nil")
|
||||
}
|
||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VolumeFSResize.NewAttacher failed", err)
|
||||
}
|
||||
|
||||
deviceMountPath, err := volumeAttacher.GetDeviceMountPath(volumeToMount.VolumeSpec)
|
||||
if err != nil {
|
||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VolumeFSResize.GetDeviceMountPath failed", err)
|
||||
}
|
||||
|
||||
fsResizeFunc := func() (error, error) {
|
||||
resizeSimpleError, resizeDetailedError := og.resizeFileSystem(volumeToMount, volumeToMount.DevicePath, deviceMountPath, volumePlugin.GetPluginName())
|
||||
if resizeSimpleError != nil || resizeDetailedError != nil {
|
||||
return resizeSimpleError, resizeDetailedError
|
||||
}
|
||||
markFSResizedErr := actualStateOfWorld.MarkVolumeAsResized(volumeToMount.PodName, volumeToMount.VolumeName)
|
||||
if markFSResizedErr != nil {
|
||||
// On failure, return error. Caller will log and retry.
|
||||
return volumeToMount.GenerateError("VolumeFSResize.MarkVolumeAsResized failed", markFSResizedErr)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
eventRecorderFunc := func(err *error) {
|
||||
if *err != nil {
|
||||
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error())
|
||||
}
|
||||
}
|
||||
|
||||
return volumetypes.GeneratedOperations{
|
||||
OperationFunc: fsResizeFunc,
|
||||
EventRecorderFunc: eventRecorderFunc,
|
||||
CompleteFunc: util.OperationCompleteHook(volumePlugin.GetPluginName(), "volume_fs_resize"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkMountOptionSupport(og *operationGenerator, volumeToMount VolumeToMount, plugin volume.VolumePlugin) error {
|
||||
mountOptions := util.MountOptionFromSpec(volumeToMount.VolumeSpec)
|
||||
|
||||
|
43
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go
generated
vendored
43
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go
generated
vendored
@ -18,6 +18,7 @@ package recyclerclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
@ -191,6 +192,8 @@ func (c *realRecyclerClient) Event(eventtype, message string) {
|
||||
c.recorder(eventtype, message)
|
||||
}
|
||||
|
||||
// WatchPod watches a pod and events related to it. It sends pod updates and events over the returned channel
|
||||
// It will continue until stopChannel is closed
|
||||
func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error) {
|
||||
podSelector, err := fields.ParseSelector("metadata.name=" + name)
|
||||
if err != nil {
|
||||
@ -217,33 +220,45 @@ func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan s
|
||||
}
|
||||
|
||||
eventCh := make(chan watch.Event, 30)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer close(eventCh)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer eventWatch.Stop()
|
||||
defer podWatch.Stop()
|
||||
defer close(eventCh)
|
||||
var podWatchChannelClosed bool
|
||||
var eventWatchChannelClosed bool
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case _ = <-stopChannel:
|
||||
return
|
||||
|
||||
case podEvent, ok := <-podWatch.ResultChan():
|
||||
if !ok {
|
||||
podWatchChannelClosed = true
|
||||
} else {
|
||||
eventCh <- podEvent
|
||||
}
|
||||
case eventEvent, ok := <-eventWatch.ResultChan():
|
||||
if !ok {
|
||||
eventWatchChannelClosed = true
|
||||
return
|
||||
} else {
|
||||
eventCh <- eventEvent
|
||||
}
|
||||
}
|
||||
if podWatchChannelClosed && eventWatchChannelClosed {
|
||||
break
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer podWatch.Stop()
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-stopChannel:
|
||||
return
|
||||
|
||||
case podEvent, ok := <-podWatch.ResultChan():
|
||||
if !ok {
|
||||
return
|
||||
} else {
|
||||
eventCh <- podEvent
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
92
vendor/k8s.io/kubernetes/pkg/volume/util/util.go
generated
vendored
92
vendor/k8s.io/kubernetes/pkg/volume/util/util.go
generated
vendored
@ -21,6 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
@ -49,6 +50,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
utypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -266,37 +268,9 @@ func GetClassForVolume(kubeClient clientset.Interface, pv *v1.PersistentVolume)
|
||||
// CheckNodeAffinity looks at the PV node affinity, and checks if the node has the same corresponding labels
|
||||
// This ensures that we don't mount a volume that doesn't belong to this node
|
||||
func CheckNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]string) error {
|
||||
if err := checkAlphaNodeAffinity(pv, nodeLabels); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkVolumeNodeAffinity(pv, nodeLabels)
|
||||
}
|
||||
|
||||
func checkAlphaNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]string) error {
|
||||
affinity, err := v1helper.GetStorageNodeAffinityFromAnnotation(pv.Annotations)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting storage node affinity: %v", err)
|
||||
}
|
||||
if affinity == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if affinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
terms := affinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
|
||||
glog.V(10).Infof("Match for RequiredDuringSchedulingIgnoredDuringExecution node selector terms %+v", terms)
|
||||
for _, term := range terms {
|
||||
selector, err := v1helper.NodeSelectorRequirementsAsSelector(term.MatchExpressions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse MatchExpressions: %v", err)
|
||||
}
|
||||
if !selector.Matches(labels.Set(nodeLabels)) {
|
||||
return fmt.Errorf("NodeSelectorTerm %+v does not match node labels", term.MatchExpressions)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkVolumeNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]string) error {
|
||||
if pv.Spec.NodeAffinity == nil {
|
||||
return nil
|
||||
@ -305,16 +279,11 @@ func checkVolumeNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]stri
|
||||
if pv.Spec.NodeAffinity.Required != nil {
|
||||
terms := pv.Spec.NodeAffinity.Required.NodeSelectorTerms
|
||||
glog.V(10).Infof("Match for Required node selector terms %+v", terms)
|
||||
for _, term := range terms {
|
||||
selector, err := v1helper.NodeSelectorRequirementsAsSelector(term.MatchExpressions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse MatchExpressions: %v", err)
|
||||
}
|
||||
if !selector.Matches(labels.Set(nodeLabels)) {
|
||||
return fmt.Errorf("NodeSelectorTerm %+v does not match node labels", term.MatchExpressions)
|
||||
}
|
||||
if !v1helper.MatchNodeSelectorTerms(terms, labels.Set(nodeLabels), nil) {
|
||||
return fmt.Errorf("No matching NodeSelectorTerms")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -749,3 +718,54 @@ func CheckVolumeModeFilesystem(volumeSpec *volume.Spec) (bool, error) {
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckPersistentVolumeClaimModeBlock checks VolumeMode.
|
||||
// If the mode is Block, return true otherwise return false.
|
||||
func CheckPersistentVolumeClaimModeBlock(pvc *v1.PersistentVolumeClaim) bool {
|
||||
return utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && pvc.Spec.VolumeMode != nil && *pvc.Spec.VolumeMode == v1.PersistentVolumeBlock
|
||||
}
|
||||
|
||||
// MakeAbsolutePath convert path to absolute path according to GOOS
|
||||
func MakeAbsolutePath(goos, path string) string {
|
||||
if goos != "windows" {
|
||||
return filepath.Clean("/" + path)
|
||||
}
|
||||
// These are all for windows
|
||||
// If there is a colon, give up.
|
||||
if strings.Contains(path, ":") {
|
||||
return path
|
||||
}
|
||||
// If there is a slash, but no drive, add 'c:'
|
||||
if strings.HasPrefix(path, "/") || strings.HasPrefix(path, "\\") {
|
||||
return "c:" + path
|
||||
}
|
||||
// Otherwise, add 'c:\'
|
||||
return "c:\\" + path
|
||||
}
|
||||
|
||||
// MapBlockVolume is a utility function to provide a common way of mounting
|
||||
// block device path for a specified volume and pod. This function should be
|
||||
// called by volume plugins that implements volume.BlockVolumeMapper.Map() method.
|
||||
func MapBlockVolume(
|
||||
devicePath,
|
||||
globalMapPath,
|
||||
podVolumeMapPath,
|
||||
volumeMapName string,
|
||||
podUID utypes.UID,
|
||||
) error {
|
||||
blkUtil := volumepathhandler.NewBlockVolumePathHandler()
|
||||
|
||||
// map devicePath to global node path
|
||||
mapErr := blkUtil.MapDevice(devicePath, globalMapPath, string(podUID))
|
||||
if mapErr != nil {
|
||||
return mapErr
|
||||
}
|
||||
|
||||
// map devicePath to pod volume path
|
||||
mapErr = blkUtil.MapDevice(devicePath, podVolumeMapPath, volumeMapName)
|
||||
if mapErr != nil {
|
||||
return mapErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
248
vendor/k8s.io/kubernetes/pkg/volume/util/util_test.go
generated
vendored
248
vendor/k8s.io/kubernetes/pkg/volume/util/util_test.go
generated
vendored
@ -19,6 +19,7 @@ package util
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
@ -29,7 +30,6 @@ import (
|
||||
"hash/fnv"
|
||||
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
||||
"reflect"
|
||||
@ -47,105 +47,6 @@ var nodeLabels map[string]string = map[string]string{
|
||||
"test-key2": "test-value2",
|
||||
}
|
||||
|
||||
func TestCheckAlphaNodeAffinity(t *testing.T) {
|
||||
type affinityTest struct {
|
||||
name string
|
||||
expectSuccess bool
|
||||
pv *v1.PersistentVolume
|
||||
}
|
||||
|
||||
cases := []affinityTest{
|
||||
{
|
||||
name: "valid-no-constraints",
|
||||
expectSuccess: true,
|
||||
pv: testVolumeWithAlphaNodeAffinity(t, &v1.NodeAffinity{}),
|
||||
},
|
||||
{
|
||||
name: "valid-constraints",
|
||||
expectSuccess: true,
|
||||
pv: testVolumeWithAlphaNodeAffinity(t, &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key1",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value1", "test-value3"},
|
||||
},
|
||||
{
|
||||
Key: "test-key2",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value0", "test-value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "invalid-key",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithAlphaNodeAffinity(t, &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key1",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value1", "test-value3"},
|
||||
},
|
||||
{
|
||||
Key: "test-key3",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value0", "test-value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "invalid-values",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithAlphaNodeAffinity(t, &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key1",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value3", "test-value4"},
|
||||
},
|
||||
{
|
||||
Key: "test-key2",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value0", "test-value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
err := CheckNodeAffinity(c.pv, nodeLabels)
|
||||
|
||||
if err != nil && c.expectSuccess {
|
||||
t.Errorf("CheckTopology %v returned error: %v", c.name, err)
|
||||
}
|
||||
if err == nil && !c.expectSuccess {
|
||||
t.Errorf("CheckTopology %v returned success, expected error", c.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
type affinityTest struct {
|
||||
name string
|
||||
@ -165,7 +66,53 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{}),
|
||||
},
|
||||
{
|
||||
name: "valid-constraints",
|
||||
name: "select-nothing",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{Required: &v1.NodeSelector{}}),
|
||||
},
|
||||
{
|
||||
name: "select-nothing-empty-terms",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "valid-multiple-terms",
|
||||
expectSuccess: true,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key3",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value1", "test-value3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key2",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value0", "test-value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "valid-multiple-match-expressions",
|
||||
expectSuccess: true,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
@ -189,7 +136,7 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "invalid-key",
|
||||
name: "invalid-multiple-match-expressions-key",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
@ -213,7 +160,7 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "invalid-values",
|
||||
name: "invalid-multiple-match-expressions-values",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
@ -236,6 +183,34 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "invalid-multiple-terms",
|
||||
expectSuccess: false,
|
||||
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||
Required: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key3",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value1", "test-value3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "test-key2",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test-value0", "test-value1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -250,19 +225,6 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testVolumeWithAlphaNodeAffinity(t *testing.T, affinity *v1.NodeAffinity) *v1.PersistentVolume {
|
||||
objMeta := metav1.ObjectMeta{Name: "test-constraints"}
|
||||
objMeta.Annotations = map[string]string{}
|
||||
err := helper.StorageNodeAffinityToAlphaAnnotation(objMeta.Annotations, affinity)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get node affinity annotation: %v", err)
|
||||
}
|
||||
|
||||
return &v1.PersistentVolume{
|
||||
ObjectMeta: objMeta,
|
||||
}
|
||||
}
|
||||
|
||||
func testVolumeWithNodeAffinity(t *testing.T, affinity *v1.VolumeNodeAffinity) *v1.PersistentVolume {
|
||||
objMeta := metav1.ObjectMeta{Name: "test-constraints"}
|
||||
return &v1.PersistentVolume{
|
||||
@ -1089,3 +1051,57 @@ func TestGetWindowsPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeAbsolutePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
goos string
|
||||
path string
|
||||
expectedPath string
|
||||
name string
|
||||
}{
|
||||
{
|
||||
goos: "linux",
|
||||
path: "non-absolute/path",
|
||||
expectedPath: "/non-absolute/path",
|
||||
name: "linux non-absolute path",
|
||||
},
|
||||
{
|
||||
goos: "linux",
|
||||
path: "/absolute/path",
|
||||
expectedPath: "/absolute/path",
|
||||
name: "linux absolute path",
|
||||
},
|
||||
{
|
||||
goos: "windows",
|
||||
path: "some\\path",
|
||||
expectedPath: "c:\\some\\path",
|
||||
name: "basic windows",
|
||||
},
|
||||
{
|
||||
goos: "windows",
|
||||
path: "/some/path",
|
||||
expectedPath: "c:/some/path",
|
||||
name: "linux path on windows",
|
||||
},
|
||||
{
|
||||
goos: "windows",
|
||||
path: "\\some\\path",
|
||||
expectedPath: "c:\\some\\path",
|
||||
name: "windows path no drive",
|
||||
},
|
||||
{
|
||||
goos: "windows",
|
||||
path: "\\:\\some\\path",
|
||||
expectedPath: "\\:\\some\\path",
|
||||
name: "windows path with colon",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if runtime.GOOS == test.goos {
|
||||
path := MakeAbsolutePath(test.goos, test.path)
|
||||
if path != test.expectedPath {
|
||||
t.Errorf("[%s] Expected %s saw %s", test.name, test.expectedPath, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user