/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.aws.s3;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.services.s3.model.CreateBucketRequest;
import com.amazonaws.services.s3.model.DeleteBucketRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import org.apache.nifi.util.file.FileUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.junit.Assert.fail;
/**
* Base class for S3 Integration Tests. Establishes a bucket and helper methods for creating test scenarios
*
* @see ITDeleteS3Object
* @see ITFetchS3Object
* @see ITPutS3Object
* @see ITListS3
*/
public abstract class AbstractS3IT {
protected final static String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
protected final static String SAMPLE_FILE_RESOURCE_NAME = "/hello.txt";
protected final static String REGION = System.getProperty("it.aws.region", "us-west-1");
// Adding REGION to bucket prevents errors of
// "A conflicting conditional operation is currently in progress against this resource."
// when bucket is rapidly added/deleted and consistency propagation causes this error.
// (Should not be necessary if REGION remains static, but added to prevent future frustration.)
// [see http://stackoverflow.com/questions/13898057/aws-error-message-a-conflicting-conditional-operation-is-currently-in-progress]
protected final static String BUCKET_NAME = "test-bucket-00000000-0000-0000-0000-123456789021-" + REGION;
// Static so multiple Tests can use same client
protected static AmazonS3Client client;
@BeforeClass
public static void oneTimeSetup() {
// Creates a client and bucket for this test
final FileInputStream fis;
try {
fis = new FileInputStream(CREDENTIALS_FILE);
} catch (FileNotFoundException e1) {
fail("Could not open credentials file " + CREDENTIALS_FILE + ": " + e1.getLocalizedMessage());
return;
}
try {
final PropertiesCredentials credentials = new PropertiesCredentials(fis);
client = new AmazonS3Client(credentials);
if (client.doesBucketExist(BUCKET_NAME)) {
fail("Bucket " + BUCKET_NAME + " exists. Choose a different bucket name to continue test");
}
CreateBucketRequest request = REGION.contains("east")
? new CreateBucketRequest(BUCKET_NAME) // See https://github.com/boto/boto3/issues/125
: new CreateBucketRequest(BUCKET_NAME, REGION);
client.createBucket(request);
} catch (final AmazonS3Exception e) {
fail("Can't create the key " + BUCKET_NAME + ": " + e.getLocalizedMessage());
} catch (final IOException e) {
fail("Caught IOException preparing tests: " + e.getLocalizedMessage());
} finally {
FileUtils.closeQuietly(fis);
}
if (!client.doesBucketExist(BUCKET_NAME)) {
fail("Setup incomplete, tests will fail");
}
}
@AfterClass
public static void oneTimeTearDown() {
// Empty the bucket before deleting it.
try {
ObjectListing objectListing = client.listObjects(BUCKET_NAME);
while (true) {
for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
client.deleteObject(BUCKET_NAME, objectSummary.getKey());
}
if (objectListing.isTruncated()) {
objectListing = client.listNextBatchOfObjects(objectListing);
} else {
break;
}
}
DeleteBucketRequest dbr = new DeleteBucketRequest(BUCKET_NAME);
client.deleteBucket(dbr);
} catch (final AmazonS3Exception e) {
System.err.println("Unable to delete bucket " + BUCKET_NAME + e.toString());
}
if (client.doesBucketExist(BUCKET_NAME)) {
Assert.fail("Incomplete teardown, subsequent tests might fail");
}
}
protected void putTestFile(String key, File file) throws AmazonS3Exception {
PutObjectRequest putRequest = new PutObjectRequest(BUCKET_NAME, key, file);
client.putObject(putRequest);
}
protected void putTestFileEncrypted(String key, File file) throws AmazonS3Exception, FileNotFoundException {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION);
PutObjectRequest putRequest = new PutObjectRequest(BUCKET_NAME, key, new FileInputStream(file), objectMetadata);
client.putObject(putRequest);
}
protected Path getResourcePath(String resourceName) {
Path path = null;
try {
path = Paths.get(getClass().getResource(resourceName).toURI());
} catch (URISyntaxException e) {
Assert.fail("Resource: " + resourceName + " does not exist" + e.getLocalizedMessage());
}
return path;
}
protected File getFileFromResourceName(String resourceName) {
URI uri = null;
try {
uri = this.getClass().getResource(resourceName).toURI();
} catch (URISyntaxException e) {
Assert.fail("Cannot proceed without File : " + resourceName);
}
return new File(uri);
}
}