Fix AWS block volume reconstruction to be like file

This commit is contained in:
Matthew Wong 2019-10-08 09:57:38 -07:00
parent d70b2db1f2
commit 82786ff720
5 changed files with 108 additions and 58 deletions

View File

@ -263,41 +263,13 @@ func (plugin *awsElasticBlockStorePlugin) ConstructVolumeSpec(volName, mountPath
if err != nil { if err != nil {
return nil, err return nil, err
} }
// This is a workaround to fix the issue in converting aws volume id from globalPDPath volumeID, err = formatVolumeID(volumeID)
// There are three aws volume id formats and their volumeID from GetDeviceNameFromMount() are: if err != nil {
// aws:///vol-1234 (aws/vol-1234) return nil, fmt.Errorf("failed to get AWS volume id from mount path %q: %v", mountPath, err)
// aws://us-east-1/vol-1234 (aws/us-east-1/vol-1234)
// vol-1234 (vol-1234)
// This code is for converting volume id to aws style volume id for the first two cases.
sourceName := volumeID
if strings.HasPrefix(volumeID, "aws/") {
names := strings.Split(volumeID, "/")
length := len(names)
if length < 2 || length > 3 {
return nil, fmt.Errorf("Failed to get AWS volume id from mount path %q: invalid volume name format %q", mountPath, volumeID)
}
volName := names[length-1]
if !strings.HasPrefix(volName, "vol-") {
return nil, fmt.Errorf("Invalid volume name format for AWS volume (%q) retrieved from mount path %q", volName, mountPath)
}
if length == 2 {
sourceName = awsURLNamePrefix + "" + "/" + volName // empty zone label
}
if length == 3 {
sourceName = awsURLNamePrefix + names[1] + "/" + volName // names[1] is the zone label
}
klog.V(4).Infof("Convert aws volume name from %q to %q ", volumeID, sourceName)
} }
awsVolume := &v1.Volume{ file := v1.PersistentVolumeFilesystem
Name: volName, return newAWSVolumeSpec(volName, volumeID, file), nil
VolumeSource: v1.VolumeSource{
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
VolumeID: sourceName,
},
},
}
return volume.NewSpecFromVolume(awsVolume), nil
} }
func (plugin *awsElasticBlockStorePlugin) RequiresFSResize() bool { func (plugin *awsElasticBlockStorePlugin) RequiresFSResize() bool {

View File

@ -25,7 +25,6 @@ import (
"strings" "strings"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
@ -52,37 +51,27 @@ func (plugin *awsElasticBlockStorePlugin) ConstructBlockVolumeSpec(podUID types.
return nil, fmt.Errorf("failed to get volume plugin information from globalMapPathUUID: %v", globalMapPathUUID) return nil, fmt.Errorf("failed to get volume plugin information from globalMapPathUUID: %v", globalMapPathUUID)
} }
return getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath) return plugin.getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath)
} }
func getVolumeSpecFromGlobalMapPath(volumeName string, globalMapPath string) (*volume.Spec, error) { func (plugin *awsElasticBlockStorePlugin) getVolumeSpecFromGlobalMapPath(volumeName string, globalMapPath string) (*volume.Spec, error) {
// Get volume spec information from globalMapPath // Get volume spec information from globalMapPath
// globalMapPath example: // globalMapPath example:
// plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumeID} // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumeID}
// plugins/kubernetes.io/aws-ebs/volumeDevices/vol-XXXXXX // plugins/kubernetes.io/aws-ebs/volumeDevices/vol-XXXXXX
vID := filepath.Base(globalMapPath) pluginDir := plugin.host.GetVolumeDevicePluginDir(awsElasticBlockStorePluginName)
if len(vID) <= 1 { if !strings.HasPrefix(globalMapPath, pluginDir) {
return nil, fmt.Errorf("failed to get volumeID from global path=%s", globalMapPath) return nil, fmt.Errorf("volume symlink %s is not in global plugin directory", globalMapPath)
} }
if !strings.Contains(vID, "vol-") { fullVolumeID := strings.TrimPrefix(globalMapPath, pluginDir) // /vol-XXXXXX
return nil, fmt.Errorf("failed to get volumeID from global path=%s, invalid volumeID format = %s", globalMapPath, vID) fullVolumeID = strings.TrimLeft(fullVolumeID, "/") // vol-XXXXXX
} vID, err := formatVolumeID(fullVolumeID)
block := v1.PersistentVolumeBlock if err != nil {
awsVolume := &v1.PersistentVolume{ return nil, fmt.Errorf("failed to get AWS volume id from map path %q: %v", globalMapPath, err)
ObjectMeta: metav1.ObjectMeta{
Name: volumeName,
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
VolumeID: vID,
},
},
VolumeMode: &block,
},
} }
return volume.NewSpecFromPersistentVolume(awsVolume, true), nil block := v1.PersistentVolumeBlock
return newAWSVolumeSpec(volumeName, vID, block), nil
} }
// NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification. // NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification.

