com.atlassian.maven.plugins.aws.it.Provisioner.kt Maven / Gradle / Ivy
package com.atlassian.maven.plugins.aws.it
import com.amazonaws.services.cloudformation.AmazonCloudFormation
import com.amazonaws.services.cloudformation.AmazonCloudFormationClientBuilder
import com.amazonaws.services.cloudformation.model.*
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder
import com.amazonaws.services.ec2.model.CreateKeyPairRequest
import com.amazonaws.services.ec2.model.KeyPair
import org.apache.commons.io.FileUtils.readFileToString
import org.apache.commons.io.FileUtils.writeStringToFile
import org.apache.maven.plugin.logging.Log
import java.io.File
import java.nio.file.Files.setPosixFilePermissions
import java.nio.file.attribute.PosixFilePermission.OWNER_READ
import java.util.UUID.randomUUID
class Provisioner(
private val awsRegion: String,
private val workingDirectory: File,
private val stackInfoProvider: StackInfoProvider,
private val log: Log
) {
fun provision(
cloudFormationTemplate: File
) {
val name = "aws-integration-test-plugin-" + randomUUID().toString()
val keyPair = provisionKeyPair(name)
val savedKey = save(keyPair)
val stackInfo = provisionStack(
keyPair.keyName,
readFileToString(cloudFormationTemplate),
name
)
stackInfoProvider.write(stackInfo)
logPopularCommands(savedKey, stackInfo)
}
private fun provisionKeyPair(
name: String
): KeyPair {
return AmazonEC2ClientBuilder.standard()
.withRegion(awsRegion)
.build()
.createKeyPair(CreateKeyPairRequest(name))
.keyPair
}
private fun save(
keyPair: KeyPair
): File {
val file = File(workingDirectory, "${keyPair.keyName}.pem")
writeStringToFile(file, keyPair.keyMaterial)
log.info("Private key location: ${file.absolutePath}")
tryToFixKeyPermissions(file)
return file
}
private fun provisionStack(
keyPairName: String,
template: String,
stackName: String
): StackInfo {
val cloudFormation = AmazonCloudFormationClientBuilder.standard()
.withRegion(awsRegion)
.build()
cloudFormation.createStack(
CreateStackRequest()
.withTags(listOf(
Tag().withKey("Name").withValue("aws-integration-test-plugin"),
Tag().withKey("service_name").withValue("aws-integration-test-plugin"),
Tag().withKey("business_unit").withValue("Engineering-Server"),
Tag().withKey("resource_owner").withValue("mkwidzinski")
))
.withStackName(stackName)
.withTemplateBody(template)
.withParameters(listOf(
Parameter().withParameterKey("KeyName").withParameterValue(keyPairName)
))
.withCapabilities(Capability.CAPABILITY_IAM)
)
waitForStackStartup(stackName, cloudFormation)
return StackInfo(
awsRegion,
stackName,
keyPairName,
cloudFormation.listStackResources(
ListStackResourcesRequest().withStackName(stackName)
)
)
}
private fun waitForStackStartup(
stackName: String,
cloudFormation: AmazonCloudFormation
) {
while (true) {
val result = cloudFormation.describeStacks(DescribeStacksRequest().withStackName(stackName))
val status = result.stacks[0].stackStatus
when (status) {
"CREATE_IN_PROGRESS" -> {
log.info("Waiting for stack $stackName startup, current status: $status")
Thread.sleep(5000)
}
"CREATE_COMPLETE" -> {
log.info("Stack $stackName successfully created")
return
}
else -> throw RuntimeException("Stack $stackName creation failed: $result")
}
}
}
private fun tryToFixKeyPermissions(key: File) {
val desiredPermissions = setOf(OWNER_READ)
try {
setPosixFilePermissions(key.toPath(), desiredPermissions)
} catch (e: Exception) {
log.warn("Failed to change $key permissions to $desiredPermissions", e)
}
}
private fun logPopularCommands(
key: File,
stackInfo: StackInfo
) {
log.info("Potentially working and potentially useful commands:")
stackInfo
.listInstances()
.map { it.publicIpAddress }
.forEach {
log.info("$ ssh -i $key ec2-user@$it")
log.info("$ ssh -i $key ubuntu@$it")
}
}
}