Skip to content

Commit

Permalink
Add Bucket APIs with context support (minio#1192)
Browse files Browse the repository at this point in the history
  • Loading branch information
dvaldivia authored and kannappanr committed Dec 5, 2019
1 parent 351efed commit 8e39284
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 27 deletions.
17 changes: 16 additions & 1 deletion api-list.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,23 @@ import (
// }
//
func (c Client) ListBuckets() ([]BucketInfo, error) {
return c.ListBucketsWithContext(context.Background())
}

// ListBucketsWithContext list all buckets owned by this authenticated user,
// accepts a context for facilitate cancellation.
//
// This call requires explicit authentication, no anonymous requests are
// allowed for listing buckets.
//
// api := client.New(....)
// for message := range api.ListBucketsWithContext(context.Background()) {
// fmt.Println(message)
// }
//
func (c Client) ListBucketsWithContext(ctx context.Context) ([]BucketInfo, error) {
// Execute GET on service.
resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{contentSHA256Hex: emptySHA256Hex})
resp, err := c.executeMethod(ctx, "GET", requestMetadata{contentSHA256Hex: emptySHA256Hex})
defer closeResponse(resp)
if err != nil {
return nil, err
Expand Down
91 changes: 70 additions & 21 deletions api-put-bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

/// Bucket operations

func (c Client) makeBucket(bucketName string, location string, objectLockEnabled bool) (err error) {
func (c Client) makeBucket(ctx context.Context, bucketName string, location string, objectLockEnabled bool) (err error) {
defer func() {
// Save the location into cache on a successful makeBucket response.
if err == nil {
Expand Down Expand Up @@ -81,7 +81,7 @@ func (c Client) makeBucket(bucketName string, location string, objectLockEnabled
}

// Execute PUT to create a new bucket.
resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
resp, err := c.executeMethod(ctx, "PUT", reqMetadata)
defer closeResponse(resp)
if err != nil {
return err
Expand All @@ -105,7 +105,18 @@ func (c Client) makeBucket(bucketName string, location string, objectLockEnabled
// For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
// For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
func (c Client) MakeBucket(bucketName string, location string) (err error) {
return c.makeBucket(bucketName, location, false)
return c.MakeBucketWithContext(context.Background(), bucketName, location)
}

// MakeBucketWithContext creates a new bucket with bucketName with a context to control cancellations and timeouts.
//
// Location is an optional argument, by default all buckets are
// created in US Standard Region.
//
// For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
// For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
func (c Client) MakeBucketWithContext(ctx context.Context, bucketName string, location string) (err error) {
return c.makeBucket(ctx, bucketName, location, false)
}

// MakeBucketWithObjectLock creates a object lock enabled new bucket with bucketName.
Expand All @@ -116,27 +127,44 @@ func (c Client) MakeBucket(bucketName string, location string) (err error) {
// For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
// For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
func (c Client) MakeBucketWithObjectLock(bucketName string, location string) (err error) {
return c.makeBucket(bucketName, location, true)
return c.MakeBucketWithObjectLockWithContext(context.Background(), bucketName, location)
}

// MakeBucketWithObjectLockWithContext creates a object lock enabled new bucket with bucketName with a context to
// control cancellations and timeouts.
//
// Location is an optional argument, by default all buckets are
// created in US Standard Region.
//
// For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
// For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
func (c Client) MakeBucketWithObjectLockWithContext(ctx context.Context, bucketName string, location string) (err error) {
return c.makeBucket(ctx, bucketName, location, true)
}

// SetBucketPolicy set the access permissions on an existing bucket.
func (c Client) SetBucketPolicy(bucketName, policy string) error {
return c.SetBucketPolicyWithContext(context.Background(), bucketName, policy)
}

// SetBucketPolicyWithContext set the access permissions on an existing bucket.
func (c Client) SetBucketPolicyWithContext(ctx context.Context, bucketName, policy string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
}

// If policy is empty then delete the bucket policy.
if policy == "" {
return c.removeBucketPolicy(bucketName)
return c.removeBucketPolicy(ctx, bucketName)
}

// Save the updated policies.
return c.putBucketPolicy(bucketName, policy)
return c.putBucketPolicy(ctx, bucketName, policy)
}

// Saves a new bucket policy.
func (c Client) putBucketPolicy(bucketName, policy string) error {
func (c Client) putBucketPolicy(ctx context.Context, bucketName, policy string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -162,7 +190,7 @@ func (c Client) putBucketPolicy(bucketName, policy string) error {
}

// Execute PUT to upload a new bucket policy.
resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
resp, err := c.executeMethod(ctx, "PUT", reqMetadata)
defer closeResponse(resp)
if err != nil {
return err
Expand All @@ -176,7 +204,7 @@ func (c Client) putBucketPolicy(bucketName, policy string) error {
}

// Removes all policies on a bucket.
func (c Client) removeBucketPolicy(bucketName string) error {
func (c Client) removeBucketPolicy(ctx context.Context, bucketName string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -187,7 +215,7 @@ func (c Client) removeBucketPolicy(bucketName string) error {
urlValues.Set("policy", "")

// Execute DELETE on objectName.
resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
resp, err := c.executeMethod(ctx, "DELETE", requestMetadata{
bucketName: bucketName,
queryValues: urlValues,
contentSHA256Hex: emptySHA256Hex,
Expand All @@ -201,22 +229,27 @@ func (c Client) removeBucketPolicy(bucketName string) error {

// SetBucketLifecycle set the lifecycle on an existing bucket.
func (c Client) SetBucketLifecycle(bucketName, lifecycle string) error {
return c.SetBucketLifecycleWithContext(context.Background(), bucketName, lifecycle)
}

// SetBucketLifecycleWithContext set the lifecycle on an existing bucket with a context to control cancellations and timeouts.
func (c Client) SetBucketLifecycleWithContext(ctx context.Context, bucketName, lifecycle string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
}

// If lifecycle is empty then delete it.
if lifecycle == "" {
return c.removeBucketLifecycle(bucketName)
return c.removeBucketLifecycle(ctx, bucketName)
}

// Save the updated lifecycle.
return c.putBucketLifecycle(bucketName, lifecycle)
return c.putBucketLifecycle(ctx, bucketName, lifecycle)
}

// Saves a new bucket lifecycle.
func (c Client) putBucketLifecycle(bucketName, lifecycle string) error {
func (c Client) putBucketLifecycle(ctx context.Context, bucketName, lifecycle string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -243,7 +276,7 @@ func (c Client) putBucketLifecycle(bucketName, lifecycle string) error {
}

// Execute PUT to upload a new bucket lifecycle.
resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
resp, err := c.executeMethod(ctx, "PUT", reqMetadata)
defer closeResponse(resp)
if err != nil {
return err
Expand All @@ -257,7 +290,7 @@ func (c Client) putBucketLifecycle(bucketName, lifecycle string) error {
}

// Remove lifecycle from a bucket.
func (c Client) removeBucketLifecycle(bucketName string) error {
func (c Client) removeBucketLifecycle(ctx context.Context, bucketName string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -268,7 +301,7 @@ func (c Client) removeBucketLifecycle(bucketName string) error {
urlValues.Set("lifecycle", "")

// Execute DELETE on objectName.
resp, err := c.executeMethod(context.Background(), "DELETE", requestMetadata{
resp, err := c.executeMethod(ctx, "DELETE", requestMetadata{
bucketName: bucketName,
queryValues: urlValues,
contentSHA256Hex: emptySHA256Hex,
Expand All @@ -282,6 +315,12 @@ func (c Client) removeBucketLifecycle(bucketName string) error {

// SetBucketNotification saves a new bucket notification.
func (c Client) SetBucketNotification(bucketName string, bucketNotification BucketNotification) error {
return c.SetBucketNotificationWithContext(context.Background(), bucketName, bucketNotification)
}

// SetBucketNotificationWithContext saves a new bucket notification with a context to control cancellations
// and timeouts.
func (c Client) SetBucketNotificationWithContext(ctx context.Context, bucketName string, bucketNotification BucketNotification) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -308,7 +347,7 @@ func (c Client) SetBucketNotification(bucketName string, bucketNotification Buck
}

// Execute PUT to upload a new bucket notification.
resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
resp, err := c.executeMethod(ctx, "PUT", reqMetadata)
defer closeResponse(resp)
if err != nil {
return err
Expand Down Expand Up @@ -338,7 +377,7 @@ var (
versionDisableConfigSHA256 = sum256Hex(versionDisableConfig)
)

func (c Client) setVersioning(bucketName string, config []byte, length int64, md5sum, sha256sum string) error {
func (c Client) setVersioning(ctx context.Context, bucketName string, config []byte, length int64, md5sum, sha256sum string) error {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return err
Expand All @@ -359,7 +398,7 @@ func (c Client) setVersioning(bucketName string, config []byte, length int64, md
}

// Execute PUT to set a bucket versioning.
resp, err := c.executeMethod(context.Background(), "PUT", reqMetadata)
resp, err := c.executeMethod(ctx, "PUT", reqMetadata)
defer closeResponse(resp)
if err != nil {
return err
Expand All @@ -374,10 +413,20 @@ func (c Client) setVersioning(bucketName string, config []byte, length int64, md

// EnableVersioning - Enable object versioning in given bucket.
func (c Client) EnableVersioning(bucketName string) error {
return c.setVersioning(bucketName, versionEnableConfig, versionEnableConfigLen, versionEnableConfigMD5Sum, versionEnableConfigSHA256)
return c.EnableVersioningWithContext(context.Background(), bucketName)
}

// EnableVersioningWithContext - Enable object versioning in given bucket with a context to control cancellations and timeouts.
func (c Client) EnableVersioningWithContext(ctx context.Context, bucketName string) error {
return c.setVersioning(ctx, bucketName, versionEnableConfig, versionEnableConfigLen, versionEnableConfigMD5Sum, versionEnableConfigSHA256)
}

// DisableVersioning - Disable object versioning in given bucket.
func (c Client) DisableVersioning(bucketName string) error {
return c.setVersioning(bucketName, versionDisableConfig, versionDisableConfigLen, versionDisableConfigMD5Sum, versionDisableConfigSHA256)
return c.DisableVersioningWithContext(context.Background(), bucketName)
}

// DisableVersioningWithContext - Disable object versioning in given bucket with a context to control cancellations and timeouts.
func (c Client) DisableVersioningWithContext(ctx context.Context, bucketName string) error {
return c.setVersioning(ctx, bucketName, versionDisableConfig, versionDisableConfigLen, versionDisableConfigMD5Sum, versionDisableConfigSHA256)
}
16 changes: 14 additions & 2 deletions api-stat.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ import (

// BucketExists verify if bucket exists and you have permission to access it.
func (c Client) BucketExists(bucketName string) (bool, error) {
return c.BucketExistsWithContext(context.Background(), bucketName)
}

// BucketExistsWithContext verify if bucket exists and you have permission to access it. Allows for a Context to
// control cancellations and timeouts.
func (c Client) BucketExistsWithContext(ctx context.Context, bucketName string) (bool, error) {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return false, err
}

// Execute HEAD on bucketName.
resp, err := c.executeMethod(context.Background(), "HEAD", requestMetadata{
resp, err := c.executeMethod(ctx, "HEAD", requestMetadata{
bucketName: bucketName,
contentSHA256Hex: emptySHA256Hex,
})
Expand Down Expand Up @@ -91,14 +97,20 @@ func extractObjMetadata(header http.Header) http.Header {

// StatObject verifies if object exists and you have permission to access.
func (c Client) StatObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
return c.StatObjectWithContext(context.Background(), bucketName, objectName, opts)
}

// StatObjectWithContext verifies if object exists and you have permission to access with a context to control
// cancellations and timeouts.
func (c Client) StatObjectWithContext(ctx context.Context, bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return ObjectInfo{}, err
}
if err := s3utils.CheckValidObjectName(objectName); err != nil {
return ObjectInfo{}, err
}
return c.statObject(context.Background(), bucketName, objectName, opts)
return c.statObject(ctx, bucketName, objectName, opts)
}

// Lower level API for statObject supporting pre-conditions and range headers.
Expand Down
8 changes: 7 additions & 1 deletion core.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,13 @@ func (c Core) GetBucketPolicy(bucket string) (string, error) {

// PutBucketPolicy - applies a new bucket access policy for a given bucket.
func (c Core) PutBucketPolicy(bucket, bucketPolicy string) error {
return c.putBucketPolicy(bucket, bucketPolicy)
return c.PutBucketPolicyWithContext(context.Background(), bucket, bucketPolicy)
}

// PutBucketPolicyWithContext - applies a new bucket access policy for a given bucket with a context to control
// cancellations and timeouts.
func (c Core) PutBucketPolicyWithContext(ctx context.Context, bucket, bucketPolicy string) error {
return c.putBucketPolicy(ctx, bucket, bucketPolicy)
}

// GetObjectWithContext is a lower level API implemented to support reading
Expand Down
45 changes: 43 additions & 2 deletions functional_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (
"strings"
"time"

humanize "github.com/dustin/go-humanize"
"github.com/dustin/go-humanize"
log "github.com/sirupsen/logrus"

"github.com/minio/minio-go/v6"
Expand Down Expand Up @@ -4207,6 +4207,23 @@ func testFunctional() {
return
}

// Verify if bucket exits and you have access with context.
function = "BucketExistsWithContext(ctx, bucketName)"
functionAll += ", " + function
args = map[string]interface{}{
"bucketName": bucketName,
}
exists, err = c.BucketExistsWithContext(context.Background(), bucketName)

if err != nil {
logError(testName, function, args, startTime, "", "BucketExistsWithContext failed", err)
return
}
if !exists {
logError(testName, function, args, startTime, "", "Could not find the bucket", err)
return
}

// Asserting the default bucket policy.
function = "GetBucketPolicy(bucketName)"
functionAll += ", " + function
Expand Down Expand Up @@ -4321,6 +4338,21 @@ func testFunctional() {
return
}

// List all buckets with context.
function = "ListBucketsWithContext()"
functionAll += ", " + function
args = nil
buckets, err = c.ListBucketsWithContext(context.Background())

if len(buckets) == 0 {
logError(testName, function, args, startTime, "", "Found bucket list to be empty", err)
return
}
if err != nil {
logError(testName, function, args, startTime, "", "ListBucketsWithContext failed", err)
return
}

// Verify if previously created bucket is listed in list buckets.
bucketFound := false
for _, bucket := range buckets {
Expand Down Expand Up @@ -5287,6 +5319,16 @@ func testFPutObjectV2() {
return
}

rGTar, err = c.StatObjectWithContext(context.Background(), bucketName, objectName+"-GTar", minio.StatObjectOptions{})
if err != nil {
logError(testName, function, args, startTime, "", "StatObject failed", err)
return
}
if rGTar.ContentType != "application/x-gtar" && rGTar.ContentType != "application/octet-stream" {
logError(testName, function, args, startTime, "", "Content-Type headers mismatched, expected: application/x-gtar , got "+rGTar.ContentType, err)
return
}

// Delete all objects and buckets
if err = cleanupBucket(bucketName, c); err != nil {
logError(testName, function, args, startTime, "", "Cleanup failed", err)
Expand Down Expand Up @@ -10025,7 +10067,6 @@ func testListObjects() {
name string
storageClass string
}{

// \x17 is a forbidden character in a xml document
{"foo\x17bar", "STANDARD"},
// Special characters
Expand Down

0 comments on commit 8e39284

Please sign in to comment.