Skip to content

Commit d813a05

Browse files
authored
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 dccd43a commit d813a05

File tree

5 files changed

+282
-223
lines changed

5 files changed

+282
-223
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 & 148 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,197 +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;
66-
}
67-
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-
}
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;
8275
}
8376

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-
throw new AmazonS3Exception("[" + blobName + "] does not exist.");
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;
134118
}
135119

136-
// the HTTP request attribute is irrelevant for reading
137-
S3ObjectInputStream stream = new S3ObjectInputStream(
138-
blobs.get(blobName), null, false);
120+
ObjectMetadata metadata = new ObjectMetadata();
121+
metadata.setContentLength(content.length);
122+
139123
S3Object s3Object = new S3Object();
140-
s3Object.setObjectContent(stream);
124+
s3Object.setObjectContent(new S3ObjectInputStream(new ByteArrayInputStream(content), null, false));
125+
s3Object.setKey(blobName);
126+
s3Object.setObjectMetadata(metadata);
127+
141128
return s3Object;
142129
}
143130

144131
@Override
145-
public ObjectListing listObjects(ListObjectsRequest listObjectsRequest)
146-
throws AmazonClientException, AmazonServiceException {
147-
simulateS3SocketConnection();
148-
MockObjectListing list = new MockObjectListing();
149-
list.setTruncated(false);
150-
151-
String blobName;
152-
String prefix = listObjectsRequest.getPrefix();
153-
154-
ArrayList<S3ObjectSummary> mockObjectSummaries = new ArrayList<>();
155-
156-
for (Map.Entry<String, InputStream> blob : blobs.entrySet()) {
157-
blobName = blob.getKey();
158-
S3ObjectSummary objectSummary = new S3ObjectSummary();
159-
160-
if (prefix.isEmpty() || blobName.startsWith(prefix)) {
161-
objectSummary.setKey(blobName);
162-
163-
try {
164-
objectSummary.setSize(getSize(blob.getValue()));
165-
} catch (IOException e) {
166-
throw new AmazonS3Exception("Object listing " +
167-
"failed for blob [" + blob.getKey() + "]");
168-
}
169-
170-
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);
171146
}
172147
}
173-
174-
list.setObjectSummaries(mockObjectSummaries);
175-
return list;
148+
return listing;
176149
}
177150

178151
@Override
179-
public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest)
180-
throws AmazonClientException, AmazonServiceException {
181-
simulateS3SocketConnection();
182-
String sourceBlobName = copyObjectRequest.getSourceKey();
183-
String targetBlobName = copyObjectRequest.getDestinationKey();
184-
185-
if (!blobs.containsKey(sourceBlobName)) {
186-
throw new AmazonS3Exception("Source blob [" +
187-
sourceBlobName + "] does not exist.");
188-
}
152+
public CopyObjectResult copyObject(final CopyObjectRequest request) throws AmazonClientException {
153+
assertThat(request.getSourceBucketName(), equalTo(bucket));
154+
assertThat(request.getDestinationBucketName(), equalTo(bucket));
155+
156+
final String sourceBlobName = request.getSourceKey();
189157

190-
if (blobs.containsKey(targetBlobName)) {
191-
throw new AmazonS3Exception("Target blob [" +
192-
targetBlobName + "] already exists.");
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;
193163
}
194164

195-
blobs.put(targetBlobName, blobs.get(sourceBlobName));
196-
return new CopyObjectResult(); // nothing is done with it
165+
blobs.put(request.getDestinationKey(), content);
166+
return new CopyObjectResult();
197167
}
198168

199169
@Override
200-
public void deleteObject(DeleteObjectRequest deleteObjectRequest)
201-
throws AmazonClientException, AmazonServiceException {
202-
simulateS3SocketConnection();
203-
String blobName = deleteObjectRequest.getKey();
204-
205-
if (!blobs.containsKey(blobName)) {
206-
throw new AmazonS3Exception("[" + blobName + "] does not exist.");
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;
207178
}
208-
209-
blobs.remove(blobName);
210179
}
211180

212-
private int getSize(InputStream stream) throws IOException {
213-
int size = stream.read(byteCounter);
214-
stream.reset(); // in case we ever need the size again
215-
return size;
216-
}
217-
218-
private class MockObjectListing extends ObjectListing {
219-
// the objectSummaries attribute in ObjectListing.java
220-
// is read-only, but we need to be able to write to it,
221-
// so we create a mock of it to work around this
222-
private List<S3ObjectSummary> mockObjectSummaries;
223-
224-
@Override
225-
public List<S3ObjectSummary> getObjectSummaries() {
226-
return mockObjectSummaries;
227-
}
228-
229-
private void setObjectSummaries(List<S3ObjectSummary> objectSummaries) {
230-
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+
}
231196
}
197+
return new DeleteObjectsResult(deletions);
232198
}
233199
}

0 commit comments

Comments
 (0)