You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1182 lines
36 KiB

  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. package storage
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "reflect"
  20. "time"
  21. "cloud.google.com/go/internal/optional"
  22. "cloud.google.com/go/internal/trace"
  23. "google.golang.org/api/googleapi"
  24. "google.golang.org/api/iterator"
  25. raw "google.golang.org/api/storage/v1"
  26. )
  27. // BucketHandle provides operations on a Google Cloud Storage bucket.
  28. // Use Client.Bucket to get a handle.
  29. type BucketHandle struct {
  30. c *Client
  31. name string
  32. acl ACLHandle
  33. defaultObjectACL ACLHandle
  34. conds *BucketConditions
  35. userProject string // project for Requester Pays buckets
  36. }
  37. // Bucket returns a BucketHandle, which provides operations on the named bucket.
  38. // This call does not perform any network operations.
  39. //
  40. // The supplied name must contain only lowercase letters, numbers, dashes,
  41. // underscores, and dots. The full specification for valid bucket names can be
  42. // found at:
  43. // https://cloud.google.com/storage/docs/bucket-naming
  44. func (c *Client) Bucket(name string) *BucketHandle {
  45. return &BucketHandle{
  46. c: c,
  47. name: name,
  48. acl: ACLHandle{
  49. c: c,
  50. bucket: name,
  51. },
  52. defaultObjectACL: ACLHandle{
  53. c: c,
  54. bucket: name,
  55. isDefault: true,
  56. },
  57. }
  58. }
  59. // Create creates the Bucket in the project.
  60. // If attrs is nil the API defaults will be used.
  61. func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) (err error) {
  62. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create")
  63. defer func() { trace.EndSpan(ctx, err) }()
  64. var bkt *raw.Bucket
  65. if attrs != nil {
  66. bkt = attrs.toRawBucket()
  67. } else {
  68. bkt = &raw.Bucket{}
  69. }
  70. bkt.Name = b.name
  71. // If there is lifecycle information but no location, explicitly set
  72. // the location. This is a GCS quirk/bug.
  73. if bkt.Location == "" && bkt.Lifecycle != nil {
  74. bkt.Location = "US"
  75. }
  76. req := b.c.raw.Buckets.Insert(projectID, bkt)
  77. setClientHeader(req.Header())
  78. if attrs != nil && attrs.PredefinedACL != "" {
  79. req.PredefinedAcl(attrs.PredefinedACL)
  80. }
  81. if attrs != nil && attrs.PredefinedDefaultObjectACL != "" {
  82. req.PredefinedDefaultObjectAcl(attrs.PredefinedDefaultObjectACL)
  83. }
  84. return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
  85. }
  86. // Delete deletes the Bucket.
  87. func (b *BucketHandle) Delete(ctx context.Context) (err error) {
  88. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Delete")
  89. defer func() { trace.EndSpan(ctx, err) }()
  90. req, err := b.newDeleteCall()
  91. if err != nil {
  92. return err
  93. }
  94. return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
  95. }
  96. func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) {
  97. req := b.c.raw.Buckets.Delete(b.name)
  98. setClientHeader(req.Header())
  99. if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil {
  100. return nil, err
  101. }
  102. if b.userProject != "" {
  103. req.UserProject(b.userProject)
  104. }
  105. return req, nil
  106. }
  107. // ACL returns an ACLHandle, which provides access to the bucket's access control list.
  108. // This controls who can list, create or overwrite the objects in a bucket.
  109. // This call does not perform any network operations.
  110. func (b *BucketHandle) ACL() *ACLHandle {
  111. return &b.acl
  112. }
  113. // DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs.
  114. // These ACLs are applied to newly created objects in this bucket that do not have a defined ACL.
  115. // This call does not perform any network operations.
  116. func (b *BucketHandle) DefaultObjectACL() *ACLHandle {
  117. return &b.defaultObjectACL
  118. }
  119. // Object returns an ObjectHandle, which provides operations on the named object.
  120. // This call does not perform any network operations.
  121. //
  122. // name must consist entirely of valid UTF-8-encoded runes. The full specification
  123. // for valid object names can be found at:
  124. // https://cloud.google.com/storage/docs/bucket-naming
  125. func (b *BucketHandle) Object(name string) *ObjectHandle {
  126. return &ObjectHandle{
  127. c: b.c,
  128. bucket: b.name,
  129. object: name,
  130. acl: ACLHandle{
  131. c: b.c,
  132. bucket: b.name,
  133. object: name,
  134. userProject: b.userProject,
  135. },
  136. gen: -1,
  137. userProject: b.userProject,
  138. }
  139. }
  140. // Attrs returns the metadata for the bucket.
  141. func (b *BucketHandle) Attrs(ctx context.Context) (attrs *BucketAttrs, err error) {
  142. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Attrs")
  143. defer func() { trace.EndSpan(ctx, err) }()
  144. req, err := b.newGetCall()
  145. if err != nil {
  146. return nil, err
  147. }
  148. var resp *raw.Bucket
  149. err = runWithRetry(ctx, func() error {
  150. resp, err = req.Context(ctx).Do()
  151. return err
  152. })
  153. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  154. return nil, ErrBucketNotExist
  155. }
  156. if err != nil {
  157. return nil, err
  158. }
  159. return newBucket(resp)
  160. }
  161. func (b *BucketHandle) newGetCall() (*raw.BucketsGetCall, error) {
  162. req := b.c.raw.Buckets.Get(b.name).Projection("full")
  163. setClientHeader(req.Header())
  164. if err := applyBucketConds("BucketHandle.Attrs", b.conds, req); err != nil {
  165. return nil, err
  166. }
  167. if b.userProject != "" {
  168. req.UserProject(b.userProject)
  169. }
  170. return req, nil
  171. }
  172. // Update updates a bucket's attributes.
  173. func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (attrs *BucketAttrs, err error) {
  174. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create")
  175. defer func() { trace.EndSpan(ctx, err) }()
  176. req, err := b.newPatchCall(&uattrs)
  177. if err != nil {
  178. return nil, err
  179. }
  180. if uattrs.PredefinedACL != "" {
  181. req.PredefinedAcl(uattrs.PredefinedACL)
  182. }
  183. if uattrs.PredefinedDefaultObjectACL != "" {
  184. req.PredefinedDefaultObjectAcl(uattrs.PredefinedDefaultObjectACL)
  185. }
  186. // TODO(jba): retry iff metagen is set?
  187. rb, err := req.Context(ctx).Do()
  188. if err != nil {
  189. return nil, err
  190. }
  191. return newBucket(rb)
  192. }
  193. func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) {
  194. rb := uattrs.toRawBucket()
  195. req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full")
  196. setClientHeader(req.Header())
  197. if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil {
  198. return nil, err
  199. }
  200. if b.userProject != "" {
  201. req.UserProject(b.userProject)
  202. }
  203. return req, nil
  204. }
  205. // BucketAttrs represents the metadata for a Google Cloud Storage bucket.
  206. // Read-only fields are ignored by BucketHandle.Create.
  207. type BucketAttrs struct {
  208. // Name is the name of the bucket.
  209. // This field is read-only.
  210. Name string
  211. // ACL is the list of access control rules on the bucket.
  212. ACL []ACLRule
  213. // BucketPolicyOnly configures access checks to use only bucket-level IAM
  214. // policies.
  215. BucketPolicyOnly BucketPolicyOnly
  216. // DefaultObjectACL is the list of access controls to
  217. // apply to new objects when no object ACL is provided.
  218. DefaultObjectACL []ACLRule
  219. // DefaultEventBasedHold is the default value for event-based hold on
  220. // newly created objects in this bucket. It defaults to false.
  221. DefaultEventBasedHold bool
  222. // If not empty, applies a predefined set of access controls. It should be set
  223. // only when creating a bucket.
  224. // It is always empty for BucketAttrs returned from the service.
  225. // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert
  226. // for valid values.
  227. PredefinedACL string
  228. // If not empty, applies a predefined set of default object access controls.
  229. // It should be set only when creating a bucket.
  230. // It is always empty for BucketAttrs returned from the service.
  231. // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert
  232. // for valid values.
  233. PredefinedDefaultObjectACL string
  234. // Location is the location of the bucket. It defaults to "US".
  235. Location string
  236. // MetaGeneration is the metadata generation of the bucket.
  237. // This field is read-only.
  238. MetaGeneration int64
  239. // StorageClass is the default storage class of the bucket. This defines
  240. // how objects in the bucket are stored and determines the SLA
  241. // and the cost of storage. Typical values are "MULTI_REGIONAL",
  242. // "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
  243. // "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which
  244. // is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on
  245. // the bucket's location settings.
  246. StorageClass string
  247. // Created is the creation time of the bucket.
  248. // This field is read-only.
  249. Created time.Time
  250. // VersioningEnabled reports whether this bucket has versioning enabled.
  251. VersioningEnabled bool
  252. // Labels are the bucket's labels.
  253. Labels map[string]string
  254. // RequesterPays reports whether the bucket is a Requester Pays bucket.
  255. // Clients performing operations on Requester Pays buckets must provide
  256. // a user project (see BucketHandle.UserProject), which will be billed
  257. // for the operations.
  258. RequesterPays bool
  259. // Lifecycle is the lifecycle configuration for objects in the bucket.
  260. Lifecycle Lifecycle
  261. // Retention policy enforces a minimum retention time for all objects
  262. // contained in the bucket. A RetentionPolicy of nil implies the bucket
  263. // has no minimum data retention.
  264. //
  265. // This feature is in private alpha release. It is not currently available to
  266. // most customers. It might be changed in backwards-incompatible ways and is not
  267. // subject to any SLA or deprecation policy.
  268. RetentionPolicy *RetentionPolicy
  269. // The bucket's Cross-Origin Resource Sharing (CORS) configuration.
  270. CORS []CORS
  271. // The encryption configuration used by default for newly inserted objects.
  272. Encryption *BucketEncryption
  273. // The logging configuration.
  274. Logging *BucketLogging
  275. // The website configuration.
  276. Website *BucketWebsite
  277. }
  278. // BucketPolicyOnly configures access checks to use only bucket-level IAM
  279. // policies.
  280. type BucketPolicyOnly struct {
  281. // Enabled specifies whether access checks use only bucket-level IAM
  282. // policies. Enabled may be disabled until the locked time.
  283. Enabled bool
  284. // LockedTime specifies the deadline for changing Enabled from true to
  285. // false.
  286. LockedTime time.Time
  287. }
  288. // Lifecycle is the lifecycle configuration for objects in the bucket.
  289. type Lifecycle struct {
  290. Rules []LifecycleRule
  291. }
  292. // RetentionPolicy enforces a minimum retention time for all objects
  293. // contained in the bucket.
  294. //
  295. // Any attempt to overwrite or delete objects younger than the retention
  296. // period will result in an error. An unlocked retention policy can be
  297. // modified or removed from the bucket via the Update method. A
  298. // locked retention policy cannot be removed or shortened in duration
  299. // for the lifetime of the bucket.
  300. //
  301. // This feature is in private alpha release. It is not currently available to
  302. // most customers. It might be changed in backwards-incompatible ways and is not
  303. // subject to any SLA or deprecation policy.
  304. type RetentionPolicy struct {
  305. // RetentionPeriod specifies the duration that objects need to be
  306. // retained. Retention duration must be greater than zero and less than
  307. // 100 years. Note that enforcement of retention periods less than a day
  308. // is not guaranteed. Such periods should only be used for testing
  309. // purposes.
  310. RetentionPeriod time.Duration
  311. // EffectiveTime is the time from which the policy was enforced and
  312. // effective. This field is read-only.
  313. EffectiveTime time.Time
  314. // IsLocked describes whether the bucket is locked. Once locked, an
  315. // object retention policy cannot be modified.
  316. // This field is read-only.
  317. IsLocked bool
  318. }
  319. const (
  320. // RFC3339 date with only the date segment, used for CreatedBefore in LifecycleRule.
  321. rfc3339Date = "2006-01-02"
  322. // DeleteAction is a lifecycle action that deletes a live and/or archived
  323. // objects. Takes precedence over SetStorageClass actions.
  324. DeleteAction = "Delete"
  325. // SetStorageClassAction changes the storage class of live and/or archived
  326. // objects.
  327. SetStorageClassAction = "SetStorageClass"
  328. )
  329. // LifecycleRule is a lifecycle configuration rule.
  330. //
  331. // When all the configured conditions are met by an object in the bucket, the
  332. // configured action will automatically be taken on that object.
  333. type LifecycleRule struct {
  334. // Action is the action to take when all of the associated conditions are
  335. // met.
  336. Action LifecycleAction
  337. // Condition is the set of conditions that must be met for the associated
  338. // action to be taken.
  339. Condition LifecycleCondition
  340. }
  341. // LifecycleAction is a lifecycle configuration action.
  342. type LifecycleAction struct {
  343. // Type is the type of action to take on matching objects.
  344. //
  345. // Acceptable values are "Delete" to delete matching objects and
  346. // "SetStorageClass" to set the storage class defined in StorageClass on
  347. // matching objects.
  348. Type string
  349. // StorageClass is the storage class to set on matching objects if the Action
  350. // is "SetStorageClass".
  351. StorageClass string
  352. }
  353. // Liveness specifies whether the object is live or not.
  354. type Liveness int
  355. const (
  356. // LiveAndArchived includes both live and archived objects.
  357. LiveAndArchived Liveness = iota
  358. // Live specifies that the object is still live.
  359. Live
  360. // Archived specifies that the object is archived.
  361. Archived
  362. )
  363. // LifecycleCondition is a set of conditions used to match objects and take an
  364. // action automatically.
  365. //
  366. // All configured conditions must be met for the associated action to be taken.
  367. type LifecycleCondition struct {
  368. // AgeInDays is the age of the object in days.
  369. AgeInDays int64
  370. // CreatedBefore is the time the object was created.
  371. //
  372. // This condition is satisfied when an object is created before midnight of
  373. // the specified date in UTC.
  374. CreatedBefore time.Time
  375. // Liveness specifies the object's liveness. Relevant only for versioned objects
  376. Liveness Liveness
  377. // MatchesStorageClasses is the condition matching the object's storage
  378. // class.
  379. //
  380. // Values include "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE",
  381. // "STANDARD", and "DURABLE_REDUCED_AVAILABILITY".
  382. MatchesStorageClasses []string
  383. // NumNewerVersions is the condition matching objects with a number of newer versions.
  384. //
  385. // If the value is N, this condition is satisfied when there are at least N
  386. // versions (including the live version) newer than this version of the
  387. // object.
  388. NumNewerVersions int64
  389. }
  390. // BucketLogging holds the bucket's logging configuration, which defines the
  391. // destination bucket and optional name prefix for the current bucket's
  392. // logs.
  393. type BucketLogging struct {
  394. // The destination bucket where the current bucket's logs
  395. // should be placed.
  396. LogBucket string
  397. // A prefix for log object names.
  398. LogObjectPrefix string
  399. }
  400. // BucketWebsite holds the bucket's website configuration, controlling how the
  401. // service behaves when accessing bucket contents as a web site. See
  402. // https://cloud.google.com/storage/docs/static-website for more information.
  403. type BucketWebsite struct {
  404. // If the requested object path is missing, the service will ensure the path has
  405. // a trailing '/', append this suffix, and attempt to retrieve the resulting
  406. // object. This allows the creation of index.html objects to represent directory
  407. // pages.
  408. MainPageSuffix string
  409. // If the requested object path is missing, and any mainPageSuffix object is
  410. // missing, if applicable, the service will return the named object from this
  411. // bucket as the content for a 404 Not Found result.
  412. NotFoundPage string
  413. }
  414. func newBucket(b *raw.Bucket) (*BucketAttrs, error) {
  415. if b == nil {
  416. return nil, nil
  417. }
  418. rp, err := toRetentionPolicy(b.RetentionPolicy)
  419. if err != nil {
  420. return nil, err
  421. }
  422. return &BucketAttrs{
  423. Name: b.Name,
  424. Location: b.Location,
  425. MetaGeneration: b.Metageneration,
  426. DefaultEventBasedHold: b.DefaultEventBasedHold,
  427. StorageClass: b.StorageClass,
  428. Created: convertTime(b.TimeCreated),
  429. VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled,
  430. ACL: toBucketACLRules(b.Acl),
  431. DefaultObjectACL: toObjectACLRules(b.DefaultObjectAcl),
  432. Labels: b.Labels,
  433. RequesterPays: b.Billing != nil && b.Billing.RequesterPays,
  434. Lifecycle: toLifecycle(b.Lifecycle),
  435. RetentionPolicy: rp,
  436. CORS: toCORS(b.Cors),
  437. Encryption: toBucketEncryption(b.Encryption),
  438. Logging: toBucketLogging(b.Logging),
  439. Website: toBucketWebsite(b.Website),
  440. BucketPolicyOnly: toBucketPolicyOnly(b.IamConfiguration),
  441. }, nil
  442. }
  443. // toRawBucket copies the editable attribute from b to the raw library's Bucket type.
  444. func (b *BucketAttrs) toRawBucket() *raw.Bucket {
  445. // Copy label map.
  446. var labels map[string]string
  447. if len(b.Labels) > 0 {
  448. labels = make(map[string]string, len(b.Labels))
  449. for k, v := range b.Labels {
  450. labels[k] = v
  451. }
  452. }
  453. // Ignore VersioningEnabled if it is false. This is OK because
  454. // we only call this method when creating a bucket, and by default
  455. // new buckets have versioning off.
  456. var v *raw.BucketVersioning
  457. if b.VersioningEnabled {
  458. v = &raw.BucketVersioning{Enabled: true}
  459. }
  460. var bb *raw.BucketBilling
  461. if b.RequesterPays {
  462. bb = &raw.BucketBilling{RequesterPays: true}
  463. }
  464. var bktIAM *raw.BucketIamConfiguration
  465. if b.BucketPolicyOnly.Enabled {
  466. bktIAM = &raw.BucketIamConfiguration{
  467. BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
  468. Enabled: true,
  469. },
  470. }
  471. }
  472. return &raw.Bucket{
  473. Name: b.Name,
  474. Location: b.Location,
  475. StorageClass: b.StorageClass,
  476. Acl: toRawBucketACL(b.ACL),
  477. DefaultObjectAcl: toRawObjectACL(b.DefaultObjectACL),
  478. Versioning: v,
  479. Labels: labels,
  480. Billing: bb,
  481. Lifecycle: toRawLifecycle(b.Lifecycle),
  482. RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(),
  483. Cors: toRawCORS(b.CORS),
  484. Encryption: b.Encryption.toRawBucketEncryption(),
  485. Logging: b.Logging.toRawBucketLogging(),
  486. Website: b.Website.toRawBucketWebsite(),
  487. IamConfiguration: bktIAM,
  488. }
  489. }
  490. // CORS is the bucket's Cross-Origin Resource Sharing (CORS) configuration.
  491. type CORS struct {
  492. // MaxAge is the value to return in the Access-Control-Max-Age
  493. // header used in preflight responses.
  494. MaxAge time.Duration
  495. // Methods is the list of HTTP methods on which to include CORS response
  496. // headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list
  497. // of methods, and means "any method".
  498. Methods []string
  499. // Origins is the list of Origins eligible to receive CORS response
  500. // headers. Note: "*" is permitted in the list of origins, and means
  501. // "any Origin".
  502. Origins []string
  503. // ResponseHeaders is the list of HTTP headers other than the simple
  504. // response headers to give permission for the user-agent to share
  505. // across domains.
  506. ResponseHeaders []string
  507. }
  508. // BucketEncryption is a bucket's encryption configuration.
  509. type BucketEncryption struct {
  510. // A Cloud KMS key name, in the form
  511. // projects/P/locations/L/keyRings/R/cryptoKeys/K, that will be used to encrypt
  512. // objects inserted into this bucket, if no encryption method is specified.
  513. // The key's location must be the same as the bucket's.
  514. DefaultKMSKeyName string
  515. }
  516. // BucketAttrsToUpdate define the attributes to update during an Update call.
  517. type BucketAttrsToUpdate struct {
  518. // If set, updates whether the bucket uses versioning.
  519. VersioningEnabled optional.Bool
  520. // If set, updates whether the bucket is a Requester Pays bucket.
  521. RequesterPays optional.Bool
  522. // DefaultEventBasedHold is the default value for event-based hold on
  523. // newly created objects in this bucket.
  524. DefaultEventBasedHold optional.Bool
  525. // BucketPolicyOnly configures access checks to use only bucket-level IAM
  526. // policies.
  527. BucketPolicyOnly *BucketPolicyOnly
  528. // If set, updates the retention policy of the bucket. Using
  529. // RetentionPolicy.RetentionPeriod = 0 will delete the existing policy.
  530. //
  531. // This feature is in private alpha release. It is not currently available to
  532. // most customers. It might be changed in backwards-incompatible ways and is not
  533. // subject to any SLA or deprecation policy.
  534. RetentionPolicy *RetentionPolicy
  535. // If set, replaces the CORS configuration with a new configuration.
  536. // An empty (rather than nil) slice causes all CORS policies to be removed.
  537. CORS []CORS
  538. // If set, replaces the encryption configuration of the bucket. Using
  539. // BucketEncryption.DefaultKMSKeyName = "" will delete the existing
  540. // configuration.
  541. Encryption *BucketEncryption
  542. // If set, replaces the lifecycle configuration of the bucket.
  543. Lifecycle *Lifecycle
  544. // If set, replaces the logging configuration of the bucket.
  545. Logging *BucketLogging
  546. // If set, replaces the website configuration of the bucket.
  547. Website *BucketWebsite
  548. // If not empty, applies a predefined set of access controls.
  549. // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch.
  550. PredefinedACL string
  551. // If not empty, applies a predefined set of default object access controls.
  552. // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch.
  553. PredefinedDefaultObjectACL string
  554. setLabels map[string]string
  555. deleteLabels map[string]bool
  556. }
  557. // SetLabel causes a label to be added or modified when ua is used
  558. // in a call to Bucket.Update.
  559. func (ua *BucketAttrsToUpdate) SetLabel(name, value string) {
  560. if ua.setLabels == nil {
  561. ua.setLabels = map[string]string{}
  562. }
  563. ua.setLabels[name] = value
  564. }
  565. // DeleteLabel causes a label to be deleted when ua is used in a
  566. // call to Bucket.Update.
  567. func (ua *BucketAttrsToUpdate) DeleteLabel(name string) {
  568. if ua.deleteLabels == nil {
  569. ua.deleteLabels = map[string]bool{}
  570. }
  571. ua.deleteLabels[name] = true
  572. }
  573. func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
  574. rb := &raw.Bucket{}
  575. if ua.CORS != nil {
  576. rb.Cors = toRawCORS(ua.CORS)
  577. rb.ForceSendFields = append(rb.ForceSendFields, "Cors")
  578. }
  579. if ua.DefaultEventBasedHold != nil {
  580. rb.DefaultEventBasedHold = optional.ToBool(ua.DefaultEventBasedHold)
  581. rb.ForceSendFields = append(rb.ForceSendFields, "DefaultEventBasedHold")
  582. }
  583. if ua.RetentionPolicy != nil {
  584. if ua.RetentionPolicy.RetentionPeriod == 0 {
  585. rb.NullFields = append(rb.NullFields, "RetentionPolicy")
  586. rb.RetentionPolicy = nil
  587. } else {
  588. rb.RetentionPolicy = ua.RetentionPolicy.toRawRetentionPolicy()
  589. }
  590. }
  591. if ua.VersioningEnabled != nil {
  592. rb.Versioning = &raw.BucketVersioning{
  593. Enabled: optional.ToBool(ua.VersioningEnabled),
  594. ForceSendFields: []string{"Enabled"},
  595. }
  596. }
  597. if ua.RequesterPays != nil {
  598. rb.Billing = &raw.BucketBilling{
  599. RequesterPays: optional.ToBool(ua.RequesterPays),
  600. ForceSendFields: []string{"RequesterPays"},
  601. }
  602. }
  603. if ua.BucketPolicyOnly != nil {
  604. rb.IamConfiguration = &raw.BucketIamConfiguration{
  605. BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
  606. Enabled: ua.BucketPolicyOnly.Enabled,
  607. },
  608. }
  609. }
  610. if ua.Encryption != nil {
  611. if ua.Encryption.DefaultKMSKeyName == "" {
  612. rb.NullFields = append(rb.NullFields, "Encryption")
  613. rb.Encryption = nil
  614. } else {
  615. rb.Encryption = ua.Encryption.toRawBucketEncryption()
  616. }
  617. }
  618. if ua.Lifecycle != nil {
  619. rb.Lifecycle = toRawLifecycle(*ua.Lifecycle)
  620. }
  621. if ua.Logging != nil {
  622. if *ua.Logging == (BucketLogging{}) {
  623. rb.NullFields = append(rb.NullFields, "Logging")
  624. rb.Logging = nil
  625. } else {
  626. rb.Logging = ua.Logging.toRawBucketLogging()
  627. }
  628. }
  629. if ua.Website != nil {
  630. if *ua.Website == (BucketWebsite{}) {
  631. rb.NullFields = append(rb.NullFields, "Website")
  632. rb.Website = nil
  633. } else {
  634. rb.Website = ua.Website.toRawBucketWebsite()
  635. }
  636. }
  637. if ua.PredefinedACL != "" {
  638. // Clear ACL or the call will fail.
  639. rb.Acl = nil
  640. rb.ForceSendFields = append(rb.ForceSendFields, "Acl")
  641. }
  642. if ua.PredefinedDefaultObjectACL != "" {
  643. // Clear ACLs or the call will fail.
  644. rb.DefaultObjectAcl = nil
  645. rb.ForceSendFields = append(rb.ForceSendFields, "DefaultObjectAcl")
  646. }
  647. if ua.setLabels != nil || ua.deleteLabels != nil {
  648. rb.Labels = map[string]string{}
  649. for k, v := range ua.setLabels {
  650. rb.Labels[k] = v
  651. }
  652. if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 {
  653. rb.ForceSendFields = append(rb.ForceSendFields, "Labels")
  654. }
  655. for l := range ua.deleteLabels {
  656. rb.NullFields = append(rb.NullFields, "Labels."+l)
  657. }
  658. }
  659. return rb
  660. }
  661. // If returns a new BucketHandle that applies a set of preconditions.
  662. // Preconditions already set on the BucketHandle are ignored.
  663. // Operations on the new handle will return an error if the preconditions are not
  664. // satisfied. The only valid preconditions for buckets are MetagenerationMatch
  665. // and MetagenerationNotMatch.
  666. func (b *BucketHandle) If(conds BucketConditions) *BucketHandle {
  667. b2 := *b
  668. b2.conds = &conds
  669. return &b2
  670. }
  671. // BucketConditions constrain bucket methods to act on specific metagenerations.
  672. //
  673. // The zero value is an empty set of constraints.
  674. type BucketConditions struct {
  675. // MetagenerationMatch specifies that the bucket must have the given
  676. // metageneration for the operation to occur.
  677. // If MetagenerationMatch is zero, it has no effect.
  678. MetagenerationMatch int64
  679. // MetagenerationNotMatch specifies that the bucket must not have the given
  680. // metageneration for the operation to occur.
  681. // If MetagenerationNotMatch is zero, it has no effect.
  682. MetagenerationNotMatch int64
  683. }
  684. func (c *BucketConditions) validate(method string) error {
  685. if *c == (BucketConditions{}) {
  686. return fmt.Errorf("storage: %s: empty conditions", method)
  687. }
  688. if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 {
  689. return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
  690. }
  691. return nil
  692. }
  693. // UserProject returns a new BucketHandle that passes the project ID as the user
  694. // project for all subsequent calls. Calls with a user project will be billed to that
  695. // project rather than to the bucket's owning project.
  696. //
  697. // A user project is required for all operations on Requester Pays buckets.
  698. func (b *BucketHandle) UserProject(projectID string) *BucketHandle {
  699. b2 := *b
  700. b2.userProject = projectID
  701. b2.acl.userProject = projectID
  702. b2.defaultObjectACL.userProject = projectID
  703. return &b2
  704. }
  705. // LockRetentionPolicy locks a bucket's retention policy until a previously-configured
  706. // RetentionPeriod past the EffectiveTime. Note that if RetentionPeriod is set to less
  707. // than a day, the retention policy is treated as a development configuration and locking
  708. // will have no effect. The BucketHandle must have a metageneration condition that
  709. // matches the bucket's metageneration. See BucketHandle.If.
  710. //
  711. // This feature is in private alpha release. It is not currently available to
  712. // most customers. It might be changed in backwards-incompatible ways and is not
  713. // subject to any SLA or deprecation policy.
  714. func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error {
  715. var metageneration int64
  716. if b.conds != nil {
  717. metageneration = b.conds.MetagenerationMatch
  718. }
  719. req := b.c.raw.Buckets.LockRetentionPolicy(b.name, metageneration)
  720. _, err := req.Context(ctx).Do()
  721. return err
  722. }
  723. // applyBucketConds modifies the provided call using the conditions in conds.
  724. // call is something that quacks like a *raw.WhateverCall.
  725. func applyBucketConds(method string, conds *BucketConditions, call interface{}) error {
  726. if conds == nil {
  727. return nil
  728. }
  729. if err := conds.validate(method); err != nil {
  730. return err
  731. }
  732. cval := reflect.ValueOf(call)
  733. switch {
  734. case conds.MetagenerationMatch != 0:
  735. if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
  736. return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
  737. }
  738. case conds.MetagenerationNotMatch != 0:
  739. if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
  740. return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
  741. }
  742. }
  743. return nil
  744. }
  745. func (rp *RetentionPolicy) toRawRetentionPolicy() *raw.BucketRetentionPolicy {
  746. if rp == nil {
  747. return nil
  748. }
  749. return &raw.BucketRetentionPolicy{
  750. RetentionPeriod: int64(rp.RetentionPeriod / time.Second),
  751. }
  752. }
  753. func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) {
  754. if rp == nil {
  755. return nil, nil
  756. }
  757. t, err := time.Parse(time.RFC3339, rp.EffectiveTime)
  758. if err != nil {
  759. return nil, err
  760. }
  761. return &RetentionPolicy{
  762. RetentionPeriod: time.Duration(rp.RetentionPeriod) * time.Second,
  763. EffectiveTime: t,
  764. IsLocked: rp.IsLocked,
  765. }, nil
  766. }
  767. func toRawCORS(c []CORS) []*raw.BucketCors {
  768. var out []*raw.BucketCors
  769. for _, v := range c {
  770. out = append(out, &raw.BucketCors{
  771. MaxAgeSeconds: int64(v.MaxAge / time.Second),
  772. Method: v.Methods,
  773. Origin: v.Origins,
  774. ResponseHeader: v.ResponseHeaders,
  775. })
  776. }
  777. return out
  778. }
  779. func toCORS(rc []*raw.BucketCors) []CORS {
  780. var out []CORS
  781. for _, v := range rc {
  782. out = append(out, CORS{
  783. MaxAge: time.Duration(v.MaxAgeSeconds) * time.Second,
  784. Methods: v.Method,
  785. Origins: v.Origin,
  786. ResponseHeaders: v.ResponseHeader,
  787. })
  788. }
  789. return out
  790. }
  791. func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle {
  792. var rl raw.BucketLifecycle
  793. if len(l.Rules) == 0 {
  794. return nil
  795. }
  796. for _, r := range l.Rules {
  797. rr := &raw.BucketLifecycleRule{
  798. Action: &raw.BucketLifecycleRuleAction{
  799. Type: r.Action.Type,
  800. StorageClass: r.Action.StorageClass,
  801. },
  802. Condition: &raw.BucketLifecycleRuleCondition{
  803. Age: r.Condition.AgeInDays,
  804. MatchesStorageClass: r.Condition.MatchesStorageClasses,
  805. NumNewerVersions: r.Condition.NumNewerVersions,
  806. },
  807. }
  808. switch r.Condition.Liveness {
  809. case LiveAndArchived:
  810. rr.Condition.IsLive = nil
  811. case Live:
  812. rr.Condition.IsLive = googleapi.Bool(true)
  813. case Archived:
  814. rr.Condition.IsLive = googleapi.Bool(false)
  815. }
  816. if !r.Condition.CreatedBefore.IsZero() {
  817. rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date)
  818. }
  819. rl.Rule = append(rl.Rule, rr)
  820. }
  821. return &rl
  822. }
  823. func toLifecycle(rl *raw.BucketLifecycle) Lifecycle {
  824. var l Lifecycle
  825. if rl == nil {
  826. return l
  827. }
  828. for _, rr := range rl.Rule {
  829. r := LifecycleRule{
  830. Action: LifecycleAction{
  831. Type: rr.Action.Type,
  832. StorageClass: rr.Action.StorageClass,
  833. },
  834. Condition: LifecycleCondition{
  835. AgeInDays: rr.Condition.Age,
  836. MatchesStorageClasses: rr.Condition.MatchesStorageClass,
  837. NumNewerVersions: rr.Condition.NumNewerVersions,
  838. },
  839. }
  840. switch {
  841. case rr.Condition.IsLive == nil:
  842. r.Condition.Liveness = LiveAndArchived
  843. case *rr.Condition.IsLive == true:
  844. r.Condition.Liveness = Live
  845. case *rr.Condition.IsLive == false:
  846. r.Condition.Liveness = Archived
  847. }
  848. if rr.Condition.CreatedBefore != "" {
  849. r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore)
  850. }
  851. l.Rules = append(l.Rules, r)
  852. }
  853. return l
  854. }
  855. func (e *BucketEncryption) toRawBucketEncryption() *raw.BucketEncryption {
  856. if e == nil {
  857. return nil
  858. }
  859. return &raw.BucketEncryption{
  860. DefaultKmsKeyName: e.DefaultKMSKeyName,
  861. }
  862. }
  863. func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption {
  864. if e == nil {
  865. return nil
  866. }
  867. return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName}
  868. }
  869. func (b *BucketLogging) toRawBucketLogging() *raw.BucketLogging {
  870. if b == nil {
  871. return nil
  872. }
  873. return &raw.BucketLogging{
  874. LogBucket: b.LogBucket,
  875. LogObjectPrefix: b.LogObjectPrefix,
  876. }
  877. }
  878. func toBucketLogging(b *raw.BucketLogging) *BucketLogging {
  879. if b == nil {
  880. return nil
  881. }
  882. return &BucketLogging{
  883. LogBucket: b.LogBucket,
  884. LogObjectPrefix: b.LogObjectPrefix,
  885. }
  886. }
  887. func (w *BucketWebsite) toRawBucketWebsite() *raw.BucketWebsite {
  888. if w == nil {
  889. return nil
  890. }
  891. return &raw.BucketWebsite{
  892. MainPageSuffix: w.MainPageSuffix,
  893. NotFoundPage: w.NotFoundPage,
  894. }
  895. }
  896. func toBucketWebsite(w *raw.BucketWebsite) *BucketWebsite {
  897. if w == nil {
  898. return nil
  899. }
  900. return &BucketWebsite{
  901. MainPageSuffix: w.MainPageSuffix,
  902. NotFoundPage: w.NotFoundPage,
  903. }
  904. }
  905. func toBucketPolicyOnly(b *raw.BucketIamConfiguration) BucketPolicyOnly {
  906. if b == nil || b.BucketPolicyOnly == nil || !b.BucketPolicyOnly.Enabled {
  907. return BucketPolicyOnly{}
  908. }
  909. lt, err := time.Parse(time.RFC3339, b.BucketPolicyOnly.LockedTime)
  910. if err != nil {
  911. return BucketPolicyOnly{
  912. Enabled: true,
  913. }
  914. }
  915. return BucketPolicyOnly{
  916. Enabled: true,
  917. LockedTime: lt,
  918. }
  919. }
  920. // Objects returns an iterator over the objects in the bucket that match the Query q.
  921. // If q is nil, no filtering is done.
  922. func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
  923. it := &ObjectIterator{
  924. ctx: ctx,
  925. bucket: b,
  926. }
  927. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  928. it.fetch,
  929. func() int { return len(it.items) },
  930. func() interface{} { b := it.items; it.items = nil; return b })
  931. if q != nil {
  932. it.query = *q
  933. }
  934. return it
  935. }
  936. // An ObjectIterator is an iterator over ObjectAttrs.
  937. type ObjectIterator struct {
  938. ctx context.Context
  939. bucket *BucketHandle
  940. query Query
  941. pageInfo *iterator.PageInfo
  942. nextFunc func() error
  943. items []*ObjectAttrs
  944. }
  945. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  946. func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  947. // Next returns the next result. Its second return value is iterator.Done if
  948. // there are no more results. Once Next returns iterator.Done, all subsequent
  949. // calls will return iterator.Done.
  950. //
  951. // If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will
  952. // have a non-empty Prefix field, and a zero value for all other fields. These
  953. // represent prefixes.
  954. func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
  955. if err := it.nextFunc(); err != nil {
  956. return nil, err
  957. }
  958. item := it.items[0]
  959. it.items = it.items[1:]
  960. return item, nil
  961. }
  962. func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
  963. req := it.bucket.c.raw.Objects.List(it.bucket.name)
  964. setClientHeader(req.Header())
  965. req.Projection("full")
  966. req.Delimiter(it.query.Delimiter)
  967. req.Prefix(it.query.Prefix)
  968. req.Versions(it.query.Versions)
  969. req.PageToken(pageToken)
  970. if it.bucket.userProject != "" {
  971. req.UserProject(it.bucket.userProject)
  972. }
  973. if pageSize > 0 {
  974. req.MaxResults(int64(pageSize))
  975. }
  976. var resp *raw.Objects
  977. var err error
  978. err = runWithRetry(it.ctx, func() error {
  979. resp, err = req.Context(it.ctx).Do()
  980. return err
  981. })
  982. if err != nil {
  983. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  984. err = ErrBucketNotExist
  985. }
  986. return "", err
  987. }
  988. for _, item := range resp.Items {
  989. it.items = append(it.items, newObject(item))
  990. }
  991. for _, prefix := range resp.Prefixes {
  992. it.items = append(it.items, &ObjectAttrs{Prefix: prefix})
  993. }
  994. return resp.NextPageToken, nil
  995. }
  996. // Buckets returns an iterator over the buckets in the project. You may
  997. // optionally set the iterator's Prefix field to restrict the list to buckets
  998. // whose names begin with the prefix. By default, all buckets in the project
  999. // are returned.
  1000. func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator {
  1001. it := &BucketIterator{
  1002. ctx: ctx,
  1003. client: c,
  1004. projectID: projectID,
  1005. }
  1006. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  1007. it.fetch,
  1008. func() int { return len(it.buckets) },
  1009. func() interface{} { b := it.buckets; it.buckets = nil; return b })
  1010. return it
  1011. }
  1012. // A BucketIterator is an iterator over BucketAttrs.
  1013. type BucketIterator struct {
  1014. // Prefix restricts the iterator to buckets whose names begin with it.
  1015. Prefix string
  1016. ctx context.Context
  1017. client *Client
  1018. projectID string
  1019. buckets []*BucketAttrs
  1020. pageInfo *iterator.PageInfo
  1021. nextFunc func() error
  1022. }
  1023. // Next returns the next result. Its second return value is iterator.Done if
  1024. // there are no more results. Once Next returns iterator.Done, all subsequent
  1025. // calls will return iterator.Done.
  1026. func (it *BucketIterator) Next() (*BucketAttrs, error) {
  1027. if err := it.nextFunc(); err != nil {
  1028. return nil, err
  1029. }
  1030. b := it.buckets[0]
  1031. it.buckets = it.buckets[1:]
  1032. return b, nil
  1033. }
  1034. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  1035. func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  1036. func (it *BucketIterator) fetch(pageSize int, pageToken string) (token string, err error) {
  1037. req := it.client.raw.Buckets.List(it.projectID)
  1038. setClientHeader(req.Header())
  1039. req.Projection("full")
  1040. req.Prefix(it.Prefix)
  1041. req.PageToken(pageToken)
  1042. if pageSize > 0 {
  1043. req.MaxResults(int64(pageSize))
  1044. }
  1045. var resp *raw.Buckets
  1046. err = runWithRetry(it.ctx, func() error {
  1047. resp, err = req.Context(it.ctx).Do()
  1048. return err
  1049. })
  1050. if err != nil {
  1051. return "", err
  1052. }
  1053. for _, item := range resp.Items {
  1054. b, err := newBucket(item)
  1055. if err != nil {
  1056. return "", err
  1057. }
  1058. it.buckets = append(it.buckets, b)
  1059. }
  1060. return resp.NextPageToken, nil
  1061. }