@@ -2,18 +2,22 @@ package proxy
22
33import (
44 "context"
5+ "errors"
6+ "fmt"
57 "strings"
68 "testing"
79
810 corev1 "k8s.io/api/core/v1"
9- "k8s.io/apimachinery/pkg/api/errors"
11+ kerrors "k8s.io/apimachinery/pkg/api/errors"
1012 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113 "k8s.io/apimachinery/pkg/labels"
1214 "k8s.io/apimachinery/pkg/runtime"
15+ ktypes "k8s.io/apimachinery/pkg/types"
1316 "k8s.io/apiserver/pkg/authentication/user"
1417 apirequest "k8s.io/apiserver/pkg/endpoints/request"
1518 "k8s.io/apiserver/pkg/registry/rest"
1619 "k8s.io/client-go/kubernetes/fake"
20+ ktesting "k8s.io/client-go/testing"
1721
1822 oapi "github.com/openshift/openshift-apiserver/pkg/api"
1923 projectapi "github.com/openshift/openshift-apiserver/pkg/project/apis/project"
@@ -81,7 +85,7 @@ func TestCreateInvalidProject(t *testing.T) {
8185 Annotations : map [string ]string {oapi .OpenShiftDisplayName : "h\t \n i" },
8286 },
8387 }, rest .ValidateAllObjectFunc , & metav1.CreateOptions {})
84- if ! errors .IsInvalid (err ) {
88+ if ! kerrors .IsInvalid (err ) {
8589 t .Errorf ("Expected 'invalid' error, got %v" , err )
8690 }
8791}
@@ -182,3 +186,169 @@ func TestDeleteProjectValidation(t *testing.T) {
182186 t .Errorf ("Expected validation function to be called" )
183187 }
184188}
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