2016-05-17 08:55:33 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2016 The Kubernetes Authors .
2016-05-17 08:55:33 -04:00
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 persistentvolume
import (
2020-02-07 21:16:47 -05:00
"context"
2016-05-17 08:55:33 -04:00
"fmt"
2021-10-19 17:26:28 -04:00
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/util/slice"
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
"strconv"
2016-05-17 08:55:33 -04:00
"time"
2019-05-17 20:15:38 -04:00
v1 "k8s.io/api/core/v1"
2017-01-13 12:48:50 -05:00
"k8s.io/apimachinery/pkg/api/errors"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/api/meta"
2017-01-16 22:38:19 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-02-16 10:08:23 -05:00
"k8s.io/apimachinery/pkg/labels"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/util/wait"
2021-03-08 07:49:57 -05:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2017-06-23 16:56:37 -04:00
coreinformers "k8s.io/client-go/informers/core/v1"
storageinformers "k8s.io/client-go/informers/storage/v1"
clientset "k8s.io/client-go/kubernetes"
2017-07-10 13:54:48 -04:00
"k8s.io/client-go/kubernetes/scheme"
2017-01-30 13:39:54 -05:00
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2017-06-23 16:56:37 -04:00
corelisters "k8s.io/client-go/listers/core/v1"
2017-01-24 09:11:51 -05:00
"k8s.io/client-go/tools/cache"
2017-01-30 13:39:54 -05:00
"k8s.io/client-go/tools/record"
2017-01-27 10:20:40 -05:00
"k8s.io/client-go/util/workqueue"
2018-09-05 18:58:22 -04:00
cloudprovider "k8s.io/cloud-provider"
2019-09-12 20:59:06 -04:00
csitrans "k8s.io/csi-translation-lib"
2017-01-02 09:17:24 -05:00
"k8s.io/kubernetes/pkg/controller"
2020-05-29 08:29:25 -04:00
"k8s.io/kubernetes/pkg/controller/volume/common"
2018-01-05 00:41:29 -05:00
"k8s.io/kubernetes/pkg/controller/volume/persistentvolume/metrics"
2019-05-04 11:57:50 -04:00
pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
2020-06-09 17:30:40 -04:00
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
2016-06-09 07:49:04 -04:00
"k8s.io/kubernetes/pkg/util/goroutinemap"
2016-05-17 08:55:33 -04:00
vol "k8s.io/kubernetes/pkg/volume"
2019-10-02 03:25:01 -04:00
"k8s.io/kubernetes/pkg/volume/csimigration"
2016-05-17 08:55:33 -04:00
2020-04-17 15:25:06 -04:00
"k8s.io/klog/v2"
2016-05-17 08:55:33 -04:00
)
// This file contains the controller base functionality, i.e. framework to
// process PV/PVC added/updated/deleted events. The real binding, provisioning,
2016-09-26 06:26:58 -04:00
// recycling and deleting is done in pv_controller.go
2016-05-17 08:55:33 -04:00
2016-09-26 08:15:25 -04:00
// ControllerParameters contains arguments for creation of a new
// PersistentVolume controller.
type ControllerParameters struct {
2017-02-16 10:08:23 -05:00
KubeClient clientset . Interface
SyncPeriod time . Duration
VolumePlugins [ ] vol . VolumePlugin
Cloud cloudprovider . Interface
ClusterName string
VolumeInformer coreinformers . PersistentVolumeInformer
ClaimInformer coreinformers . PersistentVolumeClaimInformer
ClassInformer storageinformers . StorageClassInformer
2018-02-05 09:40:25 -05:00
PodInformer coreinformers . PodInformer
2018-05-23 04:12:20 -04:00
NodeInformer coreinformers . NodeInformer
2017-02-16 10:08:23 -05:00
EventRecorder record . EventRecorder
EnableDynamicProvisioning bool
2020-06-09 17:30:40 -04:00
FilteredDialOptions * proxyutil . FilteredDialOptions
2016-09-26 08:15:25 -04:00
}
2016-05-17 08:55:34 -04:00
2016-09-26 08:15:25 -04:00
// NewController creates a new PersistentVolume controller
2017-03-13 03:41:59 -04:00
func NewController ( p ControllerParameters ) ( * PersistentVolumeController , error ) {
2016-09-26 08:15:25 -04:00
eventRecorder := p . EventRecorder
2016-05-17 08:55:34 -04:00
if eventRecorder == nil {
broadcaster := record . NewBroadcaster ( )
2020-06-08 23:01:45 -04:00
broadcaster . StartStructuredLogging ( 0 )
2018-03-29 08:24:26 -04:00
broadcaster . StartRecordingToSink ( & v1core . EventSinkImpl { Interface : p . KubeClient . CoreV1 ( ) . Events ( "" ) } )
2017-07-15 01:25:54 -04:00
eventRecorder = broadcaster . NewRecorder ( scheme . Scheme , v1 . EventSource { Component : "persistentvolume-controller" } )
2016-05-17 08:55:34 -04:00
}
2016-05-17 08:55:33 -04:00
controller := & PersistentVolumeController {
2018-10-05 15:59:38 -04:00
volumes : newPersistentVolumeOrderedIndex ( ) ,
claims : cache . NewStore ( cache . DeletionHandlingMetaNamespaceKeyFunc ) ,
kubeClient : p . KubeClient ,
eventRecorder : eventRecorder ,
runningOperations : goroutinemap . NewGoRoutineMap ( true /* exponentialBackOffOnError */ ) ,
cloud : p . Cloud ,
2016-09-26 08:15:25 -04:00
enableDynamicProvisioning : p . EnableDynamicProvisioning ,
clusterName : p . ClusterName ,
2016-05-17 08:55:33 -04:00
createProvisionedPVRetryCount : createProvisionedPVRetryCount ,
createProvisionedPVInterval : createProvisionedPVInterval ,
2017-01-02 09:17:24 -05:00
claimQueue : workqueue . NewNamed ( "claims" ) ,
volumeQueue : workqueue . NewNamed ( "volumes" ) ,
2017-07-17 07:39:08 -04:00
resyncPeriod : p . SyncPeriod ,
2019-05-17 20:15:38 -04:00
operationTimestamps : metrics . NewOperationStartTimeCache ( ) ,
2016-05-17 08:55:33 -04:00
}
2016-05-17 08:55:34 -04:00
2017-07-25 20:48:26 -04:00
// Prober is nil because PV is not aware of Flexvolume.
if err := controller . volumePluginMgr . InitPlugins ( p . VolumePlugins , nil /* prober */ , controller ) ; err != nil {
2021-04-20 23:42:51 -04:00
return nil , fmt . Errorf ( "could not initialize volume plugins for PersistentVolume Controller: %w" , err )
2017-03-13 03:41:59 -04:00
}
2016-05-17 08:55:33 -04:00
2017-07-17 07:39:08 -04:00
p . VolumeInformer . Informer ( ) . AddEventHandler (
2016-09-14 14:35:38 -04:00
cache . ResourceEventHandlerFuncs {
2017-01-02 09:17:24 -05:00
AddFunc : func ( obj interface { } ) { controller . enqueueWork ( controller . volumeQueue , obj ) } ,
UpdateFunc : func ( oldObj , newObj interface { } ) { controller . enqueueWork ( controller . volumeQueue , newObj ) } ,
DeleteFunc : func ( obj interface { } ) { controller . enqueueWork ( controller . volumeQueue , obj ) } ,
2016-09-13 10:12:34 -04:00
} ,
2017-02-16 10:08:23 -05:00
)
controller . volumeLister = p . VolumeInformer . Lister ( )
controller . volumeListerSynced = p . VolumeInformer . Informer ( ) . HasSynced
2017-07-17 07:39:08 -04:00
p . ClaimInformer . Informer ( ) . AddEventHandler (
2016-09-14 14:35:38 -04:00
cache . ResourceEventHandlerFuncs {
2017-01-02 09:17:24 -05:00
AddFunc : func ( obj interface { } ) { controller . enqueueWork ( controller . claimQueue , obj ) } ,
UpdateFunc : func ( oldObj , newObj interface { } ) { controller . enqueueWork ( controller . claimQueue , newObj ) } ,
DeleteFunc : func ( obj interface { } ) { controller . enqueueWork ( controller . claimQueue , obj ) } ,
2016-05-17 08:55:33 -04:00
} ,
2016-08-18 04:36:49 -04:00
)
2017-02-16 10:08:23 -05:00
controller . claimLister = p . ClaimInformer . Lister ( )
controller . claimListerSynced = p . ClaimInformer . Informer ( ) . HasSynced
controller . classLister = p . ClassInformer . Lister ( )
controller . classListerSynced = p . ClassInformer . Informer ( ) . HasSynced
2018-02-05 09:40:25 -05:00
controller . podLister = p . PodInformer . Lister ( )
2020-05-26 09:39:12 -04:00
controller . podIndexer = p . PodInformer . Informer ( ) . GetIndexer ( )
2018-02-05 09:40:25 -05:00
controller . podListerSynced = p . PodInformer . Informer ( ) . HasSynced
2018-05-23 04:12:20 -04:00
controller . NodeLister = p . NodeInformer . Lister ( )
controller . NodeListerSynced = p . NodeInformer . Informer ( ) . HasSynced
2019-10-02 03:25:01 -04:00
2020-05-26 09:39:12 -04:00
// This custom indexer will index pods by its PVC keys. Then we don't need
// to iterate all pods every time to find pods which reference given PVC.
2020-06-08 04:31:38 -04:00
if err := common . AddPodPVCIndexerIfNotPresent ( controller . podIndexer ) ; err != nil {
2021-04-20 23:42:51 -04:00
return nil , fmt . Errorf ( "could not initialize attach detach controller: %w" , err )
2020-05-26 09:39:12 -04:00
}
2019-10-02 03:25:01 -04:00
csiTranslator := csitrans . New ( )
controller . translator = csiTranslator
2021-03-08 07:49:57 -05:00
controller . csiMigratedPluginManager = csimigration . NewPluginManager ( csiTranslator , utilfeature . DefaultFeatureGate )
2019-10-02 03:25:01 -04:00
2020-06-09 17:30:40 -04:00
controller . filteredDialOptions = p . FilteredDialOptions
2017-03-13 03:41:59 -04:00
return controller , nil
2016-05-17 08:55:33 -04:00
}
2016-06-17 12:27:25 -04:00
// initializeCaches fills all controller caches with initial data from etcd in
2016-05-30 07:16:45 -04:00
// order to have the caches already filled when first addClaim/addVolume to
// perform initial synchronization of the controller.
2017-02-16 10:08:23 -05:00
func ( ctrl * PersistentVolumeController ) initializeCaches ( volumeLister corelisters . PersistentVolumeLister , claimLister corelisters . PersistentVolumeClaimLister ) {
volumeList , err := volumeLister . List ( labels . Everything ( ) )
2016-09-13 10:12:34 -04:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "PersistentVolumeController can't initialize caches: %v" , err )
2016-09-13 10:12:34 -04:00
return
}
2017-02-16 10:08:23 -05:00
for _ , volume := range volumeList {
2017-08-15 08:14:21 -04:00
volumeClone := volume . DeepCopy ( )
2017-04-19 07:12:32 -04:00
if _ , err = ctrl . storeVolumeUpdate ( volumeClone ) ; err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "error updating volume cache: %v" , err )
2016-06-03 08:26:06 -04:00
}
2016-05-30 07:16:45 -04:00
}
2017-02-16 10:08:23 -05:00
claimList , err := claimLister . List ( labels . Everything ( ) )
2016-05-30 07:16:45 -04:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "PersistentVolumeController can't initialize caches: %v" , err )
2016-05-30 07:16:45 -04:00
return
}
2017-02-16 10:08:23 -05:00
for _ , claim := range claimList {
2017-08-15 08:14:21 -04:00
if _ , err = ctrl . storeClaimUpdate ( claim . DeepCopy ( ) ) ; err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "error updating claim cache: %v" , err )
2017-03-04 02:49:25 -05:00
}
2016-05-30 07:16:45 -04:00
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "controller initialized" )
2016-05-30 07:16:45 -04:00
}
2017-01-02 09:17:24 -05:00
// enqueueWork adds volume or claim to given work queue.
func ( ctrl * PersistentVolumeController ) enqueueWork ( queue workqueue . Interface , obj interface { } ) {
// Beware of "xxx deleted" events
if unknown , ok := obj . ( cache . DeletedFinalStateUnknown ) ; ok && unknown . Obj != nil {
obj = unknown . Obj
}
objName , err := controller . KeyFunc ( obj )
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "failed to get key from object: %v" , err )
2017-01-02 09:17:24 -05:00
return
}
2018-11-09 13:49:10 -05:00
klog . V ( 5 ) . Infof ( "enqueued %q for sync" , objName )
2017-01-02 09:17:24 -05:00
queue . Add ( objName )
}
func ( ctrl * PersistentVolumeController ) storeVolumeUpdate ( volume interface { } ) ( bool , error ) {
2016-06-27 07:08:02 -04:00
return storeObjectUpdate ( ctrl . volumes . store , volume , "volume" )
}
2017-01-02 09:17:24 -05:00
func ( ctrl * PersistentVolumeController ) storeClaimUpdate ( claim interface { } ) ( bool , error ) {
2016-06-27 07:08:02 -04:00
return storeObjectUpdate ( ctrl . claims , claim , "claim" )
}
2017-01-02 09:17:24 -05:00
// updateVolume runs in worker thread and handles "volume added",
// "volume updated" and "periodic sync" events.
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) updateVolume ( ctx context . Context , volume * v1 . PersistentVolume ) {
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
// Store the new volume version in the cache and do not process it if this
// is an old version.
2017-01-02 09:17:24 -05:00
new , err := ctrl . storeVolumeUpdate ( volume )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "%v" , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
if ! new {
return
}
2021-04-22 14:27:59 -04:00
err = ctrl . syncVolume ( ctx , volume )
2017-01-02 09:17:24 -05:00
if err != nil {
2016-05-17 08:55:33 -04:00
if errors . IsConflict ( err ) {
// Version conflict error happens quite often and the controller
// recovers from it easily.
2018-11-09 13:49:10 -05:00
klog . V ( 3 ) . Infof ( "could not sync volume %q: %+v" , volume . Name , err )
2016-05-17 08:55:33 -04:00
} else {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "could not sync volume %q: %+v" , volume . Name , err )
2016-05-17 08:55:33 -04:00
}
}
}
2017-01-02 09:17:24 -05:00
// deleteVolume runs in worker thread and handles "volume deleted" event.
func ( ctrl * PersistentVolumeController ) deleteVolume ( volume * v1 . PersistentVolume ) {
2019-10-05 22:34:39 -04:00
if err := ctrl . volumes . store . Delete ( volume ) ; err != nil {
klog . Errorf ( "volume %q deletion encountered : %v" , volume . Name , err )
} else {
klog . V ( 4 ) . Infof ( "volume %q deleted" , volume . Name )
}
2019-05-17 20:15:38 -04:00
// record deletion metric if a deletion start timestamp is in the cache
// the following calls will be a no-op if there is nothing for this volume in the cache
// end of timestamp cache entry lifecycle, "RecordMetric" will do the clean
metrics . RecordMetric ( volume . Name , & ctrl . operationTimestamps , nil )
2016-06-03 08:26:06 -04:00
2017-01-02 09:17:24 -05:00
if volume . Spec . ClaimRef == nil {
2016-06-03 08:26:06 -04:00
return
}
2017-01-02 09:17:24 -05:00
// sync the claim when its volume is deleted. Explicitly syncing the
// claim here in response to volume deletion prevents the claim from
// waiting until the next sync period for its Lost status.
claimKey := claimrefToClaimKey ( volume . Spec . ClaimRef )
2018-11-09 13:49:10 -05:00
klog . V ( 5 ) . Infof ( "deleteVolume[%s]: scheduling sync of claim %q" , volume . Name , claimKey )
2017-01-02 09:17:24 -05:00
ctrl . claimQueue . Add ( claimKey )
}
2016-06-03 08:26:06 -04:00
2017-01-02 09:17:24 -05:00
// updateClaim runs in worker thread and handles "claim added",
// "claim updated" and "periodic sync" events.
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) updateClaim ( ctx context . Context , claim * v1 . PersistentVolumeClaim ) {
2017-01-02 09:17:24 -05:00
// Store the new claim version in the cache and do not process it if this is
// an old version.
new , err := ctrl . storeClaimUpdate ( claim )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "%v" , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
if ! new {
return
}
2021-04-22 14:27:59 -04:00
err = ctrl . syncClaim ( ctx , claim )
2017-01-02 09:17:24 -05:00
if err != nil {
2016-05-17 08:55:33 -04:00
if errors . IsConflict ( err ) {
// Version conflict error happens quite often and the controller
// recovers from it easily.
2018-11-09 13:49:10 -05:00
klog . V ( 3 ) . Infof ( "could not sync claim %q: %+v" , claimToClaimKey ( claim ) , err )
2016-05-17 08:55:33 -04:00
} else {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "could not sync volume %q: %+v" , claimToClaimKey ( claim ) , err )
2016-05-17 08:55:33 -04:00
}
}
}
2019-05-17 20:15:38 -04:00
// Unit test [5-5] [5-6] [5-7]
2017-01-02 09:17:24 -05:00
// deleteClaim runs in worker thread and handles "claim deleted" event.
func ( ctrl * PersistentVolumeController ) deleteClaim ( claim * v1 . PersistentVolumeClaim ) {
2019-10-05 22:34:39 -04:00
if err := ctrl . claims . Delete ( claim ) ; err != nil {
klog . Errorf ( "claim %q deletion encountered : %v" , claim . Name , err )
}
2019-05-17 20:15:38 -04:00
claimKey := claimToClaimKey ( claim )
klog . V ( 4 ) . Infof ( "claim %q deleted" , claimKey )
// clean any possible unfinished provision start timestamp from cache
// Unit test [5-8] [5-9]
ctrl . operationTimestamps . Delete ( claimKey )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
2017-11-06 02:23:19 -05:00
volumeName := claim . Spec . VolumeName
if volumeName == "" {
2019-05-17 20:15:38 -04:00
klog . V ( 5 ) . Infof ( "deleteClaim[%q]: volume not bound" , claimKey )
2017-11-06 02:23:19 -05:00
return
}
2019-05-17 20:15:38 -04:00
2017-01-02 09:17:24 -05:00
// sync the volume when its claim is deleted. Explicitly sync'ing the
// volume here in response to claim deletion prevents the volume from
// waiting until the next sync period for its Release.
2019-05-17 20:15:38 -04:00
klog . V ( 5 ) . Infof ( "deleteClaim[%q]: scheduling sync of volume %s" , claimKey , volumeName )
2017-01-02 09:17:24 -05:00
ctrl . volumeQueue . Add ( volumeName )
}
2016-05-17 08:55:33 -04:00
2017-01-02 09:17:24 -05:00
// Run starts all of this controller's control loops
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) Run ( ctx context . Context ) {
2017-04-12 15:49:17 -04:00
defer utilruntime . HandleCrash ( )
defer ctrl . claimQueue . ShutDown ( )
defer ctrl . volumeQueue . ShutDown ( )
2018-11-09 13:49:10 -05:00
klog . Infof ( "Starting persistent volume controller" )
defer klog . Infof ( "Shutting down persistent volume controller" )
2017-04-12 15:49:17 -04:00
2021-04-22 14:27:59 -04:00
if ! cache . WaitForNamedCacheSync ( "persistent volume" , ctx . Done ( ) , ctrl . volumeListerSynced , ctrl . claimListerSynced , ctrl . classListerSynced , ctrl . podListerSynced , ctrl . NodeListerSynced ) {
2017-02-16 10:08:23 -05:00
return
}
2017-04-12 15:49:17 -04:00
2017-02-16 10:08:23 -05:00
ctrl . initializeCaches ( ctrl . volumeLister , ctrl . claimLister )
2017-04-12 15:49:17 -04:00
2021-04-22 14:27:59 -04:00
go wait . Until ( ctrl . resync , ctrl . resyncPeriod , ctx . Done ( ) )
go wait . UntilWithContext ( ctx , ctrl . volumeWorker , time . Second )
go wait . UntilWithContext ( ctx , ctrl . claimWorker , time . Second )
2016-05-17 08:55:33 -04:00
2020-10-20 05:49:11 -04:00
metrics . Register ( ctrl . volumes . store , ctrl . claims , & ctrl . volumePluginMgr )
2018-01-05 00:41:29 -05:00
2021-04-22 14:27:59 -04:00
<- ctx . Done ( )
2016-05-17 08:55:33 -04:00
}
2021-10-19 17:26:28 -04:00
func ( ctrl * PersistentVolumeController ) updateClaimMigrationAnnotations ( ctx context . Context ,
claim * v1 . PersistentVolumeClaim ) ( * v1 . PersistentVolumeClaim , error ) {
2020-01-16 18:50:53 -05:00
// TODO: update[Claim|Volume]MigrationAnnotations can be optimized to not
// copy the claim/volume if no modifications are required. Though this
// requires some refactoring as well as an interesting change in the
// semantics of the function which may be undesirable. If no copy is made
// when no modifications are required this function could sometimes return a
// copy of the volume and sometimes return a ref to the original
claimClone := claim . DeepCopy ( )
2021-10-19 17:26:28 -04:00
modified := updateMigrationAnnotationsAndFinalizers ( ctrl . csiMigratedPluginManager , ctrl . translator , claimClone . Annotations , nil , true )
2020-01-16 18:50:53 -05:00
if ! modified {
return claimClone , nil
}
2021-04-22 14:27:59 -04:00
newClaim , err := ctrl . kubeClient . CoreV1 ( ) . PersistentVolumeClaims ( claimClone . Namespace ) . Update ( ctx , claimClone , metav1 . UpdateOptions { } )
2020-01-16 18:50:53 -05:00
if err != nil {
return nil , fmt . Errorf ( "persistent Volume Controller can't anneal migration annotations: %v" , err )
}
_ , err = ctrl . storeClaimUpdate ( newClaim )
if err != nil {
return nil , fmt . Errorf ( "persistent Volume Controller can't anneal migration annotations: %v" , err )
}
return newClaim , nil
}
2021-10-19 17:26:28 -04:00
func ( ctrl * PersistentVolumeController ) updateVolumeMigrationAnnotationsAndFinalizers ( ctx context . Context ,
volume * v1 . PersistentVolume ) ( * v1 . PersistentVolume , error ) {
2020-01-16 18:50:53 -05:00
volumeClone := volume . DeepCopy ( )
2021-10-19 17:26:28 -04:00
modified := updateMigrationAnnotationsAndFinalizers ( ctrl . csiMigratedPluginManager , ctrl . translator ,
volumeClone . Annotations , & volumeClone . Finalizers , false )
2020-01-16 18:50:53 -05:00
if ! modified {
return volumeClone , nil
}
2021-04-22 14:27:59 -04:00
newVol , err := ctrl . kubeClient . CoreV1 ( ) . PersistentVolumes ( ) . Update ( ctx , volumeClone , metav1 . UpdateOptions { } )
2020-01-16 18:50:53 -05:00
if err != nil {
2021-10-19 17:26:28 -04:00
return nil , fmt . Errorf ( "persistent Volume Controller can't anneal migration annotations or finalizer: %v" , err )
2020-01-16 18:50:53 -05:00
}
_ , err = ctrl . storeVolumeUpdate ( newVol )
if err != nil {
2021-10-19 17:26:28 -04:00
return nil , fmt . Errorf ( "persistent Volume Controller can't anneal migration annotations or finalizer: %v" , err )
2020-01-16 18:50:53 -05:00
}
return newVol , nil
}
2021-10-19 17:26:28 -04:00
// updateMigrationAnnotationsAndFinalizers takes an Annotations map and finalizers slice and checks for a
2021-08-25 19:30:45 -04:00
// provisioner name depending if the annotation came from a PVC or not.
// It will then add a "pv.kubernetes.io/migrated-to" annotation if migration with
// the CSI driver name for that provisioner is "on" based on feature flags, it will also
2021-10-19 17:26:28 -04:00
// remove the PV deletion protection finalizer and "pv.kubernetes.io/migrated-to" annotation
// if migration is "off" for that provisioner in rollback scenarios. Returns true if the annotations
// map or finalizers were modified and false otherwise.
2021-08-25 19:30:45 -04:00
// Parameters:
// - claim: true means the ann came from a PVC, false means the ann came from a PV
2021-10-19 17:26:28 -04:00
func updateMigrationAnnotationsAndFinalizers ( cmpm CSIMigratedPluginManager , translator CSINameTranslator ,
ann map [ string ] string , finalizers * [ ] string , claim bool ) bool {
2020-01-16 18:50:53 -05:00
var csiDriverName string
var err error
if ann == nil {
// No annotations so we can't get the provisioner and don't know whether
// this is migrated - no change
return false
}
2021-08-25 19:30:45 -04:00
var provisionerKey string
if claim {
provisionerKey = pvutil . AnnStorageProvisioner
} else {
provisionerKey = pvutil . AnnDynamicallyProvisioned
}
2020-01-16 18:50:53 -05:00
provisioner , ok := ann [ provisionerKey ]
if ! ok {
2021-08-25 19:30:45 -04:00
if claim {
// Also check beta AnnStorageProvisioner annontation to make sure
provisioner , ok = ann [ pvutil . AnnBetaStorageProvisioner ]
if ! ok {
return false
}
} else {
// Volume not dynamically provisioned. Ignore
return false
}
2020-01-16 18:50:53 -05:00
}
migratedToDriver := ann [ pvutil . AnnMigratedTo ]
if cmpm . IsMigrationEnabledForPlugin ( provisioner ) {
csiDriverName , err = translator . GetCSINameFromInTreeName ( provisioner )
if err != nil {
klog . Errorf ( "Could not update volume migration annotations. Migration enabled for plugin %s but could not find corresponding driver name: %v" , provisioner , err )
return false
}
if migratedToDriver != csiDriverName {
ann [ pvutil . AnnMigratedTo ] = csiDriverName
return true
}
} else {
if migratedToDriver != "" {
// Migration annotation exists but the driver isn't migrated currently
delete ( ann , pvutil . AnnMigratedTo )
2021-10-19 17:26:28 -04:00
// Remove the PV deletion protection finalizer on volumes.
if ! claim && finalizers != nil && utilfeature . DefaultFeatureGate . Enabled ( features . HonorPVReclaimPolicy ) {
if slice . ContainsString ( * finalizers , pvutil . PVDeletionProtectionFinalizer , nil ) {
* finalizers = slice . RemoveString ( * finalizers , pvutil . PVDeletionProtectionFinalizer , nil )
}
}
2020-01-16 18:50:53 -05:00
return true
}
}
return false
}
2017-01-02 09:17:24 -05:00
// volumeWorker processes items from volumeQueue. It must run only once,
// syncVolume is not assured to be reentrant.
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) volumeWorker ( ctx context . Context ) {
workFunc := func ( ctx context . Context ) bool {
2017-01-02 09:17:24 -05:00
keyObj , quit := ctrl . volumeQueue . Get ( )
if quit {
return true
}
defer ctrl . volumeQueue . Done ( keyObj )
key := keyObj . ( string )
2018-11-09 13:49:10 -05:00
klog . V ( 5 ) . Infof ( "volumeWorker[%s]" , key )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
2017-02-16 10:08:23 -05:00
_ , name , err := cache . SplitMetaNamespaceKey ( key )
2017-01-02 09:17:24 -05:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "error getting name of volume %q to get volume from informer: %v" , key , err )
2017-01-02 09:17:24 -05:00
return false
2016-05-17 08:55:33 -04:00
}
2017-02-16 10:08:23 -05:00
volume , err := ctrl . volumeLister . Get ( name )
if err == nil {
2017-01-02 09:17:24 -05:00
// The volume still exists in informer cache, the event must have
// been add/update/sync
2021-04-22 14:27:59 -04:00
ctrl . updateVolume ( ctx , volume )
2017-01-02 09:17:24 -05:00
return false
}
2017-02-16 10:08:23 -05:00
if ! errors . IsNotFound ( err ) {
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "error getting volume %q from informer: %v" , key , err )
2017-02-16 10:08:23 -05:00
return false
}
2016-06-27 07:08:02 -04:00
2017-01-02 09:17:24 -05:00
// The volume is not in informer cache, the event must have been
// "delete"
2017-02-16 10:08:23 -05:00
volumeObj , found , err := ctrl . volumes . store . GetByKey ( key )
2017-01-02 09:17:24 -05:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "error getting volume %q from cache: %v" , key , err )
2017-01-02 09:17:24 -05:00
return false
}
if ! found {
// The controller has already processed the delete event and
// deleted the volume from its cache
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "deletion of volume %q was already processed" , key )
2017-01-02 09:17:24 -05:00
return false
}
volume , ok := volumeObj . ( * v1 . PersistentVolume )
if ! ok {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "expected volume, got %+v" , volumeObj )
2017-01-02 09:17:24 -05:00
return false
}
ctrl . deleteVolume ( volume )
return false
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
2017-01-02 09:17:24 -05:00
for {
2021-04-22 14:27:59 -04:00
if quit := workFunc ( ctx ) ; quit {
2018-11-09 13:49:10 -05:00
klog . Infof ( "volume worker queue shutting down" )
2017-01-02 09:17:24 -05:00
return
2016-05-17 08:55:33 -04:00
}
}
}
2017-01-02 09:17:24 -05:00
// claimWorker processes items from claimQueue. It must run only once,
// syncClaim is not reentrant.
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) claimWorker ( ctx context . Context ) {
2017-01-02 09:17:24 -05:00
workFunc := func ( ) bool {
keyObj , quit := ctrl . claimQueue . Get ( )
if quit {
return true
}
defer ctrl . claimQueue . Done ( keyObj )
key := keyObj . ( string )
2018-11-09 13:49:10 -05:00
klog . V ( 5 ) . Infof ( "claimWorker[%s]" , key )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
2017-02-16 10:08:23 -05:00
namespace , name , err := cache . SplitMetaNamespaceKey ( key )
2017-01-02 09:17:24 -05:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "error getting namespace & name of claim %q to get claim from informer: %v" , key , err )
2017-01-02 09:17:24 -05:00
return false
}
2017-02-16 10:08:23 -05:00
claim , err := ctrl . claimLister . PersistentVolumeClaims ( namespace ) . Get ( name )
if err == nil {
2017-01-02 09:17:24 -05:00
// The claim still exists in informer cache, the event must have
// been add/update/sync
2021-04-22 14:27:59 -04:00
ctrl . updateClaim ( ctx , claim )
2017-01-02 09:17:24 -05:00
return false
2016-05-17 08:55:33 -04:00
}
2017-02-16 10:08:23 -05:00
if ! errors . IsNotFound ( err ) {
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "error getting claim %q from informer: %v" , key , err )
2017-02-16 10:08:23 -05:00
return false
}
2016-05-17 08:55:33 -04:00
2017-01-02 09:17:24 -05:00
// The claim is not in informer cache, the event must have been "delete"
2017-02-16 10:08:23 -05:00
claimObj , found , err := ctrl . claims . GetByKey ( key )
2017-01-02 09:17:24 -05:00
if err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "error getting claim %q from cache: %v" , key , err )
2017-01-02 09:17:24 -05:00
return false
}
if ! found {
// The controller has already processed the delete event and
// deleted the claim from its cache
2018-11-09 13:49:10 -05:00
klog . V ( 2 ) . Infof ( "deletion of claim %q was already processed" , key )
2017-01-02 09:17:24 -05:00
return false
}
claim , ok := claimObj . ( * v1 . PersistentVolumeClaim )
if ! ok {
2018-11-09 13:49:10 -05:00
klog . Errorf ( "expected claim, got %+v" , claimObj )
2017-01-02 09:17:24 -05:00
return false
}
ctrl . deleteClaim ( claim )
return false
2016-05-17 08:55:33 -04:00
}
2017-01-02 09:17:24 -05:00
for {
if quit := workFunc ( ) ; quit {
2018-11-09 13:49:10 -05:00
klog . Infof ( "claim worker queue shutting down" )
2017-01-02 09:17:24 -05:00
return
2016-05-17 08:55:33 -04:00
}
}
}
2017-07-17 07:39:08 -04:00
// resync supplements short resync period of shared informers - we don't want
// all consumers of PV/PVC shared informer to have a short resync period,
// therefore we do our own.
func ( ctrl * PersistentVolumeController ) resync ( ) {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "resyncing PV controller" )
2017-07-17 07:39:08 -04:00
pvcs , err := ctrl . claimLister . List ( labels . NewSelector ( ) )
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Warningf ( "cannot list claims: %s" , err )
2017-07-17 07:39:08 -04:00
return
}
for _ , pvc := range pvcs {
ctrl . enqueueWork ( ctrl . claimQueue , pvc )
}
pvs , err := ctrl . volumeLister . List ( labels . NewSelector ( ) )
if err != nil {
2018-11-09 13:49:10 -05:00
klog . Warningf ( "cannot list persistent volumes: %s" , err )
2017-07-17 07:39:08 -04:00
return
}
for _ , pv := range pvs {
ctrl . enqueueWork ( ctrl . volumeQueue , pv )
}
}
2016-11-02 09:13:34 -04:00
// setClaimProvisioner saves
2021-08-25 19:30:45 -04:00
// claim.Annotations["volume.kubernetes.io/storage-provisioner"] = class.Provisioner
2021-04-22 14:27:59 -04:00
func ( ctrl * PersistentVolumeController ) setClaimProvisioner ( ctx context . Context , claim * v1 . PersistentVolumeClaim , provisionerName string ) ( * v1 . PersistentVolumeClaim , error ) {
2019-05-04 11:57:50 -04:00
if val , ok := claim . Annotations [ pvutil . AnnStorageProvisioner ] ; ok && val == provisionerName {
2016-11-02 09:13:34 -04:00
// annotation is already set, nothing to do
return claim , nil
}
// The volume from method args can be pointing to watcher cache. We must not
// modify these, therefore create a copy.
2017-08-15 08:14:21 -04:00
claimClone := claim . DeepCopy ( )
2021-08-25 19:30:45 -04:00
// TODO: remove the beta storage provisioner anno after the deprecation period
metav1 . SetMetaDataAnnotation ( & claimClone . ObjectMeta , pvutil . AnnBetaStorageProvisioner , provisionerName )
2019-05-04 11:57:50 -04:00
metav1 . SetMetaDataAnnotation ( & claimClone . ObjectMeta , pvutil . AnnStorageProvisioner , provisionerName )
2021-10-19 17:26:28 -04:00
updateMigrationAnnotationsAndFinalizers ( ctrl . csiMigratedPluginManager , ctrl . translator , claimClone . Annotations , nil , true )
newClaim , err := ctrl . kubeClient . CoreV1 ( ) . PersistentVolumeClaims ( claim . Namespace ) . Update ( context . TODO ( ) , claimClone , metav1 . UpdateOptions { } )
2016-11-02 09:13:34 -04:00
if err != nil {
return newClaim , err
}
_ , err = ctrl . storeClaimUpdate ( newClaim )
if err != nil {
return newClaim , err
}
return newClaim , nil
}
2016-05-17 08:55:33 -04:00
// Stateless functions
2016-11-18 15:50:17 -05:00
func getClaimStatusForLogging ( claim * v1 . PersistentVolumeClaim ) string {
2019-05-04 11:57:50 -04:00
bound := metav1 . HasAnnotation ( claim . ObjectMeta , pvutil . AnnBindCompleted )
boundByController := metav1 . HasAnnotation ( claim . ObjectMeta , pvutil . AnnBoundByController )
2016-05-17 08:55:33 -04:00
return fmt . Sprintf ( "phase: %s, bound to: %q, bindCompleted: %v, boundByController: %v" , claim . Status . Phase , claim . Spec . VolumeName , bound , boundByController )
}
2016-11-18 15:50:17 -05:00
func getVolumeStatusForLogging ( volume * v1 . PersistentVolume ) string {
2019-05-04 11:57:50 -04:00
boundByController := metav1 . HasAnnotation ( volume . ObjectMeta , pvutil . AnnBoundByController )
2016-05-17 08:55:33 -04:00
claimName := ""
if volume . Spec . ClaimRef != nil {
claimName = fmt . Sprintf ( "%s/%s (uid: %s)" , volume . Spec . ClaimRef . Namespace , volume . Spec . ClaimRef . Name , volume . Spec . ClaimRef . UID )
}
return fmt . Sprintf ( "phase: %s, bound to: %q, boundByController: %v" , volume . Status . Phase , claimName , boundByController )
}
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
// storeObjectUpdate updates given cache with a new object version from Informer
// callback (i.e. with events from etcd) or with an object modified by the
// controller itself. Returns "true", if the cache was updated, false if the
// object is an old version and should be ignored.
func storeObjectUpdate ( store cache . Store , obj interface { } , className string ) ( bool , error ) {
2017-01-02 09:17:24 -05:00
objName , err := controller . KeyFunc ( obj )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "couldn't get key for object %+v: %w" , obj , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
oldObj , found , err := store . Get ( obj )
if err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "error finding %s %q in controller cache: %w" , className , objName , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
2017-01-02 09:17:24 -05:00
objAccessor , err := meta . Accessor ( obj )
if err != nil {
return false , err
}
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if ! found {
// This is a new object
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "storeObjectUpdate: adding %s %q, version %s" , className , objName , objAccessor . GetResourceVersion ( ) )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err = store . Add ( obj ) ; err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "error adding %s %q to controller cache: %w" , className , objName , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
return true , nil
}
oldObjAccessor , err := meta . Accessor ( oldObj )
if err != nil {
return false , err
}
objResourceVersion , err := strconv . ParseInt ( objAccessor . GetResourceVersion ( ) , 10 , 64 )
if err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "error parsing ResourceVersion %q of %s %q: %s" , objAccessor . GetResourceVersion ( ) , className , objName , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
oldObjResourceVersion , err := strconv . ParseInt ( oldObjAccessor . GetResourceVersion ( ) , 10 , 64 )
if err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "error parsing old ResourceVersion %q of %s %q: %s" , oldObjAccessor . GetResourceVersion ( ) , className , objName , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
// Throw away only older version, let the same version pass - we do want to
// get periodic sync events.
if oldObjResourceVersion > objResourceVersion {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "storeObjectUpdate: ignoring %s %q version %s" , className , objName , objAccessor . GetResourceVersion ( ) )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
return false , nil
}
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "storeObjectUpdate updating %s %q with version %s" , className , objName , objAccessor . GetResourceVersion ( ) )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
if err = store . Update ( obj ) ; err != nil {
2021-04-20 23:42:51 -04:00
return false , fmt . Errorf ( "error updating %s %q in controller cache: %w" , className , objName , err )
volume controller: Add cache with the latest version of PVs and PVCs
When the controller binds a PV to PVC, it saves both objects to etcd.
However, there is still an old version of these objects in the controller
Informer cache. So, when a new PVC comes, the PV is still seen as available
and may get bound to the new PVC. This will be blocked by etcd, still, it
creates unnecessary traffic that slows everything down.
Also, we save bound PV/PVC as two transactions - we save PV/PVC.Spec first
and then .Status. The controller gets "PV/PVC.Spec updated" event from etcd
and tries to fix the Status, as it seems to the controller it's outdated.
This write again fails - there already is a correct version in etcd.
We can't influence the Informer cache, it is read-only to the controller.
To prevent these useless writes to etcd, this patch introduces second cache
in the controller, which holds latest and greatest version on PVs and PVCs.
It gets updated with events from etcd *and* after etcd confirms successful
save of PV/PVC modified by the controller.
The cache stores only *pointers* to PVs/PVCs, so in ideal case it shares the
actual object data with the informer cache. They will diverge only when
the controller modifies something and the informer cache did not get update
events yet.
2016-05-19 07:31:19 -04:00
}
return true , nil
}