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 }