@@ -135,9 +135,10 @@ func storageClassNeedsRecreate(oldSC, newSC *storagev1.StorageClass) bool {
135135 return false
136136}
137137
138- // ApplyCSIDriver merges objectmeta, does not worry about anything else
138+ // ApplyCSIDriver merges objectmeta and tries to update spec if any of the required fields were cleared by the API server.
139+ // It assumes they were cleared due to a feature gate not enabled in the API server and it will be enabled soon.
140+ // When used by StaticResourceController, it will retry periodically and eventually save the spec with the field.
139141func ApplyCSIDriver (ctx context.Context , client storageclientv1.CSIDriversGetter , recorder events.Recorder , requiredOriginal * storagev1.CSIDriver ) (* storagev1.CSIDriver , bool , error ) {
140-
141142 required := requiredOriginal .DeepCopy ()
142143 if required .Annotations == nil {
143144 required .Annotations = map [string ]string {}
@@ -173,31 +174,57 @@ func ApplyCSIDriver(ctx context.Context, client storageclientv1.CSIDriversGetter
173174 }
174175 }
175176
176- metadataModified := false
177+ needsUpdate := false
178+ // Most CSIDriver fields are immutable. Any change to them should trigger Delete() + Create() calls.
179+ needsRecreate := false
180+
177181 existingCopy := existing .DeepCopy ()
178- resourcemerge .EnsureObjectMeta (& metadataModified , & existingCopy .ObjectMeta , required .ObjectMeta )
182+ // Metadata change should need just Update() call.
183+ resourcemerge .EnsureObjectMeta (& needsUpdate , & existingCopy .ObjectMeta , required .ObjectMeta )
179184
180185 requiredSpecHash := required .Annotations [specHashAnnotation ]
181186 existingSpecHash := existing .Annotations [specHashAnnotation ]
182- sameSpec := requiredSpecHash == existingSpecHash
183- if sameSpec && ! metadataModified {
187+ // Assume whole re-create is needed on any spec change.
188+ // We don't keep a track of which field is mutable.
189+ needsRecreate = requiredSpecHash != existingSpecHash
190+
191+ // TODO: remove when CSIDriver spec.nodeAllocatableUpdatePeriodSeconds is enabled by default
192+ // (https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/4876-mutable-csinode-allocatable)
193+ if ! needsRecreate && ! alphaFieldsSaved (existingCopy , required ) {
194+ // The required spec is the same as in previous succesful call, however,
195+ // the API server must have cleared some alpha/beta fields in it.
196+ // Try to save the object again. In case the fields are cleared again,
197+ // the caller (typically StaticResourceController) must retry periodically.
198+ klog .V (4 ).Infof ("Detected CSIDriver %q field cleared by the API server, updating" , required .Name )
199+
200+ // Assumption: the alpha fields are **mutable**, so only Update() is needed.
201+ // Update() with the same spec as before + the field cleared by the API server
202+ // won't generate any informer events. StaticResourceController will retry with
203+ // periodic retry (1 minute.)
204+ // We cannot use needsRecreate=true, as it will generate informer events and
205+ // StaticResourceController will retry immediately, leading to a busy loop.
206+ needsUpdate = true
207+ existingCopy .Spec = required .Spec
208+ }
209+
210+ if ! needsUpdate && ! needsRecreate {
184211 return existing , false , nil
185212 }
186213
187214 if klog .V (2 ).Enabled () {
188215 klog .Infof ("CSIDriver %q changes: %v" , required .Name , JSONPatchNoError (existing , existingCopy ))
189216 }
190217
191- if sameSpec {
192- // Update metadata by a simple Update call
218+ if ! needsRecreate {
219+ // only needsUpdate is true, update the object by a simple Update call
193220 actual , err := client .CSIDrivers ().Update (ctx , existingCopy , metav1.UpdateOptions {})
194221 resourcehelper .ReportUpdateEvent (recorder , required , err )
195222 return actual , true , err
196223 }
197224
225+ // needsRecreate is true, needsUpdate does not matter. Delete and re-create the object.
198226 existingCopy .Spec = required .Spec
199227 existingCopy .ObjectMeta .ResourceVersion = ""
200- // Spec is read-only after creation. Delete and re-create the object
201228 err = client .CSIDrivers ().Delete (ctx , existingCopy .Name , metav1.DeleteOptions {})
202229 resourcehelper .ReportDeleteEvent (recorder , existingCopy , err , "Deleting CSIDriver to re-create it with updated parameters" )
203230 if err != nil && ! apierrors .IsNotFound (err ) {
@@ -214,10 +241,17 @@ func ApplyCSIDriver(ctx context.Context, client storageclientv1.CSIDriversGetter
214241 } else if err != nil {
215242 err = fmt .Errorf ("failed to re-create CSIDriver %s: %s" , existingCopy .Name , err )
216243 }
217- resourcehelper .ReportCreateEvent (recorder , existingCopy , err )
244+ resourcehelper .ReportCreateEvent (recorder , actual , err )
218245 return actual , true , err
219246}
220247
248+ // alphaFieldsSaved checks that all required fields in the CSIDriver required spec are present and equal in the actual spec.
249+ func alphaFieldsSaved (actual , required * storagev1.CSIDriver ) bool {
250+ // DeepDerivative checks that all fields in "required" are present and equal in "actual"
251+ // Fields not present in "required" are ignored.
252+ return equality .Semantic .DeepDerivative (required .Spec , actual .Spec )
253+ }
254+
221255func validateRequiredCSIDriverLabels (required * storagev1.CSIDriver ) error {
222256 supportsEphemeralVolumes := false
223257 for _ , mode := range required .Spec .VolumeLifecycleModes {
0 commit comments