@@ -3,11 +3,13 @@ package proxy
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "time"
6
7
7
8
kerrors "k8s.io/apimachinery/pkg/api/errors"
8
9
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
9
10
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10
11
"k8s.io/apimachinery/pkg/runtime"
12
+ "k8s.io/apimachinery/pkg/util/wait"
11
13
"k8s.io/apimachinery/pkg/watch"
12
14
apirequest "k8s.io/apiserver/pkg/endpoints/request"
13
15
"k8s.io/apiserver/pkg/registry/rest"
@@ -208,32 +210,58 @@ func (s *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
208
210
209
211
var _ = rest .GracefulDeleter (& REST {})
210
212
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
+
211
221
// Delete deletes a Project specified by its name
212
222
func (s * REST ) Delete (ctx context.Context , name string , objectFunc rest.ValidateObjectFunc , options * metav1.DeleteOptions ) (runtime.Object , bool , error ) {
213
223
var opts metav1.DeleteOptions
214
224
if options != nil {
215
225
opts = * options
216
226
}
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
+ }
225
251
}
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
236
261
}
262
+ })
263
+ if wait .ErrorInterrupted (err ) != nil {
264
+ return & metav1.Status {Status : metav1 .StatusFailure }, false , lastErr
237
265
}
238
- return & metav1.Status {Status : metav1 .StatusSuccess }, false , s . client . Delete ( ctx , name , opts )
266
+ return & metav1.Status {Status : metav1 .StatusSuccess }, false , nil
239
267
}
0 commit comments