Skip to content

Commit ded1f58

Browse files
authored
Merge pull request kubernetes#90911 from divyenpatel/vsphere-csi-migration
Support for vSphere in-tree volumes migration to vSphere CSI driver
2 parents f66af10 + 148ef06 commit ded1f58

File tree

12 files changed

+588
-6
lines changed

12 files changed

+588
-6
lines changed

cmd/kube-controller-manager/app/plugins_providers.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, fea
6464
pluginMigrationStatus[plugins.GCEPDInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationGCE, pluginMigrationCompleteFeature: features.CSIMigrationGCEComplete, pluginProbeFunction: gcepd.ProbeVolumePlugins}
6565
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
6666
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
67+
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
6768

6869
var err error
6970
for pluginName, pluginInfo := range pluginMigrationStatus {
@@ -72,8 +73,6 @@ func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, fea
7273
return allPlugins, err
7374
}
7475
}
75-
76-
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
7776
return allPlugins, nil
7877
}
7978

@@ -88,6 +87,7 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
8887
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
8988
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
9089
pluginMigrationStatus[plugins.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
90+
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
9191

9292
var err error
9393
for pluginName, pluginInfo := range pluginMigrationStatus {
@@ -96,7 +96,5 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
9696
return allPlugins, err
9797
}
9898
}
99-
100-
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
10199
return allPlugins, nil
102100
}

cmd/kubelet/app/plugins_providers.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
6565
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
6666
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
6767
pluginMigrationStatus[plugins.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
68+
pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginMigrationCompleteFeature: features.CSIMigrationvSphereComplete, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
6869

6970
var err error
7071
for pluginName, pluginInfo := range pluginMigrationStatus {
@@ -73,7 +74,5 @@ func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate f
7374
return allPlugins, err
7475
}
7576
}
76-
77-
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
7877
return allPlugins, nil
7978
}

pkg/features/kube_features.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,19 @@ const (
410410
// Expects Azure File CSI Driver to be installed and configured on all nodes.
411411
CSIMigrationAzureFileComplete featuregate.Feature = "CSIMigrationAzureFileComplete"
412412

413+
// owner: @divyenpatel
414+
// alpha: v1.19
415+
//
416+
// Enables the vSphere in-tree driver to vSphere CSI Driver migration feature.
417+
CSIMigrationvSphere featuregate.Feature = "CSIMigrationvSphere"
418+
419+
// owner: @divyenpatel
420+
// alpha: v1.19
421+
//
422+
// Disables the vSphere in-tree driver.
423+
// Expects vSphere CSI Driver to be installed and configured on all nodes.
424+
CSIMigrationvSphereComplete featuregate.Feature = "CSIMigrationvSphereComplete"
425+
413426
// owner: @gnufied
414427
// alpha: v1.18
415428
// Allows user to configure volume permission change policy for fsGroups when mounting
@@ -631,6 +644,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
631644
CSIMigrationAzureDiskComplete: {Default: false, PreRelease: featuregate.Alpha},
632645
CSIMigrationAzureFile: {Default: false, PreRelease: featuregate.Alpha},
633646
CSIMigrationAzureFileComplete: {Default: false, PreRelease: featuregate.Alpha},
647+
CSIMigrationvSphere: {Default: false, PreRelease: featuregate.Alpha},
648+
CSIMigrationvSphereComplete: {Default: false, PreRelease: featuregate.Alpha},
634649
RunAsGroup: {Default: true, PreRelease: featuregate.Beta},
635650
CSIMigrationOpenStack: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires OpenStack Cinder CSI driver)
636651
CSIMigrationOpenStackComplete: {Default: false, PreRelease: featuregate.Alpha},

pkg/volume/csi/csi_plugin.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ func (p *csiPlugin) Init(host volume.VolumeHost) error {
219219
csitranslationplugins.AzureFileInTreePluginName: func() bool {
220220
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
221221
},
222+
csitranslationplugins.VSphereInTreePluginName: func() bool {
223+
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
224+
},
222225
}
223226

224227
// Initializing the label management channels

