package com.sequenceiq.cloudbreak.core.bootstrap.service; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anySet; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.HashSet; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; import com.sequenceiq.cloudbreak.TestUtil; import com.sequenceiq.cloudbreak.cloud.model.Platform; import com.sequenceiq.cloudbreak.cloud.scheduler.CancellationException; import com.sequenceiq.cloudbreak.common.type.CloudConstants; import com.sequenceiq.cloudbreak.core.CloudbreakException; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ContainerBootstrapApiCheckerTask; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ContainerClusterAvailabilityCheckerTask; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ContainerOrchestratorResolver; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.context.ContainerBootstrapApiContext; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.context.ContainerOrchestratorClusterContext; import com.sequenceiq.cloudbreak.core.bootstrap.service.host.HostBootstrapApiCheckerTask; import com.sequenceiq.cloudbreak.core.bootstrap.service.host.HostClusterAvailabilityCheckerTask; import com.sequenceiq.cloudbreak.core.bootstrap.service.host.HostOrchestratorResolver; import com.sequenceiq.cloudbreak.core.bootstrap.service.host.context.HostBootstrapApiContext; import com.sequenceiq.cloudbreak.core.bootstrap.service.host.context.HostOrchestratorClusterContext; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Orchestrator; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.orchestrator.container.ContainerOrchestrator; import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorCancelledException; import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorFailedException; import com.sequenceiq.cloudbreak.orchestrator.host.HostOrchestrator; import com.sequenceiq.cloudbreak.orchestrator.model.ContainerConfig; import com.sequenceiq.cloudbreak.orchestrator.model.GatewayConfig; import com.sequenceiq.cloudbreak.orchestrator.model.Node; import com.sequenceiq.cloudbreak.orchestrator.state.ExitCriteriaModel; import com.sequenceiq.cloudbreak.repository.OrchestratorRepository; import com.sequenceiq.cloudbreak.repository.StackRepository; import com.sequenceiq.cloudbreak.service.GatewayConfigService; import com.sequenceiq.cloudbreak.service.PollingResult; import com.sequenceiq.cloudbreak.service.PollingService; import com.sequenceiq.cloudbreak.service.StatusCheckerTask; @RunWith(MockitoJUnitRunner.class) public class ClusterBootstrapperTest { private static final com.sequenceiq.cloudbreak.cloud.model.Platform GCP_PLATFORM = Platform.platform(CloudConstants.GCP); @Mock private StackRepository stackRepository; @Mock private OrchestratorRepository orchestratorRepository; @Mock private PollingService<ContainerBootstrapApiContext> containerBootstrapApiPollingService; @Mock private PollingService<HostBootstrapApiContext> hostBootstrapApiPollingService; @Mock private ContainerBootstrapApiCheckerTask containerBootstrapApiCheckerTask; @Mock private HostBootstrapApiCheckerTask hostBootstrapApiCheckerTask; @Mock private PollingService<ContainerOrchestratorClusterContext> containerClusterAvailabilityPollingService; @Mock private PollingService<HostOrchestratorClusterContext> hostClusterAvailabilityPollingService; @Mock private ContainerClusterAvailabilityCheckerTask containerClusterAvailabilityCheckerTask; @Mock private HostClusterAvailabilityCheckerTask hostClusterAvailabilityCheckerTask; @Mock private ClusterBootstrapperErrorHandler clusterBootstrapperErrorHandler; @Mock private ContainerOrchestratorResolver containerOrchestratorResolver; @Mock private HostOrchestratorResolver hostOrchestratorResolver; @Mock private GatewayConfigService gatewayConfigService; @Mock private ContainerConfigService containerConfigService; @Mock private OrchestratorTypeResolver orchestratorTypeResolver; @InjectMocks private ClusterBootstrapper underTest; @Before public void setUp() throws CloudbreakException { reset(stackRepository, orchestratorRepository, containerBootstrapApiPollingService, hostBootstrapApiPollingService, gatewayConfigService, containerBootstrapApiCheckerTask, containerOrchestratorResolver, containerClusterAvailabilityPollingService, hostClusterAvailabilityPollingService, containerClusterAvailabilityCheckerTask, clusterBootstrapperErrorHandler, hostOrchestratorResolver, containerConfigService, orchestratorTypeResolver); when(orchestratorTypeResolver.resolveType(anyString())).thenReturn(OrchestratorType.CONTAINER); ReflectionTestUtils.setField(containerConfigService, "munchausenImageName", "sequence/testcont:0.1.1"); when(gatewayConfigService.getPrimaryGatewayConfig(any())) .thenReturn(new GatewayConfig("10.0.0.1", "198.0.0.1", "10.0.0.1", 8443, "/cert/1", false)); when(gatewayConfigService.getGatewayConfig(any(), any(), any())) .thenReturn(new GatewayConfig("10.0.0.1", "198.0.0.1", "10.0.0.1", 8443, "/cert/1", false)); when(gatewayConfigService.getGatewayIp(any(Stack.class), any(InstanceMetaData.class))).thenReturn("10.0.0.1"); } @Test public void bootstrapClusterWhenEverythingWorksNormally() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new MockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); when(orchestratorRepository.save(any(Orchestrator.class))).thenReturn(new Orchestrator()); underTest.bootstrapContainers(stack); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(clusterBootstrapperErrorHandler, times(0)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(containerBootstrapApiPollingService, times(1)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt()); verify(containerClusterAvailabilityPollingService, times(1)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test public void bootstrapClusterWhenTimeOutComesInClusterAvailabilityPoller() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new MockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.TIMEOUT); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapContainers(stack); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(clusterBootstrapperErrorHandler, times(1)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(containerBootstrapApiPollingService, times(1)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt()); verify(containerClusterAvailabilityPollingService, times(1)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test(expected = CancellationException.class) public void bootstrapClusterWhenOrchestratorDropCancelledException() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new CancelledMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapContainers(stack); } @Test(expected = CloudbreakException.class) public void bootstrapClusterWhenOrchestratorDropFailedException() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new FailedMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapContainers(stack); } @Test public void bootstrapClusterWhenEverythingWorksNormallyWithMoreBootstrapSegment() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new TwoLengthMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())).thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapContainers(stack); verify(clusterBootstrapperErrorHandler, times(0)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(containerBootstrapApiPollingService, times(1)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt()); verify(containerClusterAvailabilityPollingService, times(3)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test public void bootstrapNewNodesInClusterWhenEverythingWorksNormally() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(stackRepository.findOneWithLists(anyLong())).thenReturn(stack); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new MockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapNewNodes(stack.getId(), getPrivateIps(stack)); verify(clusterBootstrapperErrorHandler, times(0)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(containerClusterAvailabilityPollingService, times(2)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test public void bootstrapNewNodesInClusterWhenBootstrapHappeningInTwoSegments() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(stackRepository.findOneWithLists(anyLong())).thenReturn(stack); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new TwoLengthMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapNewNodes(stack.getId(), getPrivateIps(stack)); verify(clusterBootstrapperErrorHandler, times(0)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(containerClusterAvailabilityPollingService, times(3)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test public void bootstrapNewNodesInClusterWhenClusterAvailabilityDropTimeout() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(stackRepository.findOneWithLists(anyLong())).thenReturn(stack); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new MockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.TIMEOUT); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapNewNodes(stack.getId(), getPrivateIps(stack)); verify(clusterBootstrapperErrorHandler, times(1)) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), anySet()); verify(gatewayConfigService, times(1)).getGatewayConfig(any(), any(), any()); verify(containerClusterAvailabilityPollingService, times(2)).pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt()); } @Test(expected = CancellationException.class) public void bootstrapNewNodesInClusterWhenOrchestratorDropCancelledException() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(stackRepository.findOneWithLists(anyLong())).thenReturn(stack); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new CancelledNewNodesMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.TIMEOUT); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapNewNodes(stack.getId(), getPrivateIps(stack)); } @Test(expected = CloudbreakException.class) public void bootstrapNewNodesInClusterWhenOrchestratorDropFailedException() throws CloudbreakException, CloudbreakOrchestratorFailedException { Stack stack = TestUtil.stack(); stack.setCluster(TestUtil.cluster()); when(stackRepository.findOneWithLists(anyLong())).thenReturn(stack); when(containerOrchestratorResolver.get("SWARM")).thenReturn(new FailedNewNodesMockContainerOrchestrator()); when(containerBootstrapApiPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerBootstrapApiContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.SUCCESS); when(containerClusterAvailabilityPollingService.pollWithTimeoutSingleFailure(any(StatusCheckerTask.class), any(ContainerOrchestratorClusterContext.class), anyInt(), anyInt())) .thenReturn(PollingResult.TIMEOUT); doNothing().when(clusterBootstrapperErrorHandler) .terminateFailedNodes(any(HostOrchestrator.class), any(ContainerOrchestrator.class), any(Stack.class), any(GatewayConfig.class), any(Set.class)); underTest.bootstrapNewNodes(stack.getId(), getPrivateIps(stack)); } private Set<String> getPrivateIps(Stack stack) { Set<String> ips = new HashSet<>(); for (InstanceMetaData instanceMetaData : stack.getRunningInstanceMetaData()) { ips.add(instanceMetaData.getPrivateIp()); } return ips; } class FailedNewNodesMockContainerOrchestrator extends MockContainerOrchestrator { @Override public void bootstrapNewNodes(GatewayConfig gatewayConfig, ContainerConfig containerConfig, Set<Node> nodes, ExitCriteriaModel exitCriteriaModel) throws CloudbreakOrchestratorFailedException { throw new CloudbreakOrchestratorFailedException("failed"); } } class CancelledMockContainerOrchestrator extends MockContainerOrchestrator { @Override public void bootstrap(GatewayConfig gatewayConfig, ContainerConfig config, Set<Node> nodes, int consulServerCount, ExitCriteriaModel exitCriteriaModel) throws CloudbreakOrchestratorCancelledException { throw new CloudbreakOrchestratorCancelledException("cancelled"); } } class CancelledNewNodesMockContainerOrchestrator extends MockContainerOrchestrator { @Override public void bootstrapNewNodes(GatewayConfig gatewayConfig, ContainerConfig containerConfig, Set<Node> nodes, ExitCriteriaModel exitCriteriaModel) throws CloudbreakOrchestratorCancelledException { throw new CloudbreakOrchestratorCancelledException("cancelled"); } } class TwoLengthMockContainerOrchestrator extends MockContainerOrchestrator { @Override public int getMaxBootstrapNodes() { return 2; } } class FailedMockContainerOrchestrator extends MockContainerOrchestrator { @Override public void bootstrap(GatewayConfig gatewayConfig, ContainerConfig config, Set<Node> nodes, int consulServerCount, ExitCriteriaModel exitCriteriaModel) throws CloudbreakOrchestratorFailedException { throw new CloudbreakOrchestratorFailedException("failed"); } } }