]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/cloud.google.com/go/storage/storage.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / cloud.google.com / go / storage / storage.go
1 // Copyright 2014 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package storage
16
17 import (
18 "bytes"
19 "context"
20 "crypto"
21 "crypto/rand"
22 "crypto/rsa"
23 "crypto/sha256"
24 "crypto/x509"
25 "encoding/base64"
26 "encoding/pem"
27 "errors"
28 "fmt"
29 "net/http"
30 "net/url"
31 "reflect"
32 "regexp"
33 "sort"
34 "strconv"
35 "strings"
36 "time"
37 "unicode/utf8"
38
39 "cloud.google.com/go/internal/optional"
40 "cloud.google.com/go/internal/trace"
41 "cloud.google.com/go/internal/version"
42 "google.golang.org/api/googleapi"
43 "google.golang.org/api/option"
44 raw "google.golang.org/api/storage/v1"
45 htransport "google.golang.org/api/transport/http"
46 )
47
48 var (
49 // ErrBucketNotExist indicates that the bucket does not exist.
50 ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
51 // ErrObjectNotExist indicates that the object does not exist.
52 ErrObjectNotExist = errors.New("storage: object doesn't exist")
53 )
54
55 const userAgent = "gcloud-golang-storage/20151204"
56
57 const (
58 // ScopeFullControl grants permissions to manage your
59 // data and permissions in Google Cloud Storage.
60 ScopeFullControl = raw.DevstorageFullControlScope
61
62 // ScopeReadOnly grants permissions to
63 // view your data in Google Cloud Storage.
64 ScopeReadOnly = raw.DevstorageReadOnlyScope
65
66 // ScopeReadWrite grants permissions to manage your
67 // data in Google Cloud Storage.
68 ScopeReadWrite = raw.DevstorageReadWriteScope
69 )
70
71 var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
72
73 func setClientHeader(headers http.Header) {
74 headers.Set("x-goog-api-client", xGoogHeader)
75 }
76
77 // Client is a client for interacting with Google Cloud Storage.
78 //
79 // Clients should be reused instead of created as needed.
80 // The methods of Client are safe for concurrent use by multiple goroutines.
81 type Client struct {
82 hc *http.Client
83 raw *raw.Service
84 }
85
86 // NewClient creates a new Google Cloud Storage client.
87 // The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes.
88 func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
89 o := []option.ClientOption{
90 option.WithScopes(ScopeFullControl),
91 option.WithUserAgent(userAgent),
92 }
93 opts = append(o, opts...)
94 hc, ep, err := htransport.NewClient(ctx, opts...)
95 if err != nil {
96 return nil, fmt.Errorf("dialing: %v", err)
97 }
98 rawService, err := raw.New(hc)
99 if err != nil {
100 return nil, fmt.Errorf("storage client: %v", err)
101 }
102 if ep != "" {
103 rawService.BasePath = ep
104 }
105 return &Client{
106 hc: hc,
107 raw: rawService,
108 }, nil
109 }
110
111 // Close closes the Client.
112 //
113 // Close need not be called at program exit.
114 func (c *Client) Close() error {
115 // Set fields to nil so that subsequent uses will panic.
116 c.hc = nil
117 c.raw = nil
118 return nil
119 }
120
121 // SignedURLOptions allows you to restrict the access to the signed URL.
122 type SignedURLOptions struct {
123 // GoogleAccessID represents the authorizer of the signed URL generation.
124 // It is typically the Google service account client email address from
125 // the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
126 // Required.
127 GoogleAccessID string
128
129 // PrivateKey is the Google service account private key. It is obtainable
130 // from the Google Developers Console.
131 // At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
132 // create a service account client ID or reuse one of your existing service account
133 // credentials. Click on the "Generate new P12 key" to generate and download
134 // a new private key. Once you download the P12 file, use the following command
135 // to convert it into a PEM file.
136 //
137 // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
138 //
139 // Provide the contents of the PEM file as a byte slice.
140 // Exactly one of PrivateKey or SignBytes must be non-nil.
141 PrivateKey []byte
142
143 // SignBytes is a function for implementing custom signing.
144 // If your application is running on Google App Engine, you can use appengine's internal signing function:
145 // ctx := appengine.NewContext(request)
146 // acc, _ := appengine.ServiceAccount(ctx)
147 // url, err := SignedURL("bucket", "object", &SignedURLOptions{
148 // GoogleAccessID: acc,
149 // SignBytes: func(b []byte) ([]byte, error) {
150 // _, signedBytes, err := appengine.SignBytes(ctx, b)
151 // return signedBytes, err
152 // },
153 // // etc.
154 // })
155 //
156 // Exactly one of PrivateKey or SignBytes must be non-nil.
157 SignBytes func([]byte) ([]byte, error)
158
159 // Method is the HTTP method to be used with the signed URL.
160 // Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
161 // Required.
162 Method string
163
164 // Expires is the expiration time on the signed URL. It must be
165 // a datetime in the future.
166 // Required.
167 Expires time.Time
168
169 // ContentType is the content type header the client must provide
170 // to use the generated signed URL.
171 // Optional.
172 ContentType string
173
174 // Headers is a list of extension headers the client must provide
175 // in order to use the generated signed URL.
176 // Optional.
177 Headers []string
178
179 // MD5 is the base64 encoded MD5 checksum of the file.
180 // If provided, the client should provide the exact value on the request
181 // header in order to use the signed URL.
182 // Optional.
183 MD5 string
184 }
185
186 var (
187 canonicalHeaderRegexp = regexp.MustCompile(`(?i)^(x-goog-[^:]+):(.*)?$`)
188 excludedCanonicalHeaders = map[string]bool{
189 "x-goog-encryption-key": true,
190 "x-goog-encryption-key-sha256": true,
191 }
192 )
193
194 // sanitizeHeaders applies the specifications for canonical extension headers at
195 // https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
196 func sanitizeHeaders(hdrs []string) []string {
197 headerMap := map[string][]string{}
198 for _, hdr := range hdrs {
199 // No leading or trailing whitespaces.
200 sanitizedHeader := strings.TrimSpace(hdr)
201
202 // Only keep canonical headers, discard any others.
203 headerMatches := canonicalHeaderRegexp.FindStringSubmatch(sanitizedHeader)
204 if len(headerMatches) == 0 {
205 continue
206 }
207
208 header := strings.ToLower(strings.TrimSpace(headerMatches[1]))
209 if excludedCanonicalHeaders[headerMatches[1]] {
210 // Do not keep any deliberately excluded canonical headers when signing.
211 continue
212 }
213 value := strings.TrimSpace(headerMatches[2])
214 if len(value) > 0 {
215 // Remove duplicate headers by appending the values of duplicates
216 // in their order of appearance.
217 headerMap[header] = append(headerMap[header], value)
218 }
219 }
220
221 var sanitizedHeaders []string
222 for header, values := range headerMap {
223 // There should be no spaces around the colon separating the
224 // header name from the header value or around the values
225 // themselves. The values should be separated by commas.
226 // NOTE: The semantics for headers without a value are not clear.
227 // However from specifications these should be edge-cases
228 // anyway and we should assume that there will be no
229 // canonical headers using empty values. Any such headers
230 // are discarded at the regexp stage above.
231 sanitizedHeaders = append(
232 sanitizedHeaders,
233 fmt.Sprintf("%s:%s", header, strings.Join(values, ",")),
234 )
235 }
236 sort.Strings(sanitizedHeaders)
237 return sanitizedHeaders
238 }
239
240 // SignedURL returns a URL for the specified object. Signed URLs allow
241 // the users access to a restricted resource for a limited time without having a
242 // Google account or signing in. For more information about the signed
243 // URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
244 func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
245 if opts == nil {
246 return "", errors.New("storage: missing required SignedURLOptions")
247 }
248 if opts.GoogleAccessID == "" {
249 return "", errors.New("storage: missing required GoogleAccessID")
250 }
251 if (opts.PrivateKey == nil) == (opts.SignBytes == nil) {
252 return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
253 }
254 if opts.Method == "" {
255 return "", errors.New("storage: missing required method option")
256 }
257 if opts.Expires.IsZero() {
258 return "", errors.New("storage: missing required expires option")
259 }
260 if opts.MD5 != "" {
261 md5, err := base64.StdEncoding.DecodeString(opts.MD5)
262 if err != nil || len(md5) != 16 {
263 return "", errors.New("storage: invalid MD5 checksum")
264 }
265 }
266 opts.Headers = sanitizeHeaders(opts.Headers)
267
268 signBytes := opts.SignBytes
269 if opts.PrivateKey != nil {
270 key, err := parseKey(opts.PrivateKey)
271 if err != nil {
272 return "", err
273 }
274 signBytes = func(b []byte) ([]byte, error) {
275 sum := sha256.Sum256(b)
276 return rsa.SignPKCS1v15(
277 rand.Reader,
278 key,
279 crypto.SHA256,
280 sum[:],
281 )
282 }
283 }
284
285 u := &url.URL{
286 Path: fmt.Sprintf("/%s/%s", bucket, name),
287 }
288
289 buf := &bytes.Buffer{}
290 fmt.Fprintf(buf, "%s\n", opts.Method)
291 fmt.Fprintf(buf, "%s\n", opts.MD5)
292 fmt.Fprintf(buf, "%s\n", opts.ContentType)
293 fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
294 if len(opts.Headers) > 0 {
295 fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
296 }
297 fmt.Fprintf(buf, "%s", u.String())
298
299 b, err := signBytes(buf.Bytes())
300 if err != nil {
301 return "", err
302 }
303 encoded := base64.StdEncoding.EncodeToString(b)
304 u.Scheme = "https"
305 u.Host = "storage.googleapis.com"
306 q := u.Query()
307 q.Set("GoogleAccessId", opts.GoogleAccessID)
308 q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
309 q.Set("Signature", string(encoded))
310 u.RawQuery = q.Encode()
311 return u.String(), nil
312 }
313
314 // ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
315 // Use BucketHandle.Object to get a handle.
316 type ObjectHandle struct {
317 c *Client
318 bucket string
319 object string
320 acl ACLHandle
321 gen int64 // a negative value indicates latest
322 conds *Conditions
323 encryptionKey []byte // AES-256 key
324 userProject string // for requester-pays buckets
325 readCompressed bool // Accept-Encoding: gzip
326 }
327
328 // ACL provides access to the object's access control list.
329 // This controls who can read and write this object.
330 // This call does not perform any network operations.
331 func (o *ObjectHandle) ACL() *ACLHandle {
332 return &o.acl
333 }
334
335 // Generation returns a new ObjectHandle that operates on a specific generation
336 // of the object.
337 // By default, the handle operates on the latest generation. Not
338 // all operations work when given a specific generation; check the API
339 // endpoints at https://cloud.google.com/storage/docs/json_api/ for details.
340 func (o *ObjectHandle) Generation(gen int64) *ObjectHandle {
341 o2 := *o
342 o2.gen = gen
343 return &o2
344 }
345
346 // If returns a new ObjectHandle that applies a set of preconditions.
347 // Preconditions already set on the ObjectHandle are ignored.
348 // Operations on the new handle will return an error if the preconditions are not
349 // satisfied. See https://cloud.google.com/storage/docs/generations-preconditions
350 // for more details.
351 func (o *ObjectHandle) If(conds Conditions) *ObjectHandle {
352 o2 := *o
353 o2.conds = &conds
354 return &o2
355 }
356
357 // Key returns a new ObjectHandle that uses the supplied encryption
358 // key to encrypt and decrypt the object's contents.
359 //
360 // Encryption key must be a 32-byte AES-256 key.
361 // See https://cloud.google.com/storage/docs/encryption for details.
362 func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle {
363 o2 := *o
364 o2.encryptionKey = encryptionKey
365 return &o2
366 }
367
368 // Attrs returns meta information about the object.
369 // ErrObjectNotExist will be returned if the object is not found.
370 func (o *ObjectHandle) Attrs(ctx context.Context) (attrs *ObjectAttrs, err error) {
371 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Attrs")
372 defer func() { trace.EndSpan(ctx, err) }()
373
374 if err := o.validate(); err != nil {
375 return nil, err
376 }
377 call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx)
378 if err := applyConds("Attrs", o.gen, o.conds, call); err != nil {
379 return nil, err
380 }
381 if o.userProject != "" {
382 call.UserProject(o.userProject)
383 }
384 if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
385 return nil, err
386 }
387 var obj *raw.Object
388 setClientHeader(call.Header())
389 err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
390 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
391 return nil, ErrObjectNotExist
392 }
393 if err != nil {
394 return nil, err
395 }
396 return newObject(obj), nil
397 }
398
399 // Update updates an object with the provided attributes.
400 // All zero-value attributes are ignored.
401 // ErrObjectNotExist will be returned if the object is not found.
402 func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (oa *ObjectAttrs, err error) {
403 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Update")
404 defer func() { trace.EndSpan(ctx, err) }()
405
406 if err := o.validate(); err != nil {
407 return nil, err
408 }
409 var attrs ObjectAttrs
410 // Lists of fields to send, and set to null, in the JSON.
411 var forceSendFields, nullFields []string
412 if uattrs.ContentType != nil {
413 attrs.ContentType = optional.ToString(uattrs.ContentType)
414 // For ContentType, sending the empty string is a no-op.
415 // Instead we send a null.
416 if attrs.ContentType == "" {
417 nullFields = append(nullFields, "ContentType")
418 } else {
419 forceSendFields = append(forceSendFields, "ContentType")
420 }
421 }
422 if uattrs.ContentLanguage != nil {
423 attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage)
424 // For ContentLanguage it's an error to send the empty string.
425 // Instead we send a null.
426 if attrs.ContentLanguage == "" {
427 nullFields = append(nullFields, "ContentLanguage")
428 } else {
429 forceSendFields = append(forceSendFields, "ContentLanguage")
430 }
431 }
432 if uattrs.ContentEncoding != nil {
433 attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding)
434 forceSendFields = append(forceSendFields, "ContentEncoding")
435 }
436 if uattrs.ContentDisposition != nil {
437 attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition)
438 forceSendFields = append(forceSendFields, "ContentDisposition")
439 }
440 if uattrs.CacheControl != nil {
441 attrs.CacheControl = optional.ToString(uattrs.CacheControl)
442 forceSendFields = append(forceSendFields, "CacheControl")
443 }
444 if uattrs.EventBasedHold != nil {
445 attrs.EventBasedHold = optional.ToBool(uattrs.EventBasedHold)
446 forceSendFields = append(forceSendFields, "EventBasedHold")
447 }
448 if uattrs.TemporaryHold != nil {
449 attrs.TemporaryHold = optional.ToBool(uattrs.TemporaryHold)
450 forceSendFields = append(forceSendFields, "TemporaryHold")
451 }
452 if uattrs.Metadata != nil {
453 attrs.Metadata = uattrs.Metadata
454 if len(attrs.Metadata) == 0 {
455 // Sending the empty map is a no-op. We send null instead.
456 nullFields = append(nullFields, "Metadata")
457 } else {
458 forceSendFields = append(forceSendFields, "Metadata")
459 }
460 }
461 if uattrs.ACL != nil {
462 attrs.ACL = uattrs.ACL
463 // It's an error to attempt to delete the ACL, so
464 // we don't append to nullFields here.
465 forceSendFields = append(forceSendFields, "Acl")
466 }
467 rawObj := attrs.toRawObject(o.bucket)
468 rawObj.ForceSendFields = forceSendFields
469 rawObj.NullFields = nullFields
470 call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx)
471 if err := applyConds("Update", o.gen, o.conds, call); err != nil {
472 return nil, err
473 }
474 if o.userProject != "" {
475 call.UserProject(o.userProject)
476 }
477 if uattrs.PredefinedACL != "" {
478 call.PredefinedAcl(uattrs.PredefinedACL)
479 }
480 if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
481 return nil, err
482 }
483 var obj *raw.Object
484 setClientHeader(call.Header())
485 err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
486 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
487 return nil, ErrObjectNotExist
488 }
489 if err != nil {
490 return nil, err
491 }
492 return newObject(obj), nil
493 }
494
495 // BucketName returns the name of the bucket.
496 func (o *ObjectHandle) BucketName() string {
497 return o.bucket
498 }
499
500 // ObjectName returns the name of the object.
501 func (o *ObjectHandle) ObjectName() string {
502 return o.object
503 }
504
505 // ObjectAttrsToUpdate is used to update the attributes of an object.
506 // Only fields set to non-nil values will be updated.
507 // Set a field to its zero value to delete it.
508 //
509 // For example, to change ContentType and delete ContentEncoding and
510 // Metadata, use
511 // ObjectAttrsToUpdate{
512 // ContentType: "text/html",
513 // ContentEncoding: "",
514 // Metadata: map[string]string{},
515 // }
516 type ObjectAttrsToUpdate struct {
517 EventBasedHold optional.Bool
518 TemporaryHold optional.Bool
519 ContentType optional.String
520 ContentLanguage optional.String
521 ContentEncoding optional.String
522 ContentDisposition optional.String
523 CacheControl optional.String
524 Metadata map[string]string // set to map[string]string{} to delete
525 ACL []ACLRule
526
527 // If not empty, applies a predefined set of access controls. ACL must be nil.
528 // See https://cloud.google.com/storage/docs/json_api/v1/objects/patch.
529 PredefinedACL string
530 }
531
532 // Delete deletes the single specified object.
533 func (o *ObjectHandle) Delete(ctx context.Context) error {
534 if err := o.validate(); err != nil {
535 return err
536 }
537 call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx)
538 if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
539 return err
540 }
541 if o.userProject != "" {
542 call.UserProject(o.userProject)
543 }
544 // Encryption doesn't apply to Delete.
545 setClientHeader(call.Header())
546 err := runWithRetry(ctx, func() error { return call.Do() })
547 switch e := err.(type) {
548 case nil:
549 return nil
550 case *googleapi.Error:
551 if e.Code == http.StatusNotFound {
552 return ErrObjectNotExist
553 }
554 }
555 return err
556 }
557
558 // ReadCompressed when true causes the read to happen without decompressing.
559 func (o *ObjectHandle) ReadCompressed(compressed bool) *ObjectHandle {
560 o2 := *o
561 o2.readCompressed = compressed
562 return &o2
563 }
564
565 // NewWriter returns a storage Writer that writes to the GCS object
566 // associated with this ObjectHandle.
567 //
568 // A new object will be created unless an object with this name already exists.
569 // Otherwise any previous object with the same name will be replaced.
570 // The object will not be available (and any previous object will remain)
571 // until Close has been called.
572 //
573 // Attributes can be set on the object by modifying the returned Writer's
574 // ObjectAttrs field before the first call to Write. If no ContentType
575 // attribute is specified, the content type will be automatically sniffed
576 // using net/http.DetectContentType.
577 //
578 // It is the caller's responsibility to call Close when writing is done. To
579 // stop writing without saving the data, cancel the context.
580 func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer {
581 return &Writer{
582 ctx: ctx,
583 o: o,
584 donec: make(chan struct{}),
585 ObjectAttrs: ObjectAttrs{Name: o.object},
586 ChunkSize: googleapi.DefaultUploadChunkSize,
587 }
588 }
589
590 func (o *ObjectHandle) validate() error {
591 if o.bucket == "" {
592 return errors.New("storage: bucket name is empty")
593 }
594 if o.object == "" {
595 return errors.New("storage: object name is empty")
596 }
597 if !utf8.ValidString(o.object) {
598 return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
599 }
600 return nil
601 }
602
603 // parseKey converts the binary contents of a private key file to an
604 // *rsa.PrivateKey. It detects whether the private key is in a PEM container or
605 // not. If so, it extracts the private key from PEM container before
606 // conversion. It only supports PEM containers with no passphrase.
607 func parseKey(key []byte) (*rsa.PrivateKey, error) {
608 if block, _ := pem.Decode(key); block != nil {
609 key = block.Bytes
610 }
611 parsedKey, err := x509.ParsePKCS8PrivateKey(key)
612 if err != nil {
613 parsedKey, err = x509.ParsePKCS1PrivateKey(key)
614 if err != nil {
615 return nil, err
616 }
617 }
618 parsed, ok := parsedKey.(*rsa.PrivateKey)
619 if !ok {
620 return nil, errors.New("oauth2: private key is invalid")
621 }
622 return parsed, nil
623 }
624
625 // toRawObject copies the editable attributes from o to the raw library's Object type.
626 func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
627 var ret string
628 if !o.RetentionExpirationTime.IsZero() {
629 ret = o.RetentionExpirationTime.Format(time.RFC3339)
630 }
631 return &raw.Object{
632 Bucket: bucket,
633 Name: o.Name,
634 EventBasedHold: o.EventBasedHold,
635 TemporaryHold: o.TemporaryHold,
636 RetentionExpirationTime: ret,
637 ContentType: o.ContentType,
638 ContentEncoding: o.ContentEncoding,
639 ContentLanguage: o.ContentLanguage,
640 CacheControl: o.CacheControl,
641 ContentDisposition: o.ContentDisposition,
642 StorageClass: o.StorageClass,
643 Acl: toRawObjectACL(o.ACL),
644 Metadata: o.Metadata,
645 }
646 }
647
648 // ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object.
649 type ObjectAttrs struct {
650 // Bucket is the name of the bucket containing this GCS object.
651 // This field is read-only.
652 Bucket string
653
654 // Name is the name of the object within the bucket.
655 // This field is read-only.
656 Name string
657
658 // ContentType is the MIME type of the object's content.
659 ContentType string
660
661 // ContentLanguage is the content language of the object's content.
662 ContentLanguage string
663
664 // CacheControl is the Cache-Control header to be sent in the response
665 // headers when serving the object data.
666 CacheControl string
667
668 // EventBasedHold specifies whether an object is under event-based hold. New
669 // objects created in a bucket whose DefaultEventBasedHold is set will
670 // default to that value.
671 EventBasedHold bool
672
673 // TemporaryHold specifies whether an object is under temporary hold. While
674 // this flag is set to true, the object is protected against deletion and
675 // overwrites.
676 TemporaryHold bool
677
678 // RetentionExpirationTime is a server-determined value that specifies the
679 // earliest time that the object's retention period expires.
680 // This is a read-only field.
681 RetentionExpirationTime time.Time
682
683 // ACL is the list of access control rules for the object.
684 ACL []ACLRule
685
686 // If not empty, applies a predefined set of access controls. It should be set
687 // only when writing, copying or composing an object. When copying or composing,
688 // it acts as the destinationPredefinedAcl parameter.
689 // PredefinedACL is always empty for ObjectAttrs returned from the service.
690 // See https://cloud.google.com/storage/docs/json_api/v1/objects/insert
691 // for valid values.
692 PredefinedACL string
693
694 // Owner is the owner of the object. This field is read-only.
695 //
696 // If non-zero, it is in the form of "user-<userId>".
697 Owner string
698
699 // Size is the length of the object's content. This field is read-only.
700 Size int64
701
702 // ContentEncoding is the encoding of the object's content.
703 ContentEncoding string
704
705 // ContentDisposition is the optional Content-Disposition header of the object
706 // sent in the response headers.
707 ContentDisposition string
708
709 // MD5 is the MD5 hash of the object's content. This field is read-only,
710 // except when used from a Writer. If set on a Writer, the uploaded
711 // data is rejected if its MD5 hash does not match this field.
712 MD5 []byte
713
714 // CRC32C is the CRC32 checksum of the object's content using
715 // the Castagnoli93 polynomial. This field is read-only, except when
716 // used from a Writer. If set on a Writer and Writer.SendCRC32C
717 // is true, the uploaded data is rejected if its CRC32c hash does not
718 // match this field.
719 CRC32C uint32
720
721 // MediaLink is an URL to the object's content. This field is read-only.
722 MediaLink string
723
724 // Metadata represents user-provided metadata, in key/value pairs.
725 // It can be nil if no metadata is provided.
726 Metadata map[string]string
727
728 // Generation is the generation number of the object's content.
729 // This field is read-only.
730 Generation int64
731
732 // Metageneration is the version of the metadata for this
733 // object at this generation. This field is used for preconditions
734 // and for detecting changes in metadata. A metageneration number
735 // is only meaningful in the context of a particular generation
736 // of a particular object. This field is read-only.
737 Metageneration int64
738
739 // StorageClass is the storage class of the object.
740 // This value defines how objects in the bucket are stored and
741 // determines the SLA and the cost of storage. Typical values are
742 // "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
743 // and "DURABLE_REDUCED_AVAILABILITY".
744 // It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
745 // or "REGIONAL" depending on the bucket's location settings.
746 StorageClass string
747
748 // Created is the time the object was created. This field is read-only.
749 Created time.Time
750
751 // Deleted is the time the object was deleted.
752 // If not deleted, it is the zero value. This field is read-only.
753 Deleted time.Time
754
755 // Updated is the creation or modification time of the object.
756 // For buckets with versioning enabled, changing an object's
757 // metadata does not change this property. This field is read-only.
758 Updated time.Time
759
760 // CustomerKeySHA256 is the base64-encoded SHA-256 hash of the
761 // customer-supplied encryption key for the object. It is empty if there is
762 // no customer-supplied encryption key.
763 // See // https://cloud.google.com/storage/docs/encryption for more about
764 // encryption in Google Cloud Storage.
765 CustomerKeySHA256 string
766
767 // Cloud KMS key name, in the form
768 // projects/P/locations/L/keyRings/R/cryptoKeys/K, used to encrypt this object,
769 // if the object is encrypted by such a key.
770 //
771 // Providing both a KMSKeyName and a customer-supplied encryption key (via
772 // ObjectHandle.Key) will result in an error when writing an object.
773 KMSKeyName string
774
775 // Prefix is set only for ObjectAttrs which represent synthetic "directory
776 // entries" when iterating over buckets using Query.Delimiter. See
777 // ObjectIterator.Next. When set, no other fields in ObjectAttrs will be
778 // populated.
779 Prefix string
780 }
781
782 // convertTime converts a time in RFC3339 format to time.Time.
783 // If any error occurs in parsing, the zero-value time.Time is silently returned.
784 func convertTime(t string) time.Time {
785 var r time.Time
786 if t != "" {
787 r, _ = time.Parse(time.RFC3339, t)
788 }
789 return r
790 }
791
792 func newObject(o *raw.Object) *ObjectAttrs {
793 if o == nil {
794 return nil
795 }
796 owner := ""
797 if o.Owner != nil {
798 owner = o.Owner.Entity
799 }
800 md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
801 crc32c, _ := decodeUint32(o.Crc32c)
802 var sha256 string
803 if o.CustomerEncryption != nil {
804 sha256 = o.CustomerEncryption.KeySha256
805 }
806 return &ObjectAttrs{
807 Bucket: o.Bucket,
808 Name: o.Name,
809 ContentType: o.ContentType,
810 ContentLanguage: o.ContentLanguage,
811 CacheControl: o.CacheControl,
812 EventBasedHold: o.EventBasedHold,
813 TemporaryHold: o.TemporaryHold,
814 RetentionExpirationTime: convertTime(o.RetentionExpirationTime),
815 ACL: toObjectACLRules(o.Acl),
816 Owner: owner,
817 ContentEncoding: o.ContentEncoding,
818 ContentDisposition: o.ContentDisposition,
819 Size: int64(o.Size),
820 MD5: md5,
821 CRC32C: crc32c,
822 MediaLink: o.MediaLink,
823 Metadata: o.Metadata,
824 Generation: o.Generation,
825 Metageneration: o.Metageneration,
826 StorageClass: o.StorageClass,
827 CustomerKeySHA256: sha256,
828 KMSKeyName: o.KmsKeyName,
829 Created: convertTime(o.TimeCreated),
830 Deleted: convertTime(o.TimeDeleted),
831 Updated: convertTime(o.Updated),
832 }
833 }
834
835 // Decode a uint32 encoded in Base64 in big-endian byte order.
836 func decodeUint32(b64 string) (uint32, error) {
837 d, err := base64.StdEncoding.DecodeString(b64)
838 if err != nil {
839 return 0, err
840 }
841 if len(d) != 4 {
842 return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
843 }
844 return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
845 }
846
847 // Encode a uint32 as Base64 in big-endian byte order.
848 func encodeUint32(u uint32) string {
849 b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
850 return base64.StdEncoding.EncodeToString(b)
851 }
852
853 // Query represents a query to filter objects from a bucket.
854 type Query struct {
855 // Delimiter returns results in a directory-like fashion.
856 // Results will contain only objects whose names, aside from the
857 // prefix, do not contain delimiter. Objects whose names,
858 // aside from the prefix, contain delimiter will have their name,
859 // truncated after the delimiter, returned in prefixes.
860 // Duplicate prefixes are omitted.
861 // Optional.
862 Delimiter string
863
864 // Prefix is the prefix filter to query objects
865 // whose names begin with this prefix.
866 // Optional.
867 Prefix string
868
869 // Versions indicates whether multiple versions of the same
870 // object will be included in the results.
871 Versions bool
872 }
873
874 // Conditions constrain methods to act on specific generations of
875 // objects.
876 //
877 // The zero value is an empty set of constraints. Not all conditions or
878 // combinations of conditions are applicable to all methods.
879 // See https://cloud.google.com/storage/docs/generations-preconditions
880 // for details on how these operate.
881 type Conditions struct {
882 // Generation constraints.
883 // At most one of the following can be set to a non-zero value.
884
885 // GenerationMatch specifies that the object must have the given generation
886 // for the operation to occur.
887 // If GenerationMatch is zero, it has no effect.
888 // Use DoesNotExist to specify that the object does not exist in the bucket.
889 GenerationMatch int64
890
891 // GenerationNotMatch specifies that the object must not have the given
892 // generation for the operation to occur.
893 // If GenerationNotMatch is zero, it has no effect.
894 GenerationNotMatch int64
895
896 // DoesNotExist specifies that the object must not exist in the bucket for
897 // the operation to occur.
898 // If DoesNotExist is false, it has no effect.
899 DoesNotExist bool
900
901 // Metadata generation constraints.
902 // At most one of the following can be set to a non-zero value.
903
904 // MetagenerationMatch specifies that the object must have the given
905 // metageneration for the operation to occur.
906 // If MetagenerationMatch is zero, it has no effect.
907 MetagenerationMatch int64
908
909 // MetagenerationNotMatch specifies that the object must not have the given
910 // metageneration for the operation to occur.
911 // If MetagenerationNotMatch is zero, it has no effect.
912 MetagenerationNotMatch int64
913 }
914
915 func (c *Conditions) validate(method string) error {
916 if *c == (Conditions{}) {
917 return fmt.Errorf("storage: %s: empty conditions", method)
918 }
919 if !c.isGenerationValid() {
920 return fmt.Errorf("storage: %s: multiple conditions specified for generation", method)
921 }
922 if !c.isMetagenerationValid() {
923 return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
924 }
925 return nil
926 }
927
928 func (c *Conditions) isGenerationValid() bool {
929 n := 0
930 if c.GenerationMatch != 0 {
931 n++
932 }
933 if c.GenerationNotMatch != 0 {
934 n++
935 }
936 if c.DoesNotExist {
937 n++
938 }
939 return n <= 1
940 }
941
942 func (c *Conditions) isMetagenerationValid() bool {
943 return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0
944 }
945
946 // applyConds modifies the provided call using the conditions in conds.
947 // call is something that quacks like a *raw.WhateverCall.
948 func applyConds(method string, gen int64, conds *Conditions, call interface{}) error {
949 cval := reflect.ValueOf(call)
950 if gen >= 0 {
951 if !setConditionField(cval, "Generation", gen) {
952 return fmt.Errorf("storage: %s: generation not supported", method)
953 }
954 }
955 if conds == nil {
956 return nil
957 }
958 if err := conds.validate(method); err != nil {
959 return err
960 }
961 switch {
962 case conds.GenerationMatch != 0:
963 if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) {
964 return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method)
965 }
966 case conds.GenerationNotMatch != 0:
967 if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) {
968 return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method)
969 }
970 case conds.DoesNotExist:
971 if !setConditionField(cval, "IfGenerationMatch", int64(0)) {
972 return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
973 }
974 }
975 switch {
976 case conds.MetagenerationMatch != 0:
977 if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
978 return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
979 }
980 case conds.MetagenerationNotMatch != 0:
981 if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
982 return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
983 }
984 }
985 return nil
986 }
987
988 func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error {
989 if gen >= 0 {
990 call.SourceGeneration(gen)
991 }
992 if conds == nil {
993 return nil
994 }
995 if err := conds.validate("CopyTo source"); err != nil {
996 return err
997 }
998 switch {
999 case conds.GenerationMatch != 0:
1000 call.IfSourceGenerationMatch(conds.GenerationMatch)
1001 case conds.GenerationNotMatch != 0:
1002 call.IfSourceGenerationNotMatch(conds.GenerationNotMatch)
1003 case conds.DoesNotExist:
1004 call.IfSourceGenerationMatch(0)
1005 }
1006 switch {
1007 case conds.MetagenerationMatch != 0:
1008 call.IfSourceMetagenerationMatch(conds.MetagenerationMatch)
1009 case conds.MetagenerationNotMatch != 0:
1010 call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch)
1011 }
1012 return nil
1013 }
1014
1015 // setConditionField sets a field on a *raw.WhateverCall.
1016 // We can't use anonymous interfaces because the return type is
1017 // different, since the field setters are builders.
1018 func setConditionField(call reflect.Value, name string, value interface{}) bool {
1019 m := call.MethodByName(name)
1020 if !m.IsValid() {
1021 return false
1022 }
1023 m.Call([]reflect.Value{reflect.ValueOf(value)})
1024 return true
1025 }
1026
1027 // conditionsQuery returns the generation and conditions as a URL query
1028 // string suitable for URL.RawQuery. It assumes that the conditions
1029 // have been validated.
1030 func conditionsQuery(gen int64, conds *Conditions) string {
1031 // URL escapes are elided because integer strings are URL-safe.
1032 var buf []byte
1033
1034 appendParam := func(s string, n int64) {
1035 if len(buf) > 0 {
1036 buf = append(buf, '&')
1037 }
1038 buf = append(buf, s...)
1039 buf = strconv.AppendInt(buf, n, 10)
1040 }
1041
1042 if gen >= 0 {
1043 appendParam("generation=", gen)
1044 }
1045 if conds == nil {
1046 return string(buf)
1047 }
1048 switch {
1049 case conds.GenerationMatch != 0:
1050 appendParam("ifGenerationMatch=", conds.GenerationMatch)
1051 case conds.GenerationNotMatch != 0:
1052 appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch)
1053 case conds.DoesNotExist:
1054 appendParam("ifGenerationMatch=", 0)
1055 }
1056 switch {
1057 case conds.MetagenerationMatch != 0:
1058 appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch)
1059 case conds.MetagenerationNotMatch != 0:
1060 appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch)
1061 }
1062 return string(buf)
1063 }
1064
1065 // composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods
1066 // that modifyCall searches for by name.
1067 type composeSourceObj struct {
1068 src *raw.ComposeRequestSourceObjects
1069 }
1070
1071 func (c composeSourceObj) Generation(gen int64) {
1072 c.src.Generation = gen
1073 }
1074
1075 func (c composeSourceObj) IfGenerationMatch(gen int64) {
1076 // It's safe to overwrite ObjectPreconditions, since its only field is
1077 // IfGenerationMatch.
1078 c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{
1079 IfGenerationMatch: gen,
1080 }
1081 }
1082
1083 func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error {
1084 if key == nil {
1085 return nil
1086 }
1087 // TODO(jbd): Ask the API team to return a more user-friendly error
1088 // and avoid doing this check at the client level.
1089 if len(key) != 32 {
1090 return errors.New("storage: not a 32-byte AES-256 key")
1091 }
1092 var cs string
1093 if copySource {
1094 cs = "copy-source-"
1095 }
1096 headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256")
1097 headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key))
1098 keyHash := sha256.Sum256(key)
1099 headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:]))
1100 return nil
1101 }
1102
1103 // ServiceAccount fetches the email address of the given project's Google Cloud Storage service account.
1104 func (c *Client) ServiceAccount(ctx context.Context, projectID string) (string, error) {
1105 r := c.raw.Projects.ServiceAccount.Get(projectID)
1106 res, err := r.Context(ctx).Do()
1107 if err != nil {
1108 return "", err
1109 }
1110 return res.EmailAddress, nil
1111 }