@@ -46,15 +46,15 @@ func newTestSyncCtx(getResourceFunc *func(ctx context.Context, config *rest.Conf
46
46
& metav1.APIResourceList {
47
47
GroupVersion : "v1" ,
48
48
APIResources : []metav1.APIResource {
49
- {Kind : "Pod" , Group : "" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
50
- {Kind : "Service" , Group : "" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
51
- {Kind : "Namespace" , Group : "" , Version : "v1" , Namespaced : false , Verbs : standardVerbs },
49
+ {Name : "pods" , Kind : "Pod" , Group : "" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
50
+ {Name : "services" , Kind : "Service" , Group : "" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
51
+ {Name : "namespaces" , Kind : "Namespace" , Group : "" , Version : "v1" , Namespaced : false , Verbs : standardVerbs },
52
52
},
53
53
},
54
54
& metav1.APIResourceList {
55
55
GroupVersion : "apps/v1" ,
56
56
APIResources : []metav1.APIResource {
57
- {Kind : "Deployment" , Group : "apps" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
57
+ {Name : "deployments" , Kind : "Deployment" , Group : "apps" , Version : "v1" , Namespaced : true , Verbs : standardVerbs },
58
58
},
59
59
})
60
60
sc := syncContext {
@@ -854,6 +854,39 @@ func withReplaceAndServerSideApplyAnnotations(un *unstructured.Unstructured) *un
854
854
return un
855
855
}
856
856
857
+ func TestSync_HookWithReplaceAndBeforeHookCreation_AlreadyDeleted (t * testing.T ) {
858
+ // This test a race condition when Delete is called on an already deleted object
859
+ // LiveObj is set, but then the resource is deleted asynchronously in kubernetes
860
+ syncCtx := newTestSyncCtx (nil )
861
+
862
+ target := withReplaceAnnotation (testingutils .NewPod ())
863
+ target .SetNamespace (testingutils .FakeArgoCDNamespace )
864
+ target = testingutils .Annotate (target , synccommon .AnnotationKeyHookDeletePolicy , string (synccommon .HookDeletePolicyBeforeHookCreation ))
865
+ target = testingutils .Annotate (target , synccommon .AnnotationKeyHook , string (synccommon .SyncPhasePreSync ))
866
+ live := target .DeepCopy ()
867
+
868
+ syncCtx .resources = groupResources (ReconciliationResult {
869
+ Live : []* unstructured.Unstructured {live },
870
+ Target : []* unstructured.Unstructured {target },
871
+ })
872
+ syncCtx .hooks = []* unstructured.Unstructured {live }
873
+
874
+ client := fake .NewSimpleDynamicClient (runtime .NewScheme ())
875
+ deleted := false
876
+ client .PrependReactor ("delete" , "pods" , func (_ testcore.Action ) (bool , runtime.Object , error ) {
877
+ deleted = true
878
+ // simulate the race conditions where liveObj was not null, but is now deleted in k8s
879
+ return true , nil , apierrors .NewNotFound (corev1 .Resource ("pods" ), live .GetName ())
880
+ })
881
+ syncCtx .dynamicIf = client
882
+
883
+ syncCtx .Sync ()
884
+
885
+ resourceOps , _ := syncCtx .resourceOps .(* kubetest.MockResourceOps )
886
+ assert .Equal (t , "create" , resourceOps .GetLastResourceCommand (kube .GetResourceKey (target )))
887
+ assert .True (t , deleted )
888
+ }
889
+
857
890
func TestSync_ServerSideApply (t * testing.T ) {
858
891
testCases := []struct {
859
892
name string
@@ -1285,22 +1318,84 @@ func TestSyncFailureHookWithFailedSync(t *testing.T) {
1285
1318
}
1286
1319
1287
1320
func TestBeforeHookCreation (t * testing.T ) {
1321
+ finalizerRemoved := false
1288
1322
syncCtx := newTestSyncCtx (nil )
1289
- hook := testingutils .Annotate (testingutils .Annotate (testingutils .NewPod (), synccommon .AnnotationKeyHook , "Sync" ), synccommon .AnnotationKeyHookDeletePolicy , "BeforeHookCreation" )
1290
- hook .SetNamespace (testingutils .FakeArgoCDNamespace )
1323
+ hookObj := testingutils .Annotate (testingutils .Annotate (testingutils .NewPod (), synccommon .AnnotationKeyHook , "Sync" ), synccommon .AnnotationKeyHookDeletePolicy , "BeforeHookCreation" )
1324
+ hookObj .SetFinalizers ([]string {hook .HookFinalizer })
1325
+ hookObj .SetNamespace (testingutils .FakeArgoCDNamespace )
1291
1326
syncCtx .resources = groupResources (ReconciliationResult {
1292
- Live : []* unstructured.Unstructured {hook },
1327
+ Live : []* unstructured.Unstructured {hookObj },
1293
1328
Target : []* unstructured.Unstructured {nil },
1294
1329
})
1295
- syncCtx .hooks = []* unstructured.Unstructured {hook }
1296
- syncCtx .dynamicIf = fake .NewSimpleDynamicClient (runtime .NewScheme ())
1330
+ syncCtx .hooks = []* unstructured.Unstructured {hookObj }
1331
+ client := fake .NewSimpleDynamicClient (runtime .NewScheme (), hookObj )
1332
+ client .PrependReactor ("update" , "pods" , func (_ testcore.Action ) (bool , runtime.Object , error ) {
1333
+ finalizerRemoved = true
1334
+ return false , nil , nil
1335
+ })
1336
+ syncCtx .dynamicIf = client
1297
1337
1338
+ // First sync will delete the existing hook
1298
1339
syncCtx .Sync ()
1340
+ phase , _ , _ := syncCtx .GetState ()
1341
+ assert .Equal (t , synccommon .OperationRunning , phase )
1342
+ assert .True (t , finalizerRemoved )
1299
1343
1300
- _ , _ , resources := syncCtx .GetState ()
1344
+ // Second sync will create the hook
1345
+ syncCtx .Sync ()
1346
+ phase , message , resources := syncCtx .GetState ()
1347
+ assert .Equal (t , synccommon .OperationRunning , phase )
1301
1348
assert .Len (t , resources , 1 )
1302
- assert .Empty (t , resources [0 ].Message )
1303
- assert .Equal (t , "waiting for completion of hook /Pod/my-pod" , syncCtx .message )
1349
+ assert .Equal (t , synccommon .OperationRunning , resources [0 ].HookPhase )
1350
+ assert .Equal (t , "waiting for completion of hook /Pod/my-pod" , message )
1351
+ }
1352
+
1353
+ func TestSync_ExistingHooksWithFinalizer (t * testing.T ) {
1354
+ newHook := func (name string , hookType synccommon.HookType , deletePolicy synccommon.HookDeletePolicy ) * unstructured.Unstructured {
1355
+ obj := testingutils .NewPod ()
1356
+ obj .SetName (name )
1357
+ obj .SetNamespace (testingutils .FakeArgoCDNamespace )
1358
+ testingutils .Annotate (obj , synccommon .AnnotationKeyHook , string (hookType ))
1359
+ testingutils .Annotate (obj , synccommon .AnnotationKeyHookDeletePolicy , string (deletePolicy ))
1360
+ obj .SetFinalizers ([]string {hook .HookFinalizer })
1361
+ return obj
1362
+ }
1363
+
1364
+ hook1 := newHook ("existing-hook-1" , synccommon .HookTypePreSync , synccommon .HookDeletePolicyBeforeHookCreation )
1365
+ hook2 := newHook ("existing-hook-2" , synccommon .HookTypePreSync , synccommon .HookDeletePolicyHookFailed )
1366
+ hook3 := newHook ("existing-hook-3" , synccommon .HookTypePreSync , synccommon .HookDeletePolicyHookSucceeded )
1367
+
1368
+ syncCtx := newTestSyncCtx (nil )
1369
+ fakeDynamicClient := fake .NewSimpleDynamicClient (runtime .NewScheme (), hook1 , hook2 , hook3 )
1370
+ syncCtx .dynamicIf = fakeDynamicClient
1371
+ updatedCount := 0
1372
+ fakeDynamicClient .PrependReactor ("update" , "*" , func (_ testcore.Action ) (handled bool , ret runtime.Object , err error ) {
1373
+ // Removing the finalizers
1374
+ updatedCount ++
1375
+ return false , nil , nil
1376
+ })
1377
+ deletedCount := 0
1378
+ fakeDynamicClient .PrependReactor ("delete" , "*" , func (_ testcore.Action ) (handled bool , ret runtime.Object , err error ) {
1379
+ // because of HookDeletePolicyBeforeHookCreation
1380
+ deletedCount ++
1381
+ return false , nil , nil
1382
+ })
1383
+ syncCtx .resources = groupResources (ReconciliationResult {
1384
+ Live : []* unstructured.Unstructured {hook1 , hook2 , hook3 },
1385
+ Target : []* unstructured.Unstructured {nil , nil , nil },
1386
+ })
1387
+ syncCtx .hooks = []* unstructured.Unstructured {hook1 , hook2 , hook3 }
1388
+
1389
+ syncCtx .Sync ()
1390
+ phase , _ , _ := syncCtx .GetState ()
1391
+
1392
+ assert .Equal (t , synccommon .OperationRunning , phase )
1393
+ assert .Equal (t , 3 , updatedCount )
1394
+ assert .Equal (t , 1 , deletedCount )
1395
+
1396
+ _ , err := syncCtx .getResource (& syncTask {liveObj : hook1 })
1397
+ require .Error (t , err , "Expected resource to be deleted" )
1398
+ assert .True (t , apierrors .IsNotFound (err ))
1304
1399
}
1305
1400
1306
1401
func TestRunSyncFailHooksFailed (t * testing.T ) {
0 commit comments