package tw.com.integration;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.cloudformation.AmazonCloudFormationClient;
import com.amazonaws.services.cloudformation.model.*;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeSubnetsRequest;
import com.amazonaws.services.ec2.model.DescribeSubnetsResult;
import com.amazonaws.services.ec2.model.Subnet;
import com.amazonaws.services.ec2.model.Vpc;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sqs.AmazonSQSClient;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.io.FileUtils;
import org.junit.*;
import org.junit.rules.TestName;
import tw.com.*;
import tw.com.entity.ProjectAndEnv;
import tw.com.entity.StackNameAndId;
import tw.com.entity.Tagging;
import tw.com.exceptions.CfnAssistException;
import tw.com.exceptions.MissingCapabilities;
import tw.com.exceptions.WrongStackStatus;
import tw.com.providers.CloudClient;
import tw.com.providers.CloudFormationClient;
import tw.com.providers.SNSEventSource;
import tw.com.repository.CfnRepository;
import tw.com.repository.CloudRepository;
import tw.com.repository.VpcRepository;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import static org.junit.Assert.*;
public class TestCloudFormationClient {
private static AmazonCloudFormationClient cfnClient;
private static AmazonEC2Client ec2Client;
private PollingStackMonitor polligMonitor;
private ProjectAndEnv projAndEnv;
private static VpcRepository vpcRepository;
private static SNSEventSource snsNotifProvider;
private CloudFormationClient formationClient;
private DeletesStacks deletesStacks;
private Vpc mainTestVPC;
@BeforeClass
public static void onceBeforeClassRuns() {
DefaultAWSCredentialsProviderChain credentialsProvider = new DefaultAWSCredentialsProviderChain();
ec2Client = EnvironmentSetupForTests.createEC2Client(credentialsProvider);
vpcRepository = new VpcRepository(new CloudClient(ec2Client));
cfnClient = EnvironmentSetupForTests.createCFNClient(credentialsProvider);
AmazonSNSClient snsClient = EnvironmentSetupForTests.createSNSClient(credentialsProvider);
AmazonSQSClient sqsClient = EnvironmentSetupForTests.createSQSClient(credentialsProvider);
snsNotifProvider = new SNSEventSource(snsClient, sqsClient);
new DeletesStacks(cfnClient).ifPresent("queryStackTest").
ifPresent("createStackTest").
ifPresent("createIAMStackTest").act();
}
@Rule public TestName test = new TestName();
private SNSMonitor snsMonitor;
@Before
public void beforeEachTestRuns() throws MissingArgumentException, CfnAssistException, InterruptedException {
formationClient = new CloudFormationClient(cfnClient);
CloudClient cloudClient = new CloudClient(ec2Client);
CloudRepository cloudRepository = new CloudRepository(cloudClient);
CfnRepository cfnRepository = new CfnRepository(formationClient, cloudRepository, EnvironmentSetupForTests.PROJECT);
polligMonitor = new PollingStackMonitor(cfnRepository );
snsMonitor = new SNSMonitor(snsNotifProvider, cfnRepository);
snsMonitor.init();
deletesStacks = new DeletesStacks(cfnClient);
projAndEnv = EnvironmentSetupForTests.getMainProjectAndEnv();
mainTestVPC = vpcRepository.getCopyOfVpc(projAndEnv);
}
@After
public void afterEachTestHasRun() {
deletesStacks.act();
}
@Test
public void shouldCreateAndDeleteSimpleStack() throws IOException, CfnAssistException, InterruptedException {
String contents = FileUtils.readFileToString(new File(FilesForTesting.SIMPLE_STACK), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters("someVpcId");
String stackName = "createStackTest";
String comment = test.getMethodName();
List<Tag> expectedTags = EnvironmentSetupForTests.createExpectedStackTags(comment,-1, "CfnAssist");
StackNameAndId nameAndId = formationClient.createStack(projAndEnv, contents, stackName, parameters, polligMonitor, createTagging(comment));
deletesStacks.ifPresent(stackName);
assertEquals(stackName, nameAndId.getStackName());
String status = polligMonitor.waitForCreateFinished(nameAndId);
assertEquals(StackStatus.CREATE_COMPLETE.toString(), status);
DescribeStacksResult queryResult = cfnClient.describeStacks(new DescribeStacksRequest().withStackName(stackName));
assertEquals(1, queryResult.getStacks().size());
Stack stack = queryResult.getStacks().get(0);
assertEquals(stack.getStackId(), nameAndId.getStackId());
List<Tag> stackTags = stack.getTags();
assertEquals(expectedTags.size(), stackTags.size());
assert(stackTags.containsAll(expectedTags));
///////
// now delete
formationClient.deleteStack(stackName);
status = polligMonitor.waitForDeleteFinished(nameAndId);
assertEquals(StackStatus.DELETE_COMPLETE.toString(), status);
try {
cfnClient.describeStacks(new DescribeStacksRequest().withStackName(stackName));
fail("throws if stack does not exist");
}
catch(AmazonServiceException expectedException) {
assertEquals(400, expectedException.getStatusCode());
}
}
private Tagging createTagging(String comment) {
Tagging tagging = new Tagging();
tagging.setCommentTag(comment);
return tagging;
}
@Test
public void shouldThrowIfCapabilitiesNotSetCorrectly() throws IOException, CfnAssistException {
String contents = FileUtils.readFileToString(new File(FilesForTesting.STACK_IAM_CAP), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters("someVpcId");
String stackName = "createIAMStackTest";
String comment = test.getMethodName();
deletesStacks.ifPresent(stackName);
// should trigger capability exception
try {
formationClient.createStack(projAndEnv, contents, stackName, parameters, polligMonitor, createTagging(comment));
fail("Was expecting MissingCapabilities");
}
catch(MissingCapabilities expected) {
// expected exception
}
}
@Test
public void shouldCreateAndDeleteSimpleStackNeedingIAMCapbility() throws IOException, CfnAssistException, InterruptedException {
String contents = FileUtils.readFileToString(new File(FilesForTesting.STACK_IAM_CAP), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters("someVpcId");
String stackName = "createIAMStackTest";
String comment = test.getMethodName();
projAndEnv.setUseCapabilityIAM();
StackNameAndId nameAndId = formationClient.createStack(projAndEnv, contents, stackName, parameters, polligMonitor, createTagging(comment));
deletesStacks.ifPresent(stackName);
// this create will fail, but due to lack of user perms and not because of capabilities exception
try {
polligMonitor.waitForCreateFinished(nameAndId);
fail("show have failed to create");
}
catch(WrongStackStatus expected) {
// expected
}
}
@Test
public void shouldQueryCreatedStack() throws IOException, CfnAssistException, InterruptedException {
String vpcId = mainTestVPC.getVpcId();
String cidr = "10.0.10.0/24";
String contents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_CIDR_PARAM), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters(vpcId);
parameters.add(new Parameter().withParameterKey("cidr").withParameterValue(cidr));
String stackName = "queryStackTest";
StackNameAndId nameAndId = formationClient.createStack(projAndEnv, contents, stackName, parameters,
polligMonitor, createTagging(test.getMethodName()));
deletesStacks.ifPresent(nameAndId);
String status = polligMonitor.waitForCreateFinished(nameAndId);
assertEquals(StackStatus.CREATE_COMPLETE.toString(), status);
// query all stacks
List<Stack> resultStacks = formationClient.describeAllStacks();
assertTrue(resultStacks.size()>0);
boolean seen = false;
for(Stack candidate : resultStacks) {
if (candidate.getStackName().equals("queryStackTest")) {
seen = true;
break;
}
}
assertTrue(seen);
// query single stack
Stack resultStack = formationClient.describeStack("queryStackTest");
assertEquals("queryStackTest", resultStack.getStackName());
// query events
List<StackEvent> resultEvents = formationClient.describeStackEvents("queryStackTest");
assertTrue(resultEvents.size()>0); // TODO what can we check here?
// query resources
assertCIDR(nameAndId, cidr, vpcId);
}
@Test
public void shouldCreateAndThenUpdateAStack() throws IOException, CfnAssistException, InterruptedException {
String vpcId = mainTestVPC.getVpcId();
String contents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_STACK), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters(vpcId);
String stackName = "createStackTest";
StackNameAndId nameAndId = formationClient.createStack(projAndEnv, contents, stackName, parameters, polligMonitor,
createTagging(test.getMethodName()));
deletesStacks.ifPresent(stackName);
assertEquals(stackName, nameAndId.getStackName());
String status = polligMonitor.waitForCreateFinished(nameAndId);
assertEquals(StackStatus.CREATE_COMPLETE.toString(), status);
assertCIDR(nameAndId, "10.0.10.0/24", vpcId);
/////
// now update
String newContents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_STACK_DELTA), Charset.defaultCharset());
nameAndId = formationClient.updateStack(newContents, parameters, polligMonitor, stackName);
assertEquals(stackName, nameAndId.getStackName());
status = polligMonitor.waitForUpdateFinished(nameAndId);
assertEquals(StackStatus.UPDATE_COMPLETE.toString(), status);
assertCIDR(nameAndId, "10.0.99.0/24", vpcId);
}
@Test
public void shouldCreateAndThenUpdateAStackAddingSNS() throws IOException, CfnAssistException, InterruptedException {
String vpcId = mainTestVPC.getVpcId();
String contents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_STACK), Charset.defaultCharset());
Collection<Parameter> parameters = createStandardParameters(vpcId);
String stackName = "createStackTest";
StackNameAndId nameAndId = formationClient.createStack(projAndEnv, contents, stackName, parameters,
polligMonitor, createTagging(test.getMethodName()));
deletesStacks.ifPresent(stackName);
assertEquals(stackName, nameAndId.getStackName());
String status = polligMonitor.waitForCreateFinished(nameAndId);
assertEquals(StackStatus.CREATE_COMPLETE.toString(), status);
assertCIDR(nameAndId, "10.0.10.0/24", vpcId);
/////
// now update
String newContents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_STACK_DELTA), Charset.defaultCharset());
nameAndId = formationClient.updateStack(newContents, parameters, snsMonitor, stackName);
assertEquals(stackName, nameAndId.getStackName());
status = snsMonitor.waitForUpdateFinished(nameAndId);
assertEquals(StackStatus.UPDATE_COMPLETE.toString(), status);
assertCIDR(nameAndId, "10.0.99.0/24", vpcId);
}
@Test
public void shouldValidateTemplates() throws IOException {
String contents = FileUtils.readFileToString(new File(FilesForTesting.SUBNET_STACK), Charset.defaultCharset());
List<TemplateParameter> result = formationClient.validateTemplate(contents);
assertEquals(4, result.size());
int i;
for(i=0; i<4; i++) {
TemplateParameter parameter = result.get(i);
if (parameter.getParameterKey().equals("zoneA")) break;
}
TemplateParameter zoneAParameter = result.get(i);
assertEquals("zoneA", zoneAParameter.getParameterKey());
assertEquals("eu-west-1a", zoneAParameter.getDefaultValue());
assertEquals("zoneADescription", zoneAParameter.getDescription());
}
private void assertCIDR(StackNameAndId nameAndId, String initialCidr, String vpcId) {
List<StackResource> resultResources = formationClient.describeStackResources(nameAndId.getStackName());
assertEquals(1, resultResources.size());
String subnetId = resultResources.get(0).getPhysicalResourceId();
Subnet subnet = getSubnetDetails(subnetId);
assertEquals(initialCidr, subnet.getCidrBlock());
assertEquals(vpcId, subnet.getVpcId());
}
private Subnet getSubnetDetails(String physicalId) {
DescribeSubnetsRequest describeSubnetsRequest = new DescribeSubnetsRequest();
Collection<String> subnetIds = new LinkedList<>();
subnetIds.add(physicalId);
describeSubnetsRequest.setSubnetIds(subnetIds);
DescribeSubnetsResult result = ec2Client.describeSubnets(describeSubnetsRequest);
assertEquals(1, result.getSubnets().size());
return result.getSubnets().get(0);
}
private Collection<Parameter> createStandardParameters(String vpcId) {
Collection<Parameter> parameters = new LinkedList<>();
parameters.add(new Parameter().withParameterKey("env").withParameterValue("Test"));
parameters.add(new Parameter().withParameterKey("vpc").withParameterValue(vpcId));
return parameters;
}
}