blob: 947418f9a40008a34574f6c9b002bee81a44c04d [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gcsutil
import (
"context"
"errors"
"time"
"cloud.google.com/go/storage"
"go.fuchsia.dev/fuchsia/tools/lib/retry"
)
// Retry wraps a function that makes a GCS API call, adding retries for failures
// that might be transient.
func Retry(ctx context.Context, f func() error) error {
const (
initialWait = time.Second
backoff = 2
maxAttempts = 5
)
retryStrategy := retry.WithMaxAttempts(
retry.NewExponentialBackoff(initialWait, 0, backoff),
maxAttempts)
return retryWithStrategy(ctx, retryStrategy, f)
}
// Extracted to allow dependency injection for testing.
func retryWithStrategy(ctx context.Context, strategy retry.Backoff, f func() error) error {
return retry.Retry(ctx, strategy, func() error {
if err := f(); err != nil {
if errors.Is(err, storage.ErrBucketNotExist) || errors.Is(err, storage.ErrObjectNotExist) {
return retry.Fatal(err)
}
return err
}
return nil
}, nil)
}
// ObjectAttrs gets the attributes for the given object, with retries.
func ObjectAttrs(ctx context.Context, obj *storage.ObjectHandle) (*storage.ObjectAttrs, error) {
var objAttrs *storage.ObjectAttrs
err := Retry(ctx, func() error {
var err error
objAttrs, err = obj.Attrs(ctx)
return err
})
return objAttrs, err
}
// NewObjectReader gets a reader for the given object, with retries.
func NewObjectReader(ctx context.Context, obj *storage.ObjectHandle) (*storage.Reader, error) {
var reader *storage.Reader
err := Retry(ctx, func() error {
var err error
reader, err = obj.NewReader(ctx)
return err
})
return reader, err
}