@@ -3,11 +3,13 @@ package proxy
33import (
44 "context"
55 "fmt"
6+ "time"
67
78 kerrors "k8s.io/apimachinery/pkg/api/errors"
89 metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
910 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011 "k8s.io/apimachinery/pkg/runtime"
12+ "k8s.io/apimachinery/pkg/util/wait"
1113 "k8s.io/apimachinery/pkg/watch"
1214 apirequest "k8s.io/apiserver/pkg/endpoints/request"
1315 "k8s.io/apiserver/pkg/registry/rest"
@@ -208,32 +210,58 @@ func (s *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
208210
209211var _ = rest .GracefulDeleter (& REST {})
210212
213+ // maxRetriesOnConflict is the maximum retry count for Delete calls which
214+ // result in resource conflicts.
215+ const maxRetriesOnConflict = 10
216+
217+ // maxDuration set max duration of delete retries. Deleting a project affects apiserver latency,
218+ // so this should be kept as small as possible
219+ const maxDuration = time .Second
220+
211221// Delete deletes a Project specified by its name
212222func (s * REST ) Delete (ctx context.Context , name string , objectFunc rest.ValidateObjectFunc , options * metav1.DeleteOptions ) (runtime.Object , bool , error ) {
213223 var opts metav1.DeleteOptions
214224 if options != nil {
215225 opts = * options
216226 }
217- if objectFunc != nil {
218- obj , err := s .Get (ctx , name , & metav1.GetOptions {})
219- if err != nil {
220- return nil , false , err
221- }
222- projectObj , ok := obj .(* projectapi.Project )
223- if ! ok || projectObj == nil {
224- return nil , false , fmt .Errorf ("not a project: %#v" , obj )
227+ var lastErr error
228+ err := wait .ExponentialBackoffWithContext (ctx , wait.Backoff {Steps : maxRetriesOnConflict , Duration : maxDuration }, func (ctx context.Context ) (bool , error ) {
229+ var err error
230+ if objectFunc != nil {
231+ var obj runtime.Object
232+ obj , err = s .Get (ctx , name , & metav1.GetOptions {})
233+ if err != nil {
234+ return false , fmt .Errorf ("unable to get project: %w" , err )
235+ }
236+ projectObj , ok := obj .(* projectapi.Project )
237+ if ! ok || projectObj == nil {
238+ return false , fmt .Errorf ("not a project: %#v" , obj )
239+ }
240+
241+ // Make sure the object hasn't changed between Get and Delete - pass UID and RV to delete options
242+ if opts .Preconditions == nil {
243+ opts .Preconditions = & metav1.Preconditions {}
244+ }
245+ opts .Preconditions .UID = & projectObj .UID
246+ opts .Preconditions .ResourceVersion = & projectObj .ResourceVersion
247+
248+ if err := objectFunc (ctx , obj ); err != nil {
249+ return false , fmt .Errorf ("validation func failed: %w" , err )
250+ }
225251 }
226-
227- // Make sure the object hasn't changed between Get and Delete - pass UID and RV to delete options
228- if opts .Preconditions == nil {
229- opts .Preconditions = & metav1.Preconditions {}
230- }
231- opts .Preconditions .UID = & projectObj .UID
232- opts .Preconditions .ResourceVersion = & projectObj .ResourceVersion
233-
234- if err := objectFunc (ctx , obj ); err != nil {
235- return nil , false , err
252+ err = s .client .Delete (ctx , name , opts )
253+ switch {
254+ case err == nil :
255+ return true , nil
256+ case kerrors .IsConflict (err ):
257+ lastErr = err
258+ return false , nil
259+ default :
260+ return false , err
236261 }
262+ })
263+ if wait .ErrorInterrupted (err ) != nil {
264+ return & metav1.Status {Status : metav1 .StatusFailure }, false , lastErr
237265 }
238- return & metav1.Status {Status : metav1 .StatusSuccess }, false , s . client . Delete ( ctx , name , opts )
266+ return & metav1.Status {Status : metav1 .StatusSuccess }, false , nil
239267}
0 commit comments