Merge pull request #90911 from divyenpatel/vsphere-csi-migration

Support for vSphere in-tree volumes migration to vSphere CSI driver
This commit is contained in:
Kubernetes Prow Robot 2020-06-13 04:25:55 -07:00 committed by GitHub
commit ded1f58779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 588 additions and 6 deletions

View File

@ -64,6 +64,7 @@ func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, fea
pluginMigrationStatus[plugins.GCEPDInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationGCE, pluginMigrationCompleteFeature: features.CSIMigrationGCEComplete, pluginProbeFunction: gcepd.ProbeVolumePlugins}
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
var err error
for pluginName, pluginInfo := range pluginMigrationStatus {
@ -72,8 +73,6 @@ func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, fea
return allPlugins, err
}
}
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
return allPlugins, nil
}
@ -88,6 +87,7 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
pluginMigrationStatus[plugins.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
var err error
for pluginName, pluginInfo := range pluginMigrationStatus {
@ -96,7 +96,5 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
return allPlugins, err
}
}
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
return allPlugins, nil
}

View File

@ -65,6 +65,7 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
pluginMigrationStatus[plugins.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
var err error
for pluginName, pluginInfo := range pluginMigrationStatus {
@ -73,7 +74,5 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
return allPlugins, err
}
}
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
return allPlugins, nil
}

View File

@ -410,6 +410,19 @@ const (
// Expects Azure File CSI Driver to be installed and configured on all nodes.
CSIMigrationAzureFileComplete featuregate.Feature = "CSIMigrationAzureFileComplete"
// owner: @divyenpatel
// alpha: v1.19
//
// Enables the vSphere in-tree driver to vSphere CSI Driver migration feature.
CSIMigrationvSphere featuregate.Feature = "CSIMigrationvSphere"
// owner: @divyenpatel
// alpha: v1.19
//
// Disables the vSphere in-tree driver.
// Expects vSphere CSI Driver to be installed and configured on all nodes.
CSIMigrationvSphereComplete featuregate.Feature = "CSIMigrationvSphereComplete"
// owner: @gnufied
// alpha: v1.18
// Allows user to configure volume permission change policy for fsGroups when mounting
@ -631,6 +644,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
CSIMigrationAzureDiskComplete: {Default: false, PreRelease: featuregate.Alpha},
CSIMigrationAzureFile: {Default: false, PreRelease: featuregate.Alpha},
CSIMigrationAzureFileComplete: {Default: false, PreRelease: featuregate.Alpha},
CSIMigrationvSphere: {Default: false, PreRelease: featuregate.Alpha},
CSIMigrationvSphereComplete: {Default: false, PreRelease: featuregate.Alpha},
RunAsGroup: {Default: true, PreRelease: featuregate.Beta},
CSIMigrationOpenStack: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires OpenStack Cinder CSI driver)
CSIMigrationOpenStackComplete: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -219,6 +219,9 @@ func (p *csiPlugin) Init(host volume.VolumeHost) error {
csitranslationplugins.AzureFileInTreePluginName: func() bool {
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
},
csitranslationplugins.VSphereInTreePluginName: func() bool {
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
},
}
// Initializing the label management channels

View File

@ -68,6 +68,8 @@ func (pm PluginManager) IsMigrationCompleteForPlugin(pluginName string) bool {
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDiskComplete)
case csilibplugins.CinderInTreePluginName:
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStackComplete)
case csilibplugins.VSphereInTreePluginName:
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphereComplete)
default:
return false
}
@ -92,6 +94,8 @@ func (pm PluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk)
case csilibplugins.CinderInTreePluginName:
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
case csilibplugins.VSphereInTreePluginName:
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
default:
return false
}

View File

