Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RemoveObjectsWithContext call #939

Merged
merged 3 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions api-remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,8 @@ func processRemoveMultiObjectsResponse(body io.Reader, objects []string, errorCh
}
}

// RemoveObjects remove multiples objects from a bucket.
// The list of objects to remove are received from objectsCh.
// Remove failures are sent back via error channel.
func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan RemoveObjectError {
// RemoveObjectsWithContext - Identical to RemoveObjects call, but accepts context to facilitate request cancellation.
func (c Client) RemoveObjectsWithContext(ctx context.Context, bucketName string, objectsCh <-chan string) <-chan RemoveObjectError {
errorCh := make(chan RemoveObjectError, 1)

// Validate if bucket name is valid.
Expand Down Expand Up @@ -189,7 +187,7 @@ func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan
// Generate remove multi objects XML request
removeBytes := generateRemoveMultiObjectsRequest(batch)
// Execute GET on bucket to list objects.
resp, err := c.executeMethod(context.Background(), "POST", requestMetadata{
resp, err := c.executeMethod(ctx, "POST", requestMetadata{
bucketName: bucketName,
queryValues: urlValues,
contentBody: bytes.NewReader(removeBytes),
Expand All @@ -213,6 +211,13 @@ func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan
return errorCh
}

// RemoveObjects removes multiple objects from a bucket.
// The list of objects to remove are received from objectsCh.
// Remove failures are sent back via error channel.
func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan RemoveObjectError {
return c.RemoveObjectsWithContext(context.Background(), bucketName, objectsCh)
}

// RemoveIncompleteUpload aborts an partially uploaded object.
func (c Client) RemoveIncompleteUpload(bucketName, objectName string) error {
// Input validation.
Expand Down
42 changes: 42 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func main() {
| | [`GetObjectWithContext`](#GetObjectWithContext) | | | |
| | [`FPutObjectWithContext`](#FPutObjectWithContext) | | | |
| | [`FGetObjectWithContext`](#FGetObjectWithContext) | | | |
| | [`RemoveObjectsWithContext`](#RemoveObjectsWithContext) | | | |
## 1. Constructor
<a name="Minio"></a>

Expand Down Expand Up @@ -1062,6 +1063,47 @@ for rErr := range minioClient.RemoveObjects("mybucket", objectsCh) {
}
```

<a name="RemoveObjectsWithContext"></a>
### RemoveObjectsWithContext(ctx context.Context, bucketName string, objectsCh chan string) (errorCh <-chan RemoveObjectError)
*Identical to RemoveObjects operation, but accepts a context for request cancellation.*

Parameters

|Param |Type |Description |
|:---|:---| :---|
|`ctx` | _context.Context_ |Request context |
|`bucketName` | _string_ |Name of the bucket |
|`objectsCh` | _chan string_ | Channel of objects to be removed |


__Return Values__

|Param |Type |Description |
|:---|:---| :---|
|`errorCh` | _<-chan minio.RemoveObjectError_ | Receive-only channel of errors observed during deletion. |

```go
objectsCh := make(chan string)
ctx, cancel := context.WithTimeout(context.Background(), 100 * time.Second)
defer cancel()

// Send object names that are needed to be removed to objectsCh
go func() {
defer close(objectsCh)
// List all objects from a bucket-name with a matching prefix.
for object := range minioClient.ListObjects("my-bucketname", "my-prefixname", true, nil) {
if object.Err != nil {
log.Fatalln(object.Err)
}
objectsCh <- object.Key
}
}()

for rErr := range minioClient.RemoveObjects(ctx, "my-bucketname", objectsCh) {
fmt.Println("Error detected during deletion: ", rErr)
}
```

<a name="RemoveIncompleteUpload"></a>
### RemoveIncompleteUpload(bucketName, objectName string) error
Removes a partially uploaded object.
Expand Down
95 changes: 95 additions & 0 deletions functional_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,101 @@ func testGetObjectClosedTwice() {
successLogger(testName, function, args, startTime).Info()
}

// Test RemoveObjectsWithContext request context cancels after timeout
func testRemoveObjectsWithContext() {
// Initialize logging params.
startTime := time.Now()
testName := getFuncName()
function := "RemoveObjectsWithContext(ctx, bucketName, objectsCh)"
args := map[string]interface{}{
"bucketName": "",
}

// Seed random based on current tie.
rand.Seed(time.Now().Unix())

// Instantiate new minio client.
c, err := minio.New(
os.Getenv(serverEndpoint),
os.Getenv(accessKey),
os.Getenv(secretKey),
mustParseBool(os.Getenv(enableHTTPS)),
)
if err != nil {
logError(testName, function, args, startTime, "", "Minio client object creation failed", err)
return
}

// Set user agent.
c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
// Enable tracing, write to stdout.
// c.TraceOn(os.Stderr)

// Generate a new random bucket name.
bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
args["bucketName"] = bucketName

// Make a new bucket.
err = c.MakeBucket(bucketName, "us-east-1")
if err != nil {
logError(testName, function, args, startTime, "", "MakeBucket failed", err)
}

// Generate put data.
r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))

// Multi remove of 20 objects.
nrObjects := 20
objectsCh := make(chan string)
go func() {
defer close(objectsCh)
for i := 0; i < nrObjects; i++ {
objectName := "sample" + strconv.Itoa(i) + ".txt"
_, err = c.PutObject(bucketName, objectName, r, 8, minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
logError(testName, function, args, startTime, "", "PutObject failed", err)
continue
}
objectsCh <- objectName
}
}()
// Set context to cancel in 1 nanosecond.
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
args["ctx"] = ctx
defer cancel()

// Call RemoveObjectsWithContext API with short timeout.
errorCh := c.RemoveObjectsWithContext(ctx, bucketName, objectsCh)
// Check for error.
select {
case r := <-errorCh:
if r.Err == nil {
logError(testName, function, args, startTime, "", "RemoveObjectsWithContext should fail on short timeout", err)
return
}
}
// Set context with longer timeout.
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
args["ctx"] = ctx
defer cancel()
// Perform RemoveObjectsWithContext with the longer timeout. Expect the removals to succeed.
errorCh = c.RemoveObjectsWithContext(ctx, bucketName, objectsCh)
select {
case r, more := <-errorCh:
if more || r.Err != nil {
logError(testName, function, args, startTime, "", "Unexpected error", r.Err)
return
}
}

// Delete all objects and buckets.
if err = cleanupBucket(bucketName, c); err != nil {
logError(testName, function, args, startTime, "", "Cleanup failed", err)
return
}
successLogger(testName, function, args, startTime).Info()
}

// Test removing multiple objects with Remove API
func testRemoveMultipleObjects() {
// initialize logging params
Expand Down