aboutsummaryrefslogtreecommitdiff
path: root/vendor/cloud.google.com/go/storage
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/storage')
-rw-r--r--vendor/cloud.google.com/go/storage/acl.go36
-rw-r--r--vendor/cloud.google.com/go/storage/bucket.go10
-rw-r--r--vendor/cloud.google.com/go/storage/copy.go22
-rw-r--r--vendor/cloud.google.com/go/storage/doc.go5
-rw-r--r--vendor/cloud.google.com/go/storage/iam.go99
-rw-r--r--vendor/cloud.google.com/go/storage/reader.go17
-rw-r--r--vendor/cloud.google.com/go/storage/storage.go84
-rw-r--r--vendor/cloud.google.com/go/storage/writer.go18
8 files changed, 247 insertions, 44 deletions
diff --git a/vendor/cloud.google.com/go/storage/acl.go b/vendor/cloud.google.com/go/storage/acl.go
index 6c24394..041e541 100644
--- a/vendor/cloud.google.com/go/storage/acl.go
+++ b/vendor/cloud.google.com/go/storage/acl.go
@@ -96,7 +96,9 @@ func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.ObjectAccessControls
var err error
err = runWithRetry(ctx, func() error {
- acls, err = a.c.raw.DefaultObjectAccessControls.List(a.bucket).Context(ctx).Do()
+ req := a.c.raw.DefaultObjectAccessControls.List(a.bucket).Context(ctx)
+ setClientHeader(req.Header())
+ acls, err = req.Do()
return err
})
if err != nil {
@@ -112,7 +114,9 @@ func (a *ACLHandle) bucketDefaultSet(ctx context.Context, entity ACLEntity, role
Role: string(role),
}
err := runWithRetry(ctx, func() error {
- _, err := a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl).Context(ctx).Do()
+ req := a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl).Context(ctx)
+ setClientHeader(req.Header())
+ _, err := req.Do()
return err
})
if err != nil {
@@ -123,7 +127,9 @@ func (a *ACLHandle) bucketDefaultSet(ctx context.Context, entity ACLEntity, role
func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error {
err := runWithRetry(ctx, func() error {
- return a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity)).Context(ctx).Do()
+ req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity)).Context(ctx)
+ setClientHeader(req.Header())
+ return req.Do()
})
if err != nil {
return fmt.Errorf("storage: error deleting default ACL entry for bucket %q, entity %q: %v", a.bucket, entity, err)
@@ -135,7 +141,9 @@ func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.BucketAccessControls
var err error
err = runWithRetry(ctx, func() error {
- acls, err = a.c.raw.BucketAccessControls.List(a.bucket).Context(ctx).Do()
+ req := a.c.raw.BucketAccessControls.List(a.bucket).Context(ctx)
+ setClientHeader(req.Header())
+ acls, err = req.Do()
return err
})
if err != nil {
@@ -156,7 +164,9 @@ func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRol
Role: string(role),
}
err := runWithRetry(ctx, func() error {
- _, err := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl).Context(ctx).Do()
+ req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl).Context(ctx)
+ setClientHeader(req.Header())
+ _, err := req.Do()
return err
})
if err != nil {
@@ -167,7 +177,9 @@ func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRol
func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error {
err := runWithRetry(ctx, func() error {
- return a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity)).Context(ctx).Do()
+ req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity)).Context(ctx)
+ setClientHeader(req.Header())
+ return req.Do()
})
if err != nil {
return fmt.Errorf("storage: error deleting bucket ACL entry for bucket %q, entity %q: %v", a.bucket, entity, err)
@@ -179,7 +191,9 @@ func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.ObjectAccessControls
var err error
err = runWithRetry(ctx, func() error {
- acls, err = a.c.raw.ObjectAccessControls.List(a.bucket, a.object).Context(ctx).Do()
+ req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object).Context(ctx)
+ setClientHeader(req.Header())
+ acls, err = req.Do()
return err
})
if err != nil {
@@ -195,7 +209,9 @@ func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRol
Role: string(role),
}
err := runWithRetry(ctx, func() error {
- _, err := a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl).Context(ctx).Do()
+ req := a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl).Context(ctx)
+ setClientHeader(req.Header())
+ _, err := req.Do()
return err
})
if err != nil {
@@ -206,7 +222,9 @@ func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRol
func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error {
err := runWithRetry(ctx, func() error {
- return a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity)).Context(ctx).Do()
+ req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity)).Context(ctx)
+ setClientHeader(req.Header())
+ return req.Do()
})
if err != nil {
return fmt.Errorf("storage: error deleting object ACL entry for bucket %q, file %q, entity %q: %v", a.bucket, a.object, entity, err)
diff --git a/vendor/cloud.google.com/go/storage/bucket.go b/vendor/cloud.google.com/go/storage/bucket.go
index f87fe33..deed5b2 100644
--- a/vendor/cloud.google.com/go/storage/bucket.go
+++ b/vendor/cloud.google.com/go/storage/bucket.go
@@ -35,12 +35,14 @@ func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *Buck
}
bkt.Name = b.name
req := b.c.raw.Buckets.Insert(projectID, bkt)
+ setClientHeader(req.Header())
return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
}
// Delete deletes the Bucket.
func (b *BucketHandle) Delete(ctx context.Context) error {
req := b.c.raw.Buckets.Delete(b.name)
+ setClientHeader(req.Header())
return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
}
@@ -80,10 +82,12 @@ func (b *BucketHandle) Object(name string) *ObjectHandle {
// Attrs returns the metadata for the bucket.
func (b *BucketHandle) Attrs(ctx context.Context) (*BucketAttrs, error) {
+ req := b.c.raw.Buckets.Get(b.name).Projection("full")
+ setClientHeader(req.Header())
var resp *raw.Bucket
var err error
err = runWithRetry(ctx, func() error {
- resp, err = b.c.raw.Buckets.Get(b.name).Projection("full").Context(ctx).Do()
+ resp, err = req.Context(ctx).Do()
return err
})
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
@@ -113,7 +117,7 @@ type BucketAttrs struct {
// MetaGeneration is the metadata generation of the bucket.
MetaGeneration int64
- // StorageClass is the storage class of the bucket. This defines
+ // StorageClass is the default storage class of the bucket. This defines
// how objects in the bucket are stored and determines the SLA
// and the cost of storage. Typical values are "MULTI_REGIONAL",
// "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
@@ -231,6 +235,7 @@ func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
req := it.bucket.c.raw.Objects.List(it.bucket.name)
+ setClientHeader(req.Header())
req.Projection("full")
req.Delimiter(it.query.Delimiter)
req.Prefix(it.query.Prefix)
@@ -309,6 +314,7 @@ func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
func (it *BucketIterator) fetch(pageSize int, pageToken string) (string, error) {
req := it.client.raw.Buckets.List(it.projectID)
+ setClientHeader(req.Header())
req.Projection("full")
req.Prefix(it.Prefix)
req.PageToken(pageToken)
diff --git a/vendor/cloud.google.com/go/storage/copy.go b/vendor/cloud.google.com/go/storage/copy.go
index 1f28455..16e28e3 100644
--- a/vendor/cloud.google.com/go/storage/copy.go
+++ b/vendor/cloud.google.com/go/storage/copy.go
@@ -12,15 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package storage contains a Google Cloud Storage client.
-//
-// This package is experimental and may make backwards-incompatible changes.
package storage
import (
"errors"
"fmt"
- "reflect"
"golang.org/x/net/context"
raw "google.golang.org/api/storage/v1"
@@ -71,17 +67,11 @@ func (c *Copier) Run(ctx context.Context) (*ObjectAttrs, error) {
if err := c.dst.validate(); err != nil {
return nil, err
}
- var rawObject *raw.Object
- // If any attribute was set, then we make sure the name matches the destination
- // name, and we check that ContentType is non-empty so we can provide a better
- // error message than the service.
- if !reflect.DeepEqual(c.ObjectAttrs, ObjectAttrs{}) {
- c.ObjectAttrs.Name = c.dst.object
- if c.ObjectAttrs.ContentType == "" {
- return nil, errors.New("storage: Copier.ContentType must be non-empty")
- }
- rawObject = c.ObjectAttrs.toRawObject(c.dst.bucket)
- }
+ // Convert destination attributes to raw form, omitting the bucket.
+ // If the bucket is included but name or content-type aren't, the service
+ // returns a 400 with "Required" as the only message. Omitting the bucket
+ // does not cause any problems.
+ rawObject := c.ObjectAttrs.toRawObject("")
for {
res, err := c.callRewrite(ctx, c.src, rawObject)
if err != nil {
@@ -118,6 +108,7 @@ func (c *Copier) callRewrite(ctx context.Context, src *ObjectHandle, rawObj *raw
}
var res *raw.RewriteResponse
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
if err != nil {
return nil, err
@@ -189,6 +180,7 @@ func (c *Composer) Run(ctx context.Context) (*ObjectAttrs, error) {
}
var obj *raw.Object
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if err != nil {
return nil, err
diff --git a/vendor/cloud.google.com/go/storage/doc.go b/vendor/cloud.google.com/go/storage/doc.go
index cf6496b..951391f 100644
--- a/vendor/cloud.google.com/go/storage/doc.go
+++ b/vendor/cloud.google.com/go/storage/doc.go
@@ -152,5 +152,10 @@ SignedURL for details.
// TODO: Handle error.
}
fmt.Println(url)
+
+Authentication
+
+See examples of authorization and authentication at
+https://godoc.org/cloud.google.com/go#pkg-examples.
*/
package storage // import "cloud.google.com/go/storage"
diff --git a/vendor/cloud.google.com/go/storage/iam.go b/vendor/cloud.google.com/go/storage/iam.go
new file mode 100644
index 0000000..0cd9501
--- /dev/null
+++ b/vendor/cloud.google.com/go/storage/iam.go
@@ -0,0 +1,99 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// 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 (
+ "errors"
+
+ "cloud.google.com/go/iam"
+ "golang.org/x/net/context"
+ raw "google.golang.org/api/storage/v1"
+ iampb "google.golang.org/genproto/googleapis/iam/v1"
+)
+
+// IAM provides access to IAM access control for the bucket.
+func (b *BucketHandle) IAM() *iam.Handle {
+ return iam.InternalNewHandleClient(&iamClient{raw: b.c.raw}, b.name)
+}
+
+// iamClient implements the iam.client interface.
+type iamClient struct {
+ raw *raw.Service
+}
+
+func (c *iamClient) Get(ctx context.Context, resource string) (*iampb.Policy, error) {
+ req := c.raw.Buckets.GetIamPolicy(resource)
+ setClientHeader(req.Header())
+ var rp *raw.Policy
+ var err error
+ err = runWithRetry(ctx, func() error {
+ rp, err = req.Context(ctx).Do()
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+ return iamFromStoragePolicy(rp), nil
+}
+
+func (c *iamClient) Set(ctx context.Context, resource string, p *iampb.Policy) error {
+ rp := iamToStoragePolicy(p)
+ req := c.raw.Buckets.SetIamPolicy(resource, rp)
+ setClientHeader(req.Header())
+ return runWithRetry(ctx, func() error {
+ _, err := req.Context(ctx).Do()
+ return err
+ })
+}
+
+func (c *iamClient) Test(context.Context, string, []string) ([]string, error) {
+ return nil, errors.New("TestPermissions is unimplemented")
+}
+
+func iamToStoragePolicy(ip *iampb.Policy) *raw.Policy {
+ return &raw.Policy{
+ Bindings: iamToStorageBindings(ip.Bindings),
+ Etag: string(ip.Etag),
+ }
+}
+
+func iamToStorageBindings(ibs []*iampb.Binding) []*raw.PolicyBindings {
+ var rbs []*raw.PolicyBindings
+ for _, ib := range ibs {
+ rbs = append(rbs, &raw.PolicyBindings{
+ Role: ib.Role,
+ Members: ib.Members,
+ })
+ }
+ return rbs
+}
+
+func iamFromStoragePolicy(rp *raw.Policy) *iampb.Policy {
+ return &iampb.Policy{
+ Bindings: iamFromStorageBindings(rp.Bindings),
+ Etag: []byte(rp.Etag),
+ }
+}
+
+func iamFromStorageBindings(rbs []*raw.PolicyBindings) []*iampb.Binding {
+ var ibs []*iampb.Binding
+ for _, rb := range rbs {
+ ibs = append(ibs, &iampb.Binding{
+ Role: rb.Role,
+ Members: rb.Members,
+ })
+ }
+ return ibs
+}
diff --git a/vendor/cloud.google.com/go/storage/reader.go b/vendor/cloud.google.com/go/storage/reader.go
index 329a5f3..aa103c1 100644
--- a/vendor/cloud.google.com/go/storage/reader.go
+++ b/vendor/cloud.google.com/go/storage/reader.go
@@ -15,15 +15,22 @@
package storage
import (
+ "fmt"
+ "hash/crc32"
"io"
)
+var crc32cTable = crc32.MakeTable(crc32.Castagnoli)
+
// Reader reads a Cloud Storage object.
// It implements io.Reader.
type Reader struct {
body io.ReadCloser
remain, size int64
contentType string
+ checkCRC bool // should we check the CRC?
+ wantCRC uint32 // the CRC32c value the server sent in the header
+ gotCRC uint32 // running crc
}
// Close closes the Reader. It must be called when done reading.
@@ -36,6 +43,16 @@ func (r *Reader) Read(p []byte) (int, error) {
if r.remain != -1 {
r.remain -= int64(n)
}
+ if r.checkCRC {
+ r.gotCRC = crc32.Update(r.gotCRC, crc32cTable, p[:n])
+ // Check CRC here. It would be natural to check it in Close, but
+ // everybody defers Close on the assumption that it doesn't return
+ // anything worth looking at.
+ if r.remain == 0 && r.gotCRC != r.wantCRC {
+ return n, fmt.Errorf("storage: bad CRC on read: got %d, want %d",
+ r.gotCRC, r.wantCRC)
+ }
+ }
return n, err
}
diff --git a/vendor/cloud.google.com/go/storage/storage.go b/vendor/cloud.google.com/go/storage/storage.go
index 53fc029..6b82fa3 100644
--- a/vendor/cloud.google.com/go/storage/storage.go
+++ b/vendor/cloud.google.com/go/storage/storage.go
@@ -39,6 +39,7 @@ import (
"google.golang.org/api/transport"
"cloud.google.com/go/internal/optional"
+ "cloud.google.com/go/internal/version"
"golang.org/x/net/context"
"google.golang.org/api/googleapi"
raw "google.golang.org/api/storage/v1"
@@ -65,6 +66,12 @@ const (
ScopeReadWrite = raw.DevstorageReadWriteScope
)
+var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
+
+func setClientHeader(headers http.Header) {
+ headers.Set("x-goog-api-client", xGoogHeader)
+}
+
// Client is a client for interacting with Google Cloud Storage.
//
// Clients should be reused instead of created as needed.
@@ -202,7 +209,7 @@ type SignedURLOptions struct {
// If provided, the client should provide the exact value on the request
// header in order to use the signed URL.
// Optional.
- MD5 []byte
+ MD5 string
}
// SignedURL returns a URL for the specified object. Signed URLs allow
@@ -225,6 +232,12 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
if opts.Expires.IsZero() {
return "", errors.New("storage: missing required expires option")
}
+ if opts.MD5 != "" {
+ md5, err := base64.StdEncoding.DecodeString(opts.MD5)
+ if err != nil || len(md5) != 16 {
+ return "", errors.New("storage: invalid MD5 checksum")
+ }
+ }
signBytes := opts.SignBytes
if opts.PrivateKey != nil {
@@ -241,8 +254,6 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
sum[:],
)
}
- } else {
- signBytes = opts.SignBytes
}
u := &url.URL{
@@ -254,7 +265,9 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
fmt.Fprintf(buf, "%s\n", opts.MD5)
fmt.Fprintf(buf, "%s\n", opts.ContentType)
fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
- fmt.Fprintf(buf, "%s", strings.Join(opts.Headers, "\n"))
+ if len(opts.Headers) > 0 {
+ fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
+ }
fmt.Fprintf(buf, "%s", u.String())
b, err := signBytes(buf.Bytes())
@@ -339,6 +352,7 @@ func (o *ObjectHandle) Attrs(ctx context.Context) (*ObjectAttrs, error) {
}
var obj *raw.Object
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
@@ -412,6 +426,7 @@ func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (
}
var obj *raw.Object
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
@@ -452,6 +467,7 @@ func (o *ObjectHandle) Delete(ctx context.Context) error {
if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
return err
}
+ setClientHeader(call.Header())
err := runWithRetry(ctx, func() error { return call.Do() })
switch e := err.(type) {
case nil:
@@ -560,15 +576,37 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
body.Close()
body = emptyBody
}
-
+ var (
+ checkCRC bool
+ crc uint32
+ )
+ // Even if there is a CRC header, we can't compute the hash on partial data.
+ if remain == size {
+ crc, checkCRC = parseCRC32c(res)
+ }
return &Reader{
body: body,
size: size,
remain: remain,
contentType: res.Header.Get("Content-Type"),
+ wantCRC: crc,
+ checkCRC: checkCRC,
}, nil
}
+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
+ }
+ }
+ }
+ return 0, false
+}
+
var emptyBody = ioutil.NopCloser(strings.NewReader(""))
// NewWriter returns a storage Writer that writes to the GCS object
@@ -656,6 +694,7 @@ func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
ContentLanguage: o.ContentLanguage,
CacheControl: o.CacheControl,
ContentDisposition: o.ContentDisposition,
+ StorageClass: o.StorageClass,
Acl: acl,
Metadata: o.Metadata,
}
@@ -717,21 +756,20 @@ type ObjectAttrs struct {
// This field is read-only.
Generation int64
- // MetaGeneration is the version of the metadata for this
+ // Metageneration is the version of the metadata for this
// object at this generation. This field is used for preconditions
// and for detecting changes in metadata. A metageneration number
// is only meaningful in the context of a particular generation
// of a particular object. This field is read-only.
- MetaGeneration int64
+ Metageneration int64
- // StorageClass is the storage class of the bucket.
+ // StorageClass is the storage class of the object.
// This value defines how objects in the bucket are stored and
// determines the SLA and the cost of storage. Typical values are
// "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
// and "DURABLE_REDUCED_AVAILABILITY".
// It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
- // or "REGIONAL" depending on the bucket's location settings. This
- // field is read-only.
+ // or "REGIONAL" depending on the bucket's location settings.
StorageClass string
// Created is the time the object was created. This field is read-only.
@@ -786,11 +824,7 @@ func newObject(o *raw.Object) *ObjectAttrs {
owner = o.Owner.Entity
}
md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
- var crc32c uint32
- d, err := base64.StdEncoding.DecodeString(o.Crc32c)
- if err == nil && len(d) == 4 {
- crc32c = uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3])
- }
+ crc32c, _ := decodeUint32(o.Crc32c)
var sha256 string
if o.CustomerEncryption != nil {
sha256 = o.CustomerEncryption.KeySha256
@@ -810,7 +844,7 @@ func newObject(o *raw.Object) *ObjectAttrs {
MediaLink: o.MediaLink,
Metadata: o.Metadata,
Generation: o.Generation,
- MetaGeneration: o.Metageneration,
+ Metageneration: o.Metageneration,
StorageClass: o.StorageClass,
CustomerKeySHA256: sha256,
Created: convertTime(o.TimeCreated),
@@ -819,6 +853,24 @@ func newObject(o *raw.Object) *ObjectAttrs {
}
}
+// Decode a uint32 encoded in Base64 in big-endian byte order.
+func decodeUint32(b64 string) (uint32, error) {
+ d, err := base64.StdEncoding.DecodeString(b64)
+ if err != nil {
+ return 0, err
+ }
+ if len(d) != 4 {
+ return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
+ }
+ return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
+}
+
+// Encode a uint32 as Base64 in big-endian byte order.
+func encodeUint32(u uint32) string {
+ b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
+ return base64.StdEncoding.EncodeToString(b)
+}
+
// Query represents a query to filter objects from a bucket.
type Query struct {
// Delimiter returns results in a directory-like fashion.
diff --git a/vendor/cloud.google.com/go/storage/writer.go b/vendor/cloud.google.com/go/storage/writer.go
index a7a9329..21ffdb9 100644
--- a/vendor/cloud.google.com/go/storage/writer.go
+++ b/vendor/cloud.google.com/go/storage/writer.go
@@ -15,6 +15,7 @@
package storage
import (
+ "encoding/base64"
"errors"
"fmt"
"io"
@@ -32,6 +33,11 @@ type Writer struct {
// attributes are ignored.
ObjectAttrs
+ // SendCRC specifies whether to transmit a CRC32C field. It should be set
+ // to true in addition to setting the Writer's CRC32C field, because zero
+ // is a valid CRC and normally a zero would not be transmitted.
+ SendCRC32C bool
+
// ChunkSize controls the maximum number of bytes of the object that the
// Writer will attempt to send to the server in a single request. Objects
// smaller than the size will be sent in a single request, while larger
@@ -81,7 +87,14 @@ func (w *Writer) open() error {
go func() {
defer close(w.donec)
- call := w.o.c.raw.Objects.Insert(w.o.bucket, attrs.toRawObject(w.o.bucket)).
+ rawObj := attrs.toRawObject(w.o.bucket)
+ if w.SendCRC32C {
+ rawObj.Crc32c = encodeUint32(attrs.CRC32C)
+ }
+ if w.MD5 != nil {
+ rawObj.Md5Hash = base64.StdEncoding.EncodeToString(w.MD5)
+ }
+ call := w.o.c.raw.Objects.Insert(w.o.bucket, rawObj).
Media(pr, mediaOpts...).
Projection("full").
Context(w.ctx)
@@ -93,6 +106,7 @@ func (w *Writer) open() error {
var resp *raw.Object
err := applyConds("NewWriter", w.o.gen, w.o.conds, call)
if err == nil {
+ setClientHeader(call.Header())
resp, err = call.Do()
}
if err != nil {
@@ -120,7 +134,7 @@ func (w *Writer) Write(p []byte) (n int, err error) {
// Close completes the write operation and flushes any buffered data.
// If Close doesn't return an error, metadata about the written object
-// can be retrieved by calling Object.
+// can be retrieved by calling Attrs.
func (w *Writer) Close() error {
if !w.opened {
if err := w.open(); err != nil {