@@ -2,18 +2,22 @@ package proxy
2
2
3
3
import (
4
4
"context"
5
+ "errors"
6
+ "fmt"
5
7
"strings"
6
8
"testing"
7
9
8
10
corev1 "k8s.io/api/core/v1"
9
- "k8s.io/apimachinery/pkg/api/errors"
11
+ kerrors "k8s.io/apimachinery/pkg/api/errors"
10
12
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11
13
"k8s.io/apimachinery/pkg/labels"
12
14
"k8s.io/apimachinery/pkg/runtime"
15
+ ktypes "k8s.io/apimachinery/pkg/types"
13
16
"k8s.io/apiserver/pkg/authentication/user"
14
17
apirequest "k8s.io/apiserver/pkg/endpoints/request"
15
18
"k8s.io/apiserver/pkg/registry/rest"
16
19
"k8s.io/client-go/kubernetes/fake"
20
+ ktesting "k8s.io/client-go/testing"
17
21
18
22
oapi "github.com/openshift/openshift-apiserver/pkg/api"
19
23
projectapi "github.com/openshift/openshift-apiserver/pkg/project/apis/project"
@@ -81,7 +85,7 @@ func TestCreateInvalidProject(t *testing.T) {
81
85
Annotations : map [string ]string {oapi .OpenShiftDisplayName : "h\t \n i" },
82
86
},
83
87
}, rest .ValidateAllObjectFunc , & metav1.CreateOptions {})
84
- if ! errors .IsInvalid (err ) {
88
+ if ! kerrors .IsInvalid (err ) {
85
89
t .Errorf ("Expected 'invalid' error, got %v" , err )
86
90
}
87
91
}
@@ -182,3 +186,169 @@ func TestDeleteProjectValidation(t *testing.T) {
182
186
t .Errorf ("Expected validation function to be called" )
183
187
}
184
188
}
189
+
190
+ func TestDeleteProjectValidationRetries (t * testing.T ) {
191
+ mockClient := & fake.Clientset {}
192
+ storage := REST {
193
+ client : mockClient .CoreV1 ().Namespaces (),
194
+ }
195
+ maxRetries := 3
196
+ validationRetries := 0
197
+ validationFunc := func (ctx context.Context , obj runtime.Object ) error {
198
+ if validationRetries < maxRetries {
199
+ validationRetries ++
200
+ return fmt .Errorf ("not there yet" )
201
+ }
202
+ return nil
203
+ }
204
+ obj , _ , err := storage .Delete (apirequest .NewContext (), "foo" , validationFunc , & metav1.DeleteOptions {})
205
+ if obj == nil {
206
+ t .Error ("Unexpected nil obj" )
207
+ }
208
+ if err != nil {
209
+ t .Errorf ("Unexpected non-nil error: %#v" , err )
210
+ }
211
+ status , ok := obj .(* metav1.Status )
212
+ if ! ok {
213
+ t .Errorf ("Expected status type, got: %#v" , obj )
214
+ }
215
+ if status .Status != metav1 .StatusSuccess {
216
+ t .Errorf ("Expected status=success, got: %#v" , status )
217
+ }
218
+ if len (mockClient .Actions ()) != maxRetries + 2 {
219
+ t .Errorf ("Expected client action for get, got %v" , mockClient .Actions ())
220
+ }
221
+ for i := range len (mockClient .Actions ()) - 1 {
222
+ if ! mockClient .Actions ()[i ].Matches ("get" , "namespaces" ) {
223
+ t .Errorf ("Expected call #%d to get-namespace, got %#v" , i , mockClient .Actions ()[i ])
224
+ }
225
+ }
226
+ if ! mockClient .Actions ()[len (mockClient .Actions ())- 1 ].Matches ("delete" , "namespaces" ) {
227
+ t .Errorf ("Expected call #%d to delete-namespace, got %#v" , len (mockClient .Actions ())- 1 , mockClient .Actions ()[len (mockClient .Actions ())- 1 ])
228
+ }
229
+ if validationRetries != maxRetries {
230
+ t .Errorf ("Expected validation function to be retried %d times, got %d" , maxRetries , validationRetries )
231
+ }
232
+ }
233
+
234
+ func TestDeleteProjectValidationError (t * testing.T ) {
235
+ mockClient := & fake.Clientset {}
236
+ storage := REST {
237
+ client : mockClient .CoreV1 ().Namespaces (),
238
+ }
239
+ validationError := fmt .Errorf ("faulty function" )
240
+
241
+ validationFunc := func (ctx context.Context , obj runtime.Object ) error {
242
+ return validationError
243
+ }
244
+ obj , _ , err := storage .Delete (apirequest .NewContext (), "foo" , validationFunc , & metav1.DeleteOptions {})
245
+ if obj == nil {
246
+ t .Error ("Unexpected nil obj" )
247
+ }
248
+ status , ok := obj .(* metav1.Status )
249
+ if ! ok {
250
+ t .Errorf ("Expected status type, got: %#v" , obj )
251
+ }
252
+ if status .Status != metav1 .StatusFailure {
253
+ t .Errorf ("Expected status=failure, got: %#v" , status )
254
+ }
255
+ if err == nil {
256
+ t .Errorf ("Expected error but got nil" )
257
+ }
258
+ if errors .Unwrap (err ) != validationError {
259
+ t .Errorf ("Unexpected error: %#v" , errors .Unwrap (err ))
260
+ }
261
+ }
262
+
263
+ func TestDeleteProjectValidationPreconditionUID (t * testing.T ) {
264
+ uid := ktypes .UID ("first-uid" )
265
+ resourceVersion := "10"
266
+ meta := metav1.ObjectMeta {Name : "foo" , UID : uid , ResourceVersion : resourceVersion }
267
+ namespaceList := corev1.NamespaceList {
268
+ Items : []corev1.Namespace {
269
+ {
270
+ ObjectMeta : meta ,
271
+ },
272
+ },
273
+ }
274
+
275
+ tests := []struct {
276
+ testName string
277
+ uid , resourceVersion string
278
+ }{
279
+ {
280
+ testName : "all unset" ,
281
+ },
282
+ {
283
+ testName : "uid set" ,
284
+ uid : "first-uid" ,
285
+ },
286
+ {
287
+ testName : "resourceVersion set" ,
288
+ resourceVersion : "10" ,
289
+ },
290
+ {
291
+ testName : "both set" ,
292
+ uid : "first-uid" ,
293
+ resourceVersion : "10" ,
294
+ },
295
+ }
296
+ for _ , test := range tests {
297
+ t .Run (test .testName , func (t * testing.T ) {
298
+ mockClient := fake .NewSimpleClientset (& namespaceList )
299
+ storage := REST {
300
+ client : mockClient .CoreV1 ().Namespaces (),
301
+ }
302
+ validationFunc := func (ctx context.Context , obj runtime.Object ) error {
303
+ return nil
304
+ }
305
+
306
+ expectedUID := uid
307
+ expectedRV := resourceVersion
308
+ deleteOpts := & metav1.DeleteOptions {}
309
+ if len (test .uid ) > 0 {
310
+ expectedUID = ktypes .UID (test .uid )
311
+ if deleteOpts .Preconditions == nil {
312
+ deleteOpts .Preconditions = & metav1.Preconditions {}
313
+ }
314
+ deleteOpts .Preconditions .UID = & expectedUID
315
+ }
316
+ if len (test .resourceVersion ) > 0 {
317
+ expectedRV = test .resourceVersion
318
+ if deleteOpts .Preconditions == nil {
319
+ deleteOpts .Preconditions = & metav1.Preconditions {}
320
+ }
321
+ deleteOpts .Preconditions .ResourceVersion = & expectedRV
322
+ }
323
+
324
+ storage .Delete (apirequest .NewContext (), "foo" , validationFunc , deleteOpts )
325
+ if len (mockClient .Actions ()) != 2 {
326
+ t .Errorf ("Expected client action for get and delete, got %v" , mockClient .Actions ())
327
+ }
328
+ if ! mockClient .Actions ()[0 ].Matches ("get" , "namespaces" ) {
329
+ t .Errorf ("Expected call to get-namespace, got %#v" , mockClient .Actions ()[0 ])
330
+ }
331
+ lastAction := mockClient .Actions ()[1 ]
332
+ if ! lastAction .Matches ("delete" , "namespaces" ) {
333
+ t .Errorf ("Expected call to delete-namespace, got %#v" , mockClient .Actions ()[1 ])
334
+ }
335
+ deleteAction := lastAction .(ktesting.DeleteActionImpl )
336
+ preconditions := deleteAction .DeleteOptions .Preconditions
337
+ if preconditions == nil {
338
+ t .Fatalf ("Expected DeleteOptions precondition to be non-nil" )
339
+ }
340
+ if preconditions .UID == nil {
341
+ t .Fatalf ("Expected DeleteOptions precondition UID to be non-nil" )
342
+ }
343
+ if * preconditions .UID != expectedUID {
344
+ t .Errorf ("Expected DeleteOptions precondition UID to %#v, got %#v" , expectedUID , * preconditions .UID )
345
+ }
346
+ if preconditions .ResourceVersion == nil {
347
+ t .Fatalf ("Expected DeleteOptions precondition ResourceVersion to be non-nil" )
348
+ }
349
+ if * preconditions .ResourceVersion != expectedRV {
350
+ t .Errorf ("Expected DeleteOptions precondition ResourceVersion to %#v, got %#v" , expectedRV , * preconditions .ResourceVersion )
351
+ }
352
+ })
353
+ }
354
+ }
0 commit comments