Merge pull request #73653 from ddebroy/migprov1
Support dynamic provisioning for CSI migration scenarios
This commit is contained in:
commit
36787041cb
@ -62,6 +62,7 @@ go_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/errors:go_default_library",
|
"//staging/src/k8s.io/cloud-provider/volume/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
|
"//staging/src/k8s.io/csi-translation-lib:go_default_library",
|
||||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -988,6 +988,22 @@ func wrapTestWithProvisionCalls(expectedProvisionCalls []provisionCall, toWrap t
|
|||||||
return wrapTestWithPluginCalls(nil, nil, expectedProvisionCalls, toWrap)
|
return wrapTestWithPluginCalls(nil, nil, expectedProvisionCalls, toWrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wrapTestWithCSIMigrationProvisionCalls returns a testCall that:
|
||||||
|
// - configures controller with a volume plugin that emulates CSI migration
|
||||||
|
// - calls given testCall
|
||||||
|
func wrapTestWithCSIMigrationProvisionCalls(toWrap testCall) testCall {
|
||||||
|
return func(ctrl *PersistentVolumeController, reactor *volumeReactor, test controllerTest) error {
|
||||||
|
plugin := &mockVolumePlugin{
|
||||||
|
isMigratedToCSI: true,
|
||||||
|
}
|
||||||
|
ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, nil /* prober */, ctrl)
|
||||||
|
ctrl.csiNameFromIntreeNameHook = func(string) (string, error) {
|
||||||
|
return "vendor.com/MockCSIPlugin", nil
|
||||||
|
}
|
||||||
|
return toWrap(ctrl, reactor, test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// wrapTestWithInjectedOperation returns a testCall that:
|
// wrapTestWithInjectedOperation returns a testCall that:
|
||||||
// - starts the controller and lets it run original testCall until
|
// - starts the controller and lets it run original testCall until
|
||||||
// scheduleOperation() call. It blocks the controller there and calls the
|
// scheduleOperation() call. It blocks the controller there and calls the
|
||||||
@ -1229,6 +1245,7 @@ type mockVolumePlugin struct {
|
|||||||
deleteCallCounter int
|
deleteCallCounter int
|
||||||
recycleCalls []error
|
recycleCalls []error
|
||||||
recycleCallCounter int
|
recycleCallCounter int
|
||||||
|
isMigratedToCSI bool
|
||||||
provisionOptions vol.VolumeOptions
|
provisionOptions vol.VolumeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,7 +1276,7 @@ func (plugin *mockVolumePlugin) CanSupport(spec *vol.Spec) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *mockVolumePlugin) IsMigratedToCSI() bool {
|
func (plugin *mockVolumePlugin) IsMigratedToCSI() bool {
|
||||||
return false
|
return plugin.isMigratedToCSI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plugin *mockVolumePlugin) RequiresRemount() bool {
|
func (plugin *mockVolumePlugin) RequiresRemount() bool {
|
||||||
|
@ -421,6 +421,17 @@ func TestProvisionSync(t *testing.T) {
|
|||||||
[]string{"Warning ProvisioningFailed Mount options"},
|
[]string{"Warning ProvisioningFailed Mount options"},
|
||||||
noerrors, wrapTestWithProvisionCalls([]provisionCall{}, testSyncClaim),
|
noerrors, wrapTestWithProvisionCalls([]provisionCall{}, testSyncClaim),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// No provisioning due to CSI migration + normal event with external provisioner
|
||||||
|
"11-21 - external provisioner for CSI migration",
|
||||||
|
novolumes,
|
||||||
|
novolumes,
|
||||||
|
newClaimArray("claim11-21", "uid11-21", "1Gi", "", v1.ClaimPending, &classGold),
|
||||||
|
claimWithAnnotation(annStorageProvisioner, "vendor.com/MockCSIPlugin",
|
||||||
|
newClaimArray("claim11-21", "uid11-21", "1Gi", "", v1.ClaimPending, &classGold)),
|
||||||
|
[]string{"Normal ExternalProvisioning"},
|
||||||
|
noerrors, wrapTestWithCSIMigrationProvisionCalls(testSyncClaim),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
runSyncTests(t, tests, storageClasses, []*v1.Pod{})
|
runSyncTests(t, tests, storageClasses, []*v1.Pod{})
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import (
|
|||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
volerr "k8s.io/cloud-provider/volume/errors"
|
volerr "k8s.io/cloud-provider/volume/errors"
|
||||||
|
csitranslation "k8s.io/csi-translation-lib"
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/events"
|
"k8s.io/kubernetes/pkg/controller/volume/events"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/persistentvolume/metrics"
|
"k8s.io/kubernetes/pkg/controller/volume/persistentvolume/metrics"
|
||||||
@ -230,6 +231,10 @@ type PersistentVolumeController struct {
|
|||||||
|
|
||||||
createProvisionedPVRetryCount int
|
createProvisionedPVRetryCount int
|
||||||
createProvisionedPVInterval time.Duration
|
createProvisionedPVInterval time.Duration
|
||||||
|
|
||||||
|
// For testing only: hook to intercept CSI driver name <=> Intree plugin name mapping
|
||||||
|
// Not used when set to nil
|
||||||
|
csiNameFromIntreeNameHook func(pluginName string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@ -1346,6 +1351,13 @@ func (ctrl *PersistentVolumeController) provisionClaim(claim *v1.PersistentVolum
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctrl *PersistentVolumeController) getCSINameFromIntreeName(pluginName string) (string, error) {
|
||||||
|
if ctrl.csiNameFromIntreeNameHook != nil {
|
||||||
|
return ctrl.csiNameFromIntreeNameHook(pluginName)
|
||||||
|
}
|
||||||
|
return csitranslation.GetCSINameFromIntreeName(pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
// provisionClaimOperation provisions a volume. This method is running in
|
// provisionClaimOperation provisions a volume. This method is running in
|
||||||
// standalone goroutine and already has all necessary locks.
|
// standalone goroutine and already has all necessary locks.
|
||||||
func (ctrl *PersistentVolumeController) provisionClaimOperation(claim *v1.PersistentVolumeClaim) (string, error) {
|
func (ctrl *PersistentVolumeController) provisionClaimOperation(claim *v1.PersistentVolumeClaim) (string, error) {
|
||||||
@ -1362,12 +1374,26 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(claim *v1.Persis
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pluginName string
|
var pluginName string
|
||||||
|
provisionerName := storageClass.Provisioner
|
||||||
if plugin != nil {
|
if plugin != nil {
|
||||||
pluginName = plugin.GetPluginName()
|
if plugin.IsMigratedToCSI() {
|
||||||
|
// pluginName is not set here to align with existing behavior
|
||||||
|
// of not setting pluginName for external provisioners (including CSI)
|
||||||
|
// Set provisionerName to CSI plugin name for setClaimProvisioner
|
||||||
|
provisionerName, err = ctrl.getCSINameFromIntreeName(storageClass.Provisioner)
|
||||||
|
if err != nil {
|
||||||
|
strerr := fmt.Sprintf("error getting CSI name for In tree plugin %s: %v", storageClass.Provisioner, err)
|
||||||
|
klog.V(2).Infof("%s", strerr)
|
||||||
|
ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pluginName = plugin.GetPluginName()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add provisioner annotation so external provisioners know when to start
|
// Add provisioner annotation so external provisioners know when to start
|
||||||
newClaim, err := ctrl.setClaimProvisioner(claim, storageClass)
|
newClaim, err := ctrl.setClaimProvisioner(claim, provisionerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Save failed, the controller will retry in the next sync
|
// Save failed, the controller will retry in the next sync
|
||||||
klog.V(2).Infof("error saving claim %s: %v", claimToClaimKey(claim), err)
|
klog.V(2).Infof("error saving claim %s: %v", claimToClaimKey(claim), err)
|
||||||
@ -1375,7 +1401,7 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(claim *v1.Persis
|
|||||||
}
|
}
|
||||||
claim = newClaim
|
claim = newClaim
|
||||||
|
|
||||||
if plugin == nil {
|
if plugin == nil || plugin.IsMigratedToCSI() {
|
||||||
// findProvisionablePlugin returned no error nor plugin.
|
// findProvisionablePlugin returned no error nor plugin.
|
||||||
// This means that an unknown provisioner is requested. Report an event
|
// This means that an unknown provisioner is requested. Report an event
|
||||||
// and wait for the external provisioner
|
// and wait for the external provisioner
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
storage "k8s.io/api/storage/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -428,8 +427,8 @@ func (ctrl *PersistentVolumeController) resync() {
|
|||||||
|
|
||||||
// setClaimProvisioner saves
|
// setClaimProvisioner saves
|
||||||
// claim.Annotations[annStorageProvisioner] = class.Provisioner
|
// claim.Annotations[annStorageProvisioner] = class.Provisioner
|
||||||
func (ctrl *PersistentVolumeController) setClaimProvisioner(claim *v1.PersistentVolumeClaim, class *storage.StorageClass) (*v1.PersistentVolumeClaim, error) {
|
func (ctrl *PersistentVolumeController) setClaimProvisioner(claim *v1.PersistentVolumeClaim, provisionerName string) (*v1.PersistentVolumeClaim, error) {
|
||||||
if val, ok := claim.Annotations[annStorageProvisioner]; ok && val == class.Provisioner {
|
if val, ok := claim.Annotations[annStorageProvisioner]; ok && val == provisionerName {
|
||||||
// annotation is already set, nothing to do
|
// annotation is already set, nothing to do
|
||||||
return claim, nil
|
return claim, nil
|
||||||
}
|
}
|
||||||
@ -437,7 +436,7 @@ func (ctrl *PersistentVolumeController) setClaimProvisioner(claim *v1.Persistent
|
|||||||
// The volume from method args can be pointing to watcher cache. We must not
|
// The volume from method args can be pointing to watcher cache. We must not
|
||||||
// modify these, therefore create a copy.
|
// modify these, therefore create a copy.
|
||||||
claimClone := claim.DeepCopy()
|
claimClone := claim.DeepCopy()
|
||||||
metav1.SetMetaDataAnnotation(&claimClone.ObjectMeta, annStorageProvisioner, class.Provisioner)
|
metav1.SetMetaDataAnnotation(&claimClone.ObjectMeta, annStorageProvisioner, provisionerName)
|
||||||
newClaim, err := ctrl.kubeClient.CoreV1().PersistentVolumeClaims(claim.Namespace).Update(claimClone)
|
newClaim, err := ctrl.kubeClient.CoreV1().PersistentVolumeClaims(claim.Namespace).Update(claimClone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newClaim, err
|
return newClaim, err
|
||||||
|
Loading…
Reference in New Issue
Block a user