-
Notifications
You must be signed in to change notification settings - Fork 40.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c741432
commit 2c0129c
Showing
3 changed files
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
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 gce_pd | ||
|
||
import ( | ||
"fmt" | ||
"path" | ||
"path/filepath" | ||
"strconv" | ||
|
||
"github.com/golang/glog" | ||
"k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/kubernetes/pkg/util/mount" | ||
kstrings "k8s.io/kubernetes/pkg/util/strings" | ||
"k8s.io/kubernetes/pkg/volume" | ||
"k8s.io/kubernetes/pkg/volume/util" | ||
) | ||
|
||
var _ volume.VolumePlugin = &gcePersistentDiskPlugin{} | ||
var _ volume.PersistentVolumePlugin = &gcePersistentDiskPlugin{} | ||
var _ volume.BlockVolumePlugin = &gcePersistentDiskPlugin{} | ||
var _ volume.DeletableVolumePlugin = &gcePersistentDiskPlugin{} | ||
var _ volume.ProvisionableVolumePlugin = &gcePersistentDiskPlugin{} | ||
var _ volume.ExpandableVolumePlugin = &gcePersistentDiskPlugin{} | ||
|
||
func (plugin *gcePersistentDiskPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) { | ||
pluginDir := plugin.host.GetVolumeDevicePluginDir(gcePersistentDiskPluginName) | ||
blkutil := util.NewBlockVolumePathHandler() | ||
globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
glog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err) | ||
|
||
globalMapPath := filepath.Dir(globalMapPathUUID) | ||
if len(globalMapPath) <= 1 { | ||
return nil, fmt.Errorf("failed to get volume plugin information from globalMapPathUUID: %v", globalMapPathUUID) | ||
} | ||
|
||
return getVolumeSpecFromGlobalMapPath(globalMapPath) | ||
} | ||
|
||
func getVolumeSpecFromGlobalMapPath(globalMapPath string) (*volume.Spec, error) { | ||
// Get volume spec information from globalMapPath | ||
// globalMapPath example: | ||
// plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumeID} | ||
// plugins/kubernetes.io/gce-pd/volumeDevices/vol-XXXXXX | ||
pdName := filepath.Base(globalMapPath) | ||
if len(pdName) <= 1 { | ||
return nil, fmt.Errorf("failed to get pd name from global path=%s", globalMapPath) | ||
} | ||
block := v1.PersistentVolumeBlock | ||
gceVolume := &v1.PersistentVolume{ | ||
Spec: v1.PersistentVolumeSpec{ | ||
PersistentVolumeSource: v1.PersistentVolumeSource{ | ||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ | ||
PDName: pdName, | ||
}, | ||
}, | ||
VolumeMode: &block, | ||
}, | ||
} | ||
|
||
return volume.NewSpecFromPersistentVolume(gceVolume, true), nil | ||
} | ||
|
||
// NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification. | ||
func (plugin *gcePersistentDiskPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) { | ||
// If this is called via GenerateUnmapDeviceFunc(), pod is nil. | ||
// Pass empty string as dummy uid since uid isn't used in the case. | ||
var uid types.UID | ||
if pod != nil { | ||
uid = pod.UID | ||
} | ||
|
||
return plugin.newBlockVolumeMapperInternal(spec, uid, &GCEDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName())) | ||
} | ||
|
||
func (plugin *gcePersistentDiskPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.BlockVolumeMapper, error) { | ||
volumeSource, readOnly, err := getVolumeSource(spec) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pdName := volumeSource.PDName | ||
partition := "" | ||
if volumeSource.Partition != 0 { | ||
partition = strconv.Itoa(int(volumeSource.Partition)) | ||
} | ||
|
||
return &gcePersistentDiskMapper{ | ||
gcePersistentDisk: &gcePersistentDisk{ | ||
volName: spec.Name(), | ||
podUID: podUID, | ||
pdName: pdName, | ||
partition: partition, | ||
manager: manager, | ||
mounter: mounter, | ||
plugin: plugin, | ||
}, | ||
readOnly: readOnly}, nil | ||
} | ||
|
||
func (plugin *gcePersistentDiskPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) { | ||
return plugin.newUnmapperInternal(volName, podUID, &GCEDiskUtil{}) | ||
} | ||
|
||
func (plugin *gcePersistentDiskPlugin) newUnmapperInternal(volName string, podUID types.UID, manager pdManager) (volume.BlockVolumeUnmapper, error) { | ||
return &gcePersistentDiskUnmapper{ | ||
gcePersistentDisk: &gcePersistentDisk{ | ||
volName: volName, | ||
podUID: podUID, | ||
pdName: volName, | ||
manager: manager, | ||
plugin: plugin, | ||
}}, nil | ||
} | ||
|
||
func (c *gcePersistentDiskUnmapper) TearDownDevice(mapPath, devicePath string) error { | ||
return nil | ||
} | ||
|
||
type gcePersistentDiskUnmapper struct { | ||
*gcePersistentDisk | ||
} | ||
|
||
var _ volume.BlockVolumeUnmapper = &gcePersistentDiskUnmapper{} | ||
|
||
type gcePersistentDiskMapper struct { | ||
*gcePersistentDisk | ||
readOnly bool | ||
} | ||
|
||
var _ volume.BlockVolumeMapper = &gcePersistentDiskMapper{} | ||
|
||
func (b *gcePersistentDiskMapper) SetUpDevice() (string, error) { | ||
return "", nil | ||
} | ||
|
||
// GetGlobalMapPath returns global map path and error | ||
// path: plugins/kubernetes.io/{PluginName}/volumeDevices/pdName | ||
func (pd *gcePersistentDisk) GetGlobalMapPath(spec *volume.Spec) (string, error) { | ||
volumeSource, _, err := getVolumeSource(spec) | ||
if err != nil { | ||
return "", err | ||
} | ||
return path.Join(pd.plugin.host.GetVolumeDevicePluginDir(gcePersistentDiskPluginName), string(volumeSource.PDName)), nil | ||
} | ||
|
||
// GetPodDeviceMapPath returns pod device map path and volume name | ||
// path: pods/{podUid}/volumeDevices/kubernetes.io~aws | ||
func (pd *gcePersistentDisk) GetPodDeviceMapPath() (string, string) { | ||
name := gcePersistentDiskPluginName | ||
return pd.plugin.host.GetPodVolumeDeviceDir(pd.podUID, kstrings.EscapeQualifiedNameForDisk(name)), pd.volName | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/* | ||
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 gce_pd | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"testing" | ||
|
||
"k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
utiltesting "k8s.io/client-go/util/testing" | ||
"k8s.io/kubernetes/pkg/volume" | ||
volumetest "k8s.io/kubernetes/pkg/volume/testing" | ||
) | ||
|
||
const ( | ||
testPdName = "pdVol1" | ||
testPVName = "pv1" | ||
testGlobalPath = "plugins/kubernetes.io/gce-pd/volumeDevices/pdVol1" | ||
testPodPath = "pods/poduid/volumeDevices/kubernetes.io~gce-pd" | ||
) | ||
|
||
func TestGetVolumeSpecFromGlobalMapPath(t *testing.T) { | ||
// make our test path for fake GlobalMapPath | ||
// /tmp symbolized our pluginDir | ||
// /tmp/testGlobalPathXXXXX/plugins/kubernetes.io/gce-pd/volumeDevices/pdVol1 | ||
tmpVDir, err := utiltesting.MkTmpdir("gceBlockTest") | ||
if err != nil { | ||
t.Fatalf("can't make a temp dir: %v", err) | ||
} | ||
//deferred clean up | ||
defer os.RemoveAll(tmpVDir) | ||
|
||
expectedGlobalPath := path.Join(tmpVDir, testGlobalPath) | ||
|
||
//Bad Path | ||
badspec, err := getVolumeSpecFromGlobalMapPath("") | ||
if badspec != nil || err == nil { | ||
t.Errorf("Expected not to get spec from GlobalMapPath but did") | ||
} | ||
|
||
// Good Path | ||
spec, err := getVolumeSpecFromGlobalMapPath(expectedGlobalPath) | ||
if spec == nil || err != nil { | ||
t.Fatalf("Failed to get spec from GlobalMapPath: %v", err) | ||
} | ||
if spec.PersistentVolume.Spec.GCEPersistentDisk.PDName != testPdName { | ||
t.Errorf("Invalid pdName from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.GCEPersistentDisk.PDName) | ||
} | ||
block := v1.PersistentVolumeBlock | ||
specMode := spec.PersistentVolume.Spec.VolumeMode | ||
if &specMode == nil { | ||
t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v expected: %v", &specMode, block) | ||
} | ||
if *specMode != block { | ||
t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v expected: %v", *specMode, block) | ||
} | ||
} | ||
|
||
func getTestVolume(readOnly bool, path string, isBlock bool) *volume.Spec { | ||
pv := &v1.PersistentVolume{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: testPVName, | ||
}, | ||
Spec: v1.PersistentVolumeSpec{ | ||
PersistentVolumeSource: v1.PersistentVolumeSource{ | ||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ | ||
PDName: testPdName, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
if isBlock { | ||
blockMode := v1.PersistentVolumeBlock | ||
pv.Spec.VolumeMode = &blockMode | ||
} | ||
return volume.NewSpecFromPersistentVolume(pv, readOnly) | ||
} | ||
|
||
func TestGetPodAndPluginMapPaths(t *testing.T) { | ||
tmpVDir, err := utiltesting.MkTmpdir("gceBlockTest") | ||
if err != nil { | ||
t.Fatalf("can't make a temp dir: %v", err) | ||
} | ||
//deferred clean up | ||
defer os.RemoveAll(tmpVDir) | ||
|
||
expectedGlobalPath := path.Join(tmpVDir, testGlobalPath) | ||
expectedPodPath := path.Join(tmpVDir, testPodPath) | ||
|
||
spec := getTestVolume(false, tmpVDir, true /*isBlock*/) | ||
plugMgr := volume.VolumePluginMgr{} | ||
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpVDir, nil, nil)) | ||
plug, err := plugMgr.FindMapperPluginByName(gcePersistentDiskPluginName) | ||
if err != nil { | ||
os.RemoveAll(tmpVDir) | ||
t.Fatalf("Can't find the plugin by name: %q", gcePersistentDiskPluginName) | ||
} | ||
if plug.GetPluginName() != gcePersistentDiskPluginName { | ||
t.Fatalf("Wrong name: %s", plug.GetPluginName()) | ||
} | ||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} | ||
mapper, err := plug.NewBlockVolumeMapper(spec, pod, volume.VolumeOptions{}) | ||
if err != nil { | ||
t.Fatalf("Failed to make a new Mounter: %v", err) | ||
} | ||
if mapper == nil { | ||
t.Fatalf("Got a nil Mounter") | ||
} | ||
|
||
//GetGlobalMapPath | ||
gMapPath, err := mapper.GetGlobalMapPath(spec) | ||
if err != nil || len(gMapPath) == 0 { | ||
t.Fatalf("Invalid GlobalMapPath from spec: %s", spec.PersistentVolume.Spec.GCEPersistentDisk.PDName) | ||
} | ||
if gMapPath != expectedGlobalPath { | ||
t.Errorf("Failed to get GlobalMapPath: %s %s", gMapPath, expectedGlobalPath) | ||
} | ||
|
||
//GetPodDeviceMapPath | ||
gDevicePath, gVolName := mapper.GetPodDeviceMapPath() | ||
if gDevicePath != expectedPodPath { | ||
t.Errorf("Got unexpected pod path: %s, expected %s", gDevicePath, expectedPodPath) | ||
} | ||
if gVolName != testPVName { | ||
t.Errorf("Got unexpected volNamne: %s, expected %s", gVolName, testPVName) | ||
} | ||
} |