package tw.com.unit; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.cloudformation.model.*; import com.amazonaws.services.elasticloadbalancing.model.Instance; import org.easymock.EasyMock; import org.easymock.EasyMockRunner; import org.easymock.EasyMockSupport; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import tw.com.*; import tw.com.entity.*; import tw.com.exceptions.CfnAssistException; import tw.com.exceptions.InvalidStackParameterException; import tw.com.exceptions.NotReadyException; import tw.com.exceptions.WrongNumberOfStacksException; import tw.com.providers.CloudFormationClient; import tw.com.repository.CfnRepository; import tw.com.repository.CloudRepository; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import static org.junit.Assert.*; @RunWith(EasyMockRunner.class) public class TestCfnRepository extends EasyMockSupport { private ProjectAndEnv mainProjectAndEnv = new ProjectAndEnv(EnvironmentSetupForTests.PROJECT, EnvironmentSetupForTests.ENV); private CloudFormationClient formationClient; private CfnRepository repository; private EnvironmentTag envTag; private Integer noBuildNumber = -1; private CloudRepository cloudRepository; @Before public void beforeEachTestRuns() { formationClient = createMock(CloudFormationClient.class); cloudRepository = createMock(CloudRepository.class); repository = new CfnRepository(formationClient, cloudRepository, mainProjectAndEnv.getProject()); envTag = new EnvironmentTag(EnvironmentSetupForTests.ENV); } @Test public void testGetAllStacks() { List<Tag> tags = EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist"); List<Stack> list = new LinkedList<>(); list.add(new Stack().withTags(tags).withStackName("matchingStack")); list.add(new Stack().withStackName("noMatchingTags")); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); List<StackEntry> results = repository.getStacks(); assertEquals(1, results.size()); results = repository.getStacks(); // second call should hit cache assertEquals(1, results.size()); verifyAll(); } @Test public void testGetStacksByEnv() { List<Tag> tagsA = EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist"); List<Tag> tagsB = new LinkedList<>(); tagsB.add(EnvironmentSetupForTests.createCfnStackTAG("CFN_ASSIST_ENV", "WrongTest")); tagsB.add(EnvironmentSetupForTests.createCfnStackTAG("CFN_ASSIST_PROJECT", "CfnAssist")); List<Stack> list = new LinkedList<>(); list.add(new Stack().withTags(tagsA).withStackName("matchingStack")); list.add(new Stack().withStackName("wrongEnvStack").withTags(tagsB)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); List<StackEntry> results = repository.getStacks(envTag); assertEquals(1, results.size()); results = repository.getStacks(envTag); // second call should hit cache assertEquals(1, results.size()); verifyAll(); } @Test public void shouldGetStackByEndAndIndex() throws WrongNumberOfStacksException { List<Tag> tagsA = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsB = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsC = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); tagsA.add(new Tag().withKey(AwsFacade.INDEX_TAG).withValue("4")); tagsB.add(new Tag().withKey(AwsFacade.INDEX_TAG).withValue("9")); List<Stack> list = new LinkedList<>(); list.add(new Stack().withStackName("matchingStack").withTags(tagsA).withStackId("anId")); list.add(new Stack().withStackName("wrongStackB").withTags(tagsB)); list.add(new Stack().withStackName("wrongStackC").withTags(tagsC)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); StackEntry result = repository.getStacknameByIndex(envTag, 4); verifyAll(); assertEquals("matchingStack", result.getStackName()); } @Test public void shouldGetStackNameByUpdateIndexSingle() throws WrongNumberOfStacksException { List<Tag> tagsA = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsB = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsC = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); tagsA.add(new Tag().withKey(AwsFacade.UPDATE_INDEX_TAG).withValue("4")); tagsB.add(new Tag().withKey(AwsFacade.INDEX_TAG).withValue("3")); tagsC.add(new Tag().withKey(AwsFacade.UPDATE_INDEX_TAG).withValue("5")); List<Stack> list = new LinkedList<>(); list.add(new Stack().withStackName("matchingStack").withTags(tagsA).withStackId("anId")); list.add(new Stack().withStackName("wrongStackB").withTags(tagsB)); list.add(new Stack().withStackName("wrongStackC").withTags(tagsC)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); StackEntry result = repository.getStacknameByIndex(envTag, 4); verifyAll(); assertEquals("matchingStack", result.getStackName()); } @Test public void shouldGetStackNameByUpdateIndexMultiple() throws WrongNumberOfStacksException { List<Tag> tagsA = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsB = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); List<Tag> tagsC = EnvironmentSetupForTests.createExpectedStackTags("comment",noBuildNumber, mainProjectAndEnv.getProject()); tagsA.add(new Tag().withKey(AwsFacade.UPDATE_INDEX_TAG).withValue("3,4,8")); tagsB.add(new Tag().withKey(AwsFacade.INDEX_TAG).withValue("2")); tagsC.add(new Tag().withKey(AwsFacade.UPDATE_INDEX_TAG).withValue("5,6")); List<Stack> list = new LinkedList<>(); list.add(new Stack().withStackName("matchingStack").withTags(tagsA).withStackId("anId")); list.add(new Stack().withStackName("wrongStackB").withTags(tagsB)); list.add(new Stack().withStackName("wrongStackC").withTags(tagsC)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); StackEntry result = repository.getStacknameByIndex(envTag, 4); verifyAll(); assertEquals("matchingStack", result.getStackName()); } @Test public void testShouldFindResourcesByIdInsideOfAStack() { String logicalId = "logicalIdOfResource"; String expectedPhyId = "physicalIdOfResource"; String stackName = "testStackName"; List<StackResource> stackResources = new LinkedList<>(); stackResources.add(new StackResource().withLogicalResourceId("logWrong").withPhysicalResourceId("phyWrong")); stackResources.add(new StackResource().withLogicalResourceId(logicalId).withPhysicalResourceId(expectedPhyId)); List<Stack> list = new LinkedList<>(); list.add(new Stack(). withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); EasyMock.expect(formationClient.describeStackResources(stackName)).andReturn(stackResources); replayAll(); String physicalId = repository.findPhysicalIdByLogicalId(envTag, logicalId); assertEquals(expectedPhyId, physicalId); physicalId = repository.findPhysicalIdByLogicalId(envTag, logicalId); // hits cache assertEquals(expectedPhyId, physicalId); verifyAll(); } @Test public void testShouldWaitForStatusToChange() throws WrongNumberOfStacksException, InterruptedException { String stackName = "testStack"; Stack inProgressStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackStatus(StackStatus.CREATE_IN_PROGRESS); Stack doneStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackStatus(StackStatus.CREATE_COMPLETE); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(doneStack); replayAll(); String result = repository.waitForStatusToChangeFrom(stackName, StackStatus.CREATE_IN_PROGRESS, Arrays.asList(StackMonitor.CREATE_ABORTS)); assertEquals(StackStatus.CREATE_COMPLETE.toString(), result); verifyAll(); } @Test public void testWaitForStatusShouldAbort() throws WrongNumberOfStacksException, InterruptedException { String stackName = "testStack"; Stack inProgressStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackStatus(StackStatus.CREATE_IN_PROGRESS); Stack abortedStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackStatus(StackStatus.CREATE_FAILED); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(abortedStack); replayAll(); String result = repository.waitForStatusToChangeFrom(stackName, StackStatus.CREATE_IN_PROGRESS, Arrays.asList(StackMonitor.CREATE_ABORTS)); assertEquals(StackStatus.CREATE_FAILED.toString(), result); verifyAll(); } @Test public void testShouldCheckIfStackExists() throws WrongNumberOfStacksException { String stackName = "testStack"; Stack stack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(stack); replayAll(); assertTrue(repository.stackExists(stackName)); verifyAll(); } @Test public void testShouldCheckIfStackExistsButMissing() throws WrongNumberOfStacksException { String stackName = "testStack"; AmazonServiceException amazonServiceException = new AmazonServiceException("exceptionText"); amazonServiceException.setStatusCode(400); EasyMock.expect(formationClient.describeStack(stackName)).andThrow(amazonServiceException); replayAll(); assertFalse(repository.stackExists(stackName)); verifyAll(); } @Test public void testShouldCheckIfStackExistsThrowsIfNotA400() throws WrongNumberOfStacksException { String stackName = "testStack"; AmazonServiceException amazonServiceException = new AmazonServiceException("exceptionText"); amazonServiceException.setStatusCode(500); EasyMock.expect(formationClient.describeStack(stackName)).andThrow(amazonServiceException); replayAll(); try { repository.stackExists(stackName); fail("should have thrown"); } catch(AmazonServiceException expectedThisException) { // expected } verifyAll(); } @Test public void testShouldGetCurrentStackStatus() throws WrongNumberOfStacksException { String stackName = "testStack"; Stack inProgressStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackStatus(StackStatus.CREATE_IN_PROGRESS); Stack abortedStack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName("someOtherName"). withStackStatus(StackStatus.CREATE_FAILED); List<Stack> stacks = new LinkedList<>(); stacks.add(inProgressStack); stacks.add(abortedStack); EasyMock.expect(formationClient.describeAllStacks()).andReturn(stacks); // cached after first call EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(inProgressStack); replayAll(); String result = repository.getStackStatus(stackName); assertEquals(StackStatus.CREATE_IN_PROGRESS.toString(), result); result = repository.getStackStatus(stackName); assertEquals(StackStatus.CREATE_IN_PROGRESS.toString(), result); verifyAll(); } @Test public void emptyStatusIfNoSuchStack() throws WrongNumberOfStacksException, NotReadyException, IOException, InvalidStackParameterException, InterruptedException { List<Stack> stacks = new LinkedList<>(); stacks.add(new Stack().withStackName("ThisIsNotTheStackYouAreLookingFor")); EasyMock.expect(formationClient.describeAllStacks()).andReturn(stacks); replayAll(); String result = repository.getStackStatus("thisStackShouldNotExist"); assertEquals(0, result.length()); verifyAll(); } @Test public void testShouldGetIdForAStackName() throws WrongNumberOfStacksException { String stackName = "testStack"; String stackId = "theIdOfTheStack"; Stack stack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackId(stackId); EasyMock.expect(formationClient.describeStack(stackName)).andReturn(stack); replayAll(); StackNameAndId result = repository.getStackNameAndId(stackName); assertEquals(stackName, result.getStackName()); assertEquals(stackId, result.getStackId()); verifyAll(); } @Test public void shouldFindInstancesForProjectBasedOnCriteria() throws CfnAssistException { String stackName = "testStack"; String stackId = "theIdOfTheStack"; String instanceId = "theInstanceId"; SearchCriteria criteria = new SearchCriteria(EnvironmentSetupForTests.getMainProjectAndEnv()); queryForInstancesExpectations(stackName, stackId, instanceId, "", noBuildNumber); replayAll(); List<String> result = repository.getAllInstancesFor(criteria); assertEquals(result.size(),1); result = repository.getAllInstancesFor(criteria); // cached call assertEquals(result.size(),1); assertEquals(instanceId, result.get(0)); verifyAll(); } private void queryForInstancesExpectations(String stackName, String stackId, String instanceId, String comment, Integer build) { List<StackResource> resources = new LinkedList<>(); resources.add(new StackResource().withResourceType("AWS::EC2::Instance").withPhysicalResourceId(instanceId)); resources.add(new StackResource().withResourceType("AWS::EC2::ELB").withPhysicalResourceId("notAnInstance")); Stack stack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags(comment, build, "CfnAssist")). withStackName(stackName). withStackId(stackId). withStackStatus(StackStatus.CREATE_COMPLETE); List<Stack> list = new LinkedList<>(); list.add(stack); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); EasyMock.expect(formationClient.describeStackResources(stackName)).andReturn(resources); } @Test public void shouldFindInstancesForStack() { String stackName = "testStack"; String instanceId = "theInstanceId"; List<StackResource> resources = new LinkedList<>(); resources.add(new StackResource().withResourceType("AWS::EC2::Instance").withPhysicalResourceId(instanceId)); resources.add(new StackResource().withResourceType("AWS::EC2::ELB").withPhysicalResourceId("notAnInstance")); EasyMock.expect(formationClient.describeStackResources(stackName)).andReturn(resources); replayAll(); List<String> result = repository.getInstancesFor(stackName); assertEquals(result.size(),1); result = repository.getInstancesFor(stackName); // cached call verifyAll(); assertEquals(result.size(),1); assertEquals(instanceId, result.get(0)); } @Test public void shouldFindInstancesForProjAndEnvMakingTypeTag() throws CfnAssistException { String stackName = "testStack"; String stackId = "theIdOfTheStack"; String instanceIdA = "InstanceIdA"; String instanceIdB = "InstanceIdB"; SearchCriteria criteria = new SearchCriteria(EnvironmentSetupForTests.getMainProjectAndEnv()); List<StackResource> resources = new LinkedList<>(); resources.add(new StackResource().withResourceType("AWS::EC2::Instance").withPhysicalResourceId(instanceIdA)); resources.add(new StackResource().withResourceType("AWS::EC2::Instance").withPhysicalResourceId(instanceIdB)); Stack stack = new Stack().withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist")). withStackName(stackName). withStackId(stackId). withStackStatus(StackStatus.CREATE_COMPLETE); List<Stack> stacks = new LinkedList<>(); stacks.add(stack); EasyMock.expect(formationClient.describeAllStacks()).andReturn(stacks); EasyMock.expect(formationClient.describeStackResources(stackName)).andReturn(resources); String typeTag = "theTypeTag"; EasyMock.expect(cloudRepository.getTagsForInstance(instanceIdA)).andStubReturn(withTags("0042", typeTag)); EasyMock.expect(cloudRepository.getTagsForInstance(instanceIdB)).andStubReturn(withTags("0042", "wrongTypeTag")); replayAll(); List<Instance> result = repository.getAllInstancesMatchingType(criteria,typeTag); assertEquals(result.size(),1); result = repository.getAllInstancesMatchingType(criteria, typeTag); // cached call assertEquals(result.size(),1); assertEquals(instanceIdA, result.get(0).getInstanceId()); verifyAll(); } @Test public void testShouldGetStackByName() throws WrongNumberOfStacksException { String stackName = "testStack"; List<Stack> list = new LinkedList<>(); List<Tag> tags = EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist"); list.add(new Stack().withStackName(stackName).withStackId("correctId").withTags(tags)); list.add(new Stack().withStackName("someOtherName").withStackId("wrongId").withTags(tags)); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); Stack result = repository.getStack(stackName); assertEquals(stackName, result.getStackName()); assertEquals("correctId", result.getStackId()); result = repository.getStack(stackName); // cached on this call assertEquals(stackName, result.getStackName()); assertEquals("correctId", result.getStackId()); verifyAll(); } @Test public void shouldGetStacksMatchingProjectEnvAndName() { List<Stack> list = new LinkedList<>(); list.add(new Stack().withStackName("CfnAssist1TestsimpleStack").withStackId("idA").withTags(createTags(1))); list.add(new Stack().withStackName("CfnAssist78TestsomeOtherName").withStackId("idC").withTags(createTags(78))); list.add(new Stack().withStackName("CfnAssist2TestsimpleStack").withStackId("idB").withTags(createTags(2))); list.add(new Stack().withStackName("CfnAssistTestsomeOtherName").withStackId("idE"). withTags(EnvironmentSetupForTests.createExpectedStackTags("",noBuildNumber, "CfnAssist"))); EasyMock.expect(formationClient.describeAllStacks()).andReturn(list); replayAll(); List<StackEntry> result = repository.getStacksMatching(mainProjectAndEnv.getEnvTag(),"simpleStack"); verifyAll(); assertEquals(2, result.size()); assertEquals("idA", result.get(0).getStack().getStackId()); assertEquals("idB", result.get(1).getStack().getStackId()); } private List<Tag> createTags(Integer buildNumber) { return EnvironmentSetupForTests.createExpectedStackTags("", buildNumber, "CfnAssist"); } @Test public void shouldDeleteStack() { // this smells, at least until we pull cache updates down into repository formationClient.deleteStack("stackName"); EasyMock.expectLastCall(); replayAll(); repository.deleteStack("stackName"); verifyAll(); } @Test public void shouldCreateStack() throws CfnAssistException { // this smells, at least until we pull cache updates down into repository Collection<Parameter> parameters = new LinkedList<>(); MonitorStackEvents monitor = new PollingStackMonitor(repository); ProjectAndEnv projAndEnv = EnvironmentSetupForTests.getMainProjectAndEnv(); Tagging tagging = new Tagging(); EasyMock.expect(formationClient.createStack(projAndEnv, "contents", "stackName", parameters, monitor, tagging)).andReturn(new StackNameAndId("stackName", "someStackId")); replayAll(); StackNameAndId result = repository.createStack(projAndEnv, "contents", "stackName", parameters, monitor, tagging); assertEquals("stackName", result.getStackName()); assertEquals("someStackId", result.getStackId()); verifyAll(); } @Test public void shouldUpdateStack() throws CfnAssistException { // this smells, at least until we pull cache updates down into repository Collection<Parameter> parameters = new LinkedList<>(); MonitorStackEvents monitor = new PollingStackMonitor(repository); EasyMock.expect(formationClient.updateStack("contents", parameters, monitor, "stackName")). andReturn(new StackNameAndId("stackName", "someStackId")); replayAll(); StackNameAndId result = repository.updateStack("contents", parameters, monitor, "stackName"); assertEquals("stackName", result.getStackName()); assertEquals("someStackId", result.getStackId()); verifyAll(); } @Test public void shouldUpdateStackWithSNS() throws CfnAssistException { // this smells, at least until we pull cache updates down into repository SNSMonitor snsMonitor = createMock(SNSMonitor.class); Collection<Parameter> parameters = new LinkedList<>(); EasyMock.expect(formationClient.updateStack("contents", parameters, snsMonitor, "stackName")) .andReturn(new StackNameAndId("stackName", "someStackId")); replayAll(); StackNameAndId result = repository.updateStack("contents", parameters, snsMonitor, "stackName"); assertEquals("stackName", result.getStackName()); assertEquals("someStackId", result.getStackId()); verifyAll(); } @Test public void shouldValidateTemplates() { List<TemplateParameter> params = new LinkedList<>(); params.add(new TemplateParameter().withDefaultValue("aDefaultValue")); EasyMock.expect(formationClient.validateTemplate("someContents")).andReturn(params); replayAll(); List<TemplateParameter> result = repository.validateStackTemplate("someContents"); assertEquals(1, result.size()); } private List<com.amazonaws.services.ec2.model.Tag> withTags(String buidNumber, String typeTag) { List<com.amazonaws.services.ec2.model.Tag> tags = new LinkedList<>(); tags.add(new com.amazonaws.services.ec2.model.Tag().withKey(AwsFacade.BUILD_TAG).withValue(buidNumber)); tags.add(new com.amazonaws.services.ec2.model.Tag().withKey(AwsFacade.TYPE_TAG).withValue(typeTag)); return tags; } }