mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #83098 from ddebroy/disable-intree
CSI Migration phase 2: disable probing of in-tree plugins
This commit is contained in:
commit
372ebd24f5
@ -92,6 +92,7 @@ go_library(
|
|||||||
"//pkg/volume/azure_file:go_default_library",
|
"//pkg/volume/azure_file:go_default_library",
|
||||||
"//pkg/volume/cinder:go_default_library",
|
"//pkg/volume/cinder:go_default_library",
|
||||||
"//pkg/volume/csi:go_default_library",
|
"//pkg/volume/csi:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/fc:go_default_library",
|
"//pkg/volume/fc:go_default_library",
|
||||||
"//pkg/volume/flexvolume:go_default_library",
|
"//pkg/volume/flexvolume:go_default_library",
|
||||||
"//pkg/volume/flocker:go_default_library",
|
"//pkg/volume/flocker:go_default_library",
|
||||||
@ -140,10 +141,12 @@ go_library(
|
|||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/cli/globalflag:go_default_library",
|
"//staging/src/k8s.io/component-base/cli/globalflag:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/metrics/prometheus/ratelimiter:go_default_library",
|
"//staging/src/k8s.io/component-base/metrics/prometheus/ratelimiter:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/version:go_default_library",
|
"//staging/src/k8s.io/component-base/version:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/version/verflag:go_default_library",
|
"//staging/src/k8s.io/component-base/version/verflag:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib/plugins:go_default_library",
|
||||||
"//staging/src/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1:go_default_library",
|
"//staging/src/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/metrics/pkg/client/custom_metrics:go_default_library",
|
"//staging/src/k8s.io/metrics/pkg/client/custom_metrics:go_default_library",
|
||||||
"//staging/src/k8s.io/metrics/pkg/client/external_metrics:go_default_library",
|
"//staging/src/k8s.io/metrics/pkg/client/external_metrics:go_default_library",
|
||||||
|
@ -65,6 +65,7 @@ import (
|
|||||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/quota/v1/generic"
|
"k8s.io/kubernetes/pkg/quota/v1/generic"
|
||||||
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -254,10 +255,14 @@ func startRouteController(ctx ControllerContext) (http.Handler, bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startPersistentVolumeBinderController(ctx ControllerContext) (http.Handler, bool, error) {
|
func startPersistentVolumeBinderController(ctx ControllerContext) (http.Handler, bool, error) {
|
||||||
|
plugins, err := ProbeControllerVolumePlugins(ctx.Cloud, ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("failed to probe volume plugins when starting persistentvolume controller: %v", err)
|
||||||
|
}
|
||||||
params := persistentvolumecontroller.ControllerParameters{
|
params := persistentvolumecontroller.ControllerParameters{
|
||||||
KubeClient: ctx.ClientBuilder.ClientOrDie("persistent-volume-binder"),
|
KubeClient: ctx.ClientBuilder.ClientOrDie("persistent-volume-binder"),
|
||||||
SyncPeriod: ctx.ComponentConfig.PersistentVolumeBinderController.PVClaimBinderSyncPeriod.Duration,
|
SyncPeriod: ctx.ComponentConfig.PersistentVolumeBinderController.PVClaimBinderSyncPeriod.Duration,
|
||||||
VolumePlugins: ProbeControllerVolumePlugins(ctx.Cloud, ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration),
|
VolumePlugins: plugins,
|
||||||
Cloud: ctx.Cloud,
|
Cloud: ctx.Cloud,
|
||||||
ClusterName: ctx.ComponentConfig.KubeCloudShared.ClusterName,
|
ClusterName: ctx.ComponentConfig.KubeCloudShared.ClusterName,
|
||||||
VolumeInformer: ctx.InformerFactory.Core().V1().PersistentVolumes(),
|
VolumeInformer: ctx.InformerFactory.Core().V1().PersistentVolumes(),
|
||||||
@ -291,6 +296,11 @@ func startAttachDetachController(ctx ControllerContext) (http.Handler, bool, err
|
|||||||
csiDriverInformer = ctx.InformerFactory.Storage().V1beta1().CSIDrivers()
|
csiDriverInformer = ctx.InformerFactory.Storage().V1beta1().CSIDrivers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins, err := ProbeAttachableVolumePlugins()
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("failed to probe volume plugins when starting attach/detach controller: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
attachDetachController, attachDetachControllerErr :=
|
attachDetachController, attachDetachControllerErr :=
|
||||||
attachdetach.NewAttachDetachController(
|
attachdetach.NewAttachDetachController(
|
||||||
ctx.ClientBuilder.ClientOrDie("attachdetach-controller"),
|
ctx.ClientBuilder.ClientOrDie("attachdetach-controller"),
|
||||||
@ -301,7 +311,7 @@ func startAttachDetachController(ctx ControllerContext) (http.Handler, bool, err
|
|||||||
csiNodeInformer,
|
csiNodeInformer,
|
||||||
csiDriverInformer,
|
csiDriverInformer,
|
||||||
ctx.Cloud,
|
ctx.Cloud,
|
||||||
ProbeAttachableVolumePlugins(),
|
plugins,
|
||||||
GetDynamicPluginProber(ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration),
|
GetDynamicPluginProber(ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration),
|
||||||
ctx.ComponentConfig.AttachDetachController.DisableAttachDetachReconcilerSync,
|
ctx.ComponentConfig.AttachDetachController.DisableAttachDetachReconcilerSync,
|
||||||
ctx.ComponentConfig.AttachDetachController.ReconcilerSyncLoopPeriod.Duration,
|
ctx.ComponentConfig.AttachDetachController.ReconcilerSyncLoopPeriod.Duration,
|
||||||
@ -316,17 +326,23 @@ func startAttachDetachController(ctx ControllerContext) (http.Handler, bool, err
|
|||||||
|
|
||||||
func startVolumeExpandController(ctx ControllerContext) (http.Handler, bool, error) {
|
func startVolumeExpandController(ctx ControllerContext) (http.Handler, bool, error) {
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||||
|
plugins, err := ProbeExpandableVolumePlugins(ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("failed to probe volume plugins when starting volume expand controller: %v", err)
|
||||||
|
}
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
expandController, expandControllerErr := expand.NewExpandController(
|
expandController, expandControllerErr := expand.NewExpandController(
|
||||||
ctx.ClientBuilder.ClientOrDie("expand-controller"),
|
ctx.ClientBuilder.ClientOrDie("expand-controller"),
|
||||||
ctx.InformerFactory.Core().V1().PersistentVolumeClaims(),
|
ctx.InformerFactory.Core().V1().PersistentVolumeClaims(),
|
||||||
ctx.InformerFactory.Core().V1().PersistentVolumes(),
|
ctx.InformerFactory.Core().V1().PersistentVolumes(),
|
||||||
ctx.InformerFactory.Storage().V1().StorageClasses(),
|
ctx.InformerFactory.Storage().V1().StorageClasses(),
|
||||||
ctx.Cloud,
|
ctx.Cloud,
|
||||||
ProbeExpandableVolumePlugins(ctx.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration),
|
plugins,
|
||||||
csitrans.New())
|
csiTranslator,
|
||||||
|
csimigration.NewPluginManager(csiTranslator))
|
||||||
|
|
||||||
if expandControllerErr != nil {
|
if expandControllerErr != nil {
|
||||||
return nil, true, fmt.Errorf("failed to start volume expand controller : %v", expandControllerErr)
|
return nil, true, fmt.Errorf("failed to start volume expand controller: %v", expandControllerErr)
|
||||||
}
|
}
|
||||||
go expandController.Run(ctx.Stop)
|
go expandController.Run(ctx.Stop)
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
|
@ -57,10 +57,13 @@ import (
|
|||||||
// detach controller.
|
// detach controller.
|
||||||
// The list of plugins is manually compiled. This code and the plugin
|
// The list of plugins is manually compiled. This code and the plugin
|
||||||
// initialization code for kubelet really, really need a through refactor.
|
// initialization code for kubelet really, really need a through refactor.
|
||||||
func ProbeAttachableVolumePlugins() []volume.VolumePlugin {
|
func ProbeAttachableVolumePlugins() ([]volume.VolumePlugin, error) {
|
||||||
|
var err error
|
||||||
allPlugins := []volume.VolumePlugin{}
|
allPlugins := []volume.VolumePlugin{}
|
||||||
|
allPlugins, err = appendAttachableLegacyProviderVolumes(allPlugins, utilfeature.DefaultFeatureGate)
|
||||||
allPlugins = appendAttachableLegacyProviderVolumes(allPlugins)
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||||
@ -68,7 +71,7 @@ func ProbeAttachableVolumePlugins() []volume.VolumePlugin {
|
|||||||
allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||||
return allPlugins
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
|
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
|
||||||
@ -79,23 +82,26 @@ func GetDynamicPluginProber(config persistentvolumeconfig.VolumeConfiguration) v
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProbeExpandableVolumePlugins returns volume plugins which are expandable
|
// ProbeExpandableVolumePlugins returns volume plugins which are expandable
|
||||||
func ProbeExpandableVolumePlugins(config persistentvolumeconfig.VolumeConfiguration) []volume.VolumePlugin {
|
func ProbeExpandableVolumePlugins(config persistentvolumeconfig.VolumeConfiguration) ([]volume.VolumePlugin, error) {
|
||||||
|
var err error
|
||||||
allPlugins := []volume.VolumePlugin{}
|
allPlugins := []volume.VolumePlugin{}
|
||||||
|
allPlugins, err = appendExpandableLegacyProviderVolumes(allPlugins, utilfeature.DefaultFeatureGate)
|
||||||
allPlugins = appendExpandableLegacyProviderVolumes(allPlugins)
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
||||||
return allPlugins
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProbeControllerVolumePlugins collects all persistent volume plugins into an
|
// ProbeControllerVolumePlugins collects all persistent volume plugins into an
|
||||||
// easy to use list. Only volume plugins that implement any of
|
// easy to use list. Only volume plugins that implement any of
|
||||||
// provisioner/recycler/deleter interface should be returned.
|
// provisioner/recycler/deleter interface should be returned.
|
||||||
func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persistentvolumeconfig.VolumeConfiguration) []volume.VolumePlugin {
|
func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persistentvolumeconfig.VolumeConfiguration) ([]volume.VolumePlugin, error) {
|
||||||
allPlugins := []volume.VolumePlugin{}
|
allPlugins := []volume.VolumePlugin{}
|
||||||
|
|
||||||
// The list of plugins to probe is decided by this binary, not
|
// The list of plugins to probe is decided by this binary, not
|
||||||
@ -131,7 +137,11 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persiste
|
|||||||
// add rbd provisioner
|
// add rbd provisioner
|
||||||
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, quobyte.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, quobyte.ProbeVolumePlugins()...)
|
||||||
allPlugins = appendLegacyProviderVolumes(allPlugins)
|
var err error
|
||||||
|
allPlugins, err = appendExpandableLegacyProviderVolumes(allPlugins, utilfeature.DefaultFeatureGate)
|
||||||
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
|
|
||||||
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||||
@ -143,7 +153,7 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persiste
|
|||||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return allPlugins
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttemptToLoadRecycler tries decoding a pod from a filepath for use as a recycler for a volume.
|
// AttemptToLoadRecycler tries decoding a pod from a filepath for use as a recycler for a volume.
|
||||||
|
@ -19,34 +19,84 @@ limitations under the License.
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
"k8s.io/csi-translation-lib/plugins"
|
||||||
|
"k8s.io/klog"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/awsebs"
|
"k8s.io/kubernetes/pkg/volume/awsebs"
|
||||||
"k8s.io/kubernetes/pkg/volume/azure_dd"
|
"k8s.io/kubernetes/pkg/volume/azure_dd"
|
||||||
"k8s.io/kubernetes/pkg/volume/azure_file"
|
"k8s.io/kubernetes/pkg/volume/azure_file"
|
||||||
"k8s.io/kubernetes/pkg/volume/cinder"
|
"k8s.io/kubernetes/pkg/volume/cinder"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/gcepd"
|
"k8s.io/kubernetes/pkg/volume/gcepd"
|
||||||
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin) []volume.VolumePlugin {
|
type probeFn func() []volume.VolumePlugin
|
||||||
allPlugins = append(allPlugins, awsebs.ProbeVolumePlugins()...)
|
|
||||||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
func appendPluginBasedOnMigrationFeatureFlags(plugins []volume.VolumePlugin, inTreePluginName string, featureGate featuregate.FeatureGate, pluginMigration, pluginMigrationComplete featuregate.Feature, fn probeFn) ([]volume.VolumePlugin, error) {
|
||||||
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
|
// Skip appending the in-tree plugin to the list of plugins to be probed/initialized
|
||||||
allPlugins = append(allPlugins, gcepd.ProbeVolumePlugins()...)
|
// if the CSIMigration feature flag and plugin specific feature flag indicating
|
||||||
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
// CSI migration is complete
|
||||||
return allPlugins
|
err := csimigration.CheckMigrationFeatureFlags(featureGate, pluginMigration, pluginMigrationComplete)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("Unexpected CSI Migration Feature Flags combination detected: %v. CSI Migration may not take effect", err)
|
||||||
|
// TODO: fail and return here once alpha only tests can set the feature flags for a plugin correctly
|
||||||
|
}
|
||||||
|
if featureGate.Enabled(features.CSIMigration) && featureGate.Enabled(pluginMigration) && featureGate.Enabled(pluginMigrationComplete) {
|
||||||
|
klog.Infof("Skip registration of plugin %s since feature flag %v is enabled", inTreePluginName, pluginMigrationComplete)
|
||||||
|
return plugins, nil
|
||||||
|
}
|
||||||
|
plugins = append(plugins, fn()...)
|
||||||
|
return plugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendExpandableLegacyProviderVolumes(allPlugins []volume.VolumePlugin) []volume.VolumePlugin {
|
type pluginInfo struct {
|
||||||
return appendLegacyProviderVolumes(allPlugins)
|
pluginMigrationFeature featuregate.Feature
|
||||||
|
pluginMigrationCompleteFeature featuregate.Feature
|
||||||
|
pluginProbeFunction probeFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin) []volume.VolumePlugin {
|
func appendAttachableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
|
||||||
allPlugins = append(allPlugins, awsebs.ProbeVolumePlugins()...)
|
pluginMigrationStatus := make(map[string]pluginInfo)
|
||||||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
pluginMigrationStatus[plugins.AWSEBSInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAWS, pluginMigrationCompleteFeature: features.CSIMigrationAWSComplete, pluginProbeFunction: awsebs.ProbeVolumePlugins}
|
||||||
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
|
pluginMigrationStatus[plugins.GCEPDInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationGCE, pluginMigrationCompleteFeature: features.CSIMigrationGCEComplete, pluginProbeFunction: gcepd.ProbeVolumePlugins}
|
||||||
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
|
pluginMigrationStatus[plugins.CinderInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationOpenStack, pluginMigrationCompleteFeature: features.CSIMigrationOpenStackComplete, pluginProbeFunction: cinder.ProbeVolumePlugins}
|
||||||
allPlugins = append(allPlugins, gcepd.ProbeVolumePlugins()...)
|
pluginMigrationStatus[plugins.AzureDiskInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureDisk, pluginMigrationCompleteFeature: features.CSIMigrationAzureDiskComplete, pluginProbeFunction: azure_dd.ProbeVolumePlugins}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for pluginName, pluginInfo := range pluginMigrationStatus {
|
||||||
|
allPlugins, err = appendPluginBasedOnMigrationFeatureFlags(allPlugins, pluginName, featureGate, pluginInfo.pluginMigrationFeature, pluginInfo.pluginMigrationCompleteFeature, pluginInfo.pluginProbeFunction)
|
||||||
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
||||||
return allPlugins
|
return allPlugins, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendExpandableLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
|
||||||
|
return appendLegacyProviderVolumes(allPlugins, featureGate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
|
||||||
|
pluginMigrationStatus := make(map[string]pluginInfo)
|
||||||
|
pluginMigrationStatus[plugins.AWSEBSInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAWS, pluginMigrationCompleteFeature: features.CSIMigrationAWSComplete, pluginProbeFunction: awsebs.ProbeVolumePlugins}
|
||||||
|
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.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for pluginName, pluginInfo := range pluginMigrationStatus {
|
||||||
|
allPlugins, err = appendPluginBasedOnMigrationFeatureFlags(allPlugins, pluginName, featureGate, pluginInfo.pluginMigrationFeature, pluginInfo.pluginMigrationCompleteFeature, pluginInfo.pluginProbeFunction)
|
||||||
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
||||||
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ go_library(
|
|||||||
"//pkg/volume/cinder:go_default_library",
|
"//pkg/volume/cinder:go_default_library",
|
||||||
"//pkg/volume/configmap:go_default_library",
|
"//pkg/volume/configmap:go_default_library",
|
||||||
"//pkg/volume/csi:go_default_library",
|
"//pkg/volume/csi:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/downwardapi:go_default_library",
|
"//pkg/volume/downwardapi:go_default_library",
|
||||||
"//pkg/volume/emptydir:go_default_library",
|
"//pkg/volume/emptydir:go_default_library",
|
||||||
"//pkg/volume/fc:go_default_library",
|
"//pkg/volume/fc:go_default_library",
|
||||||
@ -138,8 +139,10 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
|
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/version:go_default_library",
|
"//staging/src/k8s.io/component-base/version:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/version/verflag:go_default_library",
|
"//staging/src/k8s.io/component-base/version/verflag:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib/plugins:go_default_library",
|
||||||
"//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
|
"//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
|
||||||
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
|
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
_ "k8s.io/kubernetes/pkg/credentialprovider/aws"
|
_ "k8s.io/kubernetes/pkg/credentialprovider/aws"
|
||||||
_ "k8s.io/kubernetes/pkg/credentialprovider/azure"
|
_ "k8s.io/kubernetes/pkg/credentialprovider/azure"
|
||||||
_ "k8s.io/kubernetes/pkg/credentialprovider/gcp"
|
_ "k8s.io/kubernetes/pkg/credentialprovider/gcp"
|
||||||
|
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
|
|
||||||
// Volume plugins
|
// Volume plugins
|
||||||
@ -53,7 +55,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ProbeVolumePlugins collects all volume plugins into an easy to use list.
|
// ProbeVolumePlugins collects all volume plugins into an easy to use list.
|
||||||
func ProbeVolumePlugins() []volume.VolumePlugin {
|
func ProbeVolumePlugins(featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
|
||||||
allPlugins := []volume.VolumePlugin{}
|
allPlugins := []volume.VolumePlugin{}
|
||||||
|
|
||||||
// The list of plugins to probe is decided by the kubelet binary, not
|
// The list of plugins to probe is decided by the kubelet binary, not
|
||||||
@ -62,7 +64,11 @@ func ProbeVolumePlugins() []volume.VolumePlugin {
|
|||||||
//
|
//
|
||||||
// Kubelet does not currently need to configure volume plugins.
|
// Kubelet does not currently need to configure volume plugins.
|
||||||
// If/when it does, see kube-controller-manager/app/plugins.go for example of using volume.VolumeConfig
|
// If/when it does, see kube-controller-manager/app/plugins.go for example of using volume.VolumeConfig
|
||||||
allPlugins = appendLegacyProviderVolumes(allPlugins)
|
var err error
|
||||||
|
allPlugins, err = appendLegacyProviderVolumes(allPlugins, featureGate)
|
||||||
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
allPlugins = append(allPlugins, emptydir.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, emptydir.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, git_repo.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, git_repo.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, hostpath.ProbeVolumePlugins(volume.VolumeConfig{})...)
|
allPlugins = append(allPlugins, hostpath.ProbeVolumePlugins(volume.VolumeConfig{})...)
|
||||||
@ -83,7 +89,7 @@ func ProbeVolumePlugins() []volume.VolumePlugin {
|
|||||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||||
return allPlugins
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
|
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
|
||||||
|
@ -19,21 +19,61 @@ limitations under the License.
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
"k8s.io/csi-translation-lib/plugins"
|
||||||
|
"k8s.io/klog"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/awsebs"
|
"k8s.io/kubernetes/pkg/volume/awsebs"
|
||||||
"k8s.io/kubernetes/pkg/volume/azure_dd"
|
"k8s.io/kubernetes/pkg/volume/azure_dd"
|
||||||
"k8s.io/kubernetes/pkg/volume/azure_file"
|
"k8s.io/kubernetes/pkg/volume/azure_file"
|
||||||
"k8s.io/kubernetes/pkg/volume/cinder"
|
"k8s.io/kubernetes/pkg/volume/cinder"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/gcepd"
|
"k8s.io/kubernetes/pkg/volume/gcepd"
|
||||||
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin) []volume.VolumePlugin {
|
type probeFn func() []volume.VolumePlugin
|
||||||
allPlugins = append(allPlugins, awsebs.ProbeVolumePlugins()...)
|
|
||||||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
func appendPluginBasedOnMigrationFeatureFlags(plugins []volume.VolumePlugin, inTreePluginName string, featureGate featuregate.FeatureGate, pluginMigration, pluginMigrationComplete featuregate.Feature, fn probeFn) ([]volume.VolumePlugin, error) {
|
||||||
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
|
// Skip appending the in-tree plugin to the list of plugins to be probed/initialized
|
||||||
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
|
// if the CSIMigration feature flag and plugin specific feature flag indicating
|
||||||
allPlugins = append(allPlugins, gcepd.ProbeVolumePlugins()...)
|
// CSI migration is complete
|
||||||
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
err := csimigration.CheckMigrationFeatureFlags(featureGate, pluginMigration, pluginMigrationComplete)
|
||||||
return allPlugins
|
if err != nil {
|
||||||
|
klog.Warningf("Unexpected CSI Migration Feature Flags combination detected: %v. CSI Migration may not take effect", err)
|
||||||
|
// TODO: fail and return here once alpha only tests can set the feature flags for a plugin correctly
|
||||||
|
}
|
||||||
|
if featureGate.Enabled(features.CSIMigration) && featureGate.Enabled(pluginMigration) && featureGate.Enabled(pluginMigrationComplete) {
|
||||||
|
klog.Infof("Skip registration of plugin %s since feature flag %v is enabled", inTreePluginName, pluginMigrationComplete)
|
||||||
|
return plugins, nil
|
||||||
|
}
|
||||||
|
plugins = append(plugins, fn()...)
|
||||||
|
return plugins, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginInfo struct {
|
||||||
|
pluginMigrationFeature featuregate.Feature
|
||||||
|
pluginMigrationCompleteFeature featuregate.Feature
|
||||||
|
pluginProbeFunction probeFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
|
||||||
|
pluginMigrationStatus := make(map[string]pluginInfo)
|
||||||
|
pluginMigrationStatus[plugins.AWSEBSInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAWS, pluginMigrationCompleteFeature: features.CSIMigrationAWSComplete, pluginProbeFunction: awsebs.ProbeVolumePlugins}
|
||||||
|
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.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginMigrationCompleteFeature: features.CSIMigrationAzureFileComplete, pluginProbeFunction: azure_file.ProbeVolumePlugins}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for pluginName, pluginInfo := range pluginMigrationStatus {
|
||||||
|
allPlugins, err = appendPluginBasedOnMigrationFeatureFlags(allPlugins, pluginName, featureGate, pluginInfo.pluginMigrationFeature, pluginInfo.pluginMigrationCompleteFeature, pluginInfo.pluginProbeFunction)
|
||||||
|
if err != nil {
|
||||||
|
return allPlugins, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
||||||
|
return allPlugins, nil
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ import (
|
|||||||
"k8s.io/client-go/util/keyutil"
|
"k8s.io/client-go/util/keyutil"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
cliflag "k8s.io/component-base/cli/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
"k8s.io/component-base/version"
|
"k8s.io/component-base/version"
|
||||||
"k8s.io/component-base/version/verflag"
|
"k8s.io/component-base/version/verflag"
|
||||||
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
|
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
|
||||||
@ -247,7 +248,7 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use kubeletServer to construct the default KubeletDeps
|
// use kubeletServer to construct the default KubeletDeps
|
||||||
kubeletDeps, err := UnsecuredDependencies(kubeletServer)
|
kubeletDeps, err := UnsecuredDependencies(kubeletServer, utilfeature.DefaultFeatureGate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Fatal(err)
|
klog.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -268,7 +269,7 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
|
|||||||
|
|
||||||
// run the kubelet
|
// run the kubelet
|
||||||
klog.V(5).Infof("KubeletConfiguration: %#v", kubeletServer.KubeletConfiguration)
|
klog.V(5).Infof("KubeletConfiguration: %#v", kubeletServer.KubeletConfiguration)
|
||||||
if err := Run(kubeletServer, kubeletDeps, stopCh); err != nil {
|
if err := Run(kubeletServer, kubeletDeps, utilfeature.DefaultFeatureGate, stopCh); err != nil {
|
||||||
klog.Fatal(err)
|
klog.Fatal(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -362,7 +363,7 @@ func loadConfigFile(name string) (*kubeletconfiginternal.KubeletConfiguration, e
|
|||||||
|
|
||||||
// UnsecuredDependencies returns a Dependencies suitable for being run, or an error if the server setup
|
// UnsecuredDependencies returns a Dependencies suitable for being run, or an error if the server setup
|
||||||
// is not valid. It will not start any background processes, and does not include authentication/authorization
|
// is not valid. It will not start any background processes, and does not include authentication/authorization
|
||||||
func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, error) {
|
func UnsecuredDependencies(s *options.KubeletServer, featureGate featuregate.FeatureGate) (*kubelet.Dependencies, error) {
|
||||||
// Initialize the TLS Options
|
// Initialize the TLS Options
|
||||||
tlsOptions, err := InitializeTLS(&s.KubeletFlags, &s.KubeletConfiguration)
|
tlsOptions, err := InitializeTLS(&s.KubeletFlags, &s.KubeletConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -383,6 +384,10 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins, err := ProbeVolumePlugins(featureGate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &kubelet.Dependencies{
|
return &kubelet.Dependencies{
|
||||||
Auth: nil, // default does not enforce auth[nz]
|
Auth: nil, // default does not enforce auth[nz]
|
||||||
CAdvisorInterface: nil, // cadvisor.New launches background processes (bg http.ListenAndServe, and some bg cleaners), not set here
|
CAdvisorInterface: nil, // cadvisor.New launches background processes (bg http.ListenAndServe, and some bg cleaners), not set here
|
||||||
@ -397,7 +402,7 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
|
|||||||
Subpather: subpather,
|
Subpather: subpather,
|
||||||
OOMAdjuster: oom.NewOOMAdjuster(),
|
OOMAdjuster: oom.NewOOMAdjuster(),
|
||||||
OSInterface: kubecontainer.RealOS{},
|
OSInterface: kubecontainer.RealOS{},
|
||||||
VolumePlugins: ProbeVolumePlugins(),
|
VolumePlugins: plugins,
|
||||||
DynamicPluginProber: GetDynamicPluginProber(s.VolumePluginDir, pluginRunner),
|
DynamicPluginProber: GetDynamicPluginProber(s.VolumePluginDir, pluginRunner),
|
||||||
TLSOptions: tlsOptions}, nil
|
TLSOptions: tlsOptions}, nil
|
||||||
}
|
}
|
||||||
@ -406,13 +411,13 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
|
|||||||
// The kubeDeps argument may be nil - if so, it is initialized from the settings on KubeletServer.
|
// The kubeDeps argument may be nil - if so, it is initialized from the settings on KubeletServer.
|
||||||
// Otherwise, the caller is assumed to have set up the Dependencies object and a default one will
|
// Otherwise, the caller is assumed to have set up the Dependencies object and a default one will
|
||||||
// not be generated.
|
// not be generated.
|
||||||
func Run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan struct{}) error {
|
func Run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate, stopCh <-chan struct{}) error {
|
||||||
// To help debugging, immediately log version
|
// To help debugging, immediately log version
|
||||||
klog.Infof("Version: %+v", version.Get())
|
klog.Infof("Version: %+v", version.Get())
|
||||||
if err := initForOS(s.KubeletFlags.WindowsService); err != nil {
|
if err := initForOS(s.KubeletFlags.WindowsService); err != nil {
|
||||||
return fmt.Errorf("failed OS init: %v", err)
|
return fmt.Errorf("failed OS init: %v", err)
|
||||||
}
|
}
|
||||||
if err := run(s, kubeDeps, stopCh); err != nil {
|
if err := run(s, kubeDeps, featureGate, stopCh); err != nil {
|
||||||
return fmt.Errorf("failed to run Kubelet: %v", err)
|
return fmt.Errorf("failed to run Kubelet: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -469,7 +474,7 @@ func makeEventRecorder(kubeDeps *kubelet.Dependencies, nodeName types.NodeName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan struct{}) (err error) {
|
func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate, stopCh <-chan struct{}) (err error) {
|
||||||
// Set global feature gates based on the value on the initial KubeletServer
|
// Set global feature gates based on the value on the initial KubeletServer
|
||||||
err = utilfeature.DefaultMutableFeatureGate.SetFromMap(s.KubeletConfiguration.FeatureGates)
|
err = utilfeature.DefaultMutableFeatureGate.SetFromMap(s.KubeletConfiguration.FeatureGates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -511,7 +516,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kubeDeps == nil {
|
if kubeDeps == nil {
|
||||||
kubeDeps, err = UnsecuredDependencies(s)
|
kubeDeps, err = UnsecuredDependencies(s, featureGate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ go_library(
|
|||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||||
"//pkg/volume/util/subpath:go_default_library",
|
"//pkg/volume/util/subpath:go_default_library",
|
||||||
@ -45,6 +46,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -44,6 +44,7 @@ import (
|
|||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
utilexec "k8s.io/utils/exec"
|
utilexec "k8s.io/utils/exec"
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/subpath"
|
"k8s.io/kubernetes/pkg/volume/util/subpath"
|
||||||
@ -116,6 +118,7 @@ func NewAttachDetachController(
|
|||||||
disableReconciliationSync bool,
|
disableReconciliationSync bool,
|
||||||
reconcilerSyncDuration time.Duration,
|
reconcilerSyncDuration time.Duration,
|
||||||
timerConfig TimerConfig) (AttachDetachController, error) {
|
timerConfig TimerConfig) (AttachDetachController, error) {
|
||||||
|
|
||||||
adc := &attachDetachController{
|
adc := &attachDetachController{
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
pvcLister: pvcInformer.Lister(),
|
pvcLister: pvcInformer.Lister(),
|
||||||
@ -176,6 +179,10 @@ func NewAttachDetachController(
|
|||||||
adc.nodeStatusUpdater,
|
adc.nodeStatusUpdater,
|
||||||
recorder)
|
recorder)
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
|
adc.intreeToCSITranslator = csiTranslator
|
||||||
|
adc.csiMigratedPluginManager = csimigration.NewPluginManager(csiTranslator)
|
||||||
|
|
||||||
adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
||||||
timerConfig.DesiredStateOfWorldPopulatorLoopSleepPeriod,
|
timerConfig.DesiredStateOfWorldPopulatorLoopSleepPeriod,
|
||||||
timerConfig.DesiredStateOfWorldPopulatorListPodsRetryDuration,
|
timerConfig.DesiredStateOfWorldPopulatorListPodsRetryDuration,
|
||||||
@ -183,7 +190,9 @@ func NewAttachDetachController(
|
|||||||
adc.desiredStateOfWorld,
|
adc.desiredStateOfWorld,
|
||||||
&adc.volumePluginMgr,
|
&adc.volumePluginMgr,
|
||||||
pvcInformer.Lister(),
|
pvcInformer.Lister(),
|
||||||
pvInformer.Lister())
|
pvInformer.Lister(),
|
||||||
|
adc.csiMigratedPluginManager,
|
||||||
|
adc.intreeToCSITranslator)
|
||||||
|
|
||||||
podInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
|
podInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
|
||||||
AddFunc: adc.podAdd,
|
AddFunc: adc.podAdd,
|
||||||
@ -318,6 +327,12 @@ type attachDetachController struct {
|
|||||||
|
|
||||||
// pvcQueue is used to queue pvc objects
|
// pvcQueue is used to queue pvc objects
|
||||||
pvcQueue workqueue.RateLimitingInterface
|
pvcQueue workqueue.RateLimitingInterface
|
||||||
|
|
||||||
|
// csiMigratedPluginManager detects in-tree plugins that have been migrated to CSI
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
|
|
||||||
|
// intreeToCSITranslator translates from in-tree volume specs to CSI
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adc *attachDetachController) Run(stopCh <-chan struct{}) {
|
func (adc *attachDetachController) Run(stopCh <-chan struct{}) {
|
||||||
@ -355,7 +370,9 @@ func (adc *attachDetachController) Run(stopCh <-chan struct{}) {
|
|||||||
adc.podLister,
|
adc.podLister,
|
||||||
adc.actualStateOfWorld,
|
adc.actualStateOfWorld,
|
||||||
adc.desiredStateOfWorld,
|
adc.desiredStateOfWorld,
|
||||||
&adc.volumePluginMgr)
|
&adc.volumePluginMgr,
|
||||||
|
adc.csiMigratedPluginManager,
|
||||||
|
adc.intreeToCSITranslator)
|
||||||
|
|
||||||
<-stopCh
|
<-stopCh
|
||||||
}
|
}
|
||||||
@ -421,10 +438,11 @@ func (adc *attachDetachController) populateDesiredStateOfWorld() error {
|
|||||||
podToAdd := pod
|
podToAdd := pod
|
||||||
adc.podAdd(podToAdd)
|
adc.podAdd(podToAdd)
|
||||||
for _, podVolume := range podToAdd.Spec.Volumes {
|
for _, podVolume := range podToAdd.Spec.Volumes {
|
||||||
|
nodeName := types.NodeName(podToAdd.Spec.NodeName)
|
||||||
// The volume specs present in the ActualStateOfWorld are nil, let's replace those
|
// The volume specs present in the ActualStateOfWorld are nil, let's replace those
|
||||||
// with the correct ones found on pods. The present in the ASW with no corresponding
|
// with the correct ones found on pods. The present in the ASW with no corresponding
|
||||||
// pod will be detached and the spec is irrelevant.
|
// pod will be detached and the spec is irrelevant.
|
||||||
volumeSpec, err := util.CreateVolumeSpec(podVolume, podToAdd.Namespace, adc.pvcLister, adc.pvLister)
|
volumeSpec, err := util.CreateVolumeSpec(podVolume, podToAdd.Namespace, nodeName, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf(
|
klog.Errorf(
|
||||||
"Error creating spec for volume %q, pod %q/%q: %v",
|
"Error creating spec for volume %q, pod %q/%q: %v",
|
||||||
@ -434,7 +452,6 @@ func (adc *attachDetachController) populateDesiredStateOfWorld() error {
|
|||||||
err)
|
err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nodeName := types.NodeName(podToAdd.Spec.NodeName)
|
|
||||||
plugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
|
plugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
|
||||||
if err != nil || plugin == nil {
|
if err != nil || plugin == nil {
|
||||||
klog.V(10).Infof(
|
klog.V(10).Infof(
|
||||||
@ -488,7 +505,7 @@ func (adc *attachDetachController) podAdd(obj interface{}) {
|
|||||||
true /* default volume action */)
|
true /* default volume action */)
|
||||||
|
|
||||||
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
||||||
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister)
|
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDesiredStateOfWorld returns desired state of world associated with controller
|
// GetDesiredStateOfWorld returns desired state of world associated with controller
|
||||||
@ -512,7 +529,7 @@ func (adc *attachDetachController) podUpdate(oldObj, newObj interface{}) {
|
|||||||
true /* default volume action */)
|
true /* default volume action */)
|
||||||
|
|
||||||
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
||||||
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister)
|
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adc *attachDetachController) podDelete(obj interface{}) {
|
func (adc *attachDetachController) podDelete(obj interface{}) {
|
||||||
@ -522,7 +539,7 @@ func (adc *attachDetachController) podDelete(obj interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
util.ProcessPodVolumes(pod, false, /* addVolumes */
|
util.ProcessPodVolumes(pod, false, /* addVolumes */
|
||||||
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister)
|
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adc *attachDetachController) nodeAdd(obj interface{}) {
|
func (adc *attachDetachController) nodeAdd(obj interface{}) {
|
||||||
@ -640,7 +657,7 @@ func (adc *attachDetachController) syncPVCByKey(key string) error {
|
|||||||
true /* default volume action */)
|
true /* default volume action */)
|
||||||
|
|
||||||
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
util.ProcessPodVolumes(pod, volumeActionFlag, /* addVolumes */
|
||||||
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister)
|
adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,8 @@ func Test_NewAttachDetachController_Positive(t *testing.T) {
|
|||||||
nil, /* prober */
|
nil, /* prober */
|
||||||
false,
|
false,
|
||||||
5*time.Second,
|
5*time.Second,
|
||||||
DefaultTimerConfig)
|
DefaultTimerConfig,
|
||||||
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9,8 +9,10 @@ go_library(
|
|||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/util:go_default_library",
|
"//pkg/controller/volume/attachdetach/util:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/metrics:go_default_library",
|
"//staging/src/k8s.io/component-base/metrics:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library",
|
"//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library",
|
||||||
@ -26,6 +28,7 @@ go_test(
|
|||||||
"//pkg/controller:go_default_library",
|
"//pkg/controller:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/testing:go_default_library",
|
"//pkg/controller/volume/attachdetach/testing:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/testing:go_default_library",
|
"//pkg/volume/testing:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@ -34,6 +37,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
"k8s.io/component-base/metrics"
|
"k8s.io/component-base/metrics"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
@ -27,6 +28,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,14 +62,18 @@ func Register(pvcLister corelisters.PersistentVolumeClaimLister,
|
|||||||
podLister corelisters.PodLister,
|
podLister corelisters.PodLister,
|
||||||
asw cache.ActualStateOfWorld,
|
asw cache.ActualStateOfWorld,
|
||||||
dsw cache.DesiredStateOfWorld,
|
dsw cache.DesiredStateOfWorld,
|
||||||
pluginMgr *volume.VolumePluginMgr) {
|
pluginMgr *volume.VolumePluginMgr,
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager,
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator) {
|
||||||
registerMetrics.Do(func() {
|
registerMetrics.Do(func() {
|
||||||
legacyregistry.CustomMustRegister(newAttachDetachStateCollector(pvcLister,
|
legacyregistry.CustomMustRegister(newAttachDetachStateCollector(pvcLister,
|
||||||
podLister,
|
podLister,
|
||||||
pvLister,
|
pvLister,
|
||||||
asw,
|
asw,
|
||||||
dsw,
|
dsw,
|
||||||
pluginMgr))
|
pluginMgr,
|
||||||
|
csiMigratedPluginManager,
|
||||||
|
intreeToCSITranslator))
|
||||||
legacyregistry.MustRegister(forcedDetachMetricCounter)
|
legacyregistry.MustRegister(forcedDetachMetricCounter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -75,12 +81,14 @@ func Register(pvcLister corelisters.PersistentVolumeClaimLister,
|
|||||||
type attachDetachStateCollector struct {
|
type attachDetachStateCollector struct {
|
||||||
metrics.BaseStableCollector
|
metrics.BaseStableCollector
|
||||||
|
|
||||||
pvcLister corelisters.PersistentVolumeClaimLister
|
pvcLister corelisters.PersistentVolumeClaimLister
|
||||||
podLister corelisters.PodLister
|
podLister corelisters.PodLister
|
||||||
pvLister corelisters.PersistentVolumeLister
|
pvLister corelisters.PersistentVolumeLister
|
||||||
asw cache.ActualStateOfWorld
|
asw cache.ActualStateOfWorld
|
||||||
dsw cache.DesiredStateOfWorld
|
dsw cache.DesiredStateOfWorld
|
||||||
volumePluginMgr *volume.VolumePluginMgr
|
volumePluginMgr *volume.VolumePluginMgr
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
// volumeCount is a map of maps used as a counter, e.g.:
|
// volumeCount is a map of maps used as a counter, e.g.:
|
||||||
@ -105,8 +113,10 @@ func newAttachDetachStateCollector(
|
|||||||
pvLister corelisters.PersistentVolumeLister,
|
pvLister corelisters.PersistentVolumeLister,
|
||||||
asw cache.ActualStateOfWorld,
|
asw cache.ActualStateOfWorld,
|
||||||
dsw cache.DesiredStateOfWorld,
|
dsw cache.DesiredStateOfWorld,
|
||||||
pluginMgr *volume.VolumePluginMgr) *attachDetachStateCollector {
|
pluginMgr *volume.VolumePluginMgr,
|
||||||
return &attachDetachStateCollector{pvcLister: pvcLister, podLister: podLister, pvLister: pvLister, asw: asw, dsw: dsw, volumePluginMgr: pluginMgr}
|
csiMigratedPluginManager csimigration.PluginManager,
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator) *attachDetachStateCollector {
|
||||||
|
return &attachDetachStateCollector{pvcLister: pvcLister, podLister: podLister, pvLister: pvLister, asw: asw, dsw: dsw, volumePluginMgr: pluginMgr, csiMigratedPluginManager: csiMigratedPluginManager, intreeToCSITranslator: intreeToCSITranslator}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if our collector implements necessary collector interface
|
// Check if our collector implements necessary collector interface
|
||||||
@ -158,7 +168,7 @@ func (collector *attachDetachStateCollector) getVolumeInUseCount() volumeCount {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, podVolume := range pod.Spec.Volumes {
|
for _, podVolume := range pod.Spec.Volumes {
|
||||||
volumeSpec, err := util.CreateVolumeSpec(podVolume, pod.Namespace, collector.pvcLister, collector.pvLister)
|
volumeSpec, err := util.CreateVolumeSpec(podVolume, pod.Namespace, types.NodeName(pod.Spec.NodeName), collector.volumePluginMgr, collector.pvcLister, collector.pvLister, collector.csiMigratedPluginManager, collector.intreeToCSITranslator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,11 @@ import (
|
|||||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing"
|
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
)
|
)
|
||||||
@ -107,13 +109,16 @@ func TestVolumesInUseMetricCollection(t *testing.T) {
|
|||||||
pvcLister := pvcInformer.Lister()
|
pvcLister := pvcInformer.Lister()
|
||||||
pvLister := pvInformer.Lister()
|
pvLister := pvInformer.Lister()
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
metricCollector := newAttachDetachStateCollector(
|
metricCollector := newAttachDetachStateCollector(
|
||||||
pvcLister,
|
pvcLister,
|
||||||
fakePodInformer.Lister(),
|
fakePodInformer.Lister(),
|
||||||
pvLister,
|
pvLister,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
fakeVolumePluginMgr)
|
fakeVolumePluginMgr,
|
||||||
|
csimigration.NewPluginManager(csiTranslator),
|
||||||
|
csiTranslator)
|
||||||
nodeUseMap := metricCollector.getVolumeInUseCount()
|
nodeUseMap := metricCollector.getVolumeInUseCount()
|
||||||
if len(nodeUseMap) < 1 {
|
if len(nodeUseMap) < 1 {
|
||||||
t.Errorf("Expected one volume in use got %d", len(nodeUseMap))
|
t.Errorf("Expected one volume in use got %d", len(nodeUseMap))
|
||||||
@ -145,13 +150,16 @@ func TestTotalVolumesMetricCollection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
asw.AddVolumeNode(volumeName, volumeSpec, nodeName, "", true)
|
asw.AddVolumeNode(volumeName, volumeSpec, nodeName, "", true)
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
metricCollector := newAttachDetachStateCollector(
|
metricCollector := newAttachDetachStateCollector(
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
asw,
|
asw,
|
||||||
dsw,
|
dsw,
|
||||||
fakeVolumePluginMgr)
|
fakeVolumePluginMgr,
|
||||||
|
csimigration.NewPluginManager(csiTranslator),
|
||||||
|
csiTranslator)
|
||||||
|
|
||||||
totalVolumesMap := metricCollector.getTotalVolumesCount()
|
totalVolumesMap := metricCollector.getTotalVolumesCount()
|
||||||
if len(totalVolumesMap) != 2 {
|
if len(totalVolumesMap) != 2 {
|
||||||
|
@ -14,6 +14,7 @@ go_library(
|
|||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/util:go_default_library",
|
"//pkg/controller/volume/attachdetach/util:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
@ -45,6 +46,7 @@ go_test(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller:go_default_library",
|
"//pkg/controller:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/testing:go_default_library",
|
"//pkg/volume/testing:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@ -52,5 +54,6 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volutil "k8s.io/kubernetes/pkg/volume/util"
|
volutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,27 +59,33 @@ func NewDesiredStateOfWorldPopulator(
|
|||||||
desiredStateOfWorld cache.DesiredStateOfWorld,
|
desiredStateOfWorld cache.DesiredStateOfWorld,
|
||||||
volumePluginMgr *volume.VolumePluginMgr,
|
volumePluginMgr *volume.VolumePluginMgr,
|
||||||
pvcLister corelisters.PersistentVolumeClaimLister,
|
pvcLister corelisters.PersistentVolumeClaimLister,
|
||||||
pvLister corelisters.PersistentVolumeLister) DesiredStateOfWorldPopulator {
|
pvLister corelisters.PersistentVolumeLister,
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager,
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator) DesiredStateOfWorldPopulator {
|
||||||
return &desiredStateOfWorldPopulator{
|
return &desiredStateOfWorldPopulator{
|
||||||
loopSleepDuration: loopSleepDuration,
|
loopSleepDuration: loopSleepDuration,
|
||||||
listPodsRetryDuration: listPodsRetryDuration,
|
listPodsRetryDuration: listPodsRetryDuration,
|
||||||
podLister: podLister,
|
podLister: podLister,
|
||||||
desiredStateOfWorld: desiredStateOfWorld,
|
desiredStateOfWorld: desiredStateOfWorld,
|
||||||
volumePluginMgr: volumePluginMgr,
|
volumePluginMgr: volumePluginMgr,
|
||||||
pvcLister: pvcLister,
|
pvcLister: pvcLister,
|
||||||
pvLister: pvLister,
|
pvLister: pvLister,
|
||||||
|
csiMigratedPluginManager: csiMigratedPluginManager,
|
||||||
|
intreeToCSITranslator: intreeToCSITranslator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type desiredStateOfWorldPopulator struct {
|
type desiredStateOfWorldPopulator struct {
|
||||||
loopSleepDuration time.Duration
|
loopSleepDuration time.Duration
|
||||||
podLister corelisters.PodLister
|
podLister corelisters.PodLister
|
||||||
desiredStateOfWorld cache.DesiredStateOfWorld
|
desiredStateOfWorld cache.DesiredStateOfWorld
|
||||||
volumePluginMgr *volume.VolumePluginMgr
|
volumePluginMgr *volume.VolumePluginMgr
|
||||||
pvcLister corelisters.PersistentVolumeClaimLister
|
pvcLister corelisters.PersistentVolumeClaimLister
|
||||||
pvLister corelisters.PersistentVolumeLister
|
pvLister corelisters.PersistentVolumeLister
|
||||||
listPodsRetryDuration time.Duration
|
listPodsRetryDuration time.Duration
|
||||||
timeOfLastListPods time.Time
|
timeOfLastListPods time.Time
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dswp *desiredStateOfWorldPopulator) Run(stopCh <-chan struct{}) {
|
func (dswp *desiredStateOfWorldPopulator) Run(stopCh <-chan struct{}) {
|
||||||
@ -163,7 +170,7 @@ func (dswp *desiredStateOfWorldPopulator) findAndAddActivePods() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
util.ProcessPodVolumes(pod, true,
|
util.ProcessPodVolumes(pod, true,
|
||||||
dswp.desiredStateOfWorld, dswp.volumePluginMgr, dswp.pvcLister, dswp.pvLister)
|
dswp.desiredStateOfWorld, dswp.volumePluginMgr, dswp.pvcLister, dswp.pvLister, dswp.csiMigratedPluginManager, dswp.intreeToCSITranslator)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,10 @@ import (
|
|||||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
@ -73,14 +75,17 @@ func TestFindAndAddActivePods_FindAndRemoveDeletedPods(t *testing.T) {
|
|||||||
pvcLister := fakeInformerFactory.Core().V1().PersistentVolumeClaims().Lister()
|
pvcLister := fakeInformerFactory.Core().V1().PersistentVolumeClaims().Lister()
|
||||||
pvLister := fakeInformerFactory.Core().V1().PersistentVolumes().Lister()
|
pvLister := fakeInformerFactory.Core().V1().PersistentVolumes().Lister()
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
dswp := &desiredStateOfWorldPopulator{
|
dswp := &desiredStateOfWorldPopulator{
|
||||||
loopSleepDuration: 100 * time.Millisecond,
|
loopSleepDuration: 100 * time.Millisecond,
|
||||||
listPodsRetryDuration: 3 * time.Second,
|
listPodsRetryDuration: 3 * time.Second,
|
||||||
desiredStateOfWorld: fakesDSW,
|
desiredStateOfWorld: fakesDSW,
|
||||||
volumePluginMgr: fakeVolumePluginMgr,
|
volumePluginMgr: fakeVolumePluginMgr,
|
||||||
podLister: fakePodInformer.Lister(),
|
podLister: fakePodInformer.Lister(),
|
||||||
pvcLister: pvcLister,
|
pvcLister: pvcLister,
|
||||||
pvLister: pvLister,
|
pvLister: pvLister,
|
||||||
|
csiMigratedPluginManager: csimigration.NewPluginManager(csiTranslator),
|
||||||
|
intreeToCSITranslator: csiTranslator,
|
||||||
}
|
}
|
||||||
|
|
||||||
//add the given node to the list of nodes managed by dsw
|
//add the given node to the list of nodes managed by dsw
|
||||||
|
@ -251,10 +251,6 @@ func (plugin *TestPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *TestPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *TestPlugin) RequiresRemount() bool {
|
func (plugin *TestPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,14 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/controller/volume/attachdetach/util",
|
importpath = "k8s.io/kubernetes/pkg/controller/volume/attachdetach/util",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -17,20 +17,29 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateVolumeSpec creates and returns a mutatable volume.Spec object for the
|
// CreateVolumeSpec creates and returns a mutatable volume.Spec object for the
|
||||||
// specified volume. It dereference any PVC to get PV objects, if needed.
|
// specified volume. It dereference any PVC to get PV objects, if needed.
|
||||||
func CreateVolumeSpec(podVolume v1.Volume, podNamespace string, pvcLister corelisters.PersistentVolumeClaimLister, pvLister corelisters.PersistentVolumeLister) (*volume.Spec, error) {
|
// A volume.Spec that refers to an in-tree plugin spec is translated to refer
|
||||||
|
// to a migrated CSI plugin spec if all conditions for CSI migration on a node
|
||||||
|
// for the in-tree plugin is satisfied.
|
||||||
|
func CreateVolumeSpec(podVolume v1.Volume, podNamespace string, nodeName types.NodeName, vpm *volume.VolumePluginMgr, pvcLister corelisters.PersistentVolumeClaimLister, pvLister corelisters.PersistentVolumeLister, csiMigratedPluginManager csimigration.PluginManager, csiTranslator csimigration.InTreeToCSITranslator) (*volume.Spec, error) {
|
||||||
if pvcSource := podVolume.VolumeSource.PersistentVolumeClaim; pvcSource != nil {
|
if pvcSource := podVolume.VolumeSource.PersistentVolumeClaim; pvcSource != nil {
|
||||||
klog.V(10).Infof(
|
klog.V(10).Infof(
|
||||||
"Found PVC, ClaimName: %q/%q",
|
"Found PVC, ClaimName: %q/%q",
|
||||||
@ -66,6 +75,15 @@ func CreateVolumeSpec(podVolume v1.Volume, podNamespace string, pvcLister coreli
|
|||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volumeSpec, err = translateInTreeSpecToCSIIfNeeded(volumeSpec, nodeName, vpm, csiMigratedPluginManager, csiTranslator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"error performing CSI migration checks and translation for PVC %q/%q: %v",
|
||||||
|
podNamespace,
|
||||||
|
pvcSource.ClaimName,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
klog.V(10).Infof(
|
klog.V(10).Infof(
|
||||||
"Extracted volumeSpec (%v) from bound PV (pvName %q) and PVC (ClaimName %q/%q pvcUID %v)",
|
"Extracted volumeSpec (%v) from bound PV (pvName %q) and PVC (ClaimName %q/%q pvcUID %v)",
|
||||||
volumeSpec.Name(),
|
volumeSpec.Name(),
|
||||||
@ -81,7 +99,15 @@ func CreateVolumeSpec(podVolume v1.Volume, podNamespace string, pvcLister coreli
|
|||||||
// informer it may be mutated by another consumer.
|
// informer it may be mutated by another consumer.
|
||||||
clonedPodVolume := podVolume.DeepCopy()
|
clonedPodVolume := podVolume.DeepCopy()
|
||||||
|
|
||||||
return volume.NewSpecFromVolume(clonedPodVolume), nil
|
origspec := volume.NewSpecFromVolume(clonedPodVolume)
|
||||||
|
spec, err := translateInTreeSpecToCSIIfNeeded(origspec, nodeName, vpm, csiMigratedPluginManager, csiTranslator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"error performing CSI migration checks and translation for inline volume %q: %v",
|
||||||
|
podVolume.Name,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPVCFromCacheExtractPV fetches the PVC object with the given namespace and
|
// getPVCFromCacheExtractPV fetches the PVC object with the given namespace and
|
||||||
@ -160,7 +186,7 @@ func DetermineVolumeAction(pod *v1.Pod, desiredStateOfWorld cache.DesiredStateOf
|
|||||||
|
|
||||||
// ProcessPodVolumes processes the volumes in the given pod and adds them to the
|
// ProcessPodVolumes processes the volumes in the given pod and adds them to the
|
||||||
// desired state of the world if addVolumes is true, otherwise it removes them.
|
// desired state of the world if addVolumes is true, otherwise it removes them.
|
||||||
func ProcessPodVolumes(pod *v1.Pod, addVolumes bool, desiredStateOfWorld cache.DesiredStateOfWorld, volumePluginMgr *volume.VolumePluginMgr, pvcLister corelisters.PersistentVolumeClaimLister, pvLister corelisters.PersistentVolumeLister) {
|
func ProcessPodVolumes(pod *v1.Pod, addVolumes bool, desiredStateOfWorld cache.DesiredStateOfWorld, volumePluginMgr *volume.VolumePluginMgr, pvcLister corelisters.PersistentVolumeClaimLister, pvLister corelisters.PersistentVolumeLister, csiMigratedPluginManager csimigration.PluginManager, csiTranslator csimigration.InTreeToCSITranslator) {
|
||||||
if pod == nil {
|
if pod == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -193,7 +219,7 @@ func ProcessPodVolumes(pod *v1.Pod, addVolumes bool, desiredStateOfWorld cache.D
|
|||||||
|
|
||||||
// Process volume spec for each volume defined in pod
|
// Process volume spec for each volume defined in pod
|
||||||
for _, podVolume := range pod.Spec.Volumes {
|
for _, podVolume := range pod.Spec.Volumes {
|
||||||
volumeSpec, err := CreateVolumeSpec(podVolume, pod.Namespace, pvcLister, pvLister)
|
volumeSpec, err := CreateVolumeSpec(podVolume, pod.Namespace, nodeName, volumePluginMgr, pvcLister, pvLister, csiMigratedPluginManager, csiTranslator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(10).Infof(
|
klog.V(10).Infof(
|
||||||
"Error processing volume %q for pod %q/%q: %v",
|
"Error processing volume %q for pod %q/%q: %v",
|
||||||
@ -249,3 +275,110 @@ func ProcessPodVolumes(pod *v1.Pod, addVolumes bool, desiredStateOfWorld cache.D
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func translateInTreeSpecToCSIIfNeeded(spec *volume.Spec, nodeName types.NodeName, vpm *volume.VolumePluginMgr, csiMigratedPluginManager csimigration.PluginManager, csiTranslator csimigration.InTreeToCSITranslator) (*volume.Spec, error) {
|
||||||
|
translatedSpec := spec
|
||||||
|
migratable, err := csiMigratedPluginManager.IsMigratable(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
migrationSupportedOnNode, err := isCSIMigrationSupportedOnNode(nodeName, spec, vpm, csiMigratedPluginManager)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if migratable && migrationSupportedOnNode {
|
||||||
|
translatedSpec, err = csimigration.TranslateInTreeSpecToCSI(spec, csiTranslator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translatedSpec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCSIMigrationSupportedOnNode(nodeName types.NodeName, spec *volume.Spec, vpm *volume.VolumePluginMgr, csiMigratedPluginManager csimigration.PluginManager) (bool, error) {
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) ||
|
||||||
|
!utilfeature.DefaultFeatureGate.Enabled(features.CSINodeInfo) {
|
||||||
|
// If CSIMigration is disabled, CSI migration paths will not be taken for
|
||||||
|
// the node. If CSINodeInfo is disabled, checking of installation status
|
||||||
|
// of a migrated CSI plugin cannot be performed. Therefore stick to
|
||||||
|
// in-tree plugins.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginName, err := csiMigratedPluginManager.GetInTreePluginNameFromSpec(spec.PersistentVolume, spec.Volume)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pluginName) == 0 {
|
||||||
|
// Could not find a plugin name from translation directory, assume not translated
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if csiMigratedPluginManager.IsMigrationCompleteForPlugin(pluginName) {
|
||||||
|
// All nodes are expected to have migrated CSI plugin installed and
|
||||||
|
// configured when CSI Migration Complete flag is enabled for a plugin.
|
||||||
|
// CSI migration is supported even if there is version skew between
|
||||||
|
// managers and node.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodeName) == 0 {
|
||||||
|
return false, errors.New("nodeName is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeClient := vpm.Host.GetKubeClient()
|
||||||
|
if kubeClient == nil {
|
||||||
|
// Don't handle the controller/kubelet version skew check and fallback
|
||||||
|
// to just checking the feature gates. This can happen if
|
||||||
|
// we are in a standalone (headless) Kubelet
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
adcHost, ok := vpm.Host.(volume.AttachDetachVolumeHost)
|
||||||
|
if !ok {
|
||||||
|
// Don't handle the controller/kubelet version skew check and fallback
|
||||||
|
// to just checking the feature gates. This can happen if
|
||||||
|
// "enableControllerAttachDetach" is set to true on kubelet
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if adcHost.CSINodeLister() == nil {
|
||||||
|
return false, errors.New("could not find CSINodeLister in attachDetachController")
|
||||||
|
}
|
||||||
|
|
||||||
|
csiNode, err := adcHost.CSINodeLister().Get(string(nodeName))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ann := csiNode.GetAnnotations()
|
||||||
|
if ann == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mpa := ann[v1.MigratedPluginsAnnotationKey]
|
||||||
|
tok := strings.Split(mpa, ",")
|
||||||
|
mpaSet := sets.NewString(tok...)
|
||||||
|
|
||||||
|
isMigratedOnNode := mpaSet.Has(pluginName)
|
||||||
|
|
||||||
|
if isMigratedOnNode {
|
||||||
|
installed := false
|
||||||
|
driverName, err := csiMigratedPluginManager.GetCSINameFromInTreeName(pluginName)
|
||||||
|
if err != nil {
|
||||||
|
return isMigratedOnNode, err
|
||||||
|
}
|
||||||
|
for _, driver := range csiNode.Spec.Drivers {
|
||||||
|
if driver.Name == driverName {
|
||||||
|
installed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !installed {
|
||||||
|
return true, fmt.Errorf("in-tree plugin %s is migrated on node %s but driver %s is not installed", pluginName, string(nodeName), driverName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isMigratedOnNode, nil
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ go_library(
|
|||||||
"//pkg/controller/volume/events:go_default_library",
|
"//pkg/controller/volume/events:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||||
"//pkg/volume/util/subpath:go_default_library",
|
"//pkg/volume/util/subpath:go_default_library",
|
||||||
@ -63,6 +64,7 @@ go_test(
|
|||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/awsebs:go_default_library",
|
"//pkg/volume/awsebs:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controller/volume/events"
|
"k8s.io/kubernetes/pkg/controller/volume/events"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/subpath"
|
"k8s.io/kubernetes/pkg/volume/util/subpath"
|
||||||
@ -100,6 +101,8 @@ type expandController struct {
|
|||||||
queue workqueue.RateLimitingInterface
|
queue workqueue.RateLimitingInterface
|
||||||
|
|
||||||
translator CSINameTranslator
|
translator CSINameTranslator
|
||||||
|
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExpandController expands the pvs
|
// NewExpandController expands the pvs
|
||||||
@ -110,19 +113,21 @@ func NewExpandController(
|
|||||||
scInformer storageclassinformer.StorageClassInformer,
|
scInformer storageclassinformer.StorageClassInformer,
|
||||||
cloud cloudprovider.Interface,
|
cloud cloudprovider.Interface,
|
||||||
plugins []volume.VolumePlugin,
|
plugins []volume.VolumePlugin,
|
||||||
translator CSINameTranslator) (ExpandController, error) {
|
translator CSINameTranslator,
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager) (ExpandController, error) {
|
||||||
|
|
||||||
expc := &expandController{
|
expc := &expandController{
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
cloud: cloud,
|
cloud: cloud,
|
||||||
pvcLister: pvcInformer.Lister(),
|
pvcLister: pvcInformer.Lister(),
|
||||||
pvcsSynced: pvcInformer.Informer().HasSynced,
|
pvcsSynced: pvcInformer.Informer().HasSynced,
|
||||||
pvLister: pvInformer.Lister(),
|
pvLister: pvInformer.Lister(),
|
||||||
pvSynced: pvInformer.Informer().HasSynced,
|
pvSynced: pvInformer.Informer().HasSynced,
|
||||||
classLister: scInformer.Lister(),
|
classLister: scInformer.Lister(),
|
||||||
classListerSynced: scInformer.Informer().HasSynced,
|
classListerSynced: scInformer.Informer().HasSynced,
|
||||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "volume_expand"),
|
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "volume_expand"),
|
||||||
translator: translator,
|
translator: translator,
|
||||||
|
csiMigratedPluginManager: csiMigratedPluginManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := expc.volumePluginMgr.InitPlugins(plugins, nil, expc); err != nil {
|
if err := expc.volumePluginMgr.InitPlugins(plugins, nil, expc); err != nil {
|
||||||
@ -244,25 +249,15 @@ func (expc *expandController) syncHandler(key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
|
|
||||||
volumePlugin, err := expc.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
|
||||||
volumeResizerName := class.Provisioner
|
volumeResizerName := class.Provisioner
|
||||||
|
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
|
||||||
if err != nil || volumePlugin == nil {
|
migratable, err := expc.csiMigratedPluginManager.IsMigratable(volumeSpec)
|
||||||
msg := fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
|
if err != nil {
|
||||||
"waiting for an external controller to process this PVC")
|
klog.V(4).Infof("failed to check CSI migration status for PVC: %s with error: %v", util.ClaimToClaimKey(pvc), err)
|
||||||
eventType := v1.EventTypeNormal
|
|
||||||
if err != nil {
|
|
||||||
eventType = v1.EventTypeWarning
|
|
||||||
}
|
|
||||||
expc.recorder.Event(pvc, eventType, events.ExternalExpanding, fmt.Sprintf("Ignoring the PVC: %v.", msg))
|
|
||||||
klog.Infof("Ignoring the PVC %q (uid: %q) : %v.", util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, msg)
|
|
||||||
// If we are expecting that an external plugin will handle resizing this volume then
|
|
||||||
// is no point in requeuing this PVC.
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// handle CSI migration scenarios before invoking FindExpandablePluginBySpec for in-tree
|
||||||
if volumePlugin.IsMigratedToCSI() {
|
if migratable {
|
||||||
msg := fmt.Sprintf("CSI migration enabled for %s; waiting for external resizer to expand the pvc", volumeResizerName)
|
msg := fmt.Sprintf("CSI migration enabled for %s; waiting for external resizer to expand the pvc", volumeResizerName)
|
||||||
expc.recorder.Event(pvc, v1.EventTypeNormal, events.ExternalExpanding, msg)
|
expc.recorder.Event(pvc, v1.EventTypeNormal, events.ExternalExpanding, msg)
|
||||||
csiResizerName, err := expc.translator.GetCSINameFromInTreeName(class.Provisioner)
|
csiResizerName, err := expc.translator.GetCSINameFromInTreeName(class.Provisioner)
|
||||||
@ -281,6 +276,21 @@ func (expc *expandController) syncHandler(key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volumePlugin, err := expc.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
||||||
|
if err != nil || volumePlugin == nil {
|
||||||
|
msg := fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
|
||||||
|
"waiting for an external controller to process this PVC")
|
||||||
|
eventType := v1.EventTypeNormal
|
||||||
|
if err != nil {
|
||||||
|
eventType = v1.EventTypeWarning
|
||||||
|
}
|
||||||
|
expc.recorder.Event(pvc, eventType, events.ExternalExpanding, fmt.Sprintf("Ignoring the PVC: %v.", msg))
|
||||||
|
klog.Infof("Ignoring the PVC %q (uid: %q) : %v.", util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, msg)
|
||||||
|
// If we are expecting that an external plugin will handle resizing this volume then
|
||||||
|
// is no point in requeuing this PVC.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return expc.expand(pvc, pv, volumeResizerName)
|
return expc.expand(pvc, pv, volumeResizerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/awsebs"
|
"k8s.io/kubernetes/pkg/volume/awsebs"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
)
|
)
|
||||||
@ -124,7 +125,8 @@ func TestSyncHandler(t *testing.T) {
|
|||||||
if tc.storageClass != nil {
|
if tc.storageClass != nil {
|
||||||
informerFactory.Storage().V1().StorageClasses().Informer().GetIndexer().Add(tc.storageClass)
|
informerFactory.Storage().V1().StorageClasses().Informer().GetIndexer().Add(tc.storageClass)
|
||||||
}
|
}
|
||||||
expc, err := NewExpandController(fakeKubeClient, pvcInformer, pvInformer, storageClassInformer, nil, allPlugins, csitrans.New())
|
translator := csitrans.New()
|
||||||
|
expc, err := NewExpandController(fakeKubeClient, pvcInformer, pvInformer, storageClassInformer, nil, allPlugins, translator, csimigration.NewPluginManager(translator))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating expand controller : %v", err)
|
t.Fatalf("error creating expand controller : %v", err)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ go_library(
|
|||||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/recyclerclient:go_default_library",
|
"//pkg/volume/util/recyclerclient:go_default_library",
|
||||||
"//pkg/volume/util/subpath:go_default_library",
|
"//pkg/volume/util/subpath:go_default_library",
|
||||||
|
@ -527,16 +527,21 @@ func (t fakeCSINameTranslator) GetCSINameFromInTreeName(pluginName string) (stri
|
|||||||
return "vendor.com/MockCSIPlugin", nil
|
return "vendor.com/MockCSIPlugin", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeCSIMigratedPluginManager struct{}
|
||||||
|
|
||||||
|
func (t fakeCSIMigratedPluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// wrapTestWithCSIMigrationProvisionCalls returns a testCall that:
|
// wrapTestWithCSIMigrationProvisionCalls returns a testCall that:
|
||||||
// - configures controller with a volume plugin that emulates CSI migration
|
// - configures controller with a volume plugin that emulates CSI migration
|
||||||
// - calls given testCall
|
// - calls given testCall
|
||||||
func wrapTestWithCSIMigrationProvisionCalls(toWrap testCall) testCall {
|
func wrapTestWithCSIMigrationProvisionCalls(toWrap testCall) testCall {
|
||||||
|
plugin := &mockVolumePlugin{}
|
||||||
return func(ctrl *PersistentVolumeController, reactor *pvtesting.VolumeReactor, test controllerTest) error {
|
return func(ctrl *PersistentVolumeController, reactor *pvtesting.VolumeReactor, test controllerTest) error {
|
||||||
plugin := &mockVolumePlugin{
|
|
||||||
isMigratedToCSI: true,
|
|
||||||
}
|
|
||||||
ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, nil /* prober */, ctrl)
|
ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, nil /* prober */, ctrl)
|
||||||
ctrl.translator = fakeCSINameTranslator{}
|
ctrl.translator = fakeCSINameTranslator{}
|
||||||
|
ctrl.csiMigratedPluginManager = fakeCSIMigratedPluginManager{}
|
||||||
return toWrap(ctrl, reactor, test)
|
return toWrap(ctrl, reactor, test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -782,7 +787,6 @@ type mockVolumePlugin struct {
|
|||||||
deleteCallCounter int
|
deleteCallCounter int
|
||||||
recycleCalls []error
|
recycleCalls []error
|
||||||
recycleCallCounter int
|
recycleCallCounter int
|
||||||
isMigratedToCSI bool
|
|
||||||
provisionOptions vol.VolumeOptions
|
provisionOptions vol.VolumeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,10 +816,6 @@ func (plugin *mockVolumePlugin) CanSupport(spec *vol.Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *mockVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return plugin.isMigratedToCSI
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *mockVolumePlugin) RequiresRemount() bool {
|
func (plugin *mockVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,11 @@ type CSINameTranslator interface {
|
|||||||
GetCSINameFromInTreeName(pluginName string) (string, error)
|
GetCSINameFromInTreeName(pluginName string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSIMigratedPluginManager keeps track of CSI migration status of a plugin
|
||||||
|
type CSIMigratedPluginManager interface {
|
||||||
|
IsMigrationEnabledForPlugin(pluginName string) bool
|
||||||
|
}
|
||||||
|
|
||||||
// PersistentVolumeController is a controller that synchronizes
|
// PersistentVolumeController is a controller that synchronizes
|
||||||
// PersistentVolumeClaims and PersistentVolumes. It starts two
|
// PersistentVolumeClaims and PersistentVolumes. It starts two
|
||||||
// cache.Controllers that watch PersistentVolume and PersistentVolumeClaim
|
// cache.Controllers that watch PersistentVolume and PersistentVolumeClaim
|
||||||
@ -226,7 +231,8 @@ type PersistentVolumeController struct {
|
|||||||
// abort: N.A.
|
// abort: N.A.
|
||||||
operationTimestamps metrics.OperationStartTimeCache
|
operationTimestamps metrics.OperationStartTimeCache
|
||||||
|
|
||||||
translator CSINameTranslator
|
translator CSINameTranslator
|
||||||
|
csiMigratedPluginManager CSIMigratedPluginManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// syncClaim is the main controller method to decide what to do with a claim.
|
// syncClaim is the main controller method to decide what to do with a claim.
|
||||||
@ -1324,6 +1330,7 @@ func (ctrl *PersistentVolumeController) provisionClaim(claim *v1.PersistentVolum
|
|||||||
klog.V(4).Infof("provisionClaim[%s]: started", claimToClaimKey(claim))
|
klog.V(4).Infof("provisionClaim[%s]: started", claimToClaimKey(claim))
|
||||||
opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID))
|
opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID))
|
||||||
plugin, storageClass, err := ctrl.findProvisionablePlugin(claim)
|
plugin, storageClass, err := ctrl.findProvisionablePlugin(claim)
|
||||||
|
// findProvisionablePlugin does not return err for external provisioners
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, err.Error())
|
ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, err.Error())
|
||||||
klog.Errorf("error finding provisioning plugin for claim %s: %v", claimToClaimKey(claim), err)
|
klog.Errorf("error finding provisioning plugin for claim %s: %v", claimToClaimKey(claim), err)
|
||||||
@ -1338,8 +1345,8 @@ func (ctrl *PersistentVolumeController) provisionClaim(claim *v1.PersistentVolum
|
|||||||
claimKey := claimToClaimKey(claim)
|
claimKey := claimToClaimKey(claim)
|
||||||
ctrl.operationTimestamps.AddIfNotExist(claimKey, ctrl.getProvisionerName(plugin, storageClass), "provision")
|
ctrl.operationTimestamps.AddIfNotExist(claimKey, ctrl.getProvisionerName(plugin, storageClass), "provision")
|
||||||
var err error
|
var err error
|
||||||
if plugin == nil || plugin.IsMigratedToCSI() {
|
if plugin == nil {
|
||||||
_, err = ctrl.provisionClaimOperationExternal(claim, plugin, storageClass)
|
_, err = ctrl.provisionClaimOperationExternal(claim, storageClass)
|
||||||
} else {
|
} else {
|
||||||
_, err = ctrl.provisionClaimOperation(claim, plugin, storageClass)
|
_, err = ctrl.provisionClaimOperation(claim, plugin, storageClass)
|
||||||
}
|
}
|
||||||
@ -1362,8 +1369,7 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(
|
|||||||
claimClass := v1helper.GetPersistentVolumeClaimClass(claim)
|
claimClass := v1helper.GetPersistentVolumeClaimClass(claim)
|
||||||
klog.V(4).Infof("provisionClaimOperation [%s] started, class: %q", claimToClaimKey(claim), claimClass)
|
klog.V(4).Infof("provisionClaimOperation [%s] started, class: %q", claimToClaimKey(claim), claimClass)
|
||||||
|
|
||||||
// called from provisionClaim(), in this case, plugin MUST NOT be nil and
|
// called from provisionClaim(), in this case, plugin MUST NOT be nil
|
||||||
// plugin.IsMigratedToCSI() MUST return FALSE
|
|
||||||
// NOTE: checks on plugin/storageClass has been saved
|
// NOTE: checks on plugin/storageClass has been saved
|
||||||
pluginName := plugin.GetPluginName()
|
pluginName := plugin.GetPluginName()
|
||||||
provisionerName := storageClass.Provisioner
|
provisionerName := storageClass.Provisioner
|
||||||
@ -1553,15 +1559,14 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(
|
|||||||
// This method will be running in a standalone go-routine scheduled in "provisionClaim"
|
// This method will be running in a standalone go-routine scheduled in "provisionClaim"
|
||||||
func (ctrl *PersistentVolumeController) provisionClaimOperationExternal(
|
func (ctrl *PersistentVolumeController) provisionClaimOperationExternal(
|
||||||
claim *v1.PersistentVolumeClaim,
|
claim *v1.PersistentVolumeClaim,
|
||||||
plugin vol.ProvisionableVolumePlugin,
|
|
||||||
storageClass *storage.StorageClass) (string, error) {
|
storageClass *storage.StorageClass) (string, error) {
|
||||||
claimClass := v1helper.GetPersistentVolumeClaimClass(claim)
|
claimClass := v1helper.GetPersistentVolumeClaimClass(claim)
|
||||||
klog.V(4).Infof("provisionClaimOperationExternal [%s] started, class: %q", claimToClaimKey(claim), claimClass)
|
klog.V(4).Infof("provisionClaimOperationExternal [%s] started, class: %q", claimToClaimKey(claim), claimClass)
|
||||||
// Set provisionerName to external provisioner name by setClaimProvisioner
|
// Set provisionerName to external provisioner name by setClaimProvisioner
|
||||||
var err error
|
var err error
|
||||||
provisionerName := storageClass.Provisioner
|
provisionerName := storageClass.Provisioner
|
||||||
if plugin != nil {
|
if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(storageClass.Provisioner) {
|
||||||
// update the provisioner name to use the CSI in-tree name
|
// update the provisioner name to use the migrated CSI plugin name
|
||||||
provisionerName, err = ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner)
|
provisionerName, err = ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
strerr := fmt.Sprintf("error getting CSI name for In tree plugin %s: %v", storageClass.Provisioner, err)
|
strerr := fmt.Sprintf("error getting CSI name for In tree plugin %s: %v", storageClass.Provisioner, err)
|
||||||
@ -1660,6 +1665,10 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.Persis
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find a plugin for the class
|
// Find a plugin for the class
|
||||||
|
if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(class.Provisioner) {
|
||||||
|
// CSI migration scenario - do not depend on in-tree plugin
|
||||||
|
return nil, class, nil
|
||||||
|
}
|
||||||
plugin, err := ctrl.volumePluginMgr.FindProvisionablePluginByName(class.Provisioner)
|
plugin, err := ctrl.volumePluginMgr.FindProvisionablePluginByName(class.Provisioner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !strings.HasPrefix(class.Provisioner, "kubernetes.io/") {
|
if !strings.HasPrefix(class.Provisioner, "kubernetes.io/") {
|
||||||
@ -1708,7 +1717,7 @@ func (ctrl *PersistentVolumeController) getProvisionerNameFromVolume(volume *v1.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
if plugin != nil && !plugin.IsMigratedToCSI() {
|
if plugin != nil {
|
||||||
return plugin.GetPluginName()
|
return plugin.GetPluginName()
|
||||||
}
|
}
|
||||||
// If reached here, Either an external provisioner was used for provisioning
|
// If reached here, Either an external provisioner was used for provisioning
|
||||||
@ -1722,22 +1731,25 @@ func (ctrl *PersistentVolumeController) getProvisionerNameFromVolume(volume *v1.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
if plugin != nil {
|
if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(class.Provisioner) {
|
||||||
provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(class.Provisioner)
|
provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(class.Provisioner)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return provisionerName
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
return provisionerName
|
||||||
}
|
}
|
||||||
return class.Provisioner
|
return class.Provisioner
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtain plugin/external provisioner name from plugin and storage class
|
// obtain plugin/external provisioner name from plugin and storage class for timestamp logging purposes
|
||||||
func (ctrl *PersistentVolumeController) getProvisionerName(plugin vol.ProvisionableVolumePlugin, storageClass *storage.StorageClass) string {
|
func (ctrl *PersistentVolumeController) getProvisionerName(plugin vol.ProvisionableVolumePlugin, storageClass *storage.StorageClass) string {
|
||||||
// intree plugin, returns the plugin's name
|
// non CSI-migrated in-tree plugin, returns the plugin's name
|
||||||
if plugin != nil && !plugin.IsMigratedToCSI() {
|
if plugin != nil {
|
||||||
return plugin.GetPluginName()
|
return plugin.GetPluginName()
|
||||||
} else if plugin != nil {
|
}
|
||||||
// get the CSI in-tree name from storage class provisioner name
|
if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(storageClass.Provisioner) {
|
||||||
|
// get the name of the CSI plugin that the in-tree storage class
|
||||||
|
// provisioner has migrated to
|
||||||
provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner)
|
provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
|
@ -44,6 +44,7 @@ import (
|
|||||||
pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
|
pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
|
||||||
"k8s.io/kubernetes/pkg/util/goroutinemap"
|
"k8s.io/kubernetes/pkg/util/goroutinemap"
|
||||||
vol "k8s.io/kubernetes/pkg/volume"
|
vol "k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
|
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
@ -94,7 +95,6 @@ func NewController(p ControllerParameters) (*PersistentVolumeController, error)
|
|||||||
volumeQueue: workqueue.NewNamed("volumes"),
|
volumeQueue: workqueue.NewNamed("volumes"),
|
||||||
resyncPeriod: p.SyncPeriod,
|
resyncPeriod: p.SyncPeriod,
|
||||||
operationTimestamps: metrics.NewOperationStartTimeCache(),
|
operationTimestamps: metrics.NewOperationStartTimeCache(),
|
||||||
translator: csitrans.New(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prober is nil because PV is not aware of Flexvolume.
|
// Prober is nil because PV is not aware of Flexvolume.
|
||||||
@ -128,6 +128,11 @@ func NewController(p ControllerParameters) (*PersistentVolumeController, error)
|
|||||||
controller.podListerSynced = p.PodInformer.Informer().HasSynced
|
controller.podListerSynced = p.PodInformer.Informer().HasSynced
|
||||||
controller.NodeLister = p.NodeInformer.Lister()
|
controller.NodeLister = p.NodeInformer.Lister()
|
||||||
controller.NodeListerSynced = p.NodeInformer.Informer().HasSynced
|
controller.NodeListerSynced = p.NodeInformer.Informer().HasSynced
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
|
controller.translator = csiTranslator
|
||||||
|
controller.csiMigratedPluginManager = csimigration.NewPluginManager(csiTranslator)
|
||||||
|
|
||||||
return controller, nil
|
return controller, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,24 +389,52 @@ const (
|
|||||||
// Enables the GCE PD in-tree driver to GCE CSI Driver migration feature.
|
// Enables the GCE PD in-tree driver to GCE CSI Driver migration feature.
|
||||||
CSIMigrationGCE featuregate.Feature = "CSIMigrationGCE"
|
CSIMigrationGCE featuregate.Feature = "CSIMigrationGCE"
|
||||||
|
|
||||||
|
// owner: @davidz627
|
||||||
|
// alpha: v1.17
|
||||||
|
//
|
||||||
|
// Disables the GCE PD in-tree driver.
|
||||||
|
// Expects GCE PD CSI Driver to be installed and configured on all nodes.
|
||||||
|
CSIMigrationGCEComplete featuregate.Feature = "CSIMigrationGCEComplete"
|
||||||
|
|
||||||
// owner: @leakingtapan
|
// owner: @leakingtapan
|
||||||
// alpha: v1.14
|
// alpha: v1.14
|
||||||
//
|
//
|
||||||
// Enables the AWS EBS in-tree driver to AWS EBS CSI Driver migration feature.
|
// Enables the AWS EBS in-tree driver to AWS EBS CSI Driver migration feature.
|
||||||
CSIMigrationAWS featuregate.Feature = "CSIMigrationAWS"
|
CSIMigrationAWS featuregate.Feature = "CSIMigrationAWS"
|
||||||
|
|
||||||
|
// owner: @leakingtapan
|
||||||
|
// alpha: v1.17
|
||||||
|
//
|
||||||
|
// Disables the AWS EBS in-tree driver.
|
||||||
|
// Expects AWS EBS CSI Driver to be installed and configured on all nodes.
|
||||||
|
CSIMigrationAWSComplete featuregate.Feature = "CSIMigrationAWSComplete"
|
||||||
|
|
||||||
// owner: @andyzhangx
|
// owner: @andyzhangx
|
||||||
// alpha: v1.15
|
// alpha: v1.15
|
||||||
//
|
//
|
||||||
// Enables the Azure Disk in-tree driver to Azure Disk Driver migration feature.
|
// Enables the Azure Disk in-tree driver to Azure Disk Driver migration feature.
|
||||||
CSIMigrationAzureDisk featuregate.Feature = "CSIMigrationAzureDisk"
|
CSIMigrationAzureDisk featuregate.Feature = "CSIMigrationAzureDisk"
|
||||||
|
|
||||||
|
// owner: @andyzhangx
|
||||||
|
// alpha: v1.17
|
||||||
|
//
|
||||||
|
// Disables the Azure Disk in-tree driver.
|
||||||
|
// Expects Azure Disk CSI Driver to be installed and configured on all nodes.
|
||||||
|
CSIMigrationAzureDiskComplete featuregate.Feature = "CSIMigrationAzureDiskComplete"
|
||||||
|
|
||||||
// owner: @andyzhangx
|
// owner: @andyzhangx
|
||||||
// alpha: v1.15
|
// alpha: v1.15
|
||||||
//
|
//
|
||||||
// Enables the Azure File in-tree driver to Azure File Driver migration feature.
|
// Enables the Azure File in-tree driver to Azure File Driver migration feature.
|
||||||
CSIMigrationAzureFile featuregate.Feature = "CSIMigrationAzureFile"
|
CSIMigrationAzureFile featuregate.Feature = "CSIMigrationAzureFile"
|
||||||
|
|
||||||
|
// owner: @andyzhangx
|
||||||
|
// alpha: v1.17
|
||||||
|
//
|
||||||
|
// Disables the Azure File in-tree driver.
|
||||||
|
// Expects Azure File CSI Driver to be installed and configured on all nodes.
|
||||||
|
CSIMigrationAzureFileComplete featuregate.Feature = "CSIMigrationAzureFileComplete"
|
||||||
|
|
||||||
// owner: @RobertKrawitz
|
// owner: @RobertKrawitz
|
||||||
// beta: v1.15
|
// beta: v1.15
|
||||||
//
|
//
|
||||||
@ -433,6 +461,13 @@ const (
|
|||||||
// Enables the OpenStack Cinder in-tree driver to OpenStack Cinder CSI Driver migration feature.
|
// Enables the OpenStack Cinder in-tree driver to OpenStack Cinder CSI Driver migration feature.
|
||||||
CSIMigrationOpenStack featuregate.Feature = "CSIMigrationOpenStack"
|
CSIMigrationOpenStack featuregate.Feature = "CSIMigrationOpenStack"
|
||||||
|
|
||||||
|
// owner: @adisky
|
||||||
|
// alpha: v1.17
|
||||||
|
//
|
||||||
|
// Disables the OpenStack Cinder in-tree driver.
|
||||||
|
// Expects the OpenStack Cinder CSI Driver to be installed and configured on all nodes.
|
||||||
|
CSIMigrationOpenStackComplete featuregate.Feature = "CSIMigrationOpenStackComplete"
|
||||||
|
|
||||||
// owner: @MrHohn
|
// owner: @MrHohn
|
||||||
// alpha: v1.15
|
// alpha: v1.15
|
||||||
// beta: v1.16
|
// beta: v1.16
|
||||||
@ -552,11 +587,16 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta},
|
CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CSIMigration: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigration: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
CSIMigrationGCE: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigrationGCE: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
CSIMigrationGCEComplete: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
CSIMigrationAWS: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigrationAWS: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
CSIMigrationAWSComplete: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
CSIMigrationAzureDisk: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigrationAzureDisk: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
CSIMigrationAzureDiskComplete: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
CSIMigrationAzureFile: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigrationAzureFile: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
CSIMigrationAzureFileComplete: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
RunAsGroup: {Default: true, PreRelease: featuregate.Beta},
|
RunAsGroup: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CSIMigrationOpenStack: {Default: false, PreRelease: featuregate.Alpha},
|
CSIMigrationOpenStack: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
CSIMigrationOpenStackComplete: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
VolumeSubpath: {Default: true, PreRelease: featuregate.GA},
|
VolumeSubpath: {Default: true, PreRelease: featuregate.GA},
|
||||||
BalanceAttachedNodeVolumes: {Default: false, PreRelease: featuregate.Alpha},
|
BalanceAttachedNodeVolumes: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
VolumeSubpathEnvExpansion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.19,
|
VolumeSubpathEnvExpansion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.19,
|
||||||
|
@ -25,6 +25,7 @@ go_library(
|
|||||||
"//pkg/kubelet/volumemanager/reconciler:go_default_library",
|
"//pkg/kubelet/volumemanager/reconciler:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/hostutil:go_default_library",
|
"//pkg/volume/util/hostutil:go_default_library",
|
||||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||||
@ -37,6 +38,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@ go_library(
|
|||||||
"//pkg/kubelet/util/format:go_default_library",
|
"//pkg/kubelet/util/format:go_default_library",
|
||||||
"//pkg/kubelet/volumemanager/cache:go_default_library",
|
"//pkg/kubelet/volumemanager/cache:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@ -59,6 +60,7 @@ go_test(
|
|||||||
"//pkg/kubelet/status:go_default_library",
|
"//pkg/kubelet/status:go_default_library",
|
||||||
"//pkg/kubelet/status/testing:go_default_library",
|
"//pkg/kubelet/status/testing:go_default_library",
|
||||||
"//pkg/kubelet/volumemanager/cache:go_default_library",
|
"//pkg/kubelet/volumemanager/cache:go_default_library",
|
||||||
|
"//pkg/volume/csimigration:go_default_library",
|
||||||
"//pkg/volume/testing:go_default_library",
|
"//pkg/volume/testing:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
@ -70,5 +72,6 @@ go_test(
|
|||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -43,6 +43,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
|
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
)
|
)
|
||||||
@ -87,7 +88,9 @@ func NewDesiredStateOfWorldPopulator(
|
|||||||
desiredStateOfWorld cache.DesiredStateOfWorld,
|
desiredStateOfWorld cache.DesiredStateOfWorld,
|
||||||
actualStateOfWorld cache.ActualStateOfWorld,
|
actualStateOfWorld cache.ActualStateOfWorld,
|
||||||
kubeContainerRuntime kubecontainer.Runtime,
|
kubeContainerRuntime kubecontainer.Runtime,
|
||||||
keepTerminatedPodVolumes bool) DesiredStateOfWorldPopulator {
|
keepTerminatedPodVolumes bool,
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager,
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator) DesiredStateOfWorldPopulator {
|
||||||
return &desiredStateOfWorldPopulator{
|
return &desiredStateOfWorldPopulator{
|
||||||
kubeClient: kubeClient,
|
kubeClient: kubeClient,
|
||||||
loopSleepDuration: loopSleepDuration,
|
loopSleepDuration: loopSleepDuration,
|
||||||
@ -102,6 +105,8 @@ func NewDesiredStateOfWorldPopulator(
|
|||||||
keepTerminatedPodVolumes: keepTerminatedPodVolumes,
|
keepTerminatedPodVolumes: keepTerminatedPodVolumes,
|
||||||
hasAddedPods: false,
|
hasAddedPods: false,
|
||||||
hasAddedPodsLock: sync.RWMutex{},
|
hasAddedPodsLock: sync.RWMutex{},
|
||||||
|
csiMigratedPluginManager: csiMigratedPluginManager,
|
||||||
|
intreeToCSITranslator: intreeToCSITranslator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +124,8 @@ type desiredStateOfWorldPopulator struct {
|
|||||||
keepTerminatedPodVolumes bool
|
keepTerminatedPodVolumes bool
|
||||||
hasAddedPods bool
|
hasAddedPods bool
|
||||||
hasAddedPodsLock sync.RWMutex
|
hasAddedPodsLock sync.RWMutex
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
type processedPods struct {
|
type processedPods struct {
|
||||||
@ -505,6 +512,17 @@ func (dswp *desiredStateOfWorldPopulator) createVolumeSpec(
|
|||||||
pvcSource.ClaimName,
|
pvcSource.ClaimName,
|
||||||
pvcUID)
|
pvcUID)
|
||||||
|
|
||||||
|
migratable, err := dswp.csiMigratedPluginManager.IsMigratable(volumeSpec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
if migratable {
|
||||||
|
volumeSpec, err = csimigration.TranslateInTreeSpecToCSI(volumeSpec, dswp.intreeToCSITranslator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: replace this with util.GetVolumeMode() when features.BlockVolume is removed.
|
// TODO: replace this with util.GetVolumeMode() when features.BlockVolume is removed.
|
||||||
// The function will return the right value then.
|
// The function will return the right value then.
|
||||||
volumeMode := v1.PersistentVolumeFilesystem
|
volumeMode := v1.PersistentVolumeFilesystem
|
||||||
@ -537,7 +555,18 @@ func (dswp *desiredStateOfWorldPopulator) createVolumeSpec(
|
|||||||
// Do not return the original volume object, since the source could mutate it
|
// Do not return the original volume object, since the source could mutate it
|
||||||
clonedPodVolume := podVolume.DeepCopy()
|
clonedPodVolume := podVolume.DeepCopy()
|
||||||
|
|
||||||
return nil, volume.NewSpecFromVolume(clonedPodVolume), "", nil
|
spec := volume.NewSpecFromVolume(clonedPodVolume)
|
||||||
|
migratable, err := dswp.csiMigratedPluginManager.IsMigratable(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
if migratable {
|
||||||
|
spec, err = csimigration.TranslateInTreeSpecToCSI(spec, dswp.intreeToCSITranslator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, spec, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPVCExtractPV fetches the PVC object with the given namespace and name from
|
// getPVCExtractPV fetches the PVC object with the given namespace and name from
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
core "k8s.io/client-go/testing"
|
core "k8s.io/client-go/testing"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
||||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
@ -39,6 +40,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/status"
|
"k8s.io/kubernetes/pkg/kubelet/status"
|
||||||
statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
|
statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
|
"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
@ -962,6 +964,7 @@ func createDswpWithVolume(t *testing.T, pv *v1.PersistentVolume, pvc *v1.Persist
|
|||||||
|
|
||||||
fakeStatusManager := status.NewManager(fakeClient, fakePodManager, &statustest.FakePodDeletionSafetyProvider{})
|
fakeStatusManager := status.NewManager(fakeClient, fakePodManager, &statustest.FakePodDeletionSafetyProvider{})
|
||||||
|
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
dswp := &desiredStateOfWorldPopulator{
|
dswp := &desiredStateOfWorldPopulator{
|
||||||
kubeClient: fakeClient,
|
kubeClient: fakeClient,
|
||||||
loopSleepDuration: 100 * time.Millisecond,
|
loopSleepDuration: 100 * time.Millisecond,
|
||||||
@ -974,6 +977,8 @@ func createDswpWithVolume(t *testing.T, pv *v1.PersistentVolume, pvc *v1.Persist
|
|||||||
processedPods: make(map[types.UniquePodName]bool)},
|
processedPods: make(map[types.UniquePodName]bool)},
|
||||||
kubeContainerRuntime: fakeRuntime,
|
kubeContainerRuntime: fakeRuntime,
|
||||||
keepTerminatedPodVolumes: false,
|
keepTerminatedPodVolumes: false,
|
||||||
|
csiMigratedPluginManager: csimigration.NewPluginManager(csiTranslator),
|
||||||
|
intreeToCSITranslator: csiTranslator,
|
||||||
}
|
}
|
||||||
return dswp, fakePodManager, fakesDSW
|
return dswp, fakePodManager, fakesDSW
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/config"
|
"k8s.io/kubernetes/pkg/kubelet/config"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
@ -43,6 +44,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/volumemanager/reconciler"
|
"k8s.io/kubernetes/pkg/kubelet/volumemanager/reconciler"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/csimigration"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
@ -174,6 +176,11 @@ func NewVolumeManager(
|
|||||||
blockVolumePathHandler)),
|
blockVolumePathHandler)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intreeToCSITranslator := csitrans.New()
|
||||||
|
csiMigratedPluginManager := csimigration.NewPluginManager(intreeToCSITranslator)
|
||||||
|
|
||||||
|
vm.intreeToCSITranslator = intreeToCSITranslator
|
||||||
|
vm.csiMigratedPluginManager = csiMigratedPluginManager
|
||||||
vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
||||||
@ -183,7 +190,9 @@ func NewVolumeManager(
|
|||||||
vm.desiredStateOfWorld,
|
vm.desiredStateOfWorld,
|
||||||
vm.actualStateOfWorld,
|
vm.actualStateOfWorld,
|
||||||
kubeContainerRuntime,
|
kubeContainerRuntime,
|
||||||
keepTerminatedPodVolumes)
|
keepTerminatedPodVolumes,
|
||||||
|
csiMigratedPluginManager,
|
||||||
|
intreeToCSITranslator)
|
||||||
vm.reconciler = reconciler.NewReconciler(
|
vm.reconciler = reconciler.NewReconciler(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
controllerAttachDetachEnabled,
|
controllerAttachDetachEnabled,
|
||||||
@ -238,6 +247,12 @@ type volumeManager struct {
|
|||||||
// desiredStateOfWorldPopulator runs an asynchronous periodic loop to
|
// desiredStateOfWorldPopulator runs an asynchronous periodic loop to
|
||||||
// populate the desiredStateOfWorld using the kubelet PodManager.
|
// populate the desiredStateOfWorld using the kubelet PodManager.
|
||||||
desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator
|
desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator
|
||||||
|
|
||||||
|
// csiMigratedPluginManager keeps track of CSI migration status of plugins
|
||||||
|
csiMigratedPluginManager csimigration.PluginManager
|
||||||
|
|
||||||
|
// intreeToCSITranslator translates in-tree volume specs to CSI
|
||||||
|
intreeToCSITranslator csimigration.InTreeToCSITranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *volumeManager) Run(sourcesReady config.SourcesReady, stopCh <-chan struct{}) {
|
func (vm *volumeManager) Run(sourcesReady config.SourcesReady, stopCh <-chan struct{}) {
|
||||||
|
@ -88,6 +88,7 @@ filegroup(
|
|||||||
"//pkg/volume/cinder:all-srcs",
|
"//pkg/volume/cinder:all-srcs",
|
||||||
"//pkg/volume/configmap:all-srcs",
|
"//pkg/volume/configmap:all-srcs",
|
||||||
"//pkg/volume/csi:all-srcs",
|
"//pkg/volume/csi:all-srcs",
|
||||||
|
"//pkg/volume/csimigration:all-srcs",
|
||||||
"//pkg/volume/downwardapi:all-srcs",
|
"//pkg/volume/downwardapi:all-srcs",
|
||||||
"//pkg/volume/emptydir:all-srcs",
|
"//pkg/volume/emptydir:all-srcs",
|
||||||
"//pkg/volume/fc:all-srcs",
|
"//pkg/volume/fc:all-srcs",
|
||||||
|
@ -89,11 +89,6 @@ func (plugin *awsElasticBlockStorePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.AWSElasticBlockStore != nil)
|
(spec.Volume != nil && spec.Volume.AWSElasticBlockStore != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *awsElasticBlockStorePlugin) IsMigratedToCSI() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
|
|
||||||
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWS)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *awsElasticBlockStorePlugin) RequiresRemount() bool {
|
func (plugin *awsElasticBlockStorePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/legacy-cloud-providers/azure"
|
"k8s.io/legacy-cloud-providers/azure"
|
||||||
@ -122,11 +120,6 @@ func (plugin *azureDataDiskPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.AzureDisk != nil)
|
(spec.Volume != nil && spec.Volume.AzureDisk != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *azureDataDiskPlugin) IsMigratedToCSI() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
|
|
||||||
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *azureDataDiskPlugin) RequiresRemount() bool {
|
func (plugin *azureDataDiskPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ go_library(
|
|||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/volume/azure_file",
|
importpath = "k8s.io/kubernetes/pkg/volume/azure_file",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/features:go_default_library",
|
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
@ -25,7 +24,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_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/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types: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:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
|
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
|
||||||
"//staging/src/k8s.io/legacy-cloud-providers/azure:go_default_library",
|
"//staging/src/k8s.io/legacy-cloud-providers/azure:go_default_library",
|
||||||
|
@ -28,11 +28,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
volutil "k8s.io/kubernetes/pkg/volume/util"
|
volutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
@ -85,11 +83,6 @@ func (plugin *azureFilePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.AzureFile != nil)
|
(spec.Volume != nil && spec.Volume.AzureFile != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *azureFilePlugin) IsMigratedToCSI() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
|
|
||||||
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *azureFilePlugin) RequiresRemount() bool {
|
func (plugin *azureFilePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -71,10 +71,6 @@ func (plugin *cephfsPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.Volume != nil && spec.Volume.CephFS != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CephFS != nil)
|
return (spec.Volume != nil && spec.Volume.CephFS != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CephFS != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *cephfsPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *cephfsPlugin) RequiresRemount() bool {
|
func (plugin *cephfsPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -111,11 +111,6 @@ func (plugin *cinderPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.Volume != nil && spec.Volume.Cinder != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Cinder != nil)
|
return (spec.Volume != nil && spec.Volume.Cinder != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Cinder != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *cinderPlugin) IsMigratedToCSI() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
|
|
||||||
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *cinderPlugin) RequiresRemount() bool {
|
func (plugin *cinderPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,6 @@ func (plugin *configMapPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.Volume != nil && spec.Volume.ConfigMap != nil
|
return spec.Volume != nil && spec.Volume.ConfigMap != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *configMapPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *configMapPlugin) RequiresRemount() bool {
|
func (plugin *configMapPlugin) RequiresRemount() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -314,10 +314,6 @@ func (p *csiPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil
|
return spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *csiPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *csiPlugin) RequiresRemount() bool {
|
func (p *csiPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
45
pkg/volume/csimigration/BUILD
Normal file
45
pkg/volume/csimigration/BUILD
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["plugin_manager.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/csimigration",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//pkg/volume:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib/plugins:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["plugin_manager_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//pkg/volume:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
156
pkg/volume/csimigration/plugin_manager.go
Normal file
156
pkg/volume/csimigration/plugin_manager.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 csimigration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
csilibplugins "k8s.io/csi-translation-lib/plugins"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginNameMapper contains utility methods to retrieve names of plugins
|
||||||
|
// that support a spec, map intree <=> migrated CSI plugin names, etc
|
||||||
|
type PluginNameMapper interface {
|
||||||
|
GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error)
|
||||||
|
GetCSINameFromInTreeName(pluginName string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginManager keeps track of migrated state of in-tree plugins
|
||||||
|
type PluginManager struct {
|
||||||
|
PluginNameMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPluginManager returns a new PluginManager instance
|
||||||
|
func NewPluginManager(m PluginNameMapper) PluginManager {
|
||||||
|
return PluginManager{
|
||||||
|
PluginNameMapper: m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMigrationCompleteForPlugin indicates whether CSI migration has been completed
|
||||||
|
// for a particular storage plugin
|
||||||
|
func (pm PluginManager) IsMigrationCompleteForPlugin(pluginName string) bool {
|
||||||
|
// CSIMigration feature and plugin specific migration feature flags should
|
||||||
|
// be enabled for plugin specific migration completion feature flags to be
|
||||||
|
// take effect
|
||||||
|
if !pm.IsMigrationEnabledForPlugin(pluginName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pluginName {
|
||||||
|
case csilibplugins.AWSEBSInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWSComplete)
|
||||||
|
case csilibplugins.GCEPDInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCEComplete)
|
||||||
|
case csilibplugins.AzureFileInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFileComplete)
|
||||||
|
case csilibplugins.AzureDiskInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDiskComplete)
|
||||||
|
case csilibplugins.CinderInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStackComplete)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMigrationEnabledForPlugin indicates whether CSI migration has been enabled
|
||||||
|
// for a particular storage plugin
|
||||||
|
func (pm PluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
|
||||||
|
// CSIMigration feature should be enabled along with the plugin-specific one
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pluginName {
|
||||||
|
case csilibplugins.AWSEBSInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWS)
|
||||||
|
case csilibplugins.GCEPDInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCE)
|
||||||
|
case csilibplugins.AzureFileInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
|
||||||
|
case csilibplugins.AzureDiskInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk)
|
||||||
|
case csilibplugins.CinderInTreePluginName:
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMigratable indicates whether CSI migration has been enabled for a volume
|
||||||
|
// plugin that the spec refers to
|
||||||
|
func (pm PluginManager) IsMigratable(spec *volume.Spec) (bool, error) {
|
||||||
|
if spec == nil {
|
||||||
|
return false, fmt.Errorf("could not find if plugin is migratable because volume spec is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginName, _ := pm.GetInTreePluginNameFromSpec(spec.PersistentVolume, spec.Volume)
|
||||||
|
if pluginName == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// found an in-tree plugin that supports the spec
|
||||||
|
return pm.IsMigrationEnabledForPlugin(pluginName), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InTreeToCSITranslator performs translation of Volume sources for PV and Volume objects
|
||||||
|
// from references to in-tree plugins to migrated CSI plugins
|
||||||
|
type InTreeToCSITranslator interface {
|
||||||
|
TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error)
|
||||||
|
TranslateInTreeInlineVolumeToCSI(volume *v1.Volume) (*v1.PersistentVolume, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslateInTreeSpecToCSI translates a volume spec (either PV or inline volume)
|
||||||
|
// supported by an in-tree plugin to CSI
|
||||||
|
func TranslateInTreeSpecToCSI(spec *volume.Spec, translator InTreeToCSITranslator) (*volume.Spec, error) {
|
||||||
|
var csiPV *v1.PersistentVolume
|
||||||
|
var err error
|
||||||
|
inlineVolume := false
|
||||||
|
if spec.PersistentVolume != nil {
|
||||||
|
csiPV, err = translator.TranslateInTreePVToCSI(spec.PersistentVolume)
|
||||||
|
} else if spec.Volume != nil {
|
||||||
|
csiPV, err = translator.TranslateInTreeInlineVolumeToCSI(spec.Volume)
|
||||||
|
inlineVolume = true
|
||||||
|
} else {
|
||||||
|
err = errors.New("not a valid volume spec")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to translate in-tree pv to CSI: %v", err)
|
||||||
|
}
|
||||||
|
return &volume.Spec{
|
||||||
|
PersistentVolume: csiPV,
|
||||||
|
ReadOnly: spec.ReadOnly,
|
||||||
|
InlineVolumeSpecForCSIMigration: inlineVolume,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMigrationFeatureFlags checks the configuration of feature flags related
|
||||||
|
// to CSI Migration is valid
|
||||||
|
func CheckMigrationFeatureFlags(f featuregate.FeatureGate, pluginMigration, pluginMigrationComplete featuregate.Feature) error {
|
||||||
|
if f.Enabled(pluginMigration) && !f.Enabled(features.CSIMigration) {
|
||||||
|
return fmt.Errorf("enabling %q requires CSIMigration to be enabled", pluginMigration)
|
||||||
|
}
|
||||||
|
if f.Enabled(pluginMigrationComplete) && !f.Enabled(pluginMigration) {
|
||||||
|
return fmt.Errorf("enabling %q requires %q to be enabled", pluginMigrationComplete, pluginMigration)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
325
pkg/volume/csimigration/plugin_manager_test.go
Normal file
325
pkg/volume/csimigration/plugin_manager_test.go
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 csimigration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
csitrans "k8s.io/csi-translation-lib"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsMigratable(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
pluginFeature featuregate.Feature
|
||||||
|
pluginFeatureEnabled bool
|
||||||
|
csiMigrationEnabled bool
|
||||||
|
isMigratable bool
|
||||||
|
spec *volume.Spec
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "GCE PD PV source with CSIMigrationGCE enabled",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
isMigratable: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
spec: &volume.Spec{
|
||||||
|
PersistentVolume: &v1.PersistentVolume{
|
||||||
|
Spec: v1.PersistentVolumeSpec{
|
||||||
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
|
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||||
|
PDName: "test-disk",
|
||||||
|
FSType: "ext4",
|
||||||
|
Partition: 0,
|
||||||
|
ReadOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GCE PD PV Source with CSIMigrationGCE disabled",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
isMigratable: false,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
spec: &volume.Spec{
|
||||||
|
PersistentVolume: &v1.PersistentVolume{
|
||||||
|
Spec: v1.PersistentVolumeSpec{
|
||||||
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
|
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||||
|
PDName: "test-disk",
|
||||||
|
FSType: "ext4",
|
||||||
|
Partition: 0,
|
||||||
|
ReadOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AWS EBS PV with CSIMigrationAWS enabled",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
isMigratable: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
spec: &volume.Spec{
|
||||||
|
PersistentVolume: &v1.PersistentVolume{
|
||||||
|
Spec: v1.PersistentVolumeSpec{
|
||||||
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
|
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
|
||||||
|
VolumeID: "vol01",
|
||||||
|
FSType: "ext3",
|
||||||
|
Partition: 1,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AWS EBS PV with CSIMigration and CSIMigrationAWS disabled",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
isMigratable: false,
|
||||||
|
csiMigrationEnabled: false,
|
||||||
|
spec: &volume.Spec{
|
||||||
|
PersistentVolume: &v1.PersistentVolume{
|
||||||
|
Spec: v1.PersistentVolumeSpec{
|
||||||
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
|
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
|
||||||
|
VolumeID: "vol01",
|
||||||
|
FSType: "ext3",
|
||||||
|
Partition: 1,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
|
for _, test := range testCases {
|
||||||
|
pm := NewPluginManager(csiTranslator)
|
||||||
|
t.Run(fmt.Sprintf("Testing %v", test.name), func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, test.csiMigrationEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.pluginFeature, test.pluginFeatureEnabled)()
|
||||||
|
migratable, err := pm.IsMigratable(test.spec)
|
||||||
|
if migratable != test.isMigratable {
|
||||||
|
t.Errorf("Expected migratability of spec: %v does not match obtained migratability: %v", test.isMigratable, migratable)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckMigrationFeatureFlags(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
pluginFeature featuregate.Feature
|
||||||
|
pluginFeatureEnabled bool
|
||||||
|
csiMigrationEnabled bool
|
||||||
|
pluginFeatureComplete featuregate.Feature
|
||||||
|
pluginFeatureCompleteEnabled bool
|
||||||
|
result bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plugin specific feature flag enabled with migration flag disabled",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: false,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
result: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "plugin specific complete flag enabled but plugin specific feature flag disabled",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: true,
|
||||||
|
result: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "plugin specific complete feature disabled but plugin specific migration feature and CSI migration flag enabled",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all features enabled",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: true,
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(fmt.Sprintf("Testing %v", test.name), func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, test.csiMigrationEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.pluginFeature, test.pluginFeatureEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.pluginFeatureComplete, test.pluginFeatureCompleteEnabled)()
|
||||||
|
err := CheckMigrationFeatureFlags(utilfeature.DefaultFeatureGate, test.pluginFeature, test.pluginFeatureComplete)
|
||||||
|
if err != nil && test.result == true {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err == nil && test.result == false {
|
||||||
|
t.Errorf("Unexpected validation pass")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigrationFeatureFlagStatus(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
pluginName string
|
||||||
|
csiMigrationEnabled bool
|
||||||
|
pluginFeature featuregate.Feature
|
||||||
|
pluginFeatureEnabled bool
|
||||||
|
pluginFeatureComplete featuregate.Feature
|
||||||
|
pluginFeatureCompleteEnabled bool
|
||||||
|
csiMigrationResult bool
|
||||||
|
csiMigrationCompleteResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "gce-pd migration flag disabled and migration-complete flag disabled with CSI migration flag disabled",
|
||||||
|
pluginName: "kubernetes.io/gce-pd",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
csiMigrationEnabled: false,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: false,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gce-pd migration flag disabled and migration-complete flag disabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/gce-pd",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: false,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gce-pd migration flag enabled and migration-complete flag disabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/gce-pd",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: true,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gce-pd migration flag enabled and migration-complete flag enabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/gce-pd",
|
||||||
|
pluginFeature: features.CSIMigrationGCE,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationGCEComplete,
|
||||||
|
pluginFeatureCompleteEnabled: true,
|
||||||
|
csiMigrationResult: true,
|
||||||
|
csiMigrationCompleteResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws-ebs migration flag disabled and migration-complete flag disabled with CSI migration flag disabled",
|
||||||
|
pluginName: "kubernetes.io/aws-ebs",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
csiMigrationEnabled: false,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: false,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws-ebs migration flag disabled and migration-complete flag disabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/aws-ebs",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: false,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: false,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws-ebs migration flag enabled and migration-complete flag disabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/aws-ebs",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: false,
|
||||||
|
csiMigrationResult: true,
|
||||||
|
csiMigrationCompleteResult: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aws-ebs migration flag enabled and migration-complete flag enabled with CSI migration flag enabled",
|
||||||
|
pluginName: "kubernetes.io/aws-ebs",
|
||||||
|
pluginFeature: features.CSIMigrationAWS,
|
||||||
|
pluginFeatureEnabled: true,
|
||||||
|
csiMigrationEnabled: true,
|
||||||
|
pluginFeatureComplete: features.CSIMigrationAWSComplete,
|
||||||
|
pluginFeatureCompleteEnabled: true,
|
||||||
|
csiMigrationResult: true,
|
||||||
|
csiMigrationCompleteResult: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
csiTranslator := csitrans.New()
|
||||||
|
for _, test := range testCases {
|
||||||
|
pm := NewPluginManager(csiTranslator)
|
||||||
|
t.Run(fmt.Sprintf("Testing %v", test.name), func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, test.csiMigrationEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.pluginFeature, test.pluginFeatureEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.pluginFeatureComplete, test.pluginFeatureCompleteEnabled)()
|
||||||
|
|
||||||
|
csiMigrationResult := pm.IsMigrationEnabledForPlugin(test.pluginName)
|
||||||
|
if csiMigrationResult != test.csiMigrationResult {
|
||||||
|
t.Errorf("Expected migratability of plugin %v: %v does not match obtained migratability: %v", test.pluginName, test.csiMigrationResult, csiMigrationResult)
|
||||||
|
}
|
||||||
|
csiMigrationCompleteResult := pm.IsMigrationCompleteForPlugin(test.pluginName)
|
||||||
|
if csiMigrationCompleteResult != test.csiMigrationCompleteResult {
|
||||||
|
t.Errorf("Expected migration complete status of plugin: %v: %v does not match obtained migratability: %v", test.pluginName, test.csiMigrationCompleteResult, csiMigrationResult)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -80,10 +80,6 @@ func (plugin *downwardAPIPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.Volume != nil && spec.Volume.DownwardAPI != nil
|
return spec.Volume != nil && spec.Volume.DownwardAPI != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *downwardAPIPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *downwardAPIPlugin) RequiresRemount() bool {
|
func (plugin *downwardAPIPlugin) RequiresRemount() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -90,10 +90,6 @@ func (plugin *emptyDirPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *emptyDirPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *emptyDirPlugin) RequiresRemount() bool {
|
func (plugin *emptyDirPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -88,10 +88,6 @@ func (plugin *fcPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.Volume != nil && spec.Volume.FC != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.FC != nil)
|
return (spec.Volume != nil && spec.Volume.FC != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.FC != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *fcPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *fcPlugin) RequiresRemount() bool {
|
func (plugin *fcPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -148,10 +148,6 @@ func (plugin *flexVolumePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return sourceDriver == plugin.driverName
|
return sourceDriver == plugin.driverName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *flexVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequiresRemount is part of the volume.VolumePlugin interface.
|
// RequiresRemount is part of the volume.VolumePlugin interface.
|
||||||
func (plugin *flexVolumePlugin) RequiresRemount() bool {
|
func (plugin *flexVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -107,10 +107,6 @@ func (p *flockerPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.Flocker != nil)
|
(spec.Volume != nil && spec.Volume.Flocker != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *flockerPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *flockerPlugin) RequiresRemount() bool {
|
func (p *flockerPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -100,11 +100,6 @@ func (plugin *gcePersistentDiskPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.GCEPersistentDisk != nil)
|
(spec.Volume != nil && spec.Volume.GCEPersistentDisk != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *gcePersistentDiskPlugin) IsMigratedToCSI() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
|
|
||||||
utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *gcePersistentDiskPlugin) RequiresRemount() bool {
|
func (plugin *gcePersistentDiskPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,6 @@ func (plugin *gitRepoPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.Volume != nil && spec.Volume.GitRepo != nil
|
return spec.Volume != nil && spec.Volume.GitRepo != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *gitRepoPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *gitRepoPlugin) RequiresRemount() bool {
|
func (plugin *gitRepoPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,6 @@ func (plugin *glusterfsPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.Glusterfs != nil)
|
(spec.Volume != nil && spec.Volume.Glusterfs != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *glusterfsPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *glusterfsPlugin) RequiresRemount() bool {
|
func (plugin *glusterfsPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -84,10 +84,6 @@ func (plugin *hostPathPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.HostPath != nil)
|
(spec.Volume != nil && spec.Volume.HostPath != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *hostPathPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *hostPathPlugin) RequiresRemount() bool {
|
func (plugin *hostPathPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,6 @@ func (plugin *iscsiPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.Volume != nil && spec.Volume.ISCSI != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.ISCSI != nil)
|
return (spec.Volume != nil && spec.Volume.ISCSI != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.ISCSI != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *iscsiPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *iscsiPlugin) RequiresRemount() bool {
|
func (plugin *iscsiPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,6 @@ func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
|
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *localVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *localVolumePlugin) RequiresRemount() bool {
|
func (plugin *localVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -89,10 +89,6 @@ func (plugin *nfsPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.NFS != nil)
|
(spec.Volume != nil && spec.Volume.NFS != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *nfsPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *nfsPlugin) RequiresRemount() bool {
|
func (plugin *nfsPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,6 @@ func (n *noopExpandableVolumePluginInstance) CanSupport(spec *Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopExpandableVolumePluginInstance) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopExpandableVolumePluginInstance) RequiresRemount() bool {
|
func (n *noopExpandableVolumePluginInstance) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -159,10 +159,6 @@ type VolumePlugin interface {
|
|||||||
// const.
|
// const.
|
||||||
CanSupport(spec *Spec) bool
|
CanSupport(spec *Spec) bool
|
||||||
|
|
||||||
// IsMigratedToCSI tests whether a CSIDriver implements this plugin's
|
|
||||||
// functionality
|
|
||||||
IsMigratedToCSI() bool
|
|
||||||
|
|
||||||
// RequiresRemount returns true if this plugin requires mount calls to be
|
// RequiresRemount returns true if this plugin requires mount calls to be
|
||||||
// reexecuted. Atomically updating volumes, like Downward API, depend on
|
// reexecuted. Atomically updating volumes, like Downward API, depend on
|
||||||
// this to update the contents of the volume.
|
// this to update the contents of the volume.
|
||||||
@ -693,39 +689,6 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
|
|||||||
return matches[0], nil
|
return matches[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPluginMigratableBySpec looks for a plugin that can support a given volume
|
|
||||||
// specification and whether that plugin is Migratable. If no plugins can
|
|
||||||
// support or more than one plugin can support it, return error.
|
|
||||||
func (pm *VolumePluginMgr) IsPluginMigratableBySpec(spec *Spec) (bool, error) {
|
|
||||||
pm.mutex.Lock()
|
|
||||||
defer pm.mutex.Unlock()
|
|
||||||
|
|
||||||
if spec == nil {
|
|
||||||
return false, fmt.Errorf("could not find if plugin is migratable because volume spec is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
matches := []VolumePlugin{}
|
|
||||||
for _, v := range pm.plugins {
|
|
||||||
if v.CanSupport(spec) {
|
|
||||||
matches = append(matches, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(matches) == 0 {
|
|
||||||
// Not a known plugin (flex) in which case it is not migratable
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if len(matches) > 1 {
|
|
||||||
matchedPluginNames := []string{}
|
|
||||||
for _, plugin := range matches {
|
|
||||||
matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches[0].IsMigratedToCSI(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindPluginByName fetches a plugin by name or by legacy name. If no plugin
|
// FindPluginByName fetches a plugin by name or by legacy name. If no plugin
|
||||||
// is found, returns error.
|
// is found, returns error.
|
||||||
func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
|
func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
|
||||||
|
@ -75,10 +75,6 @@ func (plugin *testPlugins) CanSupport(spec *Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *testPlugins) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *testPlugins) RequiresRemount() bool {
|
func (plugin *testPlugins) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -95,10 +95,6 @@ func (plugin *portworxVolumePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.PortworxVolume != nil)
|
(spec.Volume != nil && spec.Volume.PortworxVolume != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *portworxVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *portworxVolumePlugin) RequiresRemount() bool {
|
func (plugin *portworxVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -95,10 +95,6 @@ func (plugin *projectedPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.Volume != nil && spec.Volume.Projected != nil
|
return spec.Volume != nil && spec.Volume.Projected != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *projectedPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *projectedPlugin) RequiresRemount() bool {
|
func (plugin *projectedPlugin) RequiresRemount() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -110,10 +110,6 @@ func (plugin *quobytePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *quobytePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *quobytePlugin) RequiresRemount() bool {
|
func (plugin *quobytePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -108,10 +108,6 @@ func (plugin *rbdPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return (spec.Volume != nil && spec.Volume.RBD != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil)
|
return (spec.Volume != nil && spec.Volume.RBD != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *rbdPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *rbdPlugin) RequiresRemount() bool {
|
func (plugin *rbdPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -72,10 +72,6 @@ func (p *sioPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.ScaleIO != nil)
|
(spec.Volume != nil && spec.Volume.ScaleIO != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *sioPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *sioPlugin) RequiresRemount() bool {
|
func (p *sioPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -80,10 +80,6 @@ func (plugin *secretPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
return spec.Volume != nil && spec.Volume.Secret != nil
|
return spec.Volume != nil && spec.Volume.Secret != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *secretPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *secretPlugin) RequiresRemount() bool {
|
func (plugin *secretPlugin) RequiresRemount() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -91,10 +91,6 @@ func (plugin *storageosPlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.StorageOS != nil)
|
(spec.Volume != nil && spec.Volume.StorageOS != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *storageosPlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *storageosPlugin) RequiresRemount() bool {
|
func (plugin *storageosPlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -411,10 +411,6 @@ func (plugin *FakeVolumePlugin) CanSupport(spec *Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *FakeVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *FakeVolumePlugin) RequiresRemount() bool {
|
func (plugin *FakeVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -653,10 +649,6 @@ func (f *FakeBasicVolumePlugin) CanSupport(spec *Spec) bool {
|
|||||||
return strings.HasPrefix(spec.Name(), f.GetPluginName())
|
return strings.HasPrefix(spec.Name(), f.GetPluginName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *FakeBasicVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeBasicVolumePlugin) ConstructVolumeSpec(ame, mountPath string) (*Spec, error) {
|
func (f *FakeBasicVolumePlugin) ConstructVolumeSpec(ame, mountPath string) (*Spec, error) {
|
||||||
return f.Plugin.ConstructVolumeSpec(ame, mountPath)
|
return f.Plugin.ConstructVolumeSpec(ame, mountPath)
|
||||||
}
|
}
|
||||||
@ -750,10 +742,6 @@ func (plugin *FakeFileVolumePlugin) CanSupport(spec *Spec) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *FakeFileVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *FakeFileVolumePlugin) RequiresRemount() bool {
|
func (plugin *FakeFileVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ go_library(
|
|||||||
"//pkg/kubelet/events:go_default_library",
|
"//pkg/kubelet/events:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/csi:go_default_library",
|
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/hostutil:go_default_library",
|
"//pkg/volume/util/hostutil:go_default_library",
|
||||||
"//pkg/volume/util/nestedpendingoperations:go_default_library",
|
"//pkg/volume/util/nestedpendingoperations:go_default_library",
|
||||||
@ -30,7 +29,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_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/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
@ -48,10 +46,8 @@ go_test(
|
|||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/features:go_default_library",
|
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/awsebs:go_default_library",
|
"//pkg/volume/awsebs:go_default_library",
|
||||||
"//pkg/volume/csi:go_default_library",
|
|
||||||
"//pkg/volume/csi/testing:go_default_library",
|
"//pkg/volume/csi/testing:go_default_library",
|
||||||
"//pkg/volume/gcepd:go_default_library",
|
"//pkg/volume/gcepd:go_default_library",
|
||||||
"//pkg/volume/testing:go_default_library",
|
"//pkg/volume/testing:go_default_library",
|
||||||
@ -61,11 +57,8 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
|
|
||||||
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
|
||||||
"//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library",
|
"//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-translation-lib/plugins:go_default_library",
|
"//staging/src/k8s.io/csi-translation-lib/plugins:go_default_library",
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/csi"
|
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/nestedpendingoperations"
|
"k8s.io/kubernetes/pkg/volume/util/nestedpendingoperations"
|
||||||
@ -640,45 +639,25 @@ func (oe *operationExecutor) VerifyVolumesAreAttached(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migration: Must also check the Node since Attach would have been done with in-tree if node is not using Migration
|
volumePlugin, err :=
|
||||||
nu, err := nodeUsingCSIPlugin(oe.operationGenerator.GetCSITranslator(), oe.operationGenerator.GetVolumePluginMgr(), volumeAttached.VolumeSpec, node)
|
oe.operationGenerator.GetVolumePluginMgr().FindPluginBySpec(volumeAttached.VolumeSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.NodeUsingCSIPlugin failed", err).Error())
|
klog.Errorf(
|
||||||
|
"VolumesAreAttached.FindPluginBySpec failed for volume %q (spec.Name: %q) on node %q with error: %v",
|
||||||
|
volumeAttached.VolumeName,
|
||||||
|
volumeAttached.VolumeSpec.Name(),
|
||||||
|
volumeAttached.NodeName,
|
||||||
|
err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if volumePlugin == nil {
|
||||||
var volumePlugin volume.VolumePlugin
|
// should never happen since FindPluginBySpec always returns error if volumePlugin = nil
|
||||||
if useCSIPlugin(oe.operationGenerator.GetCSITranslator(), oe.operationGenerator.GetVolumePluginMgr(), volumeAttached.VolumeSpec) && nu {
|
klog.Errorf(
|
||||||
// The volume represented by this spec is CSI and thus should be migrated
|
"Failed to find volume plugin for volume %q (spec.Name: %q) on node %q",
|
||||||
volumePlugin, err = oe.operationGenerator.GetVolumePluginMgr().FindPluginByName(csi.CSIPluginName)
|
volumeAttached.VolumeName,
|
||||||
if err != nil || volumePlugin == nil {
|
volumeAttached.VolumeSpec.Name(),
|
||||||
klog.Errorf(
|
volumeAttached.NodeName)
|
||||||
"VolumesAreAttached.Name failed for volume %q (spec.Name: %q) on node %q with error: %v",
|
continue
|
||||||
volumeAttached.VolumeName,
|
|
||||||
volumeAttached.VolumeSpec.Name(),
|
|
||||||
volumeAttached.NodeName,
|
|
||||||
err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
csiSpec, err := translateSpec(oe.operationGenerator.GetCSITranslator(), volumeAttached.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.TranslateSpec failed", err).Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
volumeAttached.VolumeSpec = csiSpec
|
|
||||||
} else {
|
|
||||||
volumePlugin, err =
|
|
||||||
oe.operationGenerator.GetVolumePluginMgr().FindPluginBySpec(volumeAttached.VolumeSpec)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
klog.Errorf(
|
|
||||||
"VolumesAreAttached.FindPluginBySpec failed for volume %q (spec.Name: %q) on node %q with error: %v",
|
|
||||||
volumeAttached.VolumeName,
|
|
||||||
volumeAttached.VolumeSpec.Name(),
|
|
||||||
volumeAttached.NodeName,
|
|
||||||
err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginName := volumePlugin.GetPluginName()
|
pluginName := volumePlugin.GetPluginName()
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
@ -37,7 +36,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/csi"
|
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
ioutil "k8s.io/kubernetes/pkg/volume/util"
|
ioutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
||||||
@ -170,38 +168,12 @@ func (og *operationGenerator) GenerateVolumesAreAttachedFunc(
|
|||||||
klog.Errorf("VerifyVolumesAreAttached.GenerateVolumesAreAttachedFunc: nil spec for volume %s", volumeAttached.VolumeName)
|
klog.Errorf("VerifyVolumesAreAttached.GenerateVolumesAreAttachedFunc: nil spec for volume %s", volumeAttached.VolumeName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
volumePlugin, err :=
|
||||||
// Migration: Must also check the Node since Attach would have been done with in-tree if node is not using Migration
|
og.volumePluginMgr.FindPluginBySpec(volumeAttached.VolumeSpec)
|
||||||
nu, err := nodeUsingCSIPlugin(og.translator, og.volumePluginMgr, volumeAttached.VolumeSpec, nodeName)
|
if err != nil || volumePlugin == nil {
|
||||||
if err != nil {
|
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.FindPluginBySpec failed", err).Error())
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.NodeUsingCSIPlugin failed", err).Error())
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var volumePlugin volume.VolumePlugin
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeAttached.VolumeSpec) && nu {
|
|
||||||
// The volume represented by this spec is CSI and thus should be migrated
|
|
||||||
volumePlugin, err = og.volumePluginMgr.FindPluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.FindPluginByName failed", err).Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeAttached.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.TranslateSpec failed", err).Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
volumeAttached.VolumeSpec = csiSpec
|
|
||||||
} else {
|
|
||||||
volumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindPluginBySpec(volumeAttached.VolumeSpec)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.FindPluginBySpec failed", err).Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeSpecList, pluginExists := volumesPerPlugin[volumePlugin.GetPluginName()]
|
volumeSpecList, pluginExists := volumesPerPlugin[volumePlugin.GetPluginName()]
|
||||||
if !pluginExists {
|
if !pluginExists {
|
||||||
volumeSpecList = []*volume.Spec{}
|
volumeSpecList = []*volume.Spec{}
|
||||||
@ -345,34 +317,12 @@ func (og *operationGenerator) GenerateBulkVolumeVerifyFunc(
|
|||||||
func (og *operationGenerator) GenerateAttachVolumeFunc(
|
func (og *operationGenerator) GenerateAttachVolumeFunc(
|
||||||
volumeToAttach VolumeToAttach,
|
volumeToAttach VolumeToAttach,
|
||||||
actualStateOfWorld ActualStateOfWorldAttacherUpdater) volumetypes.GeneratedOperations {
|
actualStateOfWorld ActualStateOfWorldAttacherUpdater) volumetypes.GeneratedOperations {
|
||||||
originalSpec := volumeToAttach.VolumeSpec
|
|
||||||
attachVolumeFunc := func() (error, error) {
|
attachVolumeFunc := func() (error, error) {
|
||||||
var attachableVolumePlugin volume.AttachableVolumePlugin
|
attachableVolumePlugin, err :=
|
||||||
|
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec)
|
||||||
nu, err := nodeUsingCSIPlugin(og.translator, og.volumePluginMgr, volumeToAttach.VolumeSpec, volumeToAttach.NodeName)
|
if err != nil || attachableVolumePlugin == nil {
|
||||||
if err != nil {
|
return volumeToAttach.GenerateError("AttachVolume.FindAttachablePluginBySpec failed", err)
|
||||||
return volumeToAttach.GenerateError("AttachVolume.NodeUsingCSIPlugin failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// useCSIPlugin will check both CSIMigration and the plugin specific feature gates
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToAttach.VolumeSpec) && nu {
|
|
||||||
// The volume represented by this spec is CSI and thus should be migrated
|
|
||||||
attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumeToAttach.GenerateError("AttachVolume.FindAttachablePluginByName failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToAttach.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumeToAttach.GenerateError("AttachVolume.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
volumeToAttach.VolumeSpec = csiSpec
|
|
||||||
} else {
|
|
||||||
attachableVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumeToAttach.GenerateError("AttachVolume.FindAttachablePluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeAttacher, newAttacherErr := attachableVolumePlugin.NewAttacher()
|
volumeAttacher, newAttacherErr := attachableVolumePlugin.NewAttacher()
|
||||||
@ -391,7 +341,7 @@ func (og *operationGenerator) GenerateAttachVolumeFunc(
|
|||||||
}
|
}
|
||||||
addErr := actualStateOfWorld.MarkVolumeAsUncertain(
|
addErr := actualStateOfWorld.MarkVolumeAsUncertain(
|
||||||
v1.UniqueVolumeName(""),
|
v1.UniqueVolumeName(""),
|
||||||
originalSpec,
|
volumeToAttach.VolumeSpec,
|
||||||
uncertainNode)
|
uncertainNode)
|
||||||
if addErr != nil {
|
if addErr != nil {
|
||||||
klog.Errorf("AttachVolume.MarkVolumeAsUncertain fail to add the volume %q to actual state with %s", volumeToAttach.VolumeName, addErr)
|
klog.Errorf("AttachVolume.MarkVolumeAsUncertain fail to add the volume %q to actual state with %s", volumeToAttach.VolumeName, addErr)
|
||||||
@ -410,7 +360,7 @@ func (og *operationGenerator) GenerateAttachVolumeFunc(
|
|||||||
|
|
||||||
// Update actual state of world
|
// Update actual state of world
|
||||||
addVolumeNodeErr := actualStateOfWorld.MarkVolumeAsAttached(
|
addVolumeNodeErr := actualStateOfWorld.MarkVolumeAsAttached(
|
||||||
v1.UniqueVolumeName(""), originalSpec, volumeToAttach.NodeName, devicePath)
|
v1.UniqueVolumeName(""), volumeToAttach.VolumeSpec, volumeToAttach.NodeName, devicePath)
|
||||||
if addVolumeNodeErr != nil {
|
if addVolumeNodeErr != nil {
|
||||||
// On failure, return error. Caller will log and retry.
|
// On failure, return error. Caller will log and retry.
|
||||||
return volumeToAttach.GenerateError("AttachVolume.MarkVolumeAsAttached failed", addVolumeNodeErr)
|
return volumeToAttach.GenerateError("AttachVolume.MarkVolumeAsAttached failed", addVolumeNodeErr)
|
||||||
@ -428,30 +378,7 @@ func (og *operationGenerator) GenerateAttachVolumeFunc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachableVolumePluginName := unknownAttachableVolumePlugin
|
attachableVolumePluginName := unknownAttachableVolumePlugin
|
||||||
// TODO(dyzz) Ignoring this error means that if the plugin is migrated and
|
|
||||||
// any transient error is encountered (API unavailable, driver not installed)
|
|
||||||
// the operation will have it's metric registered with the in-tree plugin instead
|
|
||||||
// of the CSI Driver we migrated to. Fixing this requires a larger refactor that
|
|
||||||
// involves determining the plugin_name for the metric generating "CompleteFunc"
|
|
||||||
// during the actual "OperationFunc" and not during this generation function
|
|
||||||
|
|
||||||
nu, err := nodeUsingCSIPlugin(og.translator, og.volumePluginMgr, volumeToAttach.VolumeSpec, volumeToAttach.NodeName)
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("GenerateAttachVolumeFunc failed to check if node is using CSI Plugin, metric for this operation may be inaccurate: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to translate the spec here if the plugin is migrated so that the metrics
|
|
||||||
// emitted show the correct (migrated) plugin
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToAttach.VolumeSpec) && nu {
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToAttach.VolumeSpec)
|
|
||||||
if err == nil {
|
|
||||||
volumeToAttach.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
// If we have an error here we ignore it, the metric emitted will then be for the
|
|
||||||
// in-tree plugin. This error case(skipped one) will also trigger an error
|
|
||||||
// while the generated function is executed. And those errors will be handled during the execution of the generated
|
|
||||||
// function with a back off policy.
|
|
||||||
}
|
|
||||||
// Get attacher plugin
|
// Get attacher plugin
|
||||||
attachableVolumePlugin, err :=
|
attachableVolumePlugin, err :=
|
||||||
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec)
|
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec)
|
||||||
@ -489,32 +416,10 @@ func (og *operationGenerator) GenerateDetachVolumeFunc(
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if volumeToDetach.VolumeSpec != nil {
|
if volumeToDetach.VolumeSpec != nil {
|
||||||
// Get attacher plugin
|
attachableVolumePlugin, err =
|
||||||
nu, err := nodeUsingCSIPlugin(og.translator, og.volumePluginMgr, volumeToDetach.VolumeSpec, volumeToDetach.NodeName)
|
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToDetach.VolumeSpec)
|
||||||
if err != nil {
|
if err != nil || attachableVolumePlugin == nil {
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.NodeUsingCSIPlugin failed", err)
|
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginBySpec failed", err)
|
||||||
}
|
|
||||||
|
|
||||||
// useCSIPlugin will check both CSIMigration and the plugin specific feature gate
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToDetach.VolumeSpec) && nu {
|
|
||||||
// The volume represented by this spec is CSI and thus should be migrated
|
|
||||||
attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToDetach.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeToDetach.VolumeSpec = csiSpec
|
|
||||||
} else {
|
|
||||||
attachableVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindAttachablePluginBySpec(volumeToDetach.VolumeSpec)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeName, err =
|
volumeName, err =
|
||||||
@ -531,25 +436,9 @@ func (og *operationGenerator) GenerateDetachVolumeFunc(
|
|||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.SplitUniqueName failed", err)
|
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.SplitUniqueName failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dyzz): This case can't distinguish between PV and In-line which is necessary because
|
attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(pluginName)
|
||||||
// if it was PV it may have been migrated, but the same plugin with in-line may not have been.
|
if err != nil || attachableVolumePlugin == nil {
|
||||||
// Suggestions welcome...
|
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginByName failed", err)
|
||||||
if og.translator.IsMigratableIntreePluginByName(pluginName) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
|
|
||||||
// The volume represented by this spec is CSI and thus should be migrated
|
|
||||||
attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("AttachVolume.FindAttachablePluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
// volumeToDetach.VolumeName here is always the in-tree volume name
|
|
||||||
// therefore a workaround is required. volumeToDetach.DevicePath
|
|
||||||
// is the attachID which happens to be what volumeName is needed for in Detach.
|
|
||||||
// Therefore we set volumeName to the attachID. And CSI Detach can detect and use that.
|
|
||||||
volumeName = volumeToDetach.DevicePath
|
|
||||||
} else {
|
|
||||||
attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(pluginName)
|
|
||||||
if err != nil || attachableVolumePlugin == nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginByName failed", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -600,21 +489,8 @@ func (og *operationGenerator) GenerateMountVolumeFunc(
|
|||||||
volumeToMount VolumeToMount,
|
volumeToMount VolumeToMount,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||||
isRemount bool) volumetypes.GeneratedOperations {
|
isRemount bool) volumetypes.GeneratedOperations {
|
||||||
// Get mounter plugin
|
|
||||||
originalSpec := volumeToMount.VolumeSpec
|
|
||||||
volumePluginName := unknownVolumePlugin
|
volumePluginName := unknownVolumePlugin
|
||||||
// Need to translate the spec here if the plugin is migrated so that the metrics
|
|
||||||
// emitted show the correct (migrated) plugin
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToMount.VolumeSpec) {
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToMount.VolumeSpec)
|
|
||||||
if err == nil {
|
|
||||||
volumeToMount.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
// If we have an error here we ignore it, the metric emitted will then be for the
|
|
||||||
// in-tree plugin. This error case(skipped one) will also trigger an error
|
|
||||||
// while the generated function is executed. And those errors will be handled during the execution of the generated
|
|
||||||
// function with a back off policy.
|
|
||||||
}
|
|
||||||
volumePlugin, err :=
|
volumePlugin, err :=
|
||||||
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
||||||
if err == nil && volumePlugin != nil {
|
if err == nil && volumePlugin != nil {
|
||||||
@ -622,16 +498,7 @@ func (og *operationGenerator) GenerateMountVolumeFunc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mountVolumeFunc := func() (error, error) {
|
mountVolumeFunc := func() (error, error) {
|
||||||
|
|
||||||
// Get mounter plugin
|
// Get mounter plugin
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToMount.VolumeSpec) {
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToMount.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumeToMount.GenerateError("MountVolume.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
volumeToMount.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
volumePlugin, err := og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
volumePlugin, err := og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
||||||
if err != nil || volumePlugin == nil {
|
if err != nil || volumePlugin == nil {
|
||||||
return volumeToMount.GenerateError("MountVolume.FindPluginBySpec failed", err)
|
return volumeToMount.GenerateError("MountVolume.FindPluginBySpec failed", err)
|
||||||
@ -789,7 +656,7 @@ func (og *operationGenerator) GenerateMountVolumeFunc(
|
|||||||
nil,
|
nil,
|
||||||
volumeToMount.OuterVolumeSpecName,
|
volumeToMount.OuterVolumeSpecName,
|
||||||
volumeToMount.VolumeGidValue,
|
volumeToMount.VolumeGidValue,
|
||||||
originalSpec)
|
volumeToMount.VolumeSpec)
|
||||||
if markVolMountedErr != nil {
|
if markVolMountedErr != nil {
|
||||||
// On failure, return error. Caller will log and retry.
|
// On failure, return error. Caller will log and retry.
|
||||||
return volumeToMount.GenerateError("MountVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
return volumeToMount.GenerateError("MountVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
||||||
@ -816,16 +683,8 @@ func (og *operationGenerator) GenerateUnmountVolumeFunc(
|
|||||||
volumeToUnmount MountedVolume,
|
volumeToUnmount MountedVolume,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||||
podsDir string) (volumetypes.GeneratedOperations, error) {
|
podsDir string) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
var pluginName string
|
|
||||||
if volumeToUnmount.VolumeSpec != nil && useCSIPlugin(og.translator, og.volumePluginMgr, volumeToUnmount.VolumeSpec) {
|
|
||||||
pluginName = csi.CSIPluginName
|
|
||||||
} else {
|
|
||||||
pluginName = volumeToUnmount.PluginName
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get mountable plugin
|
// Get mountable plugin
|
||||||
volumePlugin, err := og.volumePluginMgr.FindPluginByName(pluginName)
|
volumePlugin, err := og.volumePluginMgr.FindPluginByName(volumeToUnmount.PluginName)
|
||||||
if err != nil || volumePlugin == nil {
|
if err != nil || volumePlugin == nil {
|
||||||
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmountVolume.FindPluginByName failed", err)
|
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmountVolume.FindPluginByName failed", err)
|
||||||
}
|
}
|
||||||
@ -884,22 +743,9 @@ func (og *operationGenerator) GenerateUnmountDeviceFunc(
|
|||||||
deviceToDetach AttachedVolume,
|
deviceToDetach AttachedVolume,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||||
hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) {
|
hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
var pluginName string
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, deviceToDetach.VolumeSpec) {
|
|
||||||
pluginName = csi.CSIPluginName
|
|
||||||
csiSpec, err := translateSpec(og.translator, deviceToDetach.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
deviceToDetach.VolumeSpec = csiSpec
|
|
||||||
} else {
|
|
||||||
pluginName = deviceToDetach.PluginName
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get DeviceMounter plugin
|
// Get DeviceMounter plugin
|
||||||
deviceMountableVolumePlugin, err :=
|
deviceMountableVolumePlugin, err :=
|
||||||
og.volumePluginMgr.FindDeviceMountablePluginByName(pluginName)
|
og.volumePluginMgr.FindDeviceMountablePluginByName(deviceToDetach.PluginName)
|
||||||
if err != nil || deviceMountableVolumePlugin == nil {
|
if err != nil || deviceMountableVolumePlugin == nil {
|
||||||
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.FindDeviceMountablePluginByName failed", err)
|
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.FindDeviceMountablePluginByName failed", err)
|
||||||
}
|
}
|
||||||
@ -986,17 +832,6 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
|
|||||||
volumeToMount VolumeToMount,
|
volumeToMount VolumeToMount,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
originalSpec := volumeToMount.VolumeSpec
|
|
||||||
// Translate to CSI spec if migration enabled
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, originalSpec) {
|
|
||||||
csiSpec, err := translateSpec(og.translator, originalSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("MapVolume.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeToMount.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get block volume mapper plugin
|
// Get block volume mapper plugin
|
||||||
blockVolumePlugin, err :=
|
blockVolumePlugin, err :=
|
||||||
og.volumePluginMgr.FindMapperPluginBySpec(volumeToMount.VolumeSpec)
|
og.volumePluginMgr.FindMapperPluginBySpec(volumeToMount.VolumeSpec)
|
||||||
@ -1156,7 +991,7 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
|
|||||||
blockVolumeMapper,
|
blockVolumeMapper,
|
||||||
volumeToMount.OuterVolumeSpecName,
|
volumeToMount.OuterVolumeSpecName,
|
||||||
volumeToMount.VolumeGidValue,
|
volumeToMount.VolumeGidValue,
|
||||||
originalSpec)
|
volumeToMount.VolumeSpec)
|
||||||
if markVolMountedErr != nil {
|
if markVolMountedErr != nil {
|
||||||
// On failure, return error. Caller will log and retry.
|
// On failure, return error. Caller will log and retry.
|
||||||
return volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
return volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed", markVolMountedErr)
|
||||||
@ -1187,32 +1022,12 @@ func (og *operationGenerator) GenerateUnmapVolumeFunc(
|
|||||||
volumeToUnmount MountedVolume,
|
volumeToUnmount MountedVolume,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
var blockVolumePlugin volume.BlockVolumePlugin
|
// Get block volume unmapper plugin
|
||||||
var err error
|
blockVolumePlugin, err :=
|
||||||
// Translate to CSI spec if migration enabled
|
og.volumePluginMgr.FindMapperPluginByName(volumeToUnmount.PluginName)
|
||||||
// And get block volume unmapper plugin
|
if err != nil {
|
||||||
if volumeToUnmount.VolumeSpec != nil && useCSIPlugin(og.translator, og.volumePluginMgr, volumeToUnmount.VolumeSpec) {
|
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed", err)
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToUnmount.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeToUnmount.VolumeSpec = csiSpec
|
|
||||||
|
|
||||||
blockVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindMapperPluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindMapperPluginByName(volumeToUnmount.PluginName)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var blockVolumeUnmapper volume.BlockVolumeUnmapper
|
|
||||||
if blockVolumePlugin == nil {
|
if blockVolumePlugin == nil {
|
||||||
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil)
|
return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil)
|
||||||
}
|
}
|
||||||
@ -1289,27 +1104,10 @@ func (og *operationGenerator) GenerateUnmapDeviceFunc(
|
|||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
actualStateOfWorld ActualStateOfWorldMounterUpdater,
|
||||||
hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) {
|
hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
var blockVolumePlugin volume.BlockVolumePlugin
|
blockVolumePlugin, err :=
|
||||||
var err error
|
og.volumePluginMgr.FindMapperPluginByName(deviceToDetach.PluginName)
|
||||||
// Translate to CSI spec if migration enabled
|
if err != nil {
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, deviceToDetach.VolumeSpec) {
|
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.FindMapperPluginByName failed", err)
|
||||||
csiSpec, err := translateSpec(og.translator, deviceToDetach.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.TranslateSpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceToDetach.VolumeSpec = csiSpec
|
|
||||||
blockVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindMapperPluginByName(csi.CSIPluginName)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.FindMapperPluginByName failed", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockVolumePlugin, err =
|
|
||||||
og.volumePluginMgr.FindMapperPluginByName(deviceToDetach.PluginName)
|
|
||||||
if err != nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.FindMapperPluginByName failed", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockVolumePlugin == nil {
|
if blockVolumePlugin == nil {
|
||||||
@ -1586,22 +1384,13 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc(
|
|||||||
volumeToMount VolumeToMount,
|
volumeToMount VolumeToMount,
|
||||||
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
fsResizeFunc := func() (error, error) {
|
volumePlugin, err :=
|
||||||
// Need to translate the spec here if the plugin is migrated so that the metrics
|
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
||||||
// emitted show the correct (migrated) plugin
|
if err != nil || volumePlugin == nil {
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToMount.VolumeSpec) {
|
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("NodeExpandVolume.FindPluginBySpec failed", err)
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToMount.VolumeSpec)
|
}
|
||||||
if err != nil {
|
|
||||||
return volumeToMount.GenerateError("NodeExpandVolume.translateSpec failed", err)
|
|
||||||
}
|
|
||||||
volumeToMount.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
volumePlugin, err :=
|
|
||||||
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
return volumeToMount.GenerateError("NodeExpandVolume.FindPluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fsResizeFunc := func() (error, error) {
|
||||||
var resizeDone bool
|
var resizeDone bool
|
||||||
var simpleErr, detailedErr error
|
var simpleErr, detailedErr error
|
||||||
resizeOptions := volume.NodeResizeOptions{
|
resizeOptions := volume.NodeResizeOptions{
|
||||||
@ -1659,24 +1448,6 @@ func (og *operationGenerator) GenerateExpandInUseVolumeFunc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to translate the spec here if the plugin is migrated so that the metrics
|
|
||||||
// emitted show the correct (migrated) plugin
|
|
||||||
if useCSIPlugin(og.translator, og.volumePluginMgr, volumeToMount.VolumeSpec) {
|
|
||||||
csiSpec, err := translateSpec(og.translator, volumeToMount.VolumeSpec)
|
|
||||||
if err == nil {
|
|
||||||
volumeToMount.VolumeSpec = csiSpec
|
|
||||||
}
|
|
||||||
// If we have an error here we ignore it, the metric emitted will then be for the
|
|
||||||
// in-tree plugin. This error case(skipped one) will also trigger an error
|
|
||||||
// while the generated function is executed. And those errors will be handled during the execution of the generated
|
|
||||||
// function with a back off policy.
|
|
||||||
}
|
|
||||||
volumePlugin, err :=
|
|
||||||
og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("NodeExpandVolume.FindPluginBySpec failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return volumetypes.GeneratedOperations{
|
return volumetypes.GeneratedOperations{
|
||||||
OperationName: "volume_fs_resize",
|
OperationName: "volume_fs_resize",
|
||||||
OperationFunc: fsResizeFunc,
|
OperationFunc: fsResizeFunc,
|
||||||
@ -1822,134 +1593,3 @@ func isDeviceOpened(deviceToDetach AttachedVolume, hostUtil hostutil.HostUtils)
|
|||||||
}
|
}
|
||||||
return deviceOpened, nil
|
return deviceOpened, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func useCSIPlugin(tr InTreeToCSITranslator, vpm *volume.VolumePluginMgr, spec *volume.Spec) bool {
|
|
||||||
// TODO(#75146) Check whether the driver is installed as well so that
|
|
||||||
// we can throw a better error when the driver is not installed.
|
|
||||||
// The error should be of the approximate form:
|
|
||||||
// fmt.Errorf("in-tree plugin %s is migrated on node %s but driver %s is not installed", pluginName, string(nodeName), driverName)
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if tr.IsPVMigratable(spec.PersistentVolume) || tr.IsInlineMigratable(spec.Volume) {
|
|
||||||
migratable, err := vpm.IsPluginMigratableBySpec(spec)
|
|
||||||
if err == nil && migratable {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeUsingCSIPlugin(tr InTreeToCSITranslator, vpm *volume.VolumePluginMgr, spec *volume.Spec, nodeName types.NodeName) (bool, error) {
|
|
||||||
migratable, err := vpm.IsPluginMigratableBySpec(spec)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) ||
|
|
||||||
!utilfeature.DefaultFeatureGate.Enabled(features.CSINodeInfo) ||
|
|
||||||
!migratable {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nodeName) == 0 {
|
|
||||||
return false, goerrors.New("nodeName is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
kubeClient := vpm.Host.GetKubeClient()
|
|
||||||
if kubeClient == nil {
|
|
||||||
// Don't handle the controller/kubelet version skew check and fallback
|
|
||||||
// to just checking the feature gates. This can happen if
|
|
||||||
// we are in a standalone (headless) Kubelet
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
adcHost, ok := vpm.Host.(volume.AttachDetachVolumeHost)
|
|
||||||
if !ok {
|
|
||||||
// Don't handle the controller/kubelet version skew check and fallback
|
|
||||||
// to just checking the feature gates. This can happen if
|
|
||||||
// "enableControllerAttachDetach" is set to true on kubelet
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if adcHost.CSINodeLister() == nil {
|
|
||||||
return false, goerrors.New("could not find CSINodeLister in attachDetachController")
|
|
||||||
}
|
|
||||||
|
|
||||||
csiNode, err := adcHost.CSINodeLister().Get(string(nodeName))
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ann := csiNode.GetAnnotations()
|
|
||||||
if ann == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var mpaSet sets.String
|
|
||||||
mpa := ann[v1.MigratedPluginsAnnotationKey]
|
|
||||||
tok := strings.Split(mpa, ",")
|
|
||||||
if len(mpa) == 0 {
|
|
||||||
mpaSet = sets.NewString()
|
|
||||||
} else {
|
|
||||||
mpaSet = sets.NewString(tok...)
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginName, err := tr.GetInTreePluginNameFromSpec(spec.PersistentVolume, spec.Volume)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pluginName) == 0 {
|
|
||||||
// Could not find a plugin name from translation directory, assume not translated
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
isMigratedOnNode := mpaSet.Has(pluginName)
|
|
||||||
|
|
||||||
if isMigratedOnNode {
|
|
||||||
installed := false
|
|
||||||
driverName, err := tr.GetCSINameFromInTreeName(pluginName)
|
|
||||||
if err != nil {
|
|
||||||
return isMigratedOnNode, err
|
|
||||||
}
|
|
||||||
for _, driver := range csiNode.Spec.Drivers {
|
|
||||||
if driver.Name == driverName {
|
|
||||||
installed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !installed {
|
|
||||||
return true, fmt.Errorf("in-tree plugin %s is migrated on node %s but driver %s is not installed", pluginName, string(nodeName), driverName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isMigratedOnNode, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func translateSpec(tr InTreeToCSITranslator, spec *volume.Spec) (*volume.Spec, error) {
|
|
||||||
var csiPV *v1.PersistentVolume
|
|
||||||
var err error
|
|
||||||
inlineVolume := false
|
|
||||||
if spec.PersistentVolume != nil {
|
|
||||||
// TranslateInTreePVToCSI will create a new PV
|
|
||||||
csiPV, err = tr.TranslateInTreePVToCSI(spec.PersistentVolume)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to translate in tree pv to CSI: %v", err)
|
|
||||||
}
|
|
||||||
} else if spec.Volume != nil {
|
|
||||||
// TranslateInTreeInlineVolumeToCSI will create a new PV
|
|
||||||
csiPV, err = tr.TranslateInTreeInlineVolumeToCSI(spec.Volume)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to translate in tree inline volume to CSI: %v", err)
|
|
||||||
}
|
|
||||||
inlineVolume = true
|
|
||||||
} else {
|
|
||||||
return &volume.Spec{}, goerrors.New("not a valid volume spec")
|
|
||||||
}
|
|
||||||
return &volume.Spec{
|
|
||||||
PersistentVolume: csiPV,
|
|
||||||
ReadOnly: spec.ReadOnly,
|
|
||||||
InlineVolumeSpecForCSIMigration: inlineVolume,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
@ -17,24 +17,18 @@ limitations under the License.
|
|||||||
package operationexecutor
|
package operationexecutor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/prometheus/client_model/go"
|
"github.com/prometheus/client_model/go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
fakeclient "k8s.io/client-go/kubernetes/fake"
|
fakeclient "k8s.io/client-go/kubernetes/fake"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/component-base/featuregate"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
"k8s.io/csi-translation-lib/plugins"
|
"k8s.io/csi-translation-lib/plugins"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/awsebs"
|
"k8s.io/kubernetes/pkg/volume/awsebs"
|
||||||
"k8s.io/kubernetes/pkg/volume/csi"
|
|
||||||
csitesting "k8s.io/kubernetes/pkg/volume/csi/testing"
|
csitesting "k8s.io/kubernetes/pkg/volume/csi/testing"
|
||||||
"k8s.io/kubernetes/pkg/volume/gcepd"
|
"k8s.io/kubernetes/pkg/volume/gcepd"
|
||||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||||
@ -46,21 +40,16 @@ import (
|
|||||||
// generated func so there is no need to test the plugin name that's used inside generated function
|
// generated func so there is no need to test the plugin name that's used inside generated function
|
||||||
func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
|
func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
name string
|
name string
|
||||||
isCsiMigrationEnabled bool
|
pluginName string
|
||||||
pluginName string
|
pvSpec v1.PersistentVolumeSpec
|
||||||
csiDriverName string
|
probVolumePlugins []volume.VolumePlugin
|
||||||
csiMigrationFeature featuregate.Feature
|
|
||||||
pvSpec v1.PersistentVolumeSpec
|
|
||||||
probVolumePlugins []volume.VolumePlugin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testcases := []testcase{
|
testcases := []testcase{
|
||||||
{
|
{
|
||||||
name: "gce pd plugin: csi migration disabled",
|
name: "gce pd plugin: csi migration disabled",
|
||||||
isCsiMigrationEnabled: false,
|
pluginName: plugins.GCEPDInTreePluginName,
|
||||||
pluginName: plugins.GCEPDInTreePluginName,
|
|
||||||
csiMigrationFeature: features.CSIMigrationGCE,
|
|
||||||
pvSpec: v1.PersistentVolumeSpec{
|
pvSpec: v1.PersistentVolumeSpec{
|
||||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{},
|
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{},
|
||||||
@ -68,34 +57,8 @@ func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
|
|||||||
probVolumePlugins: gcepd.ProbeVolumePlugins(),
|
probVolumePlugins: gcepd.ProbeVolumePlugins(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gce pd plugin: csi migration enabled",
|
name: "aws ebs plugin: csi migration disabled",
|
||||||
isCsiMigrationEnabled: true,
|
pluginName: plugins.AWSEBSInTreePluginName,
|
||||||
pluginName: plugins.GCEPDInTreePluginName,
|
|
||||||
csiDriverName: plugins.GCEPDDriverName,
|
|
||||||
csiMigrationFeature: features.CSIMigrationGCE,
|
|
||||||
pvSpec: v1.PersistentVolumeSpec{
|
|
||||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
|
||||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{},
|
|
||||||
}},
|
|
||||||
probVolumePlugins: gcepd.ProbeVolumePlugins(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "aws ebs plugin: csi migration disabled",
|
|
||||||
isCsiMigrationEnabled: false,
|
|
||||||
pluginName: plugins.AWSEBSInTreePluginName,
|
|
||||||
csiMigrationFeature: features.CSIMigrationAWS,
|
|
||||||
pvSpec: v1.PersistentVolumeSpec{
|
|
||||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
|
||||||
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{},
|
|
||||||
}},
|
|
||||||
probVolumePlugins: awsebs.ProbeVolumePlugins(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "aws ebs plugin: csi migration enabled",
|
|
||||||
isCsiMigrationEnabled: true,
|
|
||||||
pluginName: plugins.AWSEBSInTreePluginName,
|
|
||||||
csiDriverName: plugins.AWSEBSDriverName,
|
|
||||||
csiMigrationFeature: features.CSIMigrationAWS,
|
|
||||||
pvSpec: v1.PersistentVolumeSpec{
|
pvSpec: v1.PersistentVolumeSpec{
|
||||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||||
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{},
|
AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{},
|
||||||
@ -106,13 +69,7 @@ func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
expectedPluginName := tc.pluginName
|
expectedPluginName := tc.pluginName
|
||||||
if tc.isCsiMigrationEnabled {
|
volumePluginMgr, tmpDir := initTestPlugins(t, tc.probVolumePlugins, tc.pluginName)
|
||||||
expectedPluginName = fmt.Sprintf("%s:%s", csi.CSIPluginName, tc.csiDriverName)
|
|
||||||
}
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, tc.isCsiMigrationEnabled)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, tc.csiMigrationFeature, tc.isCsiMigrationEnabled)()
|
|
||||||
|
|
||||||
volumePluginMgr, plugin, tmpDir := initTestPlugins(t, tc.probVolumePlugins, tc.pluginName)
|
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
operationGenerator := getTestOperationGenerator(volumePluginMgr)
|
operationGenerator := getTestOperationGenerator(volumePluginMgr)
|
||||||
@ -120,21 +77,6 @@ func TestOperationGenerator_GenerateUnmapVolumeFunc_PluginName(t *testing.T) {
|
|||||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID(string(uuid.NewUUID()))}}
|
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID(string(uuid.NewUUID()))}}
|
||||||
volumeToUnmount := getTestVolumeToUnmount(pod, tc.pvSpec, tc.pluginName)
|
volumeToUnmount := getTestVolumeToUnmount(pod, tc.pvSpec, tc.pluginName)
|
||||||
|
|
||||||
if tc.isCsiMigrationEnabled {
|
|
||||||
// GenerateUnmapVolumeFunc call blockVolumePlugin.NewBlockVolumeUnmapper and when the plugin is csi,
|
|
||||||
// csi plugin looks a file that contains some information about the volume,
|
|
||||||
// and GenerateUnmapVolumeFuncfails if csi plugin can't find that file.
|
|
||||||
// So the reason for calling plugin.NewBlockVolumeMapper for csi enabled case is creating that file.
|
|
||||||
csiSpec, err := translateSpec(operationGenerator.GetCSITranslator(), volumeToUnmount.VolumeSpec)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Can't translate volume to CSI")
|
|
||||||
}
|
|
||||||
_, mapperError := (*plugin).(volume.BlockVolumePlugin).NewBlockVolumeMapper(csiSpec, pod, volume.VolumeOptions{})
|
|
||||||
if mapperError != nil {
|
|
||||||
t.Fatalf("mapper error: %v\n", mapperError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unmapVolumeFunc, e := operationGenerator.GenerateUnmapVolumeFunc(volumeToUnmount, nil)
|
unmapVolumeFunc, e := operationGenerator.GenerateUnmapVolumeFunc(volumeToUnmount, nil)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
t.Fatalf("Error occurred while generating unmapVolumeFunc: %v", e)
|
t.Fatalf("Error occurred while generating unmapVolumeFunc: %v", e)
|
||||||
@ -238,9 +180,9 @@ func getMetricFamily(metricFamilyName string) *io_prometheus_client.MetricFamily
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTestPlugins(t *testing.T, plugs []volume.VolumePlugin, pluginName string) (*volume.VolumePluginMgr, *volume.VolumePlugin, string) {
|
func initTestPlugins(t *testing.T, plugs []volume.VolumePlugin, pluginName string) (*volume.VolumePluginMgr, string) {
|
||||||
client := fakeclient.NewSimpleClientset()
|
client := fakeclient.NewSimpleClientset()
|
||||||
pluginMgr, csiPlugin, tmpDir := csitesting.NewTestPlugin(t, client)
|
pluginMgr, _, tmpDir := csitesting.NewTestPlugin(t, client)
|
||||||
|
|
||||||
err := pluginMgr.InitPlugins(plugs, nil, pluginMgr.Host)
|
err := pluginMgr.InitPlugins(plugs, nil, pluginMgr.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -252,5 +194,5 @@ func initTestPlugins(t *testing.T, plugs []volume.VolumePlugin, pluginName strin
|
|||||||
t.Fatalf("Can't find the plugin by name: %s", pluginName)
|
t.Fatalf("Can't find the plugin by name: %s", pluginName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pluginMgr, csiPlugin, tmpDir
|
return pluginMgr, tmpDir
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,6 @@ func (plugin *vsphereVolumePlugin) CanSupport(spec *volume.Spec) bool {
|
|||||||
(spec.Volume != nil && spec.Volume.VsphereVolume != nil)
|
(spec.Volume != nil && spec.Volume.VsphereVolume != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *vsphereVolumePlugin) IsMigratedToCSI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plugin *vsphereVolumePlugin) RequiresRemount() bool {
|
func (plugin *vsphereVolumePlugin) RequiresRemount() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -209,6 +209,7 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreeInlineVolumeToCSI(volume
|
|||||||
am = v1.ReadWriteOnce
|
am = v1.ReadWriteOnce
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsMode := v1.PersistentVolumeFilesystem
|
||||||
return &v1.PersistentVolume{
|
return &v1.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
// A.K.A InnerVolumeSpecName required to match for Unmount
|
// A.K.A InnerVolumeSpecName required to match for Unmount
|
||||||
@ -227,6 +228,7 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreeInlineVolumeToCSI(volume
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
AccessModes: []v1.PersistentVolumeAccessMode{am},
|
AccessModes: []v1.PersistentVolumeAccessMode{am},
|
||||||
|
VolumeMode: &fsMode,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,20 @@ func (CSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Volume) (*v1.Pe
|
|||||||
}
|
}
|
||||||
for _, curPlugin := range inTreePlugins {
|
for _, curPlugin := range inTreePlugins {
|
||||||
if curPlugin.CanSupportInline(volume) {
|
if curPlugin.CanSupportInline(volume) {
|
||||||
return curPlugin.TranslateInTreeInlineVolumeToCSI(volume)
|
pv, err := curPlugin.TranslateInTreeInlineVolumeToCSI(volume)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Inline volumes only support PersistentVolumeFilesystem (and not block).
|
||||||
|
// If VolumeMode has not been set explicitly by plugin-specific
|
||||||
|
// translator, set it to Filesystem here.
|
||||||
|
// This is only necessary for inline volumes as the default PV
|
||||||
|
// initialization that populates VolumeMode does not apply to inline volumes.
|
||||||
|
if pv.Spec.VolumeMode == nil {
|
||||||
|
volumeMode := v1.PersistentVolumeFilesystem
|
||||||
|
pv.Spec.VolumeMode = &volumeMode
|
||||||
|
}
|
||||||
|
return pv, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("could not find in-tree plugin translation logic for %#v", volume.Name)
|
return nil, fmt.Errorf("could not find in-tree plugin translation logic for %#v", volume.Name)
|
||||||
|
Loading…
Reference in New Issue
Block a user