Skip to content

Commit d1e71f3

Browse files
authored
JavaV2: Check if bucket exists example that provides replacement for V1's doesBucketExistV2 (#7057)
* Create code example for Java V1's AmazonS3Client#doesBucketExistV2()
1 parent 10b28f4 commit d1e71f3

File tree

6 files changed

+276
-1
lines changed

6 files changed

+276
-1
lines changed

.doc_gen/metadata/s3_metadata.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,3 +3524,20 @@ s3_Scenario_DownloadS3Directory:
35243524
- s3.tm.java2.download-s3-directories.main
35253525
services:
35263526
s3: {}
3527+
s3_Scenario_DoesBucketExist:
3528+
title: Check if a bucket exists
3529+
title_abbrev: Check if a bucket exists
3530+
synopsis: check if a bucket exists.
3531+
category: Scenarios
3532+
languages:
3533+
Java:
3534+
versions:
3535+
- sdk_version: 2
3536+
github: javav2/example_code/s3
3537+
sdkguide:
3538+
excerpts:
3539+
- description: You can use the following <code>doesBucketExists</code> method as a replacement for the the &Java; V1 <ulink url="https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/AmazonS3Client.html#doesBucketExistV2-java.lang.String-">AmazonS3Client#doesBucketExistV2(String)</ulink> method.
3540+
snippet_tags:
3541+
- s3.java2.does-bucket-exist-main
3542+
services:
3543+
s3: {GetBucketAcl}

javav2/example_code/s3/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Code excerpts that show you how to call individual service functions.
7979
Code examples that show you how to accomplish a specific task by calling multiple
8080
functions within the same service.
8181

82+
- [Check if a bucket exists](src/main/java/com/example/s3/DoesBucketExist.java)
8283
- [Delete incomplete multipart uploads](src/main/java/com/example/s3/AbortMultipartUploadExamples.java)
8384
- [Download S3 'directories'](src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java)
8485
- [Download objects to a local directory](src/main/java/com/example/s3/transfermanager/DownloadToDirectory.java)
@@ -129,6 +130,18 @@ This example shows you how to do the following:
129130
<!--custom.basics.s3_Scenario_GettingStarted.end-->
130131

131132

133+
#### Check if a bucket exists
134+
135+
This example shows you how to check if a bucket exists.
136+
137+
138+
<!--custom.scenario_prereqs.s3_Scenario_DoesBucketExist.start-->
139+
<!--custom.scenario_prereqs.s3_Scenario_DoesBucketExist.end-->
140+
141+
142+
<!--custom.scenarios.s3_Scenario_DoesBucketExist.start-->
143+
<!--custom.scenarios.s3_Scenario_DoesBucketExist.end-->
144+
132145
#### Delete incomplete multipart uploads
133146

134147
This example shows you how to how to delete or stop incomplete Amazon S3 multipart uploads.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.example.s3;
4+
// snippet-start:[s3.java2.does-bucket-exist-main]
5+
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
import software.amazon.awssdk.awscore.exception.AwsServiceException;
9+
import software.amazon.awssdk.http.HttpStatusCode;
10+
import software.amazon.awssdk.services.s3.S3Client;
11+
import software.amazon.awssdk.utils.Validate;
12+
13+
public class DoesBucketExist {
14+
private static final Logger logger = LoggerFactory.getLogger(DoesBucketExist.class);
15+
16+
public static void main(String[] args) {
17+
DoesBucketExist doesBucketExist = new DoesBucketExist();
18+
19+
final S3Client s3SyncClient = S3Client.builder().build();
20+
final String bucketName = "amzn-s3-demo-bucket"; // Change to the bucket name that you want to check.
21+
22+
boolean exists = doesBucketExist.doesBucketExist(bucketName, s3SyncClient);
23+
logger.info("Bucket exists: {}", exists);
24+
}
25+
26+
/**
27+
* Checks if the specified bucket exists. Amazon S3 buckets are named in a global namespace; use this method to
28+
* determine if a specified bucket name already exists, and therefore can't be used to create a new bucket.
29+
* <p>
30+
* Internally this method uses the <a
31+
* href="https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getBucketAcl(java.util.function.Consumer)">S3Client.getBucketAcl(String)</a>
32+
* operation to determine whether the bucket exists.
33+
* <p>
34+
* This method is equivalent to the AWS SDK for Java V1's <a
35+
* href="https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/AmazonS3Client.html#doesBucketExistV2-java.lang.String-">AmazonS3Client#doesBucketExistV2(String)</a>.
36+
*
37+
* @param bucketName The name of the bucket to check.
38+
* @param s3SyncClient An <code>S3Client</code> instance. The method checks for the bucket in the AWS Region
39+
* configured on the instance.
40+
* @return The value true if the specified bucket exists in Amazon S3; the value false if there is no bucket in
41+
* Amazon S3 with that name.
42+
*/
43+
public boolean doesBucketExist(String bucketName, S3Client s3SyncClient) {
44+
try {
45+
Validate.notEmpty(bucketName, "The bucket name must not be null or an empty string.", "");
46+
s3SyncClient.getBucketAcl(r -> r.bucket(bucketName));
47+
return true;
48+
} catch (AwsServiceException ase) {
49+
// A redirect error or an AccessDenied exception means the bucket exists but it's not in this region
50+
// or we don't have permissions to it.
51+
if ((ase.statusCode() == HttpStatusCode.MOVED_PERMANENTLY) || "AccessDenied".equals(ase.awsErrorDetails().errorCode())) {
52+
return true;
53+
}
54+
if (ase.statusCode() == HttpStatusCode.NOT_FOUND) {
55+
return false;
56+
}
57+
throw ase;
58+
}
59+
}
60+
}
61+
// snippet-end:[s3.java2.does-bucket-exist-main]

javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3DirectoriesDownloader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
public class S3DirectoriesDownloader {
4444
private static final Logger logger = LoggerFactory.getLogger(S3DirectoriesDownloader.class);
45-
public final String bucketName = "amzn-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name.
45+
public final String bucketName = "junk-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name.
4646
public URI destinationPathURI;
4747
private final Set<String> downloadedFileNameSet = new HashSet<>();
4848
private final String destinationDirName = "downloadDirectory";
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
3+
<AccessControlList>
4+
<Grant>
5+
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
6+
<DisplayName>owner</DisplayName>
7+
<ID>dad784318ac45facc6a8c74b9b44f18afd317c85fa8776eeb2b000c8942bc8d9</ID>
8+
</Grantee>
9+
<Permission>FULL_CONTROL</Permission>
10+
</Grant>
11+
<Grant>
12+
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
13+
<DisplayName>my-grantee</DisplayName>
14+
<URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI>
15+
</Grantee>
16+
<Permission>READ_ACP</Permission>
17+
</Grant>
18+
</AccessControlList>
19+
<Owner>
20+
<ID>dad784318ac45facc6a8c74b9b44f18afd317c85fa8776eeb2b000c8942bc8d9</ID>
21+
</Owner>
22+
</AccessControlPolicy>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.example.s3;
4+
//
5+
6+
import org.junit.jupiter.api.Tag;
7+
import org.junit.jupiter.api.Test;
8+
import software.amazon.awssdk.policybuilder.iam.IamConditionKey;
9+
import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
10+
import software.amazon.awssdk.policybuilder.iam.IamEffect;
11+
import software.amazon.awssdk.policybuilder.iam.IamPolicy;
12+
import software.amazon.awssdk.policybuilder.iam.IamPrincipalType;
13+
import software.amazon.awssdk.regions.Region;
14+
import software.amazon.awssdk.services.iam.IamClient;
15+
import software.amazon.awssdk.services.iam.model.PutRolePolicyResponse;
16+
import software.amazon.awssdk.services.s3.S3Client;
17+
import software.amazon.awssdk.services.sts.StsClient;
18+
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider;
19+
20+
import java.util.List;
21+
import java.util.UUID;
22+
23+
import static org.junit.jupiter.api.Assertions.assertFalse;
24+
import static org.junit.jupiter.api.Assertions.assertTrue;
25+
26+
class DoesBucketExistTest {
27+
private static final String ROLE_NAME = "minimal-s3-perms-role";
28+
private static final String POLICY_NAME = "minimum-s3-ability-policy";
29+
private static final IamClient iamClient = IamClient.builder().build();
30+
31+
@Test
32+
@Tag("IntegrationTest")
33+
void doesBucketExist_exists_but_not_in_client_region_should_return_true() {
34+
35+
S3Client usWestS3Client = S3Client.builder().region(Region.US_WEST_2).build();
36+
final String bucketName = "my-us-west2-bucket-" + UUID.randomUUID();
37+
createBucket(usWestS3Client, bucketName);
38+
39+
DoesBucketExist doesBucketExist = new DoesBucketExist();
40+
S3Client euCentralS3Client = S3Client.builder().region(Region.EU_CENTRAL_1).build();
41+
boolean exists = doesBucketExist.doesBucketExist(bucketName, euCentralS3Client);
42+
assertTrue(exists);
43+
44+
deleteBucket(usWestS3Client, bucketName);
45+
}
46+
47+
@Test
48+
@Tag("IntegrationTest")
49+
void doesBucketExist_does_not_exist_should_return_false() {
50+
51+
DoesBucketExist doesBucketExist = new DoesBucketExist();
52+
final String bucketName = "xx-xx-xxxx-xxxx" + UUID.randomUUID();
53+
54+
boolean exists = doesBucketExist.doesBucketExist(bucketName, S3Client.create());
55+
assertFalse(exists);
56+
}
57+
58+
@Test
59+
@Tag("IntegrationTest")
60+
void doesBucketExist_returns_true_when_bucket_exists_but_caller_does_not_have_permission() {
61+
StsClient stsClient = StsClient.create();
62+
createAssumableRole(stsClient);
63+
64+
S3Client s3Client = S3Client.create();
65+
final String bucketName = "my-bucket-" + UUID.randomUUID();
66+
createBucket(s3Client, bucketName);
67+
68+
try {
69+
70+
String roleArn = iamClient.getRole(b -> b.roleName(ROLE_NAME)).role().arn();
71+
72+
S3Client s3ClientWithoutPermission = S3Client.builder()
73+
.credentialsProvider(StsAssumeRoleCredentialsProvider.builder()
74+
.stsClient(stsClient)
75+
.refreshRequest(arr -> arr
76+
.roleArn(roleArn)
77+
.roleSessionName("test-session"))
78+
.build())
79+
.build();
80+
DoesBucketExist doesBucketExist = new DoesBucketExist();
81+
boolean existsButNoAccess = doesBucketExist.doesBucketExist(bucketName, s3ClientWithoutPermission);
82+
assertTrue(existsButNoAccess);
83+
84+
boolean exists = doesBucketExist.doesBucketExist("non-existent-bucket" + UUID.randomUUID(), s3ClientWithoutPermission);
85+
assertFalse(exists);
86+
} finally {
87+
deleteBucket(s3Client, bucketName);
88+
deleteRole();
89+
90+
}
91+
}
92+
93+
private static void createBucket(S3Client s3Client, String bucketName) {
94+
s3Client.createBucket(b -> b.bucket(bucketName));
95+
s3Client.waiter().waitUntilBucketExists(b -> b.bucket(bucketName));
96+
}
97+
98+
private static void deleteBucket(S3Client s3Client, String bucketName) {
99+
s3Client.deleteBucket(b -> b.bucket(bucketName));
100+
s3Client.waiter().waitUntilBucketNotExists(b -> b.bucket(bucketName));
101+
}
102+
103+
104+
private static void createAssumableRole(StsClient stsClient) {
105+
final String accountID = stsClient.getCallerIdentity().account();
106+
107+
IamPolicy trustIamPolicyForAnyoneInSameAccount = IamPolicy.builder()
108+
.addStatement(statement -> statement
109+
.effect(IamEffect.ALLOW)
110+
.addPrincipal(principal -> principal
111+
.type(IamPrincipalType.AWS)
112+
.id("arn:aws:iam::" + accountID + ":root")
113+
)
114+
.addAction("sts:AssumeRole")
115+
.addConditions(IamConditionOperator.STRING_EQUALS,
116+
IamConditionKey.create("aws:PrincipalType"),
117+
List.of("User", "AssumedRole")
118+
).addConditions(IamConditionOperator.STRING_LIKE,
119+
"aws:userId",
120+
List.of("AIDAX*", "AROA*:*", "AIDA*:*"))
121+
)
122+
.build();
123+
124+
iamClient.createRole(crb -> crb
125+
.roleName(ROLE_NAME)
126+
.assumeRolePolicyDocument(trustIamPolicyForAnyoneInSameAccount.toJson())
127+
);
128+
129+
IamPolicy getBucketLocationOnlyPolicy = IamPolicy.builder()
130+
.addStatement(statement -> statement
131+
.effect(IamEffect.ALLOW)
132+
.addAction("s3:GetBucketLocation")
133+
.addResource("arn:aws:s3:::*")
134+
)
135+
.build();
136+
137+
iamClient.putRolePolicy(prprb -> prprb
138+
.roleName(ROLE_NAME)
139+
.policyName(POLICY_NAME)
140+
.policyDocument(getBucketLocationOnlyPolicy.toJson()));
141+
142+
// Add a delay for the role to propagate.
143+
try {
144+
Thread.sleep(8000);
145+
} catch (InterruptedException e) {
146+
throw new RuntimeException(e);
147+
}
148+
149+
}
150+
151+
private static void deleteRole(){
152+
153+
iamClient.deleteRolePolicy(drbrb -> drbrb
154+
.roleName(ROLE_NAME)
155+
.policyName(POLICY_NAME));
156+
157+
iamClient.deleteRole(drb -> drb
158+
.roleName(ROLE_NAME)
159+
);
160+
}
161+
}
162+

0 commit comments

Comments
 (0)