mirror of https://github.com/k3s-io/k3s
176 lines
7.0 KiB
Go
176 lines
7.0 KiB
Go
/*
|
|
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"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
"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
|
|
featureGate featuregate.FeatureGate
|
|
}
|
|
|
|
// NewPluginManager returns a new PluginManager instance
|
|
func NewPluginManager(m PluginNameMapper, featureGate featuregate.FeatureGate) PluginManager {
|
|
return PluginManager{
|
|
PluginNameMapper: m,
|
|
featureGate: featureGate,
|
|
}
|
|
}
|
|
|
|
// IsMigrationCompleteForPlugin indicates whether CSI migration has been completed
|
|
// for a particular storage plugin. A complete migration will need to:
|
|
// 1. Enable CSIMigrationXX for the plugin
|
|
// 2. Unregister the in-tree plugin by setting the InTreePluginXXUnregister feature gate
|
|
func (pm PluginManager) IsMigrationCompleteForPlugin(pluginName string) bool {
|
|
// CSIMigration feature and plugin specific InTreePluginUnregister feature flags should
|
|
// be enabled for plugin specific migration completion to be take effect
|
|
if !pm.IsMigrationEnabledForPlugin(pluginName) {
|
|
return false
|
|
}
|
|
|
|
switch pluginName {
|
|
case csilibplugins.AWSEBSInTreePluginName:
|
|
return pm.featureGate.Enabled(features.InTreePluginAWSUnregister)
|
|
case csilibplugins.GCEPDInTreePluginName:
|
|
return pm.featureGate.Enabled(features.InTreePluginGCEUnregister)
|
|
case csilibplugins.AzureFileInTreePluginName:
|
|
return pm.featureGate.Enabled(features.InTreePluginAzureFileUnregister)
|
|
case csilibplugins.AzureDiskInTreePluginName:
|
|
return pm.featureGate.Enabled(features.InTreePluginAzureDiskUnregister)
|
|
case csilibplugins.CinderInTreePluginName:
|
|
return pm.featureGate.Enabled(features.InTreePluginOpenStackUnregister)
|
|
case csilibplugins.VSphereInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationvSphereComplete) || pm.featureGate.Enabled(features.InTreePluginvSphereUnregister)
|
|
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 !pm.featureGate.Enabled(features.CSIMigration) {
|
|
return false
|
|
}
|
|
|
|
switch pluginName {
|
|
case csilibplugins.AWSEBSInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationAWS)
|
|
case csilibplugins.GCEPDInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationGCE)
|
|
case csilibplugins.AzureFileInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationAzureFile)
|
|
case csilibplugins.AzureDiskInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationAzureDisk)
|
|
case csilibplugins.CinderInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationOpenStack)
|
|
case csilibplugins.VSphereInTreePluginName:
|
|
return pm.featureGate.Enabled(features.CSIMigrationvSphere)
|
|
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, podNamespace string) (*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, podNamespace string, 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, podNamespace)
|
|
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{
|
|
Migrated: true,
|
|
PersistentVolume: csiPV,
|
|
ReadOnly: spec.ReadOnly,
|
|
InlineVolumeSpecForCSIMigration: inlineVolume,
|
|
}, nil
|
|
}
|
|
|
|
// CheckMigrationFeatureFlags checks the configuration of feature flags related
|
|
// to CSI Migration is valid. It will return whether the migration is complete
|
|
// by looking up the pluginMigrationComplete and pluginUnregister flag
|
|
func CheckMigrationFeatureFlags(f featuregate.FeatureGate, pluginMigration,
|
|
pluginMigrationComplete, pluginUnregister featuregate.Feature) (migrationComplete bool, err error) {
|
|
if f.Enabled(pluginMigration) && !f.Enabled(features.CSIMigration) {
|
|
return false, fmt.Errorf("enabling %q requires CSIMigration to be enabled", pluginMigration)
|
|
}
|
|
// TODO: Remove the following two checks once the CSIMigrationXXComplete flag is removed
|
|
if pluginMigrationComplete != "" && f.Enabled(pluginMigrationComplete) && !f.Enabled(pluginMigration) {
|
|
return false, fmt.Errorf("enabling %q requires %q to be enabled", pluginMigrationComplete, pluginMigration)
|
|
}
|
|
// This is only needed for vSphere since we will deprecate the CSIMigrationvSphereComplete flag soon
|
|
if pluginMigrationComplete != "" && f.Enabled(features.CSIMigration) &&
|
|
f.Enabled(pluginMigration) && f.Enabled(pluginMigrationComplete) {
|
|
return true, nil
|
|
}
|
|
// This is for other in-tree plugin that get migration finished
|
|
if f.Enabled(pluginMigration) && f.Enabled(pluginUnregister) {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|