bucket.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  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. "fmt"
  17. "net/http"
  18. "reflect"
  19. "time"
  20. "cloud.google.com/go/internal/optional"
  21. "golang.org/x/net/context"
  22. "google.golang.org/api/googleapi"
  23. "google.golang.org/api/iterator"
  24. raw "google.golang.org/api/storage/v1"
  25. )
  26. // BucketHandle provides operations on a Google Cloud Storage bucket.
  27. // Use Client.Bucket to get a handle.
  28. type BucketHandle struct {
  29. c *Client
  30. name string
  31. acl ACLHandle
  32. defaultObjectACL ACLHandle
  33. conds *BucketConditions
  34. userProject string // project for requester-pays buckets
  35. }
  36. // Bucket returns a BucketHandle, which provides operations on the named bucket.
  37. // This call does not perform any network operations.
  38. //
  39. // The supplied name must contain only lowercase letters, numbers, dashes,
  40. // underscores, and dots. The full specification for valid bucket names can be
  41. // found at:
  42. // https://cloud.google.com/storage/docs/bucket-naming
  43. func (c *Client) Bucket(name string) *BucketHandle {
  44. return &BucketHandle{
  45. c: c,
  46. name: name,
  47. acl: ACLHandle{
  48. c: c,
  49. bucket: name,
  50. },
  51. defaultObjectACL: ACLHandle{
  52. c: c,
  53. bucket: name,
  54. isDefault: true,
  55. },
  56. }
  57. }
  58. // Create creates the Bucket in the project.
  59. // If attrs is nil the API defaults will be used.
  60. func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) error {
  61. var bkt *raw.Bucket
  62. if attrs != nil {
  63. bkt = attrs.toRawBucket()
  64. } else {
  65. bkt = &raw.Bucket{}
  66. }
  67. bkt.Name = b.name
  68. req := b.c.raw.Buckets.Insert(projectID, bkt)
  69. setClientHeader(req.Header())
  70. return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
  71. }
  72. // Delete deletes the Bucket.
  73. func (b *BucketHandle) Delete(ctx context.Context) error {
  74. req, err := b.newDeleteCall()
  75. if err != nil {
  76. return err
  77. }
  78. return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
  79. }
  80. func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) {
  81. req := b.c.raw.Buckets.Delete(b.name)
  82. setClientHeader(req.Header())
  83. if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil {
  84. return nil, err
  85. }
  86. if b.userProject != "" {
  87. req.UserProject(b.userProject)
  88. }
  89. return req, nil
  90. }
  91. // ACL returns an ACLHandle, which provides access to the bucket's access control list.
  92. // This controls who can list, create or overwrite the objects in a bucket.
  93. // This call does not perform any network operations.
  94. func (b *BucketHandle) ACL() *ACLHandle {
  95. return &b.acl
  96. }
  97. // DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs.
  98. // These ACLs are applied to newly created objects in this bucket that do not have a defined ACL.
  99. // This call does not perform any network operations.
  100. func (b *BucketHandle) DefaultObjectACL() *ACLHandle {
  101. return &b.defaultObjectACL
  102. }
  103. // Object returns an ObjectHandle, which provides operations on the named object.
  104. // This call does not perform any network operations.
  105. //
  106. // name must consist entirely of valid UTF-8-encoded runes. The full specification
  107. // for valid object names can be found at:
  108. // https://cloud.google.com/storage/docs/bucket-naming
  109. func (b *BucketHandle) Object(name string) *ObjectHandle {
  110. return &ObjectHandle{
  111. c: b.c,
  112. bucket: b.name,
  113. object: name,
  114. acl: ACLHandle{
  115. c: b.c,
  116. bucket: b.name,
  117. object: name,
  118. userProject: b.userProject,
  119. },
  120. gen: -1,
  121. userProject: b.userProject,
  122. }
  123. }
  124. // Attrs returns the metadata for the bucket.
  125. func (b *BucketHandle) Attrs(ctx context.Context) (*BucketAttrs, error) {
  126. req, err := b.newGetCall()
  127. if err != nil {
  128. return nil, err
  129. }
  130. var resp *raw.Bucket
  131. err = runWithRetry(ctx, func() error {
  132. resp, err = req.Context(ctx).Do()
  133. return err
  134. })
  135. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  136. return nil, ErrBucketNotExist
  137. }
  138. if err != nil {
  139. return nil, err
  140. }
  141. return newBucket(resp), nil
  142. }
  143. func (b *BucketHandle) newGetCall() (*raw.BucketsGetCall, error) {
  144. req := b.c.raw.Buckets.Get(b.name).Projection("full")
  145. setClientHeader(req.Header())
  146. if err := applyBucketConds("BucketHandle.Attrs", b.conds, req); err != nil {
  147. return nil, err
  148. }
  149. if b.userProject != "" {
  150. req.UserProject(b.userProject)
  151. }
  152. return req, nil
  153. }
  154. func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (*BucketAttrs, error) {
  155. req, err := b.newPatchCall(&uattrs)
  156. if err != nil {
  157. return nil, err
  158. }
  159. // TODO(jba): retry iff metagen is set?
  160. rb, err := req.Context(ctx).Do()
  161. if err != nil {
  162. return nil, err
  163. }
  164. return newBucket(rb), nil
  165. }
  166. func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) {
  167. rb := uattrs.toRawBucket()
  168. req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full")
  169. setClientHeader(req.Header())
  170. if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil {
  171. return nil, err
  172. }
  173. if b.userProject != "" {
  174. req.UserProject(b.userProject)
  175. }
  176. return req, nil
  177. }
  178. // BucketAttrs represents the metadata for a Google Cloud Storage bucket.
  179. type BucketAttrs struct {
  180. // Name is the name of the bucket.
  181. Name string
  182. // ACL is the list of access control rules on the bucket.
  183. ACL []ACLRule
  184. // DefaultObjectACL is the list of access controls to
  185. // apply to new objects when no object ACL is provided.
  186. DefaultObjectACL []ACLRule
  187. // Location is the location of the bucket. It defaults to "US".
  188. Location string
  189. // MetaGeneration is the metadata generation of the bucket.
  190. MetaGeneration int64
  191. // StorageClass is the default storage class of the bucket. This defines
  192. // how objects in the bucket are stored and determines the SLA
  193. // and the cost of storage. Typical values are "MULTI_REGIONAL",
  194. // "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
  195. // "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which
  196. // is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on
  197. // the bucket's location settings.
  198. StorageClass string
  199. // Created is the creation time of the bucket.
  200. Created time.Time
  201. // VersioningEnabled reports whether this bucket has versioning enabled.
  202. // This field is read-only.
  203. VersioningEnabled bool
  204. // Labels are the bucket's labels.
  205. Labels map[string]string
  206. // RequesterPays reports whether the bucket is a Requester Pays bucket.
  207. RequesterPays bool
  208. }
  209. func newBucket(b *raw.Bucket) *BucketAttrs {
  210. if b == nil {
  211. return nil
  212. }
  213. bucket := &BucketAttrs{
  214. Name: b.Name,
  215. Location: b.Location,
  216. MetaGeneration: b.Metageneration,
  217. StorageClass: b.StorageClass,
  218. Created: convertTime(b.TimeCreated),
  219. VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled,
  220. Labels: b.Labels,
  221. RequesterPays: b.Billing != nil && b.Billing.RequesterPays,
  222. }
  223. acl := make([]ACLRule, len(b.Acl))
  224. for i, rule := range b.Acl {
  225. acl[i] = ACLRule{
  226. Entity: ACLEntity(rule.Entity),
  227. Role: ACLRole(rule.Role),
  228. }
  229. }
  230. bucket.ACL = acl
  231. objACL := make([]ACLRule, len(b.DefaultObjectAcl))
  232. for i, rule := range b.DefaultObjectAcl {
  233. objACL[i] = ACLRule{
  234. Entity: ACLEntity(rule.Entity),
  235. Role: ACLRole(rule.Role),
  236. }
  237. }
  238. bucket.DefaultObjectACL = objACL
  239. return bucket
  240. }
  241. // toRawBucket copies the editable attribute from b to the raw library's Bucket type.
  242. func (b *BucketAttrs) toRawBucket() *raw.Bucket {
  243. var acl []*raw.BucketAccessControl
  244. if len(b.ACL) > 0 {
  245. acl = make([]*raw.BucketAccessControl, len(b.ACL))
  246. for i, rule := range b.ACL {
  247. acl[i] = &raw.BucketAccessControl{
  248. Entity: string(rule.Entity),
  249. Role: string(rule.Role),
  250. }
  251. }
  252. }
  253. dACL := toRawObjectACL(b.DefaultObjectACL)
  254. // Copy label map.
  255. var labels map[string]string
  256. if len(b.Labels) > 0 {
  257. labels = make(map[string]string, len(b.Labels))
  258. for k, v := range b.Labels {
  259. labels[k] = v
  260. }
  261. }
  262. // Ignore VersioningEnabled if it is false. This is OK because
  263. // we only call this method when creating a bucket, and by default
  264. // new buckets have versioning off.
  265. var v *raw.BucketVersioning
  266. if b.VersioningEnabled {
  267. v = &raw.BucketVersioning{Enabled: true}
  268. }
  269. var bb *raw.BucketBilling
  270. if b.RequesterPays {
  271. bb = &raw.BucketBilling{RequesterPays: true}
  272. }
  273. return &raw.Bucket{
  274. Name: b.Name,
  275. DefaultObjectAcl: dACL,
  276. Location: b.Location,
  277. StorageClass: b.StorageClass,
  278. Acl: acl,
  279. Versioning: v,
  280. Labels: labels,
  281. Billing: bb,
  282. }
  283. }
  284. type BucketAttrsToUpdate struct {
  285. // VersioningEnabled, if set, updates whether the bucket uses versioning.
  286. VersioningEnabled optional.Bool
  287. // RequesterPays, if set, updates whether the bucket is a Requester Pays bucket.
  288. RequesterPays optional.Bool
  289. setLabels map[string]string
  290. deleteLabels map[string]bool
  291. }
  292. // SetLabel causes a label to be added or modified when ua is used
  293. // in a call to Bucket.Update.
  294. func (ua *BucketAttrsToUpdate) SetLabel(name, value string) {
  295. if ua.setLabels == nil {
  296. ua.setLabels = map[string]string{}
  297. }
  298. ua.setLabels[name] = value
  299. }
  300. // DeleteLabel causes a label to be deleted when ua is used in a
  301. // call to Bucket.Update.
  302. func (ua *BucketAttrsToUpdate) DeleteLabel(name string) {
  303. if ua.deleteLabels == nil {
  304. ua.deleteLabels = map[string]bool{}
  305. }
  306. ua.deleteLabels[name] = true
  307. }
  308. func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
  309. rb := &raw.Bucket{}
  310. if ua.VersioningEnabled != nil {
  311. rb.Versioning = &raw.BucketVersioning{
  312. Enabled: optional.ToBool(ua.VersioningEnabled),
  313. ForceSendFields: []string{"Enabled"},
  314. }
  315. }
  316. if ua.RequesterPays != nil {
  317. rb.Billing = &raw.BucketBilling{
  318. RequesterPays: optional.ToBool(ua.RequesterPays),
  319. ForceSendFields: []string{"RequesterPays"},
  320. }
  321. }
  322. if ua.setLabels != nil || ua.deleteLabels != nil {
  323. rb.Labels = map[string]string{}
  324. for k, v := range ua.setLabels {
  325. rb.Labels[k] = v
  326. }
  327. if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 {
  328. rb.ForceSendFields = append(rb.ForceSendFields, "Labels")
  329. }
  330. for l := range ua.deleteLabels {
  331. rb.NullFields = append(rb.NullFields, "Labels."+l)
  332. }
  333. }
  334. return rb
  335. }
  336. // If returns a new BucketHandle that applies a set of preconditions.
  337. // Preconditions already set on the BucketHandle are ignored.
  338. // Operations on the new handle will only occur if the preconditions are
  339. // satisfied. The only valid preconditions for buckets are MetagenerationMatch
  340. // and MetagenerationNotMatch.
  341. func (b *BucketHandle) If(conds BucketConditions) *BucketHandle {
  342. b2 := *b
  343. b2.conds = &conds
  344. return &b2
  345. }
  346. // BucketConditions constrain bucket methods to act on specific metagenerations.
  347. //
  348. // The zero value is an empty set of constraints.
  349. type BucketConditions struct {
  350. // MetagenerationMatch specifies that the bucket must have the given
  351. // metageneration for the operation to occur.
  352. // If MetagenerationMatch is zero, it has no effect.
  353. MetagenerationMatch int64
  354. // MetagenerationNotMatch specifies that the bucket must not have the given
  355. // metageneration for the operation to occur.
  356. // If MetagenerationNotMatch is zero, it has no effect.
  357. MetagenerationNotMatch int64
  358. }
  359. func (c *BucketConditions) validate(method string) error {
  360. if *c == (BucketConditions{}) {
  361. return fmt.Errorf("storage: %s: empty conditions", method)
  362. }
  363. if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 {
  364. return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
  365. }
  366. return nil
  367. }
  368. // UserProject returns a new BucketHandle that passes the project ID as the user
  369. // project for all subsequent calls. A user project is required for all operations
  370. // on requester-pays buckets.
  371. func (b *BucketHandle) UserProject(projectID string) *BucketHandle {
  372. b2 := *b
  373. b2.userProject = projectID
  374. b2.acl.userProject = projectID
  375. b2.defaultObjectACL.userProject = projectID
  376. return &b2
  377. }
  378. // applyBucketConds modifies the provided call using the conditions in conds.
  379. // call is something that quacks like a *raw.WhateverCall.
  380. func applyBucketConds(method string, conds *BucketConditions, call interface{}) error {
  381. if conds == nil {
  382. return nil
  383. }
  384. if err := conds.validate(method); err != nil {
  385. return err
  386. }
  387. cval := reflect.ValueOf(call)
  388. switch {
  389. case conds.MetagenerationMatch != 0:
  390. if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
  391. return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
  392. }
  393. case conds.MetagenerationNotMatch != 0:
  394. if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
  395. return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
  396. }
  397. }
  398. return nil
  399. }
  400. // Objects returns an iterator over the objects in the bucket that match the Query q.
  401. // If q is nil, no filtering is done.
  402. func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
  403. it := &ObjectIterator{
  404. ctx: ctx,
  405. bucket: b,
  406. }
  407. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  408. it.fetch,
  409. func() int { return len(it.items) },
  410. func() interface{} { b := it.items; it.items = nil; return b })
  411. if q != nil {
  412. it.query = *q
  413. }
  414. return it
  415. }
  416. // An ObjectIterator is an iterator over ObjectAttrs.
  417. type ObjectIterator struct {
  418. ctx context.Context
  419. bucket *BucketHandle
  420. query Query
  421. pageInfo *iterator.PageInfo
  422. nextFunc func() error
  423. items []*ObjectAttrs
  424. }
  425. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  426. func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  427. // Next returns the next result. Its second return value is iterator.Done if
  428. // there are no more results. Once Next returns iterator.Done, all subsequent
  429. // calls will return iterator.Done.
  430. //
  431. // If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will
  432. // have a non-empty Prefix field, and a zero value for all other fields. These
  433. // represent prefixes.
  434. func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
  435. if err := it.nextFunc(); err != nil {
  436. return nil, err
  437. }
  438. item := it.items[0]
  439. it.items = it.items[1:]
  440. return item, nil
  441. }
  442. func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
  443. req := it.bucket.c.raw.Objects.List(it.bucket.name)
  444. setClientHeader(req.Header())
  445. req.Projection("full")
  446. req.Delimiter(it.query.Delimiter)
  447. req.Prefix(it.query.Prefix)
  448. req.Versions(it.query.Versions)
  449. req.PageToken(pageToken)
  450. if it.bucket.userProject != "" {
  451. req.UserProject(it.bucket.userProject)
  452. }
  453. if pageSize > 0 {
  454. req.MaxResults(int64(pageSize))
  455. }
  456. var resp *raw.Objects
  457. var err error
  458. err = runWithRetry(it.ctx, func() error {
  459. resp, err = req.Context(it.ctx).Do()
  460. return err
  461. })
  462. if err != nil {
  463. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  464. err = ErrBucketNotExist
  465. }
  466. return "", err
  467. }
  468. for _, item := range resp.Items {
  469. it.items = append(it.items, newObject(item))
  470. }
  471. for _, prefix := range resp.Prefixes {
  472. it.items = append(it.items, &ObjectAttrs{Prefix: prefix})
  473. }
  474. return resp.NextPageToken, nil
  475. }
  476. // TODO(jbd): Add storage.buckets.update.
  477. // Buckets returns an iterator over the buckets in the project. You may
  478. // optionally set the iterator's Prefix field to restrict the list to buckets
  479. // whose names begin with the prefix. By default, all buckets in the project
  480. // are returned.
  481. func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator {
  482. it := &BucketIterator{
  483. ctx: ctx,
  484. client: c,
  485. projectID: projectID,
  486. }
  487. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  488. it.fetch,
  489. func() int { return len(it.buckets) },
  490. func() interface{} { b := it.buckets; it.buckets = nil; return b })
  491. return it
  492. }
  493. // A BucketIterator is an iterator over BucketAttrs.
  494. type BucketIterator struct {
  495. // Prefix restricts the iterator to buckets whose names begin with it.
  496. Prefix string
  497. ctx context.Context
  498. client *Client
  499. projectID string
  500. buckets []*BucketAttrs
  501. pageInfo *iterator.PageInfo
  502. nextFunc func() error
  503. }
  504. // Next returns the next result. Its second return value is iterator.Done if
  505. // there are no more results. Once Next returns iterator.Done, all subsequent
  506. // calls will return iterator.Done.
  507. func (it *BucketIterator) Next() (*BucketAttrs, error) {
  508. if err := it.nextFunc(); err != nil {
  509. return nil, err
  510. }
  511. b := it.buckets[0]
  512. it.buckets = it.buckets[1:]
  513. return b, nil
  514. }
  515. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  516. func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  517. func (it *BucketIterator) fetch(pageSize int, pageToken string) (string, error) {
  518. req := it.client.raw.Buckets.List(it.projectID)
  519. setClientHeader(req.Header())
  520. req.Projection("full")
  521. req.Prefix(it.Prefix)
  522. req.PageToken(pageToken)
  523. if pageSize > 0 {
  524. req.MaxResults(int64(pageSize))
  525. }
  526. var resp *raw.Buckets
  527. var err error
  528. err = runWithRetry(it.ctx, func() error {
  529. resp, err = req.Context(it.ctx).Do()
  530. return err
  531. })
  532. if err != nil {
  533. return "", err
  534. }
  535. for _, item := range resp.Items {
  536. it.buckets = append(it.buckets, newBucket(item))
  537. }
  538. return resp.NextPageToken, nil
  539. }