44 */
55package aws.sdk.kotlin.e2etest
66
7- import aws.sdk.kotlin.e2etest.S3TestUtils.createMultiRegionAccessPoint
87import aws.sdk.kotlin.e2etest.S3TestUtils.deleteBucketAndAllContents
9- import aws.sdk.kotlin.e2etest.S3TestUtils.deleteMultiRegionAccessPoint
108import aws.sdk.kotlin.e2etest.S3TestUtils.getAccountId
119import aws.sdk.kotlin.e2etest.S3TestUtils.getBucketWithPrefix
12- import aws.sdk.kotlin.e2etest.S3TestUtils.getMultiRegionAccessPointArn
13- import aws.sdk.kotlin.e2etest.S3TestUtils.multiRegionAccessPointWasCreated
1410import aws.sdk.kotlin.services.s3.S3Client
1511import aws.sdk.kotlin.services.s3.deleteObject
1612import aws.sdk.kotlin.services.s3.putObject
1713import aws.sdk.kotlin.services.s3.withConfig
1814import aws.sdk.kotlin.services.s3control.S3ControlClient
15+ import aws.sdk.kotlin.services.s3control.createMultiRegionAccessPoint
16+ import aws.sdk.kotlin.services.s3control.deleteMultiRegionAccessPoint
17+ import aws.sdk.kotlin.services.s3control.describeMultiRegionAccessPointOperation
18+ import aws.sdk.kotlin.services.s3control.getMultiRegionAccessPoint
19+ import aws.sdk.kotlin.services.s3control.model.Region
20+ import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigner
21+ import aws.smithy.kotlin.runtime.auth.awssigning.DefaultAwsSigner
1922import aws.smithy.kotlin.runtime.auth.awssigning.crt.CrtAwsSigner
2023import aws.smithy.kotlin.runtime.http.auth.SigV4AsymmetricAuthScheme
24+ import kotlinx.coroutines.delay
2125import kotlinx.coroutines.runBlocking
26+ import kotlinx.coroutines.withTimeout
2227import org.junit.jupiter.api.AfterAll
2328import org.junit.jupiter.api.BeforeAll
2429import org.junit.jupiter.api.TestInstance
25- import kotlin.test.Test
30+ import org.junit.jupiter.params.ParameterizedTest
31+ import org.junit.jupiter.params.provider.Arguments
32+ import org.junit.jupiter.params.provider.MethodSource
33+ import java.util.stream.Stream
34+ import kotlin.time.Duration
35+ import kotlin.time.Duration.Companion.minutes
36+ import kotlin.time.Duration.Companion.seconds
2637
2738private const val MRAP_BUCKET_PREFIX = " s3-mrap-test-bucket-"
39+ private const val MULTI_REGION_ACCESS_POINT_NAME = " aws-sdk-for-kotlin-test-multi-region-access-point"
40+ private const val TEST_OBJECT_KEY = " test.txt"
2841
2942@TestInstance(TestInstance .Lifecycle .PER_CLASS )
3043class MutliRegionAccessPointTest {
31- private val s3West = S3Client { region = " us-west-2" }
32- private val s3East = s3West.withConfig { region = " us-east-2" }
33- private val s3SigV4a = s3West.withConfig { authSchemes = listOf (SigV4AsymmetricAuthScheme (CrtAwsSigner )) }
34- private val s3Control = S3ControlClient { region = " us-west-2" }
35-
36- private val multiRegionAccessPoint = " aws-sdk-for-kotlin-test-multi-region-access-point"
37- private val objectKey = " test.txt"
44+ private lateinit var s3West: S3Client
45+ private lateinit var s3East: S3Client
46+ private lateinit var s3Control: S3ControlClient
3847
3948 private lateinit var accountId: String
4049 private lateinit var multiRegionAccessPointArn: String
4150 private lateinit var usWestBucket: String
4251 private lateinit var usEastBucket: String
4352
4453 @BeforeAll
45- private fun setUp (): Unit = runBlocking {
54+ fun setup (): Unit = runBlocking {
55+ s3West = S3Client { region = " us-west-2" }
56+ s3East = S3Client { region = " us-east-2" }
57+ s3Control = S3ControlClient { region = " us-west-2" }
58+
4659 accountId = getAccountId()
4760 usWestBucket = getBucketWithPrefix(s3West, MRAP_BUCKET_PREFIX , " us-west-2" , accountId)
4861 usEastBucket = getBucketWithPrefix(s3East, MRAP_BUCKET_PREFIX , " us-east-2" , accountId)
4962
50- createMultiRegionAccessPoint(
51- s3Control,
52- multiRegionAccessPoint,
53- usWestBucket,
54- usEastBucket,
63+ multiRegionAccessPointArn = s3Control.createMultiRegionAccessPoint(
64+ MULTI_REGION_ACCESS_POINT_NAME ,
5565 accountId,
66+ listOf (usWestBucket, usEastBucket)
5667 )
57-
58- multiRegionAccessPointArn =
59- getMultiRegionAccessPointArn(
60- s3Control,
61- multiRegionAccessPoint,
62- accountId,
63- )
6468 }
6569
6670 @AfterAll
67- private fun cleanUp (): Unit = runBlocking {
68- if (multiRegionAccessPointWasCreated(s3Control, multiRegionAccessPoint, accountId)) {
69- deleteMultiRegionAccessPoint(s3Control, multiRegionAccessPoint, accountId)
70- }
71+ fun cleanup (): Unit = runBlocking {
72+ s3Control.deleteMultiRegionAccessPoint(MULTI_REGION_ACCESS_POINT_NAME , accountId)
7173
7274 deleteBucketAndAllContents(s3West, usWestBucket)
7375 deleteBucketAndAllContents(s3East, usEastBucket)
7476
7577 s3West.close()
7678 s3East.close()
77- s3SigV4a.close()
7879 s3Control.close()
7980 }
8081
81- @Test
82- fun testMultiRegionAccessPointOperation (): Unit = runBlocking {
82+ @ParameterizedTest
83+ @MethodSource(" signerProvider" )
84+ fun testMultiRegionAccessPointOperation (signer : AwsSigner ): Unit = runBlocking {
85+ println (" Testing multi-region access point operations with $signer " )
86+
87+ val s3SigV4a = s3West.withConfig {
88+ authSchemes = listOf (SigV4AsymmetricAuthScheme (signer))
89+ }
90+
8391 s3SigV4a.putObject {
8492 bucket = multiRegionAccessPointArn
85- key = objectKey
93+ key = TEST_OBJECT_KEY
8694 }
8795
8896 s3SigV4a.deleteObject {
8997 bucket = multiRegionAccessPointArn
90- key = objectKey
98+ key = TEST_OBJECT_KEY
9199 }
92100 }
101+
102+ fun signerProvider (): Stream <Arguments > = Stream .of(
103+ Arguments .of(DefaultAwsSigner ),
104+ Arguments .of(CrtAwsSigner ),
105+ )
106+ }
107+
108+ /* *
109+ * Create a multi-region access point named [name] in account [accountId] with [buckets] buckets.
110+ * @return the ARN of the multi-region access point that was created
111+ */
112+ private suspend fun S3ControlClient.createMultiRegionAccessPoint (
113+ name : String ,
114+ accountId : String ,
115+ buckets : List <String >,
116+ ): String {
117+ println (" Creating multi-region access point: $name " )
118+
119+ val requestTokenArn = checkNotNull(createMultiRegionAccessPoint {
120+ this .accountId = accountId
121+ details {
122+ this .name = name
123+ this .regions = buckets.map { Region { bucket = it } }
124+ }
125+ }.requestTokenArn) { " createMultiRegionAccessPoint requestTokenArn was unexpectedly null" }
126+
127+ waitUntilOperationCompletes(" createMultiRegionAccessPoint" , accountId, requestTokenArn, 10 .minutes)
128+
129+ return getMultiRegionAccessPointArn(name, accountId)
93130}
131+
132+ private suspend fun S3ControlClient.getMultiRegionAccessPointArn (
133+ name : String ,
134+ accountId : String
135+ ): String {
136+ return getMultiRegionAccessPoint {
137+ this .name = name
138+ this .accountId = accountId
139+ }.accessPoint?.alias?.let {
140+ " arn:aws:s3::$accountId :accesspoint/$it "
141+ } ? : throw IllegalStateException (" Failed to get ARN for multi-region access point $name " )
142+ }
143+
144+ private suspend fun S3ControlClient.deleteMultiRegionAccessPoint (
145+ name : String ,
146+ accountId : String
147+ ) {
148+ println (" Deleting multi-region access point $name " )
149+
150+ val requestTokenArn = checkNotNull(deleteMultiRegionAccessPoint {
151+ this .accountId = accountId
152+ details {
153+ this .name = name
154+ }
155+ }.requestTokenArn) { " deleteMultiRegionAccessPoint requestTokenArn was unexpectedly null" }
156+
157+ waitUntilOperationCompletes(" deleteMultiRegionAccessPoint" , accountId, requestTokenArn, 5 .minutes)
158+ }
159+
160+ /* *
161+ * Continuously poll the status of [requestTokenArn] until its status is "SUCCEEDED" or [timeout] duration has passed.
162+ */
163+ private suspend fun S3ControlClient.waitUntilOperationCompletes (
164+ operation : String ,
165+ accountId : String ,
166+ requestTokenArn : String ,
167+ timeout : Duration ,
168+ ) = withTimeout(timeout) {
169+ var status: String? = null
170+
171+ while (true ) {
172+ val latestStatus = describeMultiRegionAccessPointOperation {
173+ this .accountId = accountId
174+ this .requestTokenArn = requestTokenArn
175+ }.asyncOperation?.requestStatus
176+
177+ when (latestStatus) {
178+ " SUCCEEDED" -> {
179+ println (" $operation operation succeeded." )
180+ return @withTimeout
181+ }
182+ " FAILED" -> throw IllegalStateException (" $operation operation failed" )
183+ else -> {
184+ if (status == null || latestStatus != status) {
185+ println (" Waiting for $operation to complete. Status: $latestStatus " )
186+ status = latestStatus
187+ }
188+ }
189+ }
190+
191+ delay(10 .seconds) // Avoid constant status checks
192+ }
193+ }
0 commit comments