1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.services.s3control; 17 18 import static org.assertj.core.api.Assertions.assertThat; 19 20 import java.util.Iterator; 21 import java.util.List; 22 import org.junit.BeforeClass; 23 import software.amazon.awssdk.core.ClientType; 24 import software.amazon.awssdk.core.interceptor.Context; 25 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 26 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 27 import software.amazon.awssdk.regions.Region; 28 import software.amazon.awssdk.services.s3.S3AsyncClient; 29 import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; 30 import software.amazon.awssdk.services.s3.S3Client; 31 import software.amazon.awssdk.services.s3.S3ClientBuilder; 32 import software.amazon.awssdk.services.s3.model.BucketLocationConstraint; 33 import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration; 34 import software.amazon.awssdk.services.s3.model.CreateBucketRequest; 35 import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; 36 import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; 37 import software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest; 38 import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; 39 import software.amazon.awssdk.services.s3.model.ListObjectsRequest; 40 import software.amazon.awssdk.services.s3.model.ListObjectsResponse; 41 import software.amazon.awssdk.services.s3.model.NoSuchBucketException; 42 import software.amazon.awssdk.services.s3.model.S3Exception; 43 import software.amazon.awssdk.services.s3.model.S3Object; 44 import software.amazon.awssdk.services.sts.StsClient; 45 import software.amazon.awssdk.testutils.Waiter; 46 import software.amazon.awssdk.testutils.service.AwsTestBase; 47 48 /** 49 * Base class for S3 Control integration tests. Loads AWS credentials from a properties 50 * file and creates an S3 client for callers to use. 51 */ 52 public class S3ControlIntegrationTestBase extends AwsTestBase { 53 54 protected static final Region DEFAULT_REGION = Region.US_WEST_2; 55 /** 56 * The S3 client for all tests to use. 57 */ 58 protected static S3Client s3; 59 60 protected static S3AsyncClient s3Async; 61 62 protected static StsClient sts; 63 64 protected static String accountId; 65 66 /** 67 * Loads the AWS account info for the integration tests and creates an S3 68 * client for tests to use. 69 */ 70 @BeforeClass setUp()71 public static void setUp() throws Exception { 72 s3 = s3ClientBuilder().build(); 73 s3Async = s3AsyncClientBuilder().build(); 74 sts = StsClient.builder() 75 .region(DEFAULT_REGION) 76 .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) 77 .build(); 78 accountId = sts.getCallerIdentity().account(); 79 } 80 s3ClientBuilder()81 protected static S3ClientBuilder s3ClientBuilder() { 82 return S3Client.builder() 83 .region(DEFAULT_REGION) 84 .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) 85 .overrideConfiguration(o -> o.addExecutionInterceptor( 86 new UserAgentVerifyingExecutionInterceptor("Apache", ClientType.SYNC))); 87 } 88 s3AsyncClientBuilder()89 protected static S3AsyncClientBuilder s3AsyncClientBuilder() { 90 return S3AsyncClient.builder() 91 .region(DEFAULT_REGION) 92 .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) 93 .overrideConfiguration(o -> o.addExecutionInterceptor( 94 new UserAgentVerifyingExecutionInterceptor("NettyNio", ClientType.ASYNC))); 95 } 96 createBucket(String bucketName)97 protected static void createBucket(String bucketName) { 98 createBucket(bucketName, 0); 99 } 100 createBucket(String bucketName, int retryCount)101 private static void createBucket(String bucketName, int retryCount) { 102 try { 103 s3.createBucket( 104 CreateBucketRequest.builder() 105 .bucket(bucketName) 106 .createBucketConfiguration( 107 CreateBucketConfiguration.builder() 108 .locationConstraint(BucketLocationConstraint.US_WEST_2) 109 .build()) 110 .build()); 111 } catch (S3Exception e) { 112 System.err.println("Error attempting to create bucket: " + bucketName); 113 if (e.awsErrorDetails().errorCode().equals("BucketAlreadyOwnedByYou")) { 114 System.err.printf("%s bucket already exists, likely leaked by a previous run\n", bucketName); 115 } else if (e.awsErrorDetails().errorCode().equals("TooManyBuckets")) { 116 System.err.println("Printing all buckets for debug:"); 117 s3.listBuckets().buckets().forEach(System.err::println); 118 if (retryCount < 2) { 119 System.err.println("Retrying..."); 120 createBucket(bucketName, retryCount + 1); 121 } else { 122 throw e; 123 } 124 } else { 125 throw e; 126 } 127 } 128 } 129 deleteBucketAndAllContents(String bucketName)130 protected static void deleteBucketAndAllContents(String bucketName) { 131 deleteBucketAndAllContents(s3, bucketName); 132 } 133 134 private static class UserAgentVerifyingExecutionInterceptor implements ExecutionInterceptor { 135 136 private final String clientName; 137 private final ClientType clientType; 138 UserAgentVerifyingExecutionInterceptor(String clientName, ClientType clientType)139 public UserAgentVerifyingExecutionInterceptor(String clientName, ClientType clientType) { 140 this.clientName = clientName; 141 this.clientType = clientType; 142 } 143 144 @Override beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes)145 public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) { 146 assertThat(context.httpRequest().firstMatchingHeader("User-Agent").get()).containsIgnoringCase("io/" + clientType.name()); 147 assertThat(context.httpRequest().firstMatchingHeader("User-Agent").get()).containsIgnoringCase("http/" + clientName); 148 } 149 } 150 deleteBucketAndAllContents(S3Client s3, String bucketName)151 public static void deleteBucketAndAllContents(S3Client s3, String bucketName) { 152 try { 153 System.out.println("Deleting S3 bucket: " + bucketName); 154 ListObjectsResponse response = Waiter.run(() -> s3.listObjects(r -> r.bucket(bucketName))) 155 .ignoringException(NoSuchBucketException.class) 156 .orFail(); 157 List<S3Object> objectListing = response.contents(); 158 159 if (objectListing != null) { 160 while (true) { 161 for (Iterator<?> iterator = objectListing.iterator(); iterator.hasNext(); ) { 162 S3Object objectSummary = (S3Object) iterator.next(); 163 s3.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(objectSummary.key()).build()); 164 } 165 166 if (response.isTruncated()) { 167 objectListing = s3.listObjects(ListObjectsRequest.builder() 168 .bucket(bucketName) 169 .marker(response.marker()) 170 .build()) 171 .contents(); 172 } else { 173 break; 174 } 175 } 176 } 177 178 179 ListObjectVersionsResponse versions = s3 180 .listObjectVersions(ListObjectVersionsRequest.builder().bucket(bucketName).build()); 181 182 if (versions.deleteMarkers() != null) { 183 versions.deleteMarkers().forEach(v -> s3.deleteObject(DeleteObjectRequest.builder() 184 .versionId(v.versionId()) 185 .bucket(bucketName) 186 .key(v.key()) 187 .build())); 188 } 189 190 if (versions.versions() != null) { 191 versions.versions().forEach(v -> s3.deleteObject(DeleteObjectRequest.builder() 192 .versionId(v.versionId()) 193 .bucket(bucketName) 194 .key(v.key()) 195 .build())); 196 } 197 198 s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucketName).build()); 199 } catch (Exception e) { 200 System.err.println("Failed to delete bucket: " + bucketName); 201 e.printStackTrace(); 202 } 203 } 204 } 205