diff --git a/storage/bucket.go b/storage/bucket.go index 3eded017831e..5601357c9b2b 100644 --- a/storage/bucket.go +++ b/storage/bucket.go @@ -326,11 +326,14 @@ func (b *BucketHandle) defaultSignBytesFunc(email string) func([]byte) ([]byte, if err != nil { return nil, fmt.Errorf("unable to create iamcredentials client: %w", err) } - - resp, err := svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{ - Payload: base64.StdEncoding.EncodeToString(in), - }).Do() - if err != nil { + // Do the SignBlob call with a retry for transient errors. + var resp *iamcredentials.SignBlobResponse + if err := run(ctx, func(ctx context.Context) error { + resp, err = svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{ + Payload: base64.StdEncoding.EncodeToString(in), + }).Do() + return err + }, b.retry, true); err != nil { return nil, fmt.Errorf("unable to sign bytes: %w", err) } out, err := base64.StdEncoding.DecodeString(resp.SignedBlob) diff --git a/storage/bucket_test.go b/storage/bucket_test.go index 7ae7b06e7e8d..abd117d6e45a 100644 --- a/storage/bucket_test.go +++ b/storage/bucket_test.go @@ -17,6 +17,7 @@ package storage import ( "context" "fmt" + "net/http" "testing" "time" @@ -1640,3 +1641,30 @@ func TestBucketSignedURL_Endpoint_Emulator_Host(t *testing.T) { }) } } + +// Test retry logic for default SignBlob function used by BucketHandle.SignedURL. +// This cannot be tested via the emulator so we use a mock. +func TestDefaultSignBlobRetry(t *testing.T) { + ctx := context.Background() + + // Use mock transport. Return 2 503 responses before succeeding. + mt := mockTransport{} + mt.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil) + mt.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil) + mt.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil) + + client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &mt})) + if err != nil { + t.Fatalf("NewClient: %v", err) + } + + b := client.Bucket("fakebucket") + + if _, err := b.SignedURL("fakeobj", &SignedURLOptions{ + Method: "GET", + Expires: time.Now().Add(time.Hour), + SignBytes: b.defaultSignBytesFunc("example@example.com"), + }); err != nil { + t.Fatalf("BucketHandle.SignedURL: %v", err) + } +}