storj.io/uplink@v1.13.0/private/metaclient/metainfo.go (about)

     1  // Copyright (C) 2019 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package metaclient
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/zeebo/errs"
    10  
    11  	"storj.io/common/encryption"
    12  	"storj.io/common/storj"
    13  )
    14  
    15  var errClass = errs.Class("metainfo")
    16  
    17  // DB implements metainfo database.
    18  type DB struct {
    19  	metainfo             *Client
    20  	encryptionParameters storj.EncryptionParameters
    21  
    22  	encStore *encryption.Store
    23  }
    24  
    25  // New creates a new metainfo database.
    26  func New(metainfo *Client, encryptionParameters storj.EncryptionParameters, encStore *encryption.Store) *DB {
    27  	return &DB{
    28  		metainfo:             metainfo,
    29  		encryptionParameters: encryptionParameters,
    30  		encStore:             encStore,
    31  	}
    32  }
    33  
    34  // Close closes the underlying resources passed to the metainfo DB.
    35  func (db *DB) Close() error {
    36  	return db.metainfo.Close()
    37  }
    38  
    39  // CreateBucket creates a new bucket with the specified information.
    40  func (db *DB) CreateBucket(ctx context.Context, bucketName string) (newBucket Bucket, err error) {
    41  	defer mon.Task()(&ctx)(&err)
    42  
    43  	if bucketName == "" {
    44  		return Bucket{}, ErrNoBucket.New("")
    45  	}
    46  
    47  	newBucket, err = db.metainfo.CreateBucket(ctx, CreateBucketParams{
    48  		Name: []byte(bucketName),
    49  	})
    50  	return newBucket, ErrBucket.Wrap(err)
    51  }
    52  
    53  // DeleteBucket deletes bucket.
    54  func (db *DB) DeleteBucket(ctx context.Context, bucketName string, deleteAll bool) (bucket Bucket, err error) {
    55  	defer mon.Task()(&ctx)(&err)
    56  
    57  	if bucketName == "" {
    58  		return Bucket{}, ErrNoBucket.New("")
    59  	}
    60  
    61  	bucket, err = db.metainfo.DeleteBucket(ctx, DeleteBucketParams{
    62  		Name:      []byte(bucketName),
    63  		DeleteAll: deleteAll,
    64  	})
    65  	return bucket, ErrBucket.Wrap(err)
    66  }
    67  
    68  // GetBucket gets bucket information.
    69  func (db *DB) GetBucket(ctx context.Context, bucketName string) (bucket Bucket, err error) {
    70  	defer mon.Task()(&ctx)(&err)
    71  
    72  	if bucketName == "" {
    73  		return Bucket{}, ErrNoBucket.New("")
    74  	}
    75  
    76  	bucket, err = db.metainfo.GetBucket(ctx, GetBucketParams{
    77  		Name: []byte(bucketName),
    78  	})
    79  	return bucket, ErrBucket.Wrap(err)
    80  }
    81  
    82  // ListBuckets lists buckets.
    83  func (db *DB) ListBuckets(ctx context.Context, options BucketListOptions) (bucketList BucketList, err error) {
    84  	defer mon.Task()(&ctx)(&err)
    85  
    86  	bucketList, err = db.metainfo.ListBuckets(ctx, ListBucketsParams{
    87  		ListOpts: options,
    88  	})
    89  	return bucketList, ErrBucket.Wrap(err)
    90  }
    91  
    92  // IterateBucketsOptions buckets iterator options.
    93  type IterateBucketsOptions struct {
    94  	Cursor string
    95  	Limit  int
    96  
    97  	DialClientFunc func() (*Client, error)
    98  }
    99  
   100  // IterateBuckets returns iterator to go over buckets.
   101  func IterateBuckets(ctx context.Context, options IterateBucketsOptions) *BucketIterator {
   102  	defer mon.Task()(&ctx)(nil)
   103  
   104  	opts := BucketListOptions{
   105  		Direction: After,
   106  		Cursor:    options.Cursor,
   107  	}
   108  
   109  	buckets := BucketIterator{
   110  		ctx:            ctx,
   111  		dialClientFunc: options.DialClientFunc,
   112  		options:        opts,
   113  	}
   114  
   115  	return &buckets
   116  }
   117  
   118  // BucketIterator is an iterator over a collection of buckets.
   119  type BucketIterator struct {
   120  	ctx            context.Context
   121  	dialClientFunc func() (*Client, error)
   122  	options        BucketListOptions
   123  	list           *BucketList
   124  	position       int
   125  	completed      bool
   126  	err            error
   127  }
   128  
   129  // Next prepares next Bucket for reading.
   130  // It returns false if the end of the iteration is reached and there are no more buckets, or if there is an error.
   131  func (buckets *BucketIterator) Next() bool {
   132  	if buckets.err != nil {
   133  		buckets.completed = true
   134  		return false
   135  	}
   136  
   137  	if buckets.list == nil {
   138  		more := buckets.loadNext()
   139  		buckets.completed = !more
   140  		return more
   141  	}
   142  
   143  	if buckets.position >= len(buckets.list.Items)-1 {
   144  		if !buckets.list.More {
   145  			buckets.completed = true
   146  			return false
   147  		}
   148  		more := buckets.loadNext()
   149  		buckets.completed = !more
   150  		return more
   151  	}
   152  
   153  	buckets.position++
   154  
   155  	return true
   156  }
   157  
   158  func (buckets *BucketIterator) loadNext() bool {
   159  	ok, err := buckets.tryLoadNext()
   160  	if err != nil {
   161  		buckets.err = err
   162  		return false
   163  	}
   164  	return ok
   165  }
   166  
   167  func (buckets *BucketIterator) tryLoadNext() (ok bool, err error) {
   168  	client, err := buckets.dialClientFunc()
   169  	if err != nil {
   170  		return false, err
   171  	}
   172  	defer func() { err = errs.Combine(err, client.Close()) }()
   173  
   174  	list, err := client.ListBuckets(buckets.ctx, ListBucketsParams{
   175  		ListOpts: buckets.options,
   176  	})
   177  	if err != nil {
   178  		return false, err
   179  	}
   180  	buckets.list = &list
   181  	if list.More {
   182  		buckets.options = buckets.options.NextPage(list)
   183  	}
   184  	buckets.position = 0
   185  	return len(list.Items) > 0, nil
   186  }
   187  
   188  // Err returns error, if one happened during iteration.
   189  func (buckets *BucketIterator) Err() error {
   190  	return buckets.err
   191  }
   192  
   193  // Item returns the current bucket in the iterator.
   194  func (buckets *BucketIterator) Item() *Bucket {
   195  	item := buckets.item()
   196  	if item == nil {
   197  		return nil
   198  	}
   199  	return &Bucket{
   200  		Name:        item.Name,
   201  		Created:     item.Created,
   202  		Attribution: item.Attribution,
   203  	}
   204  }
   205  
   206  func (buckets *BucketIterator) item() *Bucket {
   207  	if buckets.completed {
   208  		return nil
   209  	}
   210  
   211  	if buckets.err != nil {
   212  		return nil
   213  	}
   214  
   215  	if buckets.list == nil {
   216  		return nil
   217  	}
   218  
   219  	if len(buckets.list.Items) == 0 {
   220  		return nil
   221  	}
   222  
   223  	return &buckets.list.Items[buckets.position]
   224  }