diff --git a/CHANGELOG.md b/CHANGELOG.md index 2786a59..dddf9de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,13 @@ Dropping a requirement of a major version of a dependency is a new contract. ## [Unreleased] [Unreleased]: https://github.com/atlassian-labs/aws-resources/compare/release-1.17.0...master +### Added +- Add `Investment.Builder` +- Allow overriding the resource_owner label in `Investment.Builder` + +### Changed +- Changed resource_owner default label to point to current Atlassian JPT maintainer + ## [1.17.0] - 2024-06-14 [1.17.0]: https://github.com/atlassian-labs/aws-resources/compare/release-1.16.0...release-1.17.0 diff --git a/src/main/kotlin/com/atlassian/performance/tools/aws/api/Aws.kt b/src/main/kotlin/com/atlassian/performance/tools/aws/api/Aws.kt index a482f9c..8756dfc 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/aws/api/Aws.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/aws/api/Aws.kt @@ -111,12 +111,13 @@ class Aws private constructor( private val shortTermStorage: ProvisionedStack by lazy { StackFormula( - investment = Investment( + investment = Investment.Builder( useCase = "Transport files necessary to run the tests", - reuseKey = { "jpt-short-term-storage" }, - lifespan = Duration.ofDays(30), - disposable = false - ), + lifespan = Duration.ofDays(30) + ) + .disposable(false) + .reuseKey { "jpt-short-term-storage" } + .build(), cloudformationTemplate = readResourceText("aws/short-term-storage.yaml"), parameters = listOf( Parameter().withParameterKey("PermissionBoundaryPolicyARN") @@ -130,12 +131,13 @@ class Aws private constructor( private val customDatasetStorage: ProvisionedStack by lazy { StackFormula( - investment = Investment( + investment = Investment.Builder( useCase = "Store custom datasets", - reuseKey = { "jpt-custom-datasets-storage" }, - lifespan = Duration.ofDays(800), - disposable = false - ), + lifespan = Duration.ofDays(800) + ) + .disposable(false) + .reuseKey { "jpt-custom-datasets-storage" } + .build(), cloudformationTemplate = readResourceText("aws/custom-datasets-storage.yaml"), aws = this ).provision() diff --git a/src/main/kotlin/com/atlassian/performance/tools/aws/api/Investment.kt b/src/main/kotlin/com/atlassian/performance/tools/aws/api/Investment.kt index 19fe186..c7b4cc3 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/aws/api/Investment.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/aws/api/Investment.kt @@ -12,19 +12,39 @@ import com.amazonaws.services.ec2.model.Tag as Ec2Tag * - value proposition * - cost factors * - accountability tracking - * - * @param useCase the value proposition for the investment - * @param lifespan pessimistic estimate of the duration of the investment, higher values mean higher cost - * @param disposable if true, then the investment can be cancelled even before [lifespan] and get its cost reduced - * @param reuseKey if a new investment would have the same reuse key as an existing investment, the old one can be - * reused, reducing the cost */ -data class Investment( +data class Investment +private constructor( private val useCase: String, val lifespan: Duration, private val disposable: Boolean = true, - val reuseKey: () -> String = { "jpt-${UUID.randomUUID()}" } + val reuseKey: () -> String = { "jpt-${UUID.randomUUID()}" }, + private val resourceOwner: String = defaultResourceOwner ) { + /** + * + * @param useCase the value proposition for the investment + * @param lifespan pessimistic estimate of the duration of the investment, higher values mean higher cost + * @param disposable if true, then the investment can be cancelled even before [lifespan] and get its cost reduced + * @param reuseKey if a new investment would have the same reuse key as an existing investment, the old one can be + * reused, reducing the cost + */ + @Deprecated( + message = "Use Builder instead. Public constructor will be be removed in the next major version.", + replaceWith = ReplaceWith( + expression = "Investment.Builder(useCase = useCase, lifespan = lifespan)" + + ".disposable(disposable)" + + ".reuseKey(reuseKey)" + + ".build()" + ) + ) + constructor( + useCase: String, + lifespan: Duration, + disposable: Boolean = true, + reuseKey: () -> String = { "jpt-${UUID.randomUUID()}" } + ) : this(useCase, lifespan, disposable, reuseKey, defaultResourceOwner) + /** * @return tags useful for tracking accountability of the investment */ @@ -37,7 +57,7 @@ data class Investment( Tag("Name", "Jira Performance Tests"), Tag("service_name", useCase), Tag("business_unit", "Engineering-Server"), - Tag("resource_owner", "mgrzaslewicz") + Tag("resource_owner", resourceOwner) ) /** @@ -66,12 +86,55 @@ data class Investment( } } + /** + * Describes an investment: + * - value proposition + * - cost factors + * - accountability tracking + * + * @param useCase the value proposition for the investment + * @param lifespan pessimistic estimate of the duration of the investment, higher values mean higher cost + */ + class Builder( + private val useCase: String, + private val lifespan: Duration + ) { + private var disposable: Boolean = true + private var reuseKey: () -> String = { "jpt-${UUID.randomUUID()}" } + private var resourceOwner: String = defaultResourceOwner + + /** + * @param disposable if true, then the investment can be cancelled even before [lifespan] and get its cost reduced + */ + fun disposable(disposable: Boolean) = apply { this.disposable = disposable } + /** + * @param reuseKey if a new investment would have the same reuse key as an existing investment, the old one can be + * reused, reducing the cost + */ + fun reuseKey(reuseKey: () -> String) = apply { this.reuseKey = reuseKey } + /** + * @param resourceOwner AWS resource owner, defaults to current Atlassian JPT maintainer + */ + fun resourceOwner(resourceOwner: String) = apply { this.resourceOwner = resourceOwner } + + fun build(): Investment { + return Investment( + useCase = useCase, + lifespan = lifespan, + disposable = disposable, + reuseKey = reuseKey, + resourceOwner = resourceOwner + ) + } + } + companion object TagKeys { const val lifespanKey = "lifespan" private const val expiryKey = "expiry" const val userKey = "os_user_name" const val bambooBuildKey = "bamboo_result_key" const val disposableKey = "disposable" + private const val defaultResourceOwner = "jforemski" fun parseLifespan( tags: List diff --git a/src/main/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiMod.kt b/src/main/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiMod.kt index 2061e1d..e0d9cb8 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiMod.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiMod.kt @@ -32,15 +32,12 @@ class SshAmiMod private constructor( private val useCase = sshInstanceMod.useCase - private val amiInvestment = Investment( - useCase = useCase, - lifespan = amiLifespan - ) + private val amiInvestment = Investment.Builder(useCase, amiLifespan).build() private val expectedImageSavingTime = Duration.ofMinutes(6) - private val instanceInvestment = Investment( + private val instanceInvestment = Investment.Builder( useCase = useCase, lifespan = sshInstanceMod.expectedDuration + expectedImageSavingTime - ) + ).build() /** * If the [amiCache] finds an AMI matching [sshInstanceMod] and [amiProvider], it will be reused. diff --git a/src/test/kotlin/com/atlassian/performance/tools/aws/AwsIT.kt b/src/test/kotlin/com/atlassian/performance/tools/aws/AwsIT.kt index 0809608..ea9ba51 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/aws/AwsIT.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/aws/AwsIT.kt @@ -20,11 +20,12 @@ import java.util.* class AwsIT { - private val investment = Investment( + private val investment = Investment.Builder( useCase = "Test aws-resources library", - lifespan = Duration.ofMinutes(5), - disposable = true + lifespan = Duration.ofMinutes(5) ) + .disposable(true) + .build() private val workspace = Files.createTempDirectory("AwsIT-") private val awsPrefix = "aws-resources-test-" diff --git a/src/test/kotlin/com/atlassian/performance/tools/aws/api/StackFormulaTest.kt b/src/test/kotlin/com/atlassian/performance/tools/aws/api/StackFormulaTest.kt index 5d09e4d..2a260bf 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/aws/api/StackFormulaTest.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/aws/api/StackFormulaTest.kt @@ -21,10 +21,10 @@ class StackFormulaTest { @Timeout(5, unit = SECONDS) fun shouldNotHang() { val formula = StackFormula( - investment = Investment( + investment = Investment.Builder( useCase = "Unit test the StackFormula", - lifespan = Duration.ofMinutes(1) - ), + lifespan = Duration.ofMinutes(1)) + .build(), aws = FakeAws.awsForUnitTests( batchingCloudformationRefreshPeriod = Duration.ofMillis(100), credentialsProvider = MissingCredentialsProvider() diff --git a/src/test/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiModIT.kt b/src/test/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiModIT.kt index e8bda02..e604bdb 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiModIT.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/aws/api/ami/SshAmiModIT.kt @@ -39,7 +39,7 @@ class SshAmiModIT { val newImageId = sshAmiMod.provideAmiId(aws) // then - val investment = Investment(echo.useCase, Duration.ofMinutes(10)) + val investment = Investment.Builder(echo.useCase, Duration.ofMinutes(10)).build() val prefix = investment.reuseKey() val sshKey = SshKeyFormula(aws.ec2, createTempDirectory(prefix), prefix, investment.lifespan).provision() aws.awaitingEc2.allocateInstance(investment, sshKey, vpcId = null) { launch ->