diff --git a/src/main/java/ninja/Bucket.java b/src/main/java/ninja/Bucket.java
index f08804e..4c71727 100644
--- a/src/main/java/ninja/Bucket.java
+++ b/src/main/java/ninja/Bucket.java
@@ -136,22 +136,26 @@ public boolean exists() {
*
* If the underlying directory already exists, nothing happens.
*
- * @return true if the folder for the bucket was created successfully and if it was missing before
+ * @return true if the folder for the bucket was created successfully or existed before, false else
*/
public boolean create() {
- if (folder.exists() || !folder.mkdirs()) {
+ if (folder.exists()) {
+ return true;
+ }
+
+ if (!folder.mkdirs()) {
return false;
}
// having successfully created the folder, write the version marker
writeVersion();
- return true;
+ return folder.isDirectory();
}
/**
* Deletes the bucket and all of its contents.
*
- * @return true if all files of the bucket and the bucket itself was deleted successfully, false otherwise.
+ * @return true if all files of the bucket and the bucket itself were deleted successfully, false else
*/
public boolean delete() {
if (!folder.exists()) {
@@ -161,8 +165,8 @@ public boolean delete() {
try {
sirius.kernel.commons.Files.delete(folder.toPath());
return true;
- } catch (IOException e) {
- Exceptions.handle(e);
+ } catch (IOException exception) {
+ Exceptions.handle(Storage.LOG, exception);
return false;
}
}
@@ -189,8 +193,8 @@ public void outputObjectsV1(XMLStructuredOutput output,
output.property("Prefix", prefix);
try {
walkFileTreeOurWay(folder.toPath(), visitor);
- } catch (IOException e) {
- throw Exceptions.handle(e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
output.property("IsTruncated", limit > 0 && visitor.getCount() > limit);
output.endOutput();
@@ -218,8 +222,8 @@ public void outputObjectsV2(XMLStructuredOutput output,
output.property("Prefix", prefix);
try {
walkFileTreeOurWay(folder.toPath(), visitor);
- } catch (IOException e) {
- throw Exceptions.handle(e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
output.property("IsTruncated", limit > 0 && visitor.getCount() > limit);
output.property("KeyCount", visitor.getCount());
@@ -239,32 +243,33 @@ private void walkFileTreeOurWay(Path path, FileVisitor super Path> visitor) th
}
try (Stream children = Files.list(path)) {
- children.filter(p -> filterObjects(p.toFile())).sorted(Bucket::compareUtf8Binary).forEach(p -> {
- try {
- BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class);
- visitor.visitFile(p, attrs);
- } catch (IOException e) {
- throw Exceptions.handle(e);
- }
- });
+ children.filter(childPath -> filterObjects(childPath.toFile()))
+ .sorted(Bucket::compareUtf8Binary)
+ .forEach(childPath -> {
+ try {
+ visitor.visitFile(childPath, Files.readAttributes(childPath, BasicFileAttributes.class));
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
+ }
+ });
}
}
- private static int compareUtf8Binary(Path p1, Path p2) {
- String s1 = StoredObject.decodeKey(p1.getFileName().toString());
- String s2 = StoredObject.decodeKey(p2.getFileName().toString());
+ private static int compareUtf8Binary(Path path1, Path path2) {
+ String string1 = StoredObject.decodeKey(path1.getFileName().toString());
+ String string2 = StoredObject.decodeKey(path2.getFileName().toString());
- byte[] b1 = s1.getBytes(StandardCharsets.UTF_8);
- byte[] b2 = s2.getBytes(StandardCharsets.UTF_8);
+ byte[] bytes1 = string1.getBytes(StandardCharsets.UTF_8);
+ byte[] bytes2 = string2.getBytes(StandardCharsets.UTF_8);
// unless we upgrade to java 9+ offering Arrays.compare(...), we need to compare the arrays manually :(
- int length = Math.min(b1.length, b2.length);
- for (int i = 0; i < length; ++i) {
- if (b1[i] != b2[i]) {
- return Byte.compare(b1[i], b2[i]);
+ int length = Math.min(bytes1.length, bytes2.length);
+ for (int index = 0; index < length; ++index) {
+ if (bytes1[index] != bytes2[index]) {
+ return Byte.compare(bytes1[index], bytes2[index]);
}
}
- return b1.length - b2.length;
+ return bytes1.length - bytes2.length;
}
/**
@@ -278,26 +283,35 @@ public boolean isPrivate() {
/**
* Marks the bucket as only privately accessible, i.e. non-public.
+ *
+ * @return true if the bucket is now only privately accessible, false else
*/
- public void makePrivate() {
+ public boolean makePrivate() {
if (publicMarker.exists()) {
sirius.kernel.commons.Files.delete(publicMarker);
publicAccessCache.put(getName(), false);
}
+
+ return !publicMarker.exists();
}
/**
* Marks the bucket as publicly accessible.
+ *
+ * @return true if the bucket is now publicly accessible, false else
*/
- public void makePublic() {
+ public boolean makePublic() {
if (!publicMarker.exists()) {
try {
new FileOutputStream(publicMarker).close();
- } catch (IOException e) {
- throw Exceptions.handle(Storage.LOG, e);
+ } catch (IOException exception) {
+ Exceptions.handle(Storage.LOG, exception);
+ return false;
}
}
publicAccessCache.put(getName(), true);
+
+ return publicMarker.exists();
}
/**
@@ -334,15 +348,15 @@ public StoredObject getObject(String key) {
*/
public List getObjects(@Nullable String query, Limit limit) {
try (Stream stream = Files.list(folder.toPath())) {
- return stream.filter(p -> filterObjects(p.toFile()))
+ return stream.filter(path -> filterObjects(path.toFile()))
.sorted(Bucket::compareUtf8Binary)
.map(Path::toFile)
.filter(currentFile -> isMatchingObject(query, currentFile))
.filter(limit.asPredicate())
.map(StoredObject::new)
.toList();
- } catch (IOException e) {
- throw Exceptions.handle(e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
}
@@ -357,8 +371,8 @@ public int countObjects(@Nullable String query) {
return Math.toIntExact(stream.map(Path::toFile)
.filter(currentFile -> isMatchingObject(query, currentFile))
.count());
- } catch (IOException e) {
- throw Exceptions.handle(e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
}
@@ -392,8 +406,8 @@ private int parseVersion() {
try {
// parse the version from the version marker file
return Integer.parseInt(Strings.join(Files.readAllLines(versionMarker.toPath()), "\n").trim());
- } catch (IOException e) {
- throw Exceptions.handle(Storage.LOG, e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
}
@@ -410,8 +424,8 @@ protected void writeVersion() {
try {
// write the version into the version marker file
Files.write(versionMarker.toPath(), Collections.singletonList(String.valueOf(version)));
- } catch (IOException e) {
- throw Exceptions.handle(Storage.LOG, e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(Storage.LOG, exception);
}
}
@@ -470,7 +484,7 @@ public static boolean isValidName(@Nullable String name) {
if (IP_ADDRESS_PATTERN.matcher(name).matches() && InetAddress.getByName(name) != null) {
return false;
}
- } catch (Exception e) {
+ } catch (Exception ignored) {
// ignore this, we want the conversion to fail and thus to end up here
}
diff --git a/src/main/java/ninja/NinjaController.java b/src/main/java/ninja/NinjaController.java
index 7c44100..65257ed 100644
--- a/src/main/java/ninja/NinjaController.java
+++ b/src/main/java/ninja/NinjaController.java
@@ -157,7 +157,12 @@ public void bucket(WebContext webContext, String bucketName) {
return;
}
- bucket.create();
+ if (!bucket.create()) {
+ throw Exceptions.createHandled()
+ .to(Storage.LOG)
+ .withDirectMessage("Failed creating bucket. Missing file system permission?")
+ .handle();
+ }
UserContext.message(Message.info().withTextMessage("Bucket successfully created."));
webContext.respondWith().redirectTemporarily("/ui/" + bucket.getEncodedName());
@@ -181,7 +186,12 @@ public void bucket(WebContext webContext, String bucketName) {
// handle /ui/[bucket]?make-public
if (webContext.hasParameter("make-public")) {
- bucket.makePublic();
+ if (!bucket.makePublic()) {
+ throw Exceptions.createHandled()
+ .to(Storage.LOG)
+ .withDirectMessage("Failed making bucket public. Missing file system permission?")
+ .handle();
+ }
UserContext.message(Message.info().withTextMessage("ACLs successfully changed"));
webContext.respondWith().redirectTemporarily(address);
@@ -190,7 +200,12 @@ public void bucket(WebContext webContext, String bucketName) {
// handle /ui/[bucket]?make-private
if (webContext.hasParameter("make-private")) {
- bucket.makePrivate();
+ if (!bucket.makePrivate()) {
+ throw Exceptions.createHandled()
+ .to(Storage.LOG)
+ .withDirectMessage("Failed making bucket private. Missing file system permission?")
+ .handle();
+ }
UserContext.message(Message.info().withTextMessage("ACLs successfully changed"));
webContext.respondWith().redirectTemporarily(address);
@@ -199,7 +214,12 @@ public void bucket(WebContext webContext, String bucketName) {
// handle /ui/[bucket]?delete
if (webContext.hasParameter("delete")) {
- bucket.delete();
+ if (!bucket.delete()) {
+ throw Exceptions.createHandled()
+ .to(Storage.LOG)
+ .withDirectMessage("Failed deleting bucket. Missing file system permission?")
+ .handle();
+ }
UserContext.message(Message.info().withTextMessage("Bucket successfully deleted."));
webContext.respondWith().redirectTemporarily("/ui");
diff --git a/src/main/java/ninja/S3Dispatcher.java b/src/main/java/ninja/S3Dispatcher.java
index 67eb12d..1065f72 100644
--- a/src/main/java/ninja/S3Dispatcher.java
+++ b/src/main/java/ninja/S3Dispatcher.java
@@ -92,6 +92,10 @@ public class S3Dispatcher implements WebDispatcher {
private static final String RESPONSE_BUCKET = "Bucket";
private static final String ERROR_MULTIPART_UPLOAD_DOES_NOT_EXIST = "Multipart Upload does not exist";
private static final String ERROR_BUCKET_DOES_NOT_EXIST = "Bucket does not exist";
+ private static final String ERROR_BUCKET_IS_NOT_EMPTY = "Bucket is not empty";
+ private static final String ERROR_BUCKET_ALREADY_OWNED_BY_YOU = "Bucket already owned by you";
+ private static final String ERROR_FILE_SYSTEM_ACCESS =
+ "General problems accessing the file system — Permissions set correctly?";
private static final String PATH_DELIMITER = "/";
private static class S3Request {
@@ -161,9 +165,9 @@ private static class S3Request {
builder.add('.' + myself.getHostAddress());
builder.add('.' + myself.getHostName());
builder.add('.' + myself.getCanonicalHostName());
- } catch (Exception e) {
+ } catch (Exception exception) {
// reaching this point, we failed to resolve the local host name. tant pis.
- Exceptions.ignore(e);
+ Exceptions.ignore(exception);
}
DOMAINS = builder.build();
@@ -350,14 +354,14 @@ private String getAuthHash(WebContext webContext) {
}
String authentication =
Strings.isEmpty(authorizationHeaderValue.getString()) ? "" : authorizationHeaderValue.getString();
- Matcher m = AWS_AUTH_PATTERN.matcher(authentication);
- if (m.matches()) {
- return m.group(2);
+ Matcher matcher = AWS_AUTH_PATTERN.matcher(authentication);
+ if (matcher.matches()) {
+ return matcher.group(2);
}
- m = AWS_AUTH4_PATTERN.matcher(authentication);
- if (m.matches()) {
- return m.group(7);
+ matcher = AWS_AUTH4_PATTERN.matcher(authentication);
+ if (matcher.matches()) {
+ return matcher.group(7);
}
return null;
@@ -466,17 +470,47 @@ private void bucket(WebContext webContext, String bucketName) {
if (!bucket.exists()) {
signalObjectError(webContext, bucketName, null, S3ErrorCode.NoSuchBucket, ERROR_BUCKET_DOES_NOT_EXIST);
} else {
- bucket.delete();
+ if (bucket.countObjects("") > 0) {
+ signalObjectError(webContext,
+ bucketName,
+ null,
+ S3ErrorCode.BucketNotEmpty,
+ ERROR_BUCKET_IS_NOT_EMPTY);
+ return;
+ }
+
+ if (!bucket.delete()) {
+ signalObjectError(webContext,
+ bucketName,
+ null,
+ S3ErrorCode.InternalError,
+ ERROR_FILE_SYSTEM_ACCESS);
+ return;
+ }
+
signalObjectSuccess(webContext);
webContext.respondWith().status(HttpResponseStatus.OK);
}
} else if (HttpMethod.PUT.equals(method)) {
- bucket.create();
+ if (bucket.exists()) {
+ signalObjectError(webContext,
+ bucketName,
+ null,
+ S3ErrorCode.BucketAlreadyOwnedByYou,
+ ERROR_BUCKET_ALREADY_OWNED_BY_YOU);
+ return;
+ }
+
+ if (!bucket.create()) {
+ signalObjectError(webContext, bucketName, null, S3ErrorCode.InternalError, ERROR_FILE_SYSTEM_ACCESS);
+ return;
+ }
// in order to allow creation of public buckets, we support a single canned access control list
String cannedAccessControlList = webContext.getHeader("x-amz-acl");
- if (Strings.areEqual(cannedAccessControlList, "public-read-write")) {
- bucket.makePublic();
+ if (Strings.areEqual(cannedAccessControlList, "public-read-write") && !bucket.makePublic()) {
+ signalObjectError(webContext, bucketName, null, S3ErrorCode.InternalError, ERROR_FILE_SYSTEM_ACCESS);
+ return;
}
signalObjectSuccess(webContext);
@@ -573,7 +607,14 @@ private boolean checkObjectRequest(WebContext webContext, Bucket bucket, String
if (!bucket.exists()) {
if (storage.isAutocreateBuckets()) {
- bucket.create();
+ if (!bucket.create()) {
+ signalObjectError(webContext,
+ bucket.getName(),
+ id,
+ S3ErrorCode.InternalError,
+ ERROR_FILE_SYSTEM_ACCESS);
+ return false;
+ }
} else {
signalObjectError(webContext,
bucket.getName(),
@@ -903,13 +944,14 @@ private void startMultipartUpload(WebContext webContext, Bucket bucket, String i
}
private void storePropertiesInUploadDir(Map properties, String uploadId) {
- Properties props = new Properties();
- properties.forEach(props::setProperty);
- try (FileOutputStream propsOut = new FileOutputStream(new File(getUploadDir(uploadId),
- TEMPORARY_PROPERTIES_FILENAME))) {
- props.store(propsOut, "");
- } catch (IOException e) {
- Exceptions.handle(e);
+ Properties clonedProperties = new Properties();
+ properties.forEach(clonedProperties::setProperty);
+
+ try (FileOutputStream outputStream = new FileOutputStream(new File(getUploadDir(uploadId),
+ TEMPORARY_PROPERTIES_FILENAME))) {
+ clonedProperties.store(outputStream, "");
+ } catch (IOException exception) {
+ Exceptions.handle(exception);
}
}
@@ -946,12 +988,12 @@ private void multiObject(WebContext webContext, String uploadId, String partNumb
.setHeader(HTTP_HEADER_NAME_ETAG, etag)
.addHeader(HttpHeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS, HTTP_HEADER_NAME_ETAG)
.status(HttpResponseStatus.OK);
- } catch (IOException e) {
+ } catch (IOException exception) {
errorSynthesizer.synthesiseError(webContext,
null,
null,
S3ErrorCode.InternalError,
- Exceptions.handle(e).getMessage());
+ Exceptions.handle(exception).getMessage());
}
}
@@ -987,8 +1029,8 @@ private void completeMultipartUpload(WebContext webContext,
});
try {
reader.parse(in);
- } catch (IOException e) {
- Exceptions.handle(e);
+ } catch (IOException exception) {
+ Exceptions.handle(exception);
}
File file = combineParts(id,
@@ -1030,8 +1072,8 @@ private void completeMultipartUpload(WebContext webContext,
out.property("Key", id);
out.property(HTTP_HEADER_NAME_ETAG, etag);
out.endOutput();
- } catch (IOException e) {
- Exceptions.ignore(e);
+ } catch (IOException exception) {
+ Exceptions.ignore(exception);
errorSynthesizer.synthesiseError(webContext,
null,
null,
@@ -1041,9 +1083,9 @@ private void completeMultipartUpload(WebContext webContext,
}
private void commitPropertiesFromUploadDir(String uploadId, StoredObject object) throws IOException {
- File propsFile = new File(getUploadDir(uploadId), TEMPORARY_PROPERTIES_FILENAME);
- if (propsFile.exists()) {
- Files.move(propsFile, object.getPropertiesFile());
+ File propertiesFile = new File(getUploadDir(uploadId), TEMPORARY_PROPERTIES_FILENAME);
+ if (propertiesFile.exists()) {
+ Files.move(propertiesFile, object.getPropertiesFile());
}
}
@@ -1060,11 +1102,12 @@ private File combineParts(String id, String uploadId, List parts) {
file.getName(), file.getAbsolutePath());
}
- try (FileChannel out = new FileOutputStream(file).getChannel()) {
- combine(parts, out);
+ try (FileOutputStream outStream = new FileOutputStream(file);
+ FileChannel outChannel = outStream.getChannel()) {
+ combine(parts, outChannel);
}
- } catch (IOException e) {
- throw Exceptions.handle(e);
+ } catch (IOException exception) {
+ throw Exceptions.handle(exception);
}
return file;
@@ -1072,9 +1115,9 @@ private File combineParts(String id, String uploadId, List parts) {
private void combine(List parts, FileChannel out) throws IOException {
for (File part : parts) {
- try (RandomAccessFile raf = new RandomAccessFile(part, "r")) {
- FileChannel channel = raf.getChannel();
- out.write(channel.map(FileChannel.MapMode.READ_ONLY, 0, raf.length()));
+ try (RandomAccessFile randomAccessToPart = new RandomAccessFile(part, "r")) {
+ out.write(randomAccessToPart.getChannel()
+ .map(FileChannel.MapMode.READ_ONLY, 0, randomAccessToPart.length()));
}
}
}
@@ -1094,8 +1137,8 @@ private void abortMultipartUpload(WebContext webContext, String uploadId) {
private static void delete(File file) {
try {
sirius.kernel.commons.Files.delete(file.toPath());
- } catch (IOException e) {
- Exceptions.handle(Storage.LOG, e);
+ } catch (IOException exception) {
+ Exceptions.handle(Storage.LOG, exception);
}
}
diff --git a/src/main/java/ninja/errors/S3ErrorCode.java b/src/main/java/ninja/errors/S3ErrorCode.java
index a9006a3..095c275 100644
--- a/src/main/java/ninja/errors/S3ErrorCode.java
+++ b/src/main/java/ninja/errors/S3ErrorCode.java
@@ -18,12 +18,80 @@
@SuppressWarnings("java:S115")
@Explain("We use the proper names as defined in the AWS API")
public enum S3ErrorCode {
- AccessDenied(HttpResponseStatus.FORBIDDEN), BadDigest(HttpResponseStatus.BAD_REQUEST),
- IncompleteBody(HttpResponseStatus.BAD_REQUEST), InternalError(HttpResponseStatus.INTERNAL_SERVER_ERROR),
- InvalidDigest(HttpResponseStatus.BAD_REQUEST), InvalidRequest(HttpResponseStatus.BAD_REQUEST),
- NoSuchBucket(HttpResponseStatus.NOT_FOUND), NoSuchBucketPolicy(HttpResponseStatus.NOT_FOUND),
- NoSuchKey(HttpResponseStatus.NOT_FOUND), NoSuchLifecycleConfiguration(HttpResponseStatus.NOT_FOUND),
- NoSuchUpload(HttpResponseStatus.NOT_FOUND), SignatureDoesNotMatch(HttpResponseStatus.FORBIDDEN);
+ /**
+ * Access denied.
+ */
+ AccessDenied(HttpResponseStatus.FORBIDDEN),
+
+ /**
+ * During upload, the specified checksum value did not match the calculated one.
+ */
+ BadDigest(HttpResponseStatus.BAD_REQUEST),
+
+ /**
+ * The specified bucket name is already in use by somebody else.
+ */
+ BucketAlreadyExists(HttpResponseStatus.CONFLICT),
+
+ /**
+ * The specified bucket name is already in use by yourself.
+ */
+ BucketAlreadyOwnedByYou(HttpResponseStatus.CONFLICT),
+
+ /**
+ * The specified bucket can not be deleted as it is not empty.
+ */
+ BucketNotEmpty(HttpResponseStatus.CONFLICT),
+
+ /**
+ * During upload, less than the number of bytes specified have been transmitted.
+ */
+ IncompleteBody(HttpResponseStatus.BAD_REQUEST),
+
+ /**
+ * An internal error has occurred.
+ */
+ InternalError(HttpResponseStatus.INTERNAL_SERVER_ERROR),
+
+ /**
+ * The specified checksum value is invalid.
+ */
+ InvalidDigest(HttpResponseStatus.BAD_REQUEST),
+
+ /**
+ * The current request is not valid.
+ */
+ InvalidRequest(HttpResponseStatus.BAD_REQUEST),
+
+ /**
+ * The specified bucket does not exist.
+ */
+ NoSuchBucket(HttpResponseStatus.NOT_FOUND),
+
+ /**
+ * The specified bucket does not have a policy.
+ */
+ NoSuchBucketPolicy(HttpResponseStatus.NOT_FOUND),
+
+ /**
+ * The specified key does not exist.
+ */
+ NoSuchKey(HttpResponseStatus.NOT_FOUND),
+
+ /**
+ * The specified lifecycle configuration does not exist.
+ */
+ NoSuchLifecycleConfiguration(HttpResponseStatus.NOT_FOUND),
+
+ /**
+ * The specified multipart upload does not exist.
+ */
+ NoSuchUpload(HttpResponseStatus.NOT_FOUND),
+
+ /**
+ * The provided request signature does not match the one calculated by the server.
+ */
+ SignatureDoesNotMatch(HttpResponseStatus.FORBIDDEN);
private final HttpResponseStatus httpStatusCode;
diff --git a/src/test/java/BaseAWSSpec.groovy b/src/test/java/BaseAWSSpec.groovy
index 8fb7bd2..44d4387 100644
--- a/src/test/java/BaseAWSSpec.groovy
+++ b/src/test/java/BaseAWSSpec.groovy
@@ -44,15 +44,26 @@ abstract class BaseAWSSpec extends BaseSpecification {
client.putObject(bucketName, key, new ByteArrayInputStream(data), metadata)
}
+ /**
+ * Before each test, delete all buckets and their objects. This allows to run the test based on a clean state.
+ */
+ def setup() {
+ def client = getClient()
+
+ client.listBuckets().stream().forEach {
+ def bucket = it
+ client.listObjects(bucket.getName()).getObjectSummaries().stream().forEach {
+ client.deleteObject(bucket.getName(), it.getKey())
+ }
+ client.deleteBucket(bucket.getName())
+ }
+ }
+
def "HEAD of non-existing bucket as expected"() {
given:
def bucketName = "does-not-exist"
def client = getClient()
- when:
- if (client.doesBucketExist(bucketName)) {
- client.deleteBucket(bucketName)
- }
- then:
+ expect:
!client.doesBucketExist(bucketName)
!client.doesBucketExistV2(bucketName)
}
@@ -62,13 +73,12 @@ abstract class BaseAWSSpec extends BaseSpecification {
def bucketName = DEFAULT_BUCKET_NAME
def client = getClient()
when:
- if (client.doesBucketExist(bucketName)) {
- client.deleteBucket(bucketName)
- }
client.createBucket(bucketName)
then:
client.doesBucketExist(bucketName)
client.doesBucketExistV2(bucketName)
+ cleanup:
+ client.deleteBucket(bucketName)
}
def "DELETE of non-existing bucket as expected"() {
@@ -76,14 +86,11 @@ abstract class BaseAWSSpec extends BaseSpecification {
def bucketName = "does-not-exist"
def client = getClient()
when:
- if (client.doesBucketExist(bucketName)) {
- client.deleteBucket(bucketName)
- }
- and:
client.deleteBucket(bucketName)
then:
AmazonS3Exception e = thrown()
e.statusCode == 404
+ and:
!client.doesBucketExist(bucketName)
!client.doesBucketExistV2(bucketName)
}
@@ -93,11 +100,12 @@ abstract class BaseAWSSpec extends BaseSpecification {
def bucketName = DEFAULT_BUCKET_NAME
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
- client.deleteBucket(bucketName)
+ client.createBucket(bucketName)
+ and:
+ client.doesBucketExist(bucketName)
then:
+ client.deleteBucket(bucketName)
+ and:
!client.doesBucketExist(bucketName)
}
@@ -107,9 +115,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key = DEFAULT_KEY
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
File file = File.createTempFile("test", "")
file.delete()
@@ -125,8 +131,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
tm.download(bucketName, key, download).waitForCompletion()
then:
Files.toString(file, StandardCharsets.UTF_8) == Files.toString(download, StandardCharsets.UTF_8)
- and:
+ cleanup:
client.deleteObject(bucketName, key)
+ client.deleteBucket(bucketName)
}
def "PUT and then GET work as expected"() {
@@ -135,9 +142,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key = DEFAULT_KEY
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, key, "Test")
def content = new String(
@@ -152,8 +157,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
content == "Test"
and:
downloadedData == "Test"
- and:
+ cleanup:
client.deleteObject(bucketName, key)
+ client.deleteBucket(bucketName)
}
def "PUT and then LIST work as expected"() {
@@ -163,9 +169,6 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key2 = DEFAULT_KEY + "/Zwei"
def client = getClient()
when:
- if (client.doesBucketExist(bucketName)) {
- client.deleteBucket(bucketName)
- }
client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, key1, "Eins")
@@ -176,6 +179,10 @@ abstract class BaseAWSSpec extends BaseSpecification {
summaries.size() == 2
summaries.get(0).getKey() == key1
summaries.get(1).getKey() == key2
+ cleanup:
+ client.deleteObject(bucketName, key1)
+ client.deleteObject(bucketName, key2)
+ client.deleteBucket(bucketName)
}
// reported in https://github.com/scireum/s3ninja/issues/180
@@ -187,9 +194,6 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key3 = "a/key/with a different/prefix/Drei"
def client = getClient()
when:
- if (client.doesBucketExist(bucketName)) {
- client.deleteBucket(bucketName)
- }
client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, key1, "Eins")
@@ -201,6 +205,11 @@ abstract class BaseAWSSpec extends BaseSpecification {
summaries.size() == 2
summaries.get(0).getKey() == key1
summaries.get(1).getKey() == key2
+ cleanup:
+ client.deleteObject(bucketName, key1)
+ client.deleteObject(bucketName, key2)
+ client.deleteObject(bucketName, key3)
+ client.deleteBucket(bucketName)
}
def "PUT and then DELETE work as expected"() {
@@ -209,16 +218,16 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key = DEFAULT_KEY
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, key, "Test")
- client.deleteBucket(bucketName)
+ client.deleteObject(bucketName, key)
client.getObject(bucketName, key)
then:
AmazonS3Exception e = thrown()
e.statusCode == 404
+ cleanup:
+ client.deleteBucket(bucketName)
}
def "MultipartUpload and then GET work as expected"() {
@@ -226,17 +235,15 @@ abstract class BaseAWSSpec extends BaseSpecification {
def bucketName = DEFAULT_BUCKET_NAME
def key = DEFAULT_KEY
def client = getClient()
- when:
+ and:
def transfer = TransferManagerBuilder.standard().
withS3Client(client).
withMultipartUploadThreshold(1).
withMinimumUploadPartSize(1).build()
def meta = new ObjectMetadata()
def message = "Test".getBytes(StandardCharsets.UTF_8)
- and:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ when:
+ client.createBucket(bucketName)
and:
meta.setContentLength(message.length)
meta.addUserMetadata("userdata", "test123")
@@ -249,8 +256,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
then:
content == "Test"
userdata == "test123"
- and:
+ cleanup:
client.deleteObject(bucketName, key)
+ client.deleteBucket(bucketName)
}
def "MultipartUpload and then DELETE work as expected"() {
@@ -266,18 +274,18 @@ abstract class BaseAWSSpec extends BaseSpecification {
def meta = new ObjectMetadata()
def message = "Test".getBytes(StandardCharsets.UTF_8)
and:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
meta.setContentLength(message.length)
def upload = transfer.upload(bucketName, key, new ByteArrayInputStream(message), meta)
upload.waitForUploadResult()
- client.deleteBucket(bucketName)
+ client.deleteObject(bucketName, key)
client.getObject(bucketName, key)
then:
AmazonS3Exception e = thrown()
e.statusCode == 404
+ cleanup:
+ client.deleteBucket(bucketName)
}
def "PUT on presigned URL without signed chunks works as expected"() {
@@ -286,9 +294,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key = DEFAULT_KEY
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
def content = "NotSigned"
and:
@@ -310,8 +316,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
String downloadedData = new String(ByteStreams.toByteArray(c.getInputStream()), StandardCharsets.UTF_8)
then:
downloadedData == content
- and:
+ cleanup:
client.deleteObject(bucketName, key)
+ client.deleteBucket(bucketName)
}
// reported in https://github.com/scireum/s3ninja/issues/153
@@ -321,9 +328,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key = DEFAULT_KEY
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, key, "Test")
def content = new String(
@@ -342,8 +347,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
content == "Test"
and:
downloadedData == "Test"
- and:
+ cleanup:
client.deleteObject(bucketName, key)
+ client.deleteBucket(bucketName)
}
// reported in https://github.com/scireum/s3ninja/issues/181
@@ -355,6 +361,8 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key3 = DEFAULT_KEY + "/Drei"
def client = getClient()
when:
+ client.createBucket(bucketName)
+ and:
putObjectWithContent(bucketName, key1, "Eins")
putObjectWithContent(bucketName, key2, "Zwei")
putObjectWithContent(bucketName, key3, "Drei")
@@ -367,8 +375,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
def listing = client.listObjects(bucketName)
listing.getObjectSummaries().size() == 1
listing.getObjectSummaries().get(0).getKey() == key3
- and:
+ cleanup:
client.deleteObject(bucketName, key3)
+ client.deleteBucket(bucketName)
}
// reported in https://github.com/scireum/s3ninja/issues/214
@@ -380,6 +389,8 @@ abstract class BaseAWSSpec extends BaseSpecification {
def key3 = DEFAULT_KEY + "/Drei"
def client = getClient()
when:
+ client.createBucket(bucketName)
+ and:
putObjectWithContent(bucketName, key1, "Eins")
putObjectWithContent(bucketName, key2, "Zwei")
putObjectWithContent(bucketName, key3, "Drei")
@@ -389,10 +400,11 @@ abstract class BaseAWSSpec extends BaseSpecification {
result.getObjectSummaries().size() == 2
result.getObjectSummaries().get(0).getKey() == key1
result.getObjectSummaries().get(1).getKey() == key2
- and:
+ cleanup:
client.deleteObject(bucketName, key1)
client.deleteObject(bucketName, key2)
client.deleteObject(bucketName, key3)
+ client.deleteBucket(bucketName)
}
// reported in https://github.com/scireum/s3ninja/issues/209
@@ -412,7 +424,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
connection.getResponseCode() == 200
connection.getContentLengthLong() == content.getBytes(StandardCharsets.UTF_8).length
connection.disconnect()
- and:
+ cleanup:
client.deleteObject(bucketName, key)
client.deleteBucket(bucketName)
}
@@ -426,9 +438,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
def content = "I am pointless text content, but I deserve to exist twice and will thus be copied!"
def client = getClient()
when:
- if (!client.doesBucketExist(bucketName)) {
- client.createBucket(bucketName)
- }
+ client.createBucket(bucketName)
and:
putObjectWithContent(bucketName, keyFrom, content)
and:
@@ -440,7 +450,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
String downloadedData = new String(ByteStreams.toByteArray(c.getInputStream()), StandardCharsets.UTF_8)
then:
downloadedData == content
- and:
+ cleanup:
client.deleteObject(bucketName, keyFrom)
client.deleteObject(bucketName, keyTo)
client.deleteBucket(bucketName)
@@ -455,13 +465,9 @@ abstract class BaseAWSSpec extends BaseSpecification {
def content = "I am pointless text content, but I deserve to exist twice and will thus be copied!"
def client = getClient()
when:
- if (!client.doesBucketExist(bucketNameFrom)) {
- client.createBucket(bucketNameFrom)
- }
+ client.createBucket(bucketNameFrom)
and:
- if (!client.doesBucketExist(bucketNameTo)) {
- client.createBucket(bucketNameTo)
- }
+ client.createBucket(bucketNameTo)
and:
putObjectWithContent(bucketNameFrom, key, content)
and:
@@ -473,7 +479,7 @@ abstract class BaseAWSSpec extends BaseSpecification {
String downloadedData = new String(ByteStreams.toByteArray(c.getInputStream()), StandardCharsets.UTF_8)
then:
downloadedData == content
- and:
+ cleanup:
client.deleteObject(bucketNameFrom, key)
client.deleteBucket(bucketNameFrom)
client.deleteObject(bucketNameTo, key)