Skip to content

Commit f198452

Browse files
authored
fix(storage): retry SignBlob call for URL signing (#11154)
* fix(storage): retry SignBlob call for URL signing Adds a retry to the SignBlob call made by the default SignBytes function from BucketHandle.SignedURL(). This is an idempotent call so fully safe to retry. Signed URL integration tests pass locally. * fmt * add test with mock transport
1 parent a75c8b0 commit f198452

2 files changed

Lines changed: 36 additions & 5 deletions

File tree

‎storage/bucket.go‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,14 @@ func (b *BucketHandle) defaultSignBytesFunc(email string) func([]byte) ([]byte,
326326
if err != nil {
327327
return nil, fmt.Errorf("unable to create iamcredentials client: %w", err)
328328
}
329-
330-
resp, err := svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{
331-
Payload: base64.StdEncoding.EncodeToString(in),
332-
}).Do()
333-
if err != nil {
329+
// Do the SignBlob call with a retry for transient errors.
330+
var resp *iamcredentials.SignBlobResponse
331+
if err := run(ctx, func(ctx context.Context) error {
332+
resp, err = svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{
333+
Payload: base64.StdEncoding.EncodeToString(in),
334+
}).Do()
335+
return err
336+
}, b.retry, true); err != nil {
334337
return nil, fmt.Errorf("unable to sign bytes: %w", err)
335338
}
336339
out, err := base64.StdEncoding.DecodeString(resp.SignedBlob)

‎storage/bucket_test.go‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package storage
1717
import (
1818
"context"
1919
"fmt"
20+
"net/http"
2021
"testing"
2122
"time"
2223

@@ -1640,3 +1641,30 @@ func TestBucketSignedURL_Endpoint_Emulator_Host(t *testing.T) {
16401641
})
16411642
}
16421643
}
1644+
1645+
// Test retry logic for default SignBlob function used by BucketHandle.SignedURL.
1646+
// This cannot be tested via the emulator so we use a mock.
1647+
func TestDefaultSignBlobRetry(t *testing.T) {
1648+
ctx := context.Background()
1649+
1650+
// Use mock transport. Return 2 503 responses before succeeding.
1651+
mt := mockTransport{}
1652+
mt.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil)
1653+
mt.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil)
1654+
mt.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil)
1655+
1656+
client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &mt}))
1657+
if err != nil {
1658+
t.Fatalf("NewClient: %v", err)
1659+
}
1660+
1661+
b := client.Bucket("fakebucket")
1662+
1663+
if _, err := b.SignedURL("fakeobj", &SignedURLOptions{
1664+
Method: "GET",
1665+
Expires: time.Now().Add(time.Hour),
1666+
SignBytes: b.defaultSignBytesFunc("example@example.com"),
1667+
}); err != nil {
1668+
t.Fatalf("BucketHandle.SignedURL: %v", err)
1669+
}
1670+
}

0 commit comments

Comments
 (0)