All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlassian.maven.plugins.aws.it.Provisioner.kt Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
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")
            }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy