Filesystem: Fix the MPU ETags to match S3.
Prior commit introduced a bug in the computation of the MPU ETag value,
where it was concatenating strings, rather than operating on the bytes
of the integer value.
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemBlobIntegrationTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemBlobIntegrationTest.java
index 9c1b483..cda138c 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemBlobIntegrationTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemBlobIntegrationTest.java
@@ -19,23 +19,31 @@
import static org.jclouds.filesystem.util.Utils.isMacOSX;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.assertj.core.api.Assertions.assertThat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.common.io.BaseEncoding;
import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.MultipartPart;
+import org.jclouds.blobstore.domain.MultipartUpload;
import org.jclouds.blobstore.domain.Tier;
+import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.filesystem.reference.FilesystemConstants;
import org.jclouds.filesystem.utils.TestUtils;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
import org.testng.annotations.Test;
import org.testng.SkipException;
@@ -113,12 +121,12 @@
@Override
protected void checkMPUParts(Blob blob, List<MultipartPart> partsList) {
assertThat(blob.getMetadata().getETag()).endsWith(String.format("-%d\"", partsList.size()));
- StringBuilder eTags = new StringBuilder();
+ Hasher eTagHasher = Hashing.md5().newHasher();
for (MultipartPart part : partsList) {
- eTags.append(part.partETag());
+ eTagHasher.putBytes(BaseEncoding.base16().lowerCase().decode(part.partETag()));
}
String expectedETag = new StringBuilder("\"")
- .append(Hashing.md5().hashString(eTags.toString(), US_ASCII))
+ .append(eTagHasher.hash())
.append("-")
.append(partsList.size())
.append("\"")
@@ -126,6 +134,36 @@
assertThat(blob.getMetadata().getETag()).isEqualTo(expectedETag);
}
+ @Test(groups = { "integration", "live" })
+ public void testMultipartUploadMultiplePartsKnownETag() throws Exception {
+ BlobStore blobStore = view.getBlobStore();
+ String container = getContainerName();
+ // Pre-computed ETag returned by AWS S3 for the MPU consisting of two 5MB parts filled with 'b'
+ String expectedETag = "\"84462a16f6a60478d50148808aa609c1-2\"";
+ int partSize = 5 * 1024 * 1024;
+ try {
+ String name = "blob-name";
+ BlobBuilder blobBuilder = blobStore.blobBuilder(name);
+ Blob blob = blobBuilder.build();
+ MultipartUpload mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions());
+
+ byte[] content = new byte[partSize];
+ Arrays.fill(content, (byte) 'b');
+ Payload payload = Payloads.newByteArrayPayload(content);
+
+ payload.getContentMetadata().setContentLength((long) partSize);
+
+ MultipartPart part1 = blobStore.uploadMultipartPart(mpu, 1, payload);
+ MultipartPart part2 = blobStore.uploadMultipartPart(mpu, 2, payload);
+ blobStore.completeMultipartUpload(mpu, ImmutableList.of(part1, part2));
+
+ BlobMetadata newBlobMetadata = blobStore.blobMetadata(container, name);
+ assertThat(newBlobMetadata.getETag()).isEqualTo(expectedETag);
+ } finally {
+ returnContainer(container);
+ }
+ }
+
protected void checkExtendedAttributesSupport() {
if (isMacOSX()) {
throw new SkipException("filesystem does not support extended attributes in Mac OSX");
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/config/LocalBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/config/LocalBlobStore.java
index f938b35..3717ed4 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/config/LocalBlobStore.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/config/LocalBlobStore.java
@@ -25,7 +25,6 @@
import static com.google.common.collect.Iterables.tryFind;
import static com.google.common.collect.Sets.filter;
import static com.google.common.collect.Sets.newTreeSet;
-import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
import java.io.File;
@@ -46,6 +45,7 @@
import javax.inject.Inject;
import javax.inject.Singleton;
+import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
@@ -101,6 +101,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
+import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteSource;
import com.google.common.net.HttpHeaders;
@@ -826,7 +827,7 @@
public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) {
ImmutableList.Builder<InputStream> streams = ImmutableList.builder();
long contentLength = 0;
- StringBuilder partHashes = new StringBuilder();
+ Hasher md5Hasher = Hashing.md5().newHasher();
for (MultipartPart part : parts) {
Blob blobPart = getBlob(mpu.containerName(), MULTIPART_PREFIX + mpu.id() + "-" + mpu.blobName() + "-" + part.partNumber());
@@ -838,10 +839,10 @@
throw propagate(ioe);
}
streams.add(is);
- partHashes.append(blobPart.getMetadata().getETag());
+ md5Hasher.putBytes(BaseEncoding.base16().lowerCase().decode(blobPart.getMetadata().getETag()));
}
String mpuETag = new StringBuilder("\"")
- .append(Hashing.md5().hashString(partHashes.toString(), US_ASCII).toString())
+ .append(md5Hasher.hash())
.append("-")
.append(parts.size())
.append("\"")