2020-05-11 20:57:46 +02:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package uplink
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
|
|
|
"storj.io/common/errs2"
|
|
|
|
"storj.io/common/memory"
|
|
|
|
"storj.io/common/rpc/rpcstatus"
|
|
|
|
"storj.io/common/storj"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ErrBucketNameInvalid is returned when the bucket name is invalid.
|
|
|
|
var ErrBucketNameInvalid = errors.New("bucket name invalid")
|
|
|
|
|
|
|
|
// ErrBucketAlreadyExists is returned when the bucket already exists during creation.
|
|
|
|
var ErrBucketAlreadyExists = errors.New("bucket already exists")
|
|
|
|
|
|
|
|
// ErrBucketNotEmpty is returned when the bucket is not empty during deletion.
|
|
|
|
var ErrBucketNotEmpty = errors.New("bucket not empty")
|
|
|
|
|
|
|
|
// ErrBucketNotFound is returned when the bucket is not found.
|
|
|
|
var ErrBucketNotFound = errors.New("bucket not found")
|
|
|
|
|
|
|
|
// Bucket contains information about the bucket.
|
|
|
|
type Bucket struct {
|
|
|
|
Name string
|
|
|
|
Created time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatBucket returns information about a bucket.
|
|
|
|
func (project *Project) StatBucket(ctx context.Context, bucket string) (info *Bucket, err error) {
|
2020-05-29 15:08:11 +02:00
|
|
|
defer mon.Func().RestartTrace(&ctx)(&err)
|
2020-05-11 20:57:46 +02:00
|
|
|
|
|
|
|
b, err := project.project.GetBucket(ctx, bucket)
|
|
|
|
if err != nil {
|
2020-05-29 15:08:11 +02:00
|
|
|
return nil, convertKnownErrors(err, bucket, "")
|
2020-05-11 20:57:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &Bucket{
|
|
|
|
Name: b.Name,
|
|
|
|
Created: b.Created,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateBucket creates a new bucket.
|
|
|
|
//
|
|
|
|
// When bucket already exists it returns a valid Bucket and ErrBucketExists.
|
|
|
|
func (project *Project) CreateBucket(ctx context.Context, bucket string) (created *Bucket, err error) {
|
2020-05-29 15:08:11 +02:00
|
|
|
defer mon.Func().RestartTrace(&ctx)(&err)
|
2020-05-11 20:57:46 +02:00
|
|
|
|
|
|
|
// TODO remove bucket configuration when proper fix will be deployed on satellite
|
|
|
|
b, err := project.project.CreateBucket(ctx, bucket, &storj.Bucket{
|
|
|
|
PathCipher: storj.EncAESGCM,
|
|
|
|
DefaultRedundancyScheme: storj.RedundancyScheme{
|
|
|
|
Algorithm: storj.ReedSolomon,
|
|
|
|
ShareSize: 256 * memory.B.Int32(),
|
|
|
|
RequiredShares: 29,
|
|
|
|
RepairShares: 35,
|
|
|
|
OptimalShares: 80,
|
|
|
|
TotalShares: 110,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if storj.ErrNoBucket.Has(err) {
|
|
|
|
return nil, errwrapf("%w (%q)", ErrBucketNameInvalid, bucket)
|
|
|
|
}
|
|
|
|
if errs2.IsRPC(err, rpcstatus.AlreadyExists) {
|
|
|
|
// TODO: Ideally, the satellite should return the existing bucket when this error occurs.
|
|
|
|
existing, err := project.StatBucket(ctx, bucket)
|
|
|
|
if err != nil {
|
2020-05-29 15:08:11 +02:00
|
|
|
return existing, errs.Combine(errwrapf("%w (%q)", ErrBucketAlreadyExists, bucket), convertKnownErrors(err, bucket, ""))
|
2020-05-11 20:57:46 +02:00
|
|
|
}
|
|
|
|
return existing, errwrapf("%w (%q)", ErrBucketAlreadyExists, bucket)
|
|
|
|
}
|
2020-05-29 15:08:11 +02:00
|
|
|
return nil, convertKnownErrors(err, bucket, "")
|
2020-05-11 20:57:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &Bucket{
|
|
|
|
Name: b.Name,
|
|
|
|
Created: b.Created,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnsureBucket ensures that a bucket exists or creates a new one.
|
|
|
|
//
|
|
|
|
// When bucket already exists it returns a valid Bucket and no error.
|
|
|
|
func (project *Project) EnsureBucket(ctx context.Context, bucket string) (ensured *Bucket, err error) {
|
2020-05-29 15:08:11 +02:00
|
|
|
defer mon.Func().RestartTrace(&ctx)(&err)
|
2020-05-11 20:57:46 +02:00
|
|
|
|
|
|
|
ensured, err = project.CreateBucket(ctx, bucket)
|
|
|
|
if err != nil && !errors.Is(err, ErrBucketAlreadyExists) {
|
2020-05-29 15:08:11 +02:00
|
|
|
return nil, convertKnownErrors(err, bucket, "")
|
2020-05-11 20:57:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ensured, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBucket deletes a bucket.
|
|
|
|
//
|
|
|
|
// When bucket is not empty it returns ErrBucketNotEmpty.
|
|
|
|
func (project *Project) DeleteBucket(ctx context.Context, bucket string) (deleted *Bucket, err error) {
|
2020-05-29 15:08:11 +02:00
|
|
|
defer mon.Func().RestartTrace(&ctx)(&err)
|
2020-05-11 20:57:46 +02:00
|
|
|
|
|
|
|
existing, err := project.project.DeleteBucket(ctx, bucket)
|
|
|
|
if err != nil {
|
|
|
|
if errs2.IsRPC(err, rpcstatus.FailedPrecondition) {
|
|
|
|
return nil, errwrapf("%w (%q)", ErrBucketNotEmpty, bucket)
|
|
|
|
}
|
2020-05-29 15:08:11 +02:00
|
|
|
return nil, convertKnownErrors(err, bucket, "")
|
2020-05-11 20:57:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if existing == (storj.Bucket{}) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Bucket{
|
|
|
|
Name: existing.Name,
|
|
|
|
Created: existing.Created,
|
|
|
|
}, nil
|
|
|
|
}
|