-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
misc: e2e tests for s3 mrap #1193
Conversation
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
services/build.gradle.kts
Outdated
@@ -75,6 +75,10 @@ subprojects { | |||
implementation(libraries.kotlin.test.junit5) | |||
implementation(project(":tests:e2e-test-util")) | |||
implementation(libraries.slf4j.simple) | |||
implementation("aws.sdk.kotlin:s3control:+") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: We can't just add these to every e2e test. For example sts
has it's own e2e tests and taking a dependency on it will cause classpath conflicts (or more likely unpredictable class loading behavior). We're going to need to probably give this more thought.
return accountId ?: throw Exception("Unable to get AWS account ID") | ||
} | ||
|
||
internal suspend fun createS3Bucket( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Why can't we re-use the existing S3Utils.getBucket()
?
@@ -116,3 +131,243 @@ object S3TestUtils { | |||
return connection.responseCode | |||
} | |||
} | |||
|
|||
internal suspend fun getAccountId(): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: Move to function on S3Utils
so these are all grouped.
region = "us-west-2" | ||
} | ||
|
||
val accountId = sts.getCallerIdentity( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: this operation has a default parameter, no need to construct an empty request object
val accountId = sts.getCallerIdentity()
val createRequestToken = s3ControlClient.createMultiRegionAccessPoint( | ||
CreateMultiRegionAccessPointRequest { | ||
accountId = testAccountId | ||
details = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: use the DSL builder
s3control.createMultiRegionAccessPoint {
accountId = "accont"
details {
name = "access-point-name"
regions = listOf(
Region { bucket = "one" },
Region { bucket = "two" }
)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not going to mark this everywhere but there are several other spots I'd have the same feedback
) { | ||
val startTime = System.currentTimeMillis() | ||
|
||
while (System.currentTimeMillis() - startTime < timeLimit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: You don't need this, just use withTimeout (or withTimeoutOrNull depending)
return | ||
} | ||
|
||
TimeUnit.SECONDS.sleep(10L) // Avoid constant status checks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: It doesn't matter here since these are all runBlocking
tests but this is not coroutine friendly, use delay instead
multiRegionAccessPointArn, | ||
keyForObject, | ||
) | ||
} catch (exception: Throwable) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: This is unnecessary, any exception will fail the test with the given cause.
private fun cleanUpMrapTest(): Unit = runBlocking { | ||
println("Cleaning up MutliRegionAccessPointTest tests") | ||
|
||
val accountId = getAccountId() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: You could get this once in setupMrapTest
and share it across tests.
e.g.
private lateinit var accountId: String
@BeforeAll
private fun setupMrapTest() : Unit = runBlocking {
...
accountId = getAccountId()
}
private val keyForObject = "test.txt" | ||
|
||
@BeforeAll | ||
private fun setUpMrapTest(): Unit = runBlocking { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: setup
and cleanup
are one word.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Counter-nit: "setup" and "cleanup" are nouns. "Set up" and "Clean up" are verbs, which is the preferred form of method names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: My thought on this is to just call it setup
, the test class is enough of a hint of what you are setting up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests look good. I would recommend keeping new functions out of S3TestUtils
unless you think other S3 tests will use it. For MRAP-specific things, it makes more sense to define them in your specific test file
services/build.gradle.kts
Outdated
import aws.sdk.kotlin.gradle.kmp.* | ||
import aws.sdk.kotlin.gradle.kmp.kotlin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why did this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IntelliJ does that, it doesn't like the *
imports. I'll leave it how it was
|
||
@BeforeAll | ||
private fun setUpMrapTest(): Unit = runBlocking { | ||
println("Setting up MutliRegionAccessPointTest tests") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unnecessary print
|
||
@AfterAll | ||
private fun cleanUpMrapTest(): Unit = runBlocking { | ||
println("Cleaning up MutliRegionAccessPointTest tests") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unnecessary print
internal suspend fun getAccountId(): String { | ||
println("Getting account ID") | ||
|
||
val sts = StsClient { | ||
region = "us-west-2" | ||
} | ||
|
||
val accountId = sts.getCallerIdentity( | ||
GetCallerIdentityRequest { }, | ||
).account | ||
|
||
sts.close() | ||
|
||
return accountId ?: throw Exception("Unable to get AWS account ID") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correctness: because this uses StsClient
it's not an "S3 test util", I'd recommend moving it
internal suspend fun createS3Bucket( | ||
s3Client: S3Client, | ||
bucketName: String, | ||
location: BucketLocationConstraint, | ||
) { | ||
println("Creating S3 bucket: $bucketName") | ||
|
||
s3Client.createBucket( | ||
CreateBucketRequest { | ||
bucket = bucketName | ||
createBucketConfiguration = | ||
CreateBucketConfiguration { | ||
locationConstraint = location | ||
} | ||
}, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: This seems really similar to getBucketWithPrefix
. Can that function be adapted to fit your requirements instead of making a new function that does roughly the same thing?
internal suspend fun deleteS3Bucket( | ||
s3Client: S3Client, | ||
bucketName: String, | ||
) { | ||
println("Deleting S3 bucket: $bucketName") | ||
|
||
s3Client.deleteBucket( | ||
DeleteBucketRequest { | ||
bucket = bucketName | ||
}, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here, this function is simple enough, I recommend removing it
ListObjectsV2Request { | ||
bucket = bucketName | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplification: pass prefix = keyName
to this call and S3 will do the searching for you
internal fun closeClients( | ||
vararg clients: SdkClient, | ||
) { | ||
clients.forEach { client -> | ||
client.close() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another function that's simple enough to remove
keyForObject, | ||
) | ||
} catch (exception: Throwable) { | ||
println("Test failed with exception: ${exception.cause}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unnecessary print
) | ||
}.message.shouldContain("SIGV4_ASYMMETRIC support is not yet implemented for the default signer.") | ||
} catch (exception: Throwable) { | ||
println("Test failed with exception: ${exception.cause}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unnecessary print
A new generated diff is ready to view. |
A new generated diff is ready to view. |
} | ||
} | ||
|
||
internal suspend fun multiRegionAccessPointWasCreated( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be done using a get operation and then looking at the error we get if the MRAP doesn't exist. The only issue is it returns a rather generic S3ControlException
and all we would have to differentiate a non existent MRAP from a failure would be the error message. Which doesn't seem right as it could change. Either way I can take that approach if that's what everyone thinks
@@ -115,4 +145,126 @@ object S3TestUtils { | |||
|
|||
return connection.responseCode | |||
} | |||
|
|||
internal suspend fun getAccountId(): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't think of a better place for this function. It seemed out of place in the tests class instead of here with the other utils. Placing it in a STS test utils folder could be another option but it seems a little odd since it's used for S3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this seems fine, especially now that S3 E2E tests are guaranteed to have a dependency on STS
A new generated diff is ready to view. |
private suspend fun waitUntilMultiRegionAccessPointOperationCompletes( | ||
s3ControlClient: S3ControlClient, | ||
request: String, | ||
duration: Int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: withTimeout
also accepts a Duration
, instead of passing this as an integer you could just be writing 5.minutes
in the calling code and accept a Duration
. Also probably rename to something like timeoutAfter
return@withTimeout | ||
} | ||
|
||
if (status == "FAILED") throw Exception("$operation operation failed.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style:
check(status != "FAILED") { "$operation operation failed")
@@ -88,6 +88,26 @@ subprojects { | |||
description = "Run e2e service tests" | |||
group = "verification" | |||
|
|||
if (project.name == "s3") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this works but this is getting ugly. I'm thinking we'll be revisiting the structure around e2e tests soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree,I think we could refactor some of this into functions if it starts getting used more.
private val keyForObject = "test.txt" | ||
|
||
@BeforeAll | ||
private fun setUpMrapTest(): Unit = runBlocking { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: My thought on this is to just call it setup
, the test class is enough of a hint of what you are setting up
var testBucket = if (region != null) { | ||
buckets?.firstOrNull { | ||
client.getBucketLocation { | ||
bucket = it | ||
expectedBucketOwner = accountId | ||
}.locationConstraint?.value == region && it.startsWith(prefix) | ||
} | ||
} else { | ||
buckets?.firstOrNull { it.startsWith(prefix) } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: possible simplification
var testBucket = buckets?.firstOrNull { bucketName ->
bucketName.startsWith(prefix) &&
region?.let {
client.getBucketLocation {
bucket = bucketName
expectedBucketOwner = accountId
}.locationConstraint?.value == region
} ?: true
}
@@ -115,4 +145,126 @@ object S3TestUtils { | |||
|
|||
return connection.responseCode | |||
} | |||
|
|||
internal suspend fun getAccountId(): String { | |||
println("Getting account ID") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: seems like an unnecessary print to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a couple of other print statements in the utils, why does this one seem unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because all the other prints have debuggable information like the bucket name, operation, etc. This one has nothing, so I think it's not super useful. It's totally not a blocking comment, just something I noticed
val search = s3Control.listMultiRegionAccessPoints { | ||
accountId = testAccountId | ||
}.accessPoints?.find { it.name == multiRegionAccessPointName } | ||
|
||
return search != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: simplification
return s3Control.listMultiRegionAccessPoints {
accountId = testAccountId
}.accessPoints?.any { it.name == multiRegionAccessPointName }
@@ -115,4 +145,126 @@ object S3TestUtils { | |||
|
|||
return connection.responseCode | |||
} | |||
|
|||
internal suspend fun getAccountId(): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this seems fine, especially now that S3 E2E tests are guaranteed to have a dependency on STS
Quality Gate passedIssues Measures |
A new generated diff is ready to view. |
Issue #
Description of changes
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.