Skip to content

Commit fcdce24

Browse files
Merge pull request #86 from EMCECS/test-failures-fix
resolve tagging failures in S3Encryption* tests
2 parents 3cb3eac + 599d628 commit fcdce24

9 files changed

+229
-166
lines changed

src/main/java/com/emc/object/s3/jersey/S3EncryptionClient.java

+10
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ public boolean rekey(String bucketName, String key) {
183183
}
184184
}
185185

186+
/**
187+
* Encrypted version of {@link S3JerseyClient#putObject(PutObjectRequest)}.
188+
* <p>
189+
* Note: this method will write the encrypted object first, then update the metadata to finalize encryption
190+
* properties (including original SHA1 and metadata signature). For version-enabled buckets, this will create
191+
* 2 versions.
192+
*/
186193
@Override
187194
public PutObjectResult putObject(PutObjectRequest request) {
188195
if (request.getRange() != null)
@@ -202,8 +209,11 @@ public PutObjectResult putObject(PutObjectRequest request) {
202209
// encryption filter will modify userMeta with encryption metadata *after* the object is transferred
203210
// we must send a separate metadata update or the object will be unreadable
204211
// TODO: should this be atomic? how do we handle rollback?
212+
// TODO: also, for version-enabled buckets, should we delete the intermediate version??
213+
// ... and if so, what to do if retention is enabled?
205214
CopyObjectRequest metadataUpdate = new CopyObjectRequest(request.getBucketName(), request.getKey(),
206215
request.getBucketName(), request.getKey()).withAcl(request.getAcl())
216+
.withObjectTagging(request.getObjectTagging())
207217
.withObjectMetadata(request.getObjectMetadata()).withIfMatch(result.getETag());
208218
return super.copyObject(metadataUpdate);
209219
}

src/main/java/com/emc/object/s3/request/CopyObjectRequest.java

+18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.emc.object.s3.S3ObjectMetadata;
3232
import com.emc.object.s3.bean.AccessControlList;
3333
import com.emc.object.s3.bean.CannedAcl;
34+
import com.emc.object.s3.bean.ObjectTagging;
3435
import com.emc.object.util.RestUtil;
3536

3637
import java.util.Date;
@@ -55,6 +56,8 @@ public class CopyObjectRequest extends S3ObjectRequest {
5556
private CannedAcl cannedAcl;
5657

5758
private String copyMode;
59+
private ObjectTagging objectTagging;
60+
5861

5962
public CopyObjectRequest(String sourceBucketName, String sourceKey, String bucketName, String key) {
6063
super(Method.PUT, bucketName, key, null);
@@ -87,6 +90,8 @@ public Map<String, List<Object>> getHeaders() {
8790
if (cannedAcl != null) RestUtil.putSingle(headers, S3Constants.AMZ_ACL, cannedAcl.getHeaderValue());
8891
if (copyMode != null)
8992
RestUtil.putSingle(headers, RestUtil.EMC_COPY_MODE, copyMode);
93+
if (objectTagging != null)
94+
RestUtil.putSingle(headers, S3Constants.AMZ_TAGGING, RestUtil.generateRawQueryString(objectTagging.toStringMap()));
9095
return headers;
9196
}
9297

@@ -186,6 +191,14 @@ public void setCopyMode(String copyMode) {
186191
this.copyMode = copyMode;
187192
}
188193

194+
public ObjectTagging getObjectTagging() {
195+
return objectTagging;
196+
}
197+
198+
public void setObjectTagging(ObjectTagging objectTagging) {
199+
this.objectTagging = objectTagging;
200+
}
201+
189202
public CopyObjectRequest withSourceVersionId(String sourceVersionId) {
190203
setSourceVersionId(sourceVersionId);
191204
return this;
@@ -230,4 +243,9 @@ public CopyObjectRequest withCopyMode(String copyMode) {
230243
setCopyMode(copyMode);
231244
return this;
232245
}
246+
247+
public CopyObjectRequest withObjectTagging(ObjectTagging objectTagging) {
248+
setObjectTagging(objectTagging);
249+
return this;
250+
}
233251
}

src/test/java/com/emc/object/s3/AbstractS3ClientTest.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,22 @@
2929
import com.emc.object.AbstractClientTest;
3030
import com.emc.object.ObjectConfig;
3131
import com.emc.object.Protocol;
32-
import com.emc.object.s3.bean.AbstractVersion;
33-
import com.emc.object.s3.bean.EncodingType;
34-
import com.emc.object.s3.bean.S3Object;
32+
import com.emc.object.s3.bean.*;
3533
import com.emc.object.s3.jersey.S3JerseyClient;
34+
import com.emc.object.s3.request.DeleteObjectRequest;
3635
import com.emc.object.s3.request.ListObjectsRequest;
3736
import com.emc.object.s3.request.ListVersionsRequest;
37+
import com.emc.object.s3.request.SetObjectLegalHoldRequest;
3838
import com.emc.object.util.TestProperties;
3939
import com.emc.rest.smart.LoadBalancer;
4040
import com.emc.rest.smart.ecs.Vdc;
4141
import com.emc.util.TestConfig;
4242
import org.junit.After;
43+
import org.junit.Before;
4344
import org.slf4j.Logger;
4445
import org.slf4j.LoggerFactory;
4546

47+
import java.io.IOException;
4648
import java.net.URI;
4749
import java.util.Arrays;
4850
import java.util.Properties;
@@ -55,6 +57,8 @@ public abstract class AbstractS3ClientTest extends AbstractClientTest {
5557
* may be null
5658
*/
5759
protected String ecsVersion;
60+
protected boolean isIamUser = false;
61+
protected CanonicalUser bucketOwner;
5862

5963
protected abstract S3Client createS3Client() throws Exception;
6064

@@ -67,6 +71,12 @@ protected final void initClient() throws Exception {
6771
}
6872
}
6973

74+
@Before
75+
public void checkIamUser() throws IOException {
76+
Properties props = TestConfig.getProperties();
77+
this.isIamUser = Boolean.parseBoolean(props.getProperty(TestProperties.S3_IAM_USER));
78+
}
79+
7080
@After
7181
public void dumpLBStats() {
7282
if (client != null) {
@@ -83,14 +93,22 @@ public void shutdownClient() {
8393
@Override
8494
protected void createBucket(String bucketName) throws Exception {
8595
client.createBucket(bucketName);
96+
this.bucketOwner = client.getBucketAcl(bucketName).getOwner();
8697
}
8798

8899
@Override
89100
protected void cleanUpBucket(String bucketName) {
90101
if (client != null && client.bucketExists(bucketName)) {
102+
boolean objectLockEnabled = isIamUser && client.getObjectLockConfiguration(bucketName) != null;
91103
if (client.getBucketVersioning(bucketName).getStatus() != null) {
92104
for (AbstractVersion version : client.listVersions(new ListVersionsRequest(bucketName).withEncodingType(EncodingType.url)).getVersions()) {
93-
client.deleteVersion(bucketName, version.getKey(), version.getVersionId());
105+
DeleteObjectRequest deleteRequest = new DeleteObjectRequest(bucketName, version.getKey()).withVersionId(version.getVersionId());
106+
if (objectLockEnabled) {
107+
client.setObjectLegalHold(new SetObjectLegalHoldRequest(bucketName, version.getKey()).withVersionId(version.getVersionId())
108+
.withLegalHold(new ObjectLockLegalHold().withStatus(ObjectLockLegalHold.Status.OFF)));
109+
deleteRequest.withBypassGovernanceRetention(true);
110+
}
111+
client.deleteObject(deleteRequest);
94112
}
95113
} else {
96114
for (S3Object object : client.listObjects(new ListObjectsRequest(bucketName).withEncodingType(EncodingType.url)).getObjects()) {

src/test/java/com/emc/object/s3/S3EncryptionClientBasicTest.java

+68
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import com.emc.object.s3.jersey.FaultInjectionFilter;
3636
import com.emc.object.s3.jersey.S3EncryptionClient;
3737
import com.emc.object.s3.jersey.S3JerseyClient;
38+
import com.emc.object.s3.request.DeleteObjectRequest;
39+
import com.emc.object.s3.request.GetObjectRequest;
40+
import com.emc.object.s3.request.GetObjectTaggingRequest;
3841
import com.emc.object.s3.request.PutObjectRequest;
3942
import com.emc.util.RandomInputStream;
4043
import org.apache.commons.codec.digest.DigestUtils;
@@ -52,6 +55,8 @@
5255
import java.security.KeyPair;
5356
import java.security.interfaces.RSAPrivateKey;
5457
import java.security.interfaces.RSAPublicKey;
58+
import java.util.Arrays;
59+
import java.util.List;
5560
import java.util.Properties;
5661

5762
import static org.junit.Assert.assertEquals;
@@ -557,6 +562,69 @@ public void testExtendObjectRetentionPeriod() {
557562
public void testPreSignedUrlHeaderOverrides() throws Exception {
558563
}
559564

565+
@Ignore
566+
@Override
567+
public void testSingleMultipartUploadWithRetention() {
568+
}
569+
570+
@Ignore
571+
@Override
572+
public void testCopyObjectWithLegalHoldON() {
573+
}
574+
575+
@Override
576+
public void testGetPutDeleteObjectWithTagging() {
577+
// set up env
578+
String bucketName = getTestBucket(), key = "test-object-tagging";
579+
client.setBucketVersioning(bucketName, new VersioningConfiguration().withStatus(VersioningConfiguration.Status.Enabled));
580+
581+
// write version 1
582+
client.putObject(new PutObjectRequest(bucketName, key, "Hello Version 1 !")
583+
.withObjectTagging(new ObjectTagging().withTagSet(Arrays.asList(new ObjectTag("k11", "v11"), new ObjectTag("k12", "v12")))));
584+
// write version 2
585+
client.putObject(new PutObjectRequest(bucketName, key, "Hello Version 2 !"));
586+
587+
// NOTE: encryption client creates 2 versions per PUT, due to secondary metadata update operation
588+
List<AbstractVersion> versions = client.listVersions(bucketName, key).getVersions();
589+
String versionId1a = versions.get(3).getVersionId();
590+
String versionId1b = versions.get(2).getVersionId();
591+
String versionId2a = versions.get(1).getVersionId();
592+
String versionId2b = versions.get(0).getVersionId();
593+
594+
// Only the particular version of the object should get deleted and no other versions of object should be affected
595+
// NOTE: have to delete both versions created by the encryption client
596+
client.deleteObject(new DeleteObjectRequest(bucketName, key).withVersionId(versionId2a));
597+
client.deleteObject(new DeleteObjectRequest(bucketName, key).withVersionId(versionId2b));
598+
// NOTE: actually both versions that the encryption client creates should have tagging set
599+
// but to test, we must use rclient (raw client) because encryption client cannot read the intermediate version
600+
Assert.assertEquals(2,
601+
rclient.getObject(new GetObjectRequest(bucketName, key).withVersionId(versionId1a), String.class).getObjectMetadata().getTaggingCount());
602+
Assert.assertEquals(2,
603+
client.getObject(new GetObjectRequest(bucketName, key).withVersionId(versionId1b), String.class).getObjectMetadata().getTaggingCount());
604+
605+
// Object and associated multiple tags should get deleted
606+
// NOTE: have to delete both versions created by the encryption client
607+
client.deleteObject(new DeleteObjectRequest(bucketName, key).withVersionId(versionId1a));
608+
client.deleteObject(new DeleteObjectRequest(bucketName, key).withVersionId(versionId1b));
609+
try {
610+
client.getObjectTagging(new GetObjectTaggingRequest(bucketName, key).withVersionId(versionId1b));
611+
Assert.fail("Fail was expected. Can NOT get tags from a deleted object");
612+
} catch (S3Exception e) {
613+
Assert.assertEquals(404, e.getHttpCode());
614+
Assert.assertEquals("NoSuchKey", e.getErrorCode());
615+
}
616+
}
617+
618+
@Ignore
619+
@Override
620+
public void testCopyObjectWithTaggingAndMeta() {
621+
}
622+
623+
@Ignore
624+
@Override
625+
public void testMultipartUploadWithTagging() {
626+
}
627+
560628
private class ErrorStream extends FilterInputStream {
561629
private int callCount = 0;
562630

0 commit comments

Comments
 (0)