build(deps): bump cloud.google.com/go/storage from 1.27.0 to 1.30.1

Bumps [cloud.google.com/go/storage](https://github.com/googleapis/google-cloud-go) from 1.27.0 to 1.30.1.
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/pubsub/v1.27.0...pubsub/v1.30.1)

---
updated-dependencies:
- dependency-name: cloud.google.com/go/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2023-05-22 11:35:22 +00:00 committed by Tomáš Hozza
parent cd5cc08ce6
commit 1410a1e6de
21 changed files with 2503 additions and 2365 deletions

View file

@ -1,6 +1,42 @@
# Changes
## [1.30.1](https://github.com/googleapis/google-cloud-go/compare/storage/v1.30.0...storage/v1.30.1) (2023-03-21)
### Bug Fixes
* **storage:** Retract versions with Copier bug ([#7583](https://github.com/googleapis/google-cloud-go/issues/7583)) ([9c10b6f](https://github.com/googleapis/google-cloud-go/commit/9c10b6f8a54cb8447260148b5e4a9b5160281020))
* Versions v1.25.0-v1.27.0 are retracted due to [#6857](https://github.com/googleapis/google-cloud-go/issues/6857).
* **storage:** SignedURL v4 allows headers with colons in value ([#7603](https://github.com/googleapis/google-cloud-go/issues/7603)) ([6b50f9b](https://github.com/googleapis/google-cloud-go/commit/6b50f9b368f5b271ade1706c342865cef46712e6))
## [1.30.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.29.0...storage/v1.30.0) (2023-03-15)
### Features
* **storage/internal:** Update routing annotation for CreateBucketRequest docs: Add support for end-to-end checksumming in the gRPC WriteObject flow feat!: BREAKING CHANGE - renaming Notification to NotificationConfig ([2fef56f](https://github.com/googleapis/google-cloud-go/commit/2fef56f75a63dc4ff6e0eea56c7b26d4831c8e27))
* **storage:** Json downloads ([#7158](https://github.com/googleapis/google-cloud-go/issues/7158)) ([574a86c](https://github.com/googleapis/google-cloud-go/commit/574a86c614445f8c3f5a54446820df774c31cd47))
* **storage:** Update iam and longrunning deps ([91a1f78](https://github.com/googleapis/google-cloud-go/commit/91a1f784a109da70f63b96414bba8a9b4254cddd))
### Bug Fixes
* **storage:** Specify credentials with STORAGE_EMULATOR_HOST ([#7271](https://github.com/googleapis/google-cloud-go/issues/7271)) ([940ae15](https://github.com/googleapis/google-cloud-go/commit/940ae15f725ff384e345e627feb03d22e1fd8db5))
## [1.29.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.28.1...storage/v1.29.0) (2023-01-19)
### Features
* **storage:** Add ComponentCount as part of ObjectAttrs ([#7230](https://github.com/googleapis/google-cloud-go/issues/7230)) ([a19bca6](https://github.com/googleapis/google-cloud-go/commit/a19bca60704b4fbb674cf51d828580aa653c8210))
* **storage:** Add REST client ([06a54a1](https://github.com/googleapis/google-cloud-go/commit/06a54a16a5866cce966547c51e203b9e09a25bc0))
### Documentation
* **storage/internal:** Corrected typos and spellings ([7357077](https://github.com/googleapis/google-cloud-go/commit/735707796d81d7f6f32fc3415800c512fe62297e))
## [1.28.1](https://github.com/googleapis/google-cloud-go/compare/storage/v1.28.0...storage/v1.28.1) (2022-12-02)

View file

@ -35,6 +35,7 @@ import (
raw "google.golang.org/api/storage/v1"
dpb "google.golang.org/genproto/googleapis/type/date"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
)
// BucketHandle provides operations on a Google Cloud Storage bucket.
@ -1389,12 +1390,12 @@ func (rp *RetentionPolicy) toProtoRetentionPolicy() *storagepb.Bucket_RetentionP
}
// RetentionPeriod must be greater than 0, so if it is 0, the user left it
// unset, and so we should not send it in the request i.e. nil is sent.
var period *int64
var dur *durationpb.Duration
if rp.RetentionPeriod != 0 {
period = proto.Int64(int64(rp.RetentionPeriod / time.Second))
dur = durationpb.New(rp.RetentionPeriod)
}
return &storagepb.Bucket_RetentionPolicy{
RetentionPeriod: period,
RetentionDuration: dur,
}
}
@ -1418,7 +1419,7 @@ func toRetentionPolicyFromProto(rp *storagepb.Bucket_RetentionPolicy) *Retention
return nil
}
return &RetentionPolicy{
RetentionPeriod: time.Duration(rp.GetRetentionPeriod()) * time.Second,
RetentionPeriod: rp.GetRetentionDuration().AsDuration(),
EffectiveTime: rp.GetEffectiveTime().AsTime(),
IsLocked: rp.GetIsLocked(),
}

View file

@ -19,9 +19,9 @@ import (
"io"
"time"
"cloud.google.com/go/iam/apiv1/iampb"
gax "github.com/googleapis/gax-go/v2"
"google.golang.org/api/option"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
// TODO(noahdietz): Move existing factory methods to this file.

View file

@ -36,6 +36,9 @@ The client will use your default application credentials. Clients should be
reused instead of created as needed. The methods of [Client] are safe for
concurrent use by multiple goroutines.
You may configure the client by passing in options from the [google.golang.org/api/option]
package. You may also use options defined in this package, such as [WithJSONReads].
If you only wish to access public data, you can create
an unauthenticated client with

View file

@ -17,11 +17,13 @@ package storage
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io"
"net/url"
"os"
"cloud.google.com/go/iam/apiv1/iampb"
"cloud.google.com/go/internal/trace"
gapic "cloud.google.com/go/storage/internal/apiv2"
storagepb "cloud.google.com/go/storage/internal/apiv2/stubs"
@ -29,12 +31,10 @@ import (
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/api/option/internaloption"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
)
@ -111,6 +111,11 @@ func newGRPCStorageClient(ctx context.Context, opts ...storageOption) (storageCl
s := initSettings(opts...)
s.clientOption = append(defaultGRPCOptions(), s.clientOption...)
config := newStorageConfig(s.clientOption...)
if config.readAPIWasSet {
return nil, errors.New("storage: GRPC is incompatible with any option that specifies an API for reads")
}
g, err := gapic.NewClient(ctx, s.clientOption...)
if err != nil {
return nil, err
@ -856,13 +861,6 @@ func (c *grpcStorageClient) NewRangeReader(ctx context.Context, params *newRange
ctx = setUserProjectMetadata(ctx, s.userProject)
}
// A negative length means "read to the end of the object", but the
// read_limit field it corresponds to uses zero to mean the same thing. Thus
// we coerce the length to 0 to read to the end of the object.
if params.length < 0 {
params.length = 0
}
b := bucketResourceName(globalProjectAlias, params.bucket)
req := &storagepb.ReadObjectRequest{
Bucket: b,
@ -885,13 +883,20 @@ func (c *grpcStorageClient) NewRangeReader(ctx context.Context, params *newRange
cc, cancel := context.WithCancel(ctx)
start := params.offset + seen
req.ReadOffset = params.offset + seen
// A negative length means "read to the end of the object", but the
// read_limit field it corresponds to uses zero to mean the same thing. Thus
// we coerce the length to 0 to read to the end of the object.
if params.length < 0 {
params.length = 0
}
// Only set a ReadLimit if length is greater than zero, because zero
// means read it all.
if params.length > 0 {
req.ReadLimit = params.length - seen
}
req.ReadOffset = start
if err := applyCondsProto("gRPCReader.reopen", params.gen, params.conds, req); err != nil {
cancel()
@ -964,7 +969,7 @@ func (c *grpcStorageClient) NewRangeReader(ctx context.Context, params *newRange
cr := msg.GetContentRange()
if cr != nil {
r.Attrs.StartOffset = cr.GetStart()
r.remain = cr.GetEnd() - cr.GetStart() + 1
r.remain = cr.GetEnd() - cr.GetStart()
} else {
r.remain = size
}
@ -1255,12 +1260,12 @@ func (c *grpcStorageClient) ListNotifications(ctx context.Context, bucket string
if s.userProject != "" {
ctx = setUserProjectMetadata(ctx, s.userProject)
}
req := &storagepb.ListNotificationsRequest{
req := &storagepb.ListNotificationConfigsRequest{
Parent: bucketResourceName(globalProjectAlias, bucket),
}
var notifications []*storagepb.Notification
var notifications []*storagepb.NotificationConfig
err = run(ctx, func() error {
gitr := c.raw.ListNotifications(ctx, req, s.gax...)
gitr := c.raw.ListNotificationConfigs(ctx, req, s.gax...)
for {
// PageSize is not set and fallbacks to the API default pageSize of 100.
items, nextPageToken, err := gitr.InternalFetch(int(req.GetPageSize()), req.GetPageToken())
@ -1287,14 +1292,14 @@ func (c *grpcStorageClient) CreateNotification(ctx context.Context, bucket strin
defer func() { trace.EndSpan(ctx, err) }()
s := callSettings(c.settings, opts...)
req := &storagepb.CreateNotificationRequest{
Parent: bucketResourceName(globalProjectAlias, bucket),
Notification: toProtoNotification(n),
req := &storagepb.CreateNotificationConfigRequest{
Parent: bucketResourceName(globalProjectAlias, bucket),
NotificationConfig: toProtoNotification(n),
}
var pbn *storagepb.Notification
var pbn *storagepb.NotificationConfig
err = run(ctx, func() error {
var err error
pbn, err = c.raw.CreateNotification(ctx, req, s.gax...)
pbn, err = c.raw.CreateNotificationConfig(ctx, req, s.gax...)
return err
}, s.retry, s.idempotent, setRetryHeaderGRPC(ctx))
if err != nil {
@ -1308,9 +1313,9 @@ func (c *grpcStorageClient) DeleteNotification(ctx context.Context, bucket strin
defer func() { trace.EndSpan(ctx, err) }()
s := callSettings(c.settings, opts...)
req := &storagepb.DeleteNotificationRequest{Name: id}
req := &storagepb.DeleteNotificationConfigRequest{Name: id}
return run(ctx, func() error {
return c.raw.DeleteNotification(ctx, req, s.gax...)
return c.raw.DeleteNotificationConfig(ctx, req, s.gax...)
}, s.retry, s.idempotent, setRetryHeaderGRPC(ctx))
}
@ -1496,11 +1501,16 @@ func (w *gRPCWriter) startResumableUpload() error {
if err != nil {
return err
}
req := &storagepb.StartResumableWriteRequest{
WriteObjectSpec: spec,
CommonObjectRequestParams: toProtoCommonObjectRequestParams(w.encryptionKey),
}
// TODO: Currently the checksums are only sent on the request to initialize
// the upload, but in the future, we must also support sending it
// on the *last* message of the stream.
req.ObjectChecksums = toProtoChecksums(w.sendCRC32C, w.attrs)
return run(w.ctx, func() error {
upres, err := w.c.raw.StartResumableWrite(w.ctx, &storagepb.StartResumableWriteRequest{
WriteObjectSpec: spec,
CommonObjectRequestParams: toProtoCommonObjectRequestParams(w.encryptionKey),
})
upres, err := w.c.raw.StartResumableWrite(w.ctx, req)
w.upid = upres.GetUploadId()
return err
}, w.settings.retry, w.settings.idempotent, setRetryHeaderGRPC(w.ctx))
@ -1585,25 +1595,13 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
WriteObjectSpec: spec,
}
req.CommonObjectRequestParams = toProtoCommonObjectRequestParams(w.encryptionKey)
// For a non-resumable upload, checksums must be sent in this message.
// TODO: Currently the checksums are only sent on the first message
// of the stream, but in the future, we must also support sending it
// on the *last* message of the stream (instead of the first).
req.ObjectChecksums = toProtoChecksums(w.sendCRC32C, w.attrs)
}
// TODO: Currently the checksums are only sent on the first message
// of the stream, but in the future, we must also support sending it
// on the *last* message of the stream (instead of the first).
if w.sendCRC32C {
req.ObjectChecksums = &storagepb.ObjectChecksums{
Crc32C: proto.Uint32(w.attrs.CRC32C),
}
}
if len(w.attrs.MD5) != 0 {
if cs := req.GetObjectChecksums(); cs == nil {
req.ObjectChecksums = &storagepb.ObjectChecksums{
Md5Hash: w.attrs.MD5,
}
} else {
cs.Md5Hash = w.attrs.MD5
}
}
}
err = w.stream.Send(req)

View file

@ -29,6 +29,7 @@ import (
"strings"
"time"
"cloud.google.com/go/iam/apiv1/iampb"
"cloud.google.com/go/internal/optional"
"cloud.google.com/go/internal/trace"
"golang.org/x/oauth2/google"
@ -39,7 +40,6 @@ import (
raw "google.golang.org/api/storage/v1"
"google.golang.org/api/transport"
htransport "google.golang.org/api/transport/http"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)
// httpStorageClient is the HTTP-JSON API implementation of the transport-agnostic
@ -53,6 +53,7 @@ type httpStorageClient struct {
raw *raw.Service
scheme string
settings *settings
config *storageConfig
}
// newHTTPStorageClient initializes a new storageClient that uses the HTTP-JSON
@ -62,6 +63,7 @@ type httpStorageClient struct {
func newHTTPStorageClient(ctx context.Context, opts ...storageOption) (storageClient, error) {
s := initSettings(opts...)
o := s.clientOption
config := newStorageConfig(o...)
var creds *google.Credentials
// In general, it is recommended to use raw.NewService instead of htransport.NewClient
@ -134,6 +136,7 @@ func newHTTPStorageClient(ctx context.Context, opts ...storageOption) (storageCl
raw: rawService,
scheme: u.Scheme,
settings: s,
config: &config,
}, nil
}
@ -779,6 +782,13 @@ func (c *httpStorageClient) NewRangeReader(ctx context.Context, params *newRange
s := callSettings(c.settings, opts...)
if c.config.useJSONforReads {
return c.newRangeReaderJSON(ctx, params, s)
}
return c.newRangeReaderXML(ctx, params, s)
}
func (c *httpStorageClient) newRangeReaderXML(ctx context.Context, params *newRangeReaderParams, s *settings) (r *Reader, err error) {
u := &url.URL{
Scheme: c.scheme,
Host: c.readHost,
@ -793,186 +803,51 @@ func (c *httpStorageClient) NewRangeReader(ctx context.Context, params *newRange
return nil, err
}
req = req.WithContext(ctx)
if s.userProject != "" {
req.Header.Set("X-Goog-User-Project", s.userProject)
}
if params.readCompressed {
req.Header.Set("Accept-Encoding", "gzip")
}
if err := setEncryptionHeaders(req.Header, params.encryptionKey, false); err != nil {
if err := setRangeReaderHeaders(req.Header, params); err != nil {
return nil, err
}
// Define a function that initiates a Read with offset and length, assuming we
// have already read seen bytes.
reopen := func(seen int64) (*http.Response, error) {
// If the context has already expired, return immediately without making a
// call.
if err := ctx.Err(); err != nil {
return nil, err
}
start := params.offset + seen
if params.length < 0 && start < 0 {
req.Header.Set("Range", fmt.Sprintf("bytes=%d", start))
} else if params.length < 0 && start > 0 {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", start))
} else if params.length > 0 {
// The end character isn't affected by how many bytes we've seen.
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, params.offset+params.length-1))
}
// We wait to assign conditions here because the generation number can change in between reopen() runs.
if err := setConditionsHeaders(req.Header, params.conds); err != nil {
return nil, err
}
// If an object generation is specified, include generation as query string parameters.
if params.gen >= 0 {
req.URL.RawQuery = fmt.Sprintf("generation=%d", params.gen)
}
var res *http.Response
err = run(ctx, func() error {
res, err = c.hc.Do(req)
if err != nil {
return err
}
if res.StatusCode == http.StatusNotFound {
res.Body.Close()
return ErrObjectNotExist
}
if res.StatusCode < 200 || res.StatusCode > 299 {
body, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
return &googleapi.Error{
Code: res.StatusCode,
Header: res.Header,
Body: string(body),
}
}
partialContentNotSatisfied :=
!decompressiveTranscoding(res) &&
start > 0 && params.length != 0 &&
res.StatusCode != http.StatusPartialContent
if partialContentNotSatisfied {
res.Body.Close()
return errors.New("storage: partial request not satisfied")
}
// With "Content-Encoding": "gzip" aka decompressive transcoding, GCS serves
// back the whole file regardless of the range count passed in as per:
// https://cloud.google.com/storage/docs/transcoding#range,
// thus we have to manually move the body forward by seen bytes.
if decompressiveTranscoding(res) && seen > 0 {
_, _ = io.CopyN(ioutil.Discard, res.Body, seen)
}
// If a generation hasn't been specified, and this is the first response we get, let's record the
// generation. In future requests we'll use this generation as a precondition to avoid data races.
if params.gen < 0 && res.Header.Get("X-Goog-Generation") != "" {
gen64, err := strconv.ParseInt(res.Header.Get("X-Goog-Generation"), 10, 64)
if err != nil {
return err
}
params.gen = gen64
}
return nil
}, s.retry, s.idempotent, setRetryHeaderHTTP(nil))
if err != nil {
return nil, err
}
return res, nil
}
reopen := readerReopen(ctx, req.Header, params, s,
func() (*http.Response, error) { return c.hc.Do(req) },
func() error { return setConditionsHeaders(req.Header, params.conds) },
func() { req.URL.RawQuery = fmt.Sprintf("generation=%d", params.gen) })
res, err := reopen(0)
if err != nil {
return nil, err
}
var (
size int64 // total size of object, even if a range was requested.
checkCRC bool
crc uint32
startOffset int64 // non-zero if range request.
)
if res.StatusCode == http.StatusPartialContent {
cr := strings.TrimSpace(res.Header.Get("Content-Range"))
if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
}
// Content range is formatted <first byte>-<last byte>/<total size>. We take
// the total size.
size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
}
return parseReadResponse(res, params, reopen)
}
dashIndex := strings.Index(cr, "-")
if dashIndex >= 0 {
startOffset, err = strconv.ParseInt(cr[len("bytes="):dashIndex], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q: %w", cr, err)
}
}
} else {
size = res.ContentLength
// Check the CRC iff all of the following hold:
// - We asked for content (length != 0).
// - We got all the content (status != PartialContent).
// - The server sent a CRC header.
// - The Go http stack did not uncompress the file.
// - We were not served compressed data that was uncompressed on download.
// The problem with the last two cases is that the CRC will not match -- GCS
// computes it on the compressed contents, but we compute it on the
// uncompressed contents.
if params.length != 0 && !res.Uncompressed && !uncompressedByServer(res) {
crc, checkCRC = parseCRC32c(res)
}
func (c *httpStorageClient) newRangeReaderJSON(ctx context.Context, params *newRangeReaderParams, s *settings) (r *Reader, err error) {
call := c.raw.Objects.Get(params.bucket, params.object)
setClientHeader(call.Header())
call.Context(ctx)
call.Projection("full")
if s.userProject != "" {
call.UserProject(s.userProject)
}
remain := res.ContentLength
body := res.Body
if params.length == 0 {
remain = 0
body.Close()
body = emptyBody
}
var metaGen int64
if res.Header.Get("X-Goog-Metageneration") != "" {
metaGen, err = strconv.ParseInt(res.Header.Get("X-Goog-Metageneration"), 10, 64)
if err != nil {
return nil, err
}
if err := setRangeReaderHeaders(call.Header(), params); err != nil {
return nil, err
}
var lm time.Time
if res.Header.Get("Last-Modified") != "" {
lm, err = http.ParseTime(res.Header.Get("Last-Modified"))
if err != nil {
return nil, err
}
}
reopen := readerReopen(ctx, call.Header(), params, s, func() (*http.Response, error) { return call.Download() },
func() error { return applyConds("NewReader", params.gen, params.conds, call) },
func() { call.Generation(params.gen) })
attrs := ReaderObjectAttrs{
Size: size,
ContentType: res.Header.Get("Content-Type"),
ContentEncoding: res.Header.Get("Content-Encoding"),
CacheControl: res.Header.Get("Cache-Control"),
LastModified: lm,
StartOffset: startOffset,
Generation: params.gen,
Metageneration: metaGen,
res, err := reopen(0)
if err != nil {
return nil, err
}
return &Reader{
Attrs: attrs,
size: size,
remain: remain,
wantCRC: crc,
checkCRC: checkCRC,
reader: &httpReader{
reopen: reopen,
body: body,
},
}, nil
return parseReadResponse(res, params, reopen)
}
func (c *httpStorageClient) OpenWriter(params *openWriterParams, opts ...storageOption) (*io.PipeWriter, error) {
@ -1349,3 +1224,195 @@ func (r *httpReader) Read(p []byte) (int, error) {
func (r *httpReader) Close() error {
return r.body.Close()
}
func setRangeReaderHeaders(h http.Header, params *newRangeReaderParams) error {
if params.readCompressed {
h.Set("Accept-Encoding", "gzip")
}
if err := setEncryptionHeaders(h, params.encryptionKey, false); err != nil {
return err
}
return nil
}
// readerReopen initiates a Read with offset and length, assuming we
// have already read seen bytes.
func readerReopen(ctx context.Context, header http.Header, params *newRangeReaderParams, s *settings,
doDownload func() (*http.Response, error), applyConditions func() error, setGeneration func()) func(int64) (*http.Response, error) {
return func(seen int64) (*http.Response, error) {
// If the context has already expired, return immediately without making a
// call.
if err := ctx.Err(); err != nil {
return nil, err
}
start := params.offset + seen
if params.length < 0 && start < 0 {
header.Set("Range", fmt.Sprintf("bytes=%d", start))
} else if params.length < 0 && start > 0 {
header.Set("Range", fmt.Sprintf("bytes=%d-", start))
} else if params.length > 0 {
// The end character isn't affected by how many bytes we've seen.
header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, params.offset+params.length-1))
}
// We wait to assign conditions here because the generation number can change in between reopen() runs.
if err := applyConditions(); err != nil {
return nil, err
}
// If an object generation is specified, include generation as query string parameters.
if params.gen >= 0 {
setGeneration()
}
var err error
var res *http.Response
err = run(ctx, func() error {
res, err = doDownload()
if err != nil {
var e *googleapi.Error
if errors.As(err, &e) {
if e.Code == http.StatusNotFound {
return ErrObjectNotExist
}
}
return err
}
if res.StatusCode == http.StatusNotFound {
// this check is necessary only for XML
res.Body.Close()
return ErrObjectNotExist
}
if res.StatusCode < 200 || res.StatusCode > 299 {
body, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
return &googleapi.Error{
Code: res.StatusCode,
Header: res.Header,
Body: string(body),
}
}
partialContentNotSatisfied :=
!decompressiveTranscoding(res) &&
start > 0 && params.length != 0 &&
res.StatusCode != http.StatusPartialContent
if partialContentNotSatisfied {
res.Body.Close()
return errors.New("storage: partial request not satisfied")
}
// With "Content-Encoding": "gzip" aka decompressive transcoding, GCS serves
// back the whole file regardless of the range count passed in as per:
// https://cloud.google.com/storage/docs/transcoding#range,
// thus we have to manually move the body forward by seen bytes.
if decompressiveTranscoding(res) && seen > 0 {
_, _ = io.CopyN(ioutil.Discard, res.Body, seen)
}
// If a generation hasn't been specified, and this is the first response we get, let's record the
// generation. In future requests we'll use this generation as a precondition to avoid data races.
if params.gen < 0 && res.Header.Get("X-Goog-Generation") != "" {
gen64, err := strconv.ParseInt(res.Header.Get("X-Goog-Generation"), 10, 64)
if err != nil {
return err
}
params.gen = gen64
}
return nil
}, s.retry, s.idempotent, setRetryHeaderHTTP(nil))
if err != nil {
return nil, err
}
return res, nil
}
}
func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen func(int64) (*http.Response, error)) (*Reader, error) {
var err error
var (
size int64 // total size of object, even if a range was requested.
checkCRC bool
crc uint32
startOffset int64 // non-zero if range request.
)
if res.StatusCode == http.StatusPartialContent {
cr := strings.TrimSpace(res.Header.Get("Content-Range"))
if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
}
// Content range is formatted <first byte>-<last byte>/<total size>. We take
// the total size.
size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
}
dashIndex := strings.Index(cr, "-")
if dashIndex >= 0 {
startOffset, err = strconv.ParseInt(cr[len("bytes="):dashIndex], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q: %w", cr, err)
}
}
} else {
size = res.ContentLength
// Check the CRC iff all of the following hold:
// - We asked for content (length != 0).
// - We got all the content (status != PartialContent).
// - The server sent a CRC header.
// - The Go http stack did not uncompress the file.
// - We were not served compressed data that was uncompressed on download.
// The problem with the last two cases is that the CRC will not match -- GCS
// computes it on the compressed contents, but we compute it on the
// uncompressed contents.
if params.length != 0 && !res.Uncompressed && !uncompressedByServer(res) {
crc, checkCRC = parseCRC32c(res)
}
}
remain := res.ContentLength
body := res.Body
if params.length == 0 {
remain = 0
body.Close()
body = emptyBody
}
var metaGen int64
if res.Header.Get("X-Goog-Metageneration") != "" {
metaGen, err = strconv.ParseInt(res.Header.Get("X-Goog-Metageneration"), 10, 64)
if err != nil {
return nil, err
}
}
var lm time.Time
if res.Header.Get("Last-Modified") != "" {
lm, err = http.ParseTime(res.Header.Get("Last-Modified"))
if err != nil {
return nil, err
}
}
attrs := ReaderObjectAttrs{
Size: size,
ContentType: res.Header.Get("Content-Type"),
ContentEncoding: res.Header.Get("Content-Encoding"),
CacheControl: res.Header.Get("Cache-Control"),
LastModified: lm,
StartOffset: startOffset,
Generation: params.gen,
Metageneration: metaGen,
}
return &Reader{
Attrs: attrs,
size: size,
remain: remain,
wantCRC: crc,
checkCRC: checkCRC,
reader: &httpReader{
reopen: reopen,
body: body,
},
}, nil
}

View file

@ -18,9 +18,9 @@ import (
"context"
"cloud.google.com/go/iam"
"cloud.google.com/go/iam/apiv1/iampb"
"cloud.google.com/go/internal/trace"
raw "google.golang.org/api/storage/v1"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/genproto/googleapis/type/expr"
)

View file

@ -1,4 +1,4 @@
// Copyright 2022 Google LLC
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -21,6 +21,11 @@
//
// NOTE: This package is in alpha. It is not stable, and is likely to change.
//
// # General documentation
//
// For information about setting deadlines, reusing contexts, and more
// please visit https://pkg.go.dev/cloud.google.com/go.
//
// # Example usage
//
// To get started with this package, create a client.
@ -73,9 +78,6 @@
// Individual methods on the client use the ctx given to them.
//
// To close the open connection, use the Close() method.
//
// For information about setting deadlines, reusing contexts, and more
// please visit https://pkg.go.dev/cloud.google.com/go.
package storage // import "cloud.google.com/go/storage/internal/apiv2"
import (

View file

@ -30,9 +30,9 @@
"CreateHmacKey"
]
},
"CreateNotification": {
"CreateNotificationConfig": {
"methods": [
"CreateNotification"
"CreateNotificationConfig"
]
},
"DeleteBucket": {
@ -45,9 +45,9 @@
"DeleteHmacKey"
]
},
"DeleteNotification": {
"DeleteNotificationConfig": {
"methods": [
"DeleteNotification"
"DeleteNotificationConfig"
]
},
"DeleteObject": {
@ -70,9 +70,9 @@
"GetIamPolicy"
]
},
"GetNotification": {
"GetNotificationConfig": {
"methods": [
"GetNotification"
"GetNotificationConfig"
]
},
"GetObject": {
@ -95,9 +95,9 @@
"ListHmacKeys"
]
},
"ListNotifications": {
"ListNotificationConfigs": {
"methods": [
"ListNotifications"
"ListNotificationConfigs"
]
},
"ListObjects": {

View file

@ -1,4 +1,4 @@
// Copyright 2022 Google LLC
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,13 +24,13 @@ import (
"regexp"
"strings"
iampb "cloud.google.com/go/iam/apiv1/iampb"
storagepb "cloud.google.com/go/storage/internal/apiv2/stubs"
gax "github.com/googleapis/gax-go/v2"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/api/option/internaloption"
gtransport "google.golang.org/api/transport/grpc"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
@ -49,10 +49,10 @@ type CallOptions struct {
SetIamPolicy []gax.CallOption
TestIamPermissions []gax.CallOption
UpdateBucket []gax.CallOption
DeleteNotification []gax.CallOption
GetNotification []gax.CallOption
CreateNotification []gax.CallOption
ListNotifications []gax.CallOption
DeleteNotificationConfig []gax.CallOption
GetNotificationConfig []gax.CallOption
CreateNotificationConfig []gax.CallOption
ListNotificationConfigs []gax.CallOption
ComposeObject []gax.CallOption
DeleteObject []gax.CallOption
CancelResumableWrite []gax.CallOption
@ -95,10 +95,10 @@ func defaultCallOptions() *CallOptions {
SetIamPolicy: []gax.CallOption{},
TestIamPermissions: []gax.CallOption{},
UpdateBucket: []gax.CallOption{},
DeleteNotification: []gax.CallOption{},
GetNotification: []gax.CallOption{},
CreateNotification: []gax.CallOption{},
ListNotifications: []gax.CallOption{},
DeleteNotificationConfig: []gax.CallOption{},
GetNotificationConfig: []gax.CallOption{},
CreateNotificationConfig: []gax.CallOption{},
ListNotificationConfigs: []gax.CallOption{},
ComposeObject: []gax.CallOption{},
DeleteObject: []gax.CallOption{},
CancelResumableWrite: []gax.CallOption{},
@ -133,10 +133,10 @@ type internalClient interface {
SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error)
TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error)
UpdateBucket(context.Context, *storagepb.UpdateBucketRequest, ...gax.CallOption) (*storagepb.Bucket, error)
DeleteNotification(context.Context, *storagepb.DeleteNotificationRequest, ...gax.CallOption) error
GetNotification(context.Context, *storagepb.GetNotificationRequest, ...gax.CallOption) (*storagepb.Notification, error)
CreateNotification(context.Context, *storagepb.CreateNotificationRequest, ...gax.CallOption) (*storagepb.Notification, error)
ListNotifications(context.Context, *storagepb.ListNotificationsRequest, ...gax.CallOption) *NotificationIterator
DeleteNotificationConfig(context.Context, *storagepb.DeleteNotificationConfigRequest, ...gax.CallOption) error
GetNotificationConfig(context.Context, *storagepb.GetNotificationConfigRequest, ...gax.CallOption) (*storagepb.NotificationConfig, error)
CreateNotificationConfig(context.Context, *storagepb.CreateNotificationConfigRequest, ...gax.CallOption) (*storagepb.NotificationConfig, error)
ListNotificationConfigs(context.Context, *storagepb.ListNotificationConfigsRequest, ...gax.CallOption) *NotificationConfigIterator
ComposeObject(context.Context, *storagepb.ComposeObjectRequest, ...gax.CallOption) (*storagepb.Object, error)
DeleteObject(context.Context, *storagepb.DeleteObjectRequest, ...gax.CallOption) error
CancelResumableWrite(context.Context, *storagepb.CancelResumableWriteRequest, ...gax.CallOption) (*storagepb.CancelResumableWriteResponse, error)
@ -238,17 +238,26 @@ func (c *Client) LockBucketRetentionPolicy(ctx context.Context, req *storagepb.L
}
// GetIamPolicy gets the IAM policy for a specified bucket or object.
// The resource field in the request should be
// projects//buckets/<bucket_name> for a bucket or
// projects//buckets/<bucket_name>/objects/<object_name> for an object.
func (c *Client) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
return c.internalClient.GetIamPolicy(ctx, req, opts...)
}
// SetIamPolicy updates an IAM policy for the specified bucket or object.
// The resource field in the request should be
// projects//buckets/<bucket_name> for a bucket or
// projects//buckets/<bucket_name>/objects/<object_name> for an object.
func (c *Client) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
return c.internalClient.SetIamPolicy(ctx, req, opts...)
}
// TestIamPermissions tests a set of permissions on the given bucket or object to see which, if
// any, are held by the caller.
// The resource field in the request should be
// projects//buckets/<bucket_name> for a bucket or
// projects//buckets/<bucket_name>/objects/<object_name> for an object.
func (c *Client) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
return c.internalClient.TestIamPermissions(ctx, req, opts...)
}
@ -258,27 +267,27 @@ func (c *Client) UpdateBucket(ctx context.Context, req *storagepb.UpdateBucketRe
return c.internalClient.UpdateBucket(ctx, req, opts...)
}
// DeleteNotification permanently deletes a notification subscription.
func (c *Client) DeleteNotification(ctx context.Context, req *storagepb.DeleteNotificationRequest, opts ...gax.CallOption) error {
return c.internalClient.DeleteNotification(ctx, req, opts...)
// DeleteNotificationConfig permanently deletes a NotificationConfig.
func (c *Client) DeleteNotificationConfig(ctx context.Context, req *storagepb.DeleteNotificationConfigRequest, opts ...gax.CallOption) error {
return c.internalClient.DeleteNotificationConfig(ctx, req, opts...)
}
// GetNotification view a notification config.
func (c *Client) GetNotification(ctx context.Context, req *storagepb.GetNotificationRequest, opts ...gax.CallOption) (*storagepb.Notification, error) {
return c.internalClient.GetNotification(ctx, req, opts...)
// GetNotificationConfig view a NotificationConfig.
func (c *Client) GetNotificationConfig(ctx context.Context, req *storagepb.GetNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
return c.internalClient.GetNotificationConfig(ctx, req, opts...)
}
// CreateNotification creates a notification subscription for a given bucket.
// These notifications, when triggered, publish messages to the specified
// Pub/Sub topics.
// See https://cloud.google.com/storage/docs/pubsub-notifications (at https://cloud.google.com/storage/docs/pubsub-notifications).
func (c *Client) CreateNotification(ctx context.Context, req *storagepb.CreateNotificationRequest, opts ...gax.CallOption) (*storagepb.Notification, error) {
return c.internalClient.CreateNotification(ctx, req, opts...)
// CreateNotificationConfig creates a NotificationConfig for a given bucket.
// These NotificationConfigs, when triggered, publish messages to the
// specified Pub/Sub topics. See
// https://cloud.google.com/storage/docs/pubsub-notifications (at https://cloud.google.com/storage/docs/pubsub-notifications).
func (c *Client) CreateNotificationConfig(ctx context.Context, req *storagepb.CreateNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
return c.internalClient.CreateNotificationConfig(ctx, req, opts...)
}
// ListNotifications retrieves a list of notification subscriptions for a given bucket.
func (c *Client) ListNotifications(ctx context.Context, req *storagepb.ListNotificationsRequest, opts ...gax.CallOption) *NotificationIterator {
return c.internalClient.ListNotifications(ctx, req, opts...)
// ListNotificationConfigs retrieves a list of NotificationConfigs for a given bucket.
func (c *Client) ListNotificationConfigs(ctx context.Context, req *storagepb.ListNotificationConfigsRequest, opts ...gax.CallOption) *NotificationConfigIterator {
return c.internalClient.ListNotificationConfigs(ctx, req, opts...)
}
// ComposeObject concatenates a list of existing objects into a new object in the same
@ -356,8 +365,9 @@ func (c *Client) UpdateObject(ctx context.Context, req *storagepb.UpdateObjectRe
// returned persisted_size; in this case, the service will skip data at
// offsets that were already persisted (without checking that it matches
// the previously written data), and write only the data starting from the
// persisted offset. This behavior can make client-side handling simpler
// in some cases.
// persisted offset. Even though the data isnt written, it may still
// incur a performance cost over resuming at the correct write offset.
// This behavior can make client-side handling simpler in some cases.
//
// The service will not view the object as complete until the client has
// sent a WriteObjectRequest with finish_write set to true. Sending any
@ -594,6 +604,9 @@ func (c *gRPCClient) CreateBucket(ctx context.Context, req *storagepb.CreateBuck
if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])
}
if reg := regexp.MustCompile("(?P<project>.*)"); reg.MatchString(req.GetBucket().GetProject()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetProject())[1])) > 0 {
routingHeadersMap["project"] = url.QueryEscape(reg.FindStringSubmatch(req.GetBucket().GetProject())[1])
}
for headerName, headerValue := range routingHeadersMap {
routingHeaders = fmt.Sprintf("%s%s=%s&", routingHeaders, headerName, headerValue)
}
@ -807,7 +820,7 @@ func (c *gRPCClient) UpdateBucket(ctx context.Context, req *storagepb.UpdateBuck
return resp, nil
}
func (c *gRPCClient) DeleteNotification(ctx context.Context, req *storagepb.DeleteNotificationRequest, opts ...gax.CallOption) error {
func (c *gRPCClient) DeleteNotificationConfig(ctx context.Context, req *storagepb.DeleteNotificationConfigRequest, opts ...gax.CallOption) error {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
@ -820,16 +833,16 @@ func (c *gRPCClient) DeleteNotification(ctx context.Context, req *storagepb.Dele
md := metadata.Pairs("x-goog-request-params", routingHeaders)
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append((*c.CallOptions).DeleteNotification[0:len((*c.CallOptions).DeleteNotification):len((*c.CallOptions).DeleteNotification)], opts...)
opts = append((*c.CallOptions).DeleteNotificationConfig[0:len((*c.CallOptions).DeleteNotificationConfig):len((*c.CallOptions).DeleteNotificationConfig)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
_, err = c.client.DeleteNotification(ctx, req, settings.GRPC...)
_, err = c.client.DeleteNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
return err
}
func (c *gRPCClient) GetNotification(ctx context.Context, req *storagepb.GetNotificationRequest, opts ...gax.CallOption) (*storagepb.Notification, error) {
func (c *gRPCClient) GetNotificationConfig(ctx context.Context, req *storagepb.GetNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>projects/[^/]+/buckets/[^/]+)(?:/.*)?"); reg.MatchString(req.GetName()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetName())[1])) > 0 {
@ -842,11 +855,11 @@ func (c *gRPCClient) GetNotification(ctx context.Context, req *storagepb.GetNoti
md := metadata.Pairs("x-goog-request-params", routingHeaders)
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append((*c.CallOptions).GetNotification[0:len((*c.CallOptions).GetNotification):len((*c.CallOptions).GetNotification)], opts...)
var resp *storagepb.Notification
opts = append((*c.CallOptions).GetNotificationConfig[0:len((*c.CallOptions).GetNotificationConfig):len((*c.CallOptions).GetNotificationConfig)], opts...)
var resp *storagepb.NotificationConfig
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.GetNotification(ctx, req, settings.GRPC...)
resp, err = c.client.GetNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
@ -855,7 +868,7 @@ func (c *gRPCClient) GetNotification(ctx context.Context, req *storagepb.GetNoti
return resp, nil
}
func (c *gRPCClient) CreateNotification(ctx context.Context, req *storagepb.CreateNotificationRequest, opts ...gax.CallOption) (*storagepb.Notification, error) {
func (c *gRPCClient) CreateNotificationConfig(ctx context.Context, req *storagepb.CreateNotificationConfigRequest, opts ...gax.CallOption) (*storagepb.NotificationConfig, error) {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
@ -868,11 +881,11 @@ func (c *gRPCClient) CreateNotification(ctx context.Context, req *storagepb.Crea
md := metadata.Pairs("x-goog-request-params", routingHeaders)
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append((*c.CallOptions).CreateNotification[0:len((*c.CallOptions).CreateNotification):len((*c.CallOptions).CreateNotification)], opts...)
var resp *storagepb.Notification
opts = append((*c.CallOptions).CreateNotificationConfig[0:len((*c.CallOptions).CreateNotificationConfig):len((*c.CallOptions).CreateNotificationConfig)], opts...)
var resp *storagepb.NotificationConfig
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.CreateNotification(ctx, req, settings.GRPC...)
resp, err = c.client.CreateNotificationConfig(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
@ -881,7 +894,7 @@ func (c *gRPCClient) CreateNotification(ctx context.Context, req *storagepb.Crea
return resp, nil
}
func (c *gRPCClient) ListNotifications(ctx context.Context, req *storagepb.ListNotificationsRequest, opts ...gax.CallOption) *NotificationIterator {
func (c *gRPCClient) ListNotificationConfigs(ctx context.Context, req *storagepb.ListNotificationConfigsRequest, opts ...gax.CallOption) *NotificationConfigIterator {
routingHeaders := ""
routingHeadersMap := make(map[string]string)
if reg := regexp.MustCompile("(?P<bucket>.*)"); reg.MatchString(req.GetParent()) && len(url.QueryEscape(reg.FindStringSubmatch(req.GetParent())[1])) > 0 {
@ -894,11 +907,11 @@ func (c *gRPCClient) ListNotifications(ctx context.Context, req *storagepb.ListN
md := metadata.Pairs("x-goog-request-params", routingHeaders)
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append((*c.CallOptions).ListNotifications[0:len((*c.CallOptions).ListNotifications):len((*c.CallOptions).ListNotifications)], opts...)
it := &NotificationIterator{}
req = proto.Clone(req).(*storagepb.ListNotificationsRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.Notification, string, error) {
resp := &storagepb.ListNotificationsResponse{}
opts = append((*c.CallOptions).ListNotificationConfigs[0:len((*c.CallOptions).ListNotificationConfigs):len((*c.CallOptions).ListNotificationConfigs)], opts...)
it := &NotificationConfigIterator{}
req = proto.Clone(req).(*storagepb.ListNotificationConfigsRequest)
it.InternalFetch = func(pageSize int, pageToken string) ([]*storagepb.NotificationConfig, string, error) {
resp := &storagepb.ListNotificationConfigsResponse{}
if pageToken != "" {
req.PageToken = pageToken
}
@ -909,7 +922,7 @@ func (c *gRPCClient) ListNotifications(ctx context.Context, req *storagepb.ListN
}
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
resp, err = c.client.ListNotifications(ctx, req, settings.GRPC...)
resp, err = c.client.ListNotificationConfigs(ctx, req, settings.GRPC...)
return err
}, opts...)
if err != nil {
@ -917,7 +930,7 @@ func (c *gRPCClient) ListNotifications(ctx context.Context, req *storagepb.ListN
}
it.Response = resp
return resp.GetNotifications(), resp.GetNextPageToken(), nil
return resp.GetNotificationConfigs(), resp.GetNextPageToken(), nil
}
fetch := func(pageSize int, pageToken string) (string, error) {
items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
@ -1048,6 +1061,7 @@ func (c *gRPCClient) ReadObject(ctx context.Context, req *storagepb.ReadObjectRe
md := metadata.Pairs("x-goog-request-params", routingHeaders)
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
opts = append((*c.CallOptions).ReadObject[0:len((*c.CallOptions).ReadObject):len((*c.CallOptions).ReadObject)], opts...)
var resp storagepb.Storage_ReadObjectClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
@ -1510,9 +1524,9 @@ func (it *HmacKeyMetadataIterator) takeBuf() interface{} {
return b
}
// NotificationIterator manages a stream of *storagepb.Notification.
type NotificationIterator struct {
items []*storagepb.Notification
// NotificationConfigIterator manages a stream of *storagepb.NotificationConfig.
type NotificationConfigIterator struct {
items []*storagepb.NotificationConfig
pageInfo *iterator.PageInfo
nextFunc func() error
@ -1527,18 +1541,18 @@ type NotificationIterator struct {
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Notification, nextPageToken string, err error)
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.NotificationConfig, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *NotificationIterator) PageInfo() *iterator.PageInfo {
func (it *NotificationConfigIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *NotificationIterator) Next() (*storagepb.Notification, error) {
var item *storagepb.Notification
func (it *NotificationConfigIterator) Next() (*storagepb.NotificationConfig, error) {
var item *storagepb.NotificationConfig
if err := it.nextFunc(); err != nil {
return item, err
}
@ -1547,11 +1561,11 @@ func (it *NotificationIterator) Next() (*storagepb.Notification, error) {
return item, nil
}
func (it *NotificationIterator) bufLen() int {
func (it *NotificationConfigIterator) bufLen() int {
return len(it.items)
}
func (it *NotificationIterator) takeBuf() interface{} {
func (it *NotificationConfigIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Copyright 2022 Google LLC
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -15,4 +15,4 @@
package internal
// Version is the current tagged release of the library.
const Version = "1.28.1"
const Version = "1.30.1"

View file

@ -92,7 +92,7 @@ func toNotification(rn *raw.Notification) *Notification {
return n
}
func toNotificationFromProto(pbn *storagepb.Notification) *Notification {
func toNotificationFromProto(pbn *storagepb.NotificationConfig) *Notification {
n := &Notification{
ID: pbn.GetName(),
EventTypes: pbn.GetEventTypes(),
@ -104,8 +104,8 @@ func toNotificationFromProto(pbn *storagepb.Notification) *Notification {
return n
}
func toProtoNotification(n *Notification) *storagepb.Notification {
return &storagepb.Notification{
func toProtoNotification(n *Notification) *storagepb.NotificationConfig {
return &storagepb.NotificationConfig{
Name: n.ID,
Topic: fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s",
n.TopicProjectID, n.TopicID),
@ -182,7 +182,7 @@ func notificationsToMap(rns []*raw.Notification) map[string]*Notification {
return m
}
func notificationsToMapFromProto(ns []*storagepb.Notification) map[string]*Notification {
func notificationsToMapFromProto(ns []*storagepb.NotificationConfig) map[string]*Notification {
m := map[string]*Notification{}
for _, n := range ns {
m[n.Name] = toNotificationFromProto(n)

75
vendor/cloud.google.com/go/storage/option.go generated vendored Normal file
View file

@ -0,0 +1,75 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package storage
import (
"google.golang.org/api/option"
"google.golang.org/api/option/internaloption"
)
// storageConfig contains the Storage client option configuration that can be
// set through storageClientOptions.
type storageConfig struct {
useJSONforReads bool
readAPIWasSet bool
}
// newStorageConfig generates a new storageConfig with all the given
// storageClientOptions applied.
func newStorageConfig(opts ...option.ClientOption) storageConfig {
var conf storageConfig
for _, opt := range opts {
if storageOpt, ok := opt.(storageClientOption); ok {
storageOpt.ApplyStorageOpt(&conf)
}
}
return conf
}
// A storageClientOption is an option for a Google Storage client.
type storageClientOption interface {
option.ClientOption
ApplyStorageOpt(*storageConfig)
}
// WithJSONReads is an option that may be passed to a Storage Client on creation.
// It sets the client to use the JSON API for object reads. Currently, the
// default API used for reads is XML.
// Setting this option is required to use the GenerationNotMatch condition.
//
// Note that when this option is set, reads will return a zero date for
// [ReaderObjectAttrs].LastModified and may return a different value for
// [ReaderObjectAttrs].CacheControl.
func WithJSONReads() option.ClientOption {
return &withReadAPI{useJSON: true}
}
// WithXMLReads is an option that may be passed to a Storage Client on creation.
// It sets the client to use the JSON API for object reads.
//
// This is the current default.
func WithXMLReads() option.ClientOption {
return &withReadAPI{useJSON: false}
}
type withReadAPI struct {
internaloption.EmbeddableAdapter
useJSON bool
}
func (w *withReadAPI) ApplyStorageOpt(c *storageConfig) {
c.useJSONforReads = w.useJSON
c.readAPIWasSet = true
}

View file

@ -139,15 +139,23 @@ func uncompressedByServer(res *http.Response) bool {
res.Header.Get("Content-Encoding") != "gzip"
}
// parseCRC32c parses the crc32c hash from the X-Goog-Hash header.
// It can parse headers in the form [crc32c=xxx md5=xxx] (XML responses) or the
// form [crc32c=xxx,md5=xxx] (JSON responses). The md5 hash is ignored.
func parseCRC32c(res *http.Response) (uint32, bool) {
const prefix = "crc32c="
for _, spec := range res.Header["X-Goog-Hash"] {
if strings.HasPrefix(spec, prefix) {
c, err := decodeUint32(spec[len(prefix):])
if err == nil {
return c, true
values := strings.Split(spec, ",")
for _, v := range values {
if strings.HasPrefix(v, prefix) {
c, err := decodeUint32(v[len(prefix):])
if err == nil {
return c, true
}
}
}
}
return 0, false
}

View file

@ -129,8 +129,10 @@ type Client struct {
//
// Clients should be reused instead of created as needed. The methods of Client
// are safe for concurrent use by multiple goroutines.
//
// You may configure the client by passing in options from the [google.golang.org/api/option]
// package. You may also use options defined in this package, such as [WithJSONReads].
func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
// Use the experimental gRPC client if the env var is set.
// This is an experimental API and not intended for public use.
if withGRPC := os.Getenv("STORAGE_USE_GRPC"); withGRPC != "" {
@ -179,10 +181,12 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error
endpoint := hostURL.String()
// Append the emulator host as default endpoint for the user
opts = append([]option.ClientOption{option.WithoutAuthentication()}, opts...)
opts = append(opts, internaloption.WithDefaultEndpoint(endpoint))
opts = append(opts, internaloption.WithDefaultMTLSEndpoint(endpoint))
opts = append([]option.ClientOption{
option.WithoutAuthentication(),
internaloption.SkipDialSettingsValidation(),
internaloption.WithDefaultEndpoint(endpoint),
internaloption.WithDefaultMTLSEndpoint(endpoint),
}, opts...)
}
// htransport selects the correct endpoint among WithEndpoint (user override), WithDefaultEndpoint, and WithDefaultMTLSEndpoint.
@ -535,7 +539,7 @@ func v4SanitizeHeaders(hdrs []string) []string {
sanitizedHeader := strings.TrimSpace(hdr)
var key, value string
headerMatches := strings.Split(sanitizedHeader, ":")
headerMatches := strings.SplitN(sanitizedHeader, ":", 2)
if len(headerMatches) < 2 {
continue
}
@ -649,7 +653,7 @@ var utcNow = func() time.Time {
func extractHeaderNames(kvs []string) []string {
var res []string
for _, header := range kvs {
nameValue := strings.Split(header, ":")
nameValue := strings.SplitN(header, ":", 2)
res = append(res, nameValue[0])
}
return res
@ -793,7 +797,7 @@ func sortHeadersByKey(hdrs []string) []string {
headersMap := map[string]string{}
var headersKeys []string
for _, h := range hdrs {
parts := strings.Split(h, ":")
parts := strings.SplitN(h, ":", 2)
k := parts[0]
v := parts[1]
headersMap[k] = v
@ -1315,6 +1319,11 @@ type ObjectAttrs struct {
// later value but not to an earlier one. For more information see
// https://cloud.google.com/storage/docs/metadata#custom-time .
CustomTime time.Time
// ComponentCount is the number of objects contained within a composite object.
// For non-composite objects, the value will be zero.
// This field is read-only.
ComponentCount int64
}
// convertTime converts a time in RFC3339 format to time.Time.
@ -1385,6 +1394,7 @@ func newObject(o *raw.Object) *ObjectAttrs {
Updated: convertTime(o.Updated),
Etag: o.Etag,
CustomTime: convertTime(o.CustomTime),
ComponentCount: o.ComponentCount,
}
}
@ -1419,6 +1429,7 @@ func newObjectFromProto(o *storagepb.Object) *ObjectAttrs {
Deleted: convertProtoTime(o.GetDeleteTime()),
Updated: convertProtoTime(o.GetUpdateTime()),
CustomTime: convertProtoTime(o.GetCustomTime()),
ComponentCount: int64(o.ComponentCount),
}
}
@ -1547,6 +1558,7 @@ var attrToFieldMap = map[string]string{
"Updated": "updated",
"Etag": "etag",
"CustomTime": "customTime",
"ComponentCount": "componentCount",
}
// attrToProtoFieldMap maps the field names of ObjectAttrs to the underlying field
@ -1578,6 +1590,7 @@ var attrToProtoFieldMap = map[string]string{
"Owner": "owner",
"CustomerKeySHA256": "customer_encryption",
"CustomTime": "custom_time",
"ComponentCount": "component_count",
// MediaLink was explicitly excluded from the proto as it is an HTTP-ism.
// "MediaLink": "mediaLink",
}
@ -1704,6 +1717,8 @@ type Conditions struct {
// GenerationNotMatch specifies that the object must not have the given
// generation for the operation to occur.
// If GenerationNotMatch is zero, it has no effect.
// This condition only works for object reads if the WithJSONReads client
// option is set.
GenerationNotMatch int64
// DoesNotExist specifies that the object must not exist in the bucket for
@ -1722,6 +1737,8 @@ type Conditions struct {
// MetagenerationNotMatch specifies that the object must not have the given
// metageneration for the operation to occur.
// If MetagenerationNotMatch is zero, it has no effect.
// This condition only works for object reads if the WithJSONReads client
// option is set.
MetagenerationNotMatch int64
}
@ -2079,6 +2096,25 @@ func toProtoCommonObjectRequestParams(key []byte) *storagepb.CommonObjectRequest
}
}
func toProtoChecksums(sendCRC32C bool, attrs *ObjectAttrs) *storagepb.ObjectChecksums {
var checksums *storagepb.ObjectChecksums
if sendCRC32C {
checksums = &storagepb.ObjectChecksums{
Crc32C: proto.Uint32(attrs.CRC32C),
}
}
if len(attrs.MD5) != 0 {
if checksums == nil {
checksums = &storagepb.ObjectChecksums{
Md5Hash: attrs.MD5,
}
} else {
checksums.Md5Hash = attrs.MD5
}
}
return checksums
}
// ServiceAccount fetches the email address of the given project's Google Cloud Storage service account.
func (c *Client) ServiceAccount(ctx context.Context, projectID string) (string, error) {
o := makeStorageOpts(true, c.retry, "")