package de.taimos.pipeline.aws.cloudformation.stacksets; import com.amazonaws.services.cloudformation.AmazonCloudFormation; import com.amazonaws.services.cloudformation.model.*; import hudson.model.TaskListener; import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.time.Duration; import java.util.Collections; import java.util.UUID; public class CloudFormationStackSetTest { private AmazonCloudFormation client; private SleepStrategy sleepStrategy; private CloudFormationStackSet stackSet; @Before public void setup() { TaskListener listener = Mockito.mock(TaskListener.class); Mockito.when(listener.getLogger()).thenReturn(System.out); client = Mockito.mock(AmazonCloudFormation.class); sleepStrategy = Mockito.mock(SleepStrategy.class); stackSet = new CloudFormationStackSet(client, "foo", listener, sleepStrategy); } @Test public void stackSetExists() { Mockito.when(client.describeStackSet(Mockito.any(DescribeStackSetRequest.class))) .thenReturn(new DescribeStackSetResult() .withStackSet(new StackSet()) ); Assertions.assertThat(stackSet.exists()).isTrue(); } @Test public void stackSetDoesNotExists() { AmazonCloudFormationException ex = new AmazonCloudFormationException("stack set does not exist"); ex.setErrorCode("StackSetNotFoundException"); Mockito.when(client.describeStackSet(Mockito.any(DescribeStackSetRequest.class))) .thenThrow(ex); Assertions.assertThat(stackSet.exists()).isFalse(); } @Test(expected = AmazonCloudFormationException.class) public void stackSetExistsError() { AmazonCloudFormationException ex = new AmazonCloudFormationException("stack set does not exist"); Mockito.when(client.describeStackSet(Mockito.any(DescribeStackSetRequest.class))) .thenThrow(ex); stackSet.exists(); } @Test public void createTemplateBody() { CreateStackSetResult expected = new CreateStackSetResult(); Mockito.when(client.createStackSet(Mockito.any(CreateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); CreateStackSetResult result = stackSet.create("body", null, Collections.singletonList(parameter1), Collections.singletonList(tag1), null, null); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<CreateStackSetRequest> captor = ArgumentCaptor.forClass(CreateStackSetRequest.class); Mockito.verify(client).createStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new CreateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withTemplateBody("body") ); } @Test public void createTemplateUrl() { CreateStackSetResult expected = new CreateStackSetResult(); Mockito.when(client.createStackSet(Mockito.any(CreateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); CreateStackSetResult result = stackSet.create(null, "url", Collections.singletonList(parameter1), Collections.singletonList(tag1), null, null); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<CreateStackSetRequest> captor = ArgumentCaptor.forClass(CreateStackSetRequest.class); Mockito.verify(client).createStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new CreateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withTemplateURL("url") ); } @Test(expected = IllegalArgumentException.class) public void createNoTemplate() { CreateStackSetResult expected = new CreateStackSetResult(); Mockito.when(client.createStackSet(Mockito.any(CreateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); stackSet.create(null, null, Collections.singletonList(parameter1), Collections.singletonList(tag1), null, null); } @Test public void createAdministratorRoleArn() { CreateStackSetResult expected = new CreateStackSetResult(); Mockito.when(client.createStackSet(Mockito.any(CreateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); CreateStackSetResult result = stackSet.create("body", null, Collections.singletonList(parameter1), Collections.singletonList(tag1), "foo", "baz"); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<CreateStackSetRequest> captor = ArgumentCaptor.forClass(CreateStackSetRequest.class); Mockito.verify(client).createStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new CreateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withAdministrationRoleARN("foo") .withExecutionRoleName("baz") .withTags(tag1) .withTemplateBody("body") ); } @Test public void updateTemplateBody() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); UpdateStackSetResult result = stackSet.update("body", null, new UpdateStackSetRequest() .withParameters(parameter1) .withTags(tag1)); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withTemplateBody("body") ); } @Test public void updateTemplateUrl() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); UpdateStackSetResult result = stackSet.update(null, "url", new UpdateStackSetRequest() .withParameters(parameter1) .withTags(tag1)); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withTemplateURL("url") ); } @Test public void updateTemplateKeepPrevious() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenReturn(expected); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); UpdateStackSetResult result = stackSet.update(null, null, new UpdateStackSetRequest() .withParameters(parameter1) .withTags(tag1)); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withUsePreviousTemplate(true) ); } @Test public void update_OperationInProgressException() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenThrow(OperationInProgressException.class) .thenReturn(expected); Mockito.when(this.sleepStrategy.calculateSleepDuration(Mockito.anyInt())).thenReturn(5L); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); UpdateStackSetResult result = stackSet.update(null, null, new UpdateStackSetRequest() .withParameters(parameter1) .withTags(tag1)); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client, Mockito.times(2)).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withUsePreviousTemplate(true) ); Mockito.verify(this.sleepStrategy).calculateSleepDuration(1); } @Test public void update_StaleRequestException() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenThrow(StaleRequestException.class) .thenReturn(expected); Mockito.when(this.sleepStrategy.calculateSleepDuration(Mockito.anyInt())).thenReturn(5L); Parameter parameter1 = new Parameter() .withParameterKey("foo") .withParameterValue("bar"); Tag tag1 = new Tag() .withKey("bar") .withValue("baz"); UpdateStackSetResult result = stackSet.update(null, null, new UpdateStackSetRequest() .withParameters(parameter1) .withTags(tag1)); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client, Mockito.times(2)).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withParameters(parameter1) .withTags(tag1) .withUsePreviousTemplate(true) ); Mockito.verify(this.sleepStrategy).calculateSleepDuration(1); } @Test public void update_TooManyOperations_LimitExceeded() throws InterruptedException { UpdateStackSetResult expected = new UpdateStackSetResult(); Mockito.when(client.updateStackSet(Mockito.any(UpdateStackSetRequest.class))) .thenThrow(new LimitExceededException("StackSet operations cannot involve more than 3500")) .thenReturn(expected); Mockito.when(this.sleepStrategy.calculateSleepDuration(Mockito.anyInt())).thenReturn(5L); UpdateStackSetResult result = stackSet.update(null, null, new UpdateStackSetRequest()); Assertions.assertThat(result).isSameAs(expected); ArgumentCaptor<UpdateStackSetRequest> captor = ArgumentCaptor.forClass(UpdateStackSetRequest.class); Mockito.verify(client, Mockito.times(2)).updateStackSet(captor.capture()); Assertions.assertThat(captor.getValue()).isEqualTo(new UpdateStackSetRequest() .withStackSetName("foo") .withCapabilities(Capability.values()) .withUsePreviousTemplate(true) ); Mockito.verify(this.sleepStrategy).calculateSleepDuration(1); } @Test public void waitForStackStateStatus() throws InterruptedException { Mockito.when(client.describeStackSet(new DescribeStackSetRequest() .withStackSetName("foo") )).thenReturn(new DescribeStackSetResult() .withStackSet(new StackSet() .withStatus(StackSetStatus.ACTIVE) ) ).thenReturn(new DescribeStackSetResult() .withStackSet(new StackSet() .withStatus(StackSetStatus.DELETED) ) ); stackSet.waitForStackState(StackSetStatus.DELETED, Duration.ofMillis(5)); Mockito.verify(client, Mockito.atLeast(2)) .describeStackSet(Mockito.any(DescribeStackSetRequest.class)); } @Test public void waitForOperationToComplete() throws InterruptedException { String operationId = UUID.randomUUID().toString(); Mockito.when(client.describeStackSetOperation(new DescribeStackSetOperationRequest() .withStackSetName("foo") .withOperationId(operationId) )).thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.RUNNING) ) ).thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.SUCCEEDED) ) ); stackSet.waitForOperationToComplete(operationId, Duration.ofMillis(5)); } @Test public void waitForOperationToCompleteWithThrottle() throws InterruptedException { String operationId = UUID.randomUUID().toString(); AmazonCloudFormationException ex = new AmazonCloudFormationException("error"); ex.setErrorCode("Throttling"); Mockito.when(client.describeStackSetOperation(new DescribeStackSetOperationRequest() .withStackSetName("foo") .withOperationId(operationId) )).thenThrow(ex) .thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.RUNNING) ) ).thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.SUCCEEDED) ) ); stackSet.waitForOperationToComplete(operationId, Duration.ofMillis(5)); } @Test(expected = StackSetOperationFailedException.class) public void waitForOperationToCompleteFailure() throws InterruptedException { String operationId = UUID.randomUUID().toString(); Mockito.when(client.describeStackSetOperation(new DescribeStackSetOperationRequest() .withStackSetName("foo") .withOperationId(operationId) )).thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.RUNNING) ) ).thenReturn(new DescribeStackSetOperationResult() .withStackSetOperation(new StackSetOperation() .withStatus(StackSetOperationStatus.FAILED) ) ); stackSet.waitForOperationToComplete(operationId, Duration.ofMillis(5)); } @Test public void delete() { stackSet.delete(); Mockito.verify(client).deleteStackSet(new DeleteStackSetRequest() .withStackSetName("foo") ); } }