k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/csimigration/plugin_manager.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package csimigration
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	"k8s.io/component-base/featuregate"
    25  	csilibplugins "k8s.io/csi-translation-lib/plugins"
    26  	"k8s.io/kubernetes/pkg/features"
    27  	"k8s.io/kubernetes/pkg/volume"
    28  )
    29  
    30  // PluginNameMapper contains utility methods to retrieve names of plugins
    31  // that support a spec, map intree <=> migrated CSI plugin names, etc
    32  type PluginNameMapper interface {
    33  	GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error)
    34  	GetCSINameFromInTreeName(pluginName string) (string, error)
    35  }
    36  
    37  // PluginManager keeps track of migrated state of in-tree plugins
    38  type PluginManager struct {
    39  	PluginNameMapper
    40  	featureGate featuregate.FeatureGate
    41  }
    42  
    43  // NewPluginManager returns a new PluginManager instance
    44  func NewPluginManager(m PluginNameMapper, featureGate featuregate.FeatureGate) PluginManager {
    45  	return PluginManager{
    46  		PluginNameMapper: m,
    47  		featureGate:      featureGate,
    48  	}
    49  }
    50  
    51  // IsMigrationCompleteForPlugin indicates whether CSI migration has been completed
    52  // for a particular storage plugin. A complete migration will need to:
    53  // 1. Enable CSIMigrationXX for the plugin
    54  // 2. Unregister the in-tree plugin by setting the InTreePluginXXUnregister feature gate
    55  func (pm PluginManager) IsMigrationCompleteForPlugin(pluginName string) bool {
    56  	// CSIMigration feature and plugin specific InTreePluginUnregister feature flags should
    57  	// be enabled for plugin specific migration completion to be take effect
    58  	if !pm.IsMigrationEnabledForPlugin(pluginName) {
    59  		return false
    60  	}
    61  
    62  	switch pluginName {
    63  	case csilibplugins.AWSEBSInTreePluginName:
    64  		return pm.featureGate.Enabled(features.InTreePluginAWSUnregister)
    65  	case csilibplugins.GCEPDInTreePluginName:
    66  		return pm.featureGate.Enabled(features.InTreePluginGCEUnregister)
    67  	case csilibplugins.AzureFileInTreePluginName:
    68  		return pm.featureGate.Enabled(features.InTreePluginAzureFileUnregister)
    69  	case csilibplugins.AzureDiskInTreePluginName:
    70  		return pm.featureGate.Enabled(features.InTreePluginAzureDiskUnregister)
    71  	case csilibplugins.CinderInTreePluginName:
    72  		return pm.featureGate.Enabled(features.InTreePluginOpenStackUnregister)
    73  	case csilibplugins.VSphereInTreePluginName:
    74  		return pm.featureGate.Enabled(features.InTreePluginvSphereUnregister)
    75  	case csilibplugins.PortworxVolumePluginName:
    76  		return pm.featureGate.Enabled(features.InTreePluginPortworxUnregister)
    77  	default:
    78  		return false
    79  	}
    80  }
    81  
    82  // IsMigrationEnabledForPlugin indicates whether CSI migration has been enabled
    83  // for a particular storage plugin
    84  func (pm PluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
    85  	// CSIMigration feature should be enabled along with the plugin-specific one
    86  	// CSIMigration has been GA. It will be enabled by default.
    87  
    88  	switch pluginName {
    89  	case csilibplugins.AWSEBSInTreePluginName:
    90  		return true
    91  	case csilibplugins.GCEPDInTreePluginName:
    92  		return true
    93  	case csilibplugins.AzureFileInTreePluginName:
    94  		return true
    95  	case csilibplugins.AzureDiskInTreePluginName:
    96  		return true
    97  	case csilibplugins.CinderInTreePluginName:
    98  		return true
    99  	case csilibplugins.VSphereInTreePluginName:
   100  		return true
   101  	case csilibplugins.PortworxVolumePluginName:
   102  		return pm.featureGate.Enabled(features.CSIMigrationPortworx)
   103  	default:
   104  		return false
   105  	}
   106  }
   107  
   108  // IsMigratable indicates whether CSI migration has been enabled for a volume
   109  // plugin that the spec refers to
   110  func (pm PluginManager) IsMigratable(spec *volume.Spec) (bool, error) {
   111  	if spec == nil {
   112  		return false, fmt.Errorf("could not find if plugin is migratable because volume spec is nil")
   113  	}
   114  
   115  	pluginName, _ := pm.GetInTreePluginNameFromSpec(spec.PersistentVolume, spec.Volume)
   116  	if pluginName == "" {
   117  		return false, nil
   118  	}
   119  	// found an in-tree plugin that supports the spec
   120  	return pm.IsMigrationEnabledForPlugin(pluginName), nil
   121  }
   122  
   123  // InTreeToCSITranslator performs translation of Volume sources for PV and Volume objects
   124  // from references to in-tree plugins to migrated CSI plugins
   125  type InTreeToCSITranslator interface {
   126  	TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error)
   127  	TranslateInTreeInlineVolumeToCSI(volume *v1.Volume, podNamespace string) (*v1.PersistentVolume, error)
   128  }
   129  
   130  // TranslateInTreeSpecToCSI translates a volume spec (either PV or inline volume)
   131  // supported by an in-tree plugin to CSI
   132  func TranslateInTreeSpecToCSI(spec *volume.Spec, podNamespace string, translator InTreeToCSITranslator) (*volume.Spec, error) {
   133  	var csiPV *v1.PersistentVolume
   134  	var err error
   135  	inlineVolume := false
   136  	if spec.PersistentVolume != nil {
   137  		csiPV, err = translator.TranslateInTreePVToCSI(spec.PersistentVolume)
   138  	} else if spec.Volume != nil {
   139  		csiPV, err = translator.TranslateInTreeInlineVolumeToCSI(spec.Volume, podNamespace)
   140  		inlineVolume = true
   141  	} else {
   142  		err = errors.New("not a valid volume spec")
   143  	}
   144  	if err != nil {
   145  		return nil, fmt.Errorf("failed to translate in-tree pv to CSI: %v", err)
   146  	}
   147  	return &volume.Spec{
   148  		Migrated:                        true,
   149  		PersistentVolume:                csiPV,
   150  		ReadOnly:                        spec.ReadOnly,
   151  		InlineVolumeSpecForCSIMigration: inlineVolume,
   152  	}, nil
   153  }
   154  
   155  // CheckMigrationFeatureFlags checks the configuration of feature flags related
   156  // to CSI Migration is valid. It will return whether the migration is complete
   157  // by looking up the pluginUnregister flag
   158  func CheckMigrationFeatureFlags(f featuregate.FeatureGate, pluginMigration,
   159  	pluginUnregister featuregate.Feature) (migrationComplete bool, err error) {
   160  	// This is for in-tree plugin that get migration finished
   161  	if f.Enabled(pluginMigration) && f.Enabled(pluginUnregister) {
   162  		return true, nil
   163  	}
   164  	return false, nil
   165  }