HDDS-902. MultipartUpload: S3 API for uploading a part file. Contributed by Bharat Viswanadham.
This commit is contained in:
parent
992dd9d189
commit
d66925a315
@ -21,22 +21,51 @@ Resource ../commonlib.robot
|
|||||||
Resource commonawslib.robot
|
Resource commonawslib.robot
|
||||||
Test Setup Setup s3 tests
|
Test Setup Setup s3 tests
|
||||||
|
|
||||||
|
*** Keywords ***
|
||||||
|
Create Random file for mac
|
||||||
|
Execute dd if=/dev/urandom of=/tmp/part1 bs=1m count=5
|
||||||
|
|
||||||
|
Create Random file for linux
|
||||||
|
Execute dd if=/dev/urandom of=/tmp/part1 bs=1M count=5
|
||||||
|
|
||||||
|
|
||||||
*** Variables ***
|
*** Variables ***
|
||||||
${ENDPOINT_URL} http://s3g:9878
|
${ENDPOINT_URL} http://s3g:9878
|
||||||
${BUCKET} generated
|
${BUCKET} generated
|
||||||
|
|
||||||
*** Test Cases ***
|
*** Test Cases ***
|
||||||
|
|
||||||
Initiate Multipart Upload
|
Test Multipart Upload
|
||||||
${result} = Execute AWSS3APICli create-multipart-upload --bucket ${BUCKET} --key multipartKey
|
${result} = Execute AWSS3APICli create-multipart-upload --bucket ${BUCKET} --key multipartKey --storage-class REDUCED_REDUNDANCY
|
||||||
${uploadID} = Execute and checkrc echo '${result}' | jq -r '.UploadId' 0
|
${uploadID} = Execute and checkrc echo '${result}' | jq -r '.UploadId' 0
|
||||||
Should contain ${result} ${BUCKET}
|
Should contain ${result} ${BUCKET}
|
||||||
Should contain ${result} multipartKey
|
Should contain ${result} multipartKey
|
||||||
Should contain ${result} UploadId
|
Should contain ${result} UploadId
|
||||||
# initiate again
|
# initiate again
|
||||||
${result} = Execute AWSS3APICli create-multipart-upload --bucket ${BUCKET} --key multipartKey
|
${result} = Execute AWSS3APICli create-multipart-upload --bucket ${BUCKET} --key multipartKey --storage-class REDUCED_REDUNDANCY
|
||||||
${nextUploadID} = Execute and checkrc echo '${result}' | jq -r '.UploadId' 0
|
${nextUploadID} = Execute and checkrc echo '${result}' | jq -r '.UploadId' 0
|
||||||
Should contain ${result} ${BUCKET}
|
Should contain ${result} ${BUCKET}
|
||||||
Should contain ${result} multipartKey
|
Should contain ${result} multipartKey
|
||||||
Should contain ${result} UploadId
|
Should contain ${result} UploadId
|
||||||
Should Not Be Equal ${uploadID} ${nextUploadID}
|
Should Not Be Equal ${uploadID} ${nextUploadID}
|
||||||
|
|
||||||
|
# upload part
|
||||||
|
# each part should be minimum 5mb, other wise during complete multipart
|
||||||
|
# upload we get error entity too small. So, considering further complete
|
||||||
|
# multipart upload, uploading each part as 5MB file, exception is for last part
|
||||||
|
|
||||||
|
${system} = Evaluate platform.system() platform
|
||||||
|
Run Keyword if '${system}' == 'Darwin' Create Random file for mac
|
||||||
|
Run Keyword if '${system}' == 'Linux' Create Random file for linux
|
||||||
|
${result} = Execute AWSS3APICli upload-part --bucket ${BUCKET} --key multipartKey --part-number 1 --body /tmp/part1 --upload-id ${nextUploadID}
|
||||||
|
Should contain ${result} ETag
|
||||||
|
# override part
|
||||||
|
Run Keyword if '${system}' == 'Darwin' Create Random file for mac
|
||||||
|
Run Keyword if '${system}' == 'Linux' Create Random file for linux
|
||||||
|
${result} = Execute AWSS3APICli upload-part --bucket ${BUCKET} --key multipartKey --part-number 1 --body /tmp/part1 --upload-id ${nextUploadID}
|
||||||
|
Should contain ${result} ETag
|
||||||
|
|
||||||
|
Upload part with Incorrect uploadID
|
||||||
|
Execute echo "Multipart upload" > /tmp/testfile
|
||||||
|
${result} = Execute AWSS3APICli and checkrc upload-part --bucket ${BUCKET} --key multipartKey --part-number 1 --body /tmp/testfile --upload-id "random" 255
|
||||||
|
Should contain ${result} NoSuchUpload
|
@ -18,6 +18,7 @@
|
|||||||
package org.apache.hadoop.ozone.s3.endpoint;
|
package org.apache.hadoop.ozone.s3.endpoint;
|
||||||
|
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.HEAD;
|
import javax.ws.rs.HEAD;
|
||||||
import javax.ws.rs.HeaderParam;
|
import javax.ws.rs.HeaderParam;
|
||||||
@ -48,6 +49,7 @@
|
|||||||
import org.apache.hadoop.ozone.client.OzoneKeyDetails;
|
import org.apache.hadoop.ozone.client.OzoneKeyDetails;
|
||||||
import org.apache.hadoop.ozone.client.io.OzoneInputStream;
|
import org.apache.hadoop.ozone.client.io.OzoneInputStream;
|
||||||
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
|
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmMultipartCommitUploadPartInfo;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
|
import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
|
||||||
import org.apache.hadoop.ozone.s3.SignedChunksInputStream;
|
import org.apache.hadoop.ozone.s3.SignedChunksInputStream;
|
||||||
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
|
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
|
||||||
@ -64,6 +66,8 @@
|
|||||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
||||||
import static javax.ws.rs.core.HttpHeaders.LAST_MODIFIED;
|
import static javax.ws.rs.core.HttpHeaders.LAST_MODIFIED;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NO_SUCH_UPLOAD;
|
||||||
import static org.apache.hadoop.ozone.s3.util.S3Consts.ACCEPT_RANGE_HEADER;
|
import static org.apache.hadoop.ozone.s3.util.S3Consts.ACCEPT_RANGE_HEADER;
|
||||||
import static org.apache.hadoop.ozone.s3.util.S3Consts.CONTENT_RANGE_HEADER;
|
import static org.apache.hadoop.ozone.s3.util.S3Consts.CONTENT_RANGE_HEADER;
|
||||||
import static org.apache.hadoop.ozone.s3.util.S3Consts.COPY_SOURCE_HEADER;
|
import static org.apache.hadoop.ozone.s3.util.S3Consts.COPY_SOURCE_HEADER;
|
||||||
@ -108,9 +112,18 @@ public Response put(
|
|||||||
@PathParam("bucket") String bucketName,
|
@PathParam("bucket") String bucketName,
|
||||||
@PathParam("path") String keyPath,
|
@PathParam("path") String keyPath,
|
||||||
@HeaderParam("Content-Length") long length,
|
@HeaderParam("Content-Length") long length,
|
||||||
|
@QueryParam("partNumber") int partNumber,
|
||||||
|
@QueryParam("uploadId") @DefaultValue("") String uploadID,
|
||||||
InputStream body) throws IOException, OS3Exception {
|
InputStream body) throws IOException, OS3Exception {
|
||||||
|
|
||||||
OzoneOutputStream output = null;
|
OzoneOutputStream output = null;
|
||||||
|
|
||||||
|
if (!uploadID.equals("")) {
|
||||||
|
// If uploadID is specified, it is a request for upload part
|
||||||
|
return createMultipartKey(bucketName, keyPath, length,
|
||||||
|
partNumber, uploadID, body);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String copyHeader = headers.getHeaderString(COPY_SOURCE_HEADER);
|
String copyHeader = headers.getHeaderString(COPY_SOURCE_HEADER);
|
||||||
String storageType = headers.getHeaderString(STORAGE_CLASS_HEADER);
|
String storageType = headers.getHeaderString(STORAGE_CLASS_HEADER);
|
||||||
@ -390,6 +403,32 @@ public Response initiateMultipartUpload(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Response createMultipartKey(String bucket, String key, long length,
|
||||||
|
int partNumber, String uploadID,
|
||||||
|
InputStream body)
|
||||||
|
throws IOException, OS3Exception {
|
||||||
|
try {
|
||||||
|
OzoneBucket ozoneBucket = getBucket(bucket);
|
||||||
|
OzoneOutputStream ozoneOutputStream = ozoneBucket.createMultipartKey(
|
||||||
|
key, length, partNumber, uploadID);
|
||||||
|
IOUtils.copy(body, ozoneOutputStream);
|
||||||
|
ozoneOutputStream.close();
|
||||||
|
OmMultipartCommitUploadPartInfo omMultipartCommitUploadPartInfo =
|
||||||
|
ozoneOutputStream.getCommitUploadPartInfo();
|
||||||
|
return Response.status(Status.OK).header("ETag",
|
||||||
|
omMultipartCommitUploadPartInfo.getPartName()).build();
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (ex.getMessage().contains("NO_SUCH_MULTIPART_UPLOAD_ERROR")) {
|
||||||
|
OS3Exception os3Exception = S3ErrorTable.newError(NO_SUCH_UPLOAD,
|
||||||
|
uploadID);
|
||||||
|
throw os3Exception;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void setHeaders(HttpHeaders headers) {
|
public void setHeaders(HttpHeaders headers) {
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
@ -63,6 +63,11 @@ private S3ErrorTable() {
|
|||||||
"InvalidRange", "The requested range is not satisfiable",
|
"InvalidRange", "The requested range is not satisfiable",
|
||||||
RANGE_NOT_SATISFIABLE);
|
RANGE_NOT_SATISFIABLE);
|
||||||
|
|
||||||
|
public static final OS3Exception NO_SUCH_UPLOAD = new OS3Exception(
|
||||||
|
"NoSuchUpload", "The specified multipart upload does not exist. The " +
|
||||||
|
"upload ID might be invalid, or the multipart upload might have " +
|
||||||
|
"been aborted or completed.", HTTP_NOT_FOUND);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of Error.
|
* Create a new instance of Error.
|
||||||
* @param e Error Template
|
* @param e Error Template
|
||||||
|
@ -49,6 +49,9 @@ public class OzoneBucketStub extends OzoneBucket {
|
|||||||
private Map<String, byte[]> keyContents = new HashMap<>();
|
private Map<String, byte[]> keyContents = new HashMap<>();
|
||||||
|
|
||||||
private Map<String, String> multipartUploadIdMap = new HashMap<>();
|
private Map<String, String> multipartUploadIdMap = new HashMap<>();
|
||||||
|
|
||||||
|
private Map<String, Map<Integer, Part>> partList = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs OzoneBucket instance.
|
* Constructs OzoneBucket instance.
|
||||||
*
|
*
|
||||||
@ -160,4 +163,52 @@ public OmMultipartInfo initiateMultipartUpload(String keyName,
|
|||||||
multipartUploadIdMap.put(keyName, uploadID);
|
multipartUploadIdMap.put(keyName, uploadID);
|
||||||
return new OmMultipartInfo(getVolumeName(), getName(), keyName, uploadID);
|
return new OmMultipartInfo(getVolumeName(), getName(), keyName, uploadID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OzoneOutputStream createMultipartKey(String key, long size,
|
||||||
|
int partNumber, String uploadID)
|
||||||
|
throws IOException {
|
||||||
|
String multipartUploadID = multipartUploadIdMap.get(key);
|
||||||
|
if (multipartUploadID == null || multipartUploadID != uploadID) {
|
||||||
|
throw new IOException("NO_SUCH_MULTIPART_UPLOAD_ERROR");
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream =
|
||||||
|
new ByteArrayOutputStream((int) size) {
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
Part part = new Part(key + size,
|
||||||
|
toByteArray());
|
||||||
|
if (partList.get(key) == null) {
|
||||||
|
Map<Integer, Part> parts = new HashMap<>();
|
||||||
|
parts.put(partNumber, part);
|
||||||
|
partList.put(key, parts);
|
||||||
|
} else {
|
||||||
|
partList.get(key).put(partNumber, part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new OzoneOutputStreamStub(byteArrayOutputStream, key + size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to hold part information in a upload part request.
|
||||||
|
*/
|
||||||
|
public class Part {
|
||||||
|
private String partName;
|
||||||
|
private byte[] content;
|
||||||
|
|
||||||
|
public Part(String name, byte[] data) {
|
||||||
|
this.partName = name;
|
||||||
|
this.content = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPartName() {
|
||||||
|
return partName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.ozone.client;
|
||||||
|
|
||||||
|
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmMultipartCommitUploadPartInfo;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OzoneOutputStream stub for testing.
|
||||||
|
*/
|
||||||
|
public class OzoneOutputStreamStub extends OzoneOutputStream {
|
||||||
|
|
||||||
|
private final String partName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs OzoneOutputStreamStub with outputStream and partName.
|
||||||
|
*
|
||||||
|
* @param outputStream
|
||||||
|
* @param name - partName
|
||||||
|
*/
|
||||||
|
public OzoneOutputStreamStub(OutputStream outputStream, String name) {
|
||||||
|
super(outputStream);
|
||||||
|
this.partName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
getOutputStream().write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
getOutputStream().write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void flush() throws IOException {
|
||||||
|
getOutputStream().flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() throws IOException {
|
||||||
|
//commitKey can be done here, if needed.
|
||||||
|
getOutputStream().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OmMultipartCommitUploadPartInfo getCommitUploadPartInfo() {
|
||||||
|
return new OmMultipartCommitUploadPartInfo(partName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -85,7 +85,7 @@ public void testPutObject() throws IOException, OS3Exception {
|
|||||||
|
|
||||||
//WHEN
|
//WHEN
|
||||||
Response response = objectEndpoint.put(bucketName, keyName, CONTENT
|
Response response = objectEndpoint.put(bucketName, keyName, CONTENT
|
||||||
.length(), body);
|
.length(), 1, null, body);
|
||||||
|
|
||||||
|
|
||||||
//THEN
|
//THEN
|
||||||
@ -117,7 +117,7 @@ public void testPutObjectWithSignedChunks() throws IOException, OS3Exception {
|
|||||||
|
|
||||||
//WHEN
|
//WHEN
|
||||||
Response response = objectEndpoint.put(bucketName, keyName,
|
Response response = objectEndpoint.put(bucketName, keyName,
|
||||||
chunkedContent.length(),
|
chunkedContent.length(), 1, null,
|
||||||
new ByteArrayInputStream(chunkedContent.getBytes()));
|
new ByteArrayInputStream(chunkedContent.getBytes()));
|
||||||
|
|
||||||
//THEN
|
//THEN
|
||||||
@ -142,7 +142,7 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
keyName = "sourceKey";
|
keyName = "sourceKey";
|
||||||
|
|
||||||
Response response = objectEndpoint.put(bucketName, keyName,
|
Response response = objectEndpoint.put(bucketName, keyName,
|
||||||
CONTENT.length(), body);
|
CONTENT.length(), 1, null, body);
|
||||||
|
|
||||||
String volumeName = clientStub.getObjectStore().getOzoneVolumeName(
|
String volumeName = clientStub.getObjectStore().getOzoneVolumeName(
|
||||||
bucketName);
|
bucketName);
|
||||||
@ -161,7 +161,8 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
||||||
bucketName + "/" + keyName);
|
bucketName + "/" + keyName);
|
||||||
|
|
||||||
response = objectEndpoint.put(destBucket, destkey, CONTENT.length(), body);
|
response = objectEndpoint.put(destBucket, destkey, CONTENT.length(), 1,
|
||||||
|
null, body);
|
||||||
|
|
||||||
// Check destination key and response
|
// Check destination key and response
|
||||||
volumeName = clientStub.getObjectStore().getOzoneVolumeName(destBucket);
|
volumeName = clientStub.getObjectStore().getOzoneVolumeName(destBucket);
|
||||||
@ -175,7 +176,7 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
|
|
||||||
// source and dest same
|
// source and dest same
|
||||||
try {
|
try {
|
||||||
objectEndpoint.put(bucketName, keyName, CONTENT.length(), body);
|
objectEndpoint.put(bucketName, keyName, CONTENT.length(), 1, null, body);
|
||||||
fail("test copy object failed");
|
fail("test copy object failed");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
Assert.assertTrue(ex.getErrorMessage().contains("This copy request is " +
|
Assert.assertTrue(ex.getErrorMessage().contains("This copy request is " +
|
||||||
@ -186,7 +187,7 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
try {
|
try {
|
||||||
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
||||||
nonexist + "/" + keyName);
|
nonexist + "/" + keyName);
|
||||||
objectEndpoint.put(destBucket, destkey, CONTENT.length(),
|
objectEndpoint.put(destBucket, destkey, CONTENT.length(), 1, null,
|
||||||
body);
|
body);
|
||||||
fail("test copy object failed");
|
fail("test copy object failed");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
@ -197,7 +198,7 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
try {
|
try {
|
||||||
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
||||||
bucketName + "/" + keyName);
|
bucketName + "/" + keyName);
|
||||||
objectEndpoint.put(nonexist, destkey, CONTENT.length(), body);
|
objectEndpoint.put(nonexist, destkey, CONTENT.length(), 1, null, body);
|
||||||
fail("test copy object failed");
|
fail("test copy object failed");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
||||||
@ -207,7 +208,7 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
try {
|
try {
|
||||||
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
||||||
nonexist + "/" + keyName);
|
nonexist + "/" + keyName);
|
||||||
objectEndpoint.put(nonexist, destkey, CONTENT.length(), body);
|
objectEndpoint.put(nonexist, destkey, CONTENT.length(), 1, null, body);
|
||||||
fail("test copy object failed");
|
fail("test copy object failed");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
||||||
@ -217,7 +218,8 @@ public void testCopyObject() throws IOException, OS3Exception {
|
|||||||
try {
|
try {
|
||||||
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
when(headers.getHeaderString(COPY_SOURCE_HEADER)).thenReturn(
|
||||||
bucketName + "/" + nonexist);
|
bucketName + "/" + nonexist);
|
||||||
objectEndpoint.put("nonexistent", keyName, CONTENT.length(), body);
|
objectEndpoint.put("nonexistent", keyName, CONTENT.length(), 1,
|
||||||
|
null, body);
|
||||||
fail("test copy object failed");
|
fail("test copy object failed");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
Assert.assertTrue(ex.getCode().contains("NoSuchBucket"));
|
||||||
@ -235,7 +237,7 @@ public void testInvalidStorageType() throws IOException {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Response response = objectEndpoint.put(bucketName, keyName,
|
Response response = objectEndpoint.put(bucketName, keyName,
|
||||||
CONTENT.length(), body);
|
CONTENT.length(), 1, null, body);
|
||||||
fail("testInvalidStorageType");
|
fail("testInvalidStorageType");
|
||||||
} catch (OS3Exception ex) {
|
} catch (OS3Exception ex) {
|
||||||
assertEquals(S3ErrorTable.INVALID_ARGUMENT.getErrorMessage(),
|
assertEquals(S3ErrorTable.INVALID_ARGUMENT.getErrorMessage(),
|
||||||
@ -252,8 +254,8 @@ public void testEmptyStorageType() throws IOException, OS3Exception {
|
|||||||
keyName = "sourceKey";
|
keyName = "sourceKey";
|
||||||
when(headers.getHeaderString(STORAGE_CLASS_HEADER)).thenReturn("");
|
when(headers.getHeaderString(STORAGE_CLASS_HEADER)).thenReturn("");
|
||||||
|
|
||||||
Response response =
|
Response response = objectEndpoint.put(bucketName, keyName, CONTENT
|
||||||
objectEndpoint.put(bucketName, keyName, CONTENT.length(), body);
|
.length(), 1, null, body);
|
||||||
|
|
||||||
String volumeName = clientStub.getObjectStore()
|
String volumeName = clientStub.getObjectStore()
|
||||||
.getOzoneVolumeName(bucketName);
|
.getOzoneVolumeName(bucketName);
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.ozone.s3.endpoint;
|
||||||
|
|
||||||
|
import org.apache.hadoop.ozone.client.OzoneClientStub;
|
||||||
|
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
||||||
|
import static org.apache.hadoop.ozone.s3.util.S3Consts.STORAGE_CLASS_HEADER;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class tests Upload part request.
|
||||||
|
*/
|
||||||
|
public class TestPartUpload {
|
||||||
|
|
||||||
|
private final static ObjectEndpoint REST = new ObjectEndpoint();;
|
||||||
|
private final static String BUCKET = "s3bucket";
|
||||||
|
private final static String KEY = "key1";
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
|
||||||
|
OzoneClientStub client = new OzoneClientStub();
|
||||||
|
client.getObjectStore().createS3Bucket("ozone", BUCKET);
|
||||||
|
|
||||||
|
|
||||||
|
HttpHeaders headers = Mockito.mock(HttpHeaders.class);
|
||||||
|
when(headers.getHeaderString(STORAGE_CLASS_HEADER)).thenReturn(
|
||||||
|
"STANDARD");
|
||||||
|
|
||||||
|
REST.setHeaders(headers);
|
||||||
|
REST.setClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPartUpload() throws Exception {
|
||||||
|
|
||||||
|
Response response = REST.initiateMultipartUpload(BUCKET, KEY, "");
|
||||||
|
MultipartUploadInitiateResponse multipartUploadInitiateResponse =
|
||||||
|
(MultipartUploadInitiateResponse) response.getEntity();
|
||||||
|
assertNotNull(multipartUploadInitiateResponse.getUploadID());
|
||||||
|
String uploadID = multipartUploadInitiateResponse.getUploadID();
|
||||||
|
|
||||||
|
assertEquals(response.getStatus(), 200);
|
||||||
|
|
||||||
|
String content = "Multipart Upload";
|
||||||
|
ByteArrayInputStream body = new ByteArrayInputStream(content.getBytes());
|
||||||
|
response = REST.put(BUCKET, KEY, content.length(), 1, uploadID, body);
|
||||||
|
|
||||||
|
assertNotNull(response.getHeaderString("ETag"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPartUploadWithOverride() throws Exception {
|
||||||
|
|
||||||
|
Response response = REST.initiateMultipartUpload(BUCKET, KEY, "");
|
||||||
|
MultipartUploadInitiateResponse multipartUploadInitiateResponse =
|
||||||
|
(MultipartUploadInitiateResponse) response.getEntity();
|
||||||
|
assertNotNull(multipartUploadInitiateResponse.getUploadID());
|
||||||
|
String uploadID = multipartUploadInitiateResponse.getUploadID();
|
||||||
|
|
||||||
|
assertEquals(response.getStatus(), 200);
|
||||||
|
|
||||||
|
String content = "Multipart Upload";
|
||||||
|
ByteArrayInputStream body = new ByteArrayInputStream(content.getBytes());
|
||||||
|
response = REST.put(BUCKET, KEY, content.length(), 1, uploadID, body);
|
||||||
|
|
||||||
|
assertNotNull(response.getHeaderString("ETag"));
|
||||||
|
|
||||||
|
String eTag = response.getHeaderString("ETag");
|
||||||
|
|
||||||
|
// Upload part again with same part Number, the ETag should be changed.
|
||||||
|
content = "Multipart Upload Changed";
|
||||||
|
response = REST.put(BUCKET, KEY, content.length(), 1, uploadID, body);
|
||||||
|
assertNotNull(response.getHeaderString("ETag"));
|
||||||
|
assertNotEquals(eTag, response.getHeaderString("ETag"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPartUploadWithIncorrectUploadID() throws Exception {
|
||||||
|
try {
|
||||||
|
String content = "Multipart Upload With Incorrect uploadID";
|
||||||
|
ByteArrayInputStream body = new ByteArrayInputStream(content.getBytes());
|
||||||
|
REST.put(BUCKET, KEY, content.length(), 1, "random", body);
|
||||||
|
fail("testPartUploadWithIncorrectUploadID failed");
|
||||||
|
} catch (OS3Exception ex) {
|
||||||
|
assertEquals("NoSuchUpload", ex.getCode());
|
||||||
|
assertEquals(HTTP_NOT_FOUND, ex.getHttpCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user