mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
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:
commit
ded1f58779
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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 = [
|
||||
|
208
staging/src/k8s.io/csi-translation-lib/plugins/vsphere_volume.go
Normal file
208
staging/src/k8s.io/csi-translation-lib/plugins/vsphere_volume.go
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ var (
|
||||
plugins.CinderDriverName: plugins.NewOpenStackCinderCSITranslator(),
|
||||
plugins.AzureDiskDriverName: plugins.NewAzureDiskCSITranslator(),
|
||||
plugins.AzureFileDriverName: plugins.NewAzureFileCSITranslator(),
|
||||
plugins.VSphereDriverName: plugins.NewvSphereCSITranslator(),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user