/* * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.aws.core.env.stack.config; import com.amazonaws.services.cloudformation.AmazonCloudFormation; import com.amazonaws.services.cloudformation.model.ListStackResourcesRequest; import com.amazonaws.services.cloudformation.model.ListStackResourcesResult; import com.amazonaws.services.cloudformation.model.StackResourceSummary; import org.hamcrest.CustomMatcher; import org.junit.Test; import org.springframework.cloud.aws.core.env.stack.ListableStackResourceFactory; import org.springframework.cloud.aws.core.env.stack.StackResource; import org.springframework.cloud.aws.core.env.stack.StackResourceRegistry; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class StackResourceRegistryFactoryBeanTest { private static final String STACK_NAME = "myStack"; @Test public void createInstance_stackWithTwoResources_returnsStackResourceRegistryWithTwoResources() throws Exception { // Arrange Map<String, String> resourceIdMappings = new HashMap<>(); resourceIdMappings.put("logicalResourceIdOne", "physicalResourceIdOne"); resourceIdMappings.put("logicalResourceIdTwo", "physicalResourceIdTwo"); StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, resourceIdMappings); // Act StackResourceRegistry stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalResourceIdOne"), is("physicalResourceIdOne")); assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalResourceIdTwo"), is("physicalResourceIdTwo")); } @Test public void createInstance_stackWithTwoResources_listsBothResources() throws Exception { // Arrange Map<String, String> resourceIdMappings = new HashMap<>(); resourceIdMappings.put("logicalResourceIdOne", "physicalResourceIdOne"); resourceIdMappings.put("logicalResourceIdTwo", "physicalResourceIdTwo"); StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, resourceIdMappings); // Act ListableStackResourceFactory stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.getAllResources().size(), is(2)); for (StackResource stackResource : stackResourceRegistry.getAllResources()) { assertThat(stackResource.getLogicalId(), anyOf(is("logicalResourceIdOne"), is("logicalResourceIdTwo"))); assertThat(stackResource.getPhysicalId(), anyOf(is("physicalResourceIdOne"), is("physicalResourceIdTwo"))); assertThat(stackResource.getType(), is("Amazon::SES::Test")); } } @Test public void createInstance_stackWithName_returnsStackResourceRegistryWithStackName() throws Exception { // Arrange StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, Collections.<String, String>emptyMap()); // Act StackResourceRegistry stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.getStackName(), is(STACK_NAME)); } @Test public void createInstance_stackWithNoResources_returnsStackResourceRegistryAnsweringWithNullForNonExistingLogicalResourceId() throws Exception { // Arrange StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, Collections.<String, String>emptyMap()); // Act StackResourceRegistry stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.lookupPhysicalResourceId("nonExistingLogicalResourceId"), is(nullValue())); } @Test public void createInstance_stackWithNestedStack() throws Exception { // Arrange Map<String, String> resourceIdMappings = new HashMap<>(); resourceIdMappings.put("logicalResourceIdOne", "physicalResourceIdOne"); resourceIdMappings.put("logicalNestedStack", "physicalStackId"); resourceIdMappings.put("logicalNestedStack.logicalResourceIdTwo", "physicalResourceIdTwo"); StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, resourceIdMappings); // Act ListableStackResourceFactory stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.getAllResources().size(), is(3)); for (StackResource stackResource : stackResourceRegistry.getAllResources()) { assertThat(stackResource.getLogicalId(), anyOf(is("logicalResourceIdOne"), is("logicalNestedStack"), is("logicalNestedStack.logicalResourceIdTwo"))); assertThat(stackResource.getPhysicalId(), anyOf(is("physicalResourceIdOne"), is("physicalStackId"), is("physicalResourceIdTwo"))); assertThat(stackResource.getType(), anyOf(is("AWS::CloudFormation::Stack"), is("Amazon::SES::Test"))); } assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalNestedStack.logicalResourceIdTwo"), notNullValue()); assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalResourceIdTwo"), notNullValue()); } @Test public void createInstance_stackWithNestedStack_dontReturnDuplicateResourceId() throws Exception { // Arrange Map<String, String> resourceIdMappings = new HashMap<>(); resourceIdMappings.put("logicalNested1Stack", "physicalStackId"); resourceIdMappings.put("logicalNested1Stack.logicalResource", "physicalResourceIdOne"); resourceIdMappings.put("logicalNested2Stack", "physicalStackId"); resourceIdMappings.put("logicalNested2Stack.logicalResource", "physicalResourceIdTwo"); StackResourceRegistryFactoryBean stackResourceRegistryFactoryBean = makeStackResourceRegistryFactoryBean(STACK_NAME, resourceIdMappings); // Act ListableStackResourceFactory stackResourceRegistry = stackResourceRegistryFactoryBean.createInstance(); // Assert assertThat(stackResourceRegistry.getAllResources().size(), is(4)); assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalNested1Stack.logicalResource"), notNullValue()); assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalNested2Stack.logicalResource"), notNullValue()); assertThat(stackResourceRegistry.lookupPhysicalResourceId("logicalResource"), nullValue()); } private static StackResourceRegistryFactoryBean makeStackResourceRegistryFactoryBean(String stackName, Map<String, String> resourceIdMappings) { AmazonCloudFormation amazonCloudFormationClient = makeAmazonCloudFormationClient(resourceIdMappings); StackNameProvider stackNameProvider = makeStackNameProvider(stackName); return new StackResourceRegistryFactoryBean(amazonCloudFormationClient, stackNameProvider); } private static StackNameProvider makeStackNameProvider(String stackName) { StackNameProvider stackNameProvider = mock(StackNameProvider.class); when(stackNameProvider.getStackName()).thenReturn(stackName); return stackNameProvider; } private static AmazonCloudFormation makeAmazonCloudFormationClient(Map<String, String> resourceIdMappings) { Map<String, List<StackResourceSummary>> stackResourceSummaries = new HashMap<>(); stackResourceSummaries.put(STACK_NAME, new ArrayList<StackResourceSummary>()); // allow stack with no resources for (Map.Entry<String, String> entry : resourceIdMappings.entrySet()) { String logicalResourceId = entry.getKey(); String physicalResourceId = entry.getValue(); String physicalStackName; if (logicalResourceId.contains(".")) { physicalStackName = resourceIdMappings.get(logicalResourceId.substring(0, logicalResourceId.lastIndexOf("."))); logicalResourceId = logicalResourceId.substring(logicalResourceId.lastIndexOf(".") + 1); } else { physicalStackName = STACK_NAME; } List<StackResourceSummary> list = stackResourceSummaries.get(physicalStackName); if (list == null) { list = new ArrayList<>(); stackResourceSummaries.put(physicalStackName, list); } list.add(makeStackResourceSummary(logicalResourceId, physicalResourceId)); } AmazonCloudFormation amazonCloudFormationClient = mock(AmazonCloudFormation.class); for (Map.Entry<String, List<StackResourceSummary>> entry : stackResourceSummaries.entrySet()) { final String stackName = entry.getKey(); ListStackResourcesResult listStackResourcesResult = mock(ListStackResourcesResult.class); when(listStackResourcesResult.getStackResourceSummaries()).thenReturn(entry.getValue()); when(amazonCloudFormationClient.listStackResources(argThat(new CustomMatcher<ListStackResourcesRequest>("describe stack '" + entry.getKey() + "'") { @Override public boolean matches(Object item) { return item != null && stackName.equals(((ListStackResourcesRequest) item).getStackName()); } }))).thenReturn(listStackResourcesResult); } return amazonCloudFormationClient; } private static StackResourceSummary makeStackResourceSummary(String logicalResourceId, String physicalResourceId) { StackResourceSummary stackResourceSummary = mock(StackResourceSummary.class); when(stackResourceSummary.getLogicalResourceId()).thenReturn(logicalResourceId); when(stackResourceSummary.getPhysicalResourceId()).thenReturn(physicalResourceId); when(stackResourceSummary.getResourceType()).thenReturn(logicalResourceId.endsWith("Stack") ? "AWS::CloudFormation::Stack" : "Amazon::SES::Test"); return stackResourceSummary; } }