@@ -13,7 +13,6 @@ import (
1313 "time"
1414
1515 "github.com/sirupsen/logrus"
16- logrustest "github.com/sirupsen/logrus/hooks/test"
1716 "github.com/stretchr/testify/assert"
1817 "github.com/stretchr/testify/require"
1918 "go.uber.org/mock/gomock"
@@ -30,6 +29,7 @@ import (
3029 "github.com/Azure/msi-dataplane/pkg/dataplane"
3130
3231 "github.com/Azure/ARO-RP/pkg/api"
32+ "github.com/Azure/ARO-RP/pkg/util/arm"
3333 mock_armmsi "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/azuresdk/armmsi"
3434 mock_armnetwork "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/azuresdk/armnetwork"
3535 mock_azsecrets "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/azuresdk/azsecrets"
@@ -407,135 +407,61 @@ func TestRetryableDelete(t *testing.T) {
407407 for _ , tt := range []struct {
408408 name string
409409 err error
410- wantErr bool
410+ wantErr error
411411 }{
412412 {
413413 name : "404 from inner f() is swallowed and nil returned" ,
414414 err : autorest.DetailedError {StatusCode : http .StatusNotFound },
415- wantErr : false ,
415+ wantErr : nil ,
416416 },
417417 {
418418 name : "non-404 error propagates unchanged" ,
419419 err : autorest.DetailedError {StatusCode : http .StatusConflict },
420- wantErr : true ,
420+ wantErr : autorest. DetailedError { StatusCode : http . StatusConflict } ,
421421 },
422422 {
423423 name : "nil error propagates unchanged" ,
424424 err : nil ,
425- wantErr : false ,
425+ wantErr : nil ,
426426 },
427427 } {
428428 t .Run (tt .name , func (t * testing.T ) {
429- origBackoff := transientRetryBackoff
430- transientRetryBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond }
431- defer func () { transientRetryBackoff = origBackoff }()
429+ origBackoff := arm . TransientBackoff
430+ arm . TransientBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond }
431+ defer func () { arm . TransientBackoff = origBackoff }()
432432
433- m := & manager { log : logrus .NewEntry (logrus .StandardLogger ())}
434- err := m . retryableDelete ( "test-op" , func () error {
433+ log := logrus .NewEntry (logrus .StandardLogger ())
434+ err := arm . RetryableDelete ( func () error {
435435 return tt .err
436- })
437- if tt .wantErr {
438- assert .Error (t , err )
439- } else {
440- assert .NoError (t , err )
441- }
436+ }, log , "test-op" )
437+ assert .Equal (t , tt .wantErr , err )
442438 })
443439 }
444440}
445441
446442func TestRetryableDeleteRetryPath (t * testing.T ) {
447- origBackoff := transientRetryBackoff
448- transientRetryBackoff = wait.Backoff {Steps : 2 , Duration : time .Millisecond , Factor : 2.0 }
449- defer func () { transientRetryBackoff = origBackoff }()
443+ origBackoff := arm . TransientBackoff
444+ arm . TransientBackoff = wait.Backoff {Steps : 2 , Duration : time .Millisecond , Factor : 2.0 }
445+ defer func () { arm . TransientBackoff = origBackoff }()
450446
451447 calls := 0
452- m := & manager { log : logrus .NewEntry (logrus .StandardLogger ())}
453- err := m . retryableDelete ( "test-retry-op" , func () error {
448+ log := logrus .NewEntry (logrus .StandardLogger ())
449+ err := arm . RetryableDelete ( func () error {
454450 calls ++
455451 if calls == 1 {
456452 return autorest.DetailedError {StatusCode : http .StatusTooManyRequests }
457453 }
458454 return nil
459- })
455+ }, log , "test-retry-op" )
460456 require .NoError (t , err )
461457 assert .Equal (t , 2 , calls , "expected inner function to be called twice: once for transient error, once for success" )
462458}
463459
464- func TestIsRetryable (t * testing.T ) {
465- for _ , tt := range []struct {
466- name string
467- err error
468- wantRetry bool
469- wantLogMsg string
470- }{
471- {
472- name : "retryable 429 returns true and logs" ,
473- err : autorest.DetailedError {StatusCode : http .StatusTooManyRequests },
474- wantRetry : true ,
475- wantLogMsg : "error on test-op, retrying:" ,
476- },
477- {
478- name : "non-retryable error returns false" ,
479- err : errors .New ("permanent failure" ),
480- wantRetry : false ,
481- },
482- {
483- name : "retryable azcore 429 returns true and logs" ,
484- err : & azcore.ResponseError {StatusCode : http .StatusTooManyRequests },
485- wantRetry : true ,
486- wantLogMsg : "error on test-op, retrying:" ,
487- },
488- {
489- name : "retryable autorest 409+Retry-After returns true and logs" ,
490- err : autorest.DetailedError {
491- StatusCode : http .StatusConflict ,
492- Response : & http.Response {
493- StatusCode : http .StatusConflict ,
494- Header : http.Header {"Retry-After" : []string {"1" }},
495- },
496- },
497- wantRetry : true ,
498- wantLogMsg : "error on test-op, retrying:" ,
499- },
500- {
501- name : "retryable azcore 409+Retry-After returns true and logs" ,
502- err : & azcore.ResponseError {
503- StatusCode : http .StatusConflict ,
504- RawResponse : & http.Response {
505- StatusCode : http .StatusConflict ,
506- Header : http.Header {"Retry-After" : []string {"1" }},
507- },
508- },
509- wantRetry : true ,
510- wantLogMsg : "error on test-op, retrying:" ,
511- },
512- } {
513- t .Run (tt .name , func (t * testing.T ) {
514- logger , hook := logrustest .NewNullLogger ()
515- logger .SetLevel (logrus .WarnLevel )
516-
517- m := & manager {log : logrus .NewEntry (logger )}
518- predicate := m .isRetryable ("test-op" )
519-
520- got := predicate (tt .err )
521- assert .Equal (t , tt .wantRetry , got )
522-
523- if tt .wantLogMsg != "" {
524- require .Len (t , hook .Entries , 1 )
525- assert .Contains (t , hook .LastEntry ().Message , tt .wantLogMsg )
526- assert .Equal (t , logrus .WarnLevel , hook .LastEntry ().Level )
527- } else {
528- assert .Empty (t , hook .Entries )
529- }
530- })
531- }
532- }
533-
534460// TestDisconnectSecurityGroupRetryExhausted verifies that when the azcore subnet call fails, the error is propagated.
535461func TestDisconnectSecurityGroupRetryExhausted (t * testing.T ) {
536- origBackoff := transientRetryBackoff
537- transientRetryBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond , Factor : 2.0 } // Steps: 1 = 1 attempt, 0 retries
538- defer func () { transientRetryBackoff = origBackoff }()
462+ origBackoff := arm . TransientBackoff
463+ arm . TransientBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond , Factor : 2.0 } // Steps: 1 = 1 attempt, 0 retries
464+ defer func () { arm . TransientBackoff = origBackoff }()
539465
540466 subscription := "00000000-0000-0000-0000-000000000000"
541467 resourceGroup := "test-rg"
@@ -1224,9 +1150,9 @@ func TestDeleteResourcesRetry(t *testing.T) {
12241150 },
12251151 } {
12261152 t .Run (tt .name , func (t * testing.T ) {
1227- origBackoff := transientRetryBackoff
1228- transientRetryBackoff = wait.Backoff {Steps : 2 , Duration : time .Millisecond , Factor : 2.0 }
1229- defer func () { transientRetryBackoff = origBackoff }()
1153+ origBackoff := arm . TransientBackoff
1154+ arm . TransientBackoff = wait.Backoff {Steps : 2 , Duration : time .Millisecond , Factor : 2.0 }
1155+ defer func () { arm . TransientBackoff = origBackoff }()
12301156
12311157 controller := gomock .NewController (t )
12321158 defer controller .Finish ()
@@ -1270,9 +1196,9 @@ func TestDeleteResourcesRetry(t *testing.T) {
12701196// TestDeleteResourcesRetryExhausted verifies that retry exhaustion propagates the error.
12711197// Uses a single representative error (autorest 429); the exhaustion path is the same for all retryable errors.
12721198func TestDeleteResourcesRetryExhausted (t * testing.T ) {
1273- origBackoff := transientRetryBackoff
1274- transientRetryBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond , Factor : 2.0 } // Steps: 1 = 1 attempt, 0 retries
1275- defer func () { transientRetryBackoff = origBackoff }()
1199+ origBackoff := arm . TransientBackoff
1200+ arm . TransientBackoff = wait.Backoff {Steps : 1 , Duration : time .Millisecond , Factor : 2.0 } // Steps: 1 = 1 attempt, 0 retries
1201+ defer func () { arm . TransientBackoff = origBackoff }()
12761202
12771203 subscription := "00000000-0000-0000-0000-000000000000"
12781204 resourceGroup := "test-rg"
0 commit comments