pkg/volume/csimigration/plugin_manager.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ func (pm PluginManager) IsMigrationCompleteForPlugin(pluginName string) bool {
6868
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDiskComplete)
6969
case csilibplugins.CinderInTreePluginName:
7070
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStackComplete)
71+
case csilibplugins.VSphereInTreePluginName:
72+
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphereComplete)
7173
default:
7274
return false
7375
}
@@ -92,6 +94,8 @@ func (pm PluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
9294
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk)
9395
case csilibplugins.CinderInTreePluginName:
9496
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
97+
case csilibplugins.VSphereInTreePluginName:
98+
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
9599
default:
96100
return false
97101
}

pkg/volume/vsphere_volume/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ go_library(
2020
],
2121
importpath = "k8s.io/kubernetes/pkg/volume/vsphere_volume",
2222
deps = [
23+
"//pkg/features:go_default_library",
2324
"//pkg/volume:go_default_library",
2425
"//pkg/volume/util:go_default_library",
2526
"//pkg/volume/util/volumepathhandler:go_default_library",
2627
"//staging/src/k8s.io/api/core/v1:go_default_library",
2728
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
2829
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2930
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
31+
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
3032
"//staging/src/k8s.io/cloud-provider:go_default_library",
3133
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
3234
"//staging/src/k8s.io/legacy-cloud-providers/vsphere:go_default_library",

pkg/volume/vsphere_volume/vsphere_volume.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"runtime"
2626
"strings"
2727

28+
utilfeature "k8s.io/apiserver/pkg/util/feature"
2829
"k8s.io/klog/v2"
2930
"k8s.io/utils/mount"
3031
utilstrings "k8s.io/utils/strings"
@@ -34,6 +35,8 @@ import (
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3536
"k8s.io/apimachinery/pkg/types"
3637
volumehelpers "k8s.io/cloud-provider/volume/helpers"
38+
39+
"k8s.io/kubernetes/pkg/features"
3740
"k8s.io/kubernetes/pkg/volume"
3841
"k8s.io/kubernetes/pkg/volume/util"
3942
)
@@ -70,6 +73,11 @@ func (plugin *vsphereVolumePlugin) GetPluginName() string {
7073
return vsphereVolumePluginName
7174
}
7275

76+
func (plugin *vsphereVolumePlugin) IsMigratedToCSI() bool {
77+
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
78+
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
79+
}
80+
7381
func (plugin *vsphereVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
7482
volumeSource, _, err := getVolumeSource(spec)
7583
if err != nil {

staging/src/k8s.io/csi-translation-lib/plugins/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ go_library(
99
"gce_pd.go",
1010
"in_tree_volume.go",
1111
"openstack_cinder.go",
12+
"vsphere_volume.go",
1213
],
1314
importmap = "k8s.io/kubernetes/vendor/k8s.io/csi-translation-lib/plugins",
1415
importpath = "k8s.io/csi-translation-lib/plugins",
@@ -45,6 +46,7 @@ go_test(
4546
"azure_file_test.go",
4647
"gce_pd_test.go",
4748
"in_tree_volume_test.go",
49+
"vsphere_volume_test.go",
4850
],
4951
embed = [":go_default_library"],
5052
deps = [
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package plugins
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
v1 "k8s.io/api/core/v1"
24+
storage "k8s.io/api/storage/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/klog/v2"
27+
)
28+
29+
const (
30+
// VSphereDriverName is the name of the CSI driver for vSphere Volume
31+
VSphereDriverName = "csi.vsphere.vmware.com"
32+
// VSphereInTreePluginName is the name of the in-tree plugin for vSphere Volume
33+
VSphereInTreePluginName = "kubernetes.io/vsphere-volume"
34+
35+
// paramStoragePolicyName used to supply SPBM Policy name for Volume provisioning
36+
paramStoragePolicyName = "storagepolicyname"
37+
38+
// This param is used to tell Driver to return volumePath and not VolumeID
39+
// in-tree vSphere plugin does not understand volume id, it uses volumePath
40+
paramcsiMigration = "csimigration"
41+
42+
// This param is used to supply datastore name for Volume provisioning
43+
paramDatastore = "datastore-migrationparam"
44+
45+
// This param supplies disk foramt (thin, thick, zeoredthick) for Volume provisioning
46+
paramDiskFormat = "diskformat-migrationparam"
47+
48+
// vSAN Policy Parameters
49+
paramHostFailuresToTolerate = "hostfailurestotolerate-migrationparam"
50+
paramForceProvisioning = "forceprovisioning-migrationparam"
51+
paramCacheReservation = "cachereservation-migrationparam"
52+
paramDiskstripes = "diskstripes-migrationparam"
53+
paramObjectspacereservation = "objectspacereservation-migrationparam"
54+
paramIopslimit = "iopslimit-migrationparam"
55+
56+
// AttributeInitialVolumeFilepath represents the path of volume where volume is created
57+
AttributeInitialVolumeFilepath = "initialvolumefilepath"
58+
)
59+
60+
var _ InTreePlugin = &vSphereCSITranslator{}
61+
62+
// vSphereCSITranslator handles translation of PV spec from In-tree vSphere Volume to vSphere CSI
63+
type vSphereCSITranslator struct{}
64+
65+
// NewvSphereCSITranslator returns a new instance of vSphereCSITranslator
66+
func NewvSphereCSITranslator() InTreePlugin {
67+
return &vSphereCSITranslator{}
68+
}
69+
70+
// TranslateInTreeStorageClassToCSI translates InTree vSphere storage class parameters to CSI storage class
71+
func (t *vSphereCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) {
72+
if sc == nil {
73+
return nil, fmt.Errorf("sc is nil")
74+
}
75+
var params = map[string]string{}
76+
for k, v := range sc.Parameters {
77+
switch strings.ToLower(k) {
78+
case fsTypeKey:
79+
params[csiFsTypeKey] = v
80+
case paramStoragePolicyName:
81+
params[paramStoragePolicyName] = v
82+
case "datastore":
83+
params[paramDatastore] = v
84+
case "diskformat":
85+
params[paramDiskFormat] = v
86+
case "hostfailurestotolerate":
87+
params[paramHostFailuresToTolerate] = v
88+
case "forceprovisioning":
89+
params[paramForceProvisioning] = v
90+
case "cachereservation":
91+
params[paramCacheReservation] = v
92+
case "diskstripes":
93+
params[paramDiskstripes] = v
94+
case "objectspacereservation":
95+
params[paramObjectspacereservation] = v
96+
case "iopslimit":
97+
params[paramIopslimit] = v
98+
default:
99+
klog.V(2).Infof("StorageClass parameter [name:%q, value:%q] is not supported", k, v)
100+
}
101+
}
102+
103+
// This helps vSphere CSI driver to identify in-tree provisioner request vs CSI provisioner request
104+
// When this is true, Driver returns initialvolumefilepath in the VolumeContext, which is
105+
// used in TranslateCSIPVToInTree
106+
params[paramcsiMigration] = "true"
107+
// Note: sc.AllowedTopologies for Topology based volume provisioning will be supplied as it is.
108+
sc.Parameters = params
109+
return sc, nil
110+
}
111+
112+
// TranslateInTreeInlineVolumeToCSI takes a Volume with VsphereVolume set from in-tree
113+
// and converts the VsphereVolume source to a CSIPersistentVolumeSource
114+
func (t *vSphereCSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Volume) (*v1.PersistentVolume, error) {
115+
if volume == nil || volume.VsphereVolume == nil {
116+
return nil, fmt.Errorf("volume is nil or VsphereVolume not defined on volume")
117+
}
118+
pv := &v1.PersistentVolume{
119+
ObjectMeta: metav1.ObjectMeta{
120+
// Must be unique per disk as it is used as the unique part of the
121+
// staging path
122+
Name: fmt.Sprintf("%s-%s", VSphereDriverName, volume.VsphereVolume.VolumePath),
123+
},
124+
Spec: v1.PersistentVolumeSpec{
125+
PersistentVolumeSource: v1.PersistentVolumeSource{
126+
CSI: &v1.CSIPersistentVolumeSource{
127+
Driver: VSphereDriverName,
128+
VolumeHandle: volume.VsphereVolume.VolumePath,
129+
FSType: volume.VsphereVolume.FSType,
130+
VolumeAttributes: make(map[string]string),
131+
},
132+
},
133+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
134+
},
135+
}
136+
if volume.VsphereVolume.StoragePolicyName != "" {
137+
pv.Spec.CSI.VolumeAttributes[paramStoragePolicyName] = pv.Spec.VsphereVolume.StoragePolicyName
138+
}
139+
return pv, nil
140+
}
141+
142+
// TranslateInTreePVToCSI takes a PV with VsphereVolume set from in-tree
143+
// and converts the VsphereVolume source to a CSIPersistentVolumeSource
144+
func (t *vSphereCSITranslator) TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
145+
if pv == nil || pv.Spec.VsphereVolume == nil {
146+
return nil, fmt.Errorf("pv is nil or VsphereVolume not defined on pv")
147+
}
148+
csiSource := &v1.CSIPersistentVolumeSource{
149+
Driver: VSphereDriverName,
150+
VolumeHandle: pv.Spec.VsphereVolume.VolumePath,
151+
FSType: pv.Spec.VsphereVolume.FSType,
152+
VolumeAttributes: make(map[string]string),
153+
}
154+
if pv.Spec.VsphereVolume.StoragePolicyName != "" {
155+
csiSource.VolumeAttributes[paramStoragePolicyName] = pv.Spec.VsphereVolume.StoragePolicyName
156+
}
157+
pv.Spec.VsphereVolume = nil
158+
pv.Spec.CSI = csiSource
159+
return pv, nil
160+
}
161+
162+
// TranslateCSIPVToInTree takes a PV with CSIPersistentVolumeSource set and
163+
// translates the vSphere CSI source to a vSphereVolume source.
164+
func (t *vSphereCSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
165+
if pv == nil || pv.Spec.CSI == nil {
166+
return nil, fmt.Errorf("pv is nil or CSI source not defined on pv")
167+
}
168+
csiSource := pv.Spec.CSI
169+
vsphereVirtualDiskVolumeSource := &v1.VsphereVirtualDiskVolumeSource{
170+
FSType: csiSource.FSType,
171+
}
172+
volumeFilePath, ok := csiSource.VolumeAttributes[AttributeInitialVolumeFilepath]
173+
if ok {
174+
vsphereVirtualDiskVolumeSource.VolumePath = volumeFilePath
175+
}
176+
pv.Spec.CSI = nil
177+
pv.Spec.VsphereVolume = vsphereVirtualDiskVolumeSource
178+
return pv, nil
179+
}
180+
181+
// CanSupport tests whether the plugin supports a given persistent volume
182+
// specification from the API.
183+
func (t *vSphereCSITranslator) CanSupport(pv *v1.PersistentVolume) bool {
184+
return pv != nil && pv.Spec.VsphereVolume != nil
185+
}
186+
187+
// CanSupportInline tests whether the plugin supports a given inline volume
188+
// specification from the API.
189+
func (t *vSphereCSITranslator) CanSupportInline(volume *v1.Volume) bool {
190+
return volume != nil && volume.VsphereVolume != nil
191+
}
192+
193+
// GetInTreePluginName returns the name of the in-tree plugin driver
194+
func (t *vSphereCSITranslator) GetInTreePluginName() string {
195+
return VSphereInTreePluginName
196+
}
197+
198+
// GetCSIPluginName returns the name of the CSI plugin
199+
func (t *vSphereCSITranslator) GetCSIPluginName() string {
200+
return VSphereDriverName
201+
}
202+
203+
// RepairVolumeHandle is needed in VerifyVolumesAttached on the external attacher when we need to do strict volume
204+
// handle matching to check VolumeAttachment attached status.
205+
// vSphere volume does not need patch to help verify whether that volume is attached.
206+
func (t *vSphereCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
207+
return volumeHandle, nil
208+
}

0 commit comments

Comments
 (0)