Skip to content

Commit 5c3a40b

Browse files
committed
Use ESBlobStoreRepositoryIntegTestCase to test the repository-s3 plugin (#29315)
This commit adds the S3BlobStoreRepositoryTests class that extends the base testing class for S3. It also removes some usage of socket servers that emulate socket connections in unit tests. It was added to trigger security exceptions, but this won't be needed anymore since #29296 is merged.
1 parent b7c2cd1 commit 5c3a40b

File tree

5 files changed

+223
-245
lines changed

5 files changed

+223
-245
lines changed

plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,7 @@ class S3Repository extends BlobStoreRepository {
151151
/**
152152
* Constructs an s3 backed repository
153153
*/
154-
S3Repository(RepositoryMetaData metadata, Settings settings,
155-
NamedXContentRegistry namedXContentRegistry, AwsS3Service s3Service) throws IOException {
154+
S3Repository(RepositoryMetaData metadata, Settings settings, NamedXContentRegistry namedXContentRegistry, AwsS3Service s3Service) {
156155
super(metadata, settings, namedXContentRegistry);
157156

158157
String bucket = BUCKET_SETTING.get(metadata.settings());

plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/MockAmazonS3.java

Lines changed: 114 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
package org.elasticsearch.repositories.s3;
2121

2222
import com.amazonaws.AmazonClientException;
23-
import com.amazonaws.AmazonServiceException;
2423
import com.amazonaws.SdkClientException;
2524
import com.amazonaws.services.s3.AbstractAmazonS3;
2625
import com.amazonaws.services.s3.model.AmazonS3Exception;
2726
import com.amazonaws.services.s3.model.CopyObjectRequest;
2827
import com.amazonaws.services.s3.model.CopyObjectResult;
2928
import com.amazonaws.services.s3.model.DeleteObjectRequest;
30-
import com.amazonaws.services.s3.model.GetObjectMetadataRequest;
29+
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
30+
import com.amazonaws.services.s3.model.DeleteObjectsResult;
3131
import com.amazonaws.services.s3.model.GetObjectRequest;
3232
import com.amazonaws.services.s3.model.ListObjectsRequest;
3333
import com.amazonaws.services.s3.model.ObjectListing;
@@ -37,203 +37,163 @@
3737
import com.amazonaws.services.s3.model.S3Object;
3838
import com.amazonaws.services.s3.model.S3ObjectInputStream;
3939
import com.amazonaws.services.s3.model.S3ObjectSummary;
40+
import org.elasticsearch.common.Strings;
41+
import org.elasticsearch.common.io.Streams;
4042

43+
import java.io.ByteArrayInputStream;
44+
import java.io.ByteArrayOutputStream;
4145
import java.io.IOException;
42-
import java.io.InputStream;
43-
import java.io.UncheckedIOException;
44-
import java.net.InetAddress;
45-
import java.net.Socket;
4646
import java.util.ArrayList;
4747
import java.util.List;
4848
import java.util.Map;
49-
import java.util.concurrent.ConcurrentHashMap;
49+
import java.util.Objects;
50+
import java.util.concurrent.ConcurrentMap;
5051

51-
import static org.junit.Assert.assertTrue;
52+
import static org.hamcrest.MatcherAssert.assertThat;
53+
import static org.hamcrest.Matchers.equalTo;
54+
import static org.hamcrest.Matchers.notNullValue;
55+
import static org.hamcrest.Matchers.nullValue;
5256

5357
class MockAmazonS3 extends AbstractAmazonS3 {
5458

55-
private final int mockSocketPort;
56-
57-
private Map<String, InputStream> blobs = new ConcurrentHashMap<>();
58-
59-
// in ESBlobStoreContainerTestCase.java, the maximum
60-
// length of the input data is 100 bytes
61-
private byte[] byteCounter = new byte[100];
62-
63-
64-
MockAmazonS3(int mockSocketPort) {
65-
this.mockSocketPort = mockSocketPort;
59+
private final ConcurrentMap<String, byte[]> blobs;
60+
private final String bucket;
61+
private final boolean serverSideEncryption;
62+
private final String cannedACL;
63+
private final String storageClass;
64+
65+
MockAmazonS3(final ConcurrentMap<String, byte[]> blobs,
66+
final String bucket,
67+
final boolean serverSideEncryption,
68+
final String cannedACL,
69+
final String storageClass) {
70+
this.blobs = Objects.requireNonNull(blobs);
71+
this.bucket = Objects.requireNonNull(bucket);
72+
this.serverSideEncryption = serverSideEncryption;
73+
this.cannedACL = cannedACL;
74+
this.storageClass = storageClass;
6675
}
6776

68-
// Simulate a socket connection to check that SocketAccess.doPrivileged() is used correctly.
69-
// Any method of AmazonS3 might potentially open a socket to the S3 service. Firstly, a call
70-
// to any method of AmazonS3 has to be wrapped by SocketAccess.doPrivileged().
71-
// Secondly, each method on the stack from doPrivileged to opening the socket has to be
72-
// located in a jar that is provided by the plugin.
73-
// Thirdly, a SocketPermission has to be configured in plugin-security.policy.
74-
// By opening a socket in each method of MockAmazonS3 it is ensured that in production AmazonS3
75-
// is able to to open a socket to the S3 Service without causing a SecurityException
76-
private void simulateS3SocketConnection() {
77-
try (Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), mockSocketPort)) {
78-
assertTrue(socket.isConnected()); // NOOP to keep static analysis happy
79-
} catch (IOException e) {
80-
throw new UncheckedIOException(e);
81-
}
82-
}
83-
84-
8577
@Override
86-
public boolean doesBucketExist(String bucket) {
87-
return true;
78+
public boolean doesBucketExist(final String bucket) {
79+
return this.bucket.equalsIgnoreCase(bucket);
8880
}
8981

9082
@Override
91-
public boolean doesObjectExist(String bucketName, String objectName) throws AmazonServiceException, SdkClientException {
92-
simulateS3SocketConnection();
83+
public boolean doesObjectExist(final String bucketName, final String objectName) throws SdkClientException {
84+
assertThat(bucketName, equalTo(bucket));
9385
return blobs.containsKey(objectName);
9486
}
9587

9688
@Override
97-
public ObjectMetadata getObjectMetadata(
98-
GetObjectMetadataRequest getObjectMetadataRequest)
99-
throws AmazonClientException, AmazonServiceException {
100-
simulateS3SocketConnection();
101-
String blobName = getObjectMetadataRequest.getKey();
102-
103-
if (!blobs.containsKey(blobName)) {
104-
throw new AmazonS3Exception("[" + blobName + "] does not exist.");
105-
}
106-
107-
return new ObjectMetadata(); // nothing is done with it
108-
}
109-
110-
@Override
111-
public PutObjectResult putObject(PutObjectRequest putObjectRequest)
112-
throws AmazonClientException, AmazonServiceException {
113-
simulateS3SocketConnection();
114-
String blobName = putObjectRequest.getKey();
115-
116-
if (blobs.containsKey(blobName)) {
117-
throw new AmazonS3Exception("[" + blobName + "] already exists.");
89+
public PutObjectResult putObject(final PutObjectRequest request) throws AmazonClientException {
90+
assertThat(request.getBucketName(), equalTo(bucket));
91+
assertThat(request.getMetadata().getSSEAlgorithm(), serverSideEncryption ? equalTo("AES256") : nullValue());
92+
assertThat(request.getCannedAcl(), notNullValue());
93+
assertThat(request.getCannedAcl().toString(), cannedACL != null ? equalTo(cannedACL) : equalTo("private"));
94+
assertThat(request.getStorageClass(), storageClass != null ? equalTo(storageClass) : equalTo("STANDARD"));
95+
96+
97+
final String blobName = request.getKey();
98+
final ByteArrayOutputStream out = new ByteArrayOutputStream();
99+
try {
100+
Streams.copy(request.getInputStream(), out);
101+
blobs.put(blobName, out.toByteArray());
102+
} catch (IOException e) {
103+
throw new AmazonClientException(e);
118104
}
119-
120-
blobs.put(blobName, putObjectRequest.getInputStream());
121105
return new PutObjectResult();
122106
}
123107

124108
@Override
125-
public S3Object getObject(GetObjectRequest getObjectRequest)
126-
throws AmazonClientException, AmazonServiceException {
127-
simulateS3SocketConnection();
128-
// in ESBlobStoreContainerTestCase.java, the prefix is empty,
129-
// so the key and blobName are equivalent to each other
130-
String blobName = getObjectRequest.getKey();
131-
132-
if (!blobs.containsKey(blobName)) {
133-
final AmazonS3Exception e = new AmazonS3Exception("[" + blobName + "] does not exist.");
134-
e.setStatusCode(404);
135-
throw e;
109+
public S3Object getObject(final GetObjectRequest request) throws AmazonClientException {
110+
assertThat(request.getBucketName(), equalTo(bucket));
111+
112+
final String blobName = request.getKey();
113+
final byte[] content = blobs.get(blobName);
114+
if (content == null) {
115+
AmazonS3Exception exception = new AmazonS3Exception("[" + blobName + "] does not exist.");
116+
exception.setStatusCode(404);
117+
throw exception;
136118
}
137119

138-
// the HTTP request attribute is irrelevant for reading
139-
S3ObjectInputStream stream = new S3ObjectInputStream(
140-
blobs.get(blobName), null, false);
120+
ObjectMetadata metadata = new ObjectMetadata();
121+
metadata.setContentLength(content.length);
122+
141123
S3Object s3Object = new S3Object();
142-
s3Object.setObjectContent(stream);
124+
s3Object.setObjectContent(new S3ObjectInputStream(new ByteArrayInputStream(content), null, false));
125+
s3Object.setKey(blobName);
126+
s3Object.setObjectMetadata(metadata);
127+
143128
return s3Object;
144129
}
145130

146131
@Override
147-
public ObjectListing listObjects(ListObjectsRequest listObjectsRequest)
148-
throws AmazonClientException, AmazonServiceException {
149-
simulateS3SocketConnection();
150-
MockObjectListing list = new MockObjectListing();
151-
list.setTruncated(false);
152-
153-
String blobName;
154-
String prefix = listObjectsRequest.getPrefix();
155-
156-
ArrayList<S3ObjectSummary> mockObjectSummaries = new ArrayList<>();
157-
158-
for (Map.Entry<String, InputStream> blob : blobs.entrySet()) {
159-
blobName = blob.getKey();
160-
S3ObjectSummary objectSummary = new S3ObjectSummary();
161-
162-
if (prefix.isEmpty() || blobName.startsWith(prefix)) {
163-
objectSummary.setKey(blobName);
164-
165-
try {
166-
objectSummary.setSize(getSize(blob.getValue()));
167-
} catch (IOException e) {
168-
throw new AmazonS3Exception("Object listing " +
169-
"failed for blob [" + blob.getKey() + "]");
170-
}
171-
172-
mockObjectSummaries.add(objectSummary);
132+
public ObjectListing listObjects(final ListObjectsRequest request) throws AmazonClientException {
133+
assertThat(request.getBucketName(), equalTo(bucket));
134+
135+
final ObjectListing listing = new ObjectListing();
136+
listing.setBucketName(request.getBucketName());
137+
listing.setPrefix(request.getPrefix());
138+
139+
for (Map.Entry<String, byte[]> blob : blobs.entrySet()) {
140+
if (Strings.isEmpty(request.getPrefix()) || blob.getKey().startsWith(request.getPrefix())) {
141+
S3ObjectSummary summary = new S3ObjectSummary();
142+
summary.setBucketName(request.getBucketName());
143+
summary.setKey(blob.getKey());
144+
summary.setSize(blob.getValue().length);
145+
listing.getObjectSummaries().add(summary);
173146
}
174147
}
175-
176-
list.setObjectSummaries(mockObjectSummaries);
177-
return list;
148+
return listing;
178149
}
179150

180151
@Override
181-
public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest)
182-
throws AmazonClientException, AmazonServiceException {
183-
simulateS3SocketConnection();
184-
String sourceBlobName = copyObjectRequest.getSourceKey();
185-
String targetBlobName = copyObjectRequest.getDestinationKey();
186-
187-
if (!blobs.containsKey(sourceBlobName)) {
188-
final AmazonS3Exception e = new AmazonS3Exception("Source blob [" +
189-
sourceBlobName + "] does not exist.");
190-
e.setStatusCode(404);
191-
throw e;
192-
}
152+
public CopyObjectResult copyObject(final CopyObjectRequest request) throws AmazonClientException {
153+
assertThat(request.getSourceBucketName(), equalTo(bucket));
154+
assertThat(request.getDestinationBucketName(), equalTo(bucket));
193155

194-
if (blobs.containsKey(targetBlobName)) {
195-
throw new AmazonS3Exception("Target blob [" +
196-
targetBlobName + "] already exists.");
156+
final String sourceBlobName = request.getSourceKey();
157+
158+
final byte[] content = blobs.get(sourceBlobName);
159+
if (content == null) {
160+
AmazonS3Exception exception = new AmazonS3Exception("[" + sourceBlobName + "] does not exist.");
161+
exception.setStatusCode(404);
162+
throw exception;
197163
}
198164

199-
blobs.put(targetBlobName, blobs.get(sourceBlobName));
200-
return new CopyObjectResult(); // nothing is done with it
165+
blobs.put(request.getDestinationKey(), content);
166+
return new CopyObjectResult();
201167
}
202168

203169
@Override
204-
public void deleteObject(DeleteObjectRequest deleteObjectRequest)
205-
throws AmazonClientException, AmazonServiceException {
206-
simulateS3SocketConnection();
207-
String blobName = deleteObjectRequest.getKey();
208-
209-
if (!blobs.containsKey(blobName)) {
210-
final AmazonS3Exception e = new AmazonS3Exception("[" + blobName + "] does not exist.");
211-
e.setStatusCode(404);
212-
throw e;
170+
public void deleteObject(final DeleteObjectRequest request) throws AmazonClientException {
171+
assertThat(request.getBucketName(), equalTo(bucket));
172+
173+
final String blobName = request.getKey();
174+
if (blobs.remove(blobName) == null) {
175+
AmazonS3Exception exception = new AmazonS3Exception("[" + blobName + "] does not exist.");
176+
exception.setStatusCode(404);
177+
throw exception;
213178
}
214-
215-
blobs.remove(blobName);
216179
}
217180

218-
private int getSize(InputStream stream) throws IOException {
219-
int size = stream.read(byteCounter);
220-
stream.reset(); // in case we ever need the size again
221-
return size;
222-
}
223-
224-
private class MockObjectListing extends ObjectListing {
225-
// the objectSummaries attribute in ObjectListing.java
226-
// is read-only, but we need to be able to write to it,
227-
// so we create a mock of it to work around this
228-
private List<S3ObjectSummary> mockObjectSummaries;
229-
230-
@Override
231-
public List<S3ObjectSummary> getObjectSummaries() {
232-
return mockObjectSummaries;
233-
}
234-
235-
private void setObjectSummaries(List<S3ObjectSummary> objectSummaries) {
236-
mockObjectSummaries = objectSummaries;
181+
@Override
182+
public DeleteObjectsResult deleteObjects(DeleteObjectsRequest request) throws SdkClientException {
183+
assertThat(request.getBucketName(), equalTo(bucket));
184+
185+
final List<DeleteObjectsResult.DeletedObject> deletions = new ArrayList<>();
186+
for (DeleteObjectsRequest.KeyVersion key : request.getKeys()) {
187+
if (blobs.remove(key.getKey()) == null) {
188+
AmazonS3Exception exception = new AmazonS3Exception("[" + key + "] does not exist.");
189+
exception.setStatusCode(404);
190+
throw exception;
191+
} else {
192+
DeleteObjectsResult.DeletedObject deletion = new DeleteObjectsResult.DeletedObject();
193+
deletion.setKey(key.getKey());
194+
deletions.add(deletion);
195+
}
237196
}
197+
return new DeleteObjectsResult(deletions);
238198
}
239199
}

0 commit comments

Comments
 (0)