@ -20,6 +20,7 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/volume/vsphere_volume",
deps = [
"//pkg/features:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library",
"//pkg/volume/util/volumepathhandler:go_default_library",
@ -27,6 +28,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
"//staging/src/k8s.io/legacy-cloud-providers/vsphere:go_default_library",

View File

@ -25,6 +25,7 @@ import (
"runtime"
"strings"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
"k8s.io/utils/mount"
utilstrings "k8s.io/utils/strings"
@ -34,6 +35,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
volumehelpers "k8s.io/cloud-provider/volume/helpers"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)
@ -70,6 +73,11 @@ func (plugin *vsphereVolumePlugin) GetPluginName() string {
return vsphereVolumePluginName
}
func (plugin *vsphereVolumePlugin) IsMigratedToCSI() bool {
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
}
func (plugin *vsphereVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
volumeSource, _, err := getVolumeSource(spec)
if err != nil {

View File

@ -9,6 +9,7 @@ go_library(
"gce_pd.go",
"in_tree_volume.go",
"openstack_cinder.go",
"vsphere_volume.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/csi-translation-lib/plugins",
importpath = "k8s.io/csi-translation-lib/plugins",
@ -45,6 +46,7 @@ go_test(
"azure_file_test.go",
"gce_pd_test.go",
"in_tree_volume_test.go",
"vsphere_volume_test.go",
],
embed = [":go_default_library"],
deps = [

View File

@ -0,0 +1,208 @@
/*
Copyright 2020 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 plugins
import (
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
)
const (
// VSphereDriverName is the name of the CSI driver for vSphere Volume
VSphereDriverName = "csi.vsphere.vmware.com"
// VSphereInTreePluginName is the name of the in-tree plugin for vSphere Volume
VSphereInTreePluginName = "kubernetes.io/vsphere-volume"
// paramStoragePolicyName used to supply SPBM Policy name for Volume provisioning
paramStoragePolicyName = "storagepolicyname"
// This param is used to tell Driver to return volumePath and not VolumeID
// in-tree vSphere plugin does not understand volume id, it uses volumePath
paramcsiMigration = "csimigration"
// This param is used to supply datastore name for Volume provisioning
paramDatastore = "datastore-migrationparam"
// This param supplies disk foramt (thin, thick, zeoredthick) for Volume provisioning
paramDiskFormat = "diskformat-migrationparam"
// vSAN Policy Parameters
paramHostFailuresToTolerate = "hostfailurestotolerate-migrationparam"
paramForceProvisioning = "forceprovisioning-migrationparam"
paramCacheReservation = "cachereservation-migrationparam"
paramDiskstripes = "diskstripes-migrationparam"
paramObjectspacereservation = "objectspacereservation-migrationparam"
paramIopslimit = "iopslimit-migrationparam"
// AttributeInitialVolumeFilepath represents the path of volume where volume is created
AttributeInitialVolumeFilepath = "initialvolumefilepath"
)
var _ InTreePlugin = &vSphereCSITranslator{}
// vSphereCSITranslator handles translation of PV spec from In-tree vSphere Volume to vSphere CSI
type vSphereCSITranslator struct{}
// NewvSphereCSITranslator returns a new instance of vSphereCSITranslator
func NewvSphereCSITranslator() InTreePlugin {
return &vSphereCSITranslator{}
}
// TranslateInTreeStorageClassToCSI translates InTree vSphere storage class parameters to CSI storage class
func (t *vSphereCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) {
if sc == nil {
return nil, fmt.Errorf("sc is nil")
}
var params = map[string]string{}
for k, v := range sc.Parameters {
switch strings.ToLower(k) {
case fsTypeKey:
params[csiFsTypeKey] = v
case paramStoragePolicyName:
params[paramStoragePolicyName] = v
case "datastore":
params[paramDatastore] = v
case "diskformat":
params[paramDiskFormat] = v
case "hostfailurestotolerate":
params[paramHostFailuresToTolerate] = v
case "forceprovisioning":
params[paramForceProvisioning] = v
case "cachereservation":
params[paramCacheReservation] = v
case "diskstripes":
params[paramDiskstripes] = v
case "objectspacereservation":
params[paramObjectspacereservation] = v
case "iopslimit":
params[paramIopslimit] = v
default:
klog.V(2).Infof("StorageClass parameter [name:%q, value:%q] is not supported", k, v)
}
}
// This helps vSphere CSI driver to identify in-tree provisioner request vs CSI provisioner request
// When this is true, Driver returns initialvolumefilepath in the VolumeContext, which is
// used in TranslateCSIPVToInTree
params[paramcsiMigration] = "true"
// Note: sc.AllowedTopologies for Topology based volume provisioning will be supplied as it is.
sc.Parameters = params
return sc, nil
}
// TranslateInTreeInlineVolumeToCSI takes a Volume with VsphereVolume set from in-tree
// and converts the VsphereVolume source to a CSIPersistentVolumeSource
func (t *vSphereCSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Volume) (*v1.PersistentVolume, error) {
if volume == nil || volume.VsphereVolume == nil {
return nil, fmt.Errorf("volume is nil or VsphereVolume not defined on volume")
}
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
// Must be unique per disk as it is used as the unique part of the
// staging path
Name: fmt.Sprintf("%s-%s", VSphereDriverName, volume.VsphereVolume.VolumePath),
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
Driver: VSphereDriverName,
VolumeHandle: volume.VsphereVolume.VolumePath,
FSType: volume.VsphereVolume.FSType,
VolumeAttributes: make(map[string]string),
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
}
if volume.VsphereVolume.StoragePolicyName != "" {
pv.Spec.CSI.VolumeAttributes[paramStoragePolicyName] = pv.Spec.VsphereVolume.StoragePolicyName
}
return pv, nil
}
// TranslateInTreePVToCSI takes a PV with VsphereVolume set from in-tree
// and converts the VsphereVolume source to a CSIPersistentVolumeSource
func (t *vSphereCSITranslator) TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
if pv == nil || pv.Spec.VsphereVolume == nil {
return nil, fmt.Errorf("pv is nil or VsphereVolume not defined on pv")
}
csiSource := &v1.CSIPersistentVolumeSource{
Driver: VSphereDriverName,
VolumeHandle: pv.Spec.VsphereVolume.VolumePath,
FSType: pv.Spec.VsphereVolume.FSType,
VolumeAttributes: make(map[string]string),
}
if pv.Spec.VsphereVolume.StoragePolicyName != "" {
csiSource.VolumeAttributes[paramStoragePolicyName] = pv.Spec.VsphereVolume.StoragePolicyName
}
pv.Spec.VsphereVolume = nil
pv.Spec.CSI = csiSource
return pv, nil
}
// TranslateCSIPVToInTree takes a PV with CSIPersistentVolumeSource set and
// translates the vSphere CSI source to a vSphereVolume source.
func (t *vSphereCSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
if pv == nil || pv.Spec.CSI == nil {
return nil, fmt.Errorf("pv is nil or CSI source not defined on pv")
}
csiSource := pv.Spec.CSI
vsphereVirtualDiskVolumeSource := &v1.VsphereVirtualDiskVolumeSource{
FSType: csiSource.FSType,
}
volumeFilePath, ok := csiSource.VolumeAttributes[AttributeInitialVolumeFilepath]
if ok {
vsphereVirtualDiskVolumeSource.VolumePath = volumeFilePath
}
pv.Spec.CSI = nil
pv.Spec.VsphereVolume = vsphereVirtualDiskVolumeSource
return pv, nil
}
// CanSupport tests whether the plugin supports a given persistent volume
// specification from the API.
func (t *vSphereCSITranslator) CanSupport(pv *v1.PersistentVolume) bool {
return pv != nil && pv.Spec.VsphereVolume != nil
}
// CanSupportInline tests whether the plugin supports a given inline volume
// specification from the API.
func (t *vSphereCSITranslator) CanSupportInline(volume *v1.Volume) bool {
return volume != nil && volume.VsphereVolume != nil
}
// GetInTreePluginName returns the name of the in-tree plugin driver
func (t *vSphereCSITranslator) GetInTreePluginName() string {
return VSphereInTreePluginName
}
// GetCSIPluginName returns the name of the CSI plugin
func (t *vSphereCSITranslator) GetCSIPluginName() string {
return VSphereDriverName
}
// RepairVolumeHandle is needed in VerifyVolumesAttached on the external attacher when we need to do strict volume
// handle matching to check VolumeAttachment attached status.
// vSphere volume does not need patch to help verify whether that volume is attached.
func (t *vSphereCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
return volumeHandle, nil
}

View File

@ -0,0 +1,335 @@
/*
Copyright 2020 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 plugins
import (
"fmt"
"reflect"
"testing"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestTranslatevSphereInTreeStorageClassToCSI(t *testing.T) {
translator := NewvSphereCSITranslator()
topologySelectorTerm := v1.TopologySelectorTerm{[]v1.TopologySelectorLabelRequirement{
{
Key: v1.LabelZoneFailureDomain,
Values: []string{"zone-a"},
},
{
Key: v1.LabelZoneRegion,
Values: []string{"region-a"},
},
}}
cases := []struct {
name string
sc *storage.StorageClass
expSc *storage.StorageClass
expErr bool
}{
{
name: "expect error when sc is nil",
sc: nil,
expSc: nil,
expErr: true,
},
{
name: "translate with no parameter",
sc: NewStorageClass(map[string]string{}, nil),
expSc: NewStorageClass(map[string]string{paramcsiMigration: "true"}, nil),
},
{
name: "translate with unknown parameter",
sc: NewStorageClass(map[string]string{"unknownparam": "value"}, nil),
expSc: NewStorageClass(map[string]string{paramcsiMigration: "true"}, nil),
},
{
name: "translate with storagepolicyname and datastore",
sc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", "datastore": "vsanDatastore"}, nil),
expSc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", "datastore-migrationparam": "vsanDatastore", paramcsiMigration: "true"}, nil),
},
{
name: "translate with fstype",
sc: NewStorageClass(map[string]string{"fstype": "ext4"}, nil),
expSc: NewStorageClass(map[string]string{"csi.storage.k8s.io/fstype": "ext4", paramcsiMigration: "true"}, nil),
},
{
name: "translate with storagepolicyname and fstype",
sc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", "fstype": "ext4"}, nil),
expSc: NewStorageClass(map[string]string{"csi.storage.k8s.io/fstype": "ext4", "storagepolicyname": "test-policy-name", paramcsiMigration: "true"}, nil),
},
{
name: "translate with no parameter and allowedTopology",
sc: NewStorageClass(map[string]string{}, []v1.TopologySelectorTerm{topologySelectorTerm}),
expSc: NewStorageClass(map[string]string{paramcsiMigration: "true"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
},
{
name: "translate with storagepolicyname and allowedTopology",
sc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
expSc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", paramcsiMigration: "true"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
},
{
name: "translate with raw vSAN policy parameters, datastore and diskformat",
sc: NewStorageClass(map[string]string{"hostfailurestotolerate": "2", "datastore": "vsanDatastore", "diskformat": "thin"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
expSc: NewStorageClass(map[string]string{"hostfailurestotolerate-migrationparam": "2", "datastore-migrationparam": "vsanDatastore", "diskformat-migrationparam": "thin", paramcsiMigration: "true"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
},
{
name: "translate with all parameters",
sc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", "datastore": "test-datastore-name", "fstype": "ext4", "diskformat": "thin", "hostfailurestotolerate": "1", "forceprovisioning": "yes", "cachereservation": "25", "diskstripes": "4", "objectspacereservation": "10", "iopslimit": "32"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
expSc: NewStorageClass(map[string]string{"storagepolicyname": "test-policy-name", "datastore-migrationparam": "test-datastore-name", "csi.storage.k8s.io/fstype": "ext4", "diskformat-migrationparam": "thin", "hostfailurestotolerate-migrationparam": "1", "forceprovisioning-migrationparam": "yes", "cachereservation-migrationparam": "25", "diskstripes-migrationparam": "4", "objectspacereservation-migrationparam": "10", "iopslimit-migrationparam": "32", paramcsiMigration: "true"}, []v1.TopologySelectorTerm{topologySelectorTerm}),
},
}
for _, tc := range cases {
t.Logf("Testing %v", tc.name)
got, err := translator.TranslateInTreeStorageClassToCSI(tc.sc)
if err != nil && !tc.expErr {
t.Errorf("Did not expect error but got: %v", err)
}
if err == nil && tc.expErr {
t.Errorf("Expected error, but did not get one.")
}
if !reflect.DeepEqual(got, tc.expSc) {
t.Errorf("Got parameters: %v, expected :%v", got, tc.expSc)
}
}
}
func TestTranslateVSphereCSIPVToInTree(t *testing.T) {
translator := NewvSphereCSITranslator()
cases := []struct {
name string
csiPV *v1.PersistentVolume
intreePV *v1.PersistentVolume
expErr bool
}{
{
name: "expect error when pv is nil",
csiPV: nil,
intreePV: nil,
expErr: true,
},
{
name: "expect error when pv.Spec.CSI is nil",
csiPV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-d8b4475f-2c47-486e-9b57-43ae006f9b59",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: nil,
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
intreePV: nil,
expErr: true,
},
{
name: "translate valid vSphere CSI PV to vSphere in-tree PV",
csiPV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-d8b4475f-2c47-486e-9b57-43ae006f9b59",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
Driver: VSphereDriverName,
VolumeHandle: "e4073a6d-642e-4dff-8f4a-b4e3a47c4bbd",
FSType: "ext4",
VolumeAttributes: map[string]string{
paramStoragePolicyName: "vSAN Default Storage Policy",
AttributeInitialVolumeFilepath: "[vsanDatastore] 6785a85e-268e-6352-a2e8-02008b7afadd/kubernetes-dynamic-pvc-68734c9f-a679-42e6-a694-39632c51e31f.vmdk",
},
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
intreePV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-d8b4475f-2c47-486e-9b57-43ae006f9b59",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
VolumePath: "[vsanDatastore] 6785a85e-268e-6352-a2e8-02008b7afadd/kubernetes-dynamic-pvc-68734c9f-a679-42e6-a694-39632c51e31f.vmdk",
FSType: "ext4",
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
expErr: false,
},
}
for _, tc := range cases {
t.Logf("Testing %v", tc.name)
got, err := translator.TranslateCSIPVToInTree(tc.csiPV)
if err != nil && !tc.expErr {
t.Errorf("Did not expect error but got: %v", err)
}
if err == nil && tc.expErr {
t.Errorf("Expected error, but did not get one.")
}
if !reflect.DeepEqual(got, tc.intreePV) {
t.Errorf("Got PV: %v, expected :%v", got, tc.intreePV)
}
}
}
func TestTranslateVSphereInTreePVToCSI(t *testing.T) {
translator := NewvSphereCSITranslator()
cases := []struct {
name string
intreePV *v1.PersistentVolume
csiPV *v1.PersistentVolume
expErr bool
}{
{
name: "expect error when in-tree vsphere PV is nil",
intreePV: &v1.PersistentVolume{},
csiPV: nil,
expErr: true,
},
{
name: "translate valid vSphere in-tree PV to vSphere CSI PV",
intreePV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-d8b4475f-2c47-486e-9b57-43ae006f9b59",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
VolumePath: "[vsanDatastore] 6785a85e-268e-6352-a2e8-02008b7afadd/kubernetes-dynamic-pvc-68734c9f-a679-42e6-a694-39632c51e31f.vmdk",
FSType: "ext4",
StoragePolicyName: "vSAN Default Storage Policy",
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
csiPV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-d8b4475f-2c47-486e-9b57-43ae006f9b59",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
Driver: VSphereDriverName,
VolumeHandle: "[vsanDatastore] 6785a85e-268e-6352-a2e8-02008b7afadd/kubernetes-dynamic-pvc-68734c9f-a679-42e6-a694-39632c51e31f.vmdk",
FSType: "ext4",
VolumeAttributes: map[string]string{
paramStoragePolicyName: "vSAN Default Storage Policy",
},
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
expErr: false,
},
}
for _, tc := range cases {
t.Logf("Testing %v", tc.name)
got, err := translator.TranslateInTreePVToCSI(tc.intreePV)
if err != nil && !tc.expErr {
t.Errorf("Did not expect error but got: %v", err)
}
if err == nil && tc.expErr {
t.Errorf("Expected error, but did not get one.")
}
if !reflect.DeepEqual(got, tc.csiPV) {
t.Errorf("Got PV: %v, expected :%v", got, tc.csiPV)
}
}
}
func TestTranslatevSphereInTreeInlineVolumeToCSI(t *testing.T) {
translator := NewvSphereCSITranslator()
cases := []struct {
name string
inlinevolume *v1.Volume
csiPV *v1.PersistentVolume
expErr bool
}{
{
name: "expect error when inline vsphere volume is nil",
inlinevolume: &v1.Volume{},
csiPV: nil,
expErr: true,
},
{
name: "translate valid in-tree vsphere volume to vSphere CSI PV",
inlinevolume: &v1.Volume{
Name: "inlinevolume",
VolumeSource: v1.VolumeSource{
VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
VolumePath: "[vsanDatastore] volume/inlinevolume.vmdk",
FSType: "ext4",
},
}},
csiPV: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", VSphereDriverName, "[vsanDatastore] volume/inlinevolume.vmdk"),
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
Driver: VSphereDriverName,
VolumeHandle: "[vsanDatastore] volume/inlinevolume.vmdk",
FSType: "ext4",
VolumeAttributes: make(map[string]string),
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
expErr: false,
},
}
for _, tc := range cases {
t.Logf("Testing %v", tc.name)
got, err := translator.TranslateInTreeInlineVolumeToCSI(tc.inlinevolume)
if err == nil && tc.expErr {
t.Errorf("Expected error, but did not get one.")
continue
}
if err != nil {
if tc.expErr {
t.Logf("expected error received")
continue
} else {
t.Errorf("Did not expect error but got: %v", err)
continue
}
}
if !reflect.DeepEqual(got, tc.csiPV) {
t.Errorf("Got PV: %v, expected :%v", got, tc.csiPV)
}
}
}

View File

@ -32,6 +32,7 @@ var (
plugins.CinderDriverName: plugins.NewOpenStackCinderCSITranslator(),
plugins.AzureDiskDriverName: plugins.NewAzureDiskCSITranslator(),
plugins.AzureFileDriverName: plugins.NewAzureFileCSITranslator(),
plugins.VSphereDriverName: plugins.NewvSphereCSITranslator(),
}
)

View File

@ -384,6 +384,13 @@ func generateUniqueVolumeSource(driverName string) (v1.VolumeSource, error) {
ShareName: string(uuid.NewUUID()),
},
}, nil
case plugins.VSphereDriverName:
return v1.VolumeSource{
VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
VolumePath: " [vsanDatastore] 6785a85e-268e-6352-a2e8-02008b7afadd/kubernetes-dynamic-pvc-" + string(uuid.NewUUID()+".vmdk"),
FSType: "ext4",
},
}, nil
default:
return v1.VolumeSource{}, fmt.Errorf("couldn't find logic for driver: %v", driverName)
}