View File

@ -51,14 +51,22 @@ func TestGetVolumeSpecFromGlobalMapPath(t *testing.T) {
expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath) expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpVDir, nil, nil))
plug, err := plugMgr.FindMapperPluginByName(awsElasticBlockStorePluginName)
if err != nil {
os.RemoveAll(tmpVDir)
t.Fatalf("Can't find the plugin by name: %q", awsElasticBlockStorePluginName)
}
//Bad Path //Bad Path
badspec, err := getVolumeSpecFromGlobalMapPath("", "") badspec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("", "")
if badspec != nil || err == nil { if badspec != nil || err == nil {
t.Fatalf("Expected not to get spec from GlobalMapPath but did") t.Fatalf("Expected not to get spec from GlobalMapPath but did")
} }
// Good Path // Good Path
spec, err := getVolumeSpecFromGlobalMapPath("myVolume", expectedGlobalPath) spec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("myVolume", expectedGlobalPath)
if spec == nil || err != nil { if spec == nil || err != nil {
t.Fatalf("Failed to get spec from GlobalMapPath: %v", err) t.Fatalf("Failed to get spec from GlobalMapPath: %v", err)
} }

View File

@ -414,3 +414,36 @@ func TestGetCandidateZone(t *testing.T) {
assert.Equal(t, test.expectedZones, zones) assert.Equal(t, test.expectedZones, zones)
} }
} }
func TestFormatVolumeID(t *testing.T) {
tests := []struct {
volumeIDFromPath string
expectedVolumeID string
}{
{
"aws/vol-1234",
"aws:///vol-1234",
},
{
"aws:/vol-1234",
"aws:///vol-1234",
},
{
"aws/us-east-1/vol-1234",
"aws://us-east-1/vol-1234",
},
{
"aws:/us-east-1/vol-1234",
"aws://us-east-1/vol-1234",
},
{
"vol-1234",
"vol-1234",
},
}
for _, test := range tests {
volumeID, err := formatVolumeID(test.volumeIDFromPath)
assert.Nil(t, err)
assert.Equal(t, test.expectedVolumeID, volumeID, test.volumeIDFromPath)
}
}

View File

@ -30,6 +30,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
cloudprovider "k8s.io/cloud-provider" cloudprovider "k8s.io/cloud-provider"
volumehelpers "k8s.io/cloud-provider/volume/helpers" volumehelpers "k8s.io/cloud-provider/volume/helpers"
@ -287,3 +288,50 @@ func findNvmeVolume(findName string) (device string, err error) {
return resolved, nil return resolved, nil
} }
func formatVolumeID(volumeID string) (string, error) {
// This is a workaround to fix the issue in converting aws volume id from globalPDPath and globalMapPath
// There are three formats for AWSEBSVolumeSource.VolumeID and they are stored on disk in paths like so:
// VolumeID mountPath mapPath
// aws:///vol-1234 aws/vol-1234 aws:/vol-1234
// aws://us-east-1/vol-1234 aws/us-east-1/vol-1234 aws:/us-east-1/vol-1234
// vol-1234 vol-1234 vol-1234
// This code is for converting volume ids from paths back to AWS style VolumeIDs
sourceName := volumeID
if strings.HasPrefix(volumeID, "aws/") || strings.HasPrefix(volumeID, "aws:/") {
names := strings.Split(volumeID, "/")
length := len(names)
if length < 2 || length > 3 {
return "", fmt.Errorf("invalid volume name format %q", volumeID)
}
volName := names[length-1]
if !strings.HasPrefix(volName, "vol-") {
return "", fmt.Errorf("Invalid volume name format for AWS volume (%q)", volName)
}
if length == 2 {
sourceName = awsURLNamePrefix + "" + "/" + volName // empty zone label
}
if length == 3 {
sourceName = awsURLNamePrefix + names[1] + "/" + volName // names[1] is the zone label
}
klog.V(4).Infof("Convert aws volume name from %q to %q ", volumeID, sourceName)
}
return sourceName, nil
}
func newAWSVolumeSpec(volumeName, volumeID string, mode v1.PersistentVolumeMode) *volume.Spec {
awsVolume := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: volumeName,
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
VolumeID: volumeID,
},
},
VolumeMode: &mode,
},
}
return volume.NewSpecFromPersistentVolume(awsVolume, false)
}