Home | History | Annotate | Download | only in storage
      1 // Copyright 2014 Google Inc. LiveAndArchived 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 
     15 package storage
     16 
     17 import (
     18 	"fmt"
     19 	"net/http"
     20 	"reflect"
     21 	"time"
     22 
     23 	"cloud.google.com/go/internal/optional"
     24 	"golang.org/x/net/context"
     25 	"google.golang.org/api/googleapi"
     26 	"google.golang.org/api/iterator"
     27 	raw "google.golang.org/api/storage/v1"
     28 )
     29 
     30 // BucketHandle provides operations on a Google Cloud Storage bucket.
     31 // Use Client.Bucket to get a handle.
     32 type BucketHandle struct {
     33 	c                *Client
     34 	name             string
     35 	acl              ACLHandle
     36 	defaultObjectACL ACLHandle
     37 	conds            *BucketConditions
     38 	userProject      string // project for requester-pays buckets
     39 }
     40 
     41 // Bucket returns a BucketHandle, which provides operations on the named bucket.
     42 // This call does not perform any network operations.
     43 //
     44 // The supplied name must contain only lowercase letters, numbers, dashes,
     45 // underscores, and dots. The full specification for valid bucket names can be
     46 // found at:
     47 //   https://cloud.google.com/storage/docs/bucket-naming
     48 func (c *Client) Bucket(name string) *BucketHandle {
     49 	return &BucketHandle{
     50 		c:    c,
     51 		name: name,
     52 		acl: ACLHandle{
     53 			c:      c,
     54 			bucket: name,
     55 		},
     56 		defaultObjectACL: ACLHandle{
     57 			c:         c,
     58 			bucket:    name,
     59 			isDefault: true,
     60 		},
     61 	}
     62 }
     63 
     64 // Create creates the Bucket in the project.
     65 // If attrs is nil the API defaults will be used.
     66 func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) error {
     67 	var bkt *raw.Bucket
     68 	if attrs != nil {
     69 		bkt = attrs.toRawBucket()
     70 	} else {
     71 		bkt = &raw.Bucket{}
     72 	}
     73 	bkt.Name = b.name
     74 	// If there is lifecycle information but no location, explicitly set
     75 	// the location. This is a GCS quirk/bug.
     76 	if bkt.Location == "" && bkt.Lifecycle != nil {
     77 		bkt.Location = "US"
     78 	}
     79 	req := b.c.raw.Buckets.Insert(projectID, bkt)
     80 	setClientHeader(req.Header())
     81 	return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
     82 }
     83 
     84 // Delete deletes the Bucket.
     85 func (b *BucketHandle) Delete(ctx context.Context) error {
     86 	req, err := b.newDeleteCall()
     87 	if err != nil {
     88 		return err
     89 	}
     90 	return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
     91 }
     92 
     93 func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) {
     94 	req := b.c.raw.Buckets.Delete(b.name)
     95 	setClientHeader(req.Header())
     96 	if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil {
     97 		return nil, err
     98 	}
     99 	if b.userProject != "" {
    100 		req.UserProject(b.userProject)
    101 	}
    102 	return req, nil
    103 }
    104 
    105 // ACL returns an ACLHandle, which provides access to the bucket's access control list.
    106 // This controls who can list, create or overwrite the objects in a bucket.
    107 // This call does not perform any network operations.
    108 func (b *BucketHandle) ACL() *ACLHandle {
    109 	return &b.acl
    110 }
    111 
    112 // DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs.
    113 // These ACLs are applied to newly created objects in this bucket that do not have a defined ACL.
    114 // This call does not perform any network operations.
    115 func (b *BucketHandle) DefaultObjectACL() *ACLHandle {
    116 	return &b.defaultObjectACL
    117 }
    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 
    141 // Attrs returns the metadata for the bucket.
    142 func (b *BucketHandle) Attrs(ctx context.Context) (*BucketAttrs, error) {
    143 	req, err := b.newGetCall()
    144 	if err != nil {
    145 		return nil, err
    146 	}
    147 	var resp *raw.Bucket
    148 	err = runWithRetry(ctx, func() error {
    149 		resp, err = req.Context(ctx).Do()
    150 		return err
    151 	})
    152 	if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
    153 		return nil, ErrBucketNotExist
    154 	}
    155 	if err != nil {
    156 		return nil, err
    157 	}
    158 	return newBucket(resp), nil
    159 }
    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 
    173 func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (*BucketAttrs, error) {
    174 	req, err := b.newPatchCall(&uattrs)
    175 	if err != nil {
    176 		return nil, err
    177 	}
    178 	// TODO(jba): retry iff metagen is set?
    179 	rb, err := req.Context(ctx).Do()
    180 	if err != nil {
    181 		return nil, err
    182 	}
    183 	return newBucket(rb), nil
    184 }
    185 
    186 func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) {
    187 	rb := uattrs.toRawBucket()
    188 	req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full")
    189 	setClientHeader(req.Header())
    190 	if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil {
    191 		return nil, err
    192 	}
    193 	if b.userProject != "" {
    194 		req.UserProject(b.userProject)
    195 	}
    196 	return req, nil
    197 }
    198 
    199 // BucketAttrs represents the metadata for a Google Cloud Storage bucket.
    200 type BucketAttrs struct {
    201 	// Name is the name of the bucket.
    202 	Name string
    203 
    204 	// ACL is the list of access control rules on the bucket.
    205 	ACL []ACLRule
    206 
    207 	// DefaultObjectACL is the list of access controls to
    208 	// apply to new objects when no object ACL is provided.
    209 	DefaultObjectACL []ACLRule
    210 
    211 	// Location is the location of the bucket. It defaults to "US".
    212 	Location string
    213 
    214 	// MetaGeneration is the metadata generation of the bucket.
    215 	MetaGeneration int64
    216 
    217 	// StorageClass is the default storage class of the bucket. This defines
    218 	// how objects in the bucket are stored and determines the SLA
    219 	// and the cost of storage. Typical values are "MULTI_REGIONAL",
    220 	// "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
    221 	// "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which
    222 	// is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on
    223 	// the bucket's location settings.
    224 	StorageClass string
    225 
    226 	// Created is the creation time of the bucket.
    227 	Created time.Time
    228 
    229 	// VersioningEnabled reports whether this bucket has versioning enabled.
    230 	// This field is read-only.
    231 	VersioningEnabled bool
    232 
    233 	// Labels are the bucket's labels.
    234 	Labels map[string]string
    235 
    236 	// RequesterPays reports whether the bucket is a Requester Pays bucket.
    237 	RequesterPays bool
    238 	// Lifecycle is the lifecycle configuration for objects in the bucket.
    239 	Lifecycle Lifecycle
    240 }
    241 
    242 // Lifecycle is the lifecycle configuration for objects in the bucket.
    243 type Lifecycle struct {
    244 	Rules []LifecycleRule
    245 }
    246 
    247 const (
    248 	// RFC3339 date with only the date segment, used for CreatedBefore in LifecycleRule.
    249 	rfc3339Date = "2006-01-02"
    250 
    251 	// DeleteAction is a lifecycle action that deletes a live and/or archived
    252 	// objects. Takes precendence over SetStorageClass actions.
    253 	DeleteAction = "Delete"
    254 
    255 	// SetStorageClassAction changes the storage class of live and/or archived
    256 	// objects.
    257 	SetStorageClassAction = "SetStorageClass"
    258 )
    259 
    260 // LifecycleRule is a lifecycle configuration rule.
    261 //
    262 // When all the configured conditions are met by an object in the bucket, the
    263 // configured action will automatically be taken on that object.
    264 type LifecycleRule struct {
    265 	// Action is the action to take when all of the associated conditions are
    266 	// met.
    267 	Action LifecycleAction
    268 
    269 	// Condition is the set of conditions that must be met for the associated
    270 	// action to be taken.
    271 	Condition LifecycleCondition
    272 }
    273 
    274 // LifecycleAction is a lifecycle configuration action.
    275 type LifecycleAction struct {
    276 	// Type is the type of action to take on matching objects.
    277 	//
    278 	// Acceptable values are "Delete" to delete matching objects and
    279 	// "SetStorageClass" to set the storage class defined in StorageClass on
    280 	// matching objects.
    281 	Type string
    282 
    283 	// StorageClass is the storage class to set on matching objects if the Action
    284 	// is "SetStorageClass".
    285 	StorageClass string
    286 }
    287 
    288 // Liveness specifies whether the object is live or not.
    289 type Liveness int
    290 
    291 const (
    292 	// LiveAndArchived includes both live and archived objects.
    293 	LiveAndArchived Liveness = iota
    294 	// Live specifies that the object is still live.
    295 	Live
    296 	// Archived specifies that the object is archived.
    297 	Archived
    298 )
    299 
    300 // LifecycleCondition is a set of conditions used to match objects and take an
    301 // action automatically.
    302 //
    303 // All configured conditions must be met for the associated action to be taken.
    304 type LifecycleCondition struct {
    305 	// AgeInDays is the age of the object in days.
    306 	AgeInDays int64
    307 
    308 	// CreatedBefore is the time the object was created.
    309 	//
    310 	// This condition is satisfied when an object is created before midnight of
    311 	// the specified date in UTC.
    312 	CreatedBefore time.Time
    313 
    314 	// Liveness specifies the object's liveness. Relevant only for versioned objects
    315 	Liveness Liveness
    316 
    317 	// MatchesStorageClasses is the condition matching the object's storage
    318 	// class.
    319 	//
    320 	// Values include "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE",
    321 	// "STANDARD", and "DURABLE_REDUCED_AVAILABILITY".
    322 	MatchesStorageClasses []string
    323 
    324 	// NumNewerVersions is the condition matching objects with a number of newer versions.
    325 	//
    326 	// If the value is N, this condition is satisfied when there are at least N
    327 	// versions (including the live version) newer than this version of the
    328 	// object.
    329 	NumNewerVersions int64
    330 }
    331 
    332 func newBucket(b *raw.Bucket) *BucketAttrs {
    333 	if b == nil {
    334 		return nil
    335 	}
    336 	bucket := &BucketAttrs{
    337 		Name:              b.Name,
    338 		Location:          b.Location,
    339 		MetaGeneration:    b.Metageneration,
    340 		StorageClass:      b.StorageClass,
    341 		Created:           convertTime(b.TimeCreated),
    342 		VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled,
    343 		Labels:            b.Labels,
    344 		RequesterPays:     b.Billing != nil && b.Billing.RequesterPays,
    345 		Lifecycle:         toLifecycle(b.Lifecycle),
    346 	}
    347 	acl := make([]ACLRule, len(b.Acl))
    348 	for i, rule := range b.Acl {
    349 		acl[i] = ACLRule{
    350 			Entity: ACLEntity(rule.Entity),
    351 			Role:   ACLRole(rule.Role),
    352 		}
    353 	}
    354 	bucket.ACL = acl
    355 	objACL := make([]ACLRule, len(b.DefaultObjectAcl))
    356 	for i, rule := range b.DefaultObjectAcl {
    357 		objACL[i] = ACLRule{
    358 			Entity: ACLEntity(rule.Entity),
    359 			Role:   ACLRole(rule.Role),
    360 		}
    361 	}
    362 	bucket.DefaultObjectACL = objACL
    363 	return bucket
    364 }
    365 
    366 // toRawBucket copies the editable attribute from b to the raw library's Bucket type.
    367 func (b *BucketAttrs) toRawBucket() *raw.Bucket {
    368 	var acl []*raw.BucketAccessControl
    369 	if len(b.ACL) > 0 {
    370 		acl = make([]*raw.BucketAccessControl, len(b.ACL))
    371 		for i, rule := range b.ACL {
    372 			acl[i] = &raw.BucketAccessControl{
    373 				Entity: string(rule.Entity),
    374 				Role:   string(rule.Role),
    375 			}
    376 		}
    377 	}
    378 	dACL := toRawObjectACL(b.DefaultObjectACL)
    379 	// Copy label map.
    380 	var labels map[string]string
    381 	if len(b.Labels) > 0 {
    382 		labels = make(map[string]string, len(b.Labels))
    383 		for k, v := range b.Labels {
    384 			labels[k] = v
    385 		}
    386 	}
    387 	// Ignore VersioningEnabled if it is false. This is OK because
    388 	// we only call this method when creating a bucket, and by default
    389 	// new buckets have versioning off.
    390 	var v *raw.BucketVersioning
    391 	if b.VersioningEnabled {
    392 		v = &raw.BucketVersioning{Enabled: true}
    393 	}
    394 	var bb *raw.BucketBilling
    395 	if b.RequesterPays {
    396 		bb = &raw.BucketBilling{RequesterPays: true}
    397 	}
    398 	return &raw.Bucket{
    399 		Name:             b.Name,
    400 		DefaultObjectAcl: dACL,
    401 		Location:         b.Location,
    402 		StorageClass:     b.StorageClass,
    403 		Acl:              acl,
    404 		Versioning:       v,
    405 		Labels:           labels,
    406 		Billing:          bb,
    407 		Lifecycle:        toRawLifecycle(b.Lifecycle),
    408 	}
    409 }
    410 
    411 type BucketAttrsToUpdate struct {
    412 	// VersioningEnabled, if set, updates whether the bucket uses versioning.
    413 	VersioningEnabled optional.Bool
    414 
    415 	// RequesterPays, if set, updates whether the bucket is a Requester Pays bucket.
    416 	RequesterPays optional.Bool
    417 
    418 	setLabels    map[string]string
    419 	deleteLabels map[string]bool
    420 }
    421 
    422 // SetLabel causes a label to be added or modified when ua is used
    423 // in a call to Bucket.Update.
    424 func (ua *BucketAttrsToUpdate) SetLabel(name, value string) {
    425 	if ua.setLabels == nil {
    426 		ua.setLabels = map[string]string{}
    427 	}
    428 	ua.setLabels[name] = value
    429 }
    430 
    431 // DeleteLabel causes a label to be deleted when ua is used in a
    432 // call to Bucket.Update.
    433 func (ua *BucketAttrsToUpdate) DeleteLabel(name string) {
    434 	if ua.deleteLabels == nil {
    435 		ua.deleteLabels = map[string]bool{}
    436 	}
    437 	ua.deleteLabels[name] = true
    438 }
    439 
    440 func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
    441 	rb := &raw.Bucket{}
    442 	if ua.VersioningEnabled != nil {
    443 		rb.Versioning = &raw.BucketVersioning{
    444 			Enabled:         optional.ToBool(ua.VersioningEnabled),
    445 			ForceSendFields: []string{"Enabled"},
    446 		}
    447 	}
    448 	if ua.RequesterPays != nil {
    449 		rb.Billing = &raw.BucketBilling{
    450 			RequesterPays:   optional.ToBool(ua.RequesterPays),
    451 			ForceSendFields: []string{"RequesterPays"},
    452 		}
    453 	}
    454 	if ua.setLabels != nil || ua.deleteLabels != nil {
    455 		rb.Labels = map[string]string{}
    456 		for k, v := range ua.setLabels {
    457 			rb.Labels[k] = v
    458 		}
    459 		if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 {
    460 			rb.ForceSendFields = append(rb.ForceSendFields, "Labels")
    461 		}
    462 		for l := range ua.deleteLabels {
    463 			rb.NullFields = append(rb.NullFields, "Labels."+l)
    464 		}
    465 	}
    466 	return rb
    467 }
    468 
    469 // If returns a new BucketHandle that applies a set of preconditions.
    470 // Preconditions already set on the BucketHandle are ignored.
    471 // Operations on the new handle will only occur if the preconditions are
    472 // satisfied. The only valid preconditions for buckets are MetagenerationMatch
    473 // and MetagenerationNotMatch.
    474 func (b *BucketHandle) If(conds BucketConditions) *BucketHandle {
    475 	b2 := *b
    476 	b2.conds = &conds
    477 	return &b2
    478 }
    479 
    480 // BucketConditions constrain bucket methods to act on specific metagenerations.
    481 //
    482 // The zero value is an empty set of constraints.
    483 type BucketConditions struct {
    484 	// MetagenerationMatch specifies that the bucket must have the given
    485 	// metageneration for the operation to occur.
    486 	// If MetagenerationMatch is zero, it has no effect.
    487 	MetagenerationMatch int64
    488 
    489 	// MetagenerationNotMatch specifies that the bucket must not have the given
    490 	// metageneration for the operation to occur.
    491 	// If MetagenerationNotMatch is zero, it has no effect.
    492 	MetagenerationNotMatch int64
    493 }
    494 
    495 func (c *BucketConditions) validate(method string) error {
    496 	if *c == (BucketConditions{}) {
    497 		return fmt.Errorf("storage: %s: empty conditions", method)
    498 	}
    499 	if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 {
    500 		return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
    501 	}
    502 	return nil
    503 }
    504 
    505 // UserProject returns a new BucketHandle that passes the project ID as the user
    506 // project for all subsequent calls. A user project is required for all operations
    507 // on requester-pays buckets.
    508 func (b *BucketHandle) UserProject(projectID string) *BucketHandle {
    509 	b2 := *b
    510 	b2.userProject = projectID
    511 	b2.acl.userProject = projectID
    512 	b2.defaultObjectACL.userProject = projectID
    513 	return &b2
    514 }
    515 
    516 // applyBucketConds modifies the provided call using the conditions in conds.
    517 // call is something that quacks like a *raw.WhateverCall.
    518 func applyBucketConds(method string, conds *BucketConditions, call interface{}) error {
    519 	if conds == nil {
    520 		return nil
    521 	}
    522 	if err := conds.validate(method); err != nil {
    523 		return err
    524 	}
    525 	cval := reflect.ValueOf(call)
    526 	switch {
    527 	case conds.MetagenerationMatch != 0:
    528 		if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
    529 			return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
    530 		}
    531 	case conds.MetagenerationNotMatch != 0:
    532 		if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
    533 			return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
    534 		}
    535 	}
    536 	return nil
    537 }
    538 
    539 func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle {
    540 	var rl raw.BucketLifecycle
    541 	if len(l.Rules) == 0 {
    542 		return nil
    543 	}
    544 	for _, r := range l.Rules {
    545 		rr := &raw.BucketLifecycleRule{
    546 			Action: &raw.BucketLifecycleRuleAction{
    547 				Type:         r.Action.Type,
    548 				StorageClass: r.Action.StorageClass,
    549 			},
    550 			Condition: &raw.BucketLifecycleRuleCondition{
    551 				Age:                 r.Condition.AgeInDays,
    552 				MatchesStorageClass: r.Condition.MatchesStorageClasses,
    553 				NumNewerVersions:    r.Condition.NumNewerVersions,
    554 			},
    555 		}
    556 
    557 		switch r.Condition.Liveness {
    558 		case LiveAndArchived:
    559 			rr.Condition.IsLive = nil
    560 		case Live:
    561 			rr.Condition.IsLive = googleapi.Bool(true)
    562 		case Archived:
    563 			rr.Condition.IsLive = googleapi.Bool(false)
    564 		}
    565 
    566 		if !r.Condition.CreatedBefore.IsZero() {
    567 			rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date)
    568 		}
    569 		rl.Rule = append(rl.Rule, rr)
    570 	}
    571 	return &rl
    572 }
    573 
    574 func toLifecycle(rl *raw.BucketLifecycle) Lifecycle {
    575 	var l Lifecycle
    576 	if rl == nil {
    577 		return l
    578 	}
    579 	for _, rr := range rl.Rule {
    580 		r := LifecycleRule{
    581 			Action: LifecycleAction{
    582 				Type:         rr.Action.Type,
    583 				StorageClass: rr.Action.StorageClass,
    584 			},
    585 			Condition: LifecycleCondition{
    586 				AgeInDays:             rr.Condition.Age,
    587 				MatchesStorageClasses: rr.Condition.MatchesStorageClass,
    588 				NumNewerVersions:      rr.Condition.NumNewerVersions,
    589 			},
    590 		}
    591 
    592 		switch {
    593 		case rr.Condition.IsLive == nil:
    594 			r.Condition.Liveness = LiveAndArchived
    595 		case *rr.Condition.IsLive == true:
    596 			r.Condition.Liveness = Live
    597 		case *rr.Condition.IsLive == false:
    598 			r.Condition.Liveness = Archived
    599 		}
    600 
    601 		if rr.Condition.CreatedBefore != "" {
    602 			r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore)
    603 		}
    604 	}
    605 	return l
    606 }
    607 
    608 // Objects returns an iterator over the objects in the bucket that match the Query q.
    609 // If q is nil, no filtering is done.
    610 func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
    611 	it := &ObjectIterator{
    612 		ctx:    ctx,
    613 		bucket: b,
    614 	}
    615 	it.pageInfo, it.nextFunc = iterator.NewPageInfo(
    616 		it.fetch,
    617 		func() int { return len(it.items) },
    618 		func() interface{} { b := it.items; it.items = nil; return b })
    619 	if q != nil {
    620 		it.query = *q
    621 	}
    622 	return it
    623 }
    624 
    625 // An ObjectIterator is an iterator over ObjectAttrs.
    626 type ObjectIterator struct {
    627 	ctx      context.Context
    628 	bucket   *BucketHandle
    629 	query    Query
    630 	pageInfo *iterator.PageInfo
    631 	nextFunc func() error
    632 	items    []*ObjectAttrs
    633 }
    634 
    635 // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
    636 func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
    637 
    638 // Next returns the next result. Its second return value is iterator.Done if
    639 // there are no more results. Once Next returns iterator.Done, all subsequent
    640 // calls will return iterator.Done.
    641 //
    642 // If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will
    643 // have a non-empty Prefix field, and a zero value for all other fields. These
    644 // represent prefixes.
    645 func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
    646 	if err := it.nextFunc(); err != nil {
    647 		return nil, err
    648 	}
    649 	item := it.items[0]
    650 	it.items = it.items[1:]
    651 	return item, nil
    652 }
    653 
    654 func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
    655 	req := it.bucket.c.raw.Objects.List(it.bucket.name)
    656 	setClientHeader(req.Header())
    657 	req.Projection("full")
    658 	req.Delimiter(it.query.Delimiter)
    659 	req.Prefix(it.query.Prefix)
    660 	req.Versions(it.query.Versions)
    661 	req.PageToken(pageToken)
    662 	if it.bucket.userProject != "" {
    663 		req.UserProject(it.bucket.userProject)
    664 	}
    665 	if pageSize > 0 {
    666 		req.MaxResults(int64(pageSize))
    667 	}
    668 	var resp *raw.Objects
    669 	var err error
    670 	err = runWithRetry(it.ctx, func() error {
    671 		resp, err = req.Context(it.ctx).Do()
    672 		return err
    673 	})
    674 	if err != nil {
    675 		if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
    676 			err = ErrBucketNotExist
    677 		}
    678 		return "", err
    679 	}
    680 	for _, item := range resp.Items {
    681 		it.items = append(it.items, newObject(item))
    682 	}
    683 	for _, prefix := range resp.Prefixes {
    684 		it.items = append(it.items, &ObjectAttrs{Prefix: prefix})
    685 	}
    686 	return resp.NextPageToken, nil
    687 }
    688 
    689 // TODO(jbd): Add storage.buckets.update.
    690 
    691 // Buckets returns an iterator over the buckets in the project. You may
    692 // optionally set the iterator's Prefix field to restrict the list to buckets
    693 // whose names begin with the prefix. By default, all buckets in the project
    694 // are returned.
    695 func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator {
    696 	it := &BucketIterator{
    697 		ctx:       ctx,
    698 		client:    c,
    699 		projectID: projectID,
    700 	}
    701 	it.pageInfo, it.nextFunc = iterator.NewPageInfo(
    702 		it.fetch,
    703 		func() int { return len(it.buckets) },
    704 		func() interface{} { b := it.buckets; it.buckets = nil; return b })
    705 	return it
    706 }
    707 
    708 // A BucketIterator is an iterator over BucketAttrs.
    709 type BucketIterator struct {
    710 	// Prefix restricts the iterator to buckets whose names begin with it.
    711 	Prefix string
    712 
    713 	ctx       context.Context
    714 	client    *Client
    715 	projectID string
    716 	buckets   []*BucketAttrs
    717 	pageInfo  *iterator.PageInfo
    718 	nextFunc  func() error
    719 }
    720 
    721 // Next returns the next result. Its second return value is iterator.Done if
    722 // there are no more results. Once Next returns iterator.Done, all subsequent
    723 // calls will return iterator.Done.
    724 func (it *BucketIterator) Next() (*BucketAttrs, error) {
    725 	if err := it.nextFunc(); err != nil {
    726 		return nil, err
    727 	}
    728 	b := it.buckets[0]
    729 	it.buckets = it.buckets[1:]
    730 	return b, nil
    731 }
    732 
    733 // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
    734 func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
    735 
    736 func (it *BucketIterator) fetch(pageSize int, pageToken string) (string, error) {
    737 	req := it.client.raw.Buckets.List(it.projectID)
    738 	setClientHeader(req.Header())
    739 	req.Projection("full")
    740 	req.Prefix(it.Prefix)
    741 	req.PageToken(pageToken)
    742 	if pageSize > 0 {
    743 		req.MaxResults(int64(pageSize))
    744 	}
    745 	var resp *raw.Buckets
    746 	var err error
    747 	err = runWithRetry(it.ctx, func() error {
    748 		resp, err = req.Context(it.ctx).Do()
    749 		return err
    750 	})
    751 	if err != nil {
    752 		return "", err
    753 	}
    754 	for _, item := range resp.Items {
    755 		it.buckets = append(it.buckets, newBucket(item))
    756 	}
    757 	return resp.NextPageToken, nil
    758 }